Apply GH-DEC-2026-021: identity-value pins, prose citations not reached.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

The playbook detector is the estate reference (021 §3). Add its one
addition to the reference and the checker: any v?N.N in a standard: or
companion: value is a pin. The prose-citation note moves from pending to
not reached (021 §1, A12 r3), and intent_version is noted as a key that
must not be flagged. VALIDATED_AGAINST keeps accepted v0.7 and adds
GH-DEC-2026-021 at gate-house@39d9287.

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:
tegwick 2026-09-21 13:05:24 +02:00
parent 0f9ada0b0d
commit 39791526a0
3 changed files with 61 additions and 21 deletions

View file

@ -58,12 +58,13 @@ DECL = ROOT / "layer.yaml"
VALID_SHAPES = {"5.1", "5.2", "5.3"} VALID_SHAPES = {"5.1", "5.2", "5.3"}
# What every run checks against, printed on every run (GH-DEC-2026-020 §4, A12 r2). # What every run checks against, printed on every run (GH-DEC-2026-020 §4,
# The accepted text is v0.7 at net-kingdom@66dc491; the amendments that already # GH-DEC-2026-021 §2). The accepted text is v0.7 at net-kingdom@66dc491; the
# govern through their decision records are named with it. # decision records whose rulings this checker enforces beyond v0.7 are named with it.
VALIDATED_AGAINST = ( VALIDATED_AGAINST = (
"net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491) " "net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491) "
"as amended by GH-DEC-2026-017 and GH-DEC-2026-020 (A9-A13, A12 r2; gate-house@d8c82a8)" "as amended by GH-DEC-2026-017, GH-DEC-2026-020 and GH-DEC-2026-021 "
"(A9-A13, A12 r3; gate-house@39d9287)"
) )
# What every run ranges over. pep-stance.yaml is deliberately outside it. # What every run ranges over. pep-stance.yaml is deliberately outside it.
SCOPE = "INTENT.md frontmatter, layer.yaml, src/warden/**/*.py" SCOPE = "INTENT.md frontmatter, layer.yaml, src/warden/**/*.py"
@ -72,12 +73,18 @@ SCOPE = "INTENT.md frontmatter, layer.yaml, src/warden/**/*.py"
# declaration. Keys: anything naming a standard/companion version. Values: a # declaration. Keys: anything naming a standard/companion version. Values: a
# versioned file name or path (`_v0.7`, `-v0.8.md`) or a bare version string on a # versioned file name or path (`_v0.7`, `-v0.8.md`) or a bare version string on a
# version-named key. `schema_version` is the file's own schema, not reached. # version-named key. `schema_version` is the file's own schema, not reached.
# GH-DEC-2026-021 §1/§3 (A12 r3): a version is reached only as a pin. Any version
# token (`v?N.N`) in the value of an identity-bearing key (`standard:`,
# `companion:`) is a pin; a revision cited in other prose is provenance and is not
# reached. Keys such as `intent_version` name neither and are not flagged.
VERSION_KEY = re.compile(r"(standard|companion).*version|version.*(standard|companion)", re.I) 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) VERSION_IN_VALUE = re.compile(r"[_\-.]v\d+(\.\d+)*(\.md)?\b|@v?\d+\.\d+", re.I)
NOT_REACHED_KEYS = {"schema_version"} 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 = "") -> list[str]: def find_version_pins(node, where: str = "", identity: bool = False) -> list[str]:
"""Every place in a parsed declaration that carries a standard/companion version. """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 Walks every key and value (comments are gone after parsing, which is the
@ -92,12 +99,14 @@ def find_version_pins(node, where: str = "") -> list[str]:
if VERSION_KEY.search(str(k)): if VERSION_KEY.search(str(k)):
pins.append(f"{here} (key names a standard/companion version)") pins.append(f"{here} (key names a standard/companion version)")
continue continue
pins.extend(find_version_pins(v, here)) pins.extend(find_version_pins(v, here, str(k).lower() in IDENTITY_KEYS))
elif isinstance(node, list): elif isinstance(node, list):
for i, v in enumerate(node): for i, v in enumerate(node):
pins.extend(find_version_pins(v, f"{where}[{i}]")) pins.extend(find_version_pins(v, f"{where}[{i}]", identity))
elif isinstance(node, str) and VERSION_IN_VALUE.search(node): elif isinstance(node, str) and VERSION_IN_VALUE.search(node):
pins.append(f"{where} = {node!r} (value carries a version)") 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 return pins

View file

@ -115,6 +115,21 @@ class TestDeclaration:
assert checker.find_version_pins({"layer": "Staff", "companion_version": "0.2"}) assert checker.find_version_pins({"layer": "Staff", "companion_version": "0.2"})
assert checker.find_version_pins({"nested": {"standard_version": "0.7"}}) assert checker.find_version_pins({"nested": {"standard_version": "0.7"}})
def test_version_token_in_identity_value_is_a_pin(self):
"""GH-DEC-2026-021 §3: any `v?N.N` in a standard:/companion: value is a pin."""
checker = _checker()
pins = checker.find_version_pins({"standard": "security-layer-model v0.7"})
assert pins and pins[0].startswith("standard")
assert checker.find_version_pins({"companion": "SECURITY-COMPANION 0.2"})
assert checker.find_version_pins({"nested": {"standard": ["security-layer-model v0.8"]}})
def test_prose_citation_and_intent_version_are_not_reached(self):
"""GH-DEC-2026-021 §1 (A12 r3): prose provenance and `intent_version` pass."""
checker = _checker()
assert checker.find_version_pins(
{"layer": "Staff", "note": "Outside §5 by the v0.5 scope rule", "intent_version": "0.1.0"}
) == []
def test_schema_version_is_not_reached(self): def test_schema_version_is_not_reached(self):
assert _checker().find_version_pins({"schema_version": "0.2", "layer": "Staff"}) == [] assert _checker().find_version_pins({"schema_version": "0.2", "layer": "Staff"}) == []

View file

@ -122,15 +122,16 @@ from this page.
(`tests/test_layer_conformance.py` has the reference set). Change declaration, (`tests/test_layer_conformance.py` has the reference set). Change declaration,
checker and tests in the same commit, and re-spell no layer value. checker and tests in the same commit, and re-spell no layer value.
### Reference detector and `VALIDATED_AGAINST`practice, not a ruling ### Reference detector and `VALIDATED_AGAINST`the estate reference (`GH-DEC-2026-021`)
The estate's checkers each wrote their own version detector in the 020 round, and The estate's checkers each wrote their own version detector in the 020 round, and
they diverge: some flag any key *ending* in `version`, some any key *containing* they diverge: some flag any key *ending* in `version`, some any key *containing*
it, one flags `vN.N` anywhere in prose while the others ignore prose. The it, one flags `vN.N` anywhere in prose while the others ignore prose. The
checkers also disagree on what `VALIDATED_AGAINST` names. Neither point is checkers also disagree on what `VALIDATED_AGAINST` names. `GH-DEC-2026-021` §3
ruled by `GH-DEC-2026-020`. What follows is the reference form to copy so the rules that the detector below is the **estate reference** every checker's copy
copies stop diverging; it is steward's practice, and a gate-house ruling wins converges on — when the copy is next edited, and no later than the commit that
over it. re-points its `VALIDATED_AGAINST` after the v0.8 flip (`GH-WP-0004-T11`). Each
repository implements its own copy.
**One detector.** Copy this from ops-warden's checker as-is: **One detector.** Copy this from ops-warden's checker as-is:
@ -138,35 +139,50 @@ over it.
VERSION_KEY = re.compile(r"(standard|companion).*version|version.*(standard|companion)", re.I) 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) VERSION_IN_VALUE = re.compile(r"[_\-.]v\d+(\.\d+)*(\.md)?\b|@v?\d+\.\d+", re.I)
NOT_REACHED_KEYS = {"schema_version"} 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=""): # walk every parsed key and value def find_version_pins(node, where="", identity=False): # walk every parsed key and value
... # see scripts/check_layer_conformance.py ... # see scripts/check_layer_conformance.py
``` ```
- **Keys:** flag a key that names a *standard or companion* version - **Keys:** flag a key that names a *standard or companion* version
(`standard_version`, `companion_version`, `standard_version_reviewed`) — not (`standard_version`, `companion_version`, `standard_version_reviewed`) — not
every key containing `version`. `schema_version` is the file's own schema and every key containing `version`. `schema_version` is the file's own schema and
is skipped (A12 r2). is skipped (A12 r2). **Do not flag a key such as `intent_version`**: it names
neither the standard nor its companion. `informed-decision`'s declaring
frontmatter carries `intent_version: 0.1.0` (the version of its own
`INTENT.md`), and a detector that flags every key containing or ending in
`version` fails it wrongly (`GH-DEC-2026-021`, Context).
- **Values:** flag a version carried in a *file name or path* (`_v0.7`, - **Values:** flag a version carried in a *file name or path* (`_v0.7`,
`-v0.8.md`, `@0.7`) — the `standard: …_v0.7.md` case §1 rules on. `-v0.8.md`, `@0.7`) — the `standard: …_v0.7.md` case §1 rules on.
- **Identity-bearing values:** any version token (`v?N.N`) in the value of a
`standard:` or `companion:` key is a pin (`GH-DEC-2026-021` §3), so
`standard: security-layer-model v0.7` fails.
- **Deliberate over-reach:** the path pattern flags a versioned path of *any*
document in a declaration, wider than A12's "this standard or its companion".
`GH-DEC-2026-021` §3 accepts this; a repository that needs such a path raises
it, and it is not waived by allowlist.
- **Comments:** never read; the detector runs on parsed YAML, so they are gone. - **Comments:** never read; the detector runs on parsed YAML, so they are gone.
- **Prose citations in a value** (e.g. a rationale string saying "the v0.5 scope - **Prose citations in a value** (e.g. a rationale string saying "the v0.5 scope
rule"): **pending.** Whether A12 reaches them is an open question to rule"): **not reached** (`GH-DEC-2026-021` §1, A12 r3). A revision cited in
gate-house. The reference detector deliberately does not flag a bare `vN.N` prose is provenance, not a pin. The reference detector does not flag a bare
preceded by a space, so it neither enforces nor waives an answer that has not `vN.N` outside a `standard:`/`companion:` value, and no allowlist is needed.
been given. Do not widen or narrow this in your copy until gate-house rules. A12 r3 is out for assent; if it is rejected in favour of the literal reading,
the reference is widened to any `vN.N` in any value.
**Name the text in force.** While v0.8's acceptance flip is held **Name the text in force.** While v0.8's acceptance flip is held
(`GH-DEC-2026-019`), `VALIDATED_AGAINST` names the **accepted** text — (`GH-DEC-2026-019`), `VALIDATED_AGAINST` names the **accepted** text —
`security-layer-model_v0.7.md`, with a net-kingdom commit — plus the decision `security-layer-model_v0.7.md`, with a net-kingdom commit — plus the decision
records whose amendments already govern (`GH-DEC-2026-017`, `GH-DEC-2026-020`), records whose rulings the checker enforces beyond v0.7 (`GH-DEC-2026-017`,
with a gate-house commit. Naming the proposed v0.8 states a check against text `GH-DEC-2026-020`, `GH-DEC-2026-021`), with a gate-house commit recommended
(`GH-DEC-2026-021` §2). Naming the proposed v0.8 states a check against text
that does not yet govern. Change it in the same commit that follows the flip. that does not yet govern. Change it in the same commit that follows the flip.
ops-warden's constant is the example: ops-warden's constant is the example:
```text ```text
net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491) net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491)
as amended by GH-DEC-2026-017 and GH-DEC-2026-020 (A9-A13, A12 r2; gate-house@d8c82a8) as amended by GH-DEC-2026-017, GH-DEC-2026-020 and GH-DEC-2026-021 (A9-A13, A12 r3; gate-house@39d9287)
``` ```
**Citation.** Cite the ruling by the decision's body section: `GH-DEC-2026-017` **Citation.** Cite the ruling by the decision's body section: `GH-DEC-2026-017`