Skip to content

fix: render text formatting on the RPC send bridge path#143

Merged
steipete merged 2 commits into
openclaw:mainfrom
omarshahine:fix/rpc-send-text-formatting
Jun 30, 2026
Merged

fix: render text formatting on the RPC send bridge path#143
steipete merged 2 commits into
openclaw:mainfrom
omarshahine:fix/rpc-send-text-formatting

Conversation

@omarshahine

@omarshahine omarshahine commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Problem

handleSend (RPC method send) forwards text and file to the IMCore bridge but never the attributed-text format ranges. So direct/handle sends come through plain even on macOS 15+ — only send-rich (CLI) and the RPC sendRich handler honor formatting.

This bites the OpenClaw gateway: its message tool routes plain handle targets (to: +1…) through the send method and puts format ranges in a formatting param. imsg's send ignores it, so **bold**/*italic* notifications silently lose styling. Replies render correctly only because the gateway routes those through sendRich.

Fix

Thread the format ranges through handleSendsendViaBridge → the bridge sendMessage action, mirroring what sendRich already does. Accept formatting (the key OpenClaw's message tool emits) alongside text_formatting/textFormatting for parity with handleSendRich.

  • Bridge transport only; AppleScript sends stay plain (attributed text needs the private API).
  • No behavior change when no formatting is supplied.

Tests

  • rpcSendThreadsTextFormattingToBridge — asserts ranges from the formatting key reach invokeBridge as textFormatting.
  • rpcSendWithoutFormattingOmitsTextFormatting — asserts no textFormatting key when none is supplied.

All RPCServerBridge tests pass (7/7, no regressions).

End-to-end proof (real device, macOS 15+, IMCore bridge v2)

Same RPC send call — method send, bare formatting key, transport: bridge, same self-chat ([email protected]) — run through both binaries. Only the binary differs. Verified by reading back the attributedBody Messages persisted in chat.db and decoding the attribute runs.

Request (identical except the binary that serves it):

{"method":"send","params":{"to":"[email protected]","transport":"bridge",
 "text":"… bold test …","formatting":[{"start":0,"length":N,"styles":["bold"]}]}}

BEFORE — unfixed /opt/homebrew/bin/imsg (0.11.1):

RPC response: {"ok":true,"transport":"bridge","guid":"546F64D8-…"}
chat.db attributedBody runs:
  range 0..<30 "BEFORE bold test (brew 0.11.1)"  style=(none)   ← formatting dropped, plain

AFTER — this branch's release build (bin/imsg):

RPC response: {"ok":true,"transport":"bridge","guid":"58A3EE57-…"}
chat.db attributedBody runs:
  range 0..<5  "AFTER"                    style=__kIMTextBoldAttributeName  ← bold applied
  range 5..<28 " bold test (branch fix)"  style=(none)

The unfixed send persists no bold attribute; the fixed send persists __kIMTextBoldAttributeName over exactly the requested range. send-rich already produced this; the fix brings the plain send path (the one OpenClaw's message tool uses for to: handle targets) to parity. (Lobster is a headless Mac, so this is decoded persisted styling rather than a UI screenshot.)

handleSend forwarded text and file to the IMCore bridge but never the
attributed-text format ranges, so direct/handle sends came through plain
even on macOS 15+ — only `send-rich` (and the RPC `sendRich` handler)
honored formatting. The OpenClaw gateway's `message` tool uses the plain
`send` method for handle targets, so its bold/italic notifications
silently lost styling.

Thread the format ranges through handleSend -> sendViaBridge -> the
bridge sendMessage action, accepting `formatting` (the key OpenClaw
emits) alongside `text_formatting`/`textFormatting` for parity with
`sendRich`. Bridge transport only; AppleScript sends stay plain.

Adds tests covering the threaded ranges and the no-formatting case.
@clawsweeper

clawsweeper Bot commented Jun 21, 2026

Copy link
Copy Markdown

Codex review: needs changes before merge. Reviewed June 23, 2026, 12:02 AM ET / 04:02 UTC.

Summary
The branch forwards optional formatting aliases from RPC send into the bridge textFormatting payload, adds two focused RPC bridge tests, and adds an unreleased changelog entry.

Reproducibility: yes. Source inspection shows current main drops formatting before the bridge call, and the PR body provides live before/after output from a real macOS bridge setup; I did not run a live Messages reproduction in this read-only review.

Review metrics: 3 noteworthy metrics.

  • Changed files: 3 modified. The diff is focused, but it mixes runtime code, regression tests, and release-owned changelog text.
  • Regression tests: 2 added. The tests cover both formatting forwarding and the no-formatting payload shape.
  • Release-owned files touched: 1 file. The changelog edit is the only concrete cleanup needed before maintainer merge consideration.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
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:

  • Remove the contributor-added CHANGELOG.md entry or get explicit maintainer approval to keep it.
  • [P2] Add or link full make lint and make test output before merge.

Risk before merge

  • [P1] The branch still edits CHANGELOG.md, which is release-owned in this workflow unless maintainers explicitly accept carrying the entry here.
  • [P1] The PR body reports focused RPC bridge tests passing, but it does not include full make lint and make test output requested by AGENTS.md.

Maintainer options:

  1. Decide the mitigation before merge
    Merge the bridge formatting forwarding and regression tests after removing or maintainer-accepting the changelog entry and confirming the normal lint/test gates.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • A narrow repair can remove the release-owned changelog entry; the runtime fix itself looks ready for maintainer review after standard validation.

Security
Cleared: The diff forwards an existing formatting payload through an existing bridge call and adds tests/changelog text; no dependency, secret, permission, workflow, or supply-chain concern was found.

Review findings

  • [P3] Drop the release-owned changelog entry — CHANGELOG.md:6
Review details

Best possible solution:

Merge the bridge formatting forwarding and regression tests after removing or maintainer-accepting the changelog entry and confirming the normal lint/test gates.

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

Yes. Source inspection shows current main drops formatting before the bridge call, and the PR body provides live before/after output from a real macOS bridge setup; I did not run a live Messages reproduction in this read-only review.

Is this the best way to solve the issue?

Yes. Reusing the existing bridge textFormatting contract is the narrow maintainable fix; the changelog entry should be removed or explicitly accepted by maintainers.

Full review comments:

  • [P3] Drop the release-owned changelog entry — CHANGELOG.md:6
    CHANGELOG.md is release-owned in this workflow, so this contributor PR should keep release-note context in the PR body or commit history unless a maintainer explicitly wants the changelog line carried here.
    Confidence: 0.91

Overall correctness: patch is correct
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This fixes a bounded RPC bridge formatting bug for direct handle sends with limited blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster 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 updated PR body includes copied before/after live output from a real macOS 15+ bridge setup and decoded chat.db attributedBody runs showing formatting after the fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes copied before/after live output from a real macOS 15+ bridge setup and decoded chat.db attributedBody runs showing formatting after the fix.
Evidence reviewed

Acceptance criteria:

  • [P1] git diff --check.
  • [P1] make lint.
  • [P1] make test.

What I checked:

Likely related people:

  • omarshahine: Prior merged history introduced and maintained the IMCore bridge and rich text-formatting command surface that this PR extends to plain RPC send. (role: bridge text-formatting feature contributor; confidence: high; commits: c56c24d488ef, 388c72d6fac4; files: Sources/IMsgHelper/IMsgInjected.m, Sources/imsg/Commands/BridgeMessagingCommands.swift, Tests/imsgTests/BridgeRichCommandTests.swift)
  • steipete: Current blame for handleSend and sendViaBridge points to the v0.11.1 snapshot, and prior merged commits exposed bridge RPC methods and send response behavior in the affected area. (role: recent RPC bridge area contributor; confidence: high; commits: f97a116dc8df, b71eb8c0f6f1, 6300486b7f75; files: Sources/imsg/RPCServer+Handlers.swift, Sources/imsg/RPCServer+BridgeMessageHandlers.swift, Tests/imsgTests/RPCServerBridgeTests.swift)
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 priority bug or improvement with limited blast radius. labels Jun 21, 2026
@omarshahine

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review with updated proof

@clawsweeper

clawsweeper Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦞🧹
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 proof: sufficient Contributor real behavior proof is sufficient. 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: 🦪 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. labels Jun 23, 2026
@steipete

Copy link
Copy Markdown
Collaborator

Maintainer validation complete on macOS with Swift 6.4:

  • git diff --check
  • make lint (passes; existing non-serious warnings only)
  • make test (all Swift Testing suites pass, including the two new RPC formatting regressions)
  • GitHub CI: macos and linux-read-core both green

The contributor supplied live before/after bridge proof in the PR body. I am explicitly accepting the focused unreleased changelog entry here because the fix is user-visible and the maintainer workflow requires it; it also preserves contributor credit.

@steipete
steipete merged commit 6a20d3a into openclaw:main Jun 30, 2026
2 checks passed
@omarshahine
omarshahine deleted the fix/rpc-send-text-formatting branch July 18, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants