Summary
When an agent accepts a quest, quest_accept atomically claims it via CAS UPDATE (see internal/quest/accept.go:90) and the quest stays in_progress until explicit quest_clear or quest_forfeit. If the agent crashes, disconnects, or otherwise terminates without releasing the quest, there is no heartbeat or timeout mechanism to recover the claim. The quest is held indefinitely by a dead owner.
The atomic re-claim rejection (returning AlreadyClaimedError) works correctly for the concurrent-accept race, but it also blocks legitimate recovery: a new agent cannot pick up a stuck quest, and there is no operator-side surface for diagnosing how long a quest has been in flight or which owner held it last.
Affected files
internal/quest/accept.go
internal/quest/list.go (status filters / introspection)
internal/quest/forfeit.go (manual release path)
Acceptance
- A configurable stale threshold (e.g.
claimed_at > N minutes ago) is surfaced via quest list or a new introspection verb.
- Stale claims can be auto-released, OR explicitly reclaimed via an override flag (e.g.
--force) so the choice stays with the operator.
- The current atomic re-claim contract for non-stale claims is preserved.
- Output surfaces
claimed_at and claimed_by so an operator can decide before overriding.
Summary
When an agent accepts a quest,
quest_acceptatomically claims it via CAS UPDATE (seeinternal/quest/accept.go:90) and the quest staysin_progressuntil explicitquest_clearorquest_forfeit. If the agent crashes, disconnects, or otherwise terminates without releasing the quest, there is no heartbeat or timeout mechanism to recover the claim. The quest is held indefinitely by a dead owner.The atomic re-claim rejection (returning
AlreadyClaimedError) works correctly for the concurrent-accept race, but it also blocks legitimate recovery: a new agent cannot pick up a stuck quest, and there is no operator-side surface for diagnosing how long a quest has been in flight or which owner held it last.Affected files
internal/quest/accept.gointernal/quest/list.go(status filters / introspection)internal/quest/forfeit.go(manual release path)Acceptance
claimed_at > N minutes ago) is surfaced viaquest listor a new introspection verb.--force) so the choice stays with the operator.claimed_atandclaimed_byso an operator can decide before overriding.