fleet: mission-execution-loop.ts 10 → 2 (one rule, five copies; and why these converted where store.ts's look-alikes could not) (#2711)
Claiming **`packages/engine/src/mission-execution-loop.ts`** (10). Every one of its 10 census sites is the **same rule written five times**: ```ts linkedTask.column === "done" || linkedTask.column === "archived" ``` ## Census before/after | | before | after | |---|---:|---:| | `mission-execution-loop.ts` | **10** | **2** | Converted 4 of the 5 copies (8 of 10 sites) to the complete/archived roles via core's `resolveTaskLifecycleColumns`. Each site already had `this.taskStore` in scope inside an async method **and already had the linked task fetched**, so the resolution rides along with a read that was happening anyway. ## Why these converted where `store.ts`'s look-alikes could not (#2709) Both read **another task's** column. The difference is not whose column it is: - **Here** — async methods, store at hand, one task per call. A resolution is already affordable. - **`store.ts`** — synchronous `filter` callbacks over a prefetched `taskById` map, where per-dep resolution means N awaits inside a sync predicate on a path that prefetches precisely to avoid per-item I/O. Same-looking code, opposite verdicts. The distinguishing question is **"is a resolution already affordable here"**, not "whose column is it" — worth stating because a fleet worker pattern-matching on the receiver alone would get both wrong. ## Not deduped, deliberately The right shape is one predicate used five times rather than five inline copies — and the FNXC comments above each copy show their intent has already drifted apart. But introducing that predicate is a **new abstraction**, which the fleet rules exclude, and it would fold five reviewable substitutions into one design change. Flagged as the obvious follow-up instead of smuggled in. ## Remaining: 2 The fifth copy, at the `hasLiveFixTask` site, where the terminal check is one clause of a longer `Boolean(...)` expression whose other clauses I would have had to reflow. Reviewability, not difficulty. ## Verification `pnpm test:gate` **GREEN** (158 + 10 + 487 + 71) · the four mission suites **150/150** · `pnpm lint` clean · engine `tsc` clean · `--strict` exits 0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,9 @@ import type {
|
||||
Mission,
|
||||
ValidationDiagnostics,
|
||||
} from "@fusion/core";
|
||||
import { MissionRemediationStoppedError, normalizeMissionAssertionType, normalizeValidationDiagnostics, renderValidationFailureDescription } from "@fusion/core";
|
||||
import { MissionRemediationStoppedError, normalizeMissionAssertionType, normalizeValidationDiagnostics, renderValidationFailureDescription,
|
||||
resolveTaskLifecycleColumns,
|
||||
} from "@fusion/core";
|
||||
import { GitCheckoutMaterializer, type CheckoutMaterializer, type VerificationOutcome } from "./mission-verification.js";
|
||||
import { createFnAgent, promptWithFallback, type AgentResult } from "./pi.js";
|
||||
import { mergeEffectiveSettings } from "./effective-settings.js";
|
||||
@@ -318,7 +320,13 @@ export class MissionExecutionLoop extends EventEmitter {
|
||||
// If the feature has a linked task that's already done, re-trigger validation
|
||||
if (feature.taskId) {
|
||||
const linkedTask = await this.taskStore.getTask(feature.taskId).catch(() => null);
|
||||
if (linkedTask && (linkedTask.column === "done" || linkedTask.column === "archived")) {
|
||||
const linkedLifecycle = linkedTask
|
||||
? await resolveTaskLifecycleColumns(this.taskStore, linkedTask.id)
|
||||
: undefined;
|
||||
if (linkedTask && (
|
||||
linkedTask.column === (linkedLifecycle?.complete ?? "done")
|
||||
|| linkedTask.column === (linkedLifecycle?.archived ?? "archived")
|
||||
)) {
|
||||
await this.processTaskOutcome(feature.taskId);
|
||||
}
|
||||
}
|
||||
@@ -335,7 +343,13 @@ export class MissionExecutionLoop extends EventEmitter {
|
||||
if (feature.taskId) {
|
||||
try {
|
||||
const linkedTask = await this.taskStore.getTask(feature.taskId).catch(() => null);
|
||||
if (linkedTask && (linkedTask.column === "done" || linkedTask.column === "archived")) {
|
||||
const linkedLifecycle = linkedTask
|
||||
? await resolveTaskLifecycleColumns(this.taskStore, linkedTask.id)
|
||||
: undefined;
|
||||
if (linkedTask && (
|
||||
linkedTask.column === (linkedLifecycle?.complete ?? "done")
|
||||
|| linkedTask.column === (linkedLifecycle?.archived ?? "archived")
|
||||
)) {
|
||||
await this.processTaskOutcome(feature.taskId);
|
||||
}
|
||||
recoveredCount++;
|
||||
@@ -361,7 +375,13 @@ export class MissionExecutionLoop extends EventEmitter {
|
||||
|
||||
try {
|
||||
const linkedTask = await this.taskStore.getTask(feature.taskId).catch(() => null);
|
||||
if (linkedTask && (linkedTask.column === "done" || linkedTask.column === "archived")) {
|
||||
const linkedLifecycle = linkedTask
|
||||
? await resolveTaskLifecycleColumns(this.taskStore, linkedTask.id)
|
||||
: undefined;
|
||||
if (linkedTask && (
|
||||
linkedTask.column === (linkedLifecycle?.complete ?? "done")
|
||||
|| linkedTask.column === (linkedLifecycle?.archived ?? "archived")
|
||||
)) {
|
||||
loopLog.log(`Recovery: re-triggering implementing feature ${feature.id} from completed task ${feature.taskId}`);
|
||||
await this.processTaskOutcome(feature.taskId);
|
||||
recoveredCount++;
|
||||
@@ -665,7 +685,12 @@ export class MissionExecutionLoop extends EventEmitter {
|
||||
if (!taskId) return null;
|
||||
const linkedTask = await this.taskStore.getTask(taskId).catch(() => null);
|
||||
const column = linkedTask?.column;
|
||||
if (!column || column === "done" || column === "archived") return null;
|
||||
const premergeLifecycle = linkedTask ? await resolveTaskLifecycleColumns(this.taskStore, linkedTask.id) : undefined;
|
||||
if (
|
||||
!column
|
||||
|| column === (premergeLifecycle?.complete ?? "done")
|
||||
|| column === (premergeLifecycle?.archived ?? "archived")
|
||||
) return null;
|
||||
return column;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
"packages/dashboard/app/components/TaskDetailModal.tsx": 30,
|
||||
"packages/engine/src/scheduler.ts": 26,
|
||||
"packages/core/src/store.ts": 12,
|
||||
"packages/engine/src/mission-execution-loop.ts": 10,
|
||||
"packages/core/src/task-store/async-comments-attachments.ts": 9,
|
||||
"packages/dashboard/app/components/TaskContextMenu.tsx": 9,
|
||||
"packages/dashboard/src/github-tracking-comments.ts": 9,
|
||||
@@ -92,6 +91,7 @@
|
||||
"packages/engine/src/auto-merge-finalization.ts": 2,
|
||||
"packages/engine/src/cli-agent/state-machine.ts": 2,
|
||||
"packages/engine/src/merger-scope-auto-widen.ts": 2,
|
||||
"packages/engine/src/mission-execution-loop.ts": 2,
|
||||
"plugins/fusion-plugin-even-cards/src/cards/board-cards.ts": 2,
|
||||
"plugins/fusion-plugin-reports/src/store/report-store.ts": 2,
|
||||
"packages/cli/src/commands/pr.ts": 1,
|
||||
|
||||
Reference in New Issue
Block a user