fix(infra): keep restart log tails UTF-16 safe#103757
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 10, 2026, 5:27 PM ET / 21:27 UTC. Summary PR surface: Source +1, Tests +4. Total +5 across 2 files. Reproducibility: yes. On current main, Review metrics: none identified. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Land the existing-helper reuse at the single shared log-tail boundary with the focused split-surrogate regression test, preserving current storage, budgets, and update/restart control flow. Do we have a high-confidence way to reproduce the issue? Yes. On current main, Is this the best way to solve the issue? Yes. Reusing the existing tested UTF-16 helper at the canonical shared truncation boundary is narrower and less drift-prone than repairing each update caller separately. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against d92d5774f5c6. Label changesLabel justifications:
Evidence reviewedPR surface: Source +1, Tests +4. Total +5 across 2 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
|
c599c80 to
18c567b
Compare
|
Land-ready review complete at exact head
No known proof gaps. |
|
Merged via squash.
|
What Problem This Solves
Fixes an issue where long update or restart log tails could contain a broken character when truncation landed between the two UTF-16 code units of an emoji or another supplementary-plane character.
The malformed tail was persisted in the restart sentinel and could surface as a replacement character when operators inspected update diagnostics.
Why This Change Was Made
This is a root-cause fix at the shared restart-log truncation boundary. It uses OpenClaw's existing surrogate-safe slicing helper while preserving the current tail budget, ellipsis, and start-index behavior.
The change does not alter restart sentinel storage, update/restart control flow, config, schema, or cleanup behavior. Open PR #101687 changes cleanup error logging in the same module but does not touch the log-tail truncation path.
User Impact
Operators can rely on truncated restart and update diagnostics to remain valid UTF-16, including when an emoji crosses the exact tail boundary. In that boundary case, OpenClaw drops the split surrogate pair instead of persisting one invalid half; all other log-tail behavior remains unchanged.
Evidence
The focused regression reproduced the current failure before the production change:
After the fix, the complete restart sentinel test file passed:
Local E2E live proof used a real Node subprocess whose 8,007-code-unit output placed an emoji across the 8,000-unit update-log tail boundary. The production update step captured the output, the restart sentinel persisted it in SQLite, and a real OpenClaw gateway started with the same isolated state and consumed the sentinel:
Observed before gateway startup: the persisted tail was exactly 8,000 code units, began with the truncation ellipsis, retained the final
tail, and contained no lone surrogate. Observed after startup: the gateway had consumed and cleared the sentinel.Additional validation:
node_modules/.bin/oxfmt --check src/infra/restart-sentinel.ts src/infra/restart-sentinel.test.tsnode scripts/run-oxlint.mjs src/infra/restart-sentinel.ts src/infra/restart-sentinel.test.tsgit diff --checkNot tested: a destructive package-manager self-update was not performed. The live proof exercised the real update subprocess boundary, SQLite restart sentinel persistence, and real gateway startup/consumption with isolated local state.