Cockpit Orchestrate Workflow
gitmoot orchestrate --cockpit renders one live
Herdr pane per delegation subagent so you
can watch an orchestra
fan out as it runs — in the terminal, and (through herdres) mirrored to Telegram.
It is opt-in and fail-open: with --cockpit off, or when Herdr is absent or
unreachable, orchestration is byte-identical to today. Gitmoot imports no Herdr
code; the cockpit drives Herdr over its CLI only.
Turning it on
--cockpit (alias --herdr) is a flag on gitmoot orchestrate:
gitmoot orchestrate review-panel "Review PR #123 in this repo." --repo owner/repo --cockpit
When the flag is set, every child job in the delegation tree opens its own pane:
the coordinator splits a workspace for the run, each subagent gets a pane labeled
<agent> · d<depth> · <branch>, and that pane streams the child's progress while
the job runs. Children inherit the cockpit setting from their parent, so a single
--cockpit on the root lights up the whole orchestra.
Each pane renders its explicitly selected job or seat tee log through gitmoot job watch --transcript. With opt-in [transcripts], a universal per-job tee is
retained independently of cockpit; seat logs stay transient. Codex JSONL is readable live. Kimi stream-json is turn-buffered,
and kimi-code 0.19.2 emits no usage. Claude currently emits one final JSON
envelope, so its pane remains quiet until completion and then shows final text
and usage. Shell output is redacted raw passthrough. Unknown or malformed lines
degrade individually to redacted bounded raw output; a fatal renderer exit or
panic falls back externally to tail -F. The tee bytes, runtime result parsing,
and pipeline progress stream are unchanged.
On an interactive terminal, tool-specific icons, honest multi-line output
previews, elapsed times, and lightweight narration markup make the stream easier
to scan. Pipes and redirects retain the byte-stable plain format. gitmoot job transcript <id> --export md creates a deterministic ANSI-free snapshot;
--export jsonl creates a schema-versioned redacted trajectory, and job transcript --all ... --export jsonl is the guarded bulk form. Raw retained logs
are unredacted mode-0600 files; export masking is best-effort, not a vault.
Verified Codex command/file-change events and Kimi function tool calls/results use typed compact lines; other shapes retain the generic/raw path. Render-time redaction is per-line best-effort defense in depth: a secret split across physical lines may be only partially masked, and the raw log plus external tail fallback remain unredacted.
Auto-detect: if you run gitmoot orchestrate from inside a Herdr session
(HERDR_ENV set), the cockpit turns on automatically — no --cockpit needed. An
explicit flag still works anywhere; cockpit_mode = off (below) is the host-level
veto; outside a Herdr session it stays off unless you pass the flag.
Pick where the run lands with --cockpit-session:
gitmoot orchestrate decompose-and-verify "Implement the export feature." \
--repo owner/repo --cockpit --cockpit-session feature-export
Terminal and Telegram
The same panes are visible two ways:
- Terminal — open the Herdr workspace to watch the panes split and update live as each subagent works.
- Telegram — with the herdres bridge connecting Herdr to Telegram, each pane surfaces as a forum topic / agent status you can read (and, in later phases, steer) from your phone.
Close pane ≠ cancel job
A cockpit pane is a view, not the job. Closing a pane (in the terminal or from Telegram) tears down the visible surface but does not cancel the underlying job — the child keeps running in the daemon and its result is still captured and synthesized. To stop work, cancel the job through the record path:
gitmoot job list --repo owner/repo # find the child job id
gitmoot job cancel <job-id> # cancel via the record; pane teardown follows
This separation is deliberate: a Herdr call must never fail or stall a job, so panes are best-effort and disposable while the job lifecycle stays owned by the engine.
Configuration: the [orchestrate] section
Defaults live in ~/.gitmoot/config.toml under [orchestrate] and can be
overridden per run by the flags above:
[orchestrate]
cockpit_mode = "auto" # on | off | auto (auto = on when launched in a Herdr session)
cockpit_session = "" # default Herdr session/workspace label ("" = per-run)
cockpit_max_panes = 4 # cap on simultaneous panes per run
cockpit_pane_key = "job" # job (one pane per job) | seat (reuse a pane per role)
inline_artifact_bodies = false # inline each child's artifact_body into the coordinator continuation
inline_artifact_max_bytes = 32768 # per-body cap (bytes) when inlining is on
inject_upstream_dep_context = false # inject succeeded upstream dependency results into a dependent leg's prompt; default off
max_delegation_token_budget = 0 # per-root delegation token budget (input+output); 0 = unlimited (off)
max_delegation_cost_usd = 0 # per-root delegation dollar-cost budget (USD); 0 = unlimited (off)
max_consecutive_missed_wakes = 0 # flag org roles after N stalled wakes; 0 = disabled
default_delegation_timeout = "" # default child-job timeout when a delegation omits one; "" = unbounded
default_plan_timeout = "" # per-phase defaults (plan/implement/review/gate/repair) that
default_implement_timeout = "" # win over default_delegation_timeout for legs tagged with
default_review_timeout = "" # the matching phase (or action)
default_gate_timeout = ""
default_repair_timeout = ""
cockpit_mode = "off"disables the cockpit even if--cockpitwas passed;"auto"(the default) auto-enables it when you runorchestratefrom inside a Herdr session (and honors an explicit--cockpitanywhere). Either way a pane is only actually opened whenherdr statusis ok; otherwise the requested run emits a singlecockpit_unavailablejob event and proceeds without panes.cockpit_pane_key = "job"opens one pane per child job.seatmode reuses a single pane per logical role across phases so a long run does not accumulate panes.inline_artifact_bodies(defaultfalse) inlines each child'sartifact_bodyinto the coordinator continuation prompt as a fenced block, so the coordinator reads the briefs without re-fetching them from disk. Off by default, the continuation is byte-identical to before.inline_artifact_max_bytes(default32768, i.e. 32 KiB per body) caps how many bytes of each child's body are inlined; longer bodies are rune-safe truncated with a marker pointing at the full on-disk brief. A per-continuation aggregate cap also bounds the total inlined across all children.inject_upstream_dep_context(defaultfalse) injects succeeded upstream dependency results into a dependent leg's prompt; default off.max_consecutive_missed_wakes(default0= disabled) flags an organization role inorg chartandorg statusafter that many consecutiveagent_prompt_stalledwake outcomes. A delivered wake resets the role; Herdr transport failures do not count.max_delegation_token_budget(default0= unlimited/off) bounds a delegation tree by cost in addition to depth/width/total-jobs/wall-clock. When set to a positive value, the whole tree under one root is capped at that many cumulative tokens (input + output across every job in the tree); a coordinator that tries to fan out after the tree has already used at least the budget is refused with adelegation_cost_exceededevent and routed to the graceful finalize continuation. Token capture is best-effort per runtime — Claude reports usage via--output-format json, Kimi reports it when its stream emits ausageobject, and Codex reads usage from itscodex exec --jsonJSONL stream — a resumed session's usage is session-cumulative, so it records only the per-session delta (#661); older CLIs that predate the flag fall back to0. Treat it as a coarse runaway-cost backstop, not a precise spend limit; the budget is in raw tokens. Leaving it at0is byte-identical to before the knob existed.max_delegation_cost_usd(default0= unlimited/off) is the dollar-cost analogue of the token budget (#380): it bounds the same tree by its measured spend rather than raw token count. Cost is derived from the same per-job token usage the token budget already sums, priced through a small built-in per-model price table (per-1M-token list prices — Haiku0.25/1.25, Sonnet3/15, Opus15/75input/output USD — matched by substring against each job's model id; an empty or unrecognized model id is priced at the mid-tier Sonnet default so it is never free). When the tree's accumulated cost reaches the budget, the next fan-out is refused with adelegation_cost_usd_exceededevent and routed to the same graceful finalize continuation (synthesize what completed, then stop) — it is never hard-killed. Because cost rides on the same best-effort token capture and a hardcoded price table, it is a coarse runaway-cost backstop, not a precise spend meter. Leaving it at0is byte-identical to before the knob existed.default_delegation_timeoutand the per-phasedefault_plan_timeout/default_implement_timeout/default_review_timeout/default_gate_timeout/default_repair_timeout(#548, all empty = unbounded by default) supply a child-job timeout when a delegation omits its owntimeoutfield. Precedence: per-delegationtimeout> the phase default matching the delegation'sphase(falling back to itsaction) >default_delegation_timeout> unbounded (the historical behavior). Values are Go durations (e.g."30m").
Constrained hosts
On a small box (few cores, limited terminal real estate, a shared daemon) the pane count is what bites, not the jobs. Keep it bounded:
- Lower
cockpit_max_panes. The default is4; set it to2(or1) on a constrained host. Beyond the cap, extra subagents run status-only — they still report state to Herdr but do not split a new pane, so the work fans out exactly as without the cockpit while the visible surface stays small. - Prefer
cockpit_pane_key = "seat"for long multi-phase runs so panes are reused per role instead of accumulating one per job. - The cockpit never changes the engine. The delegation DAG, the result contract, the runtime-session locks, and the checkout keys are all unchanged whether the cockpit is on, off, or unavailable — so capping panes only changes what you see, never how the orchestra runs.
If Herdr is not installed or herdr status is not ok, --cockpit is a no-op
beyond a single cockpit_unavailable event on the root job; the run proceeds
unwrapped.
Smoke test
The optional scripts/cockpit-smoke.sh script in the repository confirms the
cockpit is opt-in and fail-open. It runs against an isolated --home, exercises
the --cockpit wrap path when herdr is reachable, and skips cleanly when
herdr or gitmoot is unavailable, so it never fails a normal checkout that has
no Herdr installed.
See the Coordinator Recipes Workflow for the recipes you will most often watch in the cockpit, and the Result Contract for the delegation fields and termination bounds an orchestra runs inside.