Summary
#1809 shipped minimal recreate-race hardening: container_remove tolerates NotFound and a 409 name-conflict on the run adopts the winner's container. That converts the loser's 500 into a graceful success, but two concurrent starts can still both enter the recreate sequence (double stop/remove work, interleaved injections).
The full fix is a per-agent start lock serializing start_agent_internal, following the existing Redis SETNX+TTL precedent (agent:data_op:{name} in routers/agent_data.py).
Why deferred from #1809
New lock infrastructure on a hot path has its own failure modes (stuck lock blocks starts; TTL sizing; Redis-down behavior; wait-vs-409 contention semantics) — deliberately kept out of a P2 bug fix. See the #1809 plan's decision trail.
Design notes
Acceptance criteria
Refs #1809
Summary
#1809 shipped minimal recreate-race hardening:
container_removetoleratesNotFoundand a 409 name-conflict on the run adopts the winner's container. That converts the loser's 500 into a graceful success, but two concurrent starts can still both enter the recreate sequence (double stop/remove work, interleaved injections).The full fix is a per-agent start lock serializing
start_agent_internal, following the existing Redis SETNX+TTL precedent (agent:data_op:{name}inrouters/agent_data.py).Why deferred from #1809
New lock infrastructure on a hot path has its own failure modes (stuck lock blocks starts; TTL sizing; Redis-down behavior; wait-vs-409 contention semantics) — deliberately kept out of a P2 bug fix. See the #1809 plan's decision trail.
Design notes
agent:start:{name}(register inservices/agent_runtime_state.pyper the bug(lifecycle): stale transport circuit-breaker Redis key survives agent delete/recreate — a fresh healthy agent fast-fails as "unhealthy" #1560 keyspace-parity rule —tests/unit/test_1560_agent_redis_key_parity.pyfails CI on unregisteredagent:*keyspaces)Acceptance criteria
agent_runtime_state.pywith parity-test coverageRefs #1809