ops-warden/workplans/WARDEN-WP-0033-native-lane-handoff.md
tegwick ee94c18938 WARDEN-WP-0033 finished — key-cape accepted the issuance question five days ago
T04 was the last open task, waiting on key-cape to accept or refuse ownership of
the coding-agent OpenBao issuance identity. They accepted, in KEY-WP-0009-T03,
on 2026-08-23: codex-railiance-platform is published in their
config/service-clients.example.yaml with subject service:codex:railiance-platform,
role coding-agent, scope openbao:login, 15m lifetime, and the service-auth
semantics in docs/openbao-service-auth-contract.md. The split is the one we
routed for — KeyCape issues, railiance-platform binds the OpenBao role, OpenBao
enforces, no secret value in either repo.

We found it by reading their repository. KEY-WP-0009-T04 records replying to
ops-warden; the inbox has zero messages from key-cape, read or unread. The task
sat `wait` on an answer that already existed.

That is T05's own lesson arriving on T04: a blocker is a claim about the world at
a date. So the same pass re-verified the two lanes pointing at key-cape against
their source instead of bumping dates:

- rapp-qonto-keycape-client -> verified: source-read. KEY-WP-0009-T02 did add
  bounded service-auth, but that is client_credentials JWT issuance for OpenBao
  machine login and does not front this client_secret_basic exchange or its
  rotation. Blocker stands, now with evidence rather than memory.
- key-cape-oidc-login -> asked of key-cape today, which the entry had recorded
  as still outstanding since 2026-08-21.

Also cleared the inbox that hid this: 9 stale unread, all superseded by shipped
work, with late closes sent to secrets-engine and llm-connect on the two threads
that had asked ops-warden something and never got an answer.

391 tests pass, ruff clean, boundary coverage 0 uncovered.

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
2026-08-28 22:00:09 +02:00

17 KiB

id type title domain repo status owner topic_slug planning_priority depends_on_workplans created updated state_hub_workstream_id
WARDEN-WP-0033 workplan Native lane handoff — review secrets-engine's catalog admission, and fix what it exposed infotech ops-warden finished ops-warden netkingdom P1
WARDEN-WP-0030
2026-08-21 2026-08-28 4627d89b-4b00-562a-81e9-76e96f90fa7e

WARDEN-WP-0033 — Native lane handoff

On 2026-08-20 secrets-engine accepted five of ops-warden's seven interim proxy lanes for catalog admission (SECRETS-WP-0006, decision ae676382-1826-4e04-aa4e-bb77990c7a0d), rejected two with reasons ops-warden agrees with, drafted the five entries itself rather than waiting for the offered contribution, and asked for a line-by-line review of the non-secret metadata.

In the same hour railiance-platform closed the RISK-F-0009 coverage gap and asked ops-warden for two things back.

This workplan is the response to both, plus the defect the review turned up in ops-warden's own grading model — which is the most consequential item here and would not have been found without the second pair of eyes.

Why a workplan and not an adhoc

Four counterparties are waiting, one deliverable is a cross-repo interface another repo will consume on a schedule, and the review carries approval semantics — secrets-engine holds interim proxy ownership open until each lane has approved native verification. The convention reserves adhocs for low-risk work completed directly; this is none of those.

Tasks

id: WARDEN-WP-0033-T01
status: done
priority: high
state_hub_task_id: "154f2f03-387d-5fe6-a0f5-1929c46a2bd8"

Review the five drafted catalog entries and reply. issue-core-ingestion-api-key, reuse-surface-hub-write-token, openrouter-llm-connect, forgejo-admin-api-token, email-connect-transactional at secrets-engine@784be97.

Review the axes they asked for: owner-repo coordinates, field grouping, workload consumers, rotation/deactivation owners, delivery intent. Verify claims against the authoritative railiance-platform CCRs — never against a secret read; bao kv get on any of these paths is the 2026-07-16 vector and three of the five are risk: high.

Accept their two rejections. openbao-api-key is a routing template rather than one lane and key-cape-oidc-login belongs to key-cape — that is ADR-0005 applied to ops-warden by someone else, correctly.

id: WARDEN-WP-0033-T02
status: done
priority: high
state_hub_task_id: "6996d07f-63bb-5708-a171-68c4b1bbddde"

Correct ops-warden's per-path risk grading — the defect T01 exposed.

secrets-engine graded issue-core-ingestion-api-key high; ops-warden grades it standard, and deliberately: WP-0032-T05 regraded it down on 2026-08-19, operator-sanctioned, because test_high_risk_lanes_classified asserted standard and was judged right — "ordinary internal workload secrets are standard".

That reasoning was incomplete, and CCR-2026-0002 says so explicitly: the path carries both ISSUE_CORE_API_KEY and GITEA_BACKEND_TOKEN, a Forgejo backend token, and the CCR records keeping both as a deliberate field-set decision.

The root cause is structural, not a single bad call. ops-warden grades a lane by its headline field — the catalog carries one fetch_command naming one field and no fields list at all — but the disclosure unit is the path: a bao kv get returns every field stored there. So a lane can be graded on its least dangerous contents. ADR-0007 says every lane carries an explicit grade; it does not say the grade must cover everything a read of that path would disclose, and it should.

Fix the grade, then fix the model that produced it: record fields per lane and grade on the union. Check the other 25 lanes for the same shape.

Done 2026-08-21 — and it was two lanes, not one. While verifying reuse-surface-hub-write-token I told secrets-engine their second field was unevidenced and asked them to confirm it. It was evidenced: CCR-2026-0005's kv_path block lists both fields and its notes describe the HMAC's alignment with Forgejo org webhook id=1. A truncated grep, and a retraction sent within twenty minutes so they would not spend time on it. That lane was under-graded on exactly the same reasoning, and both had been regraded down in the same T05 pass — which is what makes this a systematic flaw rather than one bad call.

Blast radius checked and bounded: of the 8 remaining standard lanes, 6 have no KV path at all (SSH, policy check, tunnel, principals, issue-sink, key-cape login) so nothing is disclosed by a read. Two do have paths and no CCR field evidence — rein-openweights-openrouter-approle and coulomb-social-runtime-env (a whole k8s env Secret, which by shape almost certainly holds several fields). Deliberately not regraded: ADR-0008 §3 says an unknown field set is stated rather than assumed, and grading is operator-sanctioned judgement, not backfill. They are the open item on this task.

Shipped: both regrades with their evidence and the superseded reasoning kept inline, fields recorded with its CCR source, the test that had been holding the wrong answer inverted with an explanation of why it was wrong to trust it, and ADR-0008 — a lane's grade covers every field its path discloses. That is a new binding rule, so it is a record rather than a wiki note.

id: WARDEN-WP-0033-T03
status: done
priority: high
state_hub_task_id: "a736f983-94da-5a4a-aaf9-5114485518a6"

Emit the generated high-risk data-path artifact for railiance-platform.

They asked for a versioned artifact of concrete high-risk data paths (id + data_path + catalog revision) to consume instead of hand-maintaining the deny list — the half of the RISK-F-0009 fix ops-warden explicitly declined to build for them. Emitting the artifact is not the same as amending their policy: they still own what to deny. That boundary is the point of ADR-0002 and must survive this task.

Must be generated, never hand-edited, and must carry the catalog revision so a consumer can tell what it was derived from. Depends on T02 — publishing the deny set before fixing the grades would ship the wrong list to a consumer who will apply it.

Done 2026-08-21. scripts/emit_high_risk_paths.pyregistry/generated/high-risk-data-paths.yaml: 19 high-risk lanes, 14 concrete data paths, 5 with no single KV address listed separately so a consumer does not read the absence as an omission. Carries catalog_revision, its date, and a catalog_dirty flag — a revision that does not describe the file it came from is worse than none.

The T02 dependency was not theoretical. The two regrades land in this artifact, and check_agent_read_boundary.py now reports 2 uncovered against a policy railiance-platform closed to 0 yesterday. Emitting before regrading would have handed them a list that confirmed a coverage claim that had just stopped being true.

fields is emitted as null where no CCR declares the set, never as a single-element guess — ADR-0008 §3. Two CI tests: the artifact must be current, and every high-risk lane must appear in it, so a lane graded high after the last emit cannot silently fail to reach the consumer.

Boundary held: the header states this is an input and not a policy, and that railiance-platform may deny more, deny less, or dispute a grade (ADR-0002).

id: WARDEN-WP-0033-T04
status: done
priority: medium
state_hub_task_id: "5acac140-a586-5db3-b231-bbf236710786"

Route the coding-agent issuance identity question.

RAILIANCE-WP-0017 is blocked on defining a distinct coding-agent issuance identity and proving deny-wins when that identity is combined with workload read. They asked ops-warden to route ownership.

Route it; do not absorb it (ADR-0005, ADR-0003). ops-warden owns actor identity for adm/agt/atm SSH certificates and nothing else — a coding agent's OpenBao issuance identity is an IAM question. The likely owner is key-cape, with user-engine and zone-engine both plausibly involved. Say so with reasoning rather than guessing, and record the answer either way.

Note the interaction with ADR-0004: the agent read-boundary already depends on WARDEN_AGENT_ID being set, which is an honour-system marker on the ops-warden side. A real issuance identity is what would make that boundary hold on the OpenBao side too, so ops-warden is an interested consumer, not a bystander.

Routed 2026-08-21 to key-cape (msg 903b2223); waiting on accept or refuse. Reasoning given: it is an identity and issuance question about a principal authenticating to OpenBao, which is key-cape/Keycloak's. The precedent is an hour old and runs the same direction — secrets-engine declined key-cape-oidc-login as ops-warden's to hand them, on the grounds that login and identity-token issuance stay with key-cape. Absorbing this would contradict agreeing with them.

Named the adjacent parties explicitly rather than leaving them to inference: zone-engine has an interest (a coding-agent identity is a strong candidate zone subject) and user-engine is not involved — this is a machine principal, not an end-user account. Stated so nobody concludes it by elimination.

Asked for a refusal-with-pointer as an equally good answer. The failure mode to avoid is the 2026-08-17 one recorded in .claude/rules/finding-routing.md: ops-warden answered a question well and never routed it, and another repo ended up filing it.

Followed up 2026-08-23 after the immediate enforcement gap closed. RAILIANCE-WP-0022 no longer waits for this ownership decision: the platform owner deployed a dedicated coding-agent AppRole, proved deny-wins when it is combined with workload read, and identified an exact-bound KeyCape JWT role as the migration target. That makes the remaining question narrower and more important rather than obsolete: who owns issuance of the durable KeyCape/Keycloak-backed machine identity?

Direct follow-up d25d4604-5713-42a8-8e3a-1ea31b5a5cc7 asks key-cape to accept that target identity with an authoritative workplan/interface, or refuse and name the actual owner. T04 remains wait until one of those two answers is recorded; the live AppRole is operational evidence, not an ownership answer.

Answered — accepted. Closed 2026-08-28. key-cape accepted issuance ownership in KEY-WP-0009-T03 (status: done, 2026-08-23): the non-secret static registration codex-railiance-platform is published in key-cape/config/service-clients.example.yaml — subject service:codex:railiance-platform, tenant tenant:coulomb, role coding-agent, scope openbao:login, client_credentials, 15-minute lifetime — with the service-auth claims, renewal, expiry and failure semantics in key-cape/docs/openbao-service-auth-contract.md (T02). The split is exactly the one the routing asked for: KeyCape owns JWT issuance and client disablement, railiance-platform owns the exact-bound OpenBao role and policy, OpenBao owns enforcement, and no secret value enters either repository.

The answer was found by reading their repo, not by receiving it. KEY-WP-0009-T04 records replying to ops-warden; no such message exists in the ops-warden inbox — 0 messages from key-cape, read or unread. The acceptance had been sitting done for five days while this task sat wait on it.

That is the T05 lesson landing on T04: a blocker is a claim about the world at a date, and this one had stopped being true. verified: source-read is doing real work here — it is the difference between "we asked" and "we checked". The mechanism is right; what it needs is for someone to run it, which is why the lanes below were re-verified in the same pass rather than date-bumped.

Consequence for ADR-0004: the honour-system WARDEN_AGENT_ID marker now has a real issuance identity behind it on the OpenBao side. ops-warden remains a consumer of that identity and does not own it.

id: WARDEN-WP-0033-T05
status: done
priority: medium
state_hub_task_id: "75051d17-399b-5129-860b-ae00dae91c47"

Make blocker staleness a mechanism instead of an intention.

Four stale blockers surfaced in twelve hours: an OpenBao token recorded expired that was valid, a verification script recorded ready that had never been written, a ten-day secrets-engine question answerable from their source, and FLEX-WP-0007 cited as blocking policy.enabled seven weeks after it read finished. .claude/rules/finding-routing.md has said re-read a blocker before trusting it since RISK-F-0001. Saying it has not worked.

warden route gaps had a --stale-days threshold defaulting to 90, which was not loose but inert — the register was created 2026-08-15, so it could not fire before November.

Done 2026-08-21. Two changes, and the second matters more:

  1. Split the cadences. DEFAULT_STALE_DAYS (90) still governs pointer freshness — "is this the right owner and page" genuinely is quarterly. New DEFAULT_BLOCKER_STALE_DAYS (14) governs interim blockers, calibrated on the cases that cost something: ten days for the secrets-engine lanes, one day for RISK-F-0001, ~50 for FLEX-WP-0007. At ~15 lanes it surfaces about one a day rather than a wall, and it fires on zero lanes on age today.

    Then scaled by risk, after risk-nexus answered. They accepted the offer to converge and published their convention: 14 days critical/high, 30 medium, 60 low, with fix state read from the owning repo rather than remembered. Their framing — "point warden route gaps at those windows and the two will agree without either of us building a shared mechanism" — is better than a joint tool, so blocker_stale_days() now maps lane risk onto exactly those windows. ungraded takes the shortest, not the longest: ADR-0007 makes an absent grade a defect, so its blocker is the least trustworthy of all.

  2. verified: distinguishes re-checking from re-editing. reviewed records when someone touched the entry; nothing recorded whether the claim was re-established. owner-confirmed and source-read count; asked-and-waiting and unverified do not, and a lane in those states is stale regardless of its date. key-cape-oidc-login is the proof: asked today, zero days old, correctly flagged.

Current state: 14 non-draft interim lanes — 5 owner-confirmed, 1 asked-and-waiting, 8 unverified. The 8 are honest; they were carried forward this session without a check and now say so instead of hiding behind a fresh date.

--fail-on-stale exits 3 so a cron job or gate can act. Deliberately not a CI test on age: a date-triggered failure breaks the build on a calendar day with no code change, punishing whoever commits next rather than whoever owns the blocker. The CI test is structural instead — every interim lane must record how it was verified, which fails on the commit that introduces the omission.

id: WARDEN-WP-0033-T06
status: done
priority: high
state_hub_task_id: "94f77f5a-f919-5328-832c-ba1d24c6431b"

Correct first-time OpenBao administration routing. rapp-postgres reported on 2026-08-22 that warden plan mapped first-time database/config/platform-pg-2 administration to the generic openbao-api-key template. It consequently proposed paste-once KV provisioning and a workload OIDC role, neither of which can configure database connections, policies, auth roles, or token roles. Their Desk session stopped without an approval or secret action.

The authoritative platform runbook and rapp-postgres's live T04 evidence agree on the missing distinction: this is one attended KeyCape-backed OpenBao identity act at auth mount netkingdom, role platform-admin. It is not a credential value handoff. Root stays offline break-glass authority and a workload role must never be widened to make the operation pass.

Add an exact openbao-platform-admin-login lane, an operator playbook, and a regression that makes the reported request resolve to founder_required with oidc_login — never paste_once_provision. Verify the route, then return the corrected non-secret command to rapp-postgres.

Done 2026-08-22. The new active lane resolves the exact reported request at score 20, ahead of the generic KV template at 11, and returns one founder_required act: bao login -no-print -method=oidc -path=netkingdom role=platform-admin. Planner output offers only warden access openbao-platform-admin-login --fetch and the exact owner command; login lanes no longer inherit secret-value transports (--exec, --out, --wrap). The lane is explicitly a high-risk, non-workload human identity act, so the generated boundary artifact lists it as having no KV address rather than inventing one.

Verification: 384 tests pass, Ruff passes, workload accounting resolves all 28 lanes (3 resolved, 17 unknown, 8 not-applicable), and the boundary invariant reports 20 high-risk lanes, 14 concrete paths covered, 6 non-KV/pattern lanes, 0 uncovered. No login or credential action was performed.

  • secrets-engine SECRETS-WP-0006 — catalog admission, decision ae676382
  • RISK-F-0009 — closed by railiance-platform 2026-08-21; 0 uncovered
  • railiance-platform RAILIANCE-WP-0022 (boundary coverage), RAILIANCE-WP-0017
  • ADR-0002, ADR-0003, ADR-0004, ADR-0005, ADR-0007
  • WARDEN-WP-0030 — the delegation register these five lanes retire from