Apply GH-DEC-2026-017 to the layer declaration; close KG-IN-0007.
INTENT.md frontmatter governs and layer.yaml is marked derived from it. standard_version is removed from both forms (A12). The checker and its tests change in the same commit: they no longer require the field, they reject it, they fold case against the four-token vocabulary (A9) instead of comparing with plain equality, and they still fail a layer disagreement that survives the fold (A11). Neither layer value is re-spelled. The ruling answers kings-guard's "validated-against" reading of the field (§5: version-scoped state belongs in the derived conformance record), so that version now lives in the checker as VALIDATED_AGAINST and is printed in every run's output. The standard: path in INTENT.md is unversioned to match the reference form. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 63291@bnt-lap001 Assistant-Session: 8bd77868-ca68-4f49-bb1e-d539ecc0d703
This commit is contained in:
parent
6752e5e570
commit
5120adf55a
6 changed files with 259 additions and 31 deletions
16
INTENT.md
16
INTENT.md
|
|
@ -2,10 +2,18 @@
|
||||||
# NetKingdom security layer declaration (statute §11, companion §2).
|
# NetKingdom security layer declaration (statute §11, companion §2).
|
||||||
# Machine-readable form; the prose below is the same claim in our own voice.
|
# Machine-readable form; the prose below is the same claim in our own voice.
|
||||||
# Full contact map and gap records: layer.yaml
|
# Full contact map and gap records: layer.yaml
|
||||||
|
# THE layer declaration (§11, GH-DEC-2026-017 §1). `layer.yaml` is derived from
|
||||||
|
# this frontmatter and must agree with it; it does not govern. Comparison of the
|
||||||
|
# §3 vocabulary is ASCII case-insensitive, so `Staff` here and `staff` there are
|
||||||
|
# the same value and neither is re-spelled (GH-DEC-2026-017 §2).
|
||||||
layer: Staff
|
layer: Staff
|
||||||
role: null # Engines only
|
role: null # Engines only
|
||||||
standard: net-kingdom/canon/standards/security-layer-model_v0.7.md
|
# No `standard_version` here or in the sidecar (GH-DEC-2026-017 §5 / A12): the
|
||||||
standard_version: "0.7"
|
# declared layer is a standing property. The version the conformance check was
|
||||||
|
# built and validated against is recorded by the checker itself
|
||||||
|
# (VALIDATED_AGAINST in scripts/check_layer_conformance.py); assent versions
|
||||||
|
# live with each assent in decisions/decisions.md.
|
||||||
|
standard: net-kingdom/canon/standards/security-layer-model
|
||||||
declared_by: decisions/decisions.md#KG-DEC-2026-001
|
declared_by: decisions/decisions.md#KG-DEC-2026-001
|
||||||
declared_at: "2026-08-29"
|
declared_at: "2026-08-29"
|
||||||
conformance_state: blocked-clean
|
conformance_state: blocked-clean
|
||||||
|
|
@ -20,7 +28,9 @@ principal_kinds: [human, agent]
|
||||||
> ratified by `gate-house/decisions/decisions.md` GH-DEC-2026-001; assented here
|
> ratified by `gate-house/decisions/decisions.md` GH-DEC-2026-001; assented here
|
||||||
> by `decisions/decisions.md` KG-DEC-2026-001 on 2026-08-28, against v0.1.)*
|
> by `decisions/decisions.md` KG-DEC-2026-001 on 2026-08-28, against v0.1.)*
|
||||||
>
|
>
|
||||||
> **The declaration is the frontmatter above plus `layer.yaml`**, checked by
|
> **The declaration is the frontmatter above.** `layer.yaml` is its derived
|
||||||
|
> machine-readable form (contact map and gap records), marked derived and required
|
||||||
|
> to agree with it (`GH-DEC-2026-017` §1); both are checked by
|
||||||
> `scripts/check_layer_conformance.py` and tested in
|
> `scripts/check_layer_conformance.py` and tested in
|
||||||
> `tests/test_layer_conformance.py`. Prose cannot distinguish a declaration from
|
> `tests/test_layer_conformance.py`. Prose cannot distinguish a declaration from
|
||||||
> a transcribed review, so those are authoritative and this note is commentary
|
> a transcribed review, so those are authoritative and this note is commentary
|
||||||
|
|
|
||||||
|
|
@ -360,3 +360,6 @@ dereferencing a judgment. Argument: `docs/StatuteV08Review.md` F1.
|
||||||
§3.4 properties holding.
|
§3.4 properties holding.
|
||||||
- `layer.yaml` is not edited by this decision. Its `standard_version: "0.7"`
|
- `layer.yaml` is not edited by this decision. Its `standard_version: "0.7"`
|
||||||
stays pending the B4 ruling (`KG-IN-0007`, commentary F4).
|
stays pending the B4 ruling (`KG-IN-0007`, commentary F4).
|
||||||
|
*Superseded 2026-09-21:* B4 was ruled in `GH-DEC-2026-017` §5 and the field was
|
||||||
|
removed under `KG-IN-0007`; the validated-against version now lives in
|
||||||
|
`scripts/check_layer_conformance.py` (`VALIDATED_AGAINST`).
|
||||||
|
|
|
||||||
|
|
@ -233,14 +233,13 @@ state_hub_intake_id: "01a0c14f-7db6-7787-aa16-a6bb06d745d2"
|
||||||
id: KG-IN-0007
|
id: KG-IN-0007
|
||||||
kind: intake
|
kind: intake
|
||||||
title: §11 declaration-form precedence and case sensitivity
|
title: §11 declaration-form precedence and case sensitivity
|
||||||
status: open
|
status: closed
|
||||||
origin: cross-repo
|
origin: cross-repo
|
||||||
origin_ref: flex-auth FLEX-WP-0030 boundaries-review B1 (corrected 2026-09-21)
|
origin_ref: flex-auth FLEX-WP-0030 boundaries-review B1 (corrected 2026-09-21)
|
||||||
priority: medium
|
priority: medium
|
||||||
owner: kings-guard
|
owner: kings-guard
|
||||||
requested_by: flex-auth
|
requested_by: flex-auth
|
||||||
standard: net-kingdom/canon/standards/security-layer-model_v0.8.md
|
standard: net-kingdom/canon/standards/security-layer-model_v0.8.md
|
||||||
blocked_on: gate-house ruling on §11 precedence and §3 case sensitivity
|
|
||||||
related:
|
related:
|
||||||
- KG-DEC-2026-004
|
- KG-DEC-2026-004
|
||||||
- KG-DEC-2026-001
|
- KG-DEC-2026-001
|
||||||
|
|
@ -261,6 +260,19 @@ description: >
|
||||||
an assent; the field means two things and the statute does not say which. When
|
an assent; the field means two things and the statute does not say which. When
|
||||||
the rulings land, align both files and the version field in one commit.
|
the rulings land, align both files and the version field in one commit.
|
||||||
Position and argument: docs/StatuteV08Review.md F3 and F4.
|
Position and argument: docs/StatuteV08Review.md F3 and F4.
|
||||||
|
resolution: >
|
||||||
|
Ruled by gate-house GH-DEC-2026-017 (amendments A9, A11, A12) and applied.
|
||||||
|
Precedence: INTENT.md governs; layer.yaml is now marked `derived: true`,
|
||||||
|
`derived_from: INTENT.md`. Case: the §3 vocabulary is closed at four tokens
|
||||||
|
and compared ASCII case-insensitively, so `Staff` and `staff` are one value
|
||||||
|
and neither file was re-spelled; the checker and tests now fold instead of
|
||||||
|
comparing with plain equality, and still fail a disagreement that survives
|
||||||
|
the fold. Version: removed from both forms. The ruling answers kings-guard's
|
||||||
|
validated-against reading rather than leaving room for it (§5: version-scoped
|
||||||
|
state belongs in the derived conformance record; keeping the field for
|
||||||
|
information was declined), so the validated-against version moved to
|
||||||
|
VALIDATED_AGAINST in scripts/check_layer_conformance.py and is printed in
|
||||||
|
every run's output. Field and checker changed in one commit.
|
||||||
created: '2026-09-21'
|
created: '2026-09-21'
|
||||||
updated: '2026-09-21'
|
updated: '2026-09-21'
|
||||||
state_hub_intake_id: "01a0c14f-935d-7e08-9f2a-fffd0dba2473"
|
state_hub_intake_id: "01a0c14f-935d-7e08-9f2a-fffd0dba2473"
|
||||||
|
|
|
||||||
53
layer.yaml
53
layer.yaml
|
|
@ -1,6 +1,12 @@
|
||||||
# kings-guard — NetKingdom security layer declaration
|
# kings-guard — DERIVED form of the NetKingdom security layer declaration
|
||||||
#
|
#
|
||||||
# Framework: net-kingdom/canon/standards/security-layer-model_v0.7.md
|
# THIS FILE DOES NOT GOVERN. The declaration is the `layer:` key in INTENT.md's
|
||||||
|
# frontmatter; this file is a derived artifact under §11's derived-artifact rule
|
||||||
|
# and must agree with it (GH-DEC-2026-017 §1, amendment A11). A disagreement
|
||||||
|
# between the two forms is a finding in its own right and is reported by
|
||||||
|
# scripts/check_layer_conformance.py, never resolved away by precedence.
|
||||||
|
#
|
||||||
|
# Framework: net-kingdom/canon/standards/security-layer-model
|
||||||
# Assent: decisions/decisions.md KG-DEC-2026-001 (kings-guard's own voice, §11)
|
# Assent: decisions/decisions.md KG-DEC-2026-001 (kings-guard's own voice, §11)
|
||||||
# Validate: python3 scripts/check_layer_conformance.py
|
# Validate: python3 scripts/check_layer_conformance.py
|
||||||
#
|
#
|
||||||
|
|
@ -13,22 +19,41 @@
|
||||||
# capabilities that would need them sit at zero instead. Under §11 that is
|
# capabilities that would need them sit at zero instead. Under §11 that is
|
||||||
# BLOCKED-CLEAN, which MUST NOT rank below conforming.
|
# BLOCKED-CLEAN, which MUST NOT rank below conforming.
|
||||||
#
|
#
|
||||||
# OPEN — deliberately not resolved here (KG-IN-0007, commentary F3/F4):
|
# RULED, 2026-09-21 — GH-DEC-2026-017, applied here (closes KG-IN-0007):
|
||||||
# * `layer: staff` below and `layer: Staff` in INTENT.md are both forms §11
|
# * Precedence: INTENT.md governs; this file is derived (§1 / A11). Hence
|
||||||
# permits, and §11 does not say which governs when both are present. Two
|
# `derived: true` and `derived_from: INTENT.md` below.
|
||||||
# conforming conformance runs therefore disagree about this repository.
|
# * Case: §3's vocabulary is closed at four tokens {Taxonomy, Tooling, Engine,
|
||||||
# Neither file is being changed until gate-house rules precedence AND
|
# Staff} and comparison is ASCII case-insensitive (§2, §3 / A9). `layer:
|
||||||
# whether the §3 vocabulary is case-sensitive; choosing here would be
|
# staff` below and `layer: Staff` in INTENT.md are THE SAME VALUE and neither
|
||||||
# kings-guard authoring a ruling it does not own.
|
# is re-spelled. Do not "fix" either to match the other; the checker folds.
|
||||||
# * `standard_version` below names the version this declaration was VALIDATED
|
# * Version: a layer declaration MUST NOT carry a standard version (§5 / A12).
|
||||||
# against, not the version of any assent — assents live in
|
# `standard_version: "0.7"` is removed from this file and from INTENT.md.
|
||||||
# decisions/decisions.md, each naming its own version. Whether a declaration
|
#
|
||||||
# may carry a version at all is flex-auth's B4, also unruled.
|
# History of the unruled state (kept, not rewritten): before the ruling this
|
||||||
|
# block recorded both questions as OPEN and changed neither file, because
|
||||||
|
# picking a form would have been kings-guard authoring a ruling gate-house holds.
|
||||||
|
# It also argued that `standard_version` here meant the version this declaration
|
||||||
|
# was VALIDATED against, not an assent version, so flex-auth's B4 might not
|
||||||
|
# reach it. GH-DEC-2026-017 §5 answers that reading rather than leaving room for
|
||||||
|
# it: version-scoped state — which is what "validated against" is — belongs in
|
||||||
|
# the derived conformance record, and keeping the field "for information" was
|
||||||
|
# declined because a field that is present will be branched on. The
|
||||||
|
# validated-against version therefore now lives in the checker, as
|
||||||
|
# VALIDATED_AGAINST in scripts/check_layer_conformance.py, and is printed in
|
||||||
|
# every run's output (`make check-layer`). Assent versions still live with each
|
||||||
|
# assent in decisions/decisions.md.
|
||||||
# Assent to §9.5 at v0.8 is KG-DEC-2026-004 and does not move this file.
|
# Assent to §9.5 at v0.8 is KG-DEC-2026-004 and does not move this file.
|
||||||
|
|
||||||
schema_version: "0.1"
|
schema_version: "0.1"
|
||||||
framework: netkingdom-security-layer-model
|
framework: netkingdom-security-layer-model
|
||||||
standard_version: "0.7"
|
|
||||||
|
# §11 derived-artifact marking (GH-DEC-2026-017 §1 / A11).
|
||||||
|
derived: true
|
||||||
|
derived_from: INTENT.md
|
||||||
|
|
||||||
|
# NO `standard_version` KEY, AND DO NOT ADD ONE BACK (GH-DEC-2026-017 §5 / A12).
|
||||||
|
# Absence is enforced by scripts/check_layer_conformance.py.
|
||||||
|
|
||||||
repository: kings-guard
|
repository: kings-guard
|
||||||
layer: staff
|
layer: staff
|
||||||
declared_by: decisions/decisions.md#KG-DEC-2026-001
|
declared_by: decisions/decisions.md#KG-DEC-2026-001
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,21 @@ OpenBao or cluster client during an incident because the engine surface still
|
||||||
does not exist (§9.2). That is precisely the "small convenience" §6 warns about,
|
does not exist (§9.2). That is precisely the "small convenience" §6 warns about,
|
||||||
and it would arrive as a one-line import.
|
and it would arrive as a one-line import.
|
||||||
|
|
||||||
|
Declaration form (GH-DEC-2026-017, amendments A9, A11, A12): INTENT.md's
|
||||||
|
frontmatter `layer:` key governs; layer.yaml is a derived artifact that must be
|
||||||
|
marked derived, name INTENT.md, and agree with it. Layer comparison ASCII-folds
|
||||||
|
case against §3's closed four-token vocabulary, so `Staff` and `staff` agree and
|
||||||
|
neither file is re-spelled. A disagreement that survives the fold is reported as
|
||||||
|
a finding, not resolved by precedence. Neither form may carry a
|
||||||
|
`standard_version`; the version this check was built and validated against is
|
||||||
|
the checker's own, VALIDATED_AGAINST below, and is printed in every run's output
|
||||||
|
— this script's output is the derived conformance record A12 points to.
|
||||||
|
|
||||||
Review dates are reported, never enforced: a date-triggered failure breaks the
|
Review dates are reported, never enforced: a date-triggered failure breaks the
|
||||||
build on a calendar day with no code change.
|
build on a calendar day with no code change.
|
||||||
|
|
||||||
Exit 0 clean, 1 undeclared contact found, 2 declaration malformed.
|
Exit 0 clean, 1 undeclared contact found or the two declaration forms disagree,
|
||||||
|
2 declaration malformed.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -35,6 +46,18 @@ import yaml
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
SRC = ROOT / "src" / "kings_guard"
|
SRC = ROOT / "src" / "kings_guard"
|
||||||
DECL = ROOT / "layer.yaml"
|
DECL = ROOT / "layer.yaml"
|
||||||
|
INTENT = ROOT / "INTENT.md"
|
||||||
|
|
||||||
|
# The standard text this checker was built and validated against. This is the
|
||||||
|
# derived conformance record's version (GH-DEC-2026-017 §5 / A12), moved here
|
||||||
|
# from layer.yaml's former `standard_version: "0.7"`, which recorded the same
|
||||||
|
# fact in a place the ruling says a declaration must not carry it. Bump it when
|
||||||
|
# the checker is re-validated against a newer accepted text.
|
||||||
|
VALIDATED_AGAINST = "net-kingdom/canon/standards/security-layer-model_v0.7.md"
|
||||||
|
|
||||||
|
# §3's closed layer vocabulary (A9): four tokens, compared ASCII case-insensitively.
|
||||||
|
LAYER_VOCABULARY = {"taxonomy", "tooling", "engine", "staff"}
|
||||||
|
EXPECTED_LAYER = "staff" # kings-guard's own §4 row, folded
|
||||||
|
|
||||||
# Import roots that would constitute a direct Tooling-layer client under §4.
|
# Import roots that would constitute a direct Tooling-layer client under §4.
|
||||||
# Matched against the top-level module of every import in src/.
|
# Matched against the top-level module of every import in src/.
|
||||||
|
|
@ -77,25 +100,86 @@ CREDENTIAL_LITERAL = re.compile(
|
||||||
SKIP_CREDENTIAL_SCAN_DIRS = {".git", ".venv", "__pycache__", ".pytest_cache", ".ruff_cache"}
|
SKIP_CREDENTIAL_SCAN_DIRS = {".git", ".venv", "__pycache__", ".pytest_cache", ".ruff_cache"}
|
||||||
|
|
||||||
|
|
||||||
|
def fold(value: object) -> str:
|
||||||
|
"""ASCII case-fold, per §3 as amended (A9): two spellings are one token."""
|
||||||
|
return str(value).encode("ascii", "replace").decode("ascii").lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _malformed(message: str) -> None:
|
||||||
|
print(f"FAIL: {message}", file=sys.stderr)
|
||||||
|
raise SystemExit(2)
|
||||||
|
|
||||||
|
|
||||||
|
def load_governing_layer() -> str:
|
||||||
|
"""The declaration: INTENT.md frontmatter `layer:` (§11, GH-DEC-2026-017 §1)."""
|
||||||
|
if not INTENT.exists():
|
||||||
|
_malformed(f"no {INTENT.name} — §11's governing declaration form")
|
||||||
|
lines = INTENT.read_text().splitlines()
|
||||||
|
if not lines or lines[0].strip() != "---":
|
||||||
|
_malformed(f"{INTENT.name} has no frontmatter to carry the declaration (§11)")
|
||||||
|
try:
|
||||||
|
end = lines[1:].index("---") + 1
|
||||||
|
except ValueError:
|
||||||
|
_malformed(f"{INTENT.name} frontmatter is not terminated")
|
||||||
|
try:
|
||||||
|
front = yaml.safe_load("\n".join(lines[1:end])) or {}
|
||||||
|
except yaml.YAMLError as exc:
|
||||||
|
_malformed(f"{INTENT.name} frontmatter is not parseable: {exc}")
|
||||||
|
if "layer" not in front:
|
||||||
|
_malformed(f"{INTENT.name} frontmatter has no 'layer' key — §11's declaration")
|
||||||
|
if "standard_version" in front:
|
||||||
|
_malformed(
|
||||||
|
f"{INTENT.name} frontmatter carries 'standard_version' — a layer "
|
||||||
|
"declaration MUST NOT carry a standard version (§11 as amended by A12)"
|
||||||
|
)
|
||||||
|
layer = front["layer"]
|
||||||
|
if fold(layer) not in LAYER_VOCABULARY:
|
||||||
|
_malformed(
|
||||||
|
f"{INTENT.name} declares layer {layer!r}, outside §3's closed vocabulary "
|
||||||
|
f"{sorted(LAYER_VOCABULARY)} (case-insensitive)"
|
||||||
|
)
|
||||||
|
if fold(layer) != EXPECTED_LAYER:
|
||||||
|
_malformed(f"{INTENT.name} declares layer {layer!r}, expected Staff (§4 row)")
|
||||||
|
return str(layer)
|
||||||
|
|
||||||
|
|
||||||
def load_declaration() -> dict:
|
def load_declaration() -> dict:
|
||||||
|
"""The derived form, layer.yaml (§11 derived-artifact rule)."""
|
||||||
if not DECL.exists():
|
if not DECL.exists():
|
||||||
print(f"FAIL: no declaration at {DECL.relative_to(ROOT)} (§11)", file=sys.stderr)
|
_malformed(f"no declaration at {DECL.relative_to(ROOT)} (§11)")
|
||||||
raise SystemExit(2)
|
|
||||||
try:
|
try:
|
||||||
data = yaml.safe_load(DECL.read_text())
|
data = yaml.safe_load(DECL.read_text())
|
||||||
except yaml.YAMLError as exc:
|
except yaml.YAMLError as exc:
|
||||||
print(f"FAIL: {DECL.name} is not parseable: {exc}", file=sys.stderr)
|
print(f"FAIL: {DECL.name} is not parseable: {exc}", file=sys.stderr)
|
||||||
raise SystemExit(2) from exc
|
raise SystemExit(2) from exc
|
||||||
for key in ("layer", "repository", "tooling_contacts", "standard_version"):
|
for key in ("layer", "repository", "tooling_contacts", "derived", "derived_from"):
|
||||||
if key not in data:
|
if key not in data:
|
||||||
print(f"FAIL: {DECL.name} missing required key '{key}' (§11)", file=sys.stderr)
|
_malformed(f"{DECL.name} missing required key '{key}' (§11)")
|
||||||
raise SystemExit(2)
|
if data["derived"] is not True:
|
||||||
if data["layer"] != "staff":
|
_malformed(f"{DECL.name} must be marked 'derived: true' (§11, GH-DEC-2026-017 §1)")
|
||||||
print(f"FAIL: declared layer is '{data['layer']}', expected 'staff'", file=sys.stderr)
|
if data["derived_from"] != "INTENT.md":
|
||||||
raise SystemExit(2)
|
_malformed(
|
||||||
|
f"{DECL.name} derives from {data['derived_from']!r}; §11 names INTENT.md "
|
||||||
|
"as the governing declaration"
|
||||||
|
)
|
||||||
|
if "standard_version" in data:
|
||||||
|
_malformed(
|
||||||
|
f"{DECL.name} carries 'standard_version' — a layer declaration MUST NOT "
|
||||||
|
"carry a standard version (§11 as amended by A12)"
|
||||||
|
)
|
||||||
|
if fold(data["layer"]) not in LAYER_VOCABULARY:
|
||||||
|
_malformed(
|
||||||
|
f"{DECL.name} declares layer {data['layer']!r}, outside §3's closed "
|
||||||
|
f"vocabulary {sorted(LAYER_VOCABULARY)} (case-insensitive)"
|
||||||
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def forms_disagree(governing: object, derived: object) -> bool:
|
||||||
|
"""A11: the derived form must agree with INTENT.md, after the A9 case fold."""
|
||||||
|
return fold(governing) != fold(derived)
|
||||||
|
|
||||||
|
|
||||||
def imported_modules(path: Path) -> set[str]:
|
def imported_modules(path: Path) -> set[str]:
|
||||||
"""Top-level module name of every import in one file."""
|
"""Top-level module name of every import in one file."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -147,6 +231,7 @@ def main() -> int:
|
||||||
parser.add_argument("--report", action="store_true", help="print the declaration summary")
|
parser.add_argument("--report", action="store_true", help="print the declaration summary")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
governing = load_governing_layer()
|
||||||
decl = load_declaration()
|
decl = load_declaration()
|
||||||
declared = {c.get("id") for c in decl.get("tooling_contacts") or []}
|
declared = {c.get("id") for c in decl.get("tooling_contacts") or []}
|
||||||
hits = scan()
|
hits = scan()
|
||||||
|
|
@ -157,7 +242,9 @@ def main() -> int:
|
||||||
checks = decl.get("agent_principal_rule_checks") or {}
|
checks = decl.get("agent_principal_rule_checks") or {}
|
||||||
|
|
||||||
if args.report:
|
if args.report:
|
||||||
print(f"kings-guard — layer {decl['layer']}, standard v{decl['standard_version']}")
|
print(f"kings-guard — layer: {governing} (declared in INTENT.md; §11 governing form)")
|
||||||
|
print(f" layer.yaml: derived from {decl['derived_from']}, layer: {decl['layer']}")
|
||||||
|
print(f" checker validated against: {VALIDATED_AGAINST}")
|
||||||
print(f" tooling contacts declared: {len(declared)}")
|
print(f" tooling contacts declared: {len(declared)}")
|
||||||
print(f" unowned capabilities (§11 blocked-clean): "
|
print(f" unowned capabilities (§11 blocked-clean): "
|
||||||
f"{len(decl.get('unowned_capabilities') or [])}")
|
f"{len(decl.get('unowned_capabilities') or [])}")
|
||||||
|
|
@ -173,6 +260,15 @@ def main() -> int:
|
||||||
form = meta.get("form", "unspecified") if isinstance(meta, dict) else "unspecified"
|
form = meta.get("form", "unspecified") if isinstance(meta, dict) else "unspecified"
|
||||||
print(f" - {name}: {form} (claimed={rules.get(name)})")
|
print(f" - {name}: {form} (claimed={rules.get(name)})")
|
||||||
|
|
||||||
|
if forms_disagree(governing, decl["layer"]):
|
||||||
|
print("", file=sys.stderr)
|
||||||
|
print("FAIL: the two declaration forms disagree (§11, A11)", file=sys.stderr)
|
||||||
|
print(f" INTENT.md (governs): layer: {governing}", file=sys.stderr)
|
||||||
|
print(f" layer.yaml (derived): layer: {decl['layer']}", file=sys.stderr)
|
||||||
|
print(" Case is already folded, so this is a real layer disagreement.", file=sys.stderr)
|
||||||
|
print(" It is a finding in its own right; precedence does not erase it.", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
if undeclared:
|
if undeclared:
|
||||||
print("", file=sys.stderr)
|
print("", file=sys.stderr)
|
||||||
print("FAIL: undeclared Tooling-layer client (§11 undeclared violation)", file=sys.stderr)
|
print("FAIL: undeclared Tooling-layer client (§11 undeclared violation)", file=sys.stderr)
|
||||||
|
|
@ -206,7 +302,8 @@ def main() -> int:
|
||||||
if not args.report:
|
if not args.report:
|
||||||
print(
|
print(
|
||||||
f"OK: no direct Tooling client in {SRC.relative_to(ROOT)}; "
|
f"OK: no direct Tooling client in {SRC.relative_to(ROOT)}; "
|
||||||
"no standing credential (§5, §11, §3.4 rule 1)"
|
"no standing credential (§5, §11, §3.4 rule 1); "
|
||||||
|
f"declaration forms agree; validated against {VALIDATED_AGAINST}"
|
||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,91 @@ def test_declaration_exists_and_declares_staff():
|
||||||
assert DECL.exists(), "no layer.yaml — §11 requires a machine-readable declaration"
|
assert DECL.exists(), "no layer.yaml — §11 requires a machine-readable declaration"
|
||||||
data = yaml.safe_load(DECL.read_text())
|
data = yaml.safe_load(DECL.read_text())
|
||||||
assert data["repository"] == "kings-guard"
|
assert data["repository"] == "kings-guard"
|
||||||
assert data["layer"] == "staff"
|
# Folded, not equal: §3's vocabulary is case-insensitive (GH-DEC-2026-017 §2).
|
||||||
|
# An equality assertion here would perform the re-spelling the ruling declined.
|
||||||
|
assert data["layer"].lower() == "staff"
|
||||||
assert data["framework"] == "netkingdom-security-layer-model"
|
assert data["framework"] == "netkingdom-security-layer-model"
|
||||||
|
|
||||||
|
|
||||||
|
def _load_checker():
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location("check_layer_conformance", SCRIPT)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def _intent_frontmatter() -> dict:
|
||||||
|
lines = (ROOT / "INTENT.md").read_text().splitlines()
|
||||||
|
assert lines[0].strip() == "---"
|
||||||
|
end = lines[1:].index("---") + 1
|
||||||
|
return yaml.safe_load("\n".join(lines[1:end]))
|
||||||
|
|
||||||
|
|
||||||
|
def test_intent_governs_and_the_sidecar_is_marked_derived():
|
||||||
|
"""GH-DEC-2026-017 §1 / A11: INTENT.md governs; layer.yaml is derived from it."""
|
||||||
|
front = _intent_frontmatter()
|
||||||
|
assert front["layer"].lower() == "staff"
|
||||||
|
data = yaml.safe_load(DECL.read_text())
|
||||||
|
assert data["derived"] is True
|
||||||
|
assert data["derived_from"] == "INTENT.md"
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_standard_version_in_either_form():
|
||||||
|
"""GH-DEC-2026-017 §5 / A12: a layer declaration carries no standard version."""
|
||||||
|
assert "standard_version" not in _intent_frontmatter()
|
||||||
|
assert "standard_version" not in yaml.safe_load(DECL.read_text())
|
||||||
|
# The validated-against version moved to the conformance record, not away.
|
||||||
|
assert _load_checker().VALIDATED_AGAINST.startswith(
|
||||||
|
"net-kingdom/canon/standards/security-layer-model"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_two_forms_agree_after_folding_case():
|
||||||
|
"""`Staff` in INTENT.md and `staff` in layer.yaml are one value, not a finding."""
|
||||||
|
module = _load_checker()
|
||||||
|
front = _intent_frontmatter()
|
||||||
|
data = yaml.safe_load(DECL.read_text())
|
||||||
|
assert not module.forms_disagree(front["layer"], data["layer"])
|
||||||
|
assert not module.forms_disagree("Staff", "STAFF")
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_real_layer_disagreement_survives_the_fold():
|
||||||
|
"""The fold must not blind the check: Staff vs Engine is still a finding."""
|
||||||
|
module = _load_checker()
|
||||||
|
assert module.forms_disagree("Staff", "engine")
|
||||||
|
|
||||||
|
|
||||||
|
def test_vocabulary_is_the_four_tokens_including_taxonomy():
|
||||||
|
"""A9: closed at four tokens; a three-token validator carries the defect."""
|
||||||
|
assert _load_checker().LAYER_VOCABULARY == {"taxonomy", "tooling", "engine", "staff"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_checker_rejects_a_declaration_carrying_a_standard_version(tmp_path, monkeypatch):
|
||||||
|
module = _load_checker()
|
||||||
|
decl = tmp_path / "layer.yaml"
|
||||||
|
decl.write_text(DECL.read_text() + '\nstandard_version: "0.7"\n')
|
||||||
|
monkeypatch.setattr(module, "DECL", decl)
|
||||||
|
monkeypatch.setattr(module, "ROOT", tmp_path)
|
||||||
|
with pytest.raises(SystemExit) as exc:
|
||||||
|
module.load_declaration()
|
||||||
|
assert exc.value.code == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_checker_rejects_an_unmarked_sidecar(tmp_path, monkeypatch):
|
||||||
|
module = _load_checker()
|
||||||
|
data = yaml.safe_load(DECL.read_text())
|
||||||
|
data.pop("derived")
|
||||||
|
decl = tmp_path / "layer.yaml"
|
||||||
|
decl.write_text(yaml.safe_dump(data))
|
||||||
|
monkeypatch.setattr(module, "DECL", decl)
|
||||||
|
monkeypatch.setattr(module, "ROOT", tmp_path)
|
||||||
|
with pytest.raises(SystemExit) as exc:
|
||||||
|
module.load_declaration()
|
||||||
|
assert exc.value.code == 2
|
||||||
|
|
||||||
|
|
||||||
def test_no_tooling_contacts_declared():
|
def test_no_tooling_contacts_declared():
|
||||||
"""The blocked-clean position: nothing to declare, because nothing is touched."""
|
"""The blocked-clean position: nothing to declare, because nothing is touched."""
|
||||||
data = yaml.safe_load(DECL.read_text())
|
data = yaml.safe_load(DECL.read_text())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue