diff --git a/layer.yaml b/layer.yaml index 531f305..9fbdbe3 100644 --- a/layer.yaml +++ b/layer.yaml @@ -1,8 +1,18 @@ -# user-engine — NetKingdom security layer declaration +# user-engine — 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). The §3 vocabulary +# is compared after an ASCII case-fold, so `Engine` there and `engine` here are +# the same value and neither is re-spelled (GH-DEC-2026-017 §2). A disagreement +# that survives the fold is a finding in its own right and is reported. +# +# No standard version here or in INTENT.md: a layer declaration MUST NOT carry +# one (GH-DEC-2026-017 §4, amendment A12). Version-scoped state belongs in the +# derived conformance record. +# +# Framework: net-kingdom/canon/standards/security-layer-model # Companion: net-kingdom/SECURITY-COMPANION.md v0.2 -# Declare: INTENT.md (own voice) + this file (§11 machine-readable form) # Validate: python3 scripts/check_layer_conformance.py # # Engine/PIP: same authoritative user-domain inputs yield the same result. @@ -11,7 +21,11 @@ schema_version: "0.1" 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 + repository: user-engine layer: engine role: pip diff --git a/scripts/check_layer_conformance.py b/scripts/check_layer_conformance.py index f7750db..551e155 100644 --- a/scripts/check_layer_conformance.py +++ b/scripts/check_layer_conformance.py @@ -3,6 +3,7 @@ Read-only. user-engine is Engine/PIP and holds no catalogued Tooling client. PostgreSQL is the modeled-concept store, declared under own_store. +INTENT.md frontmatter governs the layer; layer.yaml is its derived form. The failure this exists to catch is a convenience: an OpenBao, Vault, LDAP, or cluster client arriving as one import. That is an undeclared violation. @@ -19,9 +20,10 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" / "user_engine" DECL = ROOT / "layer.yaml" +INTENT = ROOT / "INTENT.md" sys.path.insert(0, str(ROOT / "src")) -from user_engine.layer_yaml import load_mapping # noqa: E402 +from user_engine.layer_yaml import load_mapping, load_mapping_text # noqa: E402 TOOLING_IMPORTS = { "hvac": "OpenBao / Vault client", @@ -43,6 +45,40 @@ OWN_STORE_IMPORTS = { } +# §3's vocabulary is closed at four tokens and compared after an ASCII +# case-fold (GH-DEC-2026-017 §2, §3, amendment A9). Nothing is re-spelled. +LAYER_VOCABULARY = {"taxonomy", "tooling", "engine", "staff"} + + +def fold(value: object) -> str: + """ASCII case-fold only: non-ASCII letters are left untouched.""" + return "".join( + chr(ord(c) + 32) if "A" <= c <= "Z" else c for c in str(value).strip() + ) + + +def load_intent_layer() -> str: + """Return the governing `layer:` value from INTENT.md frontmatter (§11).""" + try: + text = INTENT.read_text() + except OSError as exc: + print(f"FAIL: cannot read {INTENT.name}: {exc}", file=sys.stderr) + raise SystemExit(2) from exc + parts = text.split("---", 2) + if not text.startswith("---") or len(parts) < 3: + print(f"FAIL: {INTENT.name} has no frontmatter (§11)", file=sys.stderr) + raise SystemExit(2) + try: + front = load_mapping_text(parts[1]) + except ValueError as exc: + print(f"FAIL: {INTENT.name} frontmatter is not parseable: {exc}", file=sys.stderr) + raise SystemExit(2) from exc + if "layer" not in front: + print(f"FAIL: {INTENT.name} frontmatter has no 'layer:' key (§11)", file=sys.stderr) + raise SystemExit(2) + return str(front["layer"]) + + def load_declaration() -> dict: if not DECL.exists(): print(f"FAIL: no declaration at {DECL.relative_to(ROOT)} (§11)", file=sys.stderr) @@ -52,13 +88,46 @@ def load_declaration() -> dict: except (ValueError, OSError) as exc: print(f"FAIL: {DECL.name} is not parseable: {exc}", file=sys.stderr) raise SystemExit(2) from exc - for key in ("layer", "role", "repository", "tooling_contacts", "standard_version"): + # No standard_version: a layer declaration MUST NOT carry one + # (GH-DEC-2026-017 §4, amendment A12). Its presence is malformed. + for key in ("layer", "role", "repository", "tooling_contacts", "derived", "derived_from"): if key not in data: print(f"FAIL: {DECL.name} missing required key '{key}' (§11)", file=sys.stderr) raise SystemExit(2) - if data["layer"] != "engine": + if "standard_version" in data: print( - f"FAIL: declared layer is {data['layer']!r}, expected 'engine'", + f"FAIL: {DECL.name} carries standard_version; a layer declaration " + "MUST NOT carry a standard version (GH-DEC-2026-017, A12)", + file=sys.stderr, + ) + raise SystemExit(2) + if data["derived"] is not True or data["derived_from"] != "INTENT.md": + print( + f"FAIL: {DECL.name} must be marked derived: true, derived_from: INTENT.md " + "(§11 derived-artifact rule, GH-DEC-2026-017, A11)", + file=sys.stderr, + ) + raise SystemExit(2) + if fold(data["layer"]) not in LAYER_VOCABULARY: + print( + f"FAIL: declared layer {data['layer']!r} is not a §3 token " + f"({', '.join(sorted(LAYER_VOCABULARY))}, case-insensitive)", + file=sys.stderr, + ) + raise SystemExit(2) + intent_layer = load_intent_layer() + if fold(intent_layer) != fold(data["layer"]): + # A finding in its own right; INTENT.md governs but the disagreement + # is reported, not resolved away by precedence (A11). + print( + f"FAIL: INTENT.md declares layer {intent_layer!r} but {DECL.name} " + f"declares {data['layer']!r}; they disagree after case-folding", + file=sys.stderr, + ) + raise SystemExit(2) + if fold(intent_layer) != "engine": + print( + f"FAIL: declared layer is {intent_layer!r}, expected Engine", file=sys.stderr, ) raise SystemExit(2) @@ -120,8 +189,8 @@ def main() -> int: if args.report: print( - f"user-engine — layer {decl['layer']}/{decl['role']}, " - f"standard v{decl['standard_version']}" + f"user-engine — layer {decl['layer']}/{decl['role']} " + f"(derived from {decl['derived_from']})" ) print(" tooling contacts declared: 0") print(f" own-store declarations: {len(own_store)}") diff --git a/tests/test_layer_conformance.py b/tests/test_layer_conformance.py index 266066f..3c1233d 100644 --- a/tests/test_layer_conformance.py +++ b/tests/test_layer_conformance.py @@ -32,7 +32,7 @@ class LayerDeclarationTests(unittest.TestCase): def test_declares_engine_pip_in_own_voice(self): data = load_mapping(LAYER) self.assertEqual(data["repository"], "user-engine") - self.assertEqual(data["layer"], "engine") + self.assertEqual(_fold(data["layer"]), "engine") self.assertEqual(data["role"], "pip") self.assertEqual(data["tooling_contacts"], []) self.assertEqual(data["pep_stance"], "pep-stance.yaml") @@ -44,8 +44,50 @@ class LayerDeclarationTests(unittest.TestCase): front = text.split("---", 2)[1] intent = load_mapping_text(front) decl = load_mapping(LAYER) - self.assertEqual(intent["layer"].lower(), decl["layer"]) - self.assertEqual(intent["role"].lower(), decl["role"]) + # Assert the fold, not equality: neither form is re-spelled, and a + # real layer divergence still fails (GH-DEC-2026-017 §2). + self.assertEqual(_fold(intent["layer"]), _fold(decl["layer"])) + self.assertEqual(_fold(intent["role"]), _fold(decl["role"])) + + def test_sidecar_is_marked_derived_from_intent(self): + decl = load_mapping(LAYER) + self.assertIs(decl["derived"], True) + self.assertEqual(decl["derived_from"], "INTENT.md") + + def test_declaration_carries_no_standard_version(self): + decl = load_mapping(LAYER) + front = load_mapping_text((ROOT / "INTENT.md").read_text().split("---", 2)[1]) + self.assertNotIn("standard_version", decl) + self.assertNotIn("standard_version", front) + + def test_vocabulary_is_four_tokens_compared_after_fold(self): + module = _checker() + self.assertEqual( + module.LAYER_VOCABULARY, {"taxonomy", "tooling", "engine", "staff"} + ) + for token in ("Taxonomy", "TOOLING", "engine", "Staff"): + self.assertIn(module.fold(token), module.LAYER_VOCABULARY) + self.assertNotIn(module.fold("surface"), module.LAYER_VOCABULARY) + + def test_checker_rejects_standard_version_and_divergence(self): + module = _checker() + good = load_mapping(LAYER) + cases = { + "standard_version": dict(good, standard_version="0.7"), + "not derived": dict(good, derived=False), + "divergent layer": dict(good, layer="staff"), + } + for label, data in cases.items(): + with self.subTest(label), patch.object( + module, "load_mapping", return_value=data + ), patch("sys.stderr"): + with self.assertRaises(SystemExit) as caught: + module.load_declaration() + self.assertEqual(caught.exception.code, 2) + with patch.object( + module, "load_mapping", return_value=dict(good, layer="ENGINE") + ): + self.assertEqual(module.load_declaration()["layer"], "ENGINE") def test_own_store_is_declared(self): data = load_mapping(LAYER) @@ -163,6 +205,17 @@ class PepStanceTests(unittest.TestCase): LocalAuthorizationCheckPort() +def _fold(value): + return "".join(chr(ord(c) + 32) if "A" <= c <= "Z" else c for c in str(value)) + + +def _checker(): + 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 _request(): from user_engine.domain import Actor, AuthorizationRequest, PrincipalType