Background daemon: reaped spare re-claimed from stale roster; duplicate 'settled' events (2.1.220)

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Jul 28, 2026

What happened

On the background/agents daemon (CLI 2.1.220, macOS), the spare-pool roster
can advertise a spare that has already been reaped, so the fleet later "claims"
a dead worker. The user-visible symptom: claude stop <id> reports success, but
the same id reappears as a live session minutes later and is then killed
again. Two related roster-consistency defects, both from the daemon log:

1. Reaped spare re-claimed (stale roster entry). A spare is spawned, reaped
while still unclaimed, then claimed ~78 min after it was already killed:

17:22:53  bg spawned      9bceedd8 (spare)      # pre-warmed, never claimed
17:45:23  bg settled       9bceedd8 (killed)     # reaped as an idle spare
19:03:41  bg claimed-spare 9bceedd8 (fleet)      # fleet claims an ALREADY-DEAD spare
19:06:52  bg settled       9bceedd8 (killed)     # re-killed

The claim at 19:03 should be impossible — 9bceedd8 was settled/killed at 17:45
and should have left the available-spare set.

2. Duplicate settled events for one worker. The same worker id settles
multiple times in a single lifecycle, e.g.:

bg settled a6844776 (killed)     # x2, ~5s apart
bg settled f34d166e (crashed: working directory no longer exists ...)   # x3, identical

A worker should settle exactly once. The repeats suggest the reap path runs
more than once against the same (already-terminal) worker.

Impact

  • claude stop <id> is not trustworthy: the id can be re-served from the stale

roster and reappear as "live", so stopping a session and confirming it's gone
requires re-checking the roster rather than trusting the stop result.

  • Ghost/duplicate lifecycle events make external fleet-management tooling

(that keys off claimed-spare / settled) double-count and mis-track state.

Expected

  • A settled/reaped spare is removed from the available-spare set atomically, so

it can never be claimed-spare afterward.

  • Each worker emits exactly one terminal settled event.

Notes

Repro is timing-dependent (idle spare gets reaped, then a claim races against
the stale roster entry), so I don't have a deterministic trigger — but the log
signature above is stable and reproduces across days on 2.1.220. Happy to share
additional (redacted) daemon-log excerpts if useful.

View original on GitHub ↗