Apply GH-DEC-2026-020: print scope on every run, widen A12 detection.
The checker now prints the standard version and the run's scope first on every run, pass or fail, and enforces A12 r2 over every key and value of the INTENT.md frontmatter and layer.yaml, not only a key named standard_version. Tests fail if a versioned standard: path or a companion_version comes back. Neither declaration form changed. KG-DEC-2026-005 records assent to A9, A10, A11 and A13 and returns A12 r2 revised, with one finding: "any value" reaches prose revision citations. 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
358f759c72
commit
7b678e5d73
3 changed files with 291 additions and 17 deletions
|
|
@ -21,10 +21,24 @@ frontmatter `layer:` key governs; layer.yaml is a derived artifact that must be
|
|||
marked derived, name INTENT.md, and agree with it. Layer comparison ASCII-folds
|
||||
case against §3's closed four-token vocabulary, so `Staff` and `staff` agree and
|
||||
neither file is re-spelled. A disagreement that survives the fold is reported as
|
||||
a finding, not resolved by precedence. Neither form may carry a
|
||||
`standard_version`; the version this check was built and validated against is
|
||||
the checker's own, VALIDATED_AGAINST below, and is printed in every run's output
|
||||
— this script's output is the derived conformance record A12 points to.
|
||||
a finding, not resolved by precedence. Neither form may carry a version of the
|
||||
standard or its companion in any key or value (A12 r2, GH-DEC-2026-020 §1–§2):
|
||||
not only a key named `standard_version`, but `companion_version`, any other
|
||||
version key, and a version-bearing path or file name such as a `standard:` of
|
||||
`…security-layer-model_v0.7.md`. Comments are not read (YAML drops them) and
|
||||
`schema_version` is the sidecar's own schema, so neither is reached. Only
|
||||
INTENT.md frontmatter and layer.yaml are read for A12: stance, claims and
|
||||
classification maps are not declarations and this run never applies A12 to them
|
||||
(GH-DEC-2026-020 §3).
|
||||
|
||||
A bare revision citation in a prose value — `layer.yaml` cites "v0.6 §13" as
|
||||
provenance in a gap record — is reported on every run, not failed: whether A12 r2's
|
||||
"any value" reaches a citation rather than a pin is raised with gate-house by
|
||||
kings-guard (see decisions/decisions.md KG-DEC-2026-005), not settled here.
|
||||
|
||||
Every run prints, first, the standard version this check was built and validated
|
||||
against (VALIDATED_AGAINST) and the scope it ranges over (SCOPE), including runs
|
||||
that fail (GH-DEC-2026-020 §4). A run is sufficient; nothing durable is emitted.
|
||||
|
||||
Review dates are reported, never enforced: a date-triggered failure breaks the
|
||||
build on a calendar day with no code change.
|
||||
|
|
@ -55,6 +69,29 @@ INTENT = ROOT / "INTENT.md"
|
|||
# the checker is re-validated against a newer accepted text.
|
||||
VALIDATED_AGAINST = "net-kingdom/canon/standards/security-layer-model_v0.7.md"
|
||||
|
||||
# The scope every run ranges over, printed on every run (GH-DEC-2026-020 §4, A11).
|
||||
# This is a single-repository run, not an estate run: it grades kings-guard, a §4
|
||||
# row, and nothing else.
|
||||
SCOPE = (
|
||||
"kings-guard only (§4 row, Staff): INTENT.md frontmatter, layer.yaml, "
|
||||
"src/kings_guard, repository tree for credential files"
|
||||
)
|
||||
|
||||
# A12 r2 detection. A key naming a version is a pin, except the sidecar's own
|
||||
# schema_version. A value naming the standard or companion with a version, or any
|
||||
# versioned file name, is a pin. In identity-bearing keys any version token is a
|
||||
# pin. A bare "vN.N" elsewhere in a value is a citation: reported, not failed.
|
||||
A12_EXEMPT_KEYS = {"schema_version"}
|
||||
A12_IDENTITY_KEYS = {"standard", "companion", "framework"}
|
||||
A12_PIN_VALUE = re.compile(
|
||||
r"""(?ix)
|
||||
(?:security[-_ ]?layer[-_ ]?model|security[-_ ]?companion)[^\s'"]*?[-_.]v?\d+(?:\.\d+)+
|
||||
| [-_]v\d+(?:\.\d+)*\.(?:md|ya?ml|json)\b
|
||||
"""
|
||||
)
|
||||
A12_VERSION_TOKEN = re.compile(r"\bv?\d+\.\d+(?:\.\d+)*\b")
|
||||
A12_CITATION = re.compile(r"\bv\d+\.\d+\b")
|
||||
|
||||
# §3's closed layer vocabulary (A9): four tokens, compared ASCII case-insensitively.
|
||||
LAYER_VOCABULARY = {"taxonomy", "tooling", "engine", "staff"}
|
||||
EXPECTED_LAYER = "staff" # kings-guard's own §4 row, folded
|
||||
|
|
@ -105,13 +142,60 @@ def fold(value: object) -> str:
|
|||
return str(value).encode("ascii", "replace").decode("ascii").lower()
|
||||
|
||||
|
||||
def a12_findings(node: object, where: str) -> tuple[list[str], list[str]]:
|
||||
"""Every key and value of a declaration form, walked (A12 r2).
|
||||
|
||||
Returns (pins, citations). Pins fail the run; citations are reported.
|
||||
"""
|
||||
pins: list[str] = []
|
||||
citations: list[str] = []
|
||||
|
||||
def walk(value: object, path: str, key: str | None) -> None:
|
||||
if isinstance(value, dict):
|
||||
for k, v in value.items():
|
||||
k_str = str(k)
|
||||
sub = f"{path}.{k_str}" if path else k_str
|
||||
if "version" in k_str.lower() and k_str not in A12_EXEMPT_KEYS:
|
||||
pins.append(f"{where}: key '{sub}' carries a version")
|
||||
if k_str in A12_EXEMPT_KEYS:
|
||||
continue
|
||||
walk(v, sub, k_str)
|
||||
return
|
||||
if isinstance(value, list):
|
||||
for i, v in enumerate(value):
|
||||
walk(v, f"{path}[{i}]", key)
|
||||
return
|
||||
if value is None or isinstance(value, bool):
|
||||
return
|
||||
text = str(value)
|
||||
if A12_PIN_VALUE.search(text) or (
|
||||
key in A12_IDENTITY_KEYS and A12_VERSION_TOKEN.search(text)
|
||||
):
|
||||
pins.append(f"{where}: '{path}' = {text!r} names a versioned standard")
|
||||
elif A12_CITATION.search(text):
|
||||
match = A12_CITATION.search(text).group(0)
|
||||
citations.append(f"{where}: '{path}' cites {match} in prose")
|
||||
|
||||
walk(node, "", None)
|
||||
return pins, citations
|
||||
|
||||
|
||||
def _refuse_pins(pins: list[str]) -> None:
|
||||
if pins:
|
||||
_malformed(
|
||||
"a layer declaration MUST NOT carry a standard or companion version "
|
||||
"in any key or value (§11 as amended by A12 r2, GH-DEC-2026-020 §1–§2):\n "
|
||||
+ "\n ".join(pins)
|
||||
)
|
||||
|
||||
|
||||
def _malformed(message: str) -> None:
|
||||
print(f"FAIL: {message}", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
def load_governing_layer() -> str:
|
||||
"""The declaration: INTENT.md frontmatter `layer:` (§11, GH-DEC-2026-017 §1)."""
|
||||
def load_intent_frontmatter() -> dict:
|
||||
"""INTENT.md's frontmatter, parsed; the governing declaration form (§11)."""
|
||||
if not INTENT.exists():
|
||||
_malformed(f"no {INTENT.name} — §11's governing declaration form")
|
||||
lines = INTENT.read_text().splitlines()
|
||||
|
|
@ -125,13 +209,17 @@ def load_governing_layer() -> str:
|
|||
front = yaml.safe_load("\n".join(lines[1:end])) or {}
|
||||
except yaml.YAMLError as exc:
|
||||
_malformed(f"{INTENT.name} frontmatter is not parseable: {exc}")
|
||||
if not isinstance(front, dict):
|
||||
_malformed(f"{INTENT.name} frontmatter is not a mapping")
|
||||
return front
|
||||
|
||||
|
||||
def load_governing_layer() -> str:
|
||||
"""The declaration: INTENT.md frontmatter `layer:` (§11, GH-DEC-2026-017 §1)."""
|
||||
front = load_intent_frontmatter()
|
||||
if "layer" not in front:
|
||||
_malformed(f"{INTENT.name} frontmatter has no 'layer' key — §11's declaration")
|
||||
if "standard_version" in front:
|
||||
_malformed(
|
||||
f"{INTENT.name} frontmatter carries 'standard_version' — a layer "
|
||||
"declaration MUST NOT carry a standard version (§11 as amended by A12)"
|
||||
)
|
||||
_refuse_pins(a12_findings(front, f"{INTENT.name} frontmatter")[0])
|
||||
layer = front["layer"]
|
||||
if fold(layer) not in LAYER_VOCABULARY:
|
||||
_malformed(
|
||||
|
|
@ -162,11 +250,7 @@ def load_declaration() -> dict:
|
|||
f"{DECL.name} derives from {data['derived_from']!r}; §11 names INTENT.md "
|
||||
"as the governing declaration"
|
||||
)
|
||||
if "standard_version" in data:
|
||||
_malformed(
|
||||
f"{DECL.name} carries 'standard_version' — a layer declaration MUST NOT "
|
||||
"carry a standard version (§11 as amended by A12)"
|
||||
)
|
||||
_refuse_pins(a12_findings(data, DECL.name)[0])
|
||||
if fold(data["layer"]) not in LAYER_VOCABULARY:
|
||||
_malformed(
|
||||
f"{DECL.name} declares layer {data['layer']!r}, outside §3's closed "
|
||||
|
|
@ -231,6 +315,11 @@ def main() -> int:
|
|||
parser.add_argument("--report", action="store_true", help="print the declaration summary")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Every run, pass or fail, states what it checks against and over what
|
||||
# (GH-DEC-2026-020 §4). Printed before anything can exit.
|
||||
print(f"checking against: {VALIDATED_AGAINST}")
|
||||
print(f"scope: {SCOPE}")
|
||||
|
||||
governing = load_governing_layer()
|
||||
decl = load_declaration()
|
||||
declared = {c.get("id") for c in decl.get("tooling_contacts") or []}
|
||||
|
|
@ -260,6 +349,13 @@ def main() -> int:
|
|||
form = meta.get("form", "unspecified") if isinstance(meta, dict) else "unspecified"
|
||||
print(f" - {name}: {form} (claimed={rules.get(name)})")
|
||||
|
||||
citations = (
|
||||
a12_findings(load_intent_frontmatter(), f"{INTENT.name} frontmatter")[1]
|
||||
+ a12_findings(decl, DECL.name)[1]
|
||||
)
|
||||
for citation in citations:
|
||||
print(f"NOTE (A12 r2 reach unruled, reported not failed): {citation}")
|
||||
|
||||
if forms_disagree(governing, decl["layer"]):
|
||||
print("", file=sys.stderr)
|
||||
print("FAIL: the two declaration forms disagree (§11, A11)", file=sys.stderr)
|
||||
|
|
@ -303,7 +399,8 @@ def main() -> int:
|
|||
print(
|
||||
f"OK: no direct Tooling client in {SRC.relative_to(ROOT)}; "
|
||||
"no standing credential (§5, §11, §3.4 rule 1); "
|
||||
f"declaration forms agree; validated against {VALIDATED_AGAINST}"
|
||||
"declaration forms agree; no standard or companion version in "
|
||||
f"either form; validated against {VALIDATED_AGAINST}; scope: {SCOPE}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue