8.5 KiB
| id | type | title | date | assessor | audience | status | related_workplans | origin | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 20261003-security-gate-packets | assessment | What the dependency graph says about how security work gets blocked, and what to change | 2026-10-03 | the-custodian (Claude) | founder | recorded |
|
Founder request 2026-10-03: use the dependency facility to shrink the blocked set and learn how to make the security infrastructure easier to maintain and extend. |
What the dependency graph says about security work
Snapshot 2026-10-03 from the workplan files of 145 registered repos
(docs/evidence/2026-10-03-dependency-graph-triage/). This is an
assessment with proposals. It changes no canon and authorizes nothing.
1. The shape of the blockage
| Measure | Value |
|---|---|
| Open workplans | 108 (100 blocked) |
| Wait tasks inside them | 232 |
Human gates (needs_human: true) |
69 |
| Human gates that named a hub decision | 0 |
Owner-return waits (blocked_on: message-from:) |
20 |
| Wait tasks whose blockers were already terminal | 8 |
| Cycles | 0 |
Three facts matter more than the counts.
The graph has no cycles and few true roots. Removing the already-
satisfied and mis-statused cases, the open set hangs off roughly a dozen
root nodes. The two largest, SAND-WP-0015-T04 (7 workplans behind it)
and SECRETS-WP-0009-T03 (5), are themselves owner-return waits on
railiance-platform and glas-harness, which in turn wait on one attended
OpenBao session. So the single largest lever is not a repo and not a
workplan; it is one kind of act by one person.
Human gates were invisible to the hub. The wait-qualifier standard
(canon/standards/task-wait-qualifiers_v0.1.md) and C-40 rule 4 already
let a resolved decision clear a task. Nobody used it: all 69 gates were
prose. Bernd therefore had 69 places to read and no list to resolve.
CUST-WP-0075-T03 routed 68 of them to eleven hub decisions (one has no
stated gate at all, WARDEN-WP-0037-T03, and goes back to its owner).
Security work is where this concentrates. Of the 69 gates, 19 are attended OpenBao provisioning acts (packet D1), 14 are the ArgoCD production-ownership sequence (D2), 3 are trust-root and custody design (D3), 3 are destructive cleanups behind retention windows (D4), 7 are production risk acceptances (D5). That is 46 of 69 in the credential, identity, delivery and custody space. Product and research gates are the minority.
2. Why credential work blocks the way it does
Reading the lane documents in railiance-platform
(docs/credential-lane-designs/, docs/openbao-approved-automation-delegation.md,
docs/credential-lane-lifecycle-runbook.md) and the ops-warden catalog
(38 routing entries) gives a consistent picture:
- Metadata is delegated, values are not. The delegated applier may write policies and auth roles from an approved CCR; secret values stay an attended operator act "unless a later workplan approves a stronger dual-control flow". Almost every D1 task is blocked at exactly that value step: the policy is designed, reviewed, often dry-run, and then waits for a session.
- Each lane negotiates its own session. Nineteen tasks in twelve repos describe the same act in their own words, with their own evidence expectations. Nothing aggregates them, so no session is ever "worth" scheduling and each lane waits for its own.
- Owner-return waits chain across repos. sand-boxer waits on railiance-platform, which waits on secrets-engine, which waits on the session. Each hop is a hub message that may or may not be answered; the C-25 unread-reply check exists but nothing sweeps it.
- The read model lags the files. The hub carried 201 dependency edges where the files carry more; task-level edges from the September backfill were "parked" behind a deploy. Anyone querying the hub for the graph got a thinner picture than the files hold.
None of this is a design flaw in OpenBao, KeyCape or ops-warden. It is the absence of a batching and routing layer between well-designed lanes and one scarce person.
3a. Two failure modes found while acting on the graph
Prose-only blockers defeat C-40. Three of the eight "satisfied" waits
were not satisfied. Their depends_on named an old in-repo predecessor;
the real gate (GLAS-WP-0012, an unread canon registration, an
independent human author) lived only in the dated notes below the task.
C-40 can only clear what was encoded, so it reported them as clearable.
The fix was to encode the real edge, not to clear the task. Rule worth
adding to the standard: when a dated note re-blocks a task, the same
edit updates depends_on / needs_human / blocked_on.
Owner returns arrive and are not read. Seventeen owner-return waits
had an unread reply from exactly the agent they wait on. One of them
(COORDINATION-WP-0004-T02) was fully satisfied for a week: info-tech-canon
finished INFO-WP-0030 on 2026-09-27 and sent the artifact id; nobody
read it. Closing it took ten minutes. This is the strongest argument for
P5 below, and it is not specific to security work, although most of the
seventeen sit in security repos (ops-mason, gate-house, user-engine,
railiance-platform).
3. Proposals (ease of maintenance and extension)
P1 — Gates are born routed. Extend C-39: a needs_human: true wait
in an open, non-residual workplan without decision_id is a WARN. Owner
repos then attach a decision at the moment they create the gate, and the
hub's open-decision list is Bernd's queue. (state-hub, small.)
P2 — The attended provisioning window is a standing event. Instead
of per-lane sessions, one dated window (fortnightly is enough at current
volume). Its checklist is generated, not written: every task carrying
the window's decision_id, joined to its CCR and lane doc, rendered by
credential-change.py as a numbered attended sequence with the receipt
each owner needs. Owners prepare to that checklist; the session runs it
top to bottom; resolving the decision raises C-40 on every task.
(railiance-platform owns the renderer; the-custodian owns the cadence.)
P3 — Decide the dual-control value path once. The delegation doc leaves "stronger dual-control" open. The honest options are (a) keep values attended but batched (P2 alone), (b) response-wrapped delivery where the issuing system writes the value into OpenBao under a one-time wrapping token the operator only unwraps, or (c) a scoped operator-write role under MFA used by a wrapped command that never prints. Pick one in packet D3 alongside the trust-root ruling; it removes the value step from most future lanes. (railiance-platform workplan.)
P4 — Catalog lane as the unit of extension. A new consumer today costs a design workplan, a CCR, an attended apply and bespoke evidence. Make the ops-warden catalog entry the template: entry + CCR skeleton + policy/role rendering + ExternalSecret manifest + evidence script scaffolded from one command. Then a new lane is a reviewed PR, not a workplan, and only its value step needs the window. (ops-warden + railiance-platform.)
P5 — Owner-return sweep. A daily check lists every
blocked_on: message-from:<agent> task whose awaited agent has sent the
waiting repo a message newer than the task's last note, read or not, and
wakes that repo's agent to act on it. C-25 already watches unread
replies; promote its finding from INFO to a work item. The 2026-10-03
sweep script is in the triage evidence folder.
P6a — Fix the qualifier sync gap (found 2026-10-03). After the
routing commits, statehub fix-consistency --repo <slug> reported PASS in
every repo, yet the hub held the decision_id for 1 of 58 routed tasks.
Running scripts/consistency_check.py --repo <slug> --fix directly from the
state-hub checkout raised C-41 and synced them. Same interpreter, same
checker file, different working directory; the cause is not isolated. Until
it is, a routing change is not visible to the hub, so P1 and P2 would act on
stale data. (state-hub, high.)
P6 — Keep the hub graph current. Run statehub fix-consistency --all --remote on railiance01 nightly so C-20 keeps the edge table equal to
the files, and finish the deploy that carries task-level edges. The
graph scripts in this triage can then run against the hub alone.
4. What this does not change
The hub stays a read model. Decisions were recorded through the
sanctioned route and every decision_id lives in the owner file, synced
by fix-consistency. No workplan or task was registered by hand. No
credential was requested, printed or moved.