fix(infra): keep heartbeat/push message preview truncation UTF-16 safe#101606
fix(infra): keep heartbeat/push message preview truncation UTF-16 safe#101606lsr911 wants to merge 3 commits into
Conversation
Replace raw .slice(0, 200) with truncateUtf16Safe from @openclaw/normalization-core/utf16-slice in heartbeat-runner (6 preview truncation sites) and push-apns parseReason (2 error-body truncation sites) to prevent dangling surrogate pairs when the 200-code-unit boundary splits an emoji or CJK supplementary character. Adds a focused regression test in push-apns.test.ts covering the non-JSON error-body path with an emoji straddling the boundary.
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: the broader open UTF-16 truncation PR carries the same heartbeat/APNs changes, adds sibling coverage, credits this contributor, and is the safer canonical landing path than keeping this overlapping branch open. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Use #101654 as the canonical UTF-16 truncation fix and close this overlapping branch. So I’m closing this here and keeping the remaining discussion on #101654. Review detailsBest possible solution: Use #101654 as the canonical UTF-16 truncation fix and close this overlapping branch. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible with high confidence: current main still slices the affected APNs reason and heartbeat preview strings at raw UTF-16 code-unit 200, so a 199-character prefix followed by an emoji crosses the boundary. I did not run tests because this review is read-only. Is this the best way to solve the issue? No for this PR as the landing target. The helper-based fix is narrow and correct, but the broader canonical PR is the better solution because it covers sibling truncation surfaces, carries focused caller tests, and preserves this contributor's credit. Security review: Security review cleared: The diff only uses an existing internal normalization helper and adds tests; it does not change dependencies, workflows, secrets handling, package metadata, or external code execution. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 60f0749b7fb3. |
The regression test in push-apns.test.ts was inadvertently nested inside the previous it() callback, causing CI failures with 'Calling the test function inside another test function is not allowed'. Move it to the correct scope inside the describe block.
|
Thanks @lsr911. The useful behavior from this PR was consolidated with the sibling UTF-16 boundary fixes into #101654 and landed on The landed fix uses the existing |
What Problem This Solves
Heartbeat event emission and APNs push error parsing both truncate user-generated text with
.slice(0, 200). When the 200-code-unit boundary lands mid-surrogate-pair (common with emoji and CJK supplementary characters in user messages or notification bodies), the dangling high surrogate renders as U+FFFD in operator-visible heartbeat events and APNs error diagnostics.Why This Change Was Made
truncateUtf16Safefrom@openclaw/normalization-core/utf16-sliceis the canonical leaf helper already shipped in the plugin SDK and used across the codebase. Using the leaf import keeps the dependency graph minimal.Evidence
Vitest — APNs parseReason surrogate-pair regression
Calls
sendApnsAlert(the exact production path throughparseReason) with an error body containing emoji straddling the 200-code-unit boundary. Asserts no U+FFFD in the returned reason.Surrogate boundary proof (branch head f70268b)
Heartbeat preview: 6
emitHeartbeatEventcall sites (heartbeatToolResponse.summary,normalized.text, 4×previewText) — all at 200 code units, all switched totruncateUtf16Safe.APNs error body:
parseReasonfallback paths usetruncateUtf16Safe(trimmed, 200)instead oftrimmed.slice(0, 200).Files Changed
src/infra/heartbeat-runner.tstruncateUtf16Safesrc/infra/push-apns.tstruncateUtf16Safesrc/infra/push-apns.test.ts