diff --git a/INTENT.md b/INTENT.md index 3522a93..57e608c 100644 --- a/INTENT.md +++ b/INTENT.md @@ -1,11 +1,20 @@ --- +# 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 role: null # Engines only: PDP | PIP | Evidence | Lifecycle pep_shaped: true # §6.4 — issuing a certificate is a protected side effect -standard: net-kingdom/canon/standards/security-layer-model_v0.7.md -standard_version: "0.7" +# No `standard_version` here or in the sidecar: GH-DEC-2026-017 §5 / A12 — +# the declared layer is a standing property that does not change when the +# standard is revised, so a version in the declaration reads as a validity +# condition it is not. Version-scoped state belongs in the derived conformance +# record. The version ops-warden assented at is recorded with the assent +# (ADR-0010), which is where a version-at-a-boundary belongs. +standard: net-kingdom/canon/standards/security-layer-model companion: net-kingdom/SECURITY-COMPANION.md -declaration: layer.yaml +declaration: layer.yaml # derived form; this frontmatter governs pep_stance: pep-stance.yaml assent: docs/adr/ADR-0010 --- diff --git a/SCOPE.md b/SCOPE.md index fb9ed06..edadb36 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -93,7 +93,7 @@ cited in the standard as the estate's reference forms: | Artifact | Declares | Status | | --- | --- | --- | -| `layer.yaml` | 5 Tooling contacts mapped to §5.1/§5.2/§5.3 shapes + non-Tooling clients so the check is total | shipped; named reference form (§11) | +| `layer.yaml` | 5 Tooling contacts mapped to §5.1/§5.2/§5.3 shapes + non-Tooling clients so the check is total; **derived** from `INTENT.md`, which governs (`GH-DEC-2026-017`) | shipped; named reference form (§11) | | `pep-stance.yaml` | unreachable-engine stance map, total per zone | shipped; registered in statute §13.1 (§6.4 obl. 3) | | `scripts/check_layer_conformance.py` | every direct Tooling client maps to a declared shape | shipped; CI-enforced | | `tests/test_layer_conformance.py` | the §5.2 no-authority property, and published stance map **equals** shipped default | shipped, 11 tests | @@ -511,7 +511,7 @@ keywords: [access, credential, secret, npm, token, api-key, openbao, key-cape, l | `wiki/WorkloadSecurityPosture.md` | Secret-store posture, workload maturity, and blocker triage | | `registry/routing/catalog.yaml` | Machine-readable routing pointer catalog | | `net-kingdom/SECURITY-COMPANION.md` | **The estate's operative security rules — start here** | -| `layer.yaml` | Layer declaration: every Tooling contact and its §5 shape | +| `layer.yaml` | Derived layer declaration: every Tooling contact and its §5 shape (`INTENT.md` frontmatter governs) | | `pep-stance.yaml` | Unreachable-engine stance map (§6.4); equals shipped behaviour by test | | `tenancy.yaml` | Declared tenancy posture (`I1 A1 E0 P n/a R n/a V0`) and why each axis sits where it does | | `wiki/NetKingdomSecurityMap.md` | Platform security component map | diff --git a/docs/layer-declaration-precedence.md b/docs/layer-declaration-precedence.md index 0f7a343..2f1f199 100644 --- a/docs/layer-declaration-precedence.md +++ b/docs/layer-declaration-precedence.md @@ -1,7 +1,12 @@ -# Which of ops-warden's two layer declarations governs — unruled, and not ours to rule +# Which of ops-warden's two layer declarations governs — ruled, and applied -**Status: open, waiting on gate-house. ops-warden will not change either file -until the ruling lands.** Route: `WARDEN-WP-0034-T06`. +**Status: RULED and APPLIED, 2026-09-21.** `GH-DEC-2026-017` (`gate-house@def0af2`) +settles it: `INTENT.md` governs, the sidecar is derived, and the §3 vocabulary is +case-insensitive — so the two values were never in disagreement about a layer. +Route: `WARDEN-WP-0034-T06`, done. **Sections 1–3 below are the position as it +stood while the question was open, kept because the ruling is only legible +against the question it answers.** What changed, and what is still open, is +[further down](#the-ruling-landed--2026-09-21--and-it-is-applied). ## The fact @@ -63,10 +68,10 @@ settled either. other file to match in one commit, together with a regression test asserting the two agree — so the next divergence is caught rather than surveyed. -## The ruling landed — 2026-09-21, and it is NOT YET APPLIED +## The ruling landed — 2026-09-21 — and it is APPLIED `GH-DEC-2026-017` (gate-house `def0af2`, message `3715e247`, amendments A9–A13) -arrived hours after this page was written. It answers both questions and asks +arrived hours after this page was written. It answers both questions and asked ops-warden for two changes, because `layer.yaml` is the estate's reference form and seven repositories copied it. @@ -74,42 +79,80 @@ and seven repositories copied it. `layer:` key is the declaration and the file is a **derived** artifact — marked derived, naming `INTENT.md` as its source, required to agree with it. - **Casing: ruled away, not ruled on.** §3's vocabulary is case-insensitive for - comparison; a conformance run **must fold case**, and **nobody re-spells + comparison, a conformance run **must fold case**, and **nobody re-spells anything**. `Staff` and `staff` agree. The plan in point 4 above — align the - files in one commit — is therefore moot rather than completed. + files in one commit — is therefore **moot rather than completed**, and that + distinction is the point: the divergence was never a disagreement about a + layer, so there was never anything to align. - **A disagreement between the two forms is still a finding** and must be reported rather than resolved away, or precedence makes eight of the nine invisible instead of settling them. -- **Ask 1:** remove `standard_version: "0.7"` from `layer.yaml`. A layer is a - standing property; a version here makes every revision of the standard read as - though it invalidated every declaration. Version-scoped state belongs in the - derived conformance record. -- **Ask 2:** mark the sidecar derived and name `INTENT.md` as its source. - `INTENT.md` already carries `layer: Staff`, so nothing needs adding there. -**Neither ask has been applied, and that is deliberate.** Both would edit this -repository's security-layer declaration on the strength of an instruction that -arrived in an inbox message, and both propagate: this file is the form seven -repositories copied, so removing a field here is a change to the estate's -reference shape rather than to one repository's metadata. A session that applies -it should be mandated to, should verify `GH-DEC-2026-017` at -`gate-house@def0af2` and the A9–A13 amendments in the repository rather than -from the message text, and should land it as one commit carrying: +### What was applied, 2026-09-21 -1. `standard_version` removed from `layer.yaml` (and from - `scripts/check_layer_conformance.py`'s required-key list and report line). -2. `derived: true` and `derived_from: INTENT.md` added. -3. A regression test asserting the sidecar's `layer` equals `INTENT.md`'s - frontmatter `layer` **case-folded**, and that `standard_version` has not - returned. -4. `pep-stance.yaml` left alone. Its `standard_version: "0.7"` with +Verified first against gate-house's own committed files — `GH-DEC-2026-017` in +`decisions/decisions.md` at `gate-house@def0af2`, amendments A9–A13 in +`docs/amendments/v0.8-section-11-declaration-amendments.md`, and §3/§4/§11 of +`net-kingdom/canon/standards/security-layer-model_v0.8.md` — not from the inbox +message and not from this page, which is a secondary record. The decision and +this page's account of it agreed. + +1. `standard_version` removed from **both** forms. The ruling's general form is + *a layer declaration MUST NOT carry a standard version*, and the governing + declaration is `INTENT.md`, so removing it from the sidecar alone would have + left the field in the one file that actually declares. `INTENT.md`'s + `standard:` pointer is de-versioned with it, for the same reason the field + goes: a version-pinned path reads as a validity condition. The version + ops-warden assented at stays with the assent (`ADR-0010`), which is where a + version-at-a-boundary belongs. +2. `derived: true` and `derived_from: INTENT.md` added to `layer.yaml`, and the + file's header now says plainly that it does not govern. +3. **No layer value changed.** `INTENT.md` still says `Staff`, `layer.yaml` + still says `staff`, and the comment marking the divergence is rewritten from + *unruled, do not touch* to *ruled, folding case is the checker's job*. +4. `scripts/check_layer_conformance.py` now reads `INTENT.md` as the governing + form, ASCII-folds before comparing, validates both values against §3's closed + four-token vocabulary (`Taxonomy` included — omitting it is the defect A9 + records), requires the derived marking, and **rejects a returning + `standard_version` in either file**. It previously listed `standard_version` + as a required key, so it would have rejected the conforming declaration this + ruling produces. +5. `tests/test_layer_conformance.py` asserts the fold rather than equality. An + equality assertion would have been this repository performing the re-spelling + the ruling explicitly declined to order; the fold still fails on a real layer + divergence, which is what point 4 of this page originally wanted. +6. `pep-stance.yaml` left alone. Its `standard_version: "0.7"` with `standard_version_reviewed: "0.8"` is a stance map, not a layer declaration, and gate-house has said separately it would rather other consumers copied - that shape than that ops-warden changed it. + that shape than that ops-warden changed it. `GH-DEC-2026-017` also states it + does not rule the sidecar's schema beyond the derived marking and the version. -Until then this page and `layer.yaml`'s comment stand: do not "fix" the -divergence ad hoc. Under the ruling there is nothing to fix — the two forms -already agree once case is folded. +### What the seven adopters must copy + +The change set is written for them in +`wiki/playbooks/netkingdom-layer-declaration.md`. ops-warden does not edit their +files. The trap worth repeating: any adopter that also copied the checker has a +required-key list containing `standard_version`, so removing the field alone +turns a conforming declaration into a `MALFORMED` exit 2. + +### Still open — where the removed version goes + +`GH-DEC-2026-017` §5 and A12 both say version-scoped state *"belongs in the +derived conformance record, which under this section's derived-artifact rule +already MUST name what it derives from and carry the version or commit it was +derived at."* Both sentences say **already**, and ops-warden does not have one: +what it has is `scripts/check_layer_conformance.py`, a re-runnable check that +emits nothing durable. So it is unresolved whether §11 expects an **emitted +record** — a committed artifact carrying the version it was derived at — or +whether a re-runnable checker discharges the obligation, with the version +supplied by whoever runs it. + +Asked of gate-house in message `4220413a` (2026-09-21). **No answer had arrived +when this change was applied, and the question is deliberately left open rather +than answered by choosing.** Nothing in the applied change depends on it: the +version is gone from the declaration either way, and the only thing the answer +decides is whether ops-warden must additionally emit a record to have somewhere +to put it. ## Why this page exists rather than an inbox thread diff --git a/layer.yaml b/layer.yaml index 5f84ef5..a944997 100644 --- a/layer.yaml +++ b/layer.yaml @@ -1,6 +1,12 @@ -# ops-warden — NetKingdom security layer declaration +# ops-warden — 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, never +# resolved away by precedence. +# +# Framework: net-kingdom/canon/standards/security-layer-model # Assent: docs/adr/ADR-0010 (ops-warden's own voice, per §11 "who must declare") # Validate: python3 scripts/check_layer_conformance.py # @@ -12,16 +18,32 @@ # Conformance rule inherited from tenancy.yaml: accuracy, not altitude. A # declared gap is TRACKED NON-CONFORMANCE (§11), never a claim of conformance. -schema_version: "0.1" +schema_version: "0.2" 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 / amendment A12: a layer declaration MUST NOT carry a +# standard version. The declared layer is a standing property of the repository +# and does not change when the standard is revised; a version here makes every +# revision read as though it invalidated every declaration. Version-scoped state +# belongs in the derived conformance record, which already must carry the +# version or commit it was derived at. Keeping the field "for information" was +# declined explicitly: a field that is present will be branched on. Absence is +# enforced by tests/test_layer_conformance.py. + repository: ops-warden -# UNRULED, 2026-09-21: INTENT.md frontmatter declares `Staff` and this file -# declares `staff`. §11 permits both forms and does not say which governs when -# both exist and disagree; gate-house holds that ruling and the case-sensitivity -# one. NEITHER VALUE IS BEING CHANGED until it rules — see -# docs/layer-declaration-precedence.md and WARDEN-WP-0034-T06. Do not "fix" this -# line to match INTENT.md, or the reverse: the divergence is the evidence. + +# RULED, 2026-09-21 (GH-DEC-2026-017 §2): INTENT.md declares `Staff` and this +# file declares `staff`, and THAT IS NOT A DISAGREEMENT. §3's vocabulary is +# closed — {Taxonomy, Tooling, Engine, Staff} — and comparison is ASCII +# case-insensitive: a conformance run MUST fold case before comparing, and a +# lowercase declaration is conforming, not tolerated. No repository was asked to +# re-spell anything. Do not "fix" this line to match INTENT.md, or the reverse; +# folding case is the checker's job, and it does it. layer: staff declared_by: docs/adr/ADR-0010 declared_at: "2026-08-29" diff --git a/scripts/check_layer_conformance.py b/scripts/check_layer_conformance.py index 5313a34..eea52c0 100644 --- a/scripts/check_layer_conformance.py +++ b/scripts/check_layer_conformance.py @@ -1,7 +1,22 @@ #!/usr/bin/env python3 """Check ops-warden against the NetKingdom security layer model (§5, §11). -Read-only. Makes §11's second mechanical check real: +Read-only. Two declaration forms are read, and the precedence between them is +`GH-DEC-2026-017` §1 (amendment A11): **INTENT.md's frontmatter governs** and +`layer.yaml` is a derived artifact that must be marked derived, must name +INTENT.md as its source, and must agree with it. The sidecar is read anyway, +because a disagreement between the two is itself a finding and is reported +rather than resolved away by precedence. + +Comparison against §3's closed four-token vocabulary ASCII-folds case before +comparing (`GH-DEC-2026-017` §2, amendment A9): two spellings of a token do not +describe two boundaries, and a check that reports findings about capital letters +buries the one real disagreement it exists to find. + +Neither form carries a `standard_version`, and their absence is enforced here +and by tests (`GH-DEC-2026-017` §5, amendment A12). + +Makes §11's second mechanical check real: every direct Tooling client in a Staff repository maps to a declared §5.1, §5.2, or §5.3 entry @@ -27,10 +42,59 @@ import yaml ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" / "warden" +INTENT = ROOT / "INTENT.md" DECL = ROOT / "layer.yaml" VALID_SHAPES = {"5.1", "5.2", "5.3"} +# §3's vocabulary, closed, four tokens (GH-DEC-2026-017 §3, amendment A9). The +# canonical spellings are §4's catalog-column forms; comparison is ASCII +# case-insensitive, so the fold is what is stored and `Taxonomy` is in the set — +# omitting it is the defect A9 records against the estate's other validator. +LAYER_VOCABULARY = {"taxonomy", "tooling", "engine", "staff"} + + +def _fold(value: str) -> 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() + + +def load_governing_layer() -> str: + """The declaration is INTENT.md's frontmatter `layer:` key (§11, GH-DEC-2026-017 §1). + + layer.yaml is derived and does not govern. Read without a YAML frontmatter + dependency: the file is read by humans first and a parser second. + """ + if not INTENT.exists(): + print(f"MISSING: {INTENT} — §11's governing declaration form") + raise SystemExit(2) + lines = INTENT.read_text().splitlines() + if not lines or lines[0].strip() != "---": + print("MALFORMED: INTENT.md has no frontmatter to carry the declaration (§11)") + raise SystemExit(2) + end = next((i for i, ln in enumerate(lines[1:], 1) if ln.strip() == "---"), None) + if end is None: + print("MALFORMED: INTENT.md frontmatter is not terminated") + raise SystemExit(2) + front = yaml.safe_load("\n".join(lines[1:end])) or {} + if "layer" not in front: + print("MALFORMED: INTENT.md frontmatter has no 'layer' key — §11's declaration") + raise SystemExit(2) + if "standard_version" in front: + print( + "MALFORMED: INTENT.md frontmatter carries 'standard_version' — a layer " + "declaration MUST NOT carry a standard version (§11 as amended by A12)" + ) + raise SystemExit(2) + layer = front["layer"] + if _fold(layer) not in LAYER_VOCABULARY: + print( + f"MALFORMED: INTENT.md declares layer {layer!r}, outside §3's closed " + f"vocabulary {sorted(LAYER_VOCABULARY)} (case-insensitive)" + ) + raise SystemExit(2) + return layer + # A direct Tooling contact is an *invocation*, not a mention. Matching the word # "bao" caught help text, a docstring, and the dev-tier doubles library that # simulates bao rather than calling it — three false positives on first run. @@ -60,10 +124,33 @@ def load_declaration() -> dict: print(f"MISSING: {DECL} — ops-warden must declare in its own voice (§11)") raise SystemExit(2) decl = yaml.safe_load(DECL.read_text()) - for key in ("layer", "repository", "standard_version", "tooling_contacts"): + for key in ("layer", "repository", "derived", "derived_from", "tooling_contacts"): if key not in decl: print(f"MALFORMED: layer.yaml has no {key!r}") raise SystemExit(2) + # §11's derived-artifact rule: marked as derived, naming what it derives from. + if decl["derived"] is not True: + print("MALFORMED: layer.yaml must be marked 'derived: true' (§11, GH-DEC-2026-017 §1)") + raise SystemExit(2) + if decl["derived_from"] != "INTENT.md": + print( + f"MALFORMED: layer.yaml derives from {decl['derived_from']!r}; §11 names " + "INTENT.md as the governing declaration" + ) + raise SystemExit(2) + # A12: the version has no home in a declaration, governing or derived. + if "standard_version" in decl: + print( + "MALFORMED: layer.yaml carries 'standard_version' — a layer declaration " + "MUST NOT carry a standard version (§11 as amended by A12)" + ) + raise SystemExit(2) + if _fold(decl["layer"]) not in LAYER_VOCABULARY: + print( + f"MALFORMED: layer.yaml declares layer {decl['layer']!r}, outside §3's " + f"closed vocabulary {sorted(LAYER_VOCABULARY)} (case-insensitive)" + ) + raise SystemExit(2) for c in decl["tooling_contacts"]: if c.get("shape") not in VALID_SHAPES: print(f"MALFORMED: {c.get('id')} has shape {c.get('shape')!r}, not one of {sorted(VALID_SHAPES)}") @@ -110,6 +197,7 @@ def main() -> int: ap.add_argument("--report", action="store_true", help="also print the declaration and gap review dates") args = ap.parse_args() + governing = load_governing_layer() decl = load_declaration() declared = {c["module"].split("/")[-1] for c in decl["tooling_contacts"]} excluded = _excluded(decl) @@ -127,7 +215,8 @@ def main() -> int: stale_decls = declared - set(found) - voluntary if args.report: - print(f"{decl['repository']} — layer: {decl['layer']} (model v{decl['standard_version']})") + print(f"{decl['repository']} — layer: {governing} (declared in INTENT.md; §11 governing form)") + print(f" layer.yaml: derived from {decl['derived_from']}, layer: {decl['layer']}") print(f"declared by {decl['declared_by']}\n") for c in decl["tooling_contacts"]: line = f" §{c['shape']} {c['id']:<28} {c['module']}" @@ -141,6 +230,21 @@ def main() -> int: print(f"\n{len(gaps)} declared gap(s) — tracked non-conformance, not conformance (§11).") ok = True + + # §11 as amended (A11): the derived form MUST agree with the governing one, + # and a disagreement is a finding in its own right — reported, not resolved + # away by precedence. Comparison folds case (A9): `Staff` and `staff` agree. + if _fold(decl["layer"]) != _fold(governing): + ok = False + print( + "\nDECLARATION DISAGREEMENT — a finding under §11, not a precedence question:\n" + f" INTENT.md (governs): layer: {governing}\n" + f" layer.yaml (derived): layer: {decl['layer']}\n" + "Precedence says which value is ops-warden's answer. It does not say the\n" + "disagreement did not happen. Case is already folded, so this is a real\n" + "disagreement about a layer, not about orthography." + ) + if undeclared: ok = False print("\nUNDECLARED TOOLING CONTACT — a finding under §11, not a tracked gap:") diff --git a/tests/test_layer_conformance.py b/tests/test_layer_conformance.py index 35e4a6f..df15f4b 100644 --- a/tests/test_layer_conformance.py +++ b/tests/test_layer_conformance.py @@ -25,15 +25,62 @@ def _decl() -> dict: return yaml.safe_load((ROOT / "layer.yaml").read_text()) +def _intent_frontmatter() -> dict: + lines = (ROOT / "INTENT.md").read_text().splitlines() + assert lines[0].strip() == "---", "INTENT.md must carry frontmatter — it is the declaration" + end = next(i for i, ln in enumerate(lines[1:], 1) if ln.strip() == "---") + return yaml.safe_load("\n".join(lines[1:end])) + + +def _fold(value: str) -> str: + return str(value).strip().encode("ascii", "ignore").decode().lower() + + class TestDeclaration: def test_declares_staff_layer_in_its_own_voice(self): d = _decl() assert d["repository"] == "ops-warden" - assert d["layer"] == "staff" - assert d["standard_version"] == "0.7" + assert _fold(d["layer"]) == "staff" # §11: "only the repository's own file, in its own voice, conforms." assert d["declared_by"] == "docs/adr/ADR-0010" + def test_intent_md_carries_the_governing_declaration(self): + """GH-DEC-2026-017 §1 / A11: INTENT.md's frontmatter is the declaration.""" + assert _fold(_intent_frontmatter()["layer"]) == "staff" + + def test_sidecar_is_marked_derived_and_names_its_source(self): + """§11's derived-artifact rule, applied to the layer sidecar.""" + d = _decl() + assert d["derived"] is True + assert d["derived_from"] == "INTENT.md" + + def test_the_two_forms_agree_once_case_is_folded(self): + """A11: the derived form must agree; A9: comparison folds case. + + `Staff` in INTENT.md and `staff` in layer.yaml are the SAME value. This + test is deliberately a fold rather than an equality: the ruling asked + nobody to re-spell anything, and an equality assertion here would be this + repository quietly doing the re-spelling the ruling declined to order. + The next *real* divergence — a different layer — still fails. + """ + assert _fold(_decl()["layer"]) == _fold(_intent_frontmatter()["layer"]) + + def test_layer_is_in_section_3_closed_vocabulary(self): + """A9: {Taxonomy, Tooling, Engine, Staff}, closed, case-insensitive.""" + vocabulary = {"taxonomy", "tooling", "engine", "staff"} + assert _fold(_intent_frontmatter()["layer"]) in vocabulary + assert _fold(_decl()["layer"]) in vocabulary + + def test_no_declaration_carries_a_standard_version(self): + """GH-DEC-2026-017 §5 / A12 — and the regression guard on its return. + + The field was removed from the estate's reference form, not just from + this file. A field that is present will be branched on, so absence is + asserted rather than trusted. + """ + assert "standard_version" not in _decl() + assert "standard_version" not in _intent_frontmatter() + def test_every_tooling_contact_maps_to_a_declared_shape(self): """§11 mechanical check — the guard against a new undeclared client.""" result = subprocess.run( diff --git a/wiki/playbooks/netkingdom-layer-declaration.md b/wiki/playbooks/netkingdom-layer-declaration.md index dc0cd77..e2cf87c 100644 --- a/wiki/playbooks/netkingdom-layer-declaration.md +++ b/wiki/playbooks/netkingdom-layer-declaration.md @@ -12,10 +12,16 @@ reference declaration artifacts. It does not redefine the layer model. 1. Read `net-kingdom/SECURITY-COMPANION.md`, then use the accepted `net-kingdom/canon/standards/security-layer-model_v0.7.md` for the normative - layer, Tooling-contact, and PEP obligations. -2. Use ops-warden's `layer.yaml` as the machine-readable declaration reference. - Adapt its repository, layer, contacts, and ownership facts; do not copy - ops-warden-specific claims as your own. + layer, Tooling-contact, and PEP obligations. `v0.8` is proposed, not accepted, + and its acceptance flip is held on amendments A9–A13 (`GH-DEC-2026-019`) — + but those amendments' substance **already governs** through the decision + record that authorises each of them, so declare to the shape below now. +2. **Declare in your own `INTENT.md` frontmatter.** That is the declaration + (`GH-DEC-2026-017` §1). Use ops-warden's `layer.yaml` as the machine-readable + reference *form*, which is a **derived** artifact that must be marked derived, + must name `INTENT.md` as its source, and must agree with it. Adapt its + repository, layer, contacts, and ownership facts; do not copy ops-warden-specific + claims as your own. 3. If the repository is PEP-shaped, use `pep-stance.yaml` as the stance-map reference and publish the resulting path in the repository's layer declaration. 4. Adapt `scripts/check_layer_conformance.py` and @@ -31,6 +37,51 @@ python3 scripts/check_layer_conformance.py pytest tests/test_layer_conformance.py ``` +## Reference-form change set — 2026-09-21 (`GH-DEC-2026-017`) + +**If you copied ops-warden's `layer.yaml` before 2026-09-21, it is now the wrong +shape in three ways.** The change is to the reference form, which is why +`gate-house` asked ops-warden to make it here rather than asking each adopter to +work it out. Apply all three in your own repository; ops-warden does not edit +anyone else's files. + +1. **Remove `standard_version:` from the sidecar *and* from your `INTENT.md` + frontmatter.** A layer declaration MUST NOT carry a standard version + (`GH-DEC-2026-017` §5, amendment A12): the declared layer is a standing + property that does not change when the standard is revised, and a version in + the declaration makes every revision read as though it invalidated every + declaration. Keeping it "for information" was declined explicitly — a field + that is present will be branched on. Version-scoped state belongs in the + derived conformance record. If your checker lists `standard_version` as a + required key, or prints it in a report line, it will now **reject a conforming + declaration** — fix the checker in the same commit. +2. **Add `derived: true` and `derived_from: INTENT.md`.** The sidecar is a derived + artifact under §11's derived-artifact rule and does not govern + (`GH-DEC-2026-017` §1, amendment A11). If your `INTENT.md` has no frontmatter + `layer:` key, add one — that, not the sidecar and not a prose line, is your + declaration. +3. **Fold ASCII case before comparing a layer value, and re-spell nothing.** §3's + vocabulary is closed and has **four** tokens — `Taxonomy`, `Tooling`, + `Engine`, `Staff` — and comparison is case-insensitive (`GH-DEC-2026-017` §2 + and §3, amendment A9). `Staff` and `staff` are the same value; a lowercase + declaration is conforming, not tolerated. Two traps: a validator that admits + only three tokens and rejects `Taxonomy` carries a defect — the layer this + standard itself occupies is in the vocabulary; and an equality assertion + between your two forms silently performs the re-spelling the ruling declined + to order. Assert the **fold**, so a real layer divergence still fails. + +A disagreement between the two forms, after folding, is a **finding in its own +right** and must be reported rather than resolved away by precedence. Precedence +says which value is your answer; it does not say the disagreement did not happen. + +`pep-stance.yaml` is **not** a layer declaration and is out of scope here: its +`standard_version` / `standard_version_reviewed` pair is a stance map's record of +what was reviewed, and stays. + +ops-warden's applied instance of this change set is commit-local: `INTENT.md`, +`layer.yaml`, `scripts/check_layer_conformance.py`, `tests/test_layer_conformance.py`. +Read those four together rather than the sidecar alone. + ## Ownership boundary `gate-house` owns what the model requires. Each repository owns the truth of its diff --git a/workplans/WARDEN-WP-0034-layer-model-v07-conformance.md b/workplans/WARDEN-WP-0034-layer-model-v07-conformance.md index ea9f9de..a7c7e25 100644 --- a/workplans/WARDEN-WP-0034-layer-model-v07-conformance.md +++ b/workplans/WARDEN-WP-0034-layer-model-v07-conformance.md @@ -235,7 +235,7 @@ T05 stays `wait` on the remaining two: `ops-mason` (no stance map published) and ```task id: WARDEN-WP-0034-T06 -status: wait +status: done priority: low state_hub_task_id: "b3b573df-2d2d-5a2c-9662-a13e435be57b" ``` @@ -247,24 +247,51 @@ while both follow the standard. Nine of nine repositories carrying both forms do the same thing (flex-auth `tools/survey_layer_declarations.go`, receipt `docs/evidence/2026-09-21-layer-declaration-survey.json`). -**Waiting on gate-house for two rulings**: which form governs, and whether the §3 -vocabulary is case-sensitive. The custodian's estate-wide sweep adds a third that -bears on it — §3's table capitalises and types engines `Engines` while §4's -catalog rows type them `Engine`, so the standard disagrees with itself one level -below B1. +**RULED 2026-09-21, APPLIED 2026-09-21.** `GH-DEC-2026-017` +(`gate-house@def0af2`, amendments A9-A13) answers both questions, and the answer +to the second dissolves the first's consequence: -**Deliberately not acting.** Aligning the two files now means choosing an answer -gate-house owns, and doing it in the one place §11 says another repository may -not speak for us. It would also erase the evidence the ruling is being made -against: the finding is visible because nine repositories exhibit it, and a -repository that quietly normalises itself makes the estate read as more -conformant than it is. Position recorded in -`docs/layer-declaration-precedence.md`; both files carry a comment saying not to -"fix" the divergence. +- **`INTENT.md` governs**; `layer.yaml` is a derived artifact that must be marked + derived, must name `INTENT.md` as its source, and must agree with it. +- **The §3 vocabulary is case-insensitive**, closed, and has four tokens + (`Taxonomy`, `Tooling`, `Engine`, `Staff`). A conformance run MUST ASCII-fold + case before comparing, and **no repository was asked to re-spell anything**. + `Staff` and `staff` are one value. +- **A declaration MUST NOT carry a standard version** — and that field is the + *reference form's*, not this repository's habit, so ops-warden was asked to + remove it here rather than each of seven adopters deciding. -**Done when:** gate-house rules, the non-governing file is changed to match in -one commit, and a regression test asserts the two declarations agree so the next -divergence is caught rather than surveyed. +Verified before editing against gate-house's own committed files rather than the +inbox message: the decision at `gate-house@def0af2`, amendments A9-A13 in +`docs/amendments/v0.8-section-11-declaration-amendments.md`, and §3/§4/§11 of +`net-kingdom/canon/standards/security-layer-model_v0.8.md`. + +**What was applied.** `standard_version` removed from `layer.yaml` **and** from +`INTENT.md` frontmatter (the ruling's subject is the declaration, and `INTENT.md` +is the declaration), with the version-pinned `standard:` path de-versioned for the +same reason; `derived: true` / `derived_from: INTENT.md` added; **no layer value +changed**; `scripts/check_layer_conformance.py` rewritten to read `INTENT.md` as +governing, fold case, validate the closed four-token vocabulary, require the +derived marking, and reject a returning `standard_version` — it previously +*required* that field and so would have rejected the conforming declaration this +ruling produces; `tests/test_layer_conformance.py` asserts the **fold** rather +than equality, because an equality assertion would perform the re-spelling the +ruling declined to order. `pep-stance.yaml` untouched: a stance map is not a layer +declaration, and the schema beyond the derived marking and the version is +explicitly not ruled. + +**Evidence.** `python3 scripts/check_layer_conformance.py --report` exits 0 and +prints the governing form and the derived one separately; `pytest +tests/test_layer_conformance.py` green; full suite and `ruff` green. Reasoning +and the adopter change set: `docs/layer-declaration-precedence.md` and +`wiki/playbooks/netkingdom-layer-declaration.md`. + +**Left open, and not decided here.** Where the removed version now lives. A12 +says version-scoped state belongs in the derived conformance record, which +"already MUST" carry the version it was derived at; ops-warden has a re-runnable +checker that emits nothing durable, not an emitted record. Whether §11 expects an +emitted artifact or a re-runnable check discharges it is asked of gate-house in +message `4220413a` and unanswered. Nothing applied above depends on the answer. **Ruled the same day, and still `wait` — deliberately.** `GH-DEC-2026-017` (message `3715e247`) landed hours later: `INTENT.md` governs, the sidecar is a