Capabilities · 5. Running a fleet
If you run more than two agents against the same repository, your two hardest problems are that you cannot see what they are doing and cannot stop them treading on each other. This chapter is about the eight capabilities that exist for those two problems, and about the one thing they have in common: almost all of them are advisory by design.
This chapter is Explanation. It says why each capability exists, what it changes on a Monday morning, and what it deliberately does not enforce. It does not reproduce route tables or field lists; those are in the work and coordination API plane and the coordination and cost chapter, and each explainer points at the right section.
If you are a team lead deciding whether this replaces your kanban board, read 5.1, 5.2 and 5.3. If you are trying to stop two agent sessions clobbering each other in one working tree, read 5.4 and 5.5. If you are organising a fleet's memory by initiative rather than by tenant, read 5.7 and 5.8.
| Capability | The collision or blindness it addresses | Status | |
|---|---|---|---|
| 5.1 | The work board | Nobody, human or agent, has a shared list | SHIPPED; status feed FLAG, default off |
| 5.2 | ExecPlan projection | The plan file and the board disagree | SHIPPED when the plans root is configured |
| 5.3 | Human approval gates | A high-risk action runs before anyone sees it | HTTP gate SHIPPED; MCP queue FLAG, default off |
| 5.4 | Presence and intent | Two sessions start the same work unaware | FLAG CORECRUXD_COORD, default on |
| 5.5 | Path leases | Two sessions edit the same files at once | FLAG CORECRUXD_PUNCHCARD, default off |
| 5.6 | Orchestrator groups | A fleet has no unit larger than one agent | FLAG, default off |
| 5.7 | Projects and planes | Memory is organised by tenant, not by initiative | SHIPPED |
| 5.8 | Storybook and dossiers | Nobody can say what a project is without reading it all | SHIPPED |
The pattern to internalise before reading further. With one exception, none of this blocks anything. Presence warns, leases warn, constraints warn, orchestrators group. That is a deliberate position: a coordination layer that blocks is a coordination layer that becomes a single point of failure for a fleet, and every operator who has had a stale lock wedge a build knows why. The exception is the approval gate in 5.3, which is the one place the design says stop and wait for a person.
5.1 The work board
Status · SHIPPED · status feed behind
CORECRUXD_FEATURE_STATUS_FEED, default off · Reached through · MCPlist_work,create_work,update_work_state,comment_on_work,status_feed·GET/POST /v1/work,GET/PATCH /v1/work/{id}· Console → Work › ExecPlans · Who it is for · both
What it does. The work board is a kanban of work items with state transitions, threaded comments and a recorded transition history, readable and writable by an agent over MCP and by a person in the console. It is the same list for both. On top of it sits a rolling status feed of lifecycle events, behind CORECRUXD_FEATURE_STATUS_FEED, default off (work.rs:685).
Why it works this way. The alternative every team tries first is to keep the human board in a separate tool and have agents report into it. That fails in a specific way: the agent's view of the work and the human's view drift within a day, and the drift is invisible until someone acts on the wrong one. Putting one board in the substrate, with the same item ids on both surfaces, means there is nothing to reconcile. The transition history is kept rather than just the current state because "how did this get to blocked" is the question a fleet lead actually asks, and a mutable status field cannot answer it. The trade-off is that this board is deliberately thin: no swimlanes, no estimates, no sprint model. It is a shared list, not a project management product.
What changes for you.
- As an operator: you get one list that an agent cannot forget to update, because updating it is how the agent claims and releases work.
- As an agent: you can discover unfinished work rather than being told about it, and you can leave a comment on an item that the next session will read.
What it does not do.
- It does not schedule or assign. Nothing picks the next item for an agent; an agent reads the board and chooses.
- It does not enforce a workflow. State transitions are recorded, not validated against a state machine you configure.
- With the status feed flag off,
status_feedreturns a disabled notice rather than an empty feed, so quiet and off are distinguishable.
Turn it on. The board itself needs nothing. The lifecycle feed is CORECRUXD_FEATURE_STATUS_FEED=1, default off.
Where the detail lives. API §6.1 for the routes and item shape; Operations §3.7 for the console board.
5.2 ExecPlan projection into the board
Status · SHIPPED, active when the ExecPlans root is configured · Reached through · MCP
list_work(source="all")·GET /v1/work?source=all· Console → Work › ExecPlans · Who it is for · both
What it does. Markdown plan files on disk are projected into the same work board as read-only items. Their state is not stored anywhere: it is derived at read time by derive_state (work_execplans.rs:861), a deterministic rule set that reads the plan's own Status: line together with the milestone and gate facts stored against that plan's slug. The plan file stays the source of truth and the board stays current with no second write.
Why it works this way. This is the design decision people most often get wrong about the board, so it is worth stating flatly: an ExecPlan item is a projection, not a record. The rejected alternative was to sync plans into the board as real items, which produces the classic two-writer problem: the file says one thing, the board says another, and now you need a reconciliation job and a rule about who wins. Deriving state at read time removes the possibility of disagreement, because there is only one place state can come from. The rules are ordered and first-match-wins, and a human declaration in the file outranks fact-derived state, with one deliberate exception: a terminal fact signal beats a stale In progress line, because plan authors forget to flip that line and finished work should not sit open forever.
What changes for you.
- As an operator: the plan document your team already writes is the board entry. There is no ceremony, and no plan can be silently out of date on the board while being current on disk.
- As an agent: resuming work means opening the plan at
plan_pathand reading the milestone facts, not guessing from a board summary.
What it does not do.
- ExecPlan items are read-only. A
PATCHwill not move one. You change its state by editing the plan'sStatus:line or by storing the gate fact that the derivation reads. - It does not watch the filesystem. The projection is computed when the board is read, so a plan edited a second ago and a plan edited last month cost the same.
- It does no interpretation. Parsing is literal string matching over the plan's structure (parse_plan); a plan that does not follow the expected shape is projected as unparseable rather than guessed at.
Turn it on. Set the ExecPlans root in the daemon's process environment. The aggregator is inactive when it is unset.
Where the detail lives. API §6.1 for source=all and the extension fields on a projected item.
5.3 Human approval gates
Status · HTTP work gate SHIPPED · MCP approval queue behind
CORECRUXD_FEATURE_APPROVAL_QUEUE, default off · Reached through ·GET /v1/work/gate/pending,POST /v1/work/gate/{actionId}/approveand/reject· MCPapproval_request,approval_decide· Console → Trust › Gates · Who it is for · human
What it does. A high-risk action stops and waits for a named person to decide. The HTTP work gate ships in the default path: pending actions are listed, a person approves or rejects, and the decision is minted as a signed receipt. A second, risk-tiered queue is available over MCP behind CORECRUXD_FEATURE_APPROVAL_QUEUE, default off.
Why it works this way. This is the one capability in this chapter that is allowed to block, and that is the whole point of it. Everything else here is advisory because advisory controls degrade gracefully; an approval gate that degrades gracefully is not an approval gate. The decision is recorded as a signed receipt rather than a log line for a specific reason: months later, the question is not "was it approved" but "who approved it, and what were they shown". A receipt fixes the decision and its context together. The trade-off accepted is latency: a gated action is a stopped action until a human returns, and there is no timeout that quietly approves.
What changes for you.
- As an operator: you have one queue for "an agent wants to do something that needs me", with an empty state that is itself informative, and a record of every decision that survives the run.
- As an agent: a high-risk action becomes a request rather than a refusal, so the run pauses instead of failing.
What it does not do.
- It does not decide what is high-risk. The caller marks an action as gated; the daemon does not classify actions for you.
- The approval receipt records the decision, not the execution. It is evidence that a named person approved, at a time, over a stated action. It is not evidence that what subsequently ran matched what was approved. Chapter 6 sets out that boundary in full.
- With the MCP queue flag off, the risk-tiered tools are not the fallback for the HTTP gate; they are a different surface that is simply not there.
Turn it on. The HTTP gate needs nothing. The MCP queue is CORECRUXD_FEATURE_APPROVAL_QUEUE=1, default off.
Where the detail lives. API §6.1 for the gate routes; API §13.13 for the tiered queue; Operations §4.9 for the console.
5.4 Live-session presence, intent and overlap warnings
Status · FLAG
CORECRUXD_COORD, default on · Reached through · MCPcoord_status,coord_announce·GET /v1/coord/active,POST /v1/coord/announce· Console → Rings › Live board · Who it is for · agent
What it does. Behind CORECRUXD_COORD, default on, each session announces what it is working on and which paths it expects to touch. When it does, the daemon compares those paths against every live peer's declared paths and returns the overlaps in the response (find_overlaps). Liveness comes free from binding and announcing; the only thing an agent has to do deliberately is declare its focus.
Why it works this way. The failure this removes is specific and common: two Claude Code sessions open on the same tree, both confident they are the only one, both about to edit the same three files. Nothing detects that today because nothing is looking. The design choice worth understanding is that overlap is computed and returned to the caller rather than acted on. A blocking coordinator would need a correct model of what "conflict" means across every editor, harness and branch, and would be wrong often enough to be disabled within a week. Returning the warning puts the judgement where the context is. Intents also expire at read time rather than being swept by a background job, which means there is no cleanup task to fail and no stale-presence bug class at all: an expired intent is simply not returned.
What changes for you.
- As an agent: announcing on every plan or milestone switch costs one call, and the response tells you immediately whether a peer is already in your paths.
- As an operator: "who is live and what are they in the middle of" is a screen rather than an inference from process lists.
What it does not do.
- It never blocks. An overlap is a signal to coordinate, not a stop. If you need a claim that other clients respect, that is 5.5, and it has its own limits.
- It does not detect what it was not told. Overlap is computed over declared paths, so a session that announces nothing is invisible to its peers and sees them as clear.
- It does not resolve anything. There is no merge, no queue and no priority between two overlapping sessions.
Turn it on. On by default. Set CORECRUXD_COORD=0 to disable the plane.
Where the detail lives. Daemon §15.1 for the intent record, the TTLs and the overlap rules; API §6.2 for the two routes.
5.5 Path leases (punchcards)
Status · FLAG
CORECRUXD_PUNCHCARD, default off; accepted valuesoff,advisory,enforce· Reached through · MCPpunch_in,punch_out,list_punchcards,force_release,check_punchcard·POST /v1/punchcards/acquire,/release,/check· Console → Rings › Punchcards · Who it is for · agent
What it does. Behind CORECRUXD_PUNCHCARD, default off, a session claims a file or a subtree before a multi-file edit and releases it afterwards. In advisory mode the daemon reports a conflict and grants the lease anyway. In enforce mode it refuses to grant a conflicting lease with a 409. A force_release exists for the case where a holder died without releasing.
Why it works this way. A lease is the stronger sibling of the presence signal in 5.4: presence says "I intend to work here", a lease says "I have claimed this". Three modes exist rather than two because the honest deployment path for any locking scheme is to run it in observe-only first and find out how often it would have fired before letting it fire. Most teams should sit in advisory for a fortnight and read the conflict counts.
The limit here is the most important sentence in this chapter, and it is a design consequence rather than a defect. In enforce mode the daemon only refuses to grant a conflicting lease. It does not deny the edit. The actual edit denial is performed by a client-side pre-tool hook that checks leases before writing. A client that does not run the hook is unaffected by any lease anyone holds. This is what it means for the whole coordination plane to be advisory: the daemon can tell every co-operating participant the truth, and it has no way to stop a participant that is not co-operating.
What changes for you.
- As an agent: claim the paths you will modify before a multi-file change, and a peer that runs the same hook will be stopped before it overwrites you.
- As an operator: you can see which claims are outstanding and release a stuck one, without restarting anything.
What it does not do.
- It does not deny edits. Enforcement lives in the client-side hook, so a non-co-operating client writes wherever it likes. With the feature disabled the check reports allow (punchcards.rs:597).
- It is not a filesystem lock. Nothing in the operating system knows about a punchcard.
- It does not survive a client that never releases, except through
force_releaseor expiry. A lease is a co-operation aid, not a durability guarantee.
Turn it on. CORECRUXD_PUNCHCARD=advisory to observe, =enforce to refuse conflicting grants. Default off.
Where the detail lives. Daemon §15.1 for the lease record and the client-side enforcement boundary; API §6.4 for the routes.
5.6 Orchestrator groups
Status · FLAG
CORECRUXD_ORCHESTRATORS, default off · Reached through · MCPcreate_orchestrator,attach_to_orchestrator,detach_from_orchestrator,list_orchestrators,update_orchestrator·/v1/orchestratorsand its sub-routes · Console → Rings › Orchestrators · Who it is for · agent
What it does. Behind CORECRUXD_ORCHESTRATORS, default off, agents and their work can be grouped under a named orchestrator with an explicit membership list (orchestrators.rs:211), and the group's aggregate work view can be read back as one thing.
Why it works this way. Once a fleet is more than a handful of agents, the useful unit of attention stops being the agent. A lead wants to ask "how is the migration crew doing", not to read six sessions. Membership is explicit rather than inferred from behaviour because an inferred group is a group you cannot correct: if the daemon decided two agents were a team because they touched the same files, you would have no way to say no. The trade-off is a small amount of setup, and the reason it is default off is that a solo operator or a three-agent fleet gains nothing from it and would be paying for a concept they do not need.
What changes for you.
- As an operator: a group is a single row to look at, and its work rolls up rather than needing to be assembled by hand.
- As an agent: attaching to an orchestrator makes your work legible as part of a larger effort rather than as an isolated session.
What it does not do.
- It does not orchestrate. Despite the name, nothing here dispatches work, sequences agents or supervises execution; it is a grouping and an aggregate read.
- It does not confer permission. Membership of an orchestrator is not a capability, and it changes nothing about what a member may do.
- It does not derive membership. An agent that never attaches is not in the group, whatever it is working on.
Turn it on. CORECRUXD_ORCHESTRATORS=1. Default off.
Where the detail lives. API §6.3 for the routes; API §13.14 for the five tools.
5.7 Projects, planes, layers and the context graph
Status · SHIPPED · Reached through · MCP
list_projects,get_project_context·/v1/projectsand its sub-routes,/v1/projects/{id}/planes,GET /v1/projects/{id}/context-graph· Console → Work › Projects · Who it is for · human
What it does. A project contains planes, and a plane carries members, tenants and layers (planes.rs:43). On top of that hierarchy sits a composed context graph per project, which assembles what the daemon holds about an initiative into one readable structure.
Why it works this way. Tenancy answers "whose data is this", which is a security question. It does not answer "what is this work part of", which is the question a fleet lead has all day. Before projects, the only organising axis was the tenant, so an initiative spanning two tenants had no home and an initiative inside one tenant was indistinguishable from everything else in it. The implementation decision worth knowing is that the whole hierarchy is stored as ordinary facts under reserved prefixes rather than as new tables. That was chosen so projects inherit the substrate's versioning, recall, export and erasure behaviour for free, and so there is no second store to back up, migrate or reconcile. The cost is that a project is exactly as durable as the fact store, with the same durability boundary.
What changes for you.
- As an operator: you can organise a fleet's memory by initiative, and ask what the daemon knows about one initiative without filtering by tenant.
- As an agent:
get_project_contextgives you the initiative you are working inside as one call rather than as a reconstruction.
What it does not do.
- It is not an access boundary. Projects organise; tenants and scopes are what isolate. Putting work in a project does not restrict who can read it.
- It does not create structure for you. A project with no planes and no members is an empty label.
- The context graph is composed at read time from what exists. It is a view, not a curated artefact, and it will show you a thin project as thin.
Where the detail lives. API §6.5 for projects, layers and the context graph; API §6.6 for planes.
5.8 Project narrative artefacts: storybook and dossiers
Status · SHIPPED · Reached through ·
/v1/projects/{id}/storybookand its sub-routes,/v1/projects/{id}/dossiersand its sub-routes · Who it is for · human
What it does. A storybook is a versioned prose snapshot of what a project is and where it stands, generated from the substrate (storybook.rs:44) with a diff between any two versions. A dossier is the same idea aimed at a specific subject, and it adds a reconciliation view that shows where the written narrative and the underlying data disagree.
Why it works this way. The artefact a fleet lead actually needs to produce is a paragraph: what is this project, where has it got to, what is blocking it. Producing that by hand goes stale the day it is written, and producing it purely automatically produces something nobody trusts. The design keeps both halves: the narrative is a first-class versioned document a person can edit, and the reconcile view continuously answers "is this still true against the data". That is why diffs between versions exist at all; the value is not the current text, it is being able to see what changed in the story and when. The trade-off is that a stale storybook is still shown to you as a storybook, and it is the reconcile view, not the document, that tells you it has drifted.
What changes for you.
- As an operator: you can hand someone a project narrative that is dated, versioned and checkable against the substrate rather than a status update in a chat thread.
- As an agent: auto-composition means a first draft exists without a person writing it, and the diff makes it obvious what a regeneration changed.
What it does not do.
- It does not keep itself true. Composition happens when asked; nothing rewrites a storybook because the data moved underneath it.
- It is not evidence. A dossier is generated prose, not a signed record, and nothing about it is verifiable in the sense chapter 6 uses that word.
- Reconciliation reports disagreement; it does not resolve it. Deciding whether the narrative or the data is wrong is a person's job.
Where the detail lives. API §6.7 for storybook generation and diffs; API §6.8 for dossiers and reconciliation.
Sources
- Work board: get_work, post_work, post_comment, get_transitions, get_status_feed
- ExecPlan projection: parse_plan, derive_state, list_execplans, execplans_root_from_env
- Approval gates: get_pending_gates, approval_request, approval_decide, approval_receipts.rs:18
- Coordination: write_intent, paths_overlap, find_overlaps
- Punchcards: acquire, punchcards.rs:419, punchcards.rs:597
- Orchestrators: create_orchestrator, add_member, list_orchestrator_work
- Projects and planes: get_projects, get_context_graph, PlaneRecord, get_planes
- Storybook and dossiers: storybook.rs:44, storybook diff, dossier auto, dossier reconciliation

