You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(web): reply labels no longer cut an emoji in half - #35701
When you reply to an assistant response, the reply's label is cut to 64 UTF-16 units before it is sanitized. If an emoji starts at unit 63, the cut lands between its two surrogates and the label ends in a lone high surrogate. That broken label is what goes into the reply card's accessible name, the inline reference that gets sent, and the text sent to the provider.
To reproduce, reply to an assistant response whose selected text has 63 characters followed by an emoji. The label ends in \uD83D instead of stopping before the emoji.
Change
responseContextRecord in composerContextRecords.ts now cuts at 63 units when the 64th unit is a high surrogate, then sanitizes the label as before. It is the same check clampContextText in that file already does. The other truncation points (#30345, #30367) already guard against this; this one was missed.
Scope
Small fix for an obvious bug. #35660 reports it. The triage bot closed it as not planned without running anything, but its own comment confirms from the code that the raw 64-unit slice has no surrogate check and that an emoji at offset 63 leaves a lone high surrogate. The new test below fails on main for exactly that reason. Only the web reply action creates this record, so mobile is not affected.
Added "shortens a long reply label without splitting an emoji" next to the existing emoji test for annotation labels in composerContextRecords.test.ts. On main it fails with 63 as followed by a lone \uD83D. With the fix it passes.
Ran the reporter's reproduction as a throwaway test covering the label, the inline reference and the provider prompt. Before: all three contain a lone surrogate. After: all three are well formed.
Checked edge cases through the real sanitizer: 62 letters plus an emoji keeps the emoji, labels shorter than 64 units are unchanged, and 63 spaces plus an emoji falls back to "response".
vp test run on the composerContextRecords, ChatView.logic and assistantTextSelection tests: 107/107 passed. vp run check:changed passed, including the apps/web typecheck.
No screenshots, since the label only changes text that is not drawn on screen. I did not check it in a running client.
Model: Claude Opus 5.5 (claude-opus-5-5), Harness: Claude Code
Replying to an assistant response sliced the label to 64 UTF-16 units
before sanitizing it, so an emoji at that boundary left a lone high
surrogate in the label, the sent reference and the provider prompt.
Back off one unit when the cut lands inside a surrogate pair.
Refs pingdotgg#35660
Macroscope's review found this PR approvable — Reply labels now avoid cutting an emoji's surrogate pair at the length boundary. The change preserves the underlying citation and existing sanitization, with no material change beyond the shortened label text.
You can add or adjust custom eligibility rules. Learn more.
Reviewing files that changed from the base of the PR and between 461faef and 459e8ce.
You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.
Use the checkbox below for a quick retry:
Trigger review
No actionable comments were generated in the recent review.
Walkthrough
Response context labels now avoid ending with a high surrogate when truncation splits an emoji. A test checks the label produced for text with an emoji at the truncation boundary.
responseContextRecord uses a helper that truncates at 64 UTF-16 code units and backs up when the last included unit is a high surrogate. A test verifies that the label contains only the 63-character prefix before the emoji.
pingdotgg/t3code#30345 — Applies a similar surrogate-boundary check in previewAnnotationContextLabel.
Suggested reviewers:maria-rcks, macroscopeapp
Merge Risk: Minimal · previous review 461fa
Reply labels no longer end in half an emoji. The change is small and tested, so merge risk is minimal.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title "fix(web): reply labels no longer cut an emoji in half" matches the main change. The change stops the reply label truncation at 64 UTF-16 units from leaving a lone high surrogate when an emo…
Description check
The description follows the template. It has Problem, Change, Scope, and Verification sections. The Problem section explains the lone high surrogate and gives reproduction steps. The Change section ex…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Title check
Explanation
The title "fix(web): reply labels no longer cut an emoji in half" matches the main change. The change stops the reply label truncation at 64 UTF-16 units from leaving a lone high surrogate when an emoji falls on the boundary. The title is a conventional commit, short, and specific to the web reply label behavior.
Full details: Description check
Explanation
The description follows the template. It has Problem, Change, Scope, and Verification sections. The Problem section explains the lone high surrogate and gives reproduction steps. The Change section explains the cut-at-63 fix and why it matches clampContextText. The Scope section explains why this small fix needs no separate request and links #35660. The Verification section lists the new test, the throwaway reproduction, edge-case checks, and test results, and states that no screenshots were taken and that the change was not checked in a running client. The description notes the model and harness, as the template asks. The description does not include a template-specific statement that the author read CONTRIBUTING.md, which is a minor gap.
Finishing Touches
Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Reviewing files that changed from the base of the PR and between 7d51718 and 461faef.
Files selected for processing: 2
apps/web/src/lib/composerContextRecords.test.ts
apps/web/src/lib/composerContextRecords.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
Real web app, Chromium, 1280×800, light theme, fresh synthetic state on each commit. Replay supplies 63 lowercase a characters followed by 😀 and and then the rest of the sentence. Open the thread, click Reply to this response, type Please continue., and press Enter. Inspect the sent reference's accessible name and markdown attribute. The labeled panel shows actual DOM values.
Proven: scenario fails on base and passes on head. Base retains a lone U+D83D and encodeURIComponent throws URIError; head retains exactly 63 a characters and encoding succeeds.
Persisted provider-facing payload was not independently inspected; this proof directly inspected the sent reference's accessible name and data-markdown-copy in the real browser.
Desktop was not run separately; verification covered the web client.
Brings in upstream through 8656cdb (pingdotgg#35701). The merge had no conflicts.
- Azure DevOps scope: upstream checks out pull requests with Git instead of
`az repos pr checkout` (pingdotgg#31170), so the scope no longer reads `--remote-name`;
the explicit-remote case and its test are gone.
- Reviewed against Cody patches with no change needed: T3 Connect device
approval (pingdotgg#36419) beside bearer renewal, server browser crash recovery (pingdotgg#36389)
keeps Windows sign-in on the replacement tab, mobile final_answer retention
(pingdotgg#36116) beside the wrap-up rule, OpenCode fork usage (pingdotgg#23120) and usage cache.
- Sync notes: Azure checkout change and version-free Cody AppImage names (pingdotgg#36306).
Pipyakas
pushed a commit
to Pipyakas/t3code
that referenced
this pull request
Nov 15, 2026
Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit 8656cdb)
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
size:S10-29 changed lines (additions + deletions).vouch:unvouchedAuthor is not yet trusted in the VOUCHED list.
2 participants
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.
Problem
When you reply to an assistant response, the reply's label is cut to 64 UTF-16 units before it is sanitized. If an emoji starts at unit 63, the cut lands between its two surrogates and the label ends in a lone high surrogate. That broken label is what goes into the reply card's accessible name, the inline reference that gets sent, and the text sent to the provider.
To reproduce, reply to an assistant response whose selected text has 63 characters followed by an emoji. The label ends in
\uD83Dinstead of stopping before the emoji.Change
responseContextRecordincomposerContextRecords.tsnow cuts at 63 units when the 64th unit is a high surrogate, then sanitizes the label as before. It is the same checkclampContextTextin that file already does. The other truncation points (#30345, #30367) already guard against this; this one was missed.Scope
Small fix for an obvious bug. #35660 reports it. The triage bot closed it as not planned without running anything, but its own comment confirms from the code that the raw 64-unit slice has no surrogate check and that an emoji at offset 63 leaves a lone high surrogate. The new test below fails on
mainfor exactly that reason. Only the web reply action creates this record, so mobile is not affected.Refs #35660
Verification
composerContextRecords.test.ts. Onmainit fails with 63as followed by a lone\uD83D. With the fix it passes."response".vp test runon the composerContextRecords, ChatView.logic and assistantTextSelection tests: 107/107 passed.vp run check:changedpassed, including theapps/webtypecheck.Model: Claude Opus 5.5 (claude-opus-5-5), Harness: Claude Code