Files
fusion/packages/engine
gsxdsm edc64138e0 feat(worktrees): task-pinned worktrees under worktreeNaming "task-id" (#2233)
## Summary

Adds **task-pinned worktrees** for `worktreeNaming: "task-id"`. Under
task-id naming, a task is pinned to exactly one derivable directory
`<worktreesDir>/<lowercased-task-id>` (e.g. `.worktrees/fn-7996`) for
its entire lifecycle — removing the ambiguity that let stale/foreign
`task.worktree` pointers strand a task (the FN-7996 shape).

`recycleWorktrees` stays fully functional and is **mutually exclusive**
with task-id pinning: the two can't be enabled together.

## Behavior

- **Pinned acquisition (`worktreeNaming: "task-id"`, recycling off):**
`acquireTaskWorktree` runs **derive → validate → reuse-or-recreate** at
the derived path — warm-reuse when the dir is a registered, usable
worktree on the task's own branch; otherwise reclaim-in-place
(`removeWorktree` + recreate at the SAME path, never a sibling name). A
disagreeing `task.worktree` cache self-corrects and emits a new
`worktree:pin-rederived` audit event, without consuming worktree-session
retries. The recycle pool is never consulted in pinned mode.
- **Mutual exclusivity:** enabling both `recycleWorktrees` and
`worktreeNaming: "task-id"` is rejected at the settings-write boundary —
HTTP 400 at `PUT /settings`, and an `Error` backstop in
`store.updateSettings` covering the CLI and every other writer
(`assertWorktreeNamingRecycleExclusive`). The runtime also gates pinned
mode on `!recycleWorktrees`, so a legacy on-disk config carrying both
degrades safely to recycling.
- **Settings UI:** the Settings → Worktrees panel enforces the
exclusivity bidirectionally — the *Recycle worktrees* toggle is disabled
while naming is *Task ID*, and the naming select is disabled while
recycling is on — so the conflicting state is unreachable, with help
text explaining why.
- **Byte-inert for the rest:** `random`/`task-title` naming and the
recycle pool (incl. `merger.ts` release) are unchanged;
worktrunk-managed layouts bypass pinning.

## Acceptance criteria (from the plan)

1. ✅ Pinned task dispatched N times only ever touches
`<worktreesDir>/<task-id>` on its own branch
2. ✅ No code path can hand task A's dir to task B (pool bypassed; path
derived from task id)
3. ✅ FN-7996 stale/foreign `task.worktree` self-corrects at next
dispatch (`worktree:pin-rederived`) without consuming session retries
4. ✅ Non-pinned modes with `recycleWorktrees: true|false` are
byte-identical (existing pool tests pass unchanged)
5. ✅ Stale same-name dir (crash leftover / archive→restore) reclaimed in
place, never suffixed
6. ✅ Docs updated (settings-reference, architecture, `worktreeNaming`
type doc); changeset (`minor`, `feature`); FNXC comments encode the
invariant

## Files

- `packages/engine/src/worktree-pinning.ts` — new pure helpers
(`isTaskPinnedWorktreeNaming`, `pinnedWorktreePathForTask`)
- `packages/engine/src/worktree-acquisition.ts` — pinned branch +
branch-match reclaim-in-place
- `packages/engine/src/run-audit.ts` — `worktree:pin-rederived` audit
type
- `packages/core/src/settings-validation.ts` (+ `index.ts`,
`task-store/settings-ops.ts`) — mutual-exclusion validator + wiring
- `packages/dashboard/src/routes/register-settings-memory-routes.ts` —
400 on conflict
-
`packages/dashboard/app/components/settings/sections/WorktreesSection.tsx`
(+ `packages/i18n/locales/en/app.json`) — bidirectional UI exclusivity
- `packages/core/src/types.ts`, `docs/*`, `.changeset/*`

## Verification

- New tests: engine `worktree-pinning` (5) +
`worktree-acquisition-pinned` (7); core
`worktree-naming-recycle-exclusive` (2); dashboard settings-route 400
(3) + WorktreesSection UI exclusivity (3)
- Regression sweep green: 194 engine
worktree/acquisition/pool/executor/merger-release tests, core settings
tests, dashboard i18n/settings-section tests
- `tsc --noEmit` clean for `@fusion/core` and `@fusion/engine`; changed
source files clean; eslint clean
- `pnpm verify:fast` PASS (build + scoped typecheck + boot smoke)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added Task ID worktree naming, providing each task with a stable,
deterministic worktree directory.
* Automatically reuses valid pinned worktrees and recreates stale or
conflicting ones at the same path.
* Added clear settings controls and validation for incompatible Task ID
naming and worktree recycling options.

* **Documentation**
* Updated worktree architecture, settings reference, and in-app guidance
to explain pinned worktrees and configuration constraints.

* **Bug Fixes**
* Improved recovery from stale or incorrect worktree assignments without
consuming session retries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:37:42 -07:00
..
2026-07-13 10:32:12 -07:00
2026-07-13 10:32:12 -07:00