Skip to content

fix(matrix): truncate reply context on code-point boundaries#97471

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
ly-wang19:fix/matrix-reply-context-surrogate-safe
Jun 28, 2026
Merged

fix(matrix): truncate reply context on code-point boundaries#97471
vincentkoc merged 1 commit into
openclaw:mainfrom
ly-wang19:fix/matrix-reply-context-surrogate-safe

Conversation

@ly-wang19

Copy link
Copy Markdown
Contributor

What Problem This Solves

Matrix reply context truncation used raw UTF-16 slicing. If an emoji or other astral character straddled the reply-context limit, the quoted context could contain a dangling surrogate half before the ellipsis.

Why This Change Was Made

The Matrix reply-context truncation now uses the existing plugin SDK sliceUtf16Safe helper, preserving the existing 500-code-unit cap while avoiding malformed text at the truncation boundary.

User Impact

Matrix reply context shown to agents no longer includes replacement characters or malformed text when long messages with emoji are truncated.

Evidence

  • git diff --check origin/main...HEAD
  • node scripts/run-vitest.mjs extensions/matrix/src/matrix/monitor/reply-context.test.ts
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Autoreview result: clean; no accepted/actionable findings.

After-fix Proof

Boundary probe on this PR branch:

{
  "usesSafeSlice": true,
  "inputLength": 501,
  "outputLength": 499,
  "hasLoneSurrogate": false,
  "outputTail": "aaa..."
}

The probe places an emoji across the 497-code-unit slice boundary before the ellipsis. The after-fix output drops the emoji whole and leaves no lone UTF-16 surrogate.

@openclaw-barnacle openclaw-barnacle Bot added channel: matrix Channel integration: matrix size: XS labels Jun 28, 2026
@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 28, 2026, 1:57 PM ET / 17:57 UTC.

Summary
The PR changes Matrix reply-context truncation to use sliceUtf16Safe and adds a surrogate-boundary regression test.

PR surface: Source +1, Tests +24. Total +25 across 2 files.

Reproducibility: yes. with high-confidence source-level reproduction. Current main slices Matrix reply context at raw UTF-16 index 497, and a 496-character prefix plus an emoji leaves a dangling high surrogate before the ellipsis.

Review metrics: none identified.

Root-cause cluster
Relationship: canonical
Canonical: #97471
Summary: This PR is the live candidate for the still-missing Matrix reply-context surrogate-safe truncation fix; the merged thread-context PR is an adjacent sibling surface, not a replacement.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

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

Rank-up moves:

  • none.

Next step before merge

  • No repair job is needed; the PR can proceed through normal required checks and maintainer review.

Security
Cleared: The diff imports an existing SDK text helper and adds focused tests; it does not touch dependencies, workflows, secrets, package resolution, or code-execution surfaces.

Review details

Best possible solution:

Land the focused reply-context safe-slice change after required checks pass; keep the merged thread-context fix as the sibling precedent.

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

Yes, with high-confidence source-level reproduction. Current main slices Matrix reply context at raw UTF-16 index 497, and a 496-character prefix plus an emoji leaves a dangling high surrogate before the ellipsis.

Is this the best way to solve the issue?

Yes. Reusing the existing plugin SDK sliceUtf16Safe helper at the current truncation boundary is the narrow owner-boundary fix and matches the sibling Matrix thread-context implementation.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This fixes a bounded Matrix channel text-rendering bug with limited blast radius and focused regression coverage.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix live output from a boundary probe that exercises the pure Matrix reply summary truncation and shows safe slicing with no lone surrogate.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix live output from a boundary probe that exercises the pure Matrix reply summary truncation and shows safe slicing with no lone surrogate.
Evidence reviewed

PR surface:

Source +1, Tests +24. Total +25 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 2 1 +1
Tests 1 24 0 +24
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 26 1 +25

What I checked:

Likely related people:

  • alberthild: PR history for reply-context.ts shows the Matrix quoted-message reply context resolver was introduced in merged PR 55056. (role: introduced reply-context behavior; confidence: high; commits: c7fbd518909a; files: extensions/matrix/src/matrix/monitor/reply-context.ts)
  • gumadeiras: CONTRIBUTING.md lists Matrix under this person, and merged PR 55056 includes their review/merge and follow-up commits in the reply-context path. (role: listed Matrix area owner and merger; confidence: high; commits: c7fbd518909a, 94693f7ff036; files: extensions/matrix/src/matrix/monitor/reply-context.ts, extensions/matrix/src/matrix/monitor/thread-context.ts)
  • Bartok9: Authored the merged Matrix thread-starter surrogate-safe truncation fix that uses the same helper and invariant. (role: adjacent fix author; confidence: medium; commits: 289865b39226; files: extensions/matrix/src/matrix/monitor/thread-context.ts, extensions/matrix/src/matrix/monitor/thread-context.test.ts)
  • steipete: Recent GitHub commit history shows repeated Matrix monitor and channel-context refactor work near the affected runtime path. (role: recent Matrix monitor contributor; confidence: medium; commits: d47055aa92e5, efd1a9ace6f5, 25149801189f; files: extensions/matrix/src/matrix/monitor/reply-context.ts, extensions/matrix/src/matrix/monitor/handler.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 proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. labels Jun 28, 2026
@vincentkoc
vincentkoc force-pushed the fix/matrix-reply-context-surrogate-safe branch from 30b05f6 to 54a9e28 Compare June 28, 2026 17:44
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 28, 2026
truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vincentkoc
vincentkoc force-pushed the fix/matrix-reply-context-surrogate-safe branch from 54a9e28 to 0a83b7a Compare June 28, 2026 17:49
@ly-wang19

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Branch rebased onto current main (0a83b7a4) — duplicate thread-context half dropped. PR diff now contains only the reply-context source + test changes. mergeable: MERGEABLE. Requesting fresh review on the new SHA.

@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 28, 2026
@vincentkoc
vincentkoc merged commit 55d7b5b into openclaw:main Jun 28, 2026
107 of 111 checks passed
@vincentkoc

Copy link
Copy Markdown
Member

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 29, 2026
…w#97471)

truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
…w#97471)

truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
…w#97471)

truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 2, 2026
…w#97471)

truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
(cherry picked from commit 55d7b5b)
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
…w#97471)

truncateReplyBody used a raw value.slice(0, MAX_REPLY_BODY_LENGTH - 3)
to shorten long reply bodies. When the cut index fell between the two
UTF-16 code units of a surrogate pair (e.g. an emoji), the slice left a
lone high surrogate before the ellipsis, which renders as a broken glyph
in the reply context shown to the agent.

Replace the raw slice with sliceUtf16Safe from the plugin SDK so the
truncation never cuts inside a surrogate pair. A normal (non-astral)
body is unaffected.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: matrix Channel integration: matrix P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants