This example shows how another coding agent can call Pixir as a local
Subagents-as-a-Service runtime through pixir delegate.
The files here are deterministic examples, but running them without --dry-run
uses the configured Pixir Provider. Use pixir for an installed escript and
./pixir from a source checkout.
Contract
Streams
- stdout is one final JSON envelope.
- stderr is empty unless progress is explicitly requested.
--progress=stderr-jsonlis currently fordelegate attach; attachedpixir delegate --spec ... --jsonrejects--progressuntil it can emit real progress frames.- The Log under
.pixir/sessions/is the durable source of truth;diagnoseandtreeare projections derived from that Log.
Exit Codes Are Path-Scoped
Do not generalize one exit code across all pixir subcommands. Branch on the
command path first, then parse stdout JSON.
| Path | Exit | Meaning for that path | Caller action |
|---|---|---|---|
pixir delegate --spec ... --dry-run --json | 0 | Spec accepted; no Provider, Subagent, Workflow, host command, or artifact execution started. | Safe to run without --dry-run if the plan matches intent. |
pixir delegate --spec ... --dry-run --json | 2 | Invalid args/spec, unreadable stdin/spec, unsupported mode, or unknown role. | Inspect kind, details, field / json_pointer, and next_actions; fix the spec before runtime. |
pixir delegate --spec ... --dry-run --json | 3 | Bounded write policy rejected the planned write scope. | Narrow the child write_set or explicitly expand write_policy.allow_writes. |
attached pixir delegate --spec ... --json | 0 | Delegate work reached a clean success state; expect ok: true and status: "completed". | Consume children, diagnostics commands, artifacts, and summaries as evidence pointers. |
attached pixir delegate --spec ... --json | 3 | Permission, workspace, read-confinement, disabled-shell, or bounded-write policy denial. | Distinguish kind: "outside_workspace", kind: "write_policy_denied", kind: "permission_denied", and kind: "bash_disabled" before retrying. |
attached pixir delegate --spec ... --json | 4 | Provider/auth/network class failure. | Inspect kind, provider diagnostics, and retry or re-auth guidance. |
attached pixir delegate --spec ... --json | 5 | Runner-level failure before delegated work reached a terminal state: backpressure, unavailable manager, or daemon requirement. | Inspect the error kind and details; retry with adjusted budgets or daemon setup. |
attached pixir delegate --spec ... --json | 6 | Domain work reached an incomplete terminal state such as partial, timed_out, failed, or cancelled — attached child timeouts normalize to status: "timed_out" and exit here, not 5. | Parse the envelope; inspect children[*].status, workflow buckets, write_destination, and diagnostics before deciding whether the result is usable. Non-completed subagent-strategy children carry ready-made resume_command / diagnose_command (recover per child, never re-run the whole spec); workflow-strategy steps carry buckets and evidence instead, so recover those via diagnostics and the step logs. |
one-shot pixir "prompt" / pixir resume ... | 6 | The turn completed without a final assistant message. | Use the emitted resume/diagnose guidance; do not infer Delegate partial semantics. |
delegate status / delegate attach / delegate cancel | path-specific | Liveness commands reuse the same JSON envelope vocabulary, but status/snapshot acceptance is not proof that delegate work succeeded. | Inspect ok, status, owner/runtime fields, and child evidence. |
Reading Delegate Results
Treat ok, status, and the shell exit code as the first branch only. A top-level
status: "partial" means "not cleanly completed"; it is an incomplete umbrella, not
proof that useful partial work exists. Before acting, inspect:
children[*].status,children[*].checkpoint_status, and childchild_session_id;- for subagents spawned from delegate
tasks[],children[*].indexis the zero-based task-array position. Treat thechildrenarray order as unspecified; join results to requested tasks bychildren[*].index, not by display position; - workflow buckets such as
held_steps,failed_steps,partial_steps,needs_orchestrator_steps, andsafe_next_actions; - bounded-write
write_destination; diagnostics.diagnose_commandanddiagnostics.tree_command.
For direct child Subagents from a root delegate Session, use max_depth: 1.
max_depth: 0 is a useful rejection fixture, not a working fanout setting.
Bounded Write Results
Bounded-write envelopes report where Pixir believes writes landed. A partial
bounded-write result can still have applied a subset of writes before a later denial
or failure. observed_applied_writes is an at-least observation from child Logs when
available; if it is absent, that is not proof of zero writes unless
write_destination.contract_status explicitly says no workspace write was applied.
Illustrative non-golden partial bounded-write envelope:
{
"ok": false,
"status": "partial",
"exit_code": 6,
"strategy": "workflow",
"write_destination": {
"writes_applied_to": "indeterminate",
"contract_status": "unverified_partial_writes",
"workspace_modes": ["shared"],
"observed_applied_writes": ["notes/e1.md"],
"observed_writes_source": "child_log",
"observed_writes_semantics": "at_least"
},
"children": [
{
"step_id": "write",
"status": "failed",
"checkpoint_status": "failed",
"writes_applied_to": "indeterminate",
"observed_applied_writes": ["notes/e1.md"],
"next_actions": ["retry_failed_step"]
}
],
"safe_next_actions": ["retry_failed_steps"]
}Denial Kinds
Do not collapse read/scope denials and write allowlist denials:
kind: "outside_workspace"withmatched_rule: "outside_workspace"means a read/scope escape, such as a shell-shaped path token resolving outside the workspace. This is a tripwire, not a full POSIX sandbox.kind: "write_policy_denied"means the requested write exceeded the bounded write policy.kind: "permission_denied"means the permission mode (ADR 0006) rejected the request, independent of any bounded write policy.kind: "bash_disabled"means the shell tool itself is disabled by the bounded write policy (v1 always disables it); the command's read/write nature is irrelevant. This denial does not end the child's Turn: the model is expected to adapt vianext_actions(use_native_read_tools,use_edit_or_write_within_allowed_globs). Do not respond by widening write globs; they are unrelated.
Illustrative non-golden outside_workspace denial:
{
"ok": false,
"status": "rejected",
"exit_code": 3,
"kind": "outside_workspace",
"message": "bash command references a path outside the workspace",
"details": {
"tool": "bash",
"token": "$HOME/notes.txt",
"requested_command": "cat $HOME/notes.txt",
"matched_rule": "outside_workspace",
"next_actions": [
"use_workspace_relative_paths",
"use_pixir_read_tool_for_file_access",
"run_pixir_from_the_intended_workspace_root"
]
}
}Canonical permission_decision events may omit next_actions even when the
underlying tool error details contain them. Prefer the terminal tool error/details
and top-level next_actions / safe_next_actions when present.
host_boundary.external_process_spawns is scoped to the delegate entrypoint, not to
host tools that child Sessions may call. Inspect child diagnostics for child
host-command evidence.
Preflight
mix escript.build
./pixir doctor --json
For installed Pixir:
pixir doctor --json
Attached Call
Use this when the caller wants Pixir to return only after the child Subagents finish or reach a terminal partial state.
./pixir delegate --spec docs/examples/delegate-cli-live/attached-subagents.json --dry-run --json
./pixir delegate --spec docs/examples/delegate-cli-live/attached-subagents.json --json
For shell callers, treat a non-zero exit as actionable even though stdout remains parseable JSON:
if ! result="$(./pixir delegate --spec docs/examples/delegate-cli-live/attached-subagents.json --json)"; then
printf '%s\n' "$result" | jq '{ok,status,kind,summary,next_actions}'
exit 1
fi
printf '%s\n' "$result" | jq '{ok,status,delegate_id,parent_session_id,children}'
Optional transport preference: subagents.transport (or top-level
transport) accepts auto, websocket, or http_sse; invalid values fail
the dry-run with a structured error, and the effective value is surfaced as
limits.transport in plan and envelope.
Optional provider knobs, mirroring ACP session/prompt _meta:
subagents.model (a model id string) and subagents.reasoning_effort
(low, medium, high, or xhigh) apply to every child of the
delegation; a spec knob wins over config defaults. Effective use is
evidenced by each child's provider_usage events, not echoed in the
delegate envelope.
Delegate specs are validated fail-closed: an unknown top-level or
subagents key is rejected as invalid_spec with field, json_pointer,
path, and next_actions (the dry-run reports it exactly like the real
run). Workflow step internals remain governed by the workflow strategy
validator.
Useful fields for callers:
okstatusdelegate_idparent_session_idchildren[].statuschildren[].index- for subagents spawned fromtasks[], the durable zero-based source task position;childrenarray order is unspecified, so callers must not rely on it for task/result matchingchildren[].child_session_idchildren[].retry_attempts/children[].retry_max_attempts/children[].current_attempt_index/children[].retry_history- present only when the runtime auto-retried that child (bounded retry for websocket-family and provider-declared-retryable server errors on read-only children);retry_historypreserves each failed attempt's session id and error kinddiagnostics.diagnose_commanddiagnostics.tree_commandhost_boundary.external_process_spawns_scope
Async Service Call
Use this when the caller wants separate start, status, attach, and cancel
invocations. Start the manual workspace-local daemon in one terminal:
./pixir delegate daemon --foreground --json
delegate start requires that resident daemon. Without it, Pixir rejects start
instead of returning a running handle whose owner would die with the short-lived CLI
process.
From another managed shell/process:
delegate_json="$(./pixir delegate start --spec docs/examples/delegate-cli-live/async-subagents.json --json)"
delegate_id="$(printf '%s\n' "$delegate_json" | jq -r '.delegate_id')"
./pixir delegate status "$delegate_id" --json
./pixir delegate attach "$delegate_id" --json --progress=stderr-jsonl --wait-horizon-ms 5000
./pixir delegate cancel "$delegate_id" --json
./pixir delegate attach "$delegate_id" --json
./pixir delegate daemon --stop --json
After the daemon stops, attach falls back to a durable snapshot from the local
Session Log. That fallback is expected and should be treated as inspectable
state, not as live owner capability. Async status, attach, and cancel
project the same children[].index values from durable parent Log
subagent_event data, even when snapshot children are sorted by subagent id.
Claude Code Pattern
Claude Code can call Pixir the same way Codex does: use the shell, write a bounded
spec, run --dry-run --json, then choose attached or async execution.
Suggested Agent Instruction Block
Use this block for Claude Code, Codex, or another shell-driven agent:
Use Pixir delegate as a local audited executor. Do not edit files.
First run ./pixir doctor --json. Create a temporary delegate spec with
strategy "subagents", mode "read_only", max_depth 1, and bounded tasks. Run
./pixir delegate --spec <spec> --dry-run --json and inspect the JSON. If accepted,
run attached with ./pixir delegate --spec <spec> --json and treat a non-zero exit
as incomplete or failed work while still parsing stdout JSON for next_actions. Use
async service mode only when a managed resident daemon is already running or can be
started and later stopped explicitly: ./pixir delegate daemon --foreground --json.
Then call start/status/attach/cancel from another managed shell. Parse stdout as
the final JSON envelope. Parse stderr as JSONL only when --progress=stderr-jsonl is
used. Use diagnose/tree commands from the result for evidence. Do not treat prose
summaries as proof.Minimal Claude shell flow:
cat > /tmp/pixir-delegate.json <<'JSON'
{
"contract_version": 1,
"strategy": "subagents",
"mode": "read_only",
"workspace_mode": "shared",
"subagents": {
"role": "explorer",
"max_threads": 2,
"max_depth": 1
},
"tasks": [
"Read README.md only. Return 2 bullets about Pixir. Do not edit files.",
"Read CONTEXT.md only. Return 2 bullets about the Log. Do not edit files."
],
"limits": {
"timeout_ms": 120000
}
}
JSON
./pixir delegate --spec /tmp/pixir-delegate.json --dry-run --json
./pixir delegate --spec /tmp/pixir-delegate.json --json | jq '{ok,status,delegate_id,parent_session_id,children}'
For Claude, the practical rule is simple: let Pixir own fanout and evidence; Claude owns the outer decision loop and reads Pixir's JSON, diagnostics, and Session ids.