M26-3 Pool selection: prefer Running over Stopped within idle tier #590

Closed
opened 2026-04-30 19:31:27 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As a service operator, I want pool selection to prefer a Running pool member over a Stopped one when both match a dispatch's labels, so that the fleet doesn't pay cold-start latency when an already-warm container can handle the task.

Acceptance criteria

Selection

  • domain/dispatch/pool.ts pickFromCandidates: within the idle subset (currentTask === null && queue.length === 0), introduce a tier ordering — Running first, Stopped second. Round-robin within each tier preserves load spreading.
  • If the idle subset has a Running member, it wins regardless of round-robin position. Stopped idle members are only chosen when no Running idle peer matches.
  • Stopped does not count as "loaded" for the least-loaded fallback (a Stopped container is more idle than a busy one).
  • Existing match_labels filtering, catch-all fallback, and last-resort behaviour are unchanged.

Lifecycle integration

  • Selection consults the lifecycle module (M26-1) for the current state — does not call docker inspect per dispatch.
  • When a Stopped member is selected, dispatch goes through the M26-1 enqueue path (which transitions to Starting → Running before queue push).

Tests

  • Unit: 2 pool members, 1 Running idle + 1 Stopped idle → Running picked.
  • Unit: all idle but all Stopped → round-robin among Stopped, dispatch starts the chosen one.
  • Unit: 1 Running busy + 1 Stopped idle → Stopped picked (busy is loaded).
  • Unit: round-robin cursor still advances correctly when Running tier is preferred.

Out of scope

  • Lifecycle module (M26-1).
  • Reconcile + watchdog (M26-2).
  • Config + dashboard (M26-4).
  • Events / metrics (M26-5).

References

  • specs/container-lazy-lifecycle.md §Pool agents.
  • apps/server/src/domain/dispatch/pool.ts — current selection logic.
  • docs/reviewer-instances.md — pool member conventions.
  • Memo: pool selection is already deterministic from match_labels + load; this issue adds a Running-vs-Stopped sub-tier inside the existing idle preference, no new scheduler decision.
## User story As a service operator, I want pool selection to prefer a Running pool member over a Stopped one when both match a dispatch's labels, so that the fleet doesn't pay cold-start latency when an already-warm container can handle the task. ## Acceptance criteria ### Selection - [ ] `domain/dispatch/pool.ts` `pickFromCandidates`: within the idle subset (`currentTask === null && queue.length === 0`), introduce a tier ordering — Running first, Stopped second. Round-robin within each tier preserves load spreading. - [ ] If the idle subset has a Running member, it wins regardless of round-robin position. Stopped idle members are only chosen when no Running idle peer matches. - [ ] Stopped does **not** count as "loaded" for the least-loaded fallback (a Stopped container is more idle than a busy one). - [ ] Existing `match_labels` filtering, catch-all fallback, and last-resort behaviour are unchanged. ### Lifecycle integration - [ ] Selection consults the lifecycle module (M26-1) for the current state — does not call `docker inspect` per dispatch. - [ ] When a Stopped member is selected, dispatch goes through the M26-1 enqueue path (which transitions to Starting → Running before queue push). ### Tests - [ ] Unit: 2 pool members, 1 Running idle + 1 Stopped idle → Running picked. - [ ] Unit: all idle but all Stopped → round-robin among Stopped, dispatch starts the chosen one. - [ ] Unit: 1 Running busy + 1 Stopped idle → Stopped picked (busy is loaded). - [ ] Unit: round-robin cursor still advances correctly when Running tier is preferred. ## Out of scope - Lifecycle module (M26-1). - Reconcile + watchdog (M26-2). - Config + dashboard (M26-4). - Events / metrics (M26-5). ## References - `specs/container-lazy-lifecycle.md` §Pool agents. - `apps/server/src/domain/dispatch/pool.ts` — current selection logic. - `docs/reviewer-instances.md` — pool member conventions. - Memo: pool selection is already deterministic from `match_labels` + load; this issue adds a Running-vs-Stopped sub-tier inside the existing idle preference, no new scheduler decision.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#590
No description provided.