Background
Stage-1 dispatcher Week 1 Foundations introduced `ConcurrencyGate` (commit e88a5d1, hardened in b17e289 + 387e4c6) which inserts stub rows into the `attempts` table to track in-flight cap usage. Two known issues need Stage-2 cleanup work:
Issue 1 — Stub row accumulation
Every `gate.acquire()` inserts a row in the `attempts` table with `runnerKind: "concurrency-gate"`. `gate.release()` updates `ended_at` but does not delete. Over months of use, the `attempts` table accumulates stub rows indefinitely.
Fix options:
- (a) Move gate state to a dedicated `gates` table that auto-cleans on release
- (b) Add a daily cleanup job that DELETEs gate rows older than 7d
- (c) Filter `runnerKind != 'concurrency-gate'` from `getActiveAttempts()` AND/OR add a periodic VACUUM
Issue 2 — Double-count coordination with checkAndReserve (Task 5)
When Task 17 wires the pipeline, both `gate.acquire()` AND `idempotency.checkAndReserve()` insert active attempt rows for a single dispatch. `getActiveAttempts().length` will count both — effectively halving the concurrency cap.
JSDoc'd in concurrency.ts as Task-17 coordination work. Three resolution paths documented in code:
- (a) Filter `runnerKind != 'concurrency-gate'` from active count
- (b) Release the gate stub immediately after `checkAndReserve` succeeds
- (c) Skip the gate entirely when `checkAndReserve` is the dispatch path
When
After Task 17 (Plan→Execute pipeline) lands and the real production dispatch flow is exercised. Both issues are non-blocking for Stage 1 daily-drive but will compound over weeks of real use.
Reference
- Branch: `oit/dispatcher-stage-1`
- Surfaced by: Grok 4 batch review of Week 1 Foundations on 2026-04-28
- Source: `src/adapters/oit/dispatcher/concurrency.ts`, `src/adapters/oit/dispatcher/state/sqlite-store.ts`
Background
Stage-1 dispatcher Week 1 Foundations introduced `ConcurrencyGate` (commit e88a5d1, hardened in b17e289 + 387e4c6) which inserts stub rows into the `attempts` table to track in-flight cap usage. Two known issues need Stage-2 cleanup work:
Issue 1 — Stub row accumulation
Every `gate.acquire()` inserts a row in the `attempts` table with `runnerKind: "concurrency-gate"`. `gate.release()` updates `ended_at` but does not delete. Over months of use, the `attempts` table accumulates stub rows indefinitely.
Fix options:
Issue 2 — Double-count coordination with checkAndReserve (Task 5)
When Task 17 wires the pipeline, both `gate.acquire()` AND `idempotency.checkAndReserve()` insert active attempt rows for a single dispatch. `getActiveAttempts().length` will count both — effectively halving the concurrency cap.
JSDoc'd in concurrency.ts as Task-17 coordination work. Three resolution paths documented in code:
When
After Task 17 (Plan→Execute pipeline) lands and the real production dispatch flow is exercised. Both issues are non-blocking for Stage 1 daily-drive but will compound over weeks of real use.
Reference