feat(FN-3977): complete Step 2 — scheduler overlap bottleneck warnings

Fusion-Task-Id: FN-3977
Fusion-Task-Lineage: c84f2ce0-3726-49b2-ad84-6703edbfecf1
This commit is contained in:
Fusion
2026-05-14 15:36:41 -07:00
committed by gsxdsm
parent 4380f01516
commit 22dd272247
2 changed files with 117 additions and 0 deletions

View File

@@ -2,6 +2,8 @@ import {
getCurrentRepo,
resolveDependencyOrder,
sortTasksByPriorityThenAgeAndId,
computeBlockerFanoutMap,
HIGH_FANOUT_BLOCKER_TODO_THRESHOLD,
type TaskStore,
type Task,
type MissionStore,
@@ -190,6 +192,7 @@ export class Scheduler {
private wasDispatchQueuedReasonLogged = new Set<string>();
private readonly staleTaskReporter: StaleTaskReporter;
private lastStaleTaskReportAt = 0;
private readonly lastHighOverlapFanoutWarningKey = new Map<string, string>();
/**
* Async listener guard convention:
@@ -504,6 +507,33 @@ export class Scheduler {
await this.store.logEntry(taskId, reason);
}
private async emitHighOverlapFanoutWarnings(tasks: Task[]): Promise<void> {
const fanoutMap = computeBlockerFanoutMap(tasks, 3);
const seenBlockers = new Set<string>();
for (const [blockerId, fanout] of fanoutMap) {
if (fanout.overlapBlockedTodoCount < HIGH_FANOUT_BLOCKER_TODO_THRESHOLD) continue;
const state = fanout.escalation ? "long-lived" : "temporary";
const dedupeKey = `${fanout.overlapBlockedTodoCount}:${state}`;
seenBlockers.add(blockerId);
if (this.lastHighOverlapFanoutWarningKey.get(blockerId) === dedupeKey) {
continue;
}
const message = `Overlap bottleneck: ${blockerId} is currently blocking ${fanout.overlapBlockedTodoCount} todo task(s) via blockedBy (${state}).`;
schedulerLog.warn(message);
await this.store.logEntry(blockerId, message);
this.lastHighOverlapFanoutWarningKey.set(blockerId, dedupeKey);
}
for (const blockerId of this.lastHighOverlapFanoutWarningKey.keys()) {
if (!seenBlockers.has(blockerId)) {
this.lastHighOverlapFanoutWarningKey.delete(blockerId);
}
}
}
private async rollbackRunningAgentsForQueuedTodoTask(taskId: string): Promise<void> {
const agentStore = this.options.agentStore;
if (!agentStore) return;
@@ -1053,6 +1083,8 @@ export class Scheduler {
}
}
await this.emitHighOverlapFanoutWarnings(tasks);
const staleWarningWindows = [settings.staleInProgressWarningMs, settings.staleInReviewWarningMs]
.filter((value): value is number => typeof value === "number" && Number.isFinite(value) && value > 0);
const minWarningMs = staleWarningWindows.length > 0 ? Math.min(...staleWarningWindows) : 0;