key-cape corrected two blockers that had stopped being true after our 2026-08-28 source-read: - rapp-qonto-keycape-client: `keycape service-token` (2026-09-05) is the native exchange the blocker recorded as absent, and `keycape verify-client` (2026-09-08) is rotation step 3 as one command. Narrowed to steps 1-2 -- successor generation and the CAS write -- rather than cleared, as they asked. rotation.automatable -> false so a future executable driver is not told a lane with no admitted custody transport is drivable; the per-step truth moves into the steps. - key-cape-oidc-login: ownership ACCEPTED by key-cape, so verified moves from asked-and-waiting to owner-confirmed. Lane stays interim -- acceptance covers the identity half, while the fetch_command yields an OpenBao token whose mount, role mapping and enforcement are not key-cape's. Two tests pinned `key-cape-oidc-login` to sitting `asked-and-waiting`, so answering the question broke them -- they failed on good news. Both now assert the property instead: an unverified blocker is stale regardless of date, over whatever lanes are in that state. Verifying the routing answer against our own front door turned up a defect: `warden plan` returns `autonomous` for a custody *write* and answers it with read transports, because it has no read-versus-mutate intent. Recorded as WARDEN-WP-0038 (proposed) -- the WP-0033-T06 shape, as a class this time. 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
71 lines
2.9 KiB
Markdown
71 lines
2.9 KiB
Markdown
---
|
|
id: WARDEN-WP-0038
|
|
type: workplan
|
|
title: "warden plan must distinguish reading a secret from mutating one"
|
|
domain: infotech
|
|
repo: ops-warden
|
|
status: proposed
|
|
owner: ops-warden
|
|
topic_slug: netkingdom
|
|
planning_priority: P1
|
|
depends_on_workplans:
|
|
- WARDEN-WP-0029
|
|
created: "2026-09-08"
|
|
updated: "2026-09-08"
|
|
---
|
|
|
|
# WARDEN-WP-0038 — Read versus mutate intent in the policy front door
|
|
|
|
## The defect
|
|
|
|
`warden plan` scores a need against catalog lanes by keyword overlap and returns
|
|
a verdict. It has no notion of what the caller wants to *do*. So a need that
|
|
says *generate a successor secret and CAS-write it to two custodians* matches the
|
|
lane that **reads** that path, scores well on the shared nouns, and returns:
|
|
|
|
```
|
|
verdict: autonomous
|
|
commands: warden access <lane> --exec / --out / --wrap
|
|
```
|
|
|
|
Three read transports offered as the answer to a write, under the verdict that
|
|
means "proceed without the founder". Reproduced 2026-09-08 against
|
|
`rapp-qonto-keycape-client` (`ADHOC-2026-09-08-T04`).
|
|
|
|
## Why this is worse than a bad suggestion
|
|
|
|
`warden plan` is the documented first step before drafting any founder credential
|
|
step (`.claude/rules/credential-routing.md`), and `autonomous` is consumed as an
|
|
authorization signal by agents that have no other check. `WARDEN-WP-0029` shipped
|
|
the verdict *because* instinct was not auditable; a verdict that is confidently
|
|
wrong in the permissive direction is worse than no verdict.
|
|
|
|
key-cape hit this and refused to rely on it — "we are not treating a generic
|
|
match as authorization" — which is the only reason it surfaced as a design
|
|
question rather than as an unadmitted custody mutation. The control that caught
|
|
it was a counterparty's caution. That is not a control.
|
|
|
|
`WARDEN-WP-0033-T06` is the same shape (rapp-postgres, generic
|
|
`openbao-api-key` match → `paste_once_provision` for an act that could not work)
|
|
and was fixed by adding one exact lane plus one regression. That fixed the
|
|
instance. This is the class.
|
|
|
|
## Shape of the fix (to be designed, not assumed)
|
|
|
|
- Classify need intent — read / mutate / establish — before scoring, and never
|
|
answer a mutate need with a read lane's transports.
|
|
- A mutate need against a lane ops-warden does not own is `unroutable` with a CCR
|
|
stub naming the custody owner, not `autonomous`. `ADR-0003`: cover gaps, never
|
|
silently own them; a permissive verdict on someone else's custody act is a
|
|
silent claim of ownership.
|
|
- Consider whether `rotation.steps` should be reachable from `plan` at all, given
|
|
they are advisory prose and several are explicitly not automatable.
|
|
- Regression from the reproduced case, in the manner of T06.
|
|
|
|
## Open question for design
|
|
|
|
Whether intent belongs in the matcher or in the lane. A lane could declare which
|
|
acts it admits (`read`, `rotate`, `provision`) and `plan` could refuse any act a
|
|
lane does not declare — stricter, and it fails closed on lanes that say nothing,
|
|
which is the majority today. That is a catalog schema change and needs its own
|
|
argument.
|