chore(FN-4350): import dependency content from fusion/fn-4326

Squash-imported the working tree of fusion/fn-4326 as a single commit so this branch carries the dep's content without inheriting its individual commits. If the dep is later squash-merged to main, this commit's patch-id should match the merge and rebase cleanly.

Fusion-Task-Id: FN-4350
Fusion-Task-Lineage: 11fff2c2-1cd1-4562-9b01-032a15217479
This commit is contained in:
gsxdsm
2026-05-13 11:57:14 -07:00
parent bb0d6118e2
commit f378874c14
11 changed files with 423 additions and 24 deletions

View File

@@ -3461,6 +3461,12 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
* clear the worktree.
*/
preserveWorktree?: boolean;
/**
* When true, do not clear task.status/task.error/task.pausedReason on
* reopen-to-todo/triage transitions. Required so recovery handlers that
* bounce through todo can keep sticky failed state context.
*/
preserveStatus?: boolean;
/**
* When transitioning to in-progress on a task that has no worktree
* assigned, invoke this allocator to pick a path. The store calls
@@ -3541,8 +3547,11 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
&& (toColumn === "todo" || toColumn === "triage");
if (isReopenToTodoOrTriage) {
task.status = undefined;
task.error = undefined;
if (!options?.preserveStatus) {
task.status = undefined;
task.error = undefined;
task.pausedReason = undefined;
}
task.blockedBy = undefined;
task.paused = undefined;
task.pausedByAgentId = undefined;
@@ -3675,7 +3684,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
async updateTask(
id: string,
updates: { title?: string; description?: string; priority?: TaskPriority | null; prompt?: string; worktree?: string | null; status?: string | null; dependencies?: string[]; steps?: import("./types.js").TaskStep[]; currentStep?: number; blockedBy?: string | null; assignedAgentId?: string | null; pausedByAgentId?: string | null; assigneeUserId?: string | null; scopeOverride?: boolean | null; scopeOverrideReason?: string | null; nodeId?: string | null; effectiveNodeId?: string | null; effectiveNodeSource?: string | null; checkedOutBy?: string | null; checkedOutAt?: string | null; checkoutNodeId?: string | null; checkoutRunId?: string | null; checkoutLeaseRenewedAt?: string | null; checkoutLeaseEpoch?: number | null; paused?: boolean; baseBranch?: string | null; branch?: string | null; executionStartBranch?: string | null; baseCommitSha?: string | null; size?: "S" | "M" | "L"; reviewLevel?: number; executionMode?: import("./types.js").ExecutionMode | null; mergeRetries?: number; workflowStepRetries?: number; stuckKillCount?: number | null; postReviewFixCount?: number | null; recoveryRetryCount?: number | null; taskDoneRetryCount?: number | null; verificationFailureCount?: number | null; mergeConflictBounceCount?: number | null; nextRecoveryAt?: string | null; enabledWorkflowSteps?: string[]; modelProvider?: string | null; modelId?: string | null; validatorModelProvider?: string | null; validatorModelId?: string | null; planningModelProvider?: string | null; planningModelId?: string | null; thinkingLevel?: string | null; error?: string | null; summary?: string | null; sessionFile?: string | null; executionStartedAt?: string | null; executionCompletedAt?: string | null; review?: import("./types.js").TaskReview | null; reviewState?: import("./types.js").TaskReviewState | null; workflowStepResults?: import("./types.js").WorkflowStepResult[] | null; mergeDetails?: import("./types.js").MergeDetails | null; sourceIssue?: import("./types.js").TaskSourceIssue | null; githubTracking?: import("./types.js").TaskGithubTracking | null; tokenUsage?: import("./types.js").TaskTokenUsage | null; modifiedFiles?: string[] | null; missionId?: string | null; sliceId?: string | null },
updates: { title?: string; description?: string; priority?: TaskPriority | null; prompt?: string; worktree?: string | null; status?: string | null; dependencies?: string[]; steps?: import("./types.js").TaskStep[]; currentStep?: number; blockedBy?: string | null; assignedAgentId?: string | null; pausedByAgentId?: string | null; pausedReason?: string | null; dispatchStormCount?: number | null; lastDispatchAt?: string | null; assigneeUserId?: string | null; scopeOverride?: boolean | null; scopeOverrideReason?: string | null; nodeId?: string | null; effectiveNodeId?: string | null; effectiveNodeSource?: string | null; checkedOutBy?: string | null; checkedOutAt?: string | null; checkoutNodeId?: string | null; checkoutRunId?: string | null; checkoutLeaseRenewedAt?: string | null; checkoutLeaseEpoch?: number | null; paused?: boolean; baseBranch?: string | null; branch?: string | null; executionStartBranch?: string | null; baseCommitSha?: string | null; size?: "S" | "M" | "L"; reviewLevel?: number; executionMode?: import("./types.js").ExecutionMode | null; mergeRetries?: number; workflowStepRetries?: number; stuckKillCount?: number | null; postReviewFixCount?: number | null; recoveryRetryCount?: number | null; taskDoneRetryCount?: number | null; verificationFailureCount?: number | null; mergeConflictBounceCount?: number | null; nextRecoveryAt?: string | null; enabledWorkflowSteps?: string[]; modelProvider?: string | null; modelId?: string | null; validatorModelProvider?: string | null; validatorModelId?: string | null; planningModelProvider?: string | null; planningModelId?: string | null; thinkingLevel?: string | null; error?: string | null; summary?: string | null; sessionFile?: string | null; executionStartedAt?: string | null; executionCompletedAt?: string | null; review?: import("./types.js").TaskReview | null; reviewState?: import("./types.js").TaskReviewState | null; workflowStepResults?: import("./types.js").WorkflowStepResult[] | null; mergeDetails?: import("./types.js").MergeDetails | null; sourceIssue?: import("./types.js").TaskSourceIssue | null; githubTracking?: import("./types.js").TaskGithubTracking | null; tokenUsage?: import("./types.js").TaskTokenUsage | null; modifiedFiles?: string[] | null; missionId?: string | null; sliceId?: string | null },
runContext?: RunMutationContext,
): Promise<Task> {
return this.withTaskLock(id, async () => {
@@ -3803,6 +3812,21 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
} else if (updates.pausedByAgentId !== undefined) {
task.pausedByAgentId = updates.pausedByAgentId;
}
if (updates.pausedReason === null) {
task.pausedReason = undefined;
} else if (updates.pausedReason !== undefined) {
task.pausedReason = updates.pausedReason;
}
if (updates.dispatchStormCount === null) {
task.dispatchStormCount = undefined;
} else if (updates.dispatchStormCount !== undefined) {
task.dispatchStormCount = updates.dispatchStormCount;
}
if (updates.lastDispatchAt === null) {
task.lastDispatchAt = undefined;
} else if (updates.lastDispatchAt !== undefined) {
task.lastDispatchAt = updates.lastDispatchAt;
}
if (updates.assigneeUserId === null) {
task.assigneeUserId = undefined;
} else if (updates.assigneeUserId !== undefined) {