Skip to content

refactor: consolidate ExecutionQueue + SlotService + BacklogService into CapacityManager #428

Description

@vybe

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)

  1. Land CapacityManager class alongside the existing three classes.
  2. Migrate callers one at a time behind a per-agent feature flag.
  3. Keep old Queue/Slot/Backlog classes until zero callers remain.
  4. 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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions