--- 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" state_hub_workstream_id: "751ad530-e44b-52c4-b70e-cb47568a0179" --- # 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 --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.