Skip to content

fix(frontend): clear the composer when a retry succeeds - #265

Merged
dovvnloading merged 1 commit into
mainfrom
fix/retry-clears-the-composer
Sep 7, 2026
Merged

fix(frontend): clear the composer when a retry succeeds#265
dovvnloading merged 1 commit into
mainfrom
fix/retry-clears-the-composer

Conversation

@dovvnloading

Copy link
Copy Markdown
Owner

The problem

A failed send deliberately leaves the text in the composer — "Your message is still here" — so the user does not lose it. Retry then sent that text without clearing it, so the message ended up in the transcript and back in the box, one Enter away from being sent a second time.

Root cause

Only submitDraft settled the composer. retryLastPrompt called startGeneration directly and skipped all of it:

const started = await startGeneration(lastPrompt, danglingUserMessageId, lastAttachments);
if (started && !threadId) onThreadCreated(started.threadId);
return Boolean(started);

That skipped more than the draft. The same block also migrates the draft and its attachments from the "new chat" placeholder scope to the real thread — which matters precisely when the retry is what creates that thread, i.e. when the very first message failed.

The fix

The settling logic is now a helper both paths call, so they cannot drift apart:

const settleComposerAfterSend = (started, submittedDraft, submittedAttachments,
                                 submittedScope, submittedAttachmentScope, submittedThreadId) => { ... }

The extraction changed no behaviour, and I checked that before building on it: the existing 33 ChatPage tests passed against the extraction alone, with retry still unwired.

Retry passes the live draft only when it is still the message being retried:

const submittedDraft = draft.trim() === lastPrompt ? draft : "";

lastPrompt is the trimmed input, hence the trimmed comparison. A draft the user has started rewriting since the failure is left alone, and the helper's existing "clear only if unchanged" guard does the rest.

Verification

Test Against original Purpose
a successful retry clears the composer fails pins the defect
a draft rewritten since the failure is left alone passes guard on the narrower half of the fix

Being straight about the second: it does not fail against the original code, because there the composer was never cleared at all — so "still holds the rewritten text" was trivially true. It earns its place by constraining the fix, not by demonstrating the bug.

The first test also asserts the text is still present before clicking Retry, so it cannot pass by the send path having cleared it early.

Check Result
npm test -- --run 275 passed, 31 files
npm run typecheck clean
npm run lint clean
python -m pytest -q 909 passed

Compatibility and rollback

One component. No API contract, stored data, or migration. submitDraft runs exactly the same code it did before, now via a named function. Reverting the commit restores the previous behaviour exactly.

Limits

A retry that fails still leaves the draft in place, which is the same deliberate behaviour as a failed send.

Attachments follow the same rule as the draft: those that were sent are released, and any staged since are kept. That is inherited from the shared helper rather than reasoned about separately for retry, which is the point of sharing it — but it means retry's attachment handling is now as good, and no better, than submit's.

🤖 Generated with Claude Code

A failed send deliberately leaves the text in the composer -- "Your message is
still here" -- so the user does not lose it. Retry then sent that text without
clearing it, so the message ended up in the transcript *and* back in the box,
one Enter away from being sent a second time.

Only `submitDraft` cleared the composer. `retryLastPrompt` called
`startGeneration` directly and skipped all of it, including the migration of
draft and attachments from the "new chat" placeholder scope to the real thread
when a retry is what creates that thread.

That settling logic is now a helper both paths use, so they cannot drift.
Extracting it changed no behaviour: the existing 33 ChatPage tests passed
against the extraction alone, before retry was wired to it.

Retry passes the live draft only when it is still the message being retried
(`draft.trim() === lastPrompt`, since lastPrompt is the trimmed input). A draft
the user has started rewriting since the failure is left alone -- the helper's
existing "clear only if unchanged" guard then does the rest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dovvnloading
dovvnloading merged commit 005e43e into main Sep 7, 2026
7 checks passed
@dovvnloading
dovvnloading deleted the fix/retry-clears-the-composer branch September 7, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant