Accept is optimistic: POST /events acknowledging the intent schedules
cleanupAcceptedSession(), which nulls pendingAcceptedSession before
live-accept.mjs has run. When the accept later failed (missing markers,
preview error, receipt conflict, source_locked), the SSE 'error' guard
keyed on pendingAcceptedSession could no longer match its id, so the
tailored recovery never fired: the user got a generic error toast, the
session was gone, and nothing said the variant was never written
(issue #384, analysis by Cursor Bugbot on #381).
Following the issue's fix sketch, an awaitingAcceptResult id is set on
the optimistic success path and deliberately survives the teardown. The
'error' case matches it and tells the user plainly that the variant was
not saved and to pick + generate again (post-teardown the wrapper may
already be gone, so restoring CYCLING is not honestly possible). The
marker is released when the real accept result arrives (complete /
accept / post-accept agent_done) or when a new session supersedes it.
Regression guard covers the set-before-teardown ordering, the error
match, and cleanupAcceptedSession leaving the marker alone; the existing
source contract now also asserts handleGo clears it.
Prepared with AI assistance (Claude Code), directed by @pbakaus.
Co-Authored-By: Claude Code <noreply@anthropic.com>