Two failures the full make-quality flagged after 06adf978 landed on master:
- mypy: _second_agent (test helper) now returns the agent's UUID, so
WorkSessionCreate(agent_id=...) receives a real uuid.UUID rather than the
ORM column type.
- xenon: the single-active-per-task supersede is extracted out of
_create_work_session_if_needed into _supersede_other_active_sessions,
bringing the former back under complexity rank B.
No behavior change; 1566 work-session/gateway/service tests green.
A task re-claimed by a different agent (pool release, reaper unclaim,
escalation redirect) left the prior holder's active work session open.
WorkSessionService.get_active_for_task then ran a one-row query over the
duplicates and raised MultipleResultsFound; the caught failure surfaced
as the cryptic "'NoneType' object has no attribute 'id'" that crashed the
claim/plan/start flow — so the task could never advance, the orchestrator
re-spawned its PM every ~30s forever, and its dependents stayed blocked.
Fixed at three layers:
- active-session lookups return the most-recent session instead of raising
- claiming a task supersedes any other agent's stale active session
(the single-active-per-task invariant), in both WorkSessionService.create
and TaskService._create_work_session_if_needed
- a partial unique index (migration 047, which de-duplicates existing rows
keeping the most recent) enforces it at the DB level; mirrored on the model
Verified: 1614 tests green (work_session + gateway + services), ruff/mypy
clean, migration chain applies + reverses, dedup proven on the real schema.