🤖 fix: match a send's text in the draft merge only as a whole block - #5577
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 839109e964
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7037a70e83
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Readiness record (head Assessments: 8 in total, under a budget exception the issue coordinator granted (ceiling 8, one further push only).
Exception: findings went 0, 1, 1, so the standard +2 extension did not apply. The coordinator authorized slots 6–8 because the slot-4 P2 was a regression this PR introduced. Rule for the last round: a P1 introduced by the last push stops the merge; anything else goes to a backlog issue with no further push. No P1 was found. Formal: local full Merge condition: required CI green on Generated with |
Summary
The backend draft merge now matches a send's text only as a whole block: a blank line or the text's edge on both sides. Before,
removeDraftBlockalso matched the send's text at the end or start of a longer line, so a window's own sentence could lose its last word. Fixes #5567.Background
removeDraftBlock(src/common/utils/composerDraftText.ts) took its end checks fromremoveSentText, which accepts any whitespace boundary.removeDraftBlock("I said yes", "yes")returned"I said". Two callers use it:mergeWriteindraftService.tstakes stale copies of pending or accepted sends out of a window's write, and checks whether a returned send is still in the text.DraftStore.observeSends(hasDraftBlock) marks a sendinUnsavedTextwhen the window's unsaved text holds it.Example from the issue: window B has unsaved "I said yes" when window A sends "yes". The send is accepted, and B's next write stored "I said".
Implementation
removeDraftBlockscans every occurrence of the block and keeps only whole-block matches. The order stays as before: the end first, then the start, then the middle.removeSentText(the composer's own send) is unchanged. The text after a removed block loses only its leading line breaks, so an indented block after it (code) keeps its indentation, and the text before it loses only its trailing line breaks, so the line before keeps its trailing spaces (a Markdown hard break). The first version of this PR stripped both in the middle case (found by the independent readiness check and the normal review).One existing test changed its expectation. "unsaved text that still holds a not-accepted send's text does not get it twice" used unsaved
"hello world"for the returned send"hello"and expected the send to count as present. Under whole-block matching,"hello world"is the user's own line, so the returned send comes back beside it ("hello\n\nhello world"): a visible duplicate instead of a silent loss. The test now covers both cases: the send typed again as its own block (not added twice), and a line that only starts with it (comes back).formal/composer-drafts/ComposerSendText.tla: comment only. Its items are whole blocks, soFoundwas already block equality. The comment now says the code matches that since this change.Validation
Pre-fix failures (new tests on
origin/main'scomposerDraftText.ts, 45 pass / 4 fail):After the fix:
composerDraftText,draftService,draftService.pendingSends,DraftStoreandDraftStore.sendIdstests pass (118 pass, 0 fail). The DraftStore tests run two real DraftStore windows against a real DraftService.formal/composer-drafts/check.sh: running on this head; the result will be added here before merge.Deferred
Two normal-review findings on
7037a70e83are tracked in #5586 (backlog): indentation on the block's own line counts as a boundary, and the per-occurrence scan cost on pathological near-limit drafts.Risks
Low. Only the backend merge's text matching changes. A send's text inside a longer line is now kept instead of cut. In the "returned send beside a line that starts with it" case the user sees the send's text twice instead of losing it.
Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$2.57