# Task-Worktree Provisioning Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use /ship (recommended) or /executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

audience: AI coding agents first.

**Goal:** Provision every v2 task worktree before any coder, reviewer, fixer, or gate enters it; classify terminal provisioning failure as blocked infrastructure.

**Architecture:** Put deterministic command resolution, bounded execution, atomic receipt creation, and receipt admission in `scratch.js`. Keep `dispatch.js` as low-level fail-closed admission. Wire provisioning before every seat/gate entry in `run.js`; provisioning occupies the task's existing concurrency slot and never enters agent retry, fallback, or gate-fix ladders.

**Tech Stack:** Node.js CommonJS, `node:test`, NDJSON journals, JSON Schema, git worktrees, existing `runChild` process runner.

---

## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | Task 1 | `modules/harness/v2/scratch.js`, `modules/harness/v2/test/scratch-provisioning.test.js` | single task |
| 2 | Task 2 | `modules/harness/v2/dispatch.js`, `modules/harness/v2/test/dispatch-provisioning.test.js` | single task |
| 3 | Task 3 | `modules/harness/v2/run.js`, `modules/harness/spec/events.schema.json`, `modules/harness/v2/test/worktree-provisioning.integration.test.js` | single task |
| 4 | Task 4 | `modules/harness/v2/test/index.js` | single task |

## File Structure

- `modules/harness/v2/scratch.js` — resolve and execute provisioning; own receipt/error contracts.
- `modules/harness/v2/dispatch.js` — reject direct dispatch into an unprovisioned worktree.
- `modules/harness/v2/run.js` — provision before every seat/gate entry; convert terminal provisioning failure into blocked infrastructure state.
- `modules/harness/spec/events.schema.json` — validate terminal `provision.failed` journal events.
- `modules/harness/v2/test/scratch-provisioning.test.js` — resolution, timeout, retry, log, receipt, and skip coverage.
- `modules/harness/v2/test/dispatch-provisioning.test.js` — low-level dispatch receipt admission coverage.
- `modules/harness/v2/test/worktree-provisioning.integration.test.js` — fresh-worktree toolchain, lifecycle, failure classification, and retry-blocked coverage.
- `modules/harness/v2/test/index.js` — full-suite registration only.

### Task 1: Provisioning Core and Receipt

**Wave:** 1
**Blocks:** Task 2, Task 3, Task 4
**Blocked by:** —

**Files:**
- Modify: `modules/harness/v2/scratch.js` — provisioning resolution, execution, receipt, and typed failure seams.
- Create: `modules/harness/v2/test/scratch-provisioning.test.js` — focused provisioning-core tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- `PROVISION_RECEIPT = '.harness-provisioned'`
- `DEFAULT_PROVISION_TIMEOUT_MS = 600000`
- `resolveProvisionCommand({ worktree, meta }) -> { source:'meta'|'hook'|'pnpm'|'npm'|'yarn'|'cargo'|'none', cmd:string|null, argv:string[]|null }`
- `provisionReceiptPath(worktree) -> string`
- `isProvisioned(worktree) -> boolean`
- `assertProvisioned(worktree, phase) -> void`
- `provisionWorktree({ worktree, meta, taskId, logPath, runChild?, now? }) -> Promise<{ receiptPath:string, receipt:{cmd:string|null,exitCode:0,at:string,durationMs:number}, attempts:number }>`
- `WorkspaceProvisionError` exposes `failureClass:'workspace-provision-failed'`, `cmd`, `exitCode`, `logPath`, and bounded `logTail`.
- Meta override runs as `/bin/sh -lc <meta.provision_cmd>`. Repo hook runs as `<worktree>/.harness/provision.sh`. Auto-detected argv: pnpm `['pnpm','install','--frozen-lockfile','--prefer-offline']`; npm `['npm','ci']`; yarn `['yarn','install','--frozen-lockfile']`; Cargo/none `null`.

**Behavior:**
- Stop at first resolution rung: non-empty `meta.provision_cmd`; executable repo hook; `pnpm-lock.yaml`; `package-lock.json`; `yarn.lock`; `Cargo.toml`; none.
- Run from worktree root with `meta.provision_timeout_ms` or 600000ms. Reject non-positive/non-integer timeout and non-string/empty explicit command before execution.
- Existing receipt skips command execution.
- Cargo and no-match are successful no-ops and still write receipt.
- Failed command or timeout retries once with same command and log path. Second failure throws `WorkspaceProvisionError`; no receipt is written.
- Successful execution atomically writes exactly one JSON line to `<worktree>/.harness-provisioned`; partial receipt is never observable.
- Provision stdout/stderr goes only to supplied log path. Error retains a 20-line, 64KiB-bounded tail.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/scratch-provisioning.test.js`
- Expected: PASS — ladder precedence, exact argv/cwd, override timeout, receipt skip/no-op/atomic write, one retry, bounded terminal failure.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/scratch.js modules/harness/v2/test/scratch-provisioning.test.js && git commit -m "feat: add worktree provisioning core"`

### Task 2: Dispatch Receipt Admission

**Wave:** 2
**Blocks:** Task 3, Task 4
**Blocked by:** Task 1

**Files:**
- Modify: `modules/harness/v2/dispatch.js` — require provisioning receipt before dispatch resolution or child launch.
- Create: `modules/harness/v2/test/dispatch-provisioning.test.js` — direct dispatch admission tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Preserve `runDispatch(options) -> Promise<ChildResult & {parkCount:number}>`.
- `runDispatch` calls `assertProvisioned(worktree, 'dispatch')` after option-shape validation and before `seatDispatch`, command substitution, parking, or `runChild`.
- Missing receipt throws `WorkspaceProvisionError` with `failureClass:'workspace-provision-failed'`; it is not returned as an agent/provider result.

**Behavior:**
- Direct coder dispatch without receipt fails closed and never resolves a binding or invokes `runChild`.
- Receipt present preserves current dispatch, placeholder substitution, rate-limit parking, and wake behavior unchanged.
- Admission checks only harness receipt state; it never provisions implicitly.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/dispatch-provisioning.test.js`
- Expected: PASS — missing receipt rejects before child launch; valid receipt admits existing dispatch behavior.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/dispatch.js modules/harness/v2/test/dispatch-provisioning.test.js && git commit -m "fix: require provisioned dispatch worktree"`

### Task 3: Provisioning Lifecycle and Infrastructure Failure

**Wave:** 3
**Blocks:** Task 4
**Blocked by:** Task 1, Task 2

**Files:**
- Modify: `modules/harness/v2/run.js` — provision/re-provision seat and gate entries; journal and block terminal failures.
- Modify: `modules/harness/spec/events.schema.json` — add terminal provisioning-failure event shape.
- Create: `modules/harness/v2/test/worktree-provisioning.integration.test.js` — end-to-end provisioning and failure-path coverage.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- `ensureTaskWorktreeProvisioned({ task, meta, repoRoot, worktree, runId, record }) -> Promise<ProvisionReceipt>` uses `<runDir(repoRoot,meta.slug,runId)>/<component(task.id)>-provision.log`.
- Every newly created task worktree calls this seam after `git worktree add` and before first stub/real gate or coder dispatch.
- Every reviewer/fixer direct `runChild` entry and every real-gate entry calls this seam again; receipt present is a no-op, receipt absent provisions before entry.
- Terminal failure appends schema-valid `{kind:'provision.failed',taskId,cmd,exitCode,logTail}` and returns task status `blocked`, trailer `Failure-Class: workspace-provision-failed`, and failure detail `failureClass:'workspace-provision-failed'`.

**Behavior:**
- Provisioning stays inside `runWave` task execution, so it consumes one existing `--concurrency` slot; file-disjoint worktrees may provision concurrently.
- Provisioning failure occurs before `dispatch.attempt`, `retry.attempt`, reviewer/fixer admission, gate-fix, or binding fallback. Agent retry ledger and seat fallback remain untouched.
- Terminal failure removes failed worktree after recording normal blocked task state; no unprovisioned worktree reaches dispatch or gate.
- `--retry-blocked` uses existing completion semantics to admit task again, creates a different fresh worktree, and provisions it from scratch.
- Fixture proving installed-tool behavior contains `pnpm-lock.yaml`; deterministic fake pnpm creates a worktree-local executable required by gate. No network dependency.
- Always-failing provision fixture runs exactly twice, blocks with required failure class/event, never launches agent wrapper, and re-runs under `--retry-blocked` in a fresh worktree.
- Tests remove receipt between lifecycle phases to prove reviewer/fixer and gate entry paths re-provision before continuing.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/worktree-provisioning.integration.test.js`
- Expected: PASS — fresh pnpm worktree gate succeeds; all seat/gate paths require receipt; terminal failure retries once, blocks as infrastructure without agent ladders, and retry-blocked uses a fresh worktree.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/run.js modules/harness/spec/events.schema.json modules/harness/v2/test/worktree-provisioning.integration.test.js && git commit -m "feat: provision task worktrees before seats"`

### Task 4: Register and Run Full v2 Regression Suite

**Wave:** 4
**Blocks:** —
**Blocked by:** Task 1, Task 2, Task 3

**Files:**
- Modify: `modules/harness/v2/test/index.js` — register each provisioning suite exactly once.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Register `scratch-provisioning.test.js`, `dispatch-provisioning.test.js`, and `worktree-provisioning.integration.test.js` once each.
- Preserve every existing suite registration.
- No skip, warning suppression, fixture-only production bypass, or reduced full-suite command.

**Behavior:**
- Syntax-check every modified production module.
- Run focused provisioning suites before full v2 suite.
- Full suite finishes cleanly without warnings and covers all §5 acceptance items.

**Acceptance (one executable check):**
- Run: `node --check modules/harness/v2/scratch.js && node --check modules/harness/v2/dispatch.js && node --check modules/harness/v2/run.js && node --test modules/harness/v2/test/scratch-provisioning.test.js modules/harness/v2/test/dispatch-provisioning.test.js modules/harness/v2/test/worktree-provisioning.integration.test.js && node modules/harness/v2/test/index.js`
- Expected: PASS — syntax, focused provisioning coverage, and complete v2 regression suite green without warnings.

- [ ] Register new test files.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/test/index.js && git commit -m "test: register worktree provisioning coverage"`
