ops-warden/wiki/ApprovalConsumption.md
tegwick 5b1a508610
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
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

77 lines
3.8 KiB
Markdown

# Approval consumption — notes for the signing lane's future consume
**Status:** notes only. **Nothing is wired.** ops-warden's pre-sign gate consults
flex-auth (`wiki/PolicyGatedSigning.md`); it does not yet consume an approval
artifact. This page exists so the corrections we have already been given survive
until it does, rather than sitting in an inbox thread.
Owner of the contract: `gate-house`, `docs/contracts/approval-consumption.md`.
ops-warden is one of two PEP-shaped consumers named there (the SSH signing lane).
## Validate across two artifacts, never one
`GH-DEC-2026-005` settles the step-1 artifact: `GET /v1/approvals/{id}/claim`
serves the **approval-claim**. flex-auth's proposed `ActionAuthorization` is not
required there and will not be served from that endpoint.
| Artifact | Carries |
| --- | --- |
| approval-claim (step 1) | the approval fact — binding digest, validity window, consumption state, freshness, issuer |
| `DecisionEnvelope` (step 2) | exact `CheckRequest` match, policy pin |
v0.8 §6.4 obligation 5 hardened this: each artifact **must** be validated against
the layer that owns its data, and a PIP **must not** republish the PDP's decision.
Neither artifact may be taken from the other.
## Three things that are easy to get wrong
**1. Do not require `provenance.authority == 'state-hub'`.**
State Hub is a read model with no runtime approval authority. This is easy to get
wrong precisely *because* the hub is where everything else about a workplan lives,
so a consumer reaches for it as the authority on approval too.
Worth holding as a shape rather than an instance: a PEP requiring the hub to have
asserted an approval is the same boundary violated from the **consuming** side
that "never register workplans or tasks by hand" forbids from the **writing**
side. Same rule, two directions.
**2. The digest comparison target was corrected. Use `binding.approval_binding_digest`.**
`GH-DEC-2026-008` as originally issued required the claim's `pdp_digest` to equal
the decision's `request_digest`. That is unimplementable — a claim travelling
inside a hashed request cannot name the digest of the request containing it — and
a fail-closed consumer obeying it would have denied permanently. Corrected by
`FLEX-DEC-2026-007` to the PDP's published **exclusion-scoped** digest,
`binding.approval_binding_digest`.
ops-warden never copied the superseded wording anywhere (checked 2026-09-09), so
there was nothing to unwind. Recorded here so it cannot be reintroduced from an
older reading of the contract.
**3. Digest comparison does not discharge attribution, and attribution is not
available today.**
`GH-DEC-2026-010` rules that a PEP must be able to **attribute** a decision to
access-engine, and that matching digests does not achieve that. flex-auth's
envelope is unsigned and its pins serve plain HTTP, so that half is a declared
§13 gap owned by access-engine until `FLEX-WP-0024` lands.
Nothing for ops-warden to build. But when this lane is wired, its validation is
**complete-minus-attribution** and must be described that way: fail-closed
protects against a decision point that is absent, not against one that lies.
## Open dependency
`WARDEN-WP-0039-T03` — whether an admitted flex-auth contract exists for a
delegated credential read where caller and resource owner differ. If the answer
changes the caller shape it may reach how this lane's consume is built, and
`gate-house` has asked to hear that before rather than after: a contract admitted
for that shape would reach §6.4 obligation 5, and whether such a delegation
attenuates or widens is doctrine they own.
## See also
- `wiki/PolicyGatedSigning.md` — the pre-sign gate as it actually is today
- `pep-stance.yaml` — unreachable-engine stance, and the marked `unknown` cell
- `history/2026-09-09-layer-model-v08-review.md` — the v0.8 assent round