One spawn shape, for every harness. The drive cadence lives in the
/dispatch router; this skill is the spawn call plus the
landing mechanics.
See lib:reference/dispatch-mechanics.md § The worker returns a working tree,
not a commit.
Spawn
scripts/spawn-confined.sh <harness> <B> <BRANCH> <DIR> <PROMPT_FILE> [BACKSTOP_SECS]
<harness> is pi or claude. The script does the whole pre-spawn sequence
itself — you do not fork separately:
- Capability probe. Linux asserts
bwrapis present and refusesbwrap-unavailablebefore minting a fork; macOS gets its named refusal fromdoctrine worktree jail-prefix, which fails closed and writes no prefix. There is no unconfined fallback — an unresolvable jail aborts the spawn rather than degrading (an empty prefix is caught by its own guard). - Fork.
doctrine worktree fork --base <B> --branch <BRANCH> --dir <DIR> --worker, orchestrator-classed, run from the project root before any confinement. The fork is deliberately unbound — no--slice/--phase(see The funnel, below). - Confine + exec.
timeout <BACKSTOP> <jail prefix> <harness exec>, delivering<PROMPT_FILE>to the worker (stdin forclaude; one compact RPC line forpi) and capturing the transcript. The jail rw-binds the fork dir and the harness config dir, ro-binds the rest, and setsDOCTRINE_WORKER=1inside the namespace. - Hand back.
claude -pexits when the turn ends — process exit is the completion signal.pi --mode rpcnever self-exits, so that profile alone holds stdin open with a fifo and polls for its typed settle event. That asymmetry is the only harness difference beyond the config dir and the exec line.
See lib:reference/dispatch-mechanics.md § Workers can silently discard their
own work for the prompt-fencing rules (clean checkout, forbidden verbs,
edit-not-discard for a red proof).
Never trust the worker's self-reported success. The tree is the ground truth.
Landing
The delta is an uncommitted working tree that persists after the process exits — nothing auto-reaps it.
- Import.
doctrine worktree import --base <B> --from-worktree <DIR> --slice <N>— gathers the tracked + untracked delta, runs theclassify_importscope belt and the reject-and-halt prove gate perlib:reference/dispatch-mechanics.md— a red delta halts staged, never auto-fixed (land-or-reject, never rewrite). Any refusal is report-and-halt — never import around it. - Commit ONE on the coordination branch — the orchestrator's own act, and the reason import is non-committing.
- Record the boundary.
doctrine slice record-delta <N> PHASE-NN --commit <S>, withSpinned at the code commit, never read fromHEADat record time. Knowledge commits trail the code commit, soHEADmay have moved;--commitrecords exactlyS's own patch, which excludes trailing knowledge and any refresh-base merge by construction. This write is not skippable —dispatch sync --prepare-review's completeness gate halts if a completed phase has no registry row. - Verify and conclude. Run the phase's verification on the coordination
tree (the worker's own report is advisory), then flip the sheet:
doctrine slice phase --status completed <N> PHASE-NN. - Reap the spent fork:
doctrine worktree gc --fork <BRANCH>— the unfunnelled-fork case its patch-id oracle is narrowed to, and idempotent. Nevergit worktree remove --forcean unimported tree: that deletes the sole copy of the delta.--dry-runprints the verdict and destroys nothing.
The funnel: a bound fork's machinery, not this path's
See lib:reference/dispatch-mechanics.md § The funnel record, and when it is
the driver — this path forks unbound, so no row lands and next sits at
spawn.
Red Flags
Never: instruct or expect a worker to commit (its .git is read-only —
there is no self-commit path); spawn outside the script (a hand-rolled exec is
an unconfined worker); omit the backstop timeout; eval the prompt; use a
heredoc for the pi profile's RPC stdin (EOF kills it); import around a scope or
prove-gate refusal (a refusal is a defect or a halt, never a detour); auto-fix a
red delta into the import; force-remove a fork before its delta has landed;
trust the worker's own green/failure labels; treat a spawn prescription from
dispatch next as a stuck funnel.
Always: halt on a non-zero spawn (fail-closed is the whole posture — a
confinement that cannot be established means no worker); pass a pre-distilled,
self-contained prompt; import with --slice so the scope belt has selectors to
check; pin S at the code commit before recording the boundary; return to the
router for the drive cadence.