From 66c51038a5fd5962ca02f8a4e39945d112979b09 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 11 Aug 2026 11:28:42 -1000 Subject: [PATCH] fix(engine): bind self-owned worktree liveness probe (#3419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Supersedes #3416 — the fork head is not writable from maintainers, so this branch carries the same fix merged onto current `main`. ## Changes - Bind the self-owned worktree liveness probe in task-executor pure facades (from #3416). - Merge current main so the lifecycle-column census baseline stays green. ## Test plan - [x] `pnpm check:lifecycle-columns` clean after merge - [ ] CI green Closes context from #3416. ## Summary by CodeRabbit * **Bug Fixes** * Fixed cleanup for self-owned worktrees so it no longer crashes before checking whether an active session is present. * Improved safety during worktree reconciliation by correctly verifying that the worktree is still active before removal. * **Documentation** * Added a changeset documenting the worktree cleanup fix. Co-authored-by: BESA-Franz <49682134+BESA-Franz@users.noreply.github.com> --- .changeset/bind-self-owned-worktree-probe.md | 7 +++++++ .../task-executor-worktree-pure-facades.ts | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/bind-self-owned-worktree-probe.md diff --git a/.changeset/bind-self-owned-worktree-probe.md b/.changeset/bind-self-owned-worktree-probe.md new file mode 100644 index 0000000000..997c4a99ac --- /dev/null +++ b/.changeset/bind-self-owned-worktree-probe.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Prevent worktree conflict cleanup from crashing before its active-session safety check. +category: fix +dev: Binds the executor live-worktree probe when the self-owned reconcile facade calls its extracted helper. diff --git a/packages/engine/src/executor/task-executor-worktree-pure-facades.ts b/packages/engine/src/executor/task-executor-worktree-pure-facades.ts index 0e6e58463d..b3be579bf1 100644 --- a/packages/engine/src/executor/task-executor-worktree-pure-facades.ts +++ b/packages/engine/src/executor/task-executor-worktree-pure-facades.ts @@ -22,7 +22,23 @@ export abstract class TaskExecutorWorktreePureFacades extends TaskExecutorState protected async isLiveCleanupRefusal(worktreePath: string, taskId: string): Promise { return pure.isLiveCleanupRefusal(this.activeWorktrees, this.store, worktreePath, taskId); } protected async cleanupStaleBranch(branch: string, taskId: string): Promise { return pure.cleanupStaleBranch(this.rootDir, this.store, branch, taskId); } protected async planSquashImportFromDep(...args: FacadeAfterSecond): ReturnType { return pure.planSquashImportFromDep(this.rootDir, this.store, ...args); } - protected async reconcileSelfOwnedBeforeRemove(...args: FacadeRestArgs): ReturnType { return pure.reconcileSelfOwnedBeforeRemove(this.store, ...args); } + /* + FNXC:WorktreeConflictRecovery 2026-08-10-20:31: + The free reconcile helper requires the executor's live-binding probe in addition to its store. + Bind that host-owned callback here so every two-argument facade caller receives the production + liveness guard instead of forwarding an undefined third argument into conflict cleanup. + */ + protected async reconcileSelfOwnedBeforeRemove( + worktreePath: string, + taskId: string, + ): ReturnType { + return pure.reconcileSelfOwnedBeforeRemove( + this.store, + worktreePath, + taskId, + (ownerTaskId, path) => this.hasActiveWorktreeBinding(ownerTaskId, path), + ); + } protected async emitStaleLockAudit(...args: FacadeRestArgs): ReturnType { return pure.emitStaleLockAudit(bags.buildStaleLockRecoveryDeps(this), ...args); } protected async recoverIndexLockIfStale(taskId: string, path: string, conflictInfo: { lockPath?: string; message?: string }): Promise { return pure.recoverIndexLockIfStale(bags.buildStaleLockRecoveryDeps(this), taskId, path, conflictInfo); } protected async recoverStaleRegistration(taskId: string, path: string, conflictInfo: { path?: string; message?: string }): Promise { return pure.recoverExecutorStaleRegistration(bags.buildStaleLockRecoveryDeps(this), taskId, path, conflictInfo); }