fix(FN-7273): prevent stale step resume regressions
This commit is contained in:
@@ -55,6 +55,19 @@ describe("TaskStore.updateStep step-order guard", () => {
|
||||
expect(updated.log.some((entry) => entry.action.includes("Ignored done→in-progress regression"))).toBe(true);
|
||||
});
|
||||
|
||||
it("no-ops out-of-order in-progress updates while an earlier step is active", async () => {
|
||||
const store = harness.store();
|
||||
const task = await harness.createTaskWithSteps();
|
||||
|
||||
await store.updateStep(task.id, 0, "in-progress");
|
||||
const updated = await store.updateStep(task.id, 2, "in-progress");
|
||||
|
||||
expect(updated.steps[0].status).toBe("in-progress");
|
||||
expect(updated.steps[2].status).toBe("pending");
|
||||
expect(updated.currentStep).toBe(0);
|
||||
expect(updated.log.some((entry) => entry.action.includes("Ignored out-of-order in-progress for step 2"))).toBe(true);
|
||||
});
|
||||
|
||||
// ── U6: graph-source projection discipline (KTD-7/KTD-11) ──────────────────
|
||||
|
||||
it("graph source: done is legal for explicitly independent steps even when an earlier step is in-progress", async () => {
|
||||
@@ -65,6 +78,7 @@ describe("TaskStore.updateStep step-order guard", () => {
|
||||
const steps = primed.steps.map((s, i) => (i === 2 ? { ...s, dependsOn: [] } : { ...s }));
|
||||
await store.updateTask(task.id, { steps });
|
||||
|
||||
await store.updateStep(task.id, 0, "done", { source: "graph" });
|
||||
await store.updateStep(task.id, 1, "in-progress", { source: "graph" });
|
||||
const updated = await store.updateStep(task.id, 2, "done", { source: "graph" });
|
||||
|
||||
@@ -73,6 +87,22 @@ describe("TaskStore.updateStep step-order guard", () => {
|
||||
expect(updated.log.some((e) => e.action.includes("Ignored out-of-order done for step 2"))).toBe(false);
|
||||
});
|
||||
|
||||
it("graph source: in-progress is legal for explicitly independent steps", async () => {
|
||||
const store = harness.store();
|
||||
const task = await harness.createTaskWithSteps();
|
||||
const primed = await store.getTask(task.id);
|
||||
const steps = primed.steps.map((s, i) => (i === 2 ? { ...s, dependsOn: [] } : { ...s }));
|
||||
await store.updateTask(task.id, { steps });
|
||||
|
||||
await store.updateStep(task.id, 0, "in-progress", { source: "graph" });
|
||||
const updated = await store.updateStep(task.id, 2, "in-progress", { source: "graph" });
|
||||
|
||||
expect(updated.steps[0].status).toBe("in-progress");
|
||||
expect(updated.steps[2].status).toBe("in-progress");
|
||||
expect(updated.currentStep).toBe(2);
|
||||
expect(updated.log.some((e) => e.action.includes("Ignored dependency-order in-progress for step 2"))).toBe(false);
|
||||
});
|
||||
|
||||
it("graph source: missing dependsOn defaults to previous step and blocks early verification", async () => {
|
||||
const store = harness.store();
|
||||
const task = await harness.createTaskWithSteps();
|
||||
|
||||
@@ -9388,15 +9388,18 @@ ${TASK_UPSERT_SQL_ASSIGNMENTS}
|
||||
return task;
|
||||
}
|
||||
|
||||
if (status === "done") {
|
||||
// The set of predecessor steps that must be done/skipped before this step
|
||||
// may go done. Legacy: strict index order (every earlier step). Graph:
|
||||
// the step's dependsOn list, with absent dependsOn defaulting to the
|
||||
// immediately-preceding step. A deliberately empty dependsOn array is the
|
||||
// opt-in for an independent graph step.
|
||||
if (status === "done" || status === "in-progress") {
|
||||
// The set of predecessor steps that must be done/skipped before this
|
||||
// step may start or finish. Legacy: strict index order (every earlier
|
||||
// step). Graph: the step's dependsOn list, with absent dependsOn
|
||||
// defaulting to the immediately-preceding step. A deliberately empty
|
||||
// dependsOn array is the opt-in for an independent graph step.
|
||||
/*
|
||||
FNXC:WorkflowStepControl 2026-06-29-10:51:
|
||||
Graph-owned execution may complete explicitly independent steps out of index order, but unannotated task plans are sequential by default. FN-7228 showed Testing & Verification starting while Preflight/implementation were still active because step-session planning treated missing dependencies as independent. Keep TaskStore projection consistent with the graph scheduler: absent dependsOn means previous-step dependency; explicit dependsOn: [] means independent.
|
||||
|
||||
FNXC:WorkflowStepControl 2026-06-30-07:45:
|
||||
FN-7260 showed the same ordering invariant can be broken earlier by agent-visible progress updates: a stale resume prompt told the executor to start Step 3 while Step 0 was still in progress, and TaskStore accepted the out-of-order `in-progress` write. Apply the predecessor/dependency gate to step start as well as step completion so the card, task detail, and executor prompt cannot advertise later sequential work before earlier steps finish.
|
||||
*/
|
||||
let blockingIndex = -1;
|
||||
let blockingStatus: import("./types.js").StepStatus | undefined;
|
||||
|
||||
Reference in New Issue
Block a user