- Task: 2.5
- Evaluated: 2026-09-05
- Decision: operator-read-model-exposed
- ADR gate:
operator-read-model(docs/adr/ADR-0076-eve-governed-delivery-lifecycle.md) - Tool:
admin_agent_fleet, registered bybuildWorkbenchReadOnlyToolBindings - Record digest:
c886a4f5fb1d57398d5df731f96a3f6cf7fec7cd233db0a3d37d2dda82285a14
The rule#
One restrained, scannable answer to "what is the fleet doing, and what needs me?": a fixed ten-section view that leads with a capped, severity-ordered attention list, answers every facet task 2.5 names from rows and events read in a single snapshot, reports a measurement this plane does not carry as an explicit absence rather than a zero, and grants no authority over anything it describes.
The sections are read from the module and must equal the view type’s own keys; every facet names a field path resolved by parsing the FleetView interface and a spec title found in the spec sources; the tool’s read-only registration is located by slicing the two builders out of the tools module.
What the task asks for, and where it is answered#
| Facet | Answered at | Proved by |
|---|---|---|
| queue/dependency depth | queue.ready, queue.blockedByDependency, queue.dependencyDepth |
"counts the chain of declared dependencies, not the number of them"; "counts only the ready items still waiting on an unverified dependency"; "reports the fleet this spec actually built, not a fixture" |
| leases/ages/owners | leases.live, leases.expired, leases.oldestLiveAgeSeconds, leases.owners |
"separates live holds from lapsed ones and names the owners" |
| triage | triage.total, triage.byClass, triage.byDisposition, triage.needingHumanDecision |
"counts triage by class and by disposition, and finds the latest"; "counts real triage records and finds the drain that wrote them" |
| last drain | lastDrain.runRef, lastDrain.at, lastDrain.itemsTriaged |
"derives the last drain from the run reference the drain stamps, taking the newest"; "reports no drain seen rather than inventing one" |
| throughput | outcomes.windowSeconds, outcomes.shipped, outcomes.verified |
"counts only what happened inside the window"; "defaults the window to a week" |
| cost | cost.configured, cost.reason |
"reports cost as unconfigured rather than zero"; "never reports cost as zero, because this plane records no spend" |
| intervention | intervention.humanActs, intervention.machineActs, intervention.humanShare |
"measures how much of the window a human had to act in" |
| verification rate | outcomes.verifiedPerShipped, outcomes.gapsPerShipped, outcomes.shipVerifyGaps |
"computes verified and gap rates against what actually shipped"; "reports a rate over zero shipped items as null, not as zero" |
| rollback rate | outcomes.rollback.observedBackwardMoves, outcomes.rollback.reopenPerShipped, outcomes.rollback.postShipRate.configured, outcomes.rollback.irreversibleAfterShip.transitionId |
"refuses to report a post-ship rollback rate, and says why in the machine's own words"; "counts the backward edges that were actually taken, and zeroes the ones that were not"; "counts a real backward move, and still refuses a post-ship rollback rate" |
| kill-switch state | killSwitch.engaged, killSwitch.source, killSwitch.reason |
"passes the engaged state and the operator's reason straight through"; "shows the kill switch engaged, and the admission door then refuses everything"; "reads the switch fail-safe: a value it does not understand halts the fleet" |
| trace links | trace.ledgerEvents, trace.ledgerEventsInWindow, trace.links.locator |
"names where every number came from, and counts the ledger it read"; "separates the whole ledger from the part inside the window" |
All 11 facets resolve to a field the FleetView type actually declares — the
paths are checked against the parsed interface, not against a hopeful string —
and each is asserted by a named spec case.
What it refuses to make up#
| Absence | Field | Proved by |
|---|---|---|
| cost | cost.configured |
"reports cost as unconfigured rather than zero" |
| post-ship-rollback-rate | outcomes.rollback.postShipRate.configured |
"refuses to report a post-ship rollback rate, and says why in the machine's own words" |
| rate-over-zero-shipped | outcomes.verifiedPerShipped |
"reports a rate over zero shipped items as null, not as zero" |
| empty-queue-and-lease-age | queue.oldestWaitSeconds |
"reports an empty queue age and lease age as null, not as zero seconds" |
| no-drain-seen | lastDrain.runRef |
"reports no drain seen rather than inventing one" |
Restraint#
- Sections (10, fixed):
attention,queue,leases,triage,lastDrain,outcomes,intervention,cost,killSwitch,trace - Attention leads the view: true
- Attention is capped at 8: true
- Attention is severity-ordered (halted > blocked > stalled > watch): true
- The section list equals the view type's own keys: true
Authority#
- Registered by
buildWorkbenchReadOnlyToolBindings: true - Absent from
buildWorkbenchAgentToolBindings: true - Registration sites in the tools module: 2, of which 0 are outside the read-only builder
- The kill switch arrives as an input, so the view reports it and cannot press it: true
- The model imports only its own siblings: true
- It takes the store as a type and never as a value: true
- The binding reads one MVCC snapshot: true
- The switch falls through ENGAGED; only
0,false,off,norelease it, so a typo halts rather than opens: true
Honest limits#
- This record closes task 2.5 only. It adds no execution isolation (2.6), no provider-free orchestrator model or parity proof (2.7), and no live drain or soak result (2.8).
- The surface is a tool binding returning a value. There is no operator UI: "restrained and scannable" is enforced as a fixed section set, a capped severity-ordered attention list, and a section order that leads with attention — not as a rendered layout anyone has looked at.
- Cost is reported as an explicit absence because no lease, report, ship, or verify event on this plane carries a token, provider, or price. Making it a number is task 2.8 and the model-cost registry, not this surface.
- A post-ship rollback rate does not exist because the work-item machine declares ship and verify irreversible. If a rollback transition is ever added, the section reports it without an edit here — but until then the honest answer is an absence, not a zero.
- The last drain is derived from the run reference the drain stamps on triage records. A drain whose every item advanced writes no triage and therefore leaves no trace on this plane, which reads as "no drain seen" rather than "no drain ran".
- Throughput, intervention and verification counts are computed over a caller-chosen window bounded to between an hour and thirty days. Nothing here retains history: the numbers are recomputed from the ledger on every call, so a ledger compaction would change them.
- The kill switch is read from the process environment of whoever calls the tool. It halts acquisition through the queue admission door; it does not reach into a drain already running in another process, which is that drain’s own bounded supervision from task 2.3.