2026-08-29 13:02:51 +02:00
|
|
|
|
"""TEN-WP-0011-T01/T02: layer declaration and published PEP stance."""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import importlib.util
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
|
|
from tenant_engine.stance import published_stance, shipped_stance
|
|
|
|
|
|
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
SCRIPT = ROOT / "scripts" / "check_layer_conformance.py"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _run(*args: str) -> subprocess.CompletedProcess[str]:
|
|
|
|
|
|
return subprocess.run([sys.executable, str(SCRIPT), *args], capture_output=True, text=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_layer_yaml_declares_engine_pip():
|
|
|
|
|
|
data = yaml.safe_load((ROOT / "layer.yaml").read_text())
|
|
|
|
|
|
assert data["repository"] == "tenant-engine"
|
|
|
|
|
|
assert data["layer"] == "engine"
|
|
|
|
|
|
assert data["role"] == "pip"
|
2026-09-21 07:35:01 +02:00
|
|
|
|
assert data["derived"] is True
|
|
|
|
|
|
assert data["derived_from"] == "INTENT.md"
|
2026-08-29 13:02:51 +02:00
|
|
|
|
assert data["tooling_contacts"] == []
|
|
|
|
|
|
assert data["pep_stance"] == "pep-stance.yaml"
|
|
|
|
|
|
assert data["pip_claims"] == "pip-claims.yaml"
|
|
|
|
|
|
ids = {c["id"] for c in data["non_tooling_clients"]}
|
|
|
|
|
|
assert "postgres-own-store" in ids
|
|
|
|
|
|
assert "sqlite-dev-store" in ids
|
|
|
|
|
|
assert "access-engine-check" in ids
|
|
|
|
|
|
assert "state-hub-work-records" in ids
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-21 07:35:01 +02:00
|
|
|
|
def _intent() -> dict:
|
|
|
|
|
|
return yaml.safe_load((ROOT / "INTENT.md").read_text().split("---", 2)[1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _fold(value: object) -> str:
|
|
|
|
|
|
return str(value).translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
|
|
|
|
"abcdefghijklmnopqrstuvwxyz"))
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-08-29 13:02:51 +02:00
|
|
|
|
def test_intent_frontmatter_agrees_with_layer_yaml():
|
2026-09-21 07:35:01 +02:00
|
|
|
|
"""A11/A9: the derived form agrees with the governing one after a case-fold.
|
|
|
|
|
|
|
|
|
|
|
|
A fold, not an equality: INTENT.md says Engine and layer.yaml says engine,
|
|
|
|
|
|
and neither is re-spelled. A real divergence still fails.
|
|
|
|
|
|
"""
|
|
|
|
|
|
intent = _intent()
|
2026-08-29 13:02:51 +02:00
|
|
|
|
decl = yaml.safe_load((ROOT / "layer.yaml").read_text())
|
2026-09-21 07:35:01 +02:00
|
|
|
|
assert _fold(intent["layer"]) == _fold(decl["layer"])
|
|
|
|
|
|
assert _fold(intent["role"]) == _fold(decl["role"])
|
|
|
|
|
|
assert _fold(intent["layer"]) in {"taxonomy", "tooling", "engine", "staff"}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-21 09:38:22 +02:00
|
|
|
|
def _checker():
|
|
|
|
|
|
spec = importlib.util.spec_from_file_location("check_layer_conformance", SCRIPT)
|
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
|
assert spec.loader is not None
|
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
return module
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-21 07:35:01 +02:00
|
|
|
|
def test_no_declaration_carries_a_standard_version():
|
2026-09-21 09:38:22 +02:00
|
|
|
|
"""A12 r2 / GH-DEC-2026-020: no version in any key or value of either form."""
|
|
|
|
|
|
checker = _checker()
|
2026-09-21 07:35:01 +02:00
|
|
|
|
assert "standard_version" not in yaml.safe_load((ROOT / "layer.yaml").read_text())
|
|
|
|
|
|
assert "standard_version" not in _intent()
|
2026-09-21 09:38:22 +02:00
|
|
|
|
assert checker.version_findings(yaml.safe_load((ROOT / "layer.yaml").read_text()),
|
|
|
|
|
|
"layer.yaml") == []
|
|
|
|
|
|
assert checker.version_findings(_intent(), "INTENT.md") == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_versioned_standard_path_or_companion_version_fails():
|
|
|
|
|
|
"""GH-DEC-2026-020 §1-§2: the pin is caught under any name, not only standard_version."""
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
base = {"layer": "Engine", "role": "PIP"}
|
|
|
|
|
|
bad = [
|
|
|
|
|
|
{**base, "standard": "net-kingdom/canon/standards/security-layer-model_v0.7.md"},
|
|
|
|
|
|
{**base, "companion_version": "0.2"},
|
|
|
|
|
|
{**base, "companion": "net-kingdom/SECURITY-COMPANION.md v0.2"},
|
|
|
|
|
|
{**base, "standard_version": "0.8"},
|
2026-09-21 13:05:05 +02:00
|
|
|
|
{**base, "standard": "security-layer-model v0.7"}, # 021 §3 addition
|
|
|
|
|
|
{**base, "ref": "net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md"},
|
2026-09-21 09:38:22 +02:00
|
|
|
|
]
|
|
|
|
|
|
for data in bad:
|
|
|
|
|
|
assert checker.version_findings(data, "x"), data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_schema_version_comments_and_section_numbers_are_not_reached():
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
data = yaml.safe_load(
|
|
|
|
|
|
"# Framework: net-kingdom/canon/standards/security-layer-model_v0.7.md\n"
|
|
|
|
|
|
"schema_version: '0.1'\nstandard: netkingdom-security-layer-model\n"
|
|
|
|
|
|
"declared_shapes: {'5.1': [], '5.2': []}\ndeclared_at: '2026-08-29'\n"
|
2026-09-21 13:05:05 +02:00
|
|
|
|
"notes: [{note: 'Outside §5 by the v0.7 scope rule.'}]\n")
|
2026-09-21 09:38:22 +02:00
|
|
|
|
assert checker.version_findings(data, "x") == []
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-21 13:05:05 +02:00
|
|
|
|
def test_prose_citation_is_not_a_pin():
|
|
|
|
|
|
"""GH-DEC-2026-021 §1: a revision cited in prose is provenance, not reached."""
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
for data in ({"notes": [{"note": "Outside §5 by the v0.7 scope rule."}]},
|
|
|
|
|
|
{"role_note": "Per Security Layer Model v0.8 §11 and companion v0.2."}):
|
|
|
|
|
|
assert checker.version_findings(data, "x") == [], data
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-21 09:38:22 +02:00
|
|
|
|
def test_checker_does_not_apply_a12_to_stance_or_claims_maps():
|
|
|
|
|
|
"""A12 r2 / GH-DEC-2026-020 §3: those files keep their version and are not read for it."""
|
|
|
|
|
|
for name in ("pep-stance.yaml", "pip-claims.yaml"):
|
|
|
|
|
|
assert "standard_version" in yaml.safe_load((ROOT / name).read_text())
|
|
|
|
|
|
assert _run().returncode == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_every_run_states_version_and_scope(tmp_path):
|
|
|
|
|
|
"""GH-DEC-2026-020 §4: the version lives in the run, on success and on failure."""
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
ok = _run()
|
|
|
|
|
|
assert ok.stdout.count(checker.VALIDATED_AGAINST) >= 2 # header and OK line
|
|
|
|
|
|
assert "scope:" in ok.stdout
|
|
|
|
|
|
report = _run("--report")
|
|
|
|
|
|
assert checker.VALIDATED_AGAINST in report.stdout and "scope:" in report.stdout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_failing_run_still_states_version_and_scope(tmp_path, monkeypatch, capsys):
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
fake = tmp_path / "INTENT.md"
|
|
|
|
|
|
fake.write_text("---\nlayer: Engine\nrole: PIP\n"
|
|
|
|
|
|
"standard: net-kingdom/canon/standards/security-layer-model_v0.7.md\n---\n")
|
|
|
|
|
|
monkeypatch.setattr(checker, "INTENT", fake)
|
|
|
|
|
|
monkeypatch.setattr(sys, "argv", ["check_layer_conformance.py"])
|
|
|
|
|
|
try:
|
|
|
|
|
|
checker.main()
|
|
|
|
|
|
except SystemExit as exc:
|
|
|
|
|
|
assert exc.code == 2
|
|
|
|
|
|
else: # pragma: no cover
|
|
|
|
|
|
raise AssertionError("a versioned standard: path must fail")
|
|
|
|
|
|
out = capsys.readouterr()
|
|
|
|
|
|
assert checker.VALIDATED_AGAINST in out.out and "scope:" in out.out
|
2026-10-11 02:21:25 +02:00
|
|
|
|
assert "value carries a version" in out.err
|
2026-09-21 07:35:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_checker_rejects_a_divergence_that_survives_the_fold(tmp_path, monkeypatch):
|
|
|
|
|
|
spec = importlib.util.spec_from_file_location("check_layer_conformance", SCRIPT)
|
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
|
assert spec.loader is not None
|
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
fake = tmp_path / "INTENT.md"
|
|
|
|
|
|
fake.write_text("---\nlayer: Staff\nrole: PIP\n---\n")
|
|
|
|
|
|
monkeypatch.setattr(module, "INTENT", fake)
|
|
|
|
|
|
try:
|
|
|
|
|
|
module.intent_frontmatter()
|
|
|
|
|
|
except SystemExit as exc:
|
|
|
|
|
|
assert exc.code == 2
|
|
|
|
|
|
else: # pragma: no cover
|
|
|
|
|
|
raise AssertionError("a Staff INTENT.md must not pass as Engine")
|
2026-08-29 13:02:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_checker_passes_on_the_real_tree():
|
|
|
|
|
|
result = _run()
|
|
|
|
|
|
assert result.returncode == 0, result.stderr + result.stdout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_checker_catches_an_undeclared_openbao_client(tmp_path, monkeypatch):
|
|
|
|
|
|
spec = importlib.util.spec_from_file_location("check_layer_conformance", SCRIPT)
|
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
|
assert spec.loader is not None
|
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
|
|
|
|
|
|
fake_src = tmp_path / "src" / "tenant_engine"
|
|
|
|
|
|
fake_src.mkdir(parents=True)
|
|
|
|
|
|
(fake_src / "vault.py").write_text("import hvac\n")
|
|
|
|
|
|
monkeypatch.setattr(module, "SRC", fake_src)
|
|
|
|
|
|
hits = module.scan()
|
|
|
|
|
|
assert hits
|
|
|
|
|
|
assert any(h[1] == "hvac" for h in hits)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_published_stance_equals_shipped_behaviour():
|
|
|
|
|
|
assert published_stance() == shipped_stance()
|
|
|
|
|
|
assert set(shipped_stance()) == {"unset", "unreachable", "non_allow", "unknown"}
|
|
|
|
|
|
assert set(shipped_stance().values()) == {"fail_closed"}
|
2026-10-11 02:21:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_accepted_v08_run_provenance_and_catalog_scope():
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
assert "security-layer-model_v0.8.md" in checker.VALIDATED_AGAINST
|
|
|
|
|
|
assert "sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b" in checker.VALIDATED_AGAINST
|
|
|
|
|
|
assert "single repository tenant-engine (catalogued in §4)" in checker.SCOPE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_reference_semantics_propagate_identity_through_nested_sequences():
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
for data in (
|
|
|
|
|
|
{"nested": {"standard": ["Security Layer Model v0.8"]}},
|
|
|
|
|
|
{"companion": [["Security Companion 0.3"]]},
|
|
|
|
|
|
{"standard": {"alternate": ["Security Layer Model v0.7"]}},
|
|
|
|
|
|
):
|
|
|
|
|
|
# A dictionary begins a fresh identity key; only sequence descendants
|
|
|
|
|
|
# inherit the immediate standard:/companion: identity in the reference.
|
|
|
|
|
|
if isinstance(data.get("standard"), dict):
|
|
|
|
|
|
assert checker.find_version_pins(data) == []
|
|
|
|
|
|
else:
|
|
|
|
|
|
assert checker.find_version_pins(data), data
|
|
|
|
|
|
assert checker.find_version_pins({"rationale": "Assented at v0.7; migrated at v0.8."}) == []
|
|
|
|
|
|
assert checker.find_version_pins({"intent_version": "0.2.0", "schema_version": "0.1"}) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ascii_folding_never_discards_non_ascii_letters():
|
|
|
|
|
|
checker = _checker()
|
|
|
|
|
|
assert checker._fold("Staff") == "staff"
|
|
|
|
|
|
assert checker._fold("Engine") == "engine"
|
|
|
|
|
|
# Deleting non-ASCII bytes would turn this fifth token into Staff.
|
|
|
|
|
|
assert checker._fold("Stαaff") not in checker.LAYER_VOCABULARY
|