fix(KB-148): fix triage concurrency and scheduler double-counting

- Add re-entrance guard to triage poll() to prevent overlapping poll cycles
- Move status update to 'specifying' inside semaphore callback so queued tasks don't appear active
- Fix scheduler double-counting specifying tasks when semaphore is present
- Add tests for poll re-entrance guard, semaphore-aware status transitions, and scheduler slot counting
- Add changeset for patch release
This commit is contained in:
Dustin Byrne
2026-03-28 01:24:02 -04:00
parent 85687be6a0
commit 711b19740d
5 changed files with 246 additions and 7 deletions

View File

@@ -218,7 +218,15 @@ export class Scheduler {
const specifying = tasks.filter(
(t) => t.column === "triage" && t.status === "specifying" && !t.paused,
);
const agentSlots = inProgress.length + specifying.length;
// When a semaphore is provided, it is the single source of truth for
// global concurrency — its availableCount already accounts for ALL
// slot holders (executors, specifiers, mergers). Counting specifying
// tasks in agentSlots as well would double-count them. Without a
// semaphore (fallback mode), count specifying tasks directly.
const agentSlots = this.options.semaphore
? inProgress.length
: inProgress.length + specifying.length;
// When a semaphore is provided, factor in its available slots so we
// don't schedule more tasks than the global limit allows. Triage and