diff --git a/INTENT.md b/INTENT.md index 3424cf4..dad5752 100644 --- a/INTENT.md +++ b/INTENT.md @@ -15,10 +15,10 @@ companion: net-kingdom/SECURITY-COMPANION.md > current implementation. See `SCOPE.md` for what is implemented today. > **Layer: Engine. Role: PIP.** This is the declaration required by -> NetKingdom Security Layer Model v0.7 §11 and the working companion v0.2. +> NetKingdom Security Layer Model v0.8 §11 and the working companion v0.3. > It is this repository's own voice, not a catalog row or a review note > written about us. The statute is -> `net-kingdom/canon/standards/security-layer-model_v0.7.md`; the companion +> `net-kingdom/canon/standards/security-layer-model_v0.8.md`; the companion > is `net-kingdom/SECURITY-COMPANION.md`. On disagreement the statute > governs, and a disagreement is a finding for `gate-house`. > @@ -251,7 +251,7 @@ the three drift: the statute wins, then the contract, then this file. - `docs/tenant-guardrail-policy.md` — ceiling contract - `docs/tenant-lifecycle-api.md` — update, retire, reactivate - `net-kingdom/SECURITY-COMPANION.md` — start here for the layer rules -- `net-kingdom/canon/standards/security-layer-model_v0.7.md` — the statute +- `net-kingdom/canon/standards/security-layer-model_v0.8.md` — the statute - `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` - `net-kingdom/canon/standards/iam-profile_v0.3.md` — the `tenant_roles` claim and carrying mechanism this service implements diff --git a/docs/evidence/2026-10-11-security-layer-v08-migration.json b/docs/evidence/2026-10-11-security-layer-v08-migration.json new file mode 100644 index 0000000..d17b3a9 --- /dev/null +++ b/docs/evidence/2026-10-11-security-layer-v08-migration.json @@ -0,0 +1,62 @@ +{ + "owner": "tenant-engine", + "accepted_standard_sha256": "3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b", + "accepted_source": "net-kingdom/canon/standards/security-layer-model_v0.8.md", + "acceptance_receipt": "net-kingdom/docs/security-layer-v0.8-acceptance.md", + "tests": { + "layer_conformance_passed": 16, + "source": "staged modified scripts/tests + read-only current source support", + "limitations": "mechanical declared scope only, not complete estate/runtime conformance" + }, + "checker_runs": [ + { + "argv": [], + "returncode": 0, + "stdout": "checking against: net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)\nscope: single repository tenant-engine (catalogued in \u00a74); declaration = INTENT.md frontmatter + layer.yaml (every key and value); tooling scan = src/tenant_engine/**/*.py; not reached: pep-stance.yaml, pip-claims.yaml, comments, schema_version\nOK: Engine/PIP declaration matches INTENT.md; no Tooling client in src/tenant_engine; no version in the declaration; validated against net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)\n", + "stderr": "" + }, + { + "argv": [ + "--report" + ], + "returncode": 0, + "stdout": "checking against: net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)\nscope: single repository tenant-engine (catalogued in \u00a74); declaration = INTENT.md frontmatter + layer.yaml (every key and value); tooling scan = src/tenant_engine/**/*.py; not reached: pep-stance.yaml, pip-claims.yaml, comments, schema_version\ntenant-engine \u2014 layer Engine, role PIP (INTENT.md governs; layer.yaml derived from INTENT.md)\n tooling contacts declared: 0\n non-tooling clients: 5\n", + "stderr": "" + } + ], + "meaningful_regressions": { + "unicode_token_original_fold": "st\u03b1aff", + "original_accepted_non_ascii_token": false, + "original_nested_standard_sequence_findings": [] + }, + "source_changes": [ + { + "repo": "tenant-engine", + "path": "scripts/check_layer_conformance.py", + "staged_path": "/tmp/layer-migration-stage/tenant-engine/scripts/check_layer_conformance.py", + "original_sha256": "fe9d5f6d47cebb789eee554192873de0ca3b7036ef5b5e6706a62757e15c6822", + "staged_sha256": "be73ae48b44fcf09115d6a3897bbd69f71bcebc54f2dce14afb307e3c1e67b5f" + }, + { + "repo": "tenant-engine", + "path": "tests/test_layer_conformance.py", + "staged_path": "/tmp/layer-migration-stage/tenant-engine/tests/test_layer_conformance.py", + "original_sha256": "f7b25dd57d933b652e9e741baeb14a0823766ae9b32932ac0b388ef859ae5394", + "staged_sha256": "19a6fd4895a4efaebd1825c189fdb1274a3b5c0d75d056e6a0f24d9e4d715ab2" + }, + { + "repo": "tenant-engine", + "path": "INTENT.md", + "staged_path": "/tmp/layer-migration-stage/tenant-engine/INTENT.md", + "original_sha256": "97075d9a53b568d5bbeab3c7bed1efe1dfb9833eb93df87c60e18335105b9ce2", + "staged_sha256": "c2deaef01f7492ca1b39f52e36bf703f6a55b04158b2888b7befae73452ce285" + } + ], + "knowledge_orientation": { + "package_version": "0.2.6", + "package_digest": "8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02", + "start_status": "withheld due changed sources; source reviewed directly" + }, + "native_runtime_changed": false, + "pushed": false +} diff --git a/scripts/check_layer_conformance.py b/scripts/check_layer_conformance.py index 8fa96bc..951cde4 100644 --- a/scripts/check_layer_conformance.py +++ b/scripts/check_layer_conformance.py @@ -51,26 +51,52 @@ SRC = ROOT / "src" / "tenant_engine" DECL = ROOT / "layer.yaml" INTENT = ROOT / "INTENT.md" -# The text in force this checker enforces (GH-DEC-2026-020 §4, GH-DEC-2026-021 -# §2): the ACCEPTED v0.7 plus the gate-house decisions enforced beyond it. v0.8 -# is held under GH-DEC-2026-019 and does not govern, so it is not named here. -# Re-point after the flip (GH-WP-0004-T11). -VALIDATED_AGAINST = ("net-kingdom/canon/standards/security-layer-model_v0.7.md " - "(net-kingdom@66dc491) as amended by GH-DEC-2026-017, " - "GH-DEC-2026-020, GH-DEC-2026-021 (gate-house@39d9287)") -SCOPE = ("declaration = INTENT.md frontmatter + layer.yaml (every key and value); " +# Accepted v0.8 publication; version provenance belongs to this run, never +# the standing layer declaration (§11). Exact publisher artifact is retained +# in net-kingdom/docs/security-layer-v0.8-acceptance.md. +VALIDATED_AGAINST = ( + "net-kingdom/canon/standards/security-layer-model_v0.8.md " + "(accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)" +) + +SCOPE = ("single repository tenant-engine (catalogued in §4); " + "declaration = INTENT.md frontmatter + layer.yaml (every key and value); " "tooling scan = src/tenant_engine/**/*.py; " "not reached: pep-stance.yaml, pip-claims.yaml, comments, schema_version") -# A12 pin detector, converged on ops-warden's estate reference (GH-DEC-2026-021 -# §3). The path pattern deliberately flags a versioned path of any document. -_VERSION_KEY = re.compile(r"(standard|companion).*version|version.*(standard|companion)", - re.IGNORECASE) -_VERSION_IN_VALUE = re.compile(r"[_\-.]v\d+(\.\d+)*(\.md)?\b|@v?\d+\.\d+", re.IGNORECASE) -_EXEMPT_KEYS = {"schema_version"} # the declaration file's own schema, not reached -# 021 §3 addition: an identity-bearing standard:/companion: value carries no version. -_IDENTITY_KEYS = {"standard", "companion"} -_ANY_VERSION = re.compile(r"v?\d+\.\d+", re.IGNORECASE) +# Declaration pin/provenance semantics are the ops-warden estate reference. +VERSION_KEY = re.compile(r"(standard|companion).*version|version.*(standard|companion)", re.I) +VERSION_IN_VALUE = re.compile(r"[_\-.]v\d+(\.\d+)*(\.md)?\b|@v?\d+\.\d+", re.I) +NOT_REACHED_KEYS = {"schema_version"} +IDENTITY_KEYS = {"standard", "companion"} +IDENTITY_VERSION = re.compile(r"\bv?\d+\.\d+", re.I) + + +def find_version_pins(node, where: str = "", identity: bool = False) -> list[str]: + """Every place in a parsed declaration that carries a standard/companion version. + + Walks every key and value (comments are gone after parsing, which is the + A12 r2 exclusion). Returns human-readable locations; empty means clean. + """ + pins: list[str] = [] + if isinstance(node, dict): + for k, v in node.items(): + here = f"{where}.{k}" if where else str(k) + if str(k) in NOT_REACHED_KEYS: + continue + if VERSION_KEY.search(str(k)): + pins.append(f"{here} (key names a standard/companion version)") + continue + pins.extend(find_version_pins(v, here, str(k).lower() in IDENTITY_KEYS)) + elif isinstance(node, list): + for i, v in enumerate(node): + pins.extend(find_version_pins(v, f"{where}[{i}]", identity)) + elif isinstance(node, str) and VERSION_IN_VALUE.search(node): + pins.append(f"{where} = {node!r} (value carries a version)") + elif isinstance(node, str) and identity and IDENTITY_VERSION.search(node): + pins.append(f"{where} = {node!r} (identity-bearing value carries a version)") + return pins + # Catalogued Tooling in statute §4 today: key-cape and OpenBao. # Import roots that would constitute a direct client of those. @@ -91,37 +117,13 @@ LAYER_VOCABULARY = {"taxonomy", "tooling", "engine", "staff"} def _fold(value: object) -> str: """ASCII case-fold only; two spellings of a token are one token.""" - return str(value).translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZ", + return str(value).strip().translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")) def version_findings(data: object, where: str) -> list[str]: - """Every pin in a parsed declaration (A12, GH-DEC-2026-021 §1, §3).""" - found: list[str] = [] - - def walk(node: object, path: str) -> None: - if isinstance(node, dict): - for key, value in node.items(): - sub = f"{path}.{key}" if path else str(key) - if str(key) in _EXEMPT_KEYS: - continue - if _VERSION_KEY.search(str(key)): - found.append(f"{where}: key '{sub}' is a version key") - continue - if (_fold(key) in _IDENTITY_KEYS and isinstance(value, str) - and _ANY_VERSION.search(value)): - found.append(f"{where}: '{sub}' carries a version: {value.strip()[:80]!r}") - continue - walk(value, sub) - elif isinstance(node, list): - for i, item in enumerate(node): - walk(item, f"{path}[{i}]") - elif isinstance(node, str): - if _VERSION_IN_VALUE.search(node): - found.append(f"{where}: '{path}' carries a version: {node.strip()[:80]!r}") - - walk(data, "") - return found + """Owner-compatible diagnostics over the estate reference declaration walk.""" + return [f"{where}: {finding}" for finding in find_version_pins(data)] def _reject_versions(data: object, where: str) -> None: diff --git a/tests/test_layer_conformance.py b/tests/test_layer_conformance.py index ff7648b..322fad6 100644 --- a/tests/test_layer_conformance.py +++ b/tests/test_layer_conformance.py @@ -142,7 +142,7 @@ def test_a_failing_run_still_states_version_and_scope(tmp_path, monkeypatch, cap raise AssertionError("a versioned standard: path must fail") out = capsys.readouterr() assert checker.VALIDATED_AGAINST in out.out and "scope:" in out.out - assert "carries a version" in out.err + assert "value carries a version" in out.err def test_checker_rejects_a_divergence_that_survives_the_fold(tmp_path, monkeypatch): @@ -185,3 +185,35 @@ def test_published_stance_equals_shipped_behaviour(): assert published_stance() == shipped_stance() assert set(shipped_stance()) == {"unset", "unreachable", "non_allow", "unknown"} assert set(shipped_stance().values()) == {"fail_closed"} + + +def test_accepted_v08_run_provenance_and_catalog_scope(): + checker = _checker() + assert "security-layer-model_v0.8.md" in checker.VALIDATED_AGAINST + assert "sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b" in checker.VALIDATED_AGAINST + assert "single repository tenant-engine (catalogued in §4)" in checker.SCOPE + + +def test_reference_semantics_propagate_identity_through_nested_sequences(): + checker = _checker() + for data in ( + {"nested": {"standard": ["Security Layer Model v0.8"]}}, + {"companion": [["Security Companion 0.3"]]}, + {"standard": {"alternate": ["Security Layer Model v0.7"]}}, + ): + # A dictionary begins a fresh identity key; only sequence descendants + # inherit the immediate standard:/companion: identity in the reference. + if isinstance(data.get("standard"), dict): + assert checker.find_version_pins(data) == [] + else: + assert checker.find_version_pins(data), data + assert checker.find_version_pins({"rationale": "Assented at v0.7; migrated at v0.8."}) == [] + assert checker.find_version_pins({"intent_version": "0.2.0", "schema_version": "0.1"}) == [] + + +def test_ascii_folding_never_discards_non_ascii_letters(): + checker = _checker() + assert checker._fold("Staff") == "staff" + assert checker._fold("Engine") == "engine" + # Deleting non-ASCII bytes would turn this fifth token into Staff. + assert checker._fold("Stαaff") not in checker.LAYER_VOCABULARY