Repository navigation
fix(server): create_threads titles no longer end in a broken character - #29163
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This change prevents truncated MCP thread titles from ending with half of a Unicode character. Its impact is confined to title formatting for newly created threads, with existing defaults and execution behavior unchanged. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Unicode-safe title truncation apps/server/src/mcp/OrchestratorMcpService.ts, apps/server/src/mcp/OrchestratorMcpService.test.ts |
threadTitle moves the cut point back one code unit when it would split a surrogate pair. A test checks the title for a prompt with an emoji at the boundary. |
Estimated code review effort: 2 (Simple) | ~5 minutes
Change: Bug fix
Suggested labels: bugfix, size:XS
Suggested reviewers: juliusmarminge, macroscopeapp
Merge Risk: 
Minimal · up to 0a67f
-
The Unicode-safe title truncation fix is ready to merge.
Pre-merge checks: 
4
-
Full details: Title check
-
Explanation
The title "fix(server): create_threads titles no longer end in a broken character" matches the main change. The PR changes the default title truncation in threadTitle so it no longer splits a UTF-16 surrogate pair, which would otherwise store U+FFFD. The title is a conventional commit, specific, and under 72 characters.
Full details: Description check
-
Explanation
The description follows the repository template. It has a Problem section that explains the surrogate-pair truncation bug, its effect on SQLite storage, and reproduction context. The Change section explains the fix in threadTitle and its reuse of the sanitizeThreadTitle check. The Scope and approval section explains why no prior issue is needed for this small fix and lists overlapping open PRs. The Verification section gives the red test before the fix, the green test run, the check:changed results, and what was not checked. The model and harness line is present at the end.
-
Finishing Touches
-
Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
|
Note Written by Verified in a real dev environmentverified unchanged head on on this pr, the same flow returned successfully to the parent. the title was exactly 76 short server transcript:
before, main: after, pr: Not verifiednative ios/android, electron desktop, macos/windows, other providers, and relay/tunnel connections were not exercised. this pass verified the linux web client and the codex provider path. the recording shows completed child threads and navigation, not the original turn streaming. |


Problem
When an agent calls
create_threadswith apromptand notitle, the server makes a default title from the prompt. It cuts the prompt to 77 UTF-16 units and adds.... If an emoji (or another character outside the BMP) sits across units 76–77, the cut keeps only half of it. SQLite stores the leftover lone surrogate as U+FFFD, so the thread shows up as…aaa�...on every client, and the broken character can't be undone.Long agent prompts with emoji are common, so anyone whose agent fans out threads can hit this.
Change
threadTitleinapps/server/src/mcp/OrchestratorMcpService.tsnow moves the cut back one unit when it would land between a high and a low surrogate. This uses the same check, limits, and...suffix assanitizeThreadTitle(#27483). Titles that don't end in a split character are unchanged.Scope and approval
Very small, focused fix for an obvious bug, so no prior issue: it changes one expression in one function, and merged PRs #27273, #27483, #27497, and #27841 fixed the same kind of bug in other places.
Overlap search: #28831 (open) fixes the same kind of cut in
scheduleTask's default title in this file. Its diff touches only that code and the scheduled-task tests, notthreadTitle, so the two PRs don't conflict. If maintainers would rather land both fixes together, that's fine. #27483 fixed only provider-coresanitizeThreadTitle. #24611 (open) changes only the sharedtruncatehelper, which isn't used here. A search for "create_threads title" found no other PRs.Verification
main: I added one morecreateThreadscall with an emoji at the cut, plus a title assertion, to the existing case "replays accepted creation on the $name without authorizing new disabled work". Both parameterized rows fail:Expected: "aaaa…aaaa..."/Received: "aaaa…aaaa�..."vp test run src/mcp/OrchestratorMcpService.test.ts: 57 passed (57).vp run check:changed: format, lint, knip, graphql, typecheck (3 packages), tests (62) all pass.Reviewed with sol-loop: 1 round with GPT-6.1 Sol on high. Sol verdict: approved, no actionable findings.
Created with Claude Opus 5.5 in Claude Code (T3 Code).
🤖 Generated with Claude Code