Apply GH-DEC-2026-017 to the layer declaration and its checker.
layer.yaml drops standard_version (A12) and is marked derived: true, derived_from: INTENT.md (A11); INTENT.md frontmatter already carries the governing layer: Engine and no standard_version. The checker changes in the same commit: standard_version is no longer required and its presence is now malformed, the derived marking is required, the layer is compared against the closed four-token vocabulary after an ASCII fold (A9), and a divergence between INTENT.md and layer.yaml that survives the fold is reported. Nothing is re-spelled: Engine and engine both stand. The tests assert the fold rather than equality. pep-stance.yaml is untouched. 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
d2ddadccd9
commit
2ac4a36bdd
3 changed files with 149 additions and 13 deletions
22
layer.yaml
22
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
|
# 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
|
# Validate: python3 scripts/check_layer_conformance.py
|
||||||
#
|
#
|
||||||
# Engine/PIP: same authoritative user-domain inputs yield the same result.
|
# Engine/PIP: same authoritative user-domain inputs yield the same result.
|
||||||
|
|
@ -11,7 +21,11 @@
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
repository: user-engine
|
repository: user-engine
|
||||||
layer: engine
|
layer: engine
|
||||||
role: pip
|
role: pip
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
Read-only. user-engine is Engine/PIP and holds no catalogued Tooling client.
|
Read-only. user-engine is Engine/PIP and holds no catalogued Tooling client.
|
||||||
PostgreSQL is the modeled-concept store, declared under own_store.
|
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,
|
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.
|
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]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
SRC = ROOT / "src" / "user_engine"
|
SRC = ROOT / "src" / "user_engine"
|
||||||
DECL = ROOT / "layer.yaml"
|
DECL = ROOT / "layer.yaml"
|
||||||
|
INTENT = ROOT / "INTENT.md"
|
||||||
sys.path.insert(0, str(ROOT / "src"))
|
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 = {
|
TOOLING_IMPORTS = {
|
||||||
"hvac": "OpenBao / Vault client",
|
"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:
|
def load_declaration() -> dict:
|
||||||
if not DECL.exists():
|
if not DECL.exists():
|
||||||
print(f"FAIL: no declaration at {DECL.relative_to(ROOT)} (§11)", file=sys.stderr)
|
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:
|
except (ValueError, OSError) 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", "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:
|
if key not in data:
|
||||||
print(f"FAIL: {DECL.name} missing required key '{key}' (§11)", file=sys.stderr)
|
print(f"FAIL: {DECL.name} missing required key '{key}' (§11)", file=sys.stderr)
|
||||||
raise SystemExit(2)
|
raise SystemExit(2)
|
||||||
if data["layer"] != "engine":
|
if "standard_version" in data:
|
||||||
print(
|
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,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
raise SystemExit(2)
|
raise SystemExit(2)
|
||||||
|
|
@ -120,8 +189,8 @@ def main() -> int:
|
||||||
|
|
||||||
if args.report:
|
if args.report:
|
||||||
print(
|
print(
|
||||||
f"user-engine — layer {decl['layer']}/{decl['role']}, "
|
f"user-engine — layer {decl['layer']}/{decl['role']} "
|
||||||
f"standard v{decl['standard_version']}"
|
f"(derived from {decl['derived_from']})"
|
||||||
)
|
)
|
||||||
print(" tooling contacts declared: 0")
|
print(" tooling contacts declared: 0")
|
||||||
print(f" own-store declarations: {len(own_store)}")
|
print(f" own-store declarations: {len(own_store)}")
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class LayerDeclarationTests(unittest.TestCase):
|
||||||
def test_declares_engine_pip_in_own_voice(self):
|
def test_declares_engine_pip_in_own_voice(self):
|
||||||
data = load_mapping(LAYER)
|
data = load_mapping(LAYER)
|
||||||
self.assertEqual(data["repository"], "user-engine")
|
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["role"], "pip")
|
||||||
self.assertEqual(data["tooling_contacts"], [])
|
self.assertEqual(data["tooling_contacts"], [])
|
||||||
self.assertEqual(data["pep_stance"], "pep-stance.yaml")
|
self.assertEqual(data["pep_stance"], "pep-stance.yaml")
|
||||||
|
|
@ -44,8 +44,50 @@ class LayerDeclarationTests(unittest.TestCase):
|
||||||
front = text.split("---", 2)[1]
|
front = text.split("---", 2)[1]
|
||||||
intent = load_mapping_text(front)
|
intent = load_mapping_text(front)
|
||||||
decl = load_mapping(LAYER)
|
decl = load_mapping(LAYER)
|
||||||
self.assertEqual(intent["layer"].lower(), decl["layer"])
|
# Assert the fold, not equality: neither form is re-spelled, and a
|
||||||
self.assertEqual(intent["role"].lower(), decl["role"])
|
# 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):
|
def test_own_store_is_declared(self):
|
||||||
data = load_mapping(LAYER)
|
data = load_mapping(LAYER)
|
||||||
|
|
@ -163,6 +205,17 @@ class PepStanceTests(unittest.TestCase):
|
||||||
LocalAuthorizationCheckPort()
|
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():
|
def _request():
|
||||||
from user_engine.domain import Actor, AuthorizationRequest, PrincipalType
|
from user_engine.domain import Actor, AuthorizationRequest, PrincipalType
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue