ops-warden/tests/test_case_admission_modes.py
tegwick a14376cf62 feat(case): KERNEL-2/3 lane resolution and mode-aware admission; CATALOG-1 draft lanes
nk48 1.3 owner review (owner-review-ops-warden.json):
- KERNEL-2: Executor.admit parses catalog_source and resolves descriptor.lane;
  refuses a missing, non-active, undelegated or ungraded lane, an owner outside
  owner_repo, and a mint/change effect on a lane not graded risk high.
  catalog-routing-acceptance still covers the whole catalog digest.
- KERNEL-3: dispatch mode is read from the type's follow_through step
  (warden_executes/attended). The step must declare both booleans and a lane
  and agree with the descriptor's lane and owner. Attended: human actor,
  WARDEN_AGENT_ID unset, parent-session revocation scope, caller lane equal to
  the step lane. Owner: native delegation, owner_repo == descriptor.owner, no
  Warden-executed lane and no Warden-brokered mint.
- CATALOG-1: draft entries keycape-attended-case-change (OW-1.3-C1),
  platform-secret-delivery, consumer-owner-deployment, apps-consumer-handover.
  Not promoted; promotion waits for owner confirmation.
- Fixture catalogs and type steps in tests; published routing_lanes coverage
  and the workload-join count follow the four new not-applicable lanes.

PARAMETER_PINS untouched (PIN-1 waits for founder acknowledgment).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 903097@bnt-lap001
Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23
2026-10-17 12:08:49 +02:00

135 lines
5.1 KiB
Python

"""KERNEL-2 lane resolution and KERNEL-3 mode-aware admission (fixture only)."""
from dataclasses import asdict, replace
import json
import pytest
import yaml
from warden.case_receipt_state import Refused
from warden.case_run import Descriptor, digest
from tests.test_case_run import MINT_STEP, fixture_catalog, lane_entry, make_rig
@pytest.fixture
def rig(tmp_path):
return make_rig(tmp_path)
EVIDENCE = {"acceptance", "materialization", "descriptor", "session"}
def admit(rig, *, step=None, entries=None, catalog=None, desc=None, **over):
executor, _, _, sources, kwargs = rig
case, typ, descriptor, _ = sources
if desc is not None:
base = Descriptor.decode(descriptor)
descriptor = json.dumps(asdict(replace(base, **desc))).encode()
doc = yaml.safe_load(typ)
doc["spec"]["execution_descriptors"] = {"mint-secret": digest(descriptor)}
if step is not None:
doc["spec"]["follow_through"] = step
typ = yaml.safe_dump(doc).encode()
if catalog is None:
catalog = fixture_catalog(*(entries or [lane_entry()]))
executor.evidence_verifier = lambda purpose, source, evidence: evidence in EVIDENCE
return executor.admit(case, typ, descriptor, catalog, **{**kwargs, **over})
def mode_step(executes, attended):
return [dict(MINT_STEP, warden_executes=executes, attended=attended)]
def refused(message, rig, **kw):
with pytest.raises(Refused, match=message):
admit(rig, **kw)
# Positive modes
def test_warden_mode_admits(rig):
assert admit(rig)[1].lane == "issuer-lane"
def test_attended_mode_admits_human_parent_session(rig, monkeypatch):
monkeypatch.delenv("WARDEN_AGENT_ID", raising=False)
admitted = admit(
rig,
step=mode_step(False, True),
desc=dict(effect="change", revocation_scopes=("parent-session",)),
)
assert admitted[1].actor == "human"
def test_owner_mode_admits_native_owner_lane(rig):
admitted = admit(rig, step=mode_step(False, False), desc=dict(effect="change"))
assert admitted[1].owner == "owner"
# KERNEL-2
@pytest.mark.parametrize(
"entries, catalog, message",
[
(None, b"not: [a catalog", "invalid catalog source shape"),
([lane_entry("other-lane")], None, "descriptor lane is not in the catalog"),
([lane_entry(status="draft")], None, "descriptor lane is not active in the catalog"),
([{k: v for k, v in lane_entry().items() if k != "delegation"}], None,
"descriptor lane has no catalog delegation record"),
([{k: v for k, v in lane_entry().items() if k != "risk"}], None,
"descriptor lane carries no risk grade"),
([lane_entry(owner="someone-else")], None,
"descriptor owner does not fit catalog lane owner_repo"),
([lane_entry(risk="standard")], None,
"catalog lane risk does not cover the descriptor effect"),
],
)
def test_catalog_lane_refusals(rig, entries, catalog, message):
refused(message, rig, entries=entries, catalog=catalog)
# KERNEL-3: the step must declare dispatch and agree with the descriptor
@pytest.mark.parametrize(
"step, message",
[
([], "type step does not declare how the kernel dispatches it"),
(mode_step(True, True), "type step does not declare how the kernel dispatches it"),
([{k: v for k, v in MINT_STEP.items() if k != "lane"}],
"type step does not declare how the kernel dispatches it"),
([dict(MINT_STEP, warden_executes="yes")],
"type step does not declare how the kernel dispatches it"),
([dict(MINT_STEP, lane="other-lane")], "type step lane or owner disagrees with descriptor"),
([dict(MINT_STEP, owner="other")], "type step lane or owner disagrees with descriptor"),
],
)
def test_step_declaration_refusals(rig, step, message):
refused(message, rig, step=step)
def test_attended_refuses_agent_session(rig, monkeypatch):
monkeypatch.setenv("WARDEN_AGENT_ID", "agent-1")
refused("attended step requires a human parent session", rig,
step=mode_step(False, True), desc=dict(effect="change"))
def test_attended_refuses_agent_actor(rig):
refused("attended step requires a human parent session", rig,
step=mode_step(False, True), desc=dict(effect="change", actor="agent"),
actor="agent")
def test_attended_refuses_caller_lane_other_than_step_lane(rig):
refused("principal actor or lane mismatch", rig,
step=mode_step(False, True), desc=dict(effect="change"), lane="login-lane")
@pytest.mark.parametrize(
"entry, desc",
[
(lane_entry(delegation={"mode": "interim", "intended_owner": "owner",
"blocked_on": "fixture", "reviewed": "2026-10-17"}),
dict(effect="change")),
(lane_entry(owner="owner/other"), dict(effect="change")),
(lane_entry(warden_executes=True), dict(effect="change")),
(lane_entry(), {}), # owner step may not be a Warden-brokered mint
],
)
def test_owner_mode_refusals(rig, entry, desc):
refused("owner step lane is not the owner's native path", rig,
step=mode_step(False, False), entries=[entry], desc=desc)