镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

fix(app): cut a conversation title without leaving half a character - #740

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
aniruddhaadak80:fix/channel-label-surrogate
Oct 6, 2026
Merged

davidmckayv merged 2 commits into
CopilotKit:mainfrom
aniruddhaadak80:fix/channel-label-surrogate

Conversation

@aniruddhaadak80

Copy link
Copy Markdown
Contributor

What this changes

conversationLabel cut the conversation title with slice(0, 60). slice counts UTF-16 code units and an emoji is two of them, so a title whose 60th unit was the first half of an emoji kept that half and dropped the second. The sidebar and the channel picker both draw this label, and both rendered a replacement character where the character should have been.

app/src/lib/voice/cut.ts already exists for this case and documents it, and app/src/lib/channels/route.ts inlines the same two-line guard for its own cut. This reuses openingOf rather than adding a third copy of it.

The cut is at most one unit shorter than before, which is the behaviour cut.ts already specifies for every other caller.

Where it runs

  • New state that outlives a request? No. openingOf is a pure function of the string it is given, called during render.
  • What happens on the second replica? Unchanged. The label is computed in the browser from data the server already sent; nothing is shared and nothing new is shared.
  • Anything serialised? No.
  • Anything fanned out to a browser? No new fan-out. The label is already rendered in the browser by the sidebar and the picker; this changes the string it renders.
  • New listener, port, or schedule? No.

Boundary and audit

  • Every acting call still goes through the gateway: resolve, decide, audit, then act. No acting call; this is a display string.
  • New refusals and new failures each write a row. No new refusal or failure. No request is made, so nothing is audited.
  • Nothing new is trusted from the client that the server can resolve itself. The title is text the person already sees, cut for display; no decision is made from it here.

Changelog

  • A line in CHANGELOG.md under Unreleased. A deployment behaves differently afterwards: a conversation whose title is cut lands on an emoji shows that emoji rather than a replacement character.

Proof

app/src/lib/channels/label.ts and the new app/tests/channel-label.test.ts, on Bun 1.3.14.

The input is 59 ASCII characters and then an emoji, so the 60th code unit is the emoji's high surrogate and the cut lands between the halves.

Without the fix:

expect(received).toBe(expected)
Expected: false
Received: true

on hasLoneSurrogate(label).

With the fix:

(pass) a conversation label > keeps a short title whole
(pass) a conversation label > cuts a long title to the limit
(pass) a conversation label > does not leave half a character when the cut lands on one
(pass) a conversation label > falls back to the name when there is no title
 4 pass
 0 fail
 6 expect() calls

bunx biome lint and bunx biome format clean on both files; bun run typecheck in app (tsc --noEmit) clean.

Copilot AI balanced review requested due to automatic review settings October 6, 2026 07:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

conversationLabel cut the title with slice(0, 60), and slice counts UTF-16 code units, so a title whose 60th unit was the first half of an emoji kept that half and dropped the second. The sidebar and the picker both draw this label, and both rendered a replacement character where the character should have been.

lib/voice/cut.ts already exists for this and documents the case; lib/channels/route.ts inlines the same two-line guard for its own cut. This reuses openingOf rather than adding a third copy.
@aniruddhaadak80
aniruddhaadak80 force-pushed the fix/channel-label-surrogate branch from 0a1f244 to 85d6c93 Compare October 6, 2026 07:39
@aniruddhaadak80

Copy link
Copy Markdown
Contributor Author

CI on this branch is waiting on a maintainer to approve the workflow run, so it reports action_required rather than a result. Nothing is outstanding on my side.

The branch is rebased onto f4bc60b and conflict-free. What I could run locally is green:

  • app/tests/channel-label.test.ts — 4 pass, 0 fail. The new test fails on 63f99cf3 with a lone surrogate in the label, which is the defect.
  • bunx biome lint and bunx biome format clean on the changed files.
  • bun run typecheck in app clean.

@davidmckayv
davidmckayv merged commit 34a29bf into CopilotKit:main Oct 6, 2026
This was referenced Oct 6, 2026
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.

3 participants