Summary
Three parallel queue/capacity abstractions exist in src/backend/services/:
ExecutionQueue — Redis LIST, serial N=1, used by /chat
SlotService — Redis ZSET, parallel N=max_parallel_tasks, used by /task
BacklogService — SQLite FIFO, persistent overflow when slots full
These are one primitive with two knobs (max_concurrent, overflow_policy) expressed as three classes with overlapping TTL logic, overlapping drain paths, and inconsistent interfaces.
Scope
Merge into a single CapacityManager with:
CapacityManager(agent_name, max_concurrent=N, overflow_policy='reject' | 'queue_in_memory' | 'queue_persistent')
/chat becomes CapacityManager(name, 1, 'queue_in_memory')
/task becomes CapacityManager(name, max_parallel, 'queue_persistent')
- One TTL reasoner, one drain callback, one counter source
Depends on
Non-goals (keep v1 tight)
Migration approach (additive-first)
- Land
CapacityManager class alongside the existing three classes.
- Migrate callers one at a time behind a per-agent feature flag.
- Keep old Queue/Slot/Backlog classes until zero callers remain.
- Delete in a separate PR after a soak period.
No multi-PR intermediate states where both paths are partially wired.
Success criteria
- Grep for direct
SlotService / ExecutionQueue / BacklogService instantiation returns zero hits outside CapacityManager and its tests.
/chat and /task semantics unchanged observable-from-outside.
- Single drain path on capacity release.
Context
See docs/planning/ORCHESTRATION_RELIABILITY_2026-04.md — Tier 2.5 Simplification.
Summary
Three parallel queue/capacity abstractions exist in
src/backend/services/:ExecutionQueue— Redis LIST, serial N=1, used by/chatSlotService— Redis ZSET, parallel N=max_parallel_tasks, used by/taskBacklogService— SQLite FIFO, persistent overflow when slots fullThese are one primitive with two knobs (
max_concurrent,overflow_policy) expressed as three classes with overlapping TTL logic, overlapping drain paths, and inconsistent interfaces.Scope
Merge into a single
CapacityManagerwith:/chatbecomesCapacityManager(name, 1, 'queue_in_memory')/taskbecomesCapacityManager(name, max_parallel, 'queue_persistent')Depends on
Non-goals (keep v1 tight)
/chatvs/tasksemanticsMigration approach (additive-first)
CapacityManagerclass alongside the existing three classes.No multi-PR intermediate states where both paths are partially wired.
Success criteria
SlotService/ExecutionQueue/BacklogServiceinstantiation returns zero hits outsideCapacityManagerand its tests./chatand/tasksemantics unchanged observable-from-outside.Context
See
docs/planning/ORCHESTRATION_RELIABILITY_2026-04.md— Tier 2.5 Simplification.