Files
fusion/packages/engine/src/executor/task-executor-worktree-pure-facades.ts
gsxdsm 56a087dac9 fix(engine): restore shipped behaviors dropped by the wave-18 executor peel
Full-suite repair, engine harness cluster (~85 red suites). Two causes:
(1) graph dispatch now fails closed without options.agentStore
(FN-8764/FN-8821, intended) — the shared executor test harness now
provisions the workflow-routing agent-store fixture for bare
TaskExecutor constructions, with explicit opt-out for the two tests
asserting the fail-closed park; (2) the wave-18 'pure peel' (#3317)
rebuilt executor.ts from a stale base and silently deleted shipped
behaviors, restored here: FN-8864 agent-activity writers (task
started/handed-off, workflow gate pass/fail, gate principal attribution
via executor/workflow-gate-activity.ts), FN-8768 Plan Review group
recognition, convergence primer, and modified-file review scoping,
FN-6782's fire-time guard on transient resume-after-restart retries,
FN-8868 session usage telemetry boundaries, recommendation-route
withheld-tool guidance, and the per-instance worktree retry cap.
Stale expectations updated for intended changes (FN-8823 shared-member
hold, FN-9060 zero-acquire fail-closed, heartbeat tool inventory,
peeled-module seams, PG harness provisioning). Verified: 23 files /
711 tests green, engine typecheck clean, merge gate green.

Known follow-ups (not addressed here): step-session error routing may
still bypass FN-5866 non-continuable classification
(post-done-continuation-no-wedge red), scheduler mission-loop trigger
gap (mission-validation-trigger-gap red), and executeWorkflowStep lost
routed workflow-principal session identity threading (untested drop
from the same peel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 15:52:52 -07:00

66 lines
8.2 KiB
TypeScript

/**
* FNXC:CodeOrganization 2026-08-04-08:05:
* Pure worktree helper facades peeled from TaskExecutor (U4). Keeps executor.ts to
* impl/bags facades while pure.* worktree ownership helpers share TaskExecutorState fields.
*
* FNXC:CodeOrganization 2026-08-04-08:55:
* Also hosts impl worktree create/conflict/cleanup facades and capture helpers so
* executor.ts drops the worktree-create tail.
*/
import * as pure from "./pure-bindings.js";
import * as impl from "./impl-bindings.js";
import * as bags from "./deps-bags.js";
import * as constants from "./executor-constants.js";
import { bindHandleWorktreeConflict, bindTryCreateWorktree } from "./worktree-create-binders.js";
import { type FacadeRestArgs, type FacadeAfterFirst, type FacadeAfterSecond } from "./facade-methods.js";
import { TaskExecutorState } from "./task-executor-state.js";
export abstract class TaskExecutorWorktreePureFacades extends TaskExecutorState {
protected hasActiveWorktreeBinding(taskId: string, worktreePath: string): boolean { return pure.hasActiveWorktreeBinding(this.activeWorktrees, taskId, worktreePath); }
protected async shouldGenerateNewWorktreeName(conflictPath: string, currentTaskId: string): Promise<boolean> { return pure.shouldGenerateNewWorktreeName(this.activeWorktrees, this.store, conflictPath, currentTaskId); }
protected async findActiveWorktreeOwner(worktreePath: string, requestingTaskId: string): Promise<string | null> { return pure.findActiveWorktreeOwner(this.activeWorktrees, this.store, worktreePath, requestingTaskId); }
protected async isLiveCleanupRefusal(worktreePath: string, taskId: string): Promise<boolean> { return pure.isLiveCleanupRefusal(this.activeWorktrees, this.store, worktreePath, taskId); }
protected async cleanupStaleBranch(branch: string, taskId: string): Promise<boolean> { return pure.cleanupStaleBranch(this.rootDir, this.store, branch, taskId); }
protected async planSquashImportFromDep(...args: FacadeAfterSecond<typeof pure.planSquashImportFromDep>): ReturnType<typeof pure.planSquashImportFromDep> { return pure.planSquashImportFromDep(this.rootDir, 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<typeof pure.reconcileSelfOwnedBeforeRemove> {
return pure.reconcileSelfOwnedBeforeRemove(
this.store,
worktreePath,
taskId,
(ownerTaskId, path) => this.hasActiveWorktreeBinding(ownerTaskId, path),
);
}
protected async emitStaleLockAudit(...args: FacadeRestArgs<typeof pure.emitStaleLockAudit>): ReturnType<typeof pure.emitStaleLockAudit> { return pure.emitStaleLockAudit(bags.buildStaleLockRecoveryDeps(this), ...args); }
protected async recoverIndexLockIfStale(taskId: string, path: string, conflictInfo: { lockPath?: string; message?: string }): Promise<boolean> { return pure.recoverIndexLockIfStale(bags.buildStaleLockRecoveryDeps(this), taskId, path, conflictInfo); }
protected async recoverStaleRegistration(taskId: string, path: string, conflictInfo: { path?: string; message?: string }): Promise<boolean> { return pure.recoverExecutorStaleRegistration(bags.buildStaleLockRecoveryDeps(this), taskId, path, conflictInfo); }
protected async normalizeReclaimableWorktreePath(...args: FacadeRestArgs<typeof pure.normalizeReclaimableWorktreePath>): ReturnType<typeof pure.normalizeReclaimableWorktreePath> { return pure.normalizeReclaimableWorktreePath(bags.buildNormalizeReclaimableWorktreePathDeps(this), ...args); }
protected async tryFreshWorktreeAfterLiveConflict(...args: FacadeRestArgs<typeof pure.tryFreshWorktreeAfterLiveConflict>): Promise<{ path: string; branch: string }> { return pure.tryFreshWorktreeAfterLiveConflict(bags.buildTryFreshWorktreeAfterLiveConflictDeps(this, bindTryCreateWorktree(this)), ...args); }
protected async removeOwnWorktreeWithReconcile(...args: FacadeRestArgs<typeof pure.removeOwnWorktreeWithReconcile>): ReturnType<typeof pure.removeOwnWorktreeWithReconcile> { return pure.removeOwnWorktreeWithReconcile(bags.buildRemoveOwnWorktreeWithReconcileDeps(this), ...args); }
protected async reclaimExistingWorktree(...args: FacadeRestArgs<typeof impl.reclaimExistingWorktreeImpl>): ReturnType<typeof impl.reclaimExistingWorktreeImpl> { return impl.reclaimExistingWorktreeImpl(bags.buildBranchConflictHandleFacadeDeps(this), ...args); }
protected async handleBranchConflict(...args: FacadeRestArgs<typeof impl.handleBranchConflictImpl>): ReturnType<typeof impl.handleBranchConflictImpl> { return impl.handleBranchConflictImpl(bags.buildBranchConflictHandleFacadeDeps(this), ...args); }
protected async recoverMissingWorktreeSessionStartFailure(...args: FacadeRestArgs<typeof impl.recoverMissingWorktreeSessionStartFailureImpl>): ReturnType<typeof impl.recoverMissingWorktreeSessionStartFailureImpl> { return impl.recoverMissingWorktreeSessionStartFailureImpl(bags.buildRecoverMissingWorktreeSessionStartFailureDeps(this), ...args); }
protected async emitWorktreeReanchoredAudit(...args: FacadeRestArgs<typeof impl.emitWorktreeReanchoredAuditImpl>): ReturnType<typeof impl.emitWorktreeReanchoredAuditImpl> { return impl.emitWorktreeReanchoredAuditImpl(bags.buildStoreRunContextDeps(this), ...args); }
listWorktreeHolders(): Array<{ taskId: string; worktreePath: string }> { return impl.listWorktreeHoldersImpl(this.activeWorktrees); }
protected async tryCreateWorktree(...args: FacadeRestArgs<typeof impl.tryCreateWorktreeImpl>): Promise<{ path: string; branch: string }> { return impl.tryCreateWorktreeImpl(bags.buildWorktreeCreateConflictFacadeDeps(this, (this as { MAX_WORKTREE_RETRIES?: number }).MAX_WORKTREE_RETRIES ?? constants.MAX_WORKTREE_RETRIES, bindHandleWorktreeConflict(this), bindTryCreateWorktree(this)), ...args); }
protected async handleWorktreeConflict(...args: FacadeRestArgs<typeof impl.handleWorktreeConflictImpl>): Promise<{ path: string; branch: string } | null> { return impl.handleWorktreeConflictImpl(bags.buildWorktreeCreateConflictFacadeDeps(this, (this as { MAX_WORKTREE_RETRIES?: number }).MAX_WORKTREE_RETRIES ?? constants.MAX_WORKTREE_RETRIES, bindHandleWorktreeConflict(this), bindTryCreateWorktree(this)), ...args); }
protected async cleanupConflictingWorktree(...args: FacadeRestArgs<typeof impl.cleanupConflictingWorktreeImpl>): ReturnType<typeof impl.cleanupConflictingWorktreeImpl> { return impl.cleanupConflictingWorktreeImpl(bags.buildCleanupConflictingWorktreeDeps(this), ...args); }
protected async resolveWorktreeStartPoint(startPoint: string, taskId: string): ReturnType<typeof impl.resolveWorktreeStartPointImpl> { return impl.resolveWorktreeStartPointImpl(this.rootDir, this.store, startPoint, taskId); }
protected async squashImportDepIntoWorktree(...args: FacadeAfterFirst<typeof impl.squashImportDepIntoWorktreeImpl>): ReturnType<typeof impl.squashImportDepIntoWorktreeImpl> { return impl.squashImportDepIntoWorktreeImpl(this.store, ...args); }
protected async rebaseNewWorktreeOntoRemote(...args: FacadeAfterSecond<typeof impl.rebaseNewWorktreeOntoRemoteImpl>): ReturnType<typeof impl.rebaseNewWorktreeOntoRemoteImpl> { return impl.rebaseNewWorktreeOntoRemoteImpl(this.rootDir, this.store, ...args); }
protected async createWorktree(...args: FacadeRestArgs<typeof impl.createWorktreeImpl>): Promise<{ path: string; branch: string }> { return impl.createWorktreeImpl(bags.buildCreateWorktreeFacadeDeps(this, bindTryCreateWorktree(this)), ...args); }
disposeStoreLifecycleDisposers(): void { impl.disposeStoreLifecycleDisposersImpl(bags.buildDisposeStoreLifecycleDisposersDeps(this)); }
async cleanup(taskId: string): Promise<void> { return impl.cleanupTaskWorktreeImpl(bags.buildCleanupTaskWorktreeDeps(this), taskId); }
getWorktreePath(taskId: string): string | undefined { return impl.getWorktreePathImpl(this.workspaceConfig, (id) => this.getActiveWorktreePaths(id), taskId); }
// getActiveWorktreePaths is hosted on session facades; abstract for getWorktreePath
protected abstract getActiveWorktreePaths(taskId: string): string[];
}