diff --git a/INTENT.md b/INTENT.md index 62a3db9..e7b7c9b 100644 --- a/INTENT.md +++ b/INTENT.md @@ -19,7 +19,7 @@ declared_at: "2026-08-29" > **Layer: Engine. Role: Lifecycle.** This is this repository's own declaration > under the accepted NetKingdom Security Layer Model -> (`net-kingdom/canon/standards/security-layer-model_v0.7.md`) and its working +> (`net-kingdom/canon/standards/security-layer-model_v0.8.md`) and its working > companion (`net-kingdom/SECURITY-COMPANION.md`). The machine-readable form > required by §11 is `layer.yaml`. A layer stated about this repository by > another repository is not a declaration; this file, in this repository's 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..7601038 --- /dev/null +++ b/docs/evidence/2026-10-11-security-layer-v08-migration.json @@ -0,0 +1,61 @@ +{ + "owner": "secrets-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": 40, + "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": "validated against: net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)\nscope: single repository secrets-engine (catalogued in \u00a74); declaration = INTENT.md frontmatter + layer.yaml (every key and value); source = src/secrets_engine/**/*.py; not reached by A12: pep-stance.yaml, evidence-classification.yaml\nPASS \u2014 Engine/Lifecycle declaration present, no decision surface, 1 OpenBao adapter module(s) owned; validated against net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b).\n", + "stderr": "" + }, + { + "argv": [ + "--report" + ], + "returncode": 0, + "stdout": "validated against: net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)\nscope: single repository secrets-engine (catalogued in \u00a74); declaration = INTENT.md frontmatter + layer.yaml (every key and value); source = src/secrets_engine/**/*.py; not reached by A12: pep-stance.yaml, evidence-classification.yaml\nsecrets-engine \u2014 layer: Engine role: Lifecycle (declared in INTENT.md; \u00a711 governing form)\nlayer.yaml: derived from INTENT.md, layer: engine\ndeclared by INTENT.md\npep stance: pep-stance.yaml\nowned OpenBao modules: ['openbao.py']\nOpenBao argv adapters found: ['openbao.py']\n\nPASS \u2014 declaration, owned tooling, and PEP stance path hold; validated against net-kingdom/canon/standards/security-layer-model_v0.8.md (accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b).\n", + "stderr": "" + } + ], + "meaningful_regressions": { + "unicode_token_original_fold": "staff", + "original_accepted_non_ascii_token": true + }, + "source_changes": [ + { + "repo": "secrets-engine", + "path": "scripts/check_layer_conformance.py", + "staged_path": "/tmp/layer-migration-stage/secrets-engine/scripts/check_layer_conformance.py", + "original_sha256": "9c5eaf2ee3b0caddc01bf3a78add43123e61318145fc35d83e45f6ead3f5d586", + "staged_sha256": "990ef566b336509cf5c7cfe75c00ce37d091396a27e1e78b7ce77aad920e8de5" + }, + { + "repo": "secrets-engine", + "path": "tests/test_layer_conformance.py", + "staged_path": "/tmp/layer-migration-stage/secrets-engine/tests/test_layer_conformance.py", + "original_sha256": "1e1f04ec54223dc2090917610b3490ad481aa7bf3e397dba107bdd080f83cda5", + "staged_sha256": "c91f69d27189d012fbf2622e92d5933793aed79647db9e14be7885f2261ecbd0" + }, + { + "repo": "secrets-engine", + "path": "INTENT.md", + "staged_path": "/tmp/layer-migration-stage/secrets-engine/INTENT.md", + "original_sha256": "0f65fdd03f3197dfdf1c77051adb64da7da5998a6f64d9e88ae0868939e6b358", + "staged_sha256": "188dc6d99ca9a441e7389a3715ba48275ab6e8745deaff686bd27d2aac34d678" + } + ], + "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 ffaa525..752a45f 100644 --- a/scripts/check_layer_conformance.py +++ b/scripts/check_layer_conformance.py @@ -60,14 +60,16 @@ DECISION_SURFACE = re.compile( LAYER_VOCABULARY = {"taxonomy", "tooling", "engine", "staff"} EXPECTED_LAYER = "engine" -# The accepted text and rulings enforced by this run (GH-DEC-2026-021 §2). -# Update together with the reference detector when the accepted text changes. +# 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.7.md (net-kingdom@66dc491) " - "as amended by GH-DEC-2026-017, GH-DEC-2026-020 and GH-DEC-2026-021 " - "(A9-A13, A12 r3; gate-house@39d9287)" + "net-kingdom/canon/standards/security-layer-model_v0.8.md " + "(accepted; sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)" ) + SCOPE = ( + "single repository secrets-engine (catalogued in §4); " "declaration = INTENT.md frontmatter + layer.yaml (every key and value); " "source = src/secrets_engine/**/*.py; " "not reached by A12: pep-stance.yaml, evidence-classification.yaml" @@ -109,7 +111,8 @@ def find_version_pins(node, where: str = "", identity: bool = False) -> list[str def _fold(value: object) -> str: """ASCII case-fold, per §3 as amended: two spellings of a token are one token.""" - return str(value).strip().encode("ascii", "ignore").decode().lower() + return str(value).strip().translate(str.maketrans( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")) def _no_standard_version(where: str, data: dict) -> None: diff --git a/tests/test_layer_conformance.py b/tests/test_layer_conformance.py index 25ff306..a3ea3e6 100644 --- a/tests/test_layer_conformance.py +++ b/tests/test_layer_conformance.py @@ -1,4 +1,4 @@ -"""Layer-model conformance (security-layer-model_v0.7 §3.3, §6, §11). +"""Layer-model conformance (security-layer-model_v0.8 §3.3, §6, §11). The declaration must be this repository's own, machine-readable, and equal to the shipped production fail-closed gate. A published stance map that may drift @@ -237,7 +237,7 @@ def test_every_run_states_version_and_scope(): ) assert result.returncode == 0, result.stdout + result.stderr assert f"validated against: {checker.VALIDATED_AGAINST}" in result.stdout - assert "scope: declaration = INTENT.md frontmatter + layer.yaml" in result.stdout + assert "scope: single repository secrets-engine (catalogued in §4); declaration = INTENT.md frontmatter + layer.yaml" in result.stdout pass_line = [l for l in result.stdout.splitlines() if l.startswith("PASS")] assert pass_line and checker.VALIDATED_AGAINST in pass_line[0] @@ -380,3 +380,35 @@ def test_proposed_contracts_exist_and_forbid_secret_material(): assert "secret values" in secret_use.lower() or "secret value" in secret_use.lower() assert "audit-core" in secret_use assert "completeness is not claimed" in secret_use.lower() + + +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 secrets-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