Skip to content

fix(web): reply labels no longer cut an emoji in half - #35701

Merged
juliusmarminge merged 2 commits into
pingdotgg:mainfrom
ck0i:keel/reply-label-emoji
Nov 14, 2026
Merged

juliusmarminge merged 2 commits into
pingdotgg:mainfrom
ck0i:keel/reply-label-emoji

Conversation

@ck0i

@ck0i ck0i commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

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 \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.

Refs #35660

Verification

  • 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
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T23:46:53.903217Z 461faef PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added vouch:unvouched Author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 12, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 461faef

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.

@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration

Commits

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.

Changes: Response label truncation

Layer / File(s) Summary
Response label boundary
apps/web/src/lib/composerContextRecords.ts, apps/web/src/lib/composerContextRecords.test.ts
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.

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix

Possibly related issues

Possibly related PRs

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: Passed 4

Check name Status Explanation
Title check Passed 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 Passed 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 Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed 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
Recent review info

Run configuration

Commits

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.

@juliusmarminge juliusmarminge added the takeover:in-progress A takeover lead is working on this PR label Nov 14, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juliusmarminge

Copy link
Copy Markdown
Member

Evidence

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.

Before After
Commit 56fd98be52 459e8ceb03
Sent reply label Before: lone surrogate in accessible name and markdown After: complete Unicode label and successful encoding
Failure capture Base assertion failure capture Passed
Interaction Before recording After recording

Scenario source

Not shown:

  • 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.

@juliusmarminge juliusmarminge added takeover:ready Takeover done; auto-lands when gates pass and removed takeover:in-progress A takeover lead is working on this PR labels Nov 14, 2026
@juliusmarminge
juliusmarminge merged commit 8656cdb into pingdotgg:main Nov 14, 2026
46 of 61 checks passed
@juliusmarminge juliusmarminge removed the takeover:ready Takeover done; auto-lands when gates pass label Nov 14, 2026
vedprakash2302 added a commit to vedprakash2302/Cody that referenced this pull request Nov 14, 2026
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants