fix(respecify): skip stale spec enforcement for in-progress/in-review/done tasks

Background stale spec checks now skip tasks that are already in an
active work state (in-progress, in-review, done, merging, merging-pr)
to avoid interrupting work that is underway or complete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-13 10:08:25 -07:00
parent 54ce431161
commit 3e2094dba3
2 changed files with 18 additions and 11 deletions

View File

@@ -13,7 +13,7 @@ import * as nodeFs from "node:fs";
import * as nodeChildProcess from "node:child_process";
import { promisify } from "node:util";
import type { TaskStore, Column, MergeResult, ScheduleType, ActivityEventType, ModelPreset, AutomationStep, MessageType, ParticipantType, MessageCreateInput, Routine, RoutineCreateInput, RoutineUpdateInput, RoutineExecutionResult, RoutineTriggerType } from "@fusion/core";
import { COLUMNS, VALID_TRANSITIONS, GLOBAL_SETTINGS_KEYS, type BatchStatusEntry, type BatchStatusResponse, type BatchStatusResult, type IssueInfo, type PrInfo, type Task, getCurrentRepo, isGhAuthenticated, AUTOMATION_PRESETS, AutomationStore, validateBackupSchedule, validateBackupRetention, validateBackupDir, syncBackupAutomation, exportSettings, importSettings, validateImportData, MessageStore, MEMORY_FILE_PATH, RoutineStore, isWebhookTrigger, resolveMemoryBackend, getMemoryBackendCapabilities, listMemoryBackendTypes, type MemoryBackendCapabilities } from "@fusion/core";
import { COLUMNS, VALID_TRANSITIONS, GLOBAL_SETTINGS_KEYS, type BatchStatusEntry, type BatchStatusResponse, type BatchStatusResult, type IssueInfo, type PrInfo, type Task, getCurrentRepo, isGhAuthenticated, AutomationStore, validateBackupSchedule, validateBackupRetention, validateBackupDir, syncBackupAutomation, exportSettings, importSettings, validateImportData, MessageStore, MEMORY_FILE_PATH, RoutineStore, isWebhookTrigger, resolveMemoryBackend, getMemoryBackendCapabilities, listMemoryBackendTypes, type MemoryBackendCapabilities } from "@fusion/core";
import type { ChatStore, ChatSessionCreateInput, ChatSessionUpdateInput } from "@fusion/core";
import type { ServerOptions } from "./server.js";
import { GitHubClient, parseBadgeUrl } from "./github.js";

View File

@@ -896,16 +896,23 @@ export class TaskExecutor {
// execute() to prevent stale tasks from entering worktree creation or agent sessions.
// If timestamp evaluation is skipped (missing/unreadable file), continue with execution
// so existing filesystem validation paths remain authoritative.
const tasksDir = join(this.store.getFusionDir(), "tasks");
const promptPath = getPromptPath(tasksDir, task.id);
const staleness = await evaluateSpecStaleness({ settings, promptPath });
if (staleness.isStale) {
executorLog.warn(`Task ${task.id} specification is stale — ${staleness.reason}`);
// Move to triage first, then set status so the task enters triage with needs-respecify
await this.store.moveTask(task.id, "triage");
await this.store.updateTask(task.id, { status: "needs-respecify" });
await this.store.logEntry(task.id, staleness.reason, undefined, this.currentRunContext);
return;
// Skip for tasks that are already in-progress, in-review, merging, or done —
// these should not be interrupted and sent back to triage for respecification.
const activeColumns = new Set(["in-progress", "in-review", "done"]);
const activeMergeStatuses = new Set(["merging", "merging-pr"]);
const isActiveTask = activeColumns.has(task.column) || activeMergeStatuses.has(task.status ?? "");
if (!isActiveTask) {
const tasksDir = join(this.store.getFusionDir(), "tasks");
const promptPath = getPromptPath(tasksDir, task.id);
const staleness = await evaluateSpecStaleness({ settings, promptPath });
if (staleness.isStale) {
executorLog.warn(`Task ${task.id} specification is stale — ${staleness.reason}`);
// Move to triage first, then set status so the task enters triage with needs-respecify
await this.store.moveTask(task.id, "triage");
await this.store.updateTask(task.id, { status: "needs-respecify" });
await this.store.logEntry(task.id, staleness.reason, undefined, this.currentRunContext);
return;
}
}
// Hoist worktreePath so it's accessible in the catch block for dep-abort cleanup