Files
roboco/tests/unit/db
e2f7097aab Persist the PM-respawn counter across orchestrator restarts (#275)
* feat(orchestrator): add respawn_tracker table + migration 051

Durable backing for AgentOrchestrator._pm_respawn_tracker (the PM-respawn
loop breaker). Kept only in memory it reset to count=1 on every restart,
re-burning the strike threshold against a still-wedged task. RespawnTrackerTable
mirrors WaitingRecordTable: composite PK (agent_slug, task_id) matching the
in-memory key; task_id is intentionally NOT a FK (the startup loader validates
against live tasks so a stale counter can't resurrect). Migration 051 verified
with a real alembic upgrade head + downgrade -1 + re-upgrade on Postgres.

* feat(orchestrator): persist the PM-respawn counter across restarts

The PM-respawn loop breaker (_pm_respawn_tracker) lived only in memory, so an
orchestrator restart reset a wedged task's strike count to 1 and re-burned the
whole threshold (4 spawns x container cost) before the gate fired again.

Write-through each gate mutation to the respawn_tracker table via a
fire-and-forget _schedule_respawn_persist (on the existing _bg_tasks strong-ref
set; a DB hiccup degrades to in-memory-only, never gates/un-gates a spawn), and
restore_respawn_tracker() repopulates the counter at startup, validating each
row against live tasks (drops terminal/missing) so a stale counter can't
resurrect against a fixed task. Best-effort + inert when the table is empty.
Cannot manufacture a spawn — the counter only ever suppresses one.

(_instances reconcile, the spec's other goal, already shipped as
_readopt_running_agents.)

* fix(types): cast Mapped[UUID] columns in project routes + self_heal

A clean `mypy roboco/ tests/` run surfaces 7 pre-existing errors in files this
branch doesn't touch: project-route handlers and self_heal_engine pass a
ProjectTable.id (declared Mapped[UUID] against SQLAlchemy's dialect UUID, so
mypy infers sqlalchemy.sql.sqltypes.UUID[Any]) where a uuid.UUID is expected.
An incremental .mypy_cache had hidden them. Apply the same targeted cast unblock
used for the prior batch; the deeper fix (migrating the ~88 Mapped[UUID]
columns to Mapped[uuid.UUID]) remains a separate dedicated task.

* docs(orchestrator): document respawn_tracker durability

Add the orchestrator runtime-state durability note to CLAUDE.md (respawn_tracker
write-through + restore; _instances reconciled-from-Docker) + the migration-051
narrative, and a CHANGELOG [Unreleased] Fixed entry. Also type-clean the new
respawn_tracker table test (cast __table__ to Table under TYPE_CHECKING).

---------

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-06-26 03:36:42 +02:00
..