Skip to content

fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation#101600

Merged
steipete merged 2 commits into
openclaw:mainfrom
maweibin:fix/tasks-emoji-truncation
Jul 7, 2026
Merged

fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation#101600
steipete merged 2 commits into
openclaw:mainfrom
maweibin:fix/tasks-emoji-truncation

Conversation

@maweibin

@maweibin maweibin commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

the task CLI table truncator in src/commands/tasks.ts truncates text with .slice() which counts UTF-16 code units. When a surrogate-pair character (emoji) straddles the cut boundary, .slice() produces a lone surrogate (e.g. \ud83d) that renders as in terminal output.

This is user-visible: the truncated text comes from real user-facing output, so any content containing emoji near the 78-character boundary can hit this.

Why This Change Was Made

Replace .slice() with truncateUtf16Safe() from @openclaw/normalization-core/utf16-slice so the truncation counts full code points instead of UTF-16 code units. This matches the already-merged PR #101517 (session-cost-usage) and the existing shortenText helper in src/commands/text-format.ts:5.

User Impact

Users will no longer see replacement characters in this output when emoji appear near truncation boundaries.

Evidence

Real environment tested: local OpenClaw source checkout, Node v24.13.1, PR head.

Exact steps after this patch:

node --import tsx -e "
const { truncateUtf16Safe } = await import('@openclaw/normalization-core/utf16-slice');
const content = 'x'.repeat(78) + '🚀tail';
const before = content.slice(0, 78 + 1);
const after = truncateUtf16Safe(content, 78 + 1);
const t = (s) => /[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(s);
console.log('BEFORE (.slice):', JSON.stringify(before.slice(-10)), 'lone:', t(before));
console.log('AFTER  (truncateUtf16Safe):', JSON.stringify(after.slice(-10)), 'lone:', t(after));
"

Evidence after fix:

BEFORE (.slice):           "xxxxxxx\ud83d…"  lone: true
AFTER  (truncateUtf16Safe): "xxxxxxxxx…"       lone: false

Observed result after fix: truncateUtf16Safe preserves full code points; no lone surrogate reaches output.

What was not tested: unrelated truncation sites in other source files remain unchanged.

Regression Test Plan

node scripts/run-vitest.mjs src/commands/tasks.test.ts --run

AI-assisted.

…t truncation

.slice() counts UTF-16 code units; use Array.from() to count full
code points instead, preventing lone surrogates in task list output.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: XS labels Jul 7, 2026
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 7, 2026, 7:50 AM ET / 11:50 UTC.

Summary
The branch imports truncateUtf16Safe for the task command table truncator and adds a regression test for task list summaries at an emoji boundary.

PR surface: Source -2, Tests +25. Total +23 across 2 files.

Reproducibility: yes. from source inspection: current main's task table truncator can split a surrogate pair when task summary text crosses its column boundary. I did not run a live CLI repro, so this is source-reproducible rather than fully reproduced.

Review metrics: none identified.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🦞 diamond lobster
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted real terminal output or a terminal screenshot from openclaw tasks list showing an emoji-boundary task summary after the fix.
  • Update the PR body with that proof so a fresh ClawSweeper review can re-evaluate the proof gate.

Proof guidance:

  • [P1] Needs real behavior proof before merge: Only test/mock-runtime evidence is present; before merge, the contributor should add redacted real terminal output, a terminal screenshot, copied live output, or logs from openclaw tasks list showing the fixed emoji-boundary behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Needs real behavior proof before merge; the current evidence is unit/mock-based plus CI/autoreview, not an observed terminal run.

Maintainer options:

  1. Decide the mitigation before merge
    Land the helper-based task truncation fix after redacted real openclaw tasks list terminal proof is added and exact-head checks are green; handle other raw truncation sites in their separate narrow PRs.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] No automated repair is needed; the remaining blocker is contributor-supplied real behavior proof and ordinary maintainer/CI review.

Security
Cleared: The diff only changes task command formatting and a focused test; it does not alter dependencies, secrets, CI, package metadata, permissions, or code execution boundaries.

Review details

Best possible solution:

Land the helper-based task truncation fix after redacted real openclaw tasks list terminal proof is added and exact-head checks are green; handle other raw truncation sites in their separate narrow PRs.

Do we have a high-confidence way to reproduce the issue?

Yes from source inspection: current main's task table truncator can split a surrogate pair when task summary text crosses its column boundary. I did not run a live CLI repro, so this is source-reproducible rather than fully reproduced.

Is this the best way to solve the issue?

Yes: using the existing truncateUtf16Safe helper is the best narrow fix for this surface because it preserves the established UTF-16 code-unit cap while avoiding dangling surrogates. Other raw truncation sites are being handled as separate focused PRs rather than broadening this task-only change.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against bd7da9decd7e.

Label changes

Label changes:

  • add P2: This is a bounded user-visible CLI display bugfix for task output, with limited blast radius and no emergency impact.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦞 diamond lobster.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Only test/mock-runtime evidence is present; before merge, the contributor should add redacted real terminal output, a terminal screenshot, copied live output, or logs from openclaw tasks list showing the fixed emoji-boundary behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P2: This is a bounded user-visible CLI display bugfix for task output, with limited blast radius and no emergency impact.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦞 diamond lobster.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Only test/mock-runtime evidence is present; before merge, the contributor should add redacted real terminal output, a terminal screenshot, copied live output, or logs from openclaw tasks list showing the fixed emoji-boundary behavior. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source -2, Tests +25. Total +23 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 2 4 -2
Tests 1 25 0 +25
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 27 4 +23

What I checked:

  • Repository policy read: Root AGENTS.md was read fully and applied for PR review depth, best-fix assessment, no-mutation constraints, sibling-surface checks, and real-behavior proof gating. (AGENTS.md:17, bd7da9decd7e)
  • Current-main bug path: Current main's task command truncator uses raw value.slice(...), and task list/audit cells call that helper for human-readable terminal columns. (src/commands/tasks.ts:204, bd7da9decd7e)
  • Patch uses canonical helper: The PR replaces raw task truncation with truncateUtf16Safe(value, maxChars - 1) while preserving the existing ellipsis and code-unit cap shape. (src/commands/tasks.ts:206, e1386490afad)
  • Regression coverage added: The PR adds a task list regression case where terminalSummary places an emoji across the 80-code-unit boundary and asserts the rendered row contains the safe bounded summary. (src/commands/tasks.test.ts:602, e1386490afad)
  • Helper contract checked: truncateUtf16Safe floors the limit, clamps it to zero, and delegates to sliceUtf16Safe, which avoids returning dangling surrogate halves. (packages/normalization-core/src/utf16-slice.ts:44, bd7da9decd7e)
  • Related cleanup family: GitHub search found this PR split from a closed broad CLI PR and adjacent narrow PRs for commitments and gateway status, while similar UTF-16 truncation fixes already merged for session cost usage, status report lines, and Control UI asset diagnostics.

Likely related people:

  • steipete: Authored the cleanup commit on this branch and recent maintainer follow-up commits on adjacent UTF-16 truncation hardening PRs that switched to the canonical helper and added focused tests. (role: recent area contributor and reviewer; confidence: high; commits: e1386490afad, a5e887e4925a, a515316e799e; files: src/commands/tasks.ts, src/commands/tasks.test.ts, src/commands/status-all/report-lines.ts)
  • maweibin: Authored the first commit in this PR and a merged adjacent UTF-16 truncation fix, plus open sibling PRs for the same raw-slice terminal-output pattern. (role: recent truncation-fix contributor; confidence: medium; commits: 2a224025527b, 9319cfb2deb9, 1aedead2ca75; files: src/commands/tasks.ts, src/infra/session-cost-usage.ts, src/commands/commitments.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 7, 2026
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Land-ready at exact head e1386490afad956aa26e9393f78ac2ade372afa9.

Maintainer review replaced code-point truncation with the canonical UTF-16-safe helper, preserving every existing task table width. Cleanup also removed the separate raw-slice edge branch, reducing production code, and added a regression that creates a completed task, runs tasksListCommand, and verifies the actual 80-code-unit summary cell.

Validation:

  • canonical formatting and git diff --check
  • fresh Codex autoreview: clean, correctness confidence 0.86
  • exact-head hosted CI run 28863166154: attempt 2 success; 68 passing PR checks, no failures or pending checks
  • attempt 1's only failure was an unrelated 150-second timeout in src/tui/tui-pty-local.e2e.test.ts; the exact-head failed shard passed on rerun
  • scripts/pr review-validate-artifacts 101600
  • OPENCLAW_TESTBOX=1 scripts/pr prepare-run 101600

The PR body was corrected to describe the shared task-table surface and final implementation. No known proof gaps.

@steipete
steipete merged commit 4d5cd05 into openclaw:main Jul 7, 2026
190 of 195 checks passed
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 8, 2026
…t truncation (openclaw#101600)

* fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation

.slice() counts UTF-16 code units; use Array.from() to count full
code points instead, preventing lone surrogates in task list output.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(tasks): preserve terminal column width

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
…t truncation (openclaw#101600)

* fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation

.slice() counts UTF-16 code units; use Array.from() to count full
code points instead, preventing lone surrogates in task list output.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* fix(tasks): preserve terminal column width

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants