Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
"""Layer-model conformance (security-layer-model_v0.4 §5, §11).
|
|
|
|
|
|
|
|
|
|
Two things are checked here. §11 makes one of them mechanical: every direct
|
|
|
|
|
Tooling client maps to a declared shape. §5.2 asks for the other: the conduit's
|
|
|
|
|
supplied-authority property covered by a test.
|
|
|
|
|
|
|
|
|
|
Deliberately absent: any assertion on a §5.3 review date. A date-triggered
|
|
|
|
|
failure breaks the build on a calendar day with no code change, punishing
|
|
|
|
|
whoever commits next rather than whoever owns the gap — the same reasoning
|
|
|
|
|
recorded in WARDEN-WP-0033-T05 for blocker staleness.
|
|
|
|
|
"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _decl() -> dict:
|
|
|
|
|
return yaml.safe_load((ROOT / "layer.yaml").read_text())
|
|
|
|
|
|
|
|
|
|
|
Apply GH-DEC-2026-017: INTENT.md governs, the sidecar is derived, no version
Verified against gate-house's own committed files before editing, not the inbox
message: 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 sections 3, 4 and 11 of net-kingdom's security-layer-model_v0.8.md. The
ruling and docs/layer-declaration-precedence.md's secondary account agreed.
INTENT.md's frontmatter is the declaration; layer.yaml is a derived artifact,
now marked derived: true / derived_from: INTENT.md, and it does not govern.
standard_version is removed from BOTH forms. The ruling's general form is that a
layer declaration must not carry a standard version, and INTENT.md is the
declaration, so removing it from the sidecar alone would have left the field in
the only file that actually declares. INTENT.md's version-pinned `standard:`
path is de-versioned for the same reason: a pinned path reads as a validity
condition. The version ops-warden assented at stays with the assent, ADR-0010.
NO LAYER VALUE IS CHANGED. INTENT.md still says Staff and layer.yaml still says
staff. Section 3's vocabulary is closed, four tokens, and case-insensitive: the
two forms were never in disagreement about a layer, and the ruling asked nobody
to re-spell anything. The comment marking the divergence is rewritten from
"unruled, do not touch" to "ruled, folding case is the checker's job".
check_layer_conformance.py would have rejected the conforming declaration this
ruling produces -- it listed standard_version as a required key. It now reads
INTENT.md as the governing form, ASCII-folds before comparing, validates both
values against the closed four-token vocabulary (Taxonomy included; omitting it
is the defect A9 records against the estate's other validator), requires the
derived marking, rejects a returning standard_version in either file, and
reports a post-fold disagreement between the forms as a finding rather than
resolving it away by precedence.
The test asserts the fold, not equality. An equality assertion here would be
this repository quietly performing the re-spelling the ruling declined to order;
the fold still fails on a real layer divergence.
pep-stance.yaml is untouched. A stance map is not a layer declaration, and the
sidecar schema beyond the derived marking and the version is explicitly not
ruled.
layer.yaml is the form seven repositories copied, so the adopter change set is
written out in wiki/playbooks/netkingdom-layer-declaration.md -- including the
trap that an adopter which also copied the checker turns a conforming
declaration into MALFORMED exit 2 by removing the field alone. No other
repository is edited here.
Still open: where the removed version lives. A12 says the derived conformance
record "already MUST" carry it; ops-warden has a re-runnable checker that emits
nothing durable. Asked of gate-house in message 4220413a, unanswered, and left
open rather than answered by choosing. Nothing above depends on it.
Carries WARDEN-WP-0034-T06 to done.
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
2026-09-21 02:38:55 +02:00
|
|
|
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]))
|
|
|
|
|
|
|
|
|
|
|
2026-09-21 09:38:17 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
Apply GH-DEC-2026-017: INTENT.md governs, the sidecar is derived, no version
Verified against gate-house's own committed files before editing, not the inbox
message: 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 sections 3, 4 and 11 of net-kingdom's security-layer-model_v0.8.md. The
ruling and docs/layer-declaration-precedence.md's secondary account agreed.
INTENT.md's frontmatter is the declaration; layer.yaml is a derived artifact,
now marked derived: true / derived_from: INTENT.md, and it does not govern.
standard_version is removed from BOTH forms. The ruling's general form is that a
layer declaration must not carry a standard version, and INTENT.md is the
declaration, so removing it from the sidecar alone would have left the field in
the only file that actually declares. INTENT.md's version-pinned `standard:`
path is de-versioned for the same reason: a pinned path reads as a validity
condition. The version ops-warden assented at stays with the assent, ADR-0010.
NO LAYER VALUE IS CHANGED. INTENT.md still says Staff and layer.yaml still says
staff. Section 3's vocabulary is closed, four tokens, and case-insensitive: the
two forms were never in disagreement about a layer, and the ruling asked nobody
to re-spell anything. The comment marking the divergence is rewritten from
"unruled, do not touch" to "ruled, folding case is the checker's job".
check_layer_conformance.py would have rejected the conforming declaration this
ruling produces -- it listed standard_version as a required key. It now reads
INTENT.md as the governing form, ASCII-folds before comparing, validates both
values against the closed four-token vocabulary (Taxonomy included; omitting it
is the defect A9 records against the estate's other validator), requires the
derived marking, rejects a returning standard_version in either file, and
reports a post-fold disagreement between the forms as a finding rather than
resolving it away by precedence.
The test asserts the fold, not equality. An equality assertion here would be
this repository quietly performing the re-spelling the ruling declined to order;
the fold still fails on a real layer divergence.
pep-stance.yaml is untouched. A stance map is not a layer declaration, and the
sidecar schema beyond the derived marking and the version is explicitly not
ruled.
layer.yaml is the form seven repositories copied, so the adopter change set is
written out in wiki/playbooks/netkingdom-layer-declaration.md -- including the
trap that an adopter which also copied the checker turns a conforming
declaration into MALFORMED exit 2 by removing the field alone. No other
repository is edited here.
Still open: where the removed version lives. A12 says the derived conformance
record "already MUST" carry it; ops-warden has a re-runnable checker that emits
nothing durable. Asked of gate-house in message 4220413a, unanswered, and left
open rather than answered by choosing. Nothing above depends on it.
Carries WARDEN-WP-0034-T06 to done.
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
2026-09-21 02:38:55 +02:00
|
|
|
def _fold(value: str) -> str:
|
|
|
|
|
return str(value).strip().encode("ascii", "ignore").decode().lower()
|
|
|
|
|
|
|
|
|
|
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
class TestDeclaration:
|
|
|
|
|
def test_declares_staff_layer_in_its_own_voice(self):
|
|
|
|
|
d = _decl()
|
|
|
|
|
assert d["repository"] == "ops-warden"
|
Apply GH-DEC-2026-017: INTENT.md governs, the sidecar is derived, no version
Verified against gate-house's own committed files before editing, not the inbox
message: 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 sections 3, 4 and 11 of net-kingdom's security-layer-model_v0.8.md. The
ruling and docs/layer-declaration-precedence.md's secondary account agreed.
INTENT.md's frontmatter is the declaration; layer.yaml is a derived artifact,
now marked derived: true / derived_from: INTENT.md, and it does not govern.
standard_version is removed from BOTH forms. The ruling's general form is that a
layer declaration must not carry a standard version, and INTENT.md is the
declaration, so removing it from the sidecar alone would have left the field in
the only file that actually declares. INTENT.md's version-pinned `standard:`
path is de-versioned for the same reason: a pinned path reads as a validity
condition. The version ops-warden assented at stays with the assent, ADR-0010.
NO LAYER VALUE IS CHANGED. INTENT.md still says Staff and layer.yaml still says
staff. Section 3's vocabulary is closed, four tokens, and case-insensitive: the
two forms were never in disagreement about a layer, and the ruling asked nobody
to re-spell anything. The comment marking the divergence is rewritten from
"unruled, do not touch" to "ruled, folding case is the checker's job".
check_layer_conformance.py would have rejected the conforming declaration this
ruling produces -- it listed standard_version as a required key. It now reads
INTENT.md as the governing form, ASCII-folds before comparing, validates both
values against the closed four-token vocabulary (Taxonomy included; omitting it
is the defect A9 records against the estate's other validator), requires the
derived marking, rejects a returning standard_version in either file, and
reports a post-fold disagreement between the forms as a finding rather than
resolving it away by precedence.
The test asserts the fold, not equality. An equality assertion here would be
this repository quietly performing the re-spelling the ruling declined to order;
the fold still fails on a real layer divergence.
pep-stance.yaml is untouched. A stance map is not a layer declaration, and the
sidecar schema beyond the derived marking and the version is explicitly not
ruled.
layer.yaml is the form seven repositories copied, so the adopter change set is
written out in wiki/playbooks/netkingdom-layer-declaration.md -- including the
trap that an adopter which also copied the checker turns a conforming
declaration into MALFORMED exit 2 by removing the field alone. No other
repository is edited here.
Still open: where the removed version lives. A12 says the derived conformance
record "already MUST" carry it; ops-warden has a re-runnable checker that emits
nothing durable. Asked of gate-house in message 4220413a, unanswered, and left
open rather than answered by choosing. Nothing above depends on it.
Carries WARDEN-WP-0034-T06 to done.
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
2026-09-21 02:38:55 +02:00
|
|
|
assert _fold(d["layer"]) == "staff"
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
# §11: "only the repository's own file, in its own voice, conforms."
|
|
|
|
|
assert d["declared_by"] == "docs/adr/ADR-0010"
|
|
|
|
|
|
Apply GH-DEC-2026-017: INTENT.md governs, the sidecar is derived, no version
Verified against gate-house's own committed files before editing, not the inbox
message: 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 sections 3, 4 and 11 of net-kingdom's security-layer-model_v0.8.md. The
ruling and docs/layer-declaration-precedence.md's secondary account agreed.
INTENT.md's frontmatter is the declaration; layer.yaml is a derived artifact,
now marked derived: true / derived_from: INTENT.md, and it does not govern.
standard_version is removed from BOTH forms. The ruling's general form is that a
layer declaration must not carry a standard version, and INTENT.md is the
declaration, so removing it from the sidecar alone would have left the field in
the only file that actually declares. INTENT.md's version-pinned `standard:`
path is de-versioned for the same reason: a pinned path reads as a validity
condition. The version ops-warden assented at stays with the assent, ADR-0010.
NO LAYER VALUE IS CHANGED. INTENT.md still says Staff and layer.yaml still says
staff. Section 3's vocabulary is closed, four tokens, and case-insensitive: the
two forms were never in disagreement about a layer, and the ruling asked nobody
to re-spell anything. The comment marking the divergence is rewritten from
"unruled, do not touch" to "ruled, folding case is the checker's job".
check_layer_conformance.py would have rejected the conforming declaration this
ruling produces -- it listed standard_version as a required key. It now reads
INTENT.md as the governing form, ASCII-folds before comparing, validates both
values against the closed four-token vocabulary (Taxonomy included; omitting it
is the defect A9 records against the estate's other validator), requires the
derived marking, rejects a returning standard_version in either file, and
reports a post-fold disagreement between the forms as a finding rather than
resolving it away by precedence.
The test asserts the fold, not equality. An equality assertion here would be
this repository quietly performing the re-spelling the ruling declined to order;
the fold still fails on a real layer divergence.
pep-stance.yaml is untouched. A stance map is not a layer declaration, and the
sidecar schema beyond the derived marking and the version is explicitly not
ruled.
layer.yaml is the form seven repositories copied, so the adopter change set is
written out in wiki/playbooks/netkingdom-layer-declaration.md -- including the
trap that an adopter which also copied the checker turns a conforming
declaration into MALFORMED exit 2 by removing the field alone. No other
repository is edited here.
Still open: where the removed version lives. A12 says the derived conformance
record "already MUST" carry it; ops-warden has a re-runnable checker that emits
nothing durable. Asked of gate-house in message 4220413a, unanswered, and left
open rather than answered by choosing. Nothing above depends on it.
Carries WARDEN-WP-0034-T06 to done.
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
2026-09-21 02:38:55 +02:00
|
|
|
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()
|
|
|
|
|
|
2026-09-21 09:38:17 +02:00
|
|
|
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"}})
|
|
|
|
|
|
2026-09-21 13:05:24 +02:00
|
|
|
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"}
|
|
|
|
|
) == []
|
|
|
|
|
|
2026-09-21 09:38:17 +02:00
|
|
|
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
|
|
|
|
|
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
def test_every_tooling_contact_maps_to_a_declared_shape(self):
|
|
|
|
|
"""§11 mechanical check — the guard against a new undeclared client."""
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
[sys.executable, str(ROOT / "scripts" / "check_layer_conformance.py")],
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
)
|
|
|
|
|
assert result.returncode == 0, (
|
|
|
|
|
f"undeclared Tooling contact — a finding under §11, not a tracked gap:\n"
|
|
|
|
|
f"{result.stdout}{result.stderr}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_declared_gaps_carry_all_four_fields(self):
|
|
|
|
|
"""§5.3 is machine-readable or it is prose wearing a schema."""
|
|
|
|
|
for c in _decl()["tooling_contacts"]:
|
|
|
|
|
if c["shape"] == "5.3":
|
|
|
|
|
for field in ("capability", "intended_owner", "blocked_on", "review"):
|
|
|
|
|
assert c.get(field), f"{c['id']} missing {field}"
|
|
|
|
|
|
|
|
|
|
def test_gaps_are_not_counted_as_conformance(self):
|
|
|
|
|
"""§11: a declared gap is tracked non-conformance. Keep that visible."""
|
|
|
|
|
text = (ROOT / "layer.yaml").read_text()
|
|
|
|
|
assert "TRACKED NON-CONFORMANCE" in text.upper()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestConduitSuppliesNoAuthority:
|
|
|
|
|
"""§5.2: 'MUST NOT present its own credential, MUST NOT widen what the
|
|
|
|
|
caller could already do.' The standard says this SHOULD be covered by a
|
|
|
|
|
test; this is that test."""
|
|
|
|
|
|
|
|
|
|
def test_conduit_supplies_no_authority_of_its_own(self, monkeypatch):
|
|
|
|
|
from warden import proxy
|
|
|
|
|
|
|
|
|
|
monkeypatch.setenv("VAULT_TOKEN", "caller-own-token")
|
|
|
|
|
monkeypatch.setenv("HOME", "/home/nobody")
|
|
|
|
|
before = dict(os.environ)
|
|
|
|
|
|
|
|
|
|
env = proxy._caller_env()
|
|
|
|
|
|
|
|
|
|
# The child environment IS the caller's environment — nothing added,
|
|
|
|
|
# nothing removed, no ops-warden credential injected.
|
|
|
|
|
assert env == before, (
|
|
|
|
|
"conduit altered the caller's environment; §5.2 requires it to "
|
|
|
|
|
"supply no authority of its own"
|
|
|
|
|
)
|
|
|
|
|
assert env["VAULT_TOKEN"] == "caller-own-token"
|
|
|
|
|
|
|
|
|
|
def test_conduit_declares_supplied_authority_none(self):
|
|
|
|
|
conduits = [c for c in _decl()["tooling_contacts"] if c["shape"] == "5.2"]
|
|
|
|
|
assert conduits, "no §5.2 conduit declared — proxy.py is one"
|
|
|
|
|
for c in conduits:
|
|
|
|
|
assert c["supplied_authority"] == "none"
|
|
|
|
|
|
|
|
|
|
def test_proxy_holds_no_credential_constant(self):
|
|
|
|
|
"""A conduit that presents its own token is not a conduit (§5.2)."""
|
|
|
|
|
src = (ROOT / "src" / "warden" / "proxy.py").read_text()
|
|
|
|
|
# It may name token ENV VARS to detect caller auth; it must not carry a
|
|
|
|
|
# token value or mint one.
|
|
|
|
|
for forbidden in ("X-Vault-Token", "auth/approle/login", "token create"):
|
|
|
|
|
assert forbidden not in src, (
|
|
|
|
|
f"proxy.py references {forbidden!r} — that is presenting or "
|
|
|
|
|
f"minting authority, not conducting the caller's"
|
|
|
|
|
)
|
Review layer model v0.6; publish the PEP stance map §6.4 requires
All three v0.4 findings were acted on — §9.1 split into pending/declared-gap and
§5's scope rule adopted as recommended and credited, and §9.6 ruled via the
load-bearing/attributive distinction with ops-warden's `# audit must not block
signing` named as the estate's live example.
Checked the favourable ruling rather than accepting it. §9.6's test is "no
control branches on its presence": the only consumer of audit.jsonl is `warden
activity`, which displays. Nothing gates on a signing record, so the lane is
genuinely attributive. AuditTrail.md now records the ruling instead of the open
question, and states that the trade must be revisited if a control ever gates on
the trail.
CONFORMANCE ACTION. §6.4 obligation 3 requires a stance map "published rather
than held in code", and requires every PEP-shaped consumer to publish one so the
maps can be inventoried — naming ADR-0009 as the reference shape. ops-warden was
not doing it: the map lived in PolicyConfig.failure_modes, a dataclass default.
Not a code comment, but not published either.
pep-stance.yaml publishes it, and the test asserts the published map EQUALS the
shipped default. A published map that may drift from the code is worse than no
map, because it invites reliance it cannot support.
Two findings sent to gate-house, in history/2026-08-29-layer-model-v06-review.md:
§6.4 obligation 1 (no side effect without a decision record) contradicts
obligation 3 and §9.3, with ops-warden's blessed fail-open stance as the
instance; and §6.4 mandates a stance-map inventory in §13 that §13 does not
implement — where ops-warden is currently the only PEP to have published one.
402 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 10:20:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestPepStanceMap:
|
|
|
|
|
"""§6.4: every PEP-shaped consumer MUST publish its unreachable-engine
|
|
|
|
|
stance map, total and per zone, 'published rather than held in code'.
|
|
|
|
|
ADR-0009 is named as the reference shape, so it should actually hold."""
|
|
|
|
|
|
|
|
|
|
def _stance(self) -> dict:
|
|
|
|
|
return yaml.safe_load((ROOT / "pep-stance.yaml").read_text())
|
|
|
|
|
|
|
|
|
|
def test_published_map_equals_shipped_behaviour(self):
|
|
|
|
|
"""The whole point. A published map that may drift from the code is
|
|
|
|
|
worse than none, because it invites reliance it cannot support."""
|
|
|
|
|
from warden.config import PolicyConfig
|
|
|
|
|
|
|
|
|
|
assert self._stance()["stance"] == PolicyConfig().failure_modes
|
|
|
|
|
|
|
|
|
|
def test_stance_is_total_over_the_zone_model(self):
|
|
|
|
|
"""§6.4 obligation 3: total, no implicit default."""
|
|
|
|
|
stance = self._stance()["stance"]
|
|
|
|
|
required = {
|
|
|
|
|
"z0-experimental", "z1-operational", "z2-protected",
|
|
|
|
|
"z2-continuity", "z3-critical", "unknown", "not-applicable",
|
|
|
|
|
}
|
|
|
|
|
assert required <= set(stance), f"stance not total; missing {required - set(stance)}"
|
|
|
|
|
assert set(stance.values()) <= {"fail_open", "fail_closed"}
|
|
|
|
|
|
|
|
|
|
def test_critical_zone_fails_closed(self):
|
|
|
|
|
"""ADR-0009's one non-negotiable row."""
|
|
|
|
|
assert self._stance()["stance"]["z3-critical"] == "fail_closed"
|
|
|
|
|
|
|
|
|
|
def test_verdict_is_never_cached(self):
|
|
|
|
|
"""§6.4 obligation 2: caching an input claim is permitted; caching the
|
|
|
|
|
answer is a second decision point deciding early (§6.1)."""
|
|
|
|
|
assert self._stance()["verdict_caching"] == "none"
|
2026-09-05 01:19:48 +02:00
|
|
|
|
|
|
|
|
def test_revocation_visibility_deadline_equals_enforced_ttl_policy(self):
|
|
|
|
|
"""§9.7.2: a published replay window must not drift from issuance."""
|
|
|
|
|
from warden.models import ActorType, MAX_TTL_HOURS
|
|
|
|
|
|
|
|
|
|
published = self._stance()["revocation_visibility"]
|
|
|
|
|
expected = {actor.value: MAX_TTL_HOURS[actor] for actor in ActorType}
|
|
|
|
|
assert published["deadline_hours"] == expected
|
|
|
|
|
assert published["mechanism"] == "ttl_expiry"
|
|
|
|
|
assert published["revocation_channel"] == "none"
|
|
|
|
|
|
|
|
|
|
def test_attributive_emission_cadence_deferral_carries_measurement(self):
|
|
|
|
|
cadence = self._stance()["emission_cadence"]
|
|
|
|
|
assert cadence["classification"] == "attributive"
|
|
|
|
|
assert cadence["status"] == "deferred"
|
|
|
|
|
assert cadence["observed_window"]["signature_records"] == 3
|
|
|
|
|
assert cadence["observed_window"]["active_signature_days"] == 2
|
|
|
|
|
assert cadence["reason"]
|
docs: mark the unknown cell, measure the coverage we asked to publish
gate-house ruled the v0.8 assent round (GH-DEC-2026-011, net-kingdom@64394e9):
ask 1 declined, ask 2 adopted.
Ask 1's refusal is accepted without reservation and the reason is better than
the ask -- a sanctioned transitional fail_open is indistinguishable at runtime
from the stance the rule forbids, and would make the rule optional at the only
moment it costs anything.
Ask 2 gave §13.1 a Coverage column with this repo's figures as its first
entries. Since we asked for the column, we owe it accuracy:
scripts/report_coverage.py measures both populations from the artifacts the
runtime uses (reusing the workload-join build rather than re-deriving it), and
a test asserts pep-stance.yaml's published block equals what it measures.
A hand-counted number in a register that explicitly does not recompute it
decays silently, and a stale figure beside a marked cell is worse than the
blank the other four rows carry.
pep-stance.yaml marks the unknown cell inline as a declared gap -- assent, the
measured reason for not flipping, the declined ask, WARDEN-WP-0040 as route --
and a second test keeps it marked while it is fail_open, failing when it is
flipped. standard_version stays 0.7 because that is what binds; v0.8 is
proposed, so it gains standard_version_reviewed rather than pre-adopting.
Separately, gate-house corrected GH-DEC-2026-008: the claim/decision digest
comparison it originally required is unimplementable and a fail-closed
consumer obeying it would have denied permanently. We had never copied the
wording, so nothing to unwind -- but everything they have sent about this lane
was living in an inbox thread, a bad home for a correction that only matters
when someone finally wires the consume. Now wiki/ApprovalConsumption.md,
leading with "nothing is wired", carrying the corrected target and the
attribution gap that digest matching does not discharge.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EPuTc18FjU5WFqoSEKH3C
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1276224@bnt-lap001
Assistant-Session: 426ec497-e1c4-4dd3-b417-dfce1ca1dbc3
2026-09-10 08:02:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- classification coverage (v0.8 §6.4 obligation 3) -------------------------
|
|
|
|
|
|
|
|
|
|
def test_published_coverage_equals_measured_coverage():
|
|
|
|
|
"""The published figure must equal what the repo actually measures.
|
|
|
|
|
|
|
|
|
|
ops-warden asked gate-house for §13.1's Coverage column and its figures are
|
|
|
|
|
that column's first entries, so their accuracy is ours to hold. The register
|
|
|
|
|
explicitly does not compute anyone's coverage, and a stale number beside a
|
|
|
|
|
marked cell is worse than a blank -- a blank at least reads as "not reported".
|
|
|
|
|
|
|
|
|
|
This is the same property that makes the stance map worth publishing (the map
|
|
|
|
|
equals PolicyConfig.failure_modes by test), applied one level up.
|
|
|
|
|
"""
|
|
|
|
|
import importlib.util
|
|
|
|
|
|
|
|
|
|
repo = Path(__file__).resolve().parents[1]
|
|
|
|
|
spec = importlib.util.spec_from_file_location(
|
|
|
|
|
"report_coverage", repo / "scripts" / "report_coverage.py"
|
|
|
|
|
)
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
|
|
|
|
published = yaml.safe_load((repo / "pep-stance.yaml").read_text())[
|
|
|
|
|
"classification_coverage"
|
|
|
|
|
]
|
|
|
|
|
measured = module.measure()
|
|
|
|
|
|
|
|
|
|
for population in ("signing_targets", "routing_lanes"):
|
|
|
|
|
assert published[population] == measured[population], population
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_unknown_cell_is_marked_as_a_declared_gap():
|
|
|
|
|
"""A non-conformant cell must say so where it is declared, not only in a review.
|
|
|
|
|
|
|
|
|
|
§11's marking obligation, which ops-warden argued for in the v0.6 round and
|
|
|
|
|
then acquired a marked cell under. If the cell is ever flipped to fail_closed
|
|
|
|
|
this test fails, which is the correct time to remove the marking.
|
|
|
|
|
"""
|
|
|
|
|
repo = Path(__file__).resolve().parents[1]
|
|
|
|
|
text = (repo / "pep-stance.yaml").read_text()
|
|
|
|
|
stance = yaml.safe_load(text)["stance"]
|
|
|
|
|
|
|
|
|
|
if stance["unknown"] == "fail_open":
|
|
|
|
|
assert "DECLARED GAP" in text
|
|
|
|
|
assert "WARDEN-WP-0040" in text
|
|
|
|
|
else:
|
|
|
|
|
assert stance["unknown"] == "fail_closed"
|