FN-6174: remove overlap priority inversion audit

Remove the unused scheduler overlap-priority inversion audit path.

- drop the scheduler memoization and audit emission for overlap-priority inversion blockers
- delete the reliability interaction test that only covered the removed audit behavior
- update architecture documentation to note the audit removal and keep the overlap deferral rule

Files changed:
 docs/architecture.md                               |   3 +-
 packages/engine/src/__tests__/reliability-interactions/scheduler-overlap-priority-inversion.test.ts   | 212 ---------------------
 packages/engine/src/scheduler.ts                   |  51 -----
 3 files changed, 1 insertion(+), 265 deletions(-)

Fusion-Task-Id: FN-6174

Fusion-Task-Lineage: 3062b405-1f1d-473d-a02f-09a1077376c0
This commit is contained in:
gsxdsm
2026-06-09 23:37:31 -07:00
parent b84f416e07
commit e326c5e8db
3 changed files with 1 additions and 265 deletions

View File

@@ -1,212 +0,0 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { Scheduler } from "../../scheduler.js";
import type { Task, TaskStore } from "@fusion/core";
function makeTask(overrides: Partial<Task> = {}): Task {
return {
id: "FN-001",
title: "task",
description: "",
column: "todo",
dependencies: [],
steps: [],
currentStep: 0,
log: [],
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
...overrides,
} as Task;
}
function createStore(tasks: Task[], scopes: Record<string, string[]>) {
const updateTask = vi.fn(async (id: string, patch: Partial<Task>) => {
const task = tasks.find((candidate) => candidate.id === id);
if (task) Object.assign(task, patch);
return task as Task;
});
const moveTask = vi.fn(async (id: string, column: Task["column"]) => {
const task = tasks.find((candidate) => candidate.id === id);
if (task) task.column = column;
return task as Task;
});
const store = {
listTasks: vi.fn(async () => tasks),
getSettings: vi.fn(async () => ({ maxConcurrent: 10, maxWorktrees: 10, groupOverlappingFiles: true })),
parseFileScopeFromPrompt: vi.fn(async (id: string) => scopes[id] ?? []),
updateTask,
moveTask,
getTask: vi.fn(async (id: string) => tasks.find((task) => task.id === id) ?? null),
logEntry: vi.fn(async () => undefined),
getRootDir: vi.fn(() => "/tmp/project"),
getTasksDir: vi.fn(() => "/tmp/project/.fusion/tasks"),
on: vi.fn(),
off: vi.fn(),
recordRunAuditEvent: vi.fn(async () => undefined),
} as unknown as TaskStore;
return { store, updateTask, moveTask };
}
describe("reliability interactions: FN-5325 scheduler overlap priority inversion", () => {
beforeEach(() => {
vi.restoreAllMocks();
vi.spyOn(Scheduler.prototype as any, "validateTaskFilesystem").mockResolvedValue({ valid: true });
});
it("defers lower-priority overlap while urgent queued task dispatches first", async () => {
const tasks = [
makeTask({ id: "FN-1", priority: "urgent", status: "queued", createdAt: "2026-01-01T00:00:00.000Z" }),
makeTask({ id: "FN-2", priority: "normal", createdAt: "2026-01-01T00:01:00.000Z" }),
];
const { store, moveTask, updateTask } = createStore(tasks, { "FN-1": ["src/a.ts"], "FN-2": ["src/a.ts"] });
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
expect(moveTask).toHaveBeenCalledWith("FN-1", "in-progress", expect.anything());
expect(updateTask).toHaveBeenCalledWith("FN-2", expect.objectContaining({ status: "queued", overlapBlockedBy: "FN-1" }));
});
it("uses createdAt tiebreaker for equal-priority overlap", async () => {
const tasks = [
makeTask({ id: "FN-1", priority: "normal", status: "queued", createdAt: "2026-01-01T00:00:00.000Z" }),
makeTask({ id: "FN-2", priority: "normal", createdAt: "2026-01-01T00:05:00.000Z" }),
];
const { store } = createStore(tasks, { "FN-1": ["src/a.ts"], "FN-2": ["src/a.ts"] });
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
expect(store.logEntry).toHaveBeenCalledWith("FN-2", "queued — blocked by active file-scope lease FN-1 (column=in-progress)");
});
it("preserves FN-4969 fanout ordering and only defers when overlap exists", async () => {
const sharedStamp = "2026-01-01T00:00:00.000Z";
const tasks = [
makeTask({ id: "FN-10", priority: "normal", createdAt: sharedStamp }),
makeTask({ id: "FN-11", priority: "normal", createdAt: sharedStamp }),
makeTask({ id: "FN-21", dependencies: ["FN-10"] }),
makeTask({ id: "FN-22", dependencies: ["FN-10"] }),
];
const { store, moveTask, updateTask } = createStore(tasks, {
"FN-10": ["src/a.ts"],
"FN-11": ["src/b.ts"],
"FN-21": ["src/c.ts"],
"FN-22": ["src/d.ts"],
});
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
expect(moveTask.mock.calls[0][0]).toBe("FN-10");
expect(moveTask).toHaveBeenCalledWith("FN-11", "in-progress", expect.anything());
expect(updateTask).not.toHaveBeenCalledWith("FN-11", expect.objectContaining({ overlapBlockedBy: expect.any(String) }));
tasks.find((task) => task.id === "FN-11")!.column = "todo";
tasks.find((task) => task.id === "FN-10")!.column = "in-progress";
(store.parseFileScopeFromPrompt as any).mockImplementation(async (id: string) => ({ "FN-10": ["src/a.ts"], "FN-11": ["src/a.ts"] }[id] ?? ["src/x.ts"]));
await scheduler.schedule();
expect(updateTask).toHaveBeenCalledWith("FN-11", expect.objectContaining({ status: "queued", overlapBlockedBy: "FN-10" }));
});
it("does not treat implementation task as coordination-only when scope includes source files", async () => {
const tasks = [
makeTask({ id: "FN-1", column: "in-progress", priority: "normal", createdAt: "2026-01-01T00:00:00.000Z" }),
makeTask({ id: "FN-2", column: "todo", status: "queued", priority: "normal", createdAt: "2026-01-01T00:01:00.000Z" }),
];
const { store, updateTask } = createStore(tasks, {
"FN-1": ["packages/engine/src/scheduler.ts"],
"FN-2": ["docs/task-management.md", "packages/engine/src/scheduler.ts"],
});
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
expect(updateTask).toHaveBeenCalledWith("FN-2", expect.objectContaining({ overlapBlockedBy: "FN-1" }));
});
it("emits one inversion audit row across repeated unchanged polls", async () => {
const tasks = [
makeTask({ id: "FN-1", column: "in-progress", priority: undefined, createdAt: "2026-01-01T00:01:00.000Z" }),
makeTask({ id: "FN-2", priority: "urgent", status: "queued", createdAt: "2026-01-01T00:00:00.000Z" }),
];
const { store } = createStore(tasks, { "FN-1": ["src/a.ts"], "FN-2": ["src/a.ts"] });
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
await scheduler.schedule();
await scheduler.schedule();
const calls = (store.recordRunAuditEvent as any).mock.calls.filter(
(call: any[]) => call[0]?.mutationType === "scheduler:overlap-priority-inversion",
);
expect(calls).toHaveLength(1);
expect(calls[0][0]).toMatchObject({
target: "FN-2",
metadata: expect.objectContaining({
candidateId: "FN-2",
blockerId: "FN-1",
candidatePriority: "urgent",
blockerPriority: null,
blockerColumn: "in-progress",
}),
});
});
it("re-emits inversion when the blocker changes", async () => {
const firstBlocker = makeTask({ id: "FN-1", column: "in-progress", priority: "normal", createdAt: "2026-01-01T00:01:00.000Z" });
const secondBlocker = makeTask({ id: "FN-3", column: "todo", priority: "low", createdAt: "2026-01-01T00:02:00.000Z" });
const candidate = makeTask({ id: "FN-2", priority: "urgent", status: "queued", createdAt: "2026-01-01T00:00:00.000Z" });
const tasks = [firstBlocker, secondBlocker, candidate];
const { store } = createStore(tasks, { "FN-1": ["src/a.ts"], "FN-2": ["src/a.ts"], "FN-3": ["src/a.ts"] });
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
firstBlocker.column = "done";
secondBlocker.column = "in-progress";
await scheduler.schedule();
const calls = (store.recordRunAuditEvent as any).mock.calls.filter(
(call: any[]) => call[0]?.mutationType === "scheduler:overlap-priority-inversion",
);
expect(calls).toHaveLength(2);
expect(calls[0][0]?.metadata?.blockerId).toBe("FN-1");
expect(calls[1][0]?.metadata?.blockerId).toBe("FN-3");
});
it("re-emits inversion after overlap clears and later returns", async () => {
const blocker = makeTask({ id: "FN-1", column: "in-progress", priority: "normal", createdAt: "2026-01-01T00:01:00.000Z" });
const candidate = makeTask({ id: "FN-2", priority: "urgent", status: "queued", createdAt: "2026-01-01T00:00:00.000Z" });
const tasks = [blocker, candidate];
const scopes: Record<string, string[]> = { "FN-1": ["src/a.ts"], "FN-2": ["src/a.ts"] };
const { store } = createStore(tasks, scopes);
const scheduler = new Scheduler(store);
(scheduler as any).running = true;
await scheduler.schedule();
blocker.column = "done";
candidate.overlapBlockedBy = undefined;
scopes["FN-2"] = ["src/b.ts"];
await scheduler.schedule();
candidate.column = "todo";
candidate.status = "queued";
blocker.column = "in-progress";
scopes["FN-2"] = ["src/a.ts"];
await scheduler.schedule();
const calls = (store.recordRunAuditEvent as any).mock.calls.filter(
(call: any[]) => call[0]?.mutationType === "scheduler:overlap-priority-inversion",
);
expect(calls).toHaveLength(2);
expect(calls.every((call: any[]) => call[0]?.metadata?.blockerId === "FN-1")).toBe(true);
});
});

View File

@@ -502,8 +502,6 @@ export class Scheduler {
private wasPermanentAgentUnavailable = new Set<string>();
/** Tracks dispatch-queued reason signatures to avoid per-tick log spam. */
private wasDispatchQueuedReasonLogged = new Set<string>();
/** Tracks the last overlap blocker that emitted a priority inversion audit for a task. */
private overlapPriorityInversionMemo = new Map<string, string>();
/** Tracks the last stable concurrency-block signature emitted for a task. */
private dispatchQueuedConcurrencyAuditMemo = new Map<string, string>();
/** Tracks per-task candidacy fingerprints for task:updated auto-claim invalidation gating. */
@@ -793,7 +791,6 @@ export class Scheduler {
this.wasNodeBlocked.delete(task.id);
this.wasPermanentAgentUnavailable.delete(task.id);
this.clearDispatchQueuedReasonMemo(task.id);
this.clearOverlapPriorityInversionMemo(task.id);
this.clearDispatchQueuedConcurrencyAuditMemo(task.id);
void (async () => {
@@ -939,7 +936,6 @@ export class Scheduler {
this.wasNodeDispatchValidationBlocked.clear();
this.wasPermanentAgentUnavailable.clear();
this.wasDispatchQueuedReasonLogged.clear();
this.overlapPriorityInversionMemo.clear();
this.dispatchQueuedConcurrencyAuditMemo.clear();
schedulerLog.log("Stopped");
}
@@ -967,19 +963,6 @@ export class Scheduler {
return true;
}
private shouldEmitOverlapPriorityInversion(taskId: string, blockerId: string): boolean {
const lastBlockerId = this.overlapPriorityInversionMemo.get(taskId);
if (lastBlockerId === blockerId) {
return false;
}
this.overlapPriorityInversionMemo.set(taskId, blockerId);
return true;
}
private clearOverlapPriorityInversionMemo(taskId: string): void {
this.overlapPriorityInversionMemo.delete(taskId);
}
private shouldEmitDispatchQueuedConcurrencyAudit(taskId: string, signature: string): boolean {
const lastSignature = this.dispatchQueuedConcurrencyAuditMemo.get(taskId);
if (lastSignature === signature) {
@@ -1663,37 +1646,6 @@ export class Scheduler {
}
const overlapBlockerTask = tasks.find((candidate) => candidate.id === overlappingTaskId);
if (
overlapBlockerTask
&& this.shouldEmitOverlapPriorityInversion(task.id, overlappingTaskId)
&& compareTasksByPriorityThenAgeAndId(task, overlapBlockerTask) < 0
) {
try {
await this.store.recordRunAuditEvent?.({
taskId: task.id,
agentId: "scheduler",
runId: generateSyntheticRunId("scheduler", task.id),
domain: "database",
mutationType: "scheduler:overlap-priority-inversion",
target: task.id,
metadata: {
candidateId: task.id,
candidatePriority: task.priority ?? null,
candidateCreatedAt: task.createdAt ?? null,
blockerId: overlapBlockerTask.id,
blockerPriority: overlapBlockerTask.priority ?? null,
blockerCreatedAt: overlapBlockerTask.createdAt ?? null,
blockerColumn: activeScopeColumns.get(overlappingTaskId) ?? overlapBlockerTask.column,
source: "scheduler.overlap-priority-inversion",
},
});
} catch (error) {
schedulerLog.warn(
`Task ${task.id} failed to emit overlap priority inversion audit: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
await this.rollbackRunningAgentsForQueuedTodoTask(task.id);
const activeLeaseColumn = activeScopeColumns.get(overlappingTaskId) ?? overlapBlockerTask?.column ?? "unknown";
await this.logDispatchQueuedReason(
@@ -1706,10 +1658,8 @@ export class Scheduler {
if (task.overlapBlockedBy) {
await this.store.updateTask(task.id, { overlapBlockedBy: null });
}
this.clearOverlapPriorityInversionMemo(task.id);
} else if (coordinationOnlyTask && task.overlapBlockedBy) {
await this.store.updateTask(task.id, { overlapBlockedBy: null });
this.clearOverlapPriorityInversionMemo(task.id);
await this.store.logEntry(
task.id,
"coordination/no-commit task bypassed non-implementation overlap lease",
@@ -2064,7 +2014,6 @@ export class Scheduler {
this.wasNodeDispatchValidationBlocked.delete(task.id);
this.wasPermanentAgentUnavailable.delete(task.id);
this.clearDispatchQueuedReasonMemo(task.id);
this.clearOverlapPriorityInversionMemo(task.id);
this.clearDispatchQueuedConcurrencyAuditMemo(task.id);
await this.store.logEntry(task.id, `Node routing resolved: ${effectiveNode.nodeId ?? "local"} (source: ${effectiveNode.source})`);
this.options.onSchedule?.(task);