fix: address stuck-loop parking review comments
This commit is contained in:
@@ -190,7 +190,7 @@ describe("reliability interactions: non-progress churn", () => {
|
||||
manager.stop();
|
||||
});
|
||||
|
||||
it("re-queues incomplete STUCK_LOOP_EXHAUSTED tasks in todo when the churn signal does not fire", async () => {
|
||||
it("parks incomplete STUCK_LOOP_EXHAUSTED tasks in todo when the churn signal does not fire", async () => {
|
||||
const task = baseTask({ id: "FN-5168-LOOP", stuckKillCount: 6 });
|
||||
const store = createStore(task);
|
||||
const manager = new SelfHealingManager(store, { rootDir: "/tmp/repo" });
|
||||
@@ -210,19 +210,19 @@ describe("reliability interactions: non-progress churn", () => {
|
||||
|
||||
await detector.killAndRetry(task.id, 60_000);
|
||||
|
||||
expect(task.error).toBeNull();
|
||||
expect(task.status).toBe("queued");
|
||||
expect(task.error).toContain("STUCK_LOOP_EXHAUSTED: incomplete task exhausted stuck kill budget");
|
||||
expect(task.status).toBe("failed");
|
||||
expect(task.column).toBe("todo");
|
||||
expect(task.paused).toBe(false);
|
||||
expect(task.paused).toBe(true);
|
||||
// FN-6252 / Move-Task contract: engine rebounds do not write userPaused,
|
||||
// so a never-user-paused task remains undefined while still not user-paused.
|
||||
expect(task.userPaused).not.toBe(true);
|
||||
expect(task.pausedReason).toBeNull();
|
||||
expect(task.pausedReason).toBe("stuck-loop-exhausted-manual-intervention-required");
|
||||
expect(task.stuckKillCount).toBe(7);
|
||||
expect(task.steps).toEqual([{ name: "Implement", status: "in-progress" }]);
|
||||
expect(task.log?.some((entry) => entry.action.includes("incomplete task exhausted stuck kill budget"))).toBe(true);
|
||||
expect(task.log?.some((entry) => entry.action.includes("Parked in todo with progress preserved"))).toBe(true);
|
||||
expect(store.handoffToReview).not.toHaveBeenCalled();
|
||||
expect(isRunnableQueuedOverlapCandidate(task, [task])).toBe(true);
|
||||
expect(isRunnableQueuedOverlapCandidate(task, [task])).toBe(false);
|
||||
|
||||
manager.stop();
|
||||
});
|
||||
|
||||
@@ -290,7 +290,7 @@ describe("FN-5941 reliability interactions: todo/in-progress flapping", () => {
|
||||
manager.stop();
|
||||
});
|
||||
|
||||
it("still requeues a genuinely dead task when stuck-kill budget is exhausted", async () => {
|
||||
it("parks a genuinely dead incomplete task when stuck-kill budget is exhausted", async () => {
|
||||
const task = makeTask(rootDir, {
|
||||
id: "FN-5941-DEAD",
|
||||
stuckKillCount: 6,
|
||||
@@ -314,7 +314,10 @@ describe("FN-5941 reliability interactions: todo/in-progress flapping", () => {
|
||||
}));
|
||||
expect(task.column).toBe("todo");
|
||||
expect(task.stuckKillCount).toBe(7);
|
||||
expect(task.status).toBe("queued");
|
||||
expect(task.status).toBe("failed");
|
||||
expect(task.paused).toBe(true);
|
||||
expect(task.pausedReason).toBe("stuck-loop-exhausted-manual-intervention-required");
|
||||
expect(task.userPaused).not.toBe(true);
|
||||
|
||||
manager.stop();
|
||||
});
|
||||
|
||||
@@ -1214,6 +1214,10 @@ export class SelfHealingManager {
|
||||
* progress preserved, marked failed, and paused for manual resume or
|
||||
* decomposition; tasks with only terminal steps keep the legacy failed
|
||||
* `in-review` handoff path.
|
||||
*
|
||||
* FNXC:SelfHealing 2026-06-14-10:51:
|
||||
* Incomplete stuck-loop exhaustion must park work in a failed/paused state before moving columns, because a post-move patch failure must not leave the task scheduler-runnable.
|
||||
* Engine-owned recovery must not mutate `userPaused`; user intent stays authoritative across races.
|
||||
* - `STUCK_NO_PROGRESS_CHURN`: skips the budget entirely and terminalizes on
|
||||
* the first trigger with operator guidance to decompose or rescope.
|
||||
*
|
||||
@@ -1316,9 +1320,7 @@ export class SelfHealingManager {
|
||||
}
|
||||
|
||||
log.warn(`${taskId} exceeded stuck kill budget (${newCount}/${maxKills}, reason=${reason}) with incomplete steps — parking in todo with progress preserved`);
|
||||
const exhaustedError =
|
||||
`STUCK_LOOP_EXHAUSTED: incomplete task exhausted stuck kill budget (${newCount}/${maxKills}) after last reason=${reason}. ` +
|
||||
"Progress was preserved; manually retry, decompose, or rescope before execution resumes.";
|
||||
const exhaustedError = `STUCK_LOOP_EXHAUSTED: incomplete task exhausted stuck kill budget (${newCount}/${maxKills}) after last reason=${reason}. Progress was preserved; manually retry, decompose, or rescope before execution resumes.`;
|
||||
const parkUpdate = {
|
||||
stuckKillCount: newCount,
|
||||
status: "failed",
|
||||
|
||||
Reference in New Issue
Block a user