Repository navigation
fix(web): preserve user-authored text during prompt recall #10056
Description
Activity
Triage
Confirmed bug on current
main. Prompt recall reconstructs “what the user typed” by stripping send-time decorations from persistedmessage.text. Those decorations are inferred by string match only; the timeline does not record which spans the app appended.Diagnosis
History is built on each ArrowUp/ArrowDown from timeline user messages (
buildComposerPromptHistoryEntries→recallableComposerPromptinapps/web/src/components/chat/composerPromptHistory.ts). The helper, unchanged since #9173 (fd773172e):- Drops a leading
Ultrathink:\n(CLAUDE_ULTRATHINK_PREFIX). - Drops a trailing run of
<review_comment>…</review_comment>blocks (stripTrailingReviewComments). - Then strips trailing preview / element / terminal context the same way.
- Returns
""(entry omitted) when the remainder equalsATTACHMENT_ONLY_BOOTSTRAP_PROMPTor starts withPLEASE IMPLEMENT THIS PLAN:\n.
Send time writes a single decorated string.
ChatViewappends terminal, element, preview, and review-comment blocks, thenformatOutgoingPrompt→applyClaudePromptEffortPrefixmay addUltrathink:\n.OrchestrationMessagestores onlyid,role,text, attachments, and turn timestamps — no original composer text and no send-time span list.A user-authored prompt that begins with
Ultrathink:\nor ends with a review-comment block is therefore indistinguishable from an app-generated prefix/suffix. Mid-prompt review-comment blocks already survive (covered by existing tests); a trailing user-authored block does not. Existing tests encode the strip behavior; they do not distinguish identical text with different origins.This is the behavior #9173 shipped (“send-time appends are stripped so you get the text you typed”), implemented as a heuristic because that PR stored nothing extra. It is not caused by the V2 reconciliation. Open PR #10051 preserves this main helper and already names #10056 as the P2 follow-up.
Repro (source-verified)
-
Send exactly:
Ultrathink: Keep this prefix in my example -
Clear the composer.
-
Press ArrowUp.
Recalled text is
Keep this prefix in my example. A prompt whose only extra content is a trailing<review_comment>…</review_comment>block recalls without that block. Same outcome onmain09aac7156/ cited6270a6f88; the helper file has one commit (#9173). No OS-level keyboard run in this triage.Ultrathink: …without the newline is not stripped (startsWith("Ultrathink:\n")only). That is a prefix-shape mismatch, not provenance.Expected
Recall should keep user-authored bytes and drop only context the app added at send time. Origin cannot be inferred from text match alone.
Acceptance (unchanged)
- Literal user-authored prefixes and trailing blocks survive recall.
- Actually generated ultrathink / review-comment / terminal / element / preview / attachment-bootstrap / plan-implementation context stays excluded.
- Tests cover identical text with different origins and leave normal history navigation intact.
Priority and fix shape
P2. Common path (real send-time appends) works. Failure is an edge case: the user typed the same strings the app injects. Timeline still shows the full sent message; workaround is copy from the thread.
A durable fix needs send-time provenance (store original composer text, or the spans the app appended) and recall from that. Tweaking the strip regexes cannot tell the two cases apart and will regress one or the other. #9173 explicitly avoided new persisted fields; that is the constraint, not a reason to close this.
Related
- feat(web): recall sent prompts with the up arrow #9173 — feature that introduced recall + text-only stripping (merged).
- [Feature]: [UX] Terminal style history for chat input text field. #1777 — original history request (closed by feat(web): recall sent prompts with the up arrow #9173).
- fix(orchestrator): preserve task-step elapsed time across restart #10051 — open main→V2 reconciliation; documents this as a known P2 and does not claim a fix.
Not a duplicate of #9173/#10051. Keep this issue open as the tracker. No
needs-triageon the issue today.Filed by Cursor Grok 4.6 via cloud-agent issue triage. Investigation only; no branch, commit, or PR.
- Drops a leading
- addedvia-triageFiled through npx t3 triageFiled through npx t3 triagebugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.
on Sep 5, 2026
Prompt recall can change text that the user actually typed when it resembles content appended by the app. This is an existing main behavior from #9173, verified while reviewing the main-to-V2 reconciliation #10051; it is not caused by the V2 merge.
recallableComposerPromptinapps/web/src/components/chat/composerPromptHistory.tsstrips anUltrathink:\nprefix and trailing<review_comment>…</review_comment>blocks, and omits text matching the attachment-only bootstrap or plan-implementation prefix, using text matching without send-time provenance. A user-authored prompt with those same strings is indistinguishable from generated context.Reproduction: send literal text
Ultrathink:followed by a newline andKeep this prefix in my example. With an empty composer, ArrowUp recalls onlyKeep this prefix in my example. A literal trailing review-comment example can similarly disappear. This is source-verified in main6270a6f88and reconciliationda3cf59dd; the helper file is identical on those revisions. No OS-level keyboard reproduction was performed for this edge case.Expected: recall preserves user-authored text byte-for-byte while excluding context that the application added at send time. Use reliable provenance or another representation that distinguishes the two; avoid inferring origin solely from matching text.
Acceptance:
Verified P2 follow-up; the V2 reconciliation preserves existing main behavior and does not claim this fixed. Checked existing prompt-history issue searches and #9173 before filing.
Reported through the OpenAI Codex harness by GPT-6 Astra.