orchestrate
The coordinator for orchestrator mode. The human names the action:
/orchestrate status
/orchestrate start [feature NN | all] [--workers N]
/orchestrate resume <key>
/orchestrate integrate [<key>]
The orchestrator decides what approved work can execute now and how; the
integrator action decides whether completed work can land under human approval.
Neither implements, reviews for correctness, or accepts work. Every lifecycle
transition remains the ordinary ticket action.
When this applies
Only in a project whose context/execution-mode.md says
<!-- pathfinder:execution-mode orchestrator -->, read as
skills/ticket/SKILL.md §Execution mode defines it. In any other project —
human-in-the-loop, no mode file, or an invalid one — every action refuses,
names the file and the two values, and changes nothing.
Process
- Take the action from the invocation. If none was given, list the actions below and stop. If it is not one of them, say so, list them, and stop.
- Read only
skills/orchestrate/actions/<action>.mdand follow it exactly.
Actions
-
status— the operator’s view: every ticket in scope with its worker, execution state, branch and worktree, gate or blocker, and last recorded result. Reads only. -
start— plan the scope, ask the human once, then claim, dispatch, review, and surface gates for one round, and stop at the round boundary. A new session continues the run from engine state. -
resume— continue one existing claim deliberately: a stale worker, or one whose human gate was resolved. -
integrate— assume the integrator role, check completed work, request any necessary revalidation, present each merge for approval, complete and release.
The model
- A worker is a worktree. Each claimed ticket has exactly one linked Git
worktree at
.pathfinder/worktrees/<key>on one branchticket/<key>-<slug>. The worktree path is the worker’s identity and the claim. The worktree’s owncontext/current-ticket.mdis that worker’s transient state, so two workers never share one. - Git is the lock. A claim first creates
refs/pathfinder/claims/<key>, which Git creates only if it does not exist, under its own ref lock, so of any number of concurrent claims exactly one proceeds. A claim that then fails removes the branch and ref it created. The engine also refuses a ticket that already has a worktree, aticket/<key>-*branch, a claim ref, or an unregistered directory at its worktree path. There is no lock file, database, daemon, or server, and the claim ref is not sent by a normal push. - Claims outlive sessions. A session that dies leaves its worktree, branch,
and state file behind. Any claim this session did not start is
staleuntil a person confirms its old session has stopped. It is then continued deliberately, throughresumeor as a pending claim understart§Continuing a run, and never dispatched while another session may still own it. .pathfinder/is machine-local and ignored. The project’s.gitignoremust carry/.pathfinder/; the engine refuses to claim until it does and never edits the file itself.- The store is the board. Eligibility is
skills/ticket/actions/load.md§Readiness, computed from the configured store and nothing else. A status the engine cannot read — an unknown word, a GitHub issue closed while still labelled in progress, two status labels — is never eligible and never unblocks a dependent.
Engine
Paths below are relative to this skill’s own directory. The engine needs Node
and, for a GitHub Issues store, the gh CLI. It holds no state of its own:
every call re-derives its answer from Git, the worktrees, and the store.
engine/bin/orchestrate.mjs board | claim | owner | status | estimate | brief | check | judge | release | scratch | diskengine/store.mjs reads the configured ticket storeengine/board.mjs eligibilityengine/claims.mjs claims, from git worktrees and state filesengine/claim.mjs the one write: a claim ref, then a worktree on a new branchengine/integration.mjs conflict/divergence/overlap evidence and safe releaseengine/status.mjs the operator's view and its state vocabularyengine/mode.mjs the execution-mode and routing-policy markersengine/estimate.mjs estimate: complexity, context, parallel safety, riskengine/profile.mjs the pathfinder.execution-profile/1 schemaengine/policies/ routing policies; select: role, model, effortengine/route.mjs estimate, then select, into one profileengine/brief.mjs the worker brief and each harness's translationengine/plan.mjs the dispatch plan: claim now, wait, blocked, staleengine/comments.mjs every note the orchestrator writes, one spelling eachengine/tracker.mjs notes and the gate label, idempotent by markerengine/statefile.mjs updates to a worker's state file linesengine/stage.mjs current-head checkpoint validation and recovery sessionengine/experiments.mjs bounded transient Adversary experimentsengine/findings.mjs bounded transient independent Tester reviewengine/judgment.mjs optional Evidence Judge: bundle, validation, policy, checkpointengine/judges/ Evidence Judge providers; jev is the firstengine/scratch.mjs review scratch runs: owner, ticket, role, head; reclaim and pruneengine/deps.mjs content-addressed dependency cache and copy-on-write treesengine/disk.mjs physical free space, warning and floor thresholdsengine/process.mjs process identity and ancestry on macOS, Linux, WSL and Windowsprofile.md documents the profile, its thresholds, and routing policies.
brief.md documents the brief and the harness translation table.
worker-brief.md and resume-brief.md state what each brief asks a worker to do.
evidence-judge.md documents the optional Evidence Judge and its trust boundary.
Review scratch
Adversary and Tester rounds build their trees — base, head, mutants — in a
run under .pathfinder/scratch/<key>/, never in a harness’s private temporary
area, and never with a fresh npm ci per tree:
node skills/orchestrate/engine/bin/orchestrate.mjs scratch new <key> --role tester --head <sha> # prints <run-dir>, then: session <s>node skills/orchestrate/engine/bin/orchestrate.mjs scratch deps <run-dir> --tree head --session <s>PATHFINDER_SCRATCH_RUN=<run-dir> PATHFINDER_SCRATCH_SESSION=<s> \ node skills/orchestrate/engine/findings.mjs --checkpoint < report.jsonscratch deps installs each lockfile once into .pathfinder/deps/<key>/,
keyed by lockfile, package.json, Node, npm, platform and architecture, and
gives the tree its own copy-on-write clone: an APFS clone on macOS (only when
the cache and the tree share one APFS volume — cp -c silently copies
anywhere else), a reflink
on Linux filesystems that support it (Btrfs, XFS), otherwise a full,
independent copy with a warning — always on Windows, and on ext4, overlay or a
WSL /mnt/<drive> path. A full copy is checked against the disk floor first.
A mutant’s edits stay in its own tree; an install that links outside its own
tree (workspaces, file: packages, junctions) is refused rather than shared.
Windows, WSL and containers each key their own entries, and an owner recorded
on another platform or host is never judged dead. A checkpoint written with
PATHFINDER_SCRATCH_RUN, or scratch done, reclaims the run’s node_modules
and .next and keeps every log, report, patch and source. A failed or
interrupted run is preserved; scratch prune is a dry run until --execute,
and reclaims a preserved run’s dependencies only with --include-preserved.
Only a run’s owner process — or anyone, once that process is provably dead —
may add trees to it, finish it or fail it, and prune never touches a run whose
owner is alive or cannot be checked. One harness process can host several
sessions, so every run also has a session: scratch new mints a unique one
(or records --session), and deps, done, fail and the checkpoint hook
(PATHFINDER_SCRATCH_SESSION) require it. Where ancestry cannot be read (no
ps), scratch new needs --owner-pid, and the session then stands as the
proof of ownership. npm settings that change the install — NODE_ENV, the
install-affecting npm_config_*, the user and global npmrc — are part of the
cache key; lifecycle scripts that read other variables or files are not. npm never runs through a shell: on Windows the engine runs
the npm-cli.js beside Node, and --npm must name a .js entry point. A
reviewer killed mid-copy can leave an orphaned clone finishing in the
background; a retry into that tree refuses, and scratch done reclaims it. Clean-ups that remove a cache entry take its lock and rename it
out of place first, so a copy in progress is never left without its source.
New runs are refused below the disk floor — <!-- pathfinder:disk warn=25 floor=10 --> in context/execution-mode.md, in GB, or
PATHFINDER_DISK_WARN_GB / PATHFINDER_DISK_FLOOR_GB — unless
--allow-low-disk names the human’s explicit approval. disk reports
physical free space (per-file totals overstate clones); status stays
byte-stable and only names a threshold once it is crossed.
Verified so far: macOS (APFS clones) and Linux (full-copy fallback) run the
whole orchestrate suite; native Windows runs the scratch and cache tests in CI
and always takes the full-copy fallback. Not yet verified: the rest of the
orchestrate suite on Windows (its older harness tests are not portable), a
checkpoint reclaiming its own run end to end on Windows (scratch done is
tested there), the Windows paths for unreadable ancestry, a planted cache link,
a failed reclaim after checkpoint and antivirus EBUSY retries, a successful
Linux reflink on Btrfs or XFS, and WSL2 in any form.
A store other than local Markdown needs one machine-readable line in
context/tracker.md, described in skills/ticket/store.md.
Rules
- Run the one action the human named.
- Never implement, review, accept, or merge. Those belong to
developer,tester, and the human. - Never delete a worktree or a branch unless the human asks.
- Never edit
.gitignore,context/execution-mode.md, or a ticket’s substance. - Human authority is unchanged: approval, acceptance, merge, and release are the human’s in this mode exactly as in human-in-the-loop.