Worktrees
One git worktree per blueprint, plus the synthetic prepare step that keeps it ready.
contractor runs every blueprint in its own git worktree under contractor/.worktrees/<blueprint-name>/ on a contractor/<blueprint-name> branch. The worktree directory is gitignored from the main repo so the in-flight branches don't pollute its git status. Several blueprints can be in flight simultaneously without stepping on each other, and a stalled blueprint can be picked up later without disturbing whatever else has happened on main.
contractor blueprint new --worktree just checks out the branch — it does not install dependencies, build, or otherwise prepare the directory for development. That work is the prepare step's job, and contractor injects it on demand.
The prepare step
Any agent or shell step in a pipeline may declare needs_prepared_worktree: true. Both shipped schemas set the flag on their implement phase, so any blueprint using contractor-base or contractor-lite gets a fresh prepare on first implement (and again whenever HEAD moves).
When the runner reaches a flagged step, it consults the global worktree_prepare table. If no row matches (repo_root, blueprint, worktree_path), or the row's head_commit no longer matches git rev-parse HEAD in the worktree, the runner injects a synthetic prepare step before the scheduled step. On success, the runner upserts the row with the current HEAD. A prepare already executed in the same pipeline run is memoized in process memory, so a second flagged step does not re-trigger.
The prepare step is itself an agent step with id prepare, spawned through the same agent-step executor as any other agent step — it gets its own runs row, its own log, and its own metrics. Its instruction is rendered by renderWorktreePrepareInstruction(repoRoot) from the shipped template at packages/cli/schemas/partials/prepare.md, with the user's contractor/partials/prepare.md (if any) inlined into a prepare-block region. See Partials.
The prepare instruction explicitly forbids editing source files. The agent may only touch dependency artifacts: lockfiles, node_modules/, .venv/, target/, language-specific cache directories. This keeps prepare cheap to re-run and keeps it from accidentally drifting the working tree.
Prepare failures propagate: the scheduled step does not run, the pipeline fails, and the marker is not written. A subsequent resume re-evaluates the marker and re-runs prepare if it is still invalid.
HEAD-based invalidation
The marker stores the worktree's HEAD at prepare time. Before each run, the runner compares the stored head_commit against git rev-parse HEAD:
- HEAD unchanged — marker valid, prepare skipped.
- HEAD moved — marker invalid, prepare re-runs.
A rebase, pull, or checkout that moves HEAD invalidates the marker. This over-triggers on commits that did not touch dependencies, but the prepare agent's own fast-path ("are dependencies already installed and the project ready?") keeps the re-run cheap. Lockfile-hash invalidation was a deliberate non-goal — it would require contractor to know which lockfiles to hash, which is a per-stack guess.
The worktree_prepare table
The marker lives in the global SQLite database at ~/.contractor/contractor.db:
worktree_prepare
repo_root TEXT NOT NULL
blueprint TEXT NOT NULL
worktree_path TEXT NOT NULL
prepared_at TEXT NOT NULL
head_commit TEXT NOT NULL
PRIMARY KEY (repo_root, blueprint, worktree_path)
The composite primary key reflects that the same blueprint name in different repos is distinct, and the same blueprint in different worktree paths (a rare case, but real) is also distinct. Nothing is per-machine — the DB is per-user and shared across every repo on the host.
Manual prepare
You can pre-warm a worktree outside a pipeline run:
contractor worktree prepare # infer blueprint from CWD
contractor worktree prepare <name> # explicit blueprint
contractor worktree prepare <name> --force # bypass marker, always runThis spawns a degenerate pipeline run consisting of a single shell step (true) flagged needs_prepared_worktree: true, so the synthetic prepare runs and writes the marker just like any other run. Useful for warming up dependencies before walking away to make tea, or for forcing a re-prepare after a tooling upgrade that did not move HEAD.