Repository navigation
Description
Expected behavior
The identity pass — the step that turns a dequeue from a guess into proof — must retire the queued notification the committed user entry actually carries.
Actual behavior
entryClaims matches a queued task-notification on its correlation id alone:
if (!isTaskNotification(entry.text)) return false
const id = notificationIdOf(item.content)
return id !== null && entry.text.includes(id)The docstring justifies this with "Notifications match on a correlation id (present on 1144/1144 in the current corpus)" — presence is universal, but uniqueness is not.
In testing/fixtures/queue-operations/divergence-stranded-background-commands.json, 2 of 126 enqueued notification task ids carry two different bodies. Upstream reuses the id when reconciling a background shell from a previous session, so the queue can hold both under one id:
Background command "Watch both workflows to completion" was stoppedNo completion record was found for this background shell command from the previous session…
With both queued, state.pending.find(...) returns whichever was enqueued first. If the committed entry carries the second verbatim, the first is retired as delivered-observed using the second's UUID as evidence, and the item that actually departed stays pending.
That is the module's defining failure — the wrong item removed, with a convincing-looking cause — and the queue-length-based assertions the module warns about would bless it.
Reproduction
Replay divergence-stranded-background-commands.json events 111–114. The resulting decision's preview names the first notification while its evidence names the second's committed UUID, and the second remains pending. Duplicate ids occur at fixture lines 628 and 1973.
Scope
Pre-existing. entryClaims is byte-identical at 3223c51c, so this reproduces on main today and is not introduced by PR #670.
Filed separately rather than folded into #670 for two reasons. It is outside that PR's problem statement (#665 is about durable queued prompts never rendering), and more importantly the recorded corpus expectations were blessed while this misattribution was live — so correcting it changes recorded outcomes and needs its own evidence pass rather than a merge-gate patch.
Suggested direction
PR #670 fixes the same class of defect on the sibling remove-carrier path via resolveRemoveCarrierTarget: prefer exact normalized text, fall back to id only when unambiguous. entryClaims cannot reuse it directly, because a committed entry legitimately wraps the notification rather than equalling it (that is why prompts use prefix containment). The equivalent strengthening is to prefer the candidate whose full content is contained in the entry text, and fall back to id matching only when a single pending item carries that id.
Evidence
src/renderer/src/session-runtime/claudeQueue/reconcile.ts—entryClaims.testing/fixtures/queue-operations/divergence-stranded-background-commands.json— the two duplicate-id pairs.git show 3223c51c:...reconcile.ts— identical pre-PR definition.
Found by an orchestrated review of PR #670.
Activity
fix(claude-queue): resolve remove carriers by exact body before task id