Contention prevention (why tasks shared a path at all):
- Planning ran `tools: "coding"` at the repo root, so every planner had write
tools in the operator's checkout and all planners shared one path. Planning
now acquires the task's own worktree (TriageProcessor.acquirePlanningWorktree
-> TaskExecutor.ensureTaskWorktreeForPlanning).
- Graph nodes with no worktree acquired one instead of falling back to rootDir,
so Plan Review / Code Review / custom gates all run isolated. Plan Review
re-acquires when its recorded worktree is gone, replacing FN-7996's
run-from-the-repo-root degrade. Workspace projects are unchanged.
- Registration goes through acquireActiveSessionPath, which reclaims a leaked
entry whose holder is provably dead and aged past the FN-5256 floor. A live
holder still contends — real serialization is never clobbered.
Classification (the reported symptom):
- A lease held by another task is no longer a provider failure. It carries
SESSION_CONTENTION_HOLD_VALUE, classifies transient, is excluded from
isNonPlanDefectPlanReviewFailure, and stops burning the node's fast retries.
- The executor waits it out on a 10-attempt 5s->60s ladder and then leaves the
task cleanly queued. There is no terminal branch: contention always ends, so
parking would only ask a human to press Retry on a condition that fixed
itself.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>