Apply GH-DEC-2026-020: checker prints version and scope, A12 r2 by content.
The layer conformance checker now prints VALIDATED_AGAINST and SCOPE on every run, including the PASS line (kings-guard pattern), and enforces A12 r2 over every key and value of INTENT.md frontmatter and layer.yaml: a versioned standard: path and a companion_version are caught, schema_version and comments are not reached, pep-stance.yaml is outside the run. Tests guard both returns. The playbook carries the adopter change set and confirms the section 5 citation is canonical. WARDEN-WP-0034's open 4220413a note is closed. 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
b9151e20da
commit
c23918d6ec
4 changed files with 191 additions and 18 deletions
|
|
@ -13,8 +13,19 @@ 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
|
describe two boundaries, and a check that reports findings about capital letters
|
||||||
buries the one real disagreement it exists to find.
|
buries the one real disagreement it exists to find.
|
||||||
|
|
||||||
Neither form carries a `standard_version`, and their absence is enforced here
|
Neither form carries a version of the standard or of its companion, in any key
|
||||||
and by tests (`GH-DEC-2026-017` §5, amendment A12).
|
or value (`GH-DEC-2026-017` §5, amendment A12 as refined by A12 r2 /
|
||||||
|
`GH-DEC-2026-020` §1-§2). The rule reaches content, not a key name: a
|
||||||
|
`standard_version` key, a `companion_version` key, and a version-bearing path
|
||||||
|
such as `standard: .../security-layer-model_v0.7.md` are the same pin. Comments
|
||||||
|
and a file's own `schema_version` are not reached. Stance, claims and
|
||||||
|
evidence-classification maps (`pep-stance.yaml`) are NOT declarations and this
|
||||||
|
check does not read them (`GH-DEC-2026-020` §3).
|
||||||
|
|
||||||
|
The version belongs to the run (`GH-DEC-2026-020` §4): every run prints
|
||||||
|
VALIDATED_AGAINST and SCOPE below, including the PASS line, following
|
||||||
|
kings-guard's pattern. A retained copy of this output is a derived artifact
|
||||||
|
whose version is owed by whoever retains it.
|
||||||
|
|
||||||
Makes §11's second mechanical check real:
|
Makes §11's second mechanical check real:
|
||||||
|
|
||||||
|
|
@ -47,6 +58,61 @@ 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).
|
||||||
|
# The accepted text is v0.7 at net-kingdom@66dc491; the amendments that already
|
||||||
|
# govern through their decision records are named with it.
|
||||||
|
VALIDATED_AGAINST = (
|
||||||
|
"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)"
|
||||||
|
)
|
||||||
|
# What every run ranges over. pep-stance.yaml is deliberately outside it.
|
||||||
|
SCOPE = "INTENT.md frontmatter, layer.yaml, src/warden/**/*.py"
|
||||||
|
|
||||||
|
# A12 r2: a version of the standard or companion in any key or value of the
|
||||||
|
# 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
|
||||||
|
# version-named key. `schema_version` is the file's own schema, not reached.
|
||||||
|
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"}
|
||||||
|
|
||||||
|
|
||||||
|
def find_version_pins(node, where: str = "") -> 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))
|
||||||
|
elif isinstance(node, list):
|
||||||
|
for i, v in enumerate(node):
|
||||||
|
pins.extend(find_version_pins(v, f"{where}[{i}]"))
|
||||||
|
elif isinstance(node, str) and VERSION_IN_VALUE.search(node):
|
||||||
|
pins.append(f"{where} = {node!r} (value carries a version)")
|
||||||
|
return pins
|
||||||
|
|
||||||
|
|
||||||
|
def _reject_version_pins(label: str, node) -> None:
|
||||||
|
pins = find_version_pins(node)
|
||||||
|
if pins:
|
||||||
|
print(
|
||||||
|
f"MALFORMED: {label} carries a standard/companion version — a layer "
|
||||||
|
"declaration MUST NOT, in any key or value (§11 as amended by A12 r2, "
|
||||||
|
"GH-DEC-2026-020 §1-§2):"
|
||||||
|
)
|
||||||
|
for p in pins:
|
||||||
|
print(f" {p}")
|
||||||
|
raise SystemExit(2)
|
||||||
|
|
||||||
# §3's vocabulary, closed, four tokens (GH-DEC-2026-017 §3, amendment A9). The
|
# §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
|
# 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 —
|
# case-insensitive, so the fold is what is stored and `Taxonomy` is in the set —
|
||||||
|
|
@ -80,12 +146,7 @@ def load_governing_layer() -> str:
|
||||||
if "layer" not in front:
|
if "layer" not in front:
|
||||||
print("MALFORMED: INTENT.md frontmatter has no 'layer' key — §11's declaration")
|
print("MALFORMED: INTENT.md frontmatter has no 'layer' key — §11's declaration")
|
||||||
raise SystemExit(2)
|
raise SystemExit(2)
|
||||||
if "standard_version" in front:
|
_reject_version_pins("INTENT.md frontmatter", 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"]
|
layer = front["layer"]
|
||||||
if _fold(layer) not in LAYER_VOCABULARY:
|
if _fold(layer) not in LAYER_VOCABULARY:
|
||||||
print(
|
print(
|
||||||
|
|
@ -139,12 +200,7 @@ def load_declaration() -> dict:
|
||||||
)
|
)
|
||||||
raise SystemExit(2)
|
raise SystemExit(2)
|
||||||
# A12: the version has no home in a declaration, governing or derived.
|
# A12: the version has no home in a declaration, governing or derived.
|
||||||
if "standard_version" in decl:
|
_reject_version_pins("layer.yaml", 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:
|
if _fold(decl["layer"]) not in LAYER_VOCABULARY:
|
||||||
print(
|
print(
|
||||||
f"MALFORMED: layer.yaml declares layer {decl['layer']!r}, outside §3's "
|
f"MALFORMED: layer.yaml declares layer {decl['layer']!r}, outside §3's "
|
||||||
|
|
@ -197,6 +253,10 @@ def main() -> int:
|
||||||
ap.add_argument("--report", action="store_true", help="also print the declaration and gap review dates")
|
ap.add_argument("--report", action="store_true", help="also print the declaration and gap review dates")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
# Printed before anything can fail, so even a MALFORMED run states what it
|
||||||
|
# checked against and over what (GH-DEC-2026-020 §4).
|
||||||
|
print(f"validated against: {VALIDATED_AGAINST}")
|
||||||
|
print(f"scope: {SCOPE}")
|
||||||
governing = load_governing_layer()
|
governing = load_governing_layer()
|
||||||
decl = load_declaration()
|
decl = load_declaration()
|
||||||
declared = {c["module"].split("/")[-1] for c in decl["tooling_contacts"]}
|
declared = {c["module"].split("/")[-1] for c in decl["tooling_contacts"]}
|
||||||
|
|
@ -258,9 +318,15 @@ def main() -> int:
|
||||||
print(f" {m}")
|
print(f" {m}")
|
||||||
|
|
||||||
if ok and not args.report:
|
if ok and not args.report:
|
||||||
print(f"PASS — {len(found)} module(s) with Tooling contact, all declared.")
|
print(
|
||||||
|
f"PASS — {len(found)} module(s) with Tooling contact, all declared; "
|
||||||
|
f"validated against {VALIDATED_AGAINST}"
|
||||||
|
)
|
||||||
elif ok:
|
elif ok:
|
||||||
print("\nPASS — every direct Tooling contact maps to a declared shape.")
|
print(
|
||||||
|
"\nPASS — every direct Tooling contact maps to a declared shape; "
|
||||||
|
f"validated against {VALIDATED_AGAINST}"
|
||||||
|
)
|
||||||
return 0 if ok else 1
|
return 0 if ok else 1
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,17 @@ def _intent_frontmatter() -> dict:
|
||||||
return yaml.safe_load("\n".join(lines[1:end]))
|
return yaml.safe_load("\n".join(lines[1:end]))
|
||||||
|
|
||||||
|
|
||||||
|
def _checker():
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"check_layer_conformance", ROOT / "scripts" / "check_layer_conformance.py"
|
||||||
|
)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
def _fold(value: str) -> str:
|
def _fold(value: str) -> str:
|
||||||
return str(value).strip().encode("ascii", "ignore").decode().lower()
|
return str(value).strip().encode("ascii", "ignore").decode().lower()
|
||||||
|
|
||||||
|
|
@ -81,6 +92,51 @@ class TestDeclaration:
|
||||||
assert "standard_version" not in _decl()
|
assert "standard_version" not in _decl()
|
||||||
assert "standard_version" not in _intent_frontmatter()
|
assert "standard_version" not in _intent_frontmatter()
|
||||||
|
|
||||||
|
def test_no_version_anywhere_in_either_declaration(self):
|
||||||
|
"""A12 r2 / GH-DEC-2026-020 §1-§2: content, not a key name.
|
||||||
|
|
||||||
|
A versioned `standard:` path or a `companion_version` is the same pin as
|
||||||
|
`standard_version`, so the guard walks every key and value of both forms.
|
||||||
|
"""
|
||||||
|
checker = _checker()
|
||||||
|
assert checker.find_version_pins(_intent_frontmatter()) == []
|
||||||
|
assert checker.find_version_pins(_decl()) == []
|
||||||
|
assert not str(_intent_frontmatter()["standard"]).endswith(".md")
|
||||||
|
|
||||||
|
def test_checker_catches_a_versioned_standard_path(self):
|
||||||
|
checker = _checker()
|
||||||
|
pins = checker.find_version_pins(
|
||||||
|
{"layer": "Staff", "standard": "net-kingdom/canon/standards/security-layer-model_v0.7.md"}
|
||||||
|
)
|
||||||
|
assert pins and pins[0].startswith("standard")
|
||||||
|
|
||||||
|
def test_checker_catches_a_companion_version(self):
|
||||||
|
checker = _checker()
|
||||||
|
assert checker.find_version_pins({"layer": "Staff", "companion_version": "0.2"})
|
||||||
|
assert checker.find_version_pins({"nested": {"standard_version": "0.7"}})
|
||||||
|
|
||||||
|
def test_schema_version_is_not_reached(self):
|
||||||
|
assert _checker().find_version_pins({"schema_version": "0.2", "layer": "Staff"}) == []
|
||||||
|
|
||||||
|
def test_stance_map_is_outside_the_run(self):
|
||||||
|
"""GH-DEC-2026-020 §3: a stance map keeps its version; the run must not read it."""
|
||||||
|
stance = yaml.safe_load((ROOT / "pep-stance.yaml").read_text())
|
||||||
|
assert "standard_version" in stance, "pep-stance.yaml keeps its clause-scoped version"
|
||||||
|
assert "pep-stance" not in _checker().SCOPE
|
||||||
|
|
||||||
|
def test_every_run_states_version_and_scope(self):
|
||||||
|
"""GH-DEC-2026-020 §4: the version belongs to the run, printed every time."""
|
||||||
|
checker = _checker()
|
||||||
|
out = subprocess.run(
|
||||||
|
[sys.executable, str(ROOT / "scripts" / "check_layer_conformance.py")],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
).stdout
|
||||||
|
assert f"validated against: {checker.VALIDATED_AGAINST}" in out
|
||||||
|
assert f"scope: {checker.SCOPE}" in out
|
||||||
|
pass_line = next(ln for ln in out.splitlines() if ln.startswith("PASS"))
|
||||||
|
assert checker.VALIDATED_AGAINST in pass_line
|
||||||
|
|
||||||
def test_every_tooling_contact_maps_to_a_declared_shape(self):
|
def test_every_tooling_contact_maps_to_a_declared_shape(self):
|
||||||
"""§11 mechanical check — the guard against a new undeclared client."""
|
"""§11 mechanical check — the guard against a new undeclared client."""
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,10 @@ anyone else's files.
|
||||||
property that does not change when the standard is revised, and a version in
|
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
|
the declaration makes every revision read as though it invalidated every
|
||||||
declaration. Keeping it "for information" was declined explicitly — a field
|
declaration. Keeping it "for information" was declined explicitly — a field
|
||||||
that is present will be branched on. Version-scoped state belongs in the
|
that is present will be branched on. Version-scoped state belongs to the
|
||||||
derived conformance record. If your checker lists `standard_version` as a
|
conformance *run* (see the 2026-09-21 `GH-DEC-2026-020` section below; the
|
||||||
|
"derived conformance record" this line used to name was a defect in §5 and
|
||||||
|
nobody is required to emit one). If your checker lists `standard_version` as a
|
||||||
required key, or prints it in a report line, it will now **reject a conforming
|
required key, or prints it in a report line, it will now **reject a conforming
|
||||||
declaration** — fix the checker in the same commit.
|
declaration** — fix the checker in the same commit.
|
||||||
2. **Add `derived: true` and `derived_from: INTENT.md`.** The sidecar is a derived
|
2. **Add `derived: true` and `derived_from: INTENT.md`.** The sidecar is a derived
|
||||||
|
|
@ -82,6 +84,49 @@ 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`.
|
`layer.yaml`, `scripts/check_layer_conformance.py`, `tests/test_layer_conformance.py`.
|
||||||
Read those four together rather than the sidecar alone.
|
Read those four together rather than the sidecar alone.
|
||||||
|
|
||||||
|
## Reach and run-version change set — 2026-09-21 (`GH-DEC-2026-020`, A12 r2)
|
||||||
|
|
||||||
|
**If you copied ops-warden's checker before this change, it enforces A12 by key
|
||||||
|
name only, and prints no version.** Both are now defects. `GH-DEC-2026-020`
|
||||||
|
refines A12 as A12 r2; verify it in `gate-house` `decisions/decisions.md` and
|
||||||
|
`docs/amendments/v0.8-section-11-declaration-amendments.md` (§ "A12 r2"), not
|
||||||
|
from this page.
|
||||||
|
|
||||||
|
1. **A version anywhere in the declaration counts.** The declaration is every key
|
||||||
|
and value of your `INTENT.md` frontmatter and of your derived sidecar. No key
|
||||||
|
or value carries a version of the standard **or of its companion**, including
|
||||||
|
a version-bearing path: `standard: .../security-layer-model_v0.7.md` becomes
|
||||||
|
`standard: .../security-layer-model` (`GH-DEC-2026-020` §1). A de-versioned
|
||||||
|
path was a required change, not a voluntary one.
|
||||||
|
2. **`companion_version` counts** and comes out of the declaration (§2).
|
||||||
|
3. **Not reached:** comments, and a file's own `schema_version`. Keeping or
|
||||||
|
removing them is equally fine; do not edit them just to tidy.
|
||||||
|
4. **Not a declaration, not checked:** stance maps (`pep-stance.yaml`), claims
|
||||||
|
maps, evidence classifications. Their version is what makes them re-readable
|
||||||
|
when clause text changes; keep it. Your checker **MUST NOT** apply A12 to
|
||||||
|
them (§3).
|
||||||
|
5. **The version belongs to the run.** A re-runnable checker is sufficient —
|
||||||
|
not "for now" — if **every** run prints the standard version or commit it
|
||||||
|
checks against and the scope it ranged over, including the PASS line (§4).
|
||||||
|
No repository must emit a durable conformance record; whoever retains a run's
|
||||||
|
output owes that copy's version. The pattern is kings-guard's: a
|
||||||
|
`VALIDATED_AGAINST` constant in the checker, printed on every run.
|
||||||
|
6. **Widen your checker from key name to content.** ops-warden's
|
||||||
|
`scripts/check_layer_conformance.py` now carries `VALIDATED_AGAINST`, `SCOPE`
|
||||||
|
and `find_version_pins()`, which walks every parsed key and value, skips
|
||||||
|
`schema_version`, and flags any key naming a standard/companion version and
|
||||||
|
any value carrying a versioned file name or path. It prints version and scope
|
||||||
|
before loading anything, so even a MALFORMED run states them. Copy that, set
|
||||||
|
`VALIDATED_AGAINST` to what *your* run checks against, and add tests that fail
|
||||||
|
if a versioned `standard:` path or a `companion_version` comes back
|
||||||
|
(`tests/test_layer_conformance.py` has the reference set). Change declaration,
|
||||||
|
checker and tests in the same commit, and re-spell no layer value.
|
||||||
|
|
||||||
|
**Citation.** Cite the ruling by the decision's body section: `GH-DEC-2026-017`
|
||||||
|
§5, statute A12 (now A12 r2). This page's "§5" is canonical
|
||||||
|
(`GH-DEC-2026-020`); the decision's `rationale:` part numbers are a summary and
|
||||||
|
are not cited.
|
||||||
|
|
||||||
## Ownership boundary
|
## Ownership boundary
|
||||||
|
|
||||||
`gate-house` owns what the model requires. Each repository owns the truth of its
|
`gate-house` owns what the model requires. Each repository owns the truth of its
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,12 @@ says version-scoped state belongs in the derived conformance record, which
|
||||||
checker that emits nothing durable, not an emitted record. Whether §11 expects an
|
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
|
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.
|
message `4220413a` and unanswered. Nothing applied above depends on the answer.
|
||||||
|
**Closed 2026-09-21 by `GH-DEC-2026-020` §4 (gate-house@104f3fc):** §5 was wrong
|
||||||
|
that the record exists; a re-runnable checker is sufficient if every run prints
|
||||||
|
the version it checks against and its scope. Applied: the checker now prints
|
||||||
|
`VALIDATED_AGAINST` and `SCOPE` on every run and enforces A12 r2 by content, not
|
||||||
|
key name (versioned `standard:` path, `companion_version`); `pep-stance.yaml`
|
||||||
|
untouched per §3; the playbook carries the adopter change set.
|
||||||
|
|
||||||
**Ruled the same day, and still `wait` — deliberately.** `GH-DEC-2026-017`
|
**Ruled the same day, and still `wait` — deliberately.** `GH-DEC-2026-017`
|
||||||
(message `3715e247`) landed hours later: `INTENT.md` governs, the sidecar is a
|
(message `3715e247`) landed hours later: `INTENT.md` governs, the sidecar is a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue