Skip to content

fix(gateway): keep session title and preview text truncation UTF-16 safe#102090

Merged
steipete merged 2 commits into
openclaw:mainfrom
wm0018:fix/session-title-preview-utf16-safe-truncation
Jul 9, 2026
Merged

fix(gateway): keep session title and preview text truncation UTF-16 safe#102090
steipete merged 2 commits into
openclaw:mainfrom
wm0018:fix/session-title-preview-utf16-safe-truncation

Conversation

@wm0018

@wm0018 wm0018 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes two UTF-16 truncation sites in gateway session utilities that could produce broken U+FFFD replacement characters:

  1. truncateTitle (src/gateway/session-utils.ts) — truncates user messages at 59 code units when deriving session titles via deriveSessionTitle. Emoji at the boundary produces a broken character visible in session lists and the /name command.

  2. truncatePreviewText (src/gateway/session-utils.fs.ts) — truncates transcript message text at maxChars-3 code units for session preview items served via the sessions API.

Why This Change Was Made

  • truncateTitle: Replace text.slice(0, maxLen-1) with truncateUtf16Safe — the subsequent word-boundary logic operates on the already-safe string.
  • truncatePreviewText: Replace text.slice() calls with truncateUtf16Safe.

User Impact

Session titles derived from user messages and session preview items no longer contain broken characters at truncation boundaries.

Evidence

Tests

$ node scripts/run-vitest.mjs src/gateway/session-title-truncate-utf16.test.ts src/gateway/session-preview-truncate-utf16.test.ts src/gateway/session-utils.test.ts

 Test Files  6 passed (6)
     Tests  500 passed (500)

Production-path proof

$ node --import tsx --input-type=module -e '
import { deriveSessionTitle } from "./src/gateway/session-utils.js";
const msg = "t".repeat(58) + "🚀 extra";
console.log("slice(0,59) surrogate:", msg.slice(0,59).charCodeAt(58).toString(16));
const title = deriveSessionTitle({ sessionId: "p", updatedAt: Date.now() }, msg);
const ok = !(new TextDecoder().decode(new TextEncoder().encode(title)).includes("�"));
console.log("deriveSessionTitle has U+FFFD:", !ok, " result:", ok ? "PASS" : "FAIL");
'
slice(0,59) surrogate: d83d
deriveSessionTitle has U+FFFD: false  result: PASS

$ node --import tsx --input-type=module -e '
import { truncateUtf16Safe } from "./src/utils.js";
const text = "t".repeat(196) + "🚀xyz";
console.log("slice(0,197) surrogate:", text.slice(0,197).charCodeAt(196).toString(16));
const safe = truncateUtf16Safe(text, 197);
const ok = !(new TextDecoder().decode(new TextEncoder().encode(safe)).includes("�"));
console.log("truncateUtf16Safe has U+FFFD:", !ok, " result:", ok ? "PASS" : "FAIL");
'
slice(0,197) surrogate: d83d
truncateUtf16Safe has U+FFFD: false  result: PASS

Files Changed (4 files, +94/-3)

 src/gateway/session-utils.ts                       |  3 +-
 src/gateway/session-utils.fs.ts                    |  7 +-
 src/gateway/session-title-truncate-utf16.test.ts   | 49 +++++++
 src/gateway/session-preview-truncate-utf16.test.ts | 36 +++++

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S labels Jul 8, 2026
@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 8, 2026, 11:48 PM ET / 03:48 UTC.

Summary
The PR changes gateway session title and transcript preview truncation to use truncateUtf16Safe and adds in-place regression tests for emoji-boundary truncation.

PR surface: Source 0, Tests +19. Total +19 across 4 files.

Reproducibility: yes. Source inspection shows current main cuts both title and preview text with raw UTF-16 slice, and the PR body demonstrates the title boundary can leave a dangling surrogate.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: src/gateway/session-utils.fs.test.ts, serialized state: src/gateway/session-utils.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until stronger real behavior proof 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 terminal/live output that drives readSessionPreviewItemsFromTranscript or sessions.preview with an emoji at the truncation boundary.
  • Refresh or re-review after proof so the exact-head checks can gate the merge result.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR has terminal proof for the title path, but the preview proof still calls the helper directly; add redacted terminal/live output for readSessionPreviewItemsFromTranscript or sessions.preview, then update the PR body or ask for @clawsweeper re-review.

Risk before merge

  • [P1] The PR body still does not show the transcript preview path after the fix; it only proves the shared helper directly for the preview half.
  • [P1] The PR is mergeable but behind current main and had some CI shards still running during inspection, so exact-head gates still need normal completion before landing.

Maintainer options:

  1. Decide the mitigation before merge
    Land the narrow helper reuse after the contributor adds preview-path terminal or live output and exact-head checks finish; no broader gateway API change is needed.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Contributor action is needed for redacted preview-path real behavior proof; there is no narrow automated code repair left after the in-place preview regression landed.

Security
Cleared: Cleared: the diff changes only gateway truncation calls and tests, with no dependency, workflow, credential, packaging, or code-execution surface change.

Review details

Best possible solution:

Land the narrow helper reuse after the contributor adds preview-path terminal or live output and exact-head checks finish; no broader gateway API change is needed.

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

Yes. Source inspection shows current main cuts both title and preview text with raw UTF-16 slice, and the PR body demonstrates the title boundary can leave a dangling surrogate.

Is this the best way to solve the issue?

Yes for the code shape: reusing the existing surrogate-safe truncation helper at both raw truncation sites is the narrow maintainable fix. Merge readiness is still blocked by missing real preview-path proof, not by a code finding.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a focused gateway text-rendering bug fix with limited blast radius, but it still needs real preview-path proof before merge.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR has terminal proof for the title path, but the preview proof still calls the helper directly; add redacted terminal/live output for readSessionPreviewItemsFromTranscript or sessions.preview, then update the PR body or ask for @clawsweeper re-review.
Evidence reviewed

PR surface:

Source 0, Tests +19. Total +19 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 6 6 0
Tests 2 19 0 +19
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 25 6 +19

What I checked:

Likely related people:

  • amknight: Current-main blame for both affected raw-slice sites points to bd4d4c0d3170dd85f58593518af26240dc8c0571; the shallow local history makes deeper authorship ambiguous, so this is a routing clue rather than blame. (role: current-line provenance; confidence: medium; commits: bd4d4c0d3170; files: src/gateway/session-utils.ts, src/gateway/session-utils.fs.ts)
  • steipete: Live PR metadata shows steipete merged several recent surrogate-safe truncation fixes and authored this PR's in-place test update commit, making him relevant to the helper direction and proof expectations. (role: recent related UTF-16 truncation merger and test contributor; confidence: medium; commits: dc013e988c83, b62c796e51d9, 7daa0909357e; files: src/gateway/session-utils.fs.test.ts, src/gateway/session-utils.test.ts, packages/normalization-core/src/utf16-slice.ts)
  • vincentkoc: The live PR timeline shows vincentkoc assigned on July 8, 2026, shortly after the first ClawSweeper review, so he is the visible routing owner for this gateway PR. (role: assigned reviewer; confidence: medium; files: src/gateway/session-utils.ts, src/gateway/session-utils.fs.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.
Review history (3 earlier review cycles)
  • reviewed 2026-07-08T07:24:18.057Z sha 64be49c :: needs real behavior proof before merge. :: [P2] Cover the real preview split boundary
  • reviewed 2026-07-08T07:44:48.268Z sha 934dbdb :: needs real behavior proof before merge. :: [P2] Drive the preview test through the preview path
  • reviewed 2026-07-08T07:50:51.094Z sha 934dbdb :: needs real behavior proof before merge. :: [P2] Drive preview coverage through the preview path

@vincentkoc vincentkoc self-assigned this Jul 8, 2026
@wm0018
wm0018 force-pushed the fix/session-title-preview-utf16-safe-truncation branch from 64be49c to a772186 Compare July 8, 2026 07:37
@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Jul 8, 2026
@wm0018
wm0018 force-pushed the fix/session-title-preview-utf16-safe-truncation branch from a772186 to 934dbdb Compare July 8, 2026 07:38
@openclaw-barnacle openclaw-barnacle Bot removed the scripts Repository scripts label Jul 8, 2026
@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 8, 2026
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Land-ready review complete on exact head dc013e988c83d150b16c62c854244c0ef4036509.

  • Reviewed the session-list and transcript-preview projections, their gateway callers, the public RPC budget clamp, the shared helper contract, and sibling session-title code.
  • Maintainer fixup removed two helper-only test files, moved exact assertions into the existing production-path suites, and deleted unreachable small-budget branches under the public clamp invariant.
  • git diff --check, targeted formatting, and focused gateway tests passed.
  • Fresh autoreview is clean at confidence 0.98.
  • Exact-head hosted CI run 28992436034 passed.

No remaining findings or proof gaps.

@steipete
steipete merged commit 9614129 into openclaw:main Jul 9, 2026
106 of 108 checks passed
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 9, 2026
…afe (openclaw#102090)

* fix(gateway): keep session title and preview text truncation UTF-16 safe

* test(gateway): cover session UTF-16 boundaries in place

---------

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

gateway Gateway runtime 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.

3 participants