type: meta

slug: runplan-dep-provisioning

topic: deterministic dependency provisioning for per-task worktrees in bin/runplan (src/runner.js) — lockfile-keyed immutable cache + CoW materialize

exec_mode: ship

scheduler: sequential

land_mode: merge-to-main

base_branch: main

gate0_mode: strict

status_enum: BLOCKED PENDING WORKING WRITTEN REVIEWED COMMITTED

truth_rule: this file is ground truth for INTENT (goals/task list); git is ground truth for DONE-NESS — reconcile statuses against git, never trust a COMMITTED line with no commit

report_rule: at session end emit brief: done / blocked / deferred / canceled


type: goal

text: give per-task worktrees deterministic dependency provisioning so gate0 strict stops failing on environmental missing-node_modules reds, via a lockfile-keyed immutable cache + copy-on-write materialization that installs once per unique lockfile and reuses everywhere


type: methodology

text: contract-level plan per [[plan]] skill — pin seams (provision-deps.sh CLI surface, cache key, runner.js wiring point, distinct dep-provision-failed error) not bodies; implementer reads src/runner.js + lib/gates.sh + lib/resolve-seat.sh directly. Provisioning is deterministic/non-LLM → lives in lib/provision-deps.sh invoked via runCli, mirroring gate0. NEVER a seat.


type: intent

text: builds on the landed worktree-isolation topology (per-task worktree at repoRoot/tmp/wt-<slug>--<taskId>); provisioning runs BEFORE the coder (not just the gate) so the coder can verify its own work, replacing the old engine's non-deterministic agent-installs-deps-itself behavior


type: anchor

what: spec — § Dependency provisioning is source of truth for design+rationale


what: plan — pinned per-task contracts (### Task N)


type: task

seat: coder

tier: medium

desc: lib/provision-deps.sh: toolchain detection (corepack packageManager -> lockfile precedence) + CLI contract skeleton (detect/provision, exit 17 = dep-provision-failed)

status: PENDING


desc: cache key (sha256 pmId+node+arch+lockfile) + atomic populate (flock, temp+rename commit, frozen offline install per pm, chmod read-only) under repoRoot/.runplan-cache/deps/

blocker: waiting on w1.t1


desc: materialize into worktree (reflink->hardlink->copy probe, memoized) + pnpm/yarn-berry native-store delegation (no custom cache, must not assume node_modules exists)

blocker: waiting on w2.t1


desc: wire provisionDeps into src/runner.js runTask after ensureTaskWorktree, before coder dispatch; provision intWt too; distinct dep-provision-failed throw (never conflated with gate-not-green)

blocker: waiting on w3.t1


desc: cache GC (LRU by mtime keep-N AND byte-cap, never evict live-worktree key, sweep orphan *.tmp.*) + .gitignore .runplan-cache/

blocker: waiting on w4.t1


desc: extend test/runner-integration.sh: green-with-no-manual-install, install-once-per-key, distinct-entry-on-lock-change, dep-provision-failed-not-gate-red, real-node_modules-unchanged (no leak)

blocker: waiting on w5.t1