docs: narrow qonto blocker, record key-cape lane ownership
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
2026-09-08 14:55:02 +02:00
|
|
|
---
|
|
|
|
|
id: WARDEN-WP-0038
|
|
|
|
|
type: workplan
|
|
|
|
|
title: "warden plan must distinguish reading a secret from mutating one"
|
|
|
|
|
domain: infotech
|
|
|
|
|
repo: ops-warden
|
2026-09-10 08:02:10 +02:00
|
|
|
status: finished
|
docs: narrow qonto blocker, record key-cape lane ownership
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
2026-09-08 14:55:02 +02:00
|
|
|
owner: ops-warden
|
|
|
|
|
topic_slug: netkingdom
|
|
|
|
|
planning_priority: P1
|
|
|
|
|
depends_on_workplans:
|
|
|
|
|
- WARDEN-WP-0029
|
|
|
|
|
created: "2026-09-08"
|
2026-09-10 08:02:10 +02:00
|
|
|
updated: "2026-09-09"
|
2026-09-08 14:57:42 +02:00
|
|
|
state_hub_workstream_id: "751ad530-e44b-52c4-b70e-cb47568a0179"
|
docs: narrow qonto blocker, record key-cape lane ownership
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
2026-09-08 14:55:02 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# 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.
|
2026-09-10 08:02:10 +02:00
|
|
|
|
|
|
|
|
## Confirmed independently, then fixed — 2026-09-09
|
|
|
|
|
|
|
|
|
|
`railiance-platform` hit the same defect from the other side while answering the
|
|
|
|
|
Qonto custody routing question (msg `7c7228ac`). Having stated that the correct
|
|
|
|
|
verdict is `founder_required` — attended OIDC via `netkingdom` `role=platform-admin`,
|
|
|
|
|
through the governed `openbao-platform-admin-login` lane — they added:
|
|
|
|
|
|
|
|
|
|
> Note this is the same verdict your `warden plan` should have returned; it
|
|
|
|
|
> returning `autonomous` and offering read transports for a write is a real
|
|
|
|
|
> defect and WARDEN-WP-0038 is the right place for it. Until that is fixed, do
|
|
|
|
|
> not let a plan result stand in for this answer.
|
|
|
|
|
|
|
|
|
|
Two counterparties reporting the same wrong verdict in two days, one of whom is
|
|
|
|
|
the write authority being wrongly bypassed, moved this from proposed to shipped.
|
|
|
|
|
|
|
|
|
|
### What shipped
|
|
|
|
|
|
|
|
|
|
`_need_intent()` classifies a need as `mutate` or `read` by token match, and
|
|
|
|
|
`_owns_write_authority()` tests `delegation.mode == "permanent"`. A mutating need
|
|
|
|
|
on a lane ops-warden does not permanently own can no longer reach any branch that
|
|
|
|
|
returns `autonomous`:
|
|
|
|
|
|
|
|
|
|
- lane declares a `rotation` → `founder_required`, with an `approve` act naming
|
|
|
|
|
the write owner, the rotation method, whether it is automatable, and
|
|
|
|
|
`warden rotate-guide` as guidance;
|
|
|
|
|
- lane declares none → `unroutable` with a CCR stub whose `owner_hint` names the
|
|
|
|
|
write authority.
|
|
|
|
|
|
|
|
|
|
**Commands are empty of read transports either way.** That is the half that made
|
|
|
|
|
the wrong verdict actionable rather than merely wrong: `--out`, `--exec`,
|
|
|
|
|
`--wrap` and a bare `bao kv get` were being offered as the answer to a write, and
|
|
|
|
|
a test now asserts none of them appears.
|
|
|
|
|
|
|
|
|
|
### The design question in the plan, answered by use
|
|
|
|
|
|
|
|
|
|
The plan asked whether intent belongs in the matcher or in the lane. It went in
|
|
|
|
|
the matcher, with the *ownership* test doing the work that a verb list cannot:
|
|
|
|
|
SSH certificate issuance is itself a mutating act, and `warden sign` must stay
|
|
|
|
|
`autonomous`. `delegation.mode: permanent` is what separates ops-warden's own
|
|
|
|
|
front door from someone else's custody — not the absence of a verb from a list.
|
|
|
|
|
A regression asserts the SSH lane still proceeds, because a guard that refuses
|
|
|
|
|
our own lane would be a worse defect than the one it fixes.
|
|
|
|
|
|
|
|
|
|
The stricter alternative — lanes declaring which acts they admit, `plan` refusing
|
|
|
|
|
anything undeclared — is not built. It is a catalog schema change that would fail
|
|
|
|
|
closed on the majority of lanes, which say nothing today, and it needs its own
|
|
|
|
|
argument rather than arriving as a side effect of this fix.
|
|
|
|
|
|
|
|
|
|
### Not claimed
|
|
|
|
|
|
|
|
|
|
This narrows what `warden plan` will assert; it does not make the tool an
|
|
|
|
|
authority on custody. railiance-platform's instruction stands and is the right
|
|
|
|
|
standing posture: a plan result does not substitute for the owner's answer.
|