Make the risk grade fail safe, and gate CI on absence
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

The mechanism behind RISK-F-0003 was sharper than the finding described.
is_high_risk was risk == "high", but risk was never absent at the model layer:
RouteEntry.risk carried a dataclass default of "standard". An omitted grade was
not unhandled, it was actively resolved to the permissive value — fail-open by
construction, which is why nothing ever warned.

The default is now "ungraded" and is_high_risk returns true for anything outside
an explicit low-risk vocabulary (standard / low / accepted). An omitted grade and
an unrecognised grade from a newer catalog both resolve to high, so the boundary
fails safe in both directions rather than reading an unknown value as permission.

test_every_repo_catalog_lane_is_explicitly_graded is the CI gate that stops an
ungraded lane being committed, per ADR-0007: absence is not a grade.

"accepted" is in the low-risk vocabulary deliberately, ready for the
maturity-derived default — an experimental-context lane may be explicitly
accepted, which is a graded decision rather than an omission.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-20 01:13:35 +02:00
parent ac85259c20
commit d0d4f9d8fc
4 changed files with 110 additions and 5 deletions

View file

@ -709,3 +709,62 @@ def test_cli_route_show_includes_delegation(repo_catalog_env):
assert data["delegation"]["mode"] == "interim"
assert data["delegation"]["intended_owner"] == "tenant-engine"
assert data["delegation"]["implicit"] is False
# --- ADR-0007: absence is not a grade (WARDEN-WP-0032-T06) ------------------
def _bare_entry(**overrides):
"""A minimal RouteEntry, so these tests exercise defaults and nothing else."""
from warden.routing.models import RouteEntry
fields = dict(
id="x",
title="t",
need_keywords=[],
owner_repo="r",
subsystem="s",
warden_executes=False,
wiki_ref="w",
canon_ref="c",
reviewed="2026-08-20",
status="active",
)
fields.update(overrides)
return RouteEntry(**fields)
def test_every_repo_catalog_lane_is_explicitly_graded():
"""The CI gate. A lane added without a `risk` grade is a defect (ADR-0007)."""
catalog = load_catalog(_repo_catalog())
ungraded = sorted(e.id for e in catalog.entries if not e.is_graded)
assert ungraded == [], (
f"{len(ungraded)} catalog lane(s) carry no explicit risk grade: {ungraded}. "
"ADR-0007: absence is not a grade — grade the lane on merit."
)
def test_ungraded_lane_fails_safe_to_high_risk():
"""RISK-F-0003 regression: an omitted grade must not wave a lane through.
Before ADR-0007 the dataclass default was "standard", so a lane that simply
omitted the field landed outside the agent read-boundary silently.
"""
entry = _bare_entry()
assert entry.risk == "ungraded"
assert entry.is_graded is False
assert entry.is_high_risk is True
def test_unrecognised_grade_is_treated_as_high():
"""A grade from a newer catalog must not be read as permission."""
entry = _bare_entry(risk="spicy")
assert entry.is_high_risk is True
assert entry.is_graded is False
def test_low_risk_vocabulary_is_explicit():
for grade in ("standard", "low", "accepted"):
entry = _bare_entry(risk=grade)
assert entry.is_high_risk is False, grade
assert entry.is_graded is True, grade