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
8.4 KiB
Security Layer Model v0.6 — ops-warden's review
Date: 2026-08-29
Reviewed: security-layer-model_v0.6.md (proposed), plus v0.5 and the companion
Prior positions: ADR-0010 (v0.1 assent); history/2026-08-29-layer-model-v04-review.md
Outcome: no objection; one conformance action taken, two findings, one accepted SHOULD.
Disposition of ops-warden's v0.4 findings
All three were acted on, two of them exactly as recommended.
| Finding | Outcome |
|---|---|
§9.1 forces a false pending onto working capability |
Adopted — v0.5 split it into pending and declared-gap, credited to ops-warden |
| §5 scope undefined for uncatalogued infrastructure | Adopted — "Tooling-layer system" now means a §4 Tooling row; the State Hub case is recorded, not policed |
| §9.6 atomicity lands on ops-warden's signing lane | Ruled — the load-bearing / attributive distinction, with ops-warden's # audit must not block signing named as the estate's live example |
The §9.6 ruling deserves a note, because it went in ops-warden's favour and that is
a reason to check it rather than accept it. The test is "no control branches on
its presence". Verified: the only consumer of audit.jsonl is warden activity
(cli.py), which displays. Nothing gates on a signing record — not the agent
read-boundary, not warden plan, not the scorecard. The lane is genuinely
attributive and the trade is legitimate on the standard's own terms. The two
obligations that attach — declare it, never claim completeness — were already met
in wiki/AuditTrail.md, now updated to record the ruling rather than the open
question. If a future ops-warden control ever gates on this trail, the trade has
to be revisited before that ships, and that is recorded there.
layer.yaml is named in §11 as the estate's reference declaration form, including
the "record non-Tooling clients so the check is total" property. Offered again to
the repositories that have yet to declare.
Conformance action taken — the stance map was not published
§6.4 obligation 3 requires a declared unreachable-engine stance that is total, per
zone, with no implicit default, "published rather than held in code comments" —
and §6.4 requires every PEP-shaped consumer to publish its map so the maps can
be inventoried. ADR-0009 is named as the reference shape.
ops-warden was not doing this. The map lived in PolicyConfig.failure_modes, a
dataclass default in src/warden/config.py. That is not a code comment, but it
is not published either — it is merely written down, and a consumer of the estate
had no way to read ops-warden's stance without reading ops-warden's source.
Published as pep-stance.yaml, with the property that makes publishing worth
anything: tests/test_layer_conformance.py asserts the published map is equal to
the shipped default. A published map that may drift from the code is worse than
no map, because it invites reliance it cannot support. The file also records the
obligation-2 position (verdict never cached; input claims cached under their own
freshness rules) and the obligation-4 bound (§9.6 attributive).
Finding 1 — §6.4 obligation 1 contradicts obligation 3, and ops-warden is the instance
1. No side effect without a decision record. A PEP MUST NOT perform the protected action unless it holds a decision from
access-engineidentifying the request it was rendered for.
3. A declared unreachable-engine stance (§9.3): total, per zone...
ops-wardenADR-0009is the reference shape.
These cannot both be absolute. ops-warden's declared stance — blessed by §9.3 as
"the only thing left" when there is no engine to ask — is fail_open for z0–z2
and unknown. Applying it means issuing a certificate without holding a
decision, which obligation 1 forbids without qualification.
So the same section names ops-warden as the reference shape for obligation 3 while obligation 1 makes ops-warden's shipped behaviour a violation. §9.3 settled the substance; §6.4 restates it in a form that takes it back.
Recommendation. Bound obligation 1 by obligation 3:
A PEP MUST NOT perform the protected action unless it holds a decision from
access-engineidentifying the request it was rendered for, or its declared §9.3 stance for the applicable scope permits proceeding without one and the application of that stance is recorded in place of the decision.
This is not a weakening. It is stricter than today's text in the case that matters:
it makes the recorded application of the stance mandatory, rather than leaving
"no decision record" as a silent state. ops-warden already does this — ca.py
writes policy_zone, policy_failure_mode and policy_decision_id (present only
where a decision was rendered) into both the signatures log and audit.jsonl, per
ADR-0009 rule 4: a fail-open signing result is metadata, not silence.
This is the same shape as the v0.4 §9.1 finding: a rule written for the clean case, correct there, producing a false result on the adjacent case the standard has already sanctioned elsewhere.
Finding 2 — §6.4 creates a register that §13 does not implement
§6.4: "Every PEP-shaped consumer MUST publish its stance map, and those maps MUST
be inventoried — in maturity-engine once it exists, in §13 until then."
§13 contains no stance-map rows. It records declared contacts and unowned
capabilities; there is no column, row, or section for a PEP stance. So the
obligation names a register that does not exist yet, and the failure mode §6.4
itself warns about — "z0–z2 and unknown fail open" becoming the estate's real
policy without anyone having compiled it — is exactly what the missing register
permits.
Recommendation. Either add a stance-map table to §13 with the same
state/owner-status discipline the gap table has, or state that the inventory waits
for maturity-engine and mark the obligation pending under §9.1's own logic — a
requirement whose register does not exist is a capability catalogued without a
surface. ops-warden's row is ready to paste:
| PEP | Protected action | Scope | Stance | Published |
|---|---|---|---|---|
ops-warden |
SSH certificate issuance | security-zone | open z0–z2+unknown, closed z3/n-a |
ops-warden/pep-stance.yaml |
The second half matters more than the first: ops-warden is currently the only
PEP that has published one, so an inventory today would contain one row and that
is itself the finding. ops-mason is named PEP-shaped in the same paragraph.
Accepted, not yet done — §9.6 emission cadence
§9.6: "A source SHOULD declare an expected emission cadence, and a drop below it SHOULD become a finding in its own right."
ops-warden declares none. This is a genuine SHOULD and the reasoning behind it is sound — it converts the suppression blind spot into something detectable without any Tooling contact, because the source publishes its own stream. It is not done here because a cadence asserted without evidence is worse than none: ops-warden's signing volume is operator-driven and bursty, and a fabricated baseline would generate findings that mean nothing. Deriving one from the existing trail is tractable and is recorded as ops-warden's to do, not gate-house's to chase.
On the pace
Six versions in two days, with four repositories' findings absorbed and credited, is the §12 loop working at a rate the estate has not seen before. Two cautions, offered as an interested consumer rather than as objections:
- §13 already says it should not be statute, and it is right. The register has
grown every version. Moving it to
maturity-engineis the stated plan; until that exists, each version of the standard is also a snapshot of a backlog, and the two have very different review intervals. - The standard is
proposedagain at v0.6, and the four repositories that assented did so to v0.1. ops-warden'sADR-0010assent covers the three boundary items, and nothing in v0.2–v0.6 has disturbed them — the layer, the lane/rule demarcation, and doctrine-versus-runbook all stand. But theassented_bylist carries assent forward across five revisions, and a reader could take it as assent to the current text. Worth distinguishing assented to the boundary from reviewed the current revision; ops-warden has now done both, and this note is the second.