Skip to content

plan.json schema

A plan.json describes a DAG of agent tasks submitted to the orchestrator via agora orch submit. It deserializes into a Run — a set of WorkItems plus their edges, placed on a named queue. The schema below is the Run / WorkItem shape from agora-orchestrator.

interface Run {
id: string; // run id (also overridable via `agora orch submit --queue` for queue)
queue: string; // named queue this run is placed on
items: WorkItem[]; // the DAG nodes
}

agora orch submit --queue <name> overrides the plan’s queue at submit time.

Each entry in items is one dispatchable DAG node:

interface WorkItem {
id: string; // unique within the run
executor: string; // id of the registered Executor that runs this item
inputs: Record<string, unknown>; // forwarded to the executor
depends_on: string[]; // ids of items that must reach `done` before this readies
resourceLocks: string[]; // shared resource keys that serialize contending items
subagentShape?: string; // optional: id of a registered SubagentShape; when set, `inputs` is validated against its inputSchema
}
FieldTypeRequiredMeaning
idstringyesItem id, unique within the run. Referenced by other items’ depends_on.
executorstringyesThe registered Executor that runs this item (e.g. dispatch).
inputsobjectyesFree-form inputs forwarded to the executor. For the dispatch executor these include subagent and workerInput.
depends_onstring[]yesIds of items in the same run that must reach done before this item readies. Empty array = no dependencies.
resourceLocksstring[]yesShared resource keys. Items holding overlapping keys serialize; items with disjoint keys fan out in parallel. Empty array = no locks.
subagentShapestringnoWhen set, the item’s inputs are validated against the named SubagentShape’s inputSchema.

This is examples/offload-fanout/plan.json — a four-item fan-out: three independent edits (disjoint locks, run in parallel) followed by a verify that depends on all three.

{
"id": "fanout-1",
"queue": "default",
"items": [
{
"id": "edit-alpha",
"executor": "dispatch",
"inputs": { "subagent": "code-edit", "workerInput": { "file": "alpha.ts" } },
"depends_on": [],
"resourceLocks": ["fixture/alpha.ts"]
},
{
"id": "edit-beta",
"executor": "dispatch",
"inputs": { "subagent": "code-edit", "workerInput": { "file": "beta.ts" } },
"depends_on": [],
"resourceLocks": ["fixture/beta.ts"]
},
{
"id": "edit-shared",
"executor": "dispatch",
"inputs": { "subagent": "code-edit", "workerInput": { "file": "shared.ts" } },
"depends_on": [],
"resourceLocks": ["fixture/shared.ts"]
},
{
"id": "verify",
"executor": "dispatch",
"inputs": { "subagent": "verify" },
"depends_on": ["edit-alpha", "edit-beta", "edit-shared"],
"resourceLocks": []
}
]
}

Once submitted, each item carries a mutable status from this closed set: pending, ready, running, done, failed, skipped, cancelled. The terminal subset is done / failed / skipped / cancelled. When an item fails or is cascaded, its persisted state carries a reason string. These are internal run-state fields (ItemState), not part of the submitted plan.

A WorkItem itself does not pin a target, env bundle, or worker image — those bindings live on the executor configured in agora.config, not in the plan. For the dispatch executor (DispatchExecutor), the agora.config.mjs wires target, workerImage, and secrets; the plan item supplies only inputs.subagent and the per-item workerInput. This keeps the plan portable across environments — the same plan.json runs locally or against Fargate depending solely on the executor wiring.