Run ledger protocol
Source candidate for the Run ledger over executor Sessions
(maia.run-ledger/v1, S19): the coordinator's durable memory of a task graph
whose dispatches are Codex or Claude Code Session turns
(decision). The local-v1 terminal Run
(run.create and its run.* siblings) is a separate, unchanged record.
Operations
| Method | Kind | Exposure | Receipt | MCP tool |
|---|---|---|---|---|
run.ledger.create | mutation | unsupported | yes | run_ledger_create |
run.ledger.list | read | unsupported | no | run_ledger_list |
run.ledger.task.list | read | unsupported | no | run_ledger_task_list |
run.ledger.task.show | read | unsupported | no | run_ledger_task_show |
run.ledger.task.dispatch | mutation | unsupported | yes | run_ledger_task_dispatch |
run.ledger.task.settle | mutation | unsupported | yes | run_ledger_task_settle |
Exposure is Agent-definition eligibility, not a grant: worker Sessions cannot
call these until S18 roles exist. A mutation retried with the same
commandId and input replays its first receipt; the same commandId with
other input conflicts.
Records
- Run:
id,workspaceId,createdAt,taskCountsper Task state and a derivedstate:succeededwhen every Task succeeded,blockedwhen no Task is ready or active (a stopped Task blocks its dependents), otherwiseopen. - Task:
clientKey,dependencyTaskIds,revision,failedDispatches,dispatches(oldest first) and astatederived from them:activewhile its last dispatch is active,succeededonce one succeeded,stoppedafter three failed, otherwisereadywhen every dependency succeeded andblockedwhen one has not. The full Task adds theobjectiveand every dispatch'sevidence. - Dispatch: one attempt at a Task on one Session:
ordinal1 to 3,sessionId, the queuedturnId,state(active,succeeded,failed) and, once settled,settledTurnIdandevidence(1 to 8192 UTF-8 bytes).
The Task revision starts at 1 and advances by one with each dispatch and
settlement; both compare it, so two coordinators cannot both dispatch one
Task.
Requests
run.ledger.create:commandId,workspaceId,tasks(1 to 32{ clientKey, dependsOn, objective }, the local-v1 Task input). The graph keeps the local-v1 bounds (8 direct dependencies and dependents, 128 edges, depth 8, 32 KiB of objectives) without a root or Dispatch Task.run.ledger.list:workspaceId,limit(1 to 100), optionalafterCursor.run.ledger.task.list:runId, optionalready: truefor the ready Tasks alone.run.ledger.task.show:runId,taskId.run.ledger.task.dispatch:commandId,runId,taskId,expectedTaskRevision,sessionId,expectedSessionRevision. The Node queues the objective as a turn on a running Codex or Claude Code Session of the Run's Workspace in the transaction that records the dispatch; a Session holds at most one active dispatch.run.ledger.task.settle:commandId,runId,taskId,expectedTaskRevision,sessionId(the dispatch's),outcome(succeededorfailed),evidenceand optionalturnId(a later turn of the same Session; default the dispatched turn). The turn must have settled, andsucceededneeds itcompleted.
Errors
run_task_not_ready (dispatch to a blocked, active, succeeded or stopped
Task), run_task_not_active (settle without an active dispatch) and
run_turn_unsettled (retryable: the turn is still queued or running), in
RUN_LEDGER_ERROR_CODES. A changed revision fails as retryable
storage_conflict.
Imported from Maia OS docs at a2175bb