fix(FN-4168): populate stalled review on full hydration paths

Fusion-Task-Id: FN-4168
Fusion-Task-Lineage: 781ffc66-cb1c-42b9-85e1-e5cbe0655863
This commit is contained in:
Fusion
2026-05-13 02:14:48 -07:00
committed by gsxdsm
parent e871a02560
commit 13d2cf6e70
2 changed files with 21 additions and 5 deletions

View File

@@ -22,7 +22,7 @@ describe("TaskStore stalledReview hydration", () => {
await rm(rootDir, { recursive: true, force: true });
});
it("populates stalledReview on slim listings when reenqueue churn threshold is met", async () => {
async function seedStalledInReviewTask() {
const task = await store.createTask({
description: "stalled review candidate",
column: "in-review",
@@ -32,10 +32,28 @@ describe("TaskStore stalledReview hydration", () => {
await store.logEntry(task.id, "Auto-recovered: eligible in-review task re-enqueued for merge");
}
return task;
}
it("populates stalledReview on slim listings when reenqueue churn threshold is met", async () => {
const task = await seedStalledInReviewTask();
const slimTasks = await store.listTasks({ slim: true, column: "in-review" });
const hydrated = slimTasks.find((entry) => entry.id === task.id);
expect(hydrated?.stalledReview?.heuristic).toBe("reenqueue-churn");
expect(hydrated?.stalledReview?.matchCount).toBe(3);
});
it("populates stalledReview on full listings and detail fetches", async () => {
const task = await seedStalledInReviewTask();
const fullTasks = await store.listTasks({ slim: false, column: "in-review" });
const hydrated = fullTasks.find((entry) => entry.id === task.id);
expect(hydrated?.stalledReview?.heuristic).toBe("reenqueue-churn");
const detail = await store.getTask(task.id);
expect(detail.stalledReview?.heuristic).toBe("reenqueue-churn");
expect(detail.stalledReview?.matchCount).toBe(3);
});
});

View File

@@ -2996,6 +2996,8 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
};
}
task.stalledReview = detectStalledReview(task, { now: Date.now() });
// Sync steps from PROMPT.md if task.steps is empty
if (task.steps.length === 0) {
task.steps = await this.parseStepsFromPrompt(id);
@@ -3085,9 +3087,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
// the board card has no way to display the same total-execution
// figure that the task detail panel shows.
if (slim) {
const now = Date.now();
task.timedExecutionMs = this.computeTimedExecutionMs(task.log);
task.stalledReview = detectStalledReview(task, { now });
task.log = [];
task.githubTracking = undefined;
}
@@ -3281,9 +3281,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
// Slim path mirrors `listTasks`: aggregate timed execution server-side
// before stripping the heavy log payload from the wire response.
if (slim) {
const now = Date.now();
task.timedExecutionMs = this.computeTimedExecutionMs(task.log);
task.stalledReview = detectStalledReview(task, { now });
task.log = [];
task.githubTracking = undefined;
}