fix: use truncateUtf16Safe to avoid splitting UTF-16 surrogate pairs#101684
fix: use truncateUtf16Safe to avoid splitting UTF-16 surrogate pairs#101684lzw112 wants to merge 1 commit into
Conversation
Replace .slice(0, N) with truncateUtf16Safe in call sites where bounded text truncation could split a surrogate pair. - workboard: capText(), truncateBadgeText() - diagnostics-otel: clampOtelLogText() - session-cost-usage: user message content (3 sites) - heartbeat-runner: event preview text (6 sites) - chat/commands: clampText() - task-completion-contract: task title - system-presence: presence text
|
Codex review: needs real behavior proof before merge. Reviewed July 7, 2026, 9:33 AM ET / 13:33 UTC. Summary PR surface: Source +9. Total +9 across 8 files. Reproducibility: yes. source-level. Current main still has raw Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Refresh this work on current main, keep only the unique remaining raw truncation sites or fold them into the maintainer consolidation, and add focused regression or redacted runtime proof for the changed display/log paths. Do we have a high-confidence way to reproduce the issue? Yes, source-level. Current main still has raw Is this the best way to solve the issue? Yes for the remaining sites after refresh. The existing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against e591dcfa8213. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +9. Total +9 across 8 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Superseded by #101711, now landed as The useful still-missing boundaries landed through the shared UTF-16 helpers with caller-level regression coverage. Paths already fixed on current Thanks @lzw112 for the broad audit that exposed these remaining edges. |
What Problem This Solves
Text bounded with
.slice(0, N)can split a UTF-16 surrogate pair when the cut point lands between a high surrogate (\uD800–\uDBFF) and its paired low surrogate (\uDC00–\uDFFF). This produces a lone surrogate — invalid Unicode that renders as garbled text (e.g.�or mojibake in emoji / CJK content).Discord's
summarizeDiscordResponseBodywas already fixed in #101355 usingtruncateUtf16Safe. This PR applies the same fix to 12 remaining call sites across 8 files.Why This Change Was Made
truncateUtf16Safe(from@openclaw/normalization-core/utf16-slice) preserves surrogate-pair integrity:.slice(0, N).User Impact
Bounded text (card titles, usage summaries, heartbeat previews, presence status, chat command text) is no longer at risk of displaying broken emoji when truncated near a multi-byte boundary.
Zero behavioral change for ASCII-only text.
Evidence
Verified against commit
0a5552f5on branchfix/workboard-utf16-safe-truncation.All changes follow the mechanical pattern established in #101355:
extensions/workboard/src/store.tscapText()max - 1ui/src/pages/workboard/view.tstruncateBadgeText()maxLength - 1extensions/diagnostics-otel/src/service.tsclampOtelLogText()maxCharssrc/infra/session-cost-usage.ts100,100,maxLensrc/infra/heartbeat-runner.ts200ui/src/lib/chat/commands.tsclampText()maxLengthsrc/tasks/task-completion-contract.ts159src/infra/system-presence.ts64Tests and Validation
Existing truncateUtf16Safe tests cover surrogate-pair edge cases; this PR only adds call sites that consume the existing utility.
Risk Checklist
truncateUtf16Safeis a no-op for ASCII; only emoji/CJK at exact truncation boundaries are affected, and only to correct garbled output.Closes: N/A