feat(telegram-codex): survive a restart with cause-aware recovery context (DIVE-3965) - #69
Merged
Merged
Conversation
…text, not an apology (DIVE-3965) The dispatcher already resumed its Codex thread, but everything a restart had cost was collapsed into one sentence asking the person to resend. Three facts were missing from it. A clean stop and a crash now read differently: markCleanShutdown() persists the intent on the way out, and its ABSENCE is the load-bearing half — a SIGKILL, an OOM or a host reboot never gets to write it. A thread the app-server can no longer resume is now recorded rather than silently replaced, so the person is not the only one who notices the conversation is gone. Both facts become a one-line recovery context that rides the NEXT real turn's input. The interrupted message is never resubmitted: replaying it would duplicate work Codex may already have done before it died. The context is consumed only once the turn it rode on exists, so a failed turn/start keeps it for the retry. The interrupted-turn notice is sent exactly once per interrupted turn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
P06 of the Codex channels series (DIVE-3965), plugin half. The CLI half is 5dive-ai/5dive#PENDING.
The dispatcher already resumed its Codex thread across a restart. What it did not do was
say what the restart had COST — and three different costs were collapsed into one sentence
asking the person to resend.
What changed
markCleanShutdown()persists theintent on the way out, synchronously, and
initialize()clears it. The ABSENCE of thatflag is the load-bearing half: a SIGKILL, an OOM or a host reboot never gets to write it,
so "restarted" stops being what the chat is told about a crash.
thread/resumethe app-serverrejects already fell back to
thread/start; now the reason is captured and carried.input item. The interrupted message is never resubmitted — replaying it would duplicate
work Codex may already have done before it died, which is what "without duplicating
turns" means. It is consumed only once the turn it rode on actually exists, so a
turn/startthat threw keeps it for the retry.Tests
test/codex-dispatcher.test.ts, six new arms: clean-vs-crash wording,markCleanShutdownpersistence, recovery-context injection with an explicit "exactly one turn/start, and it is
NOT the interrupted message" assertion, stale-thread recovery, the once-only notice, and
the shape of the recovery line itself.
bun testat this head: 1204 pass / 0 fail across 47 files.🤖 Generated with Claude Code