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
|
||||
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).
|
||||
Neither form carries a version of the standard or of its companion, in any key
|
||||
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:
|
||||
|
||||
|
|
@ -47,6 +58,61 @@ DECL = ROOT / "layer.yaml"
|
|||
|
||||
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
|
||||
# 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 —
|
||||
|
|
@ -80,12 +146,7 @@ def load_governing_layer() -> str:
|
|||
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)
|
||||
_reject_version_pins("INTENT.md frontmatter", front)
|
||||
layer = front["layer"]
|
||||
if _fold(layer) not in LAYER_VOCABULARY:
|
||||
print(
|
||||
|
|
@ -139,12 +200,7 @@ def load_declaration() -> dict:
|
|||
)
|
||||
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)
|
||||
_reject_version_pins("layer.yaml", decl)
|
||||
if _fold(decl["layer"]) not in LAYER_VOCABULARY:
|
||||
print(
|
||||
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")
|
||||
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()
|
||||
decl = load_declaration()
|
||||
declared = {c["module"].split("/")[-1] for c in decl["tooling_contacts"]}
|
||||
|
|
@ -258,9 +318,15 @@ def main() -> int:
|
|||
print(f" {m}")
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,17 @@ def _intent_frontmatter() -> dict:
|
|||
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:
|
||||
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 _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):
|
||||
"""§11 mechanical check — the guard against a new undeclared client."""
|
||||
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
|
||||
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
|
||||
that is present will be branched on. Version-scoped state belongs to the
|
||||
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
|
||||
declaration** — fix the checker in the same commit.
|
||||
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`.
|
||||
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
|
||||
|
||||
`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
|
||||
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.
|
||||
**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`
|
||||
(message `3715e247`) landed hours later: `INTENT.md` governs, the sidecar is a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue