Skip to content

fix(chat): multi-line composer messages sent with stray carriage returns - #1682

Merged
shanselman merged 3 commits into
mainfrom
fix/chat-enter-send-line-endings
Oct 15, 2026
Merged

shanselman merged 3 commits into
mainfrom
fix/chat-enter-send-line-endings

Conversation

@larroy

@larroy larroy commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

What Problem This Solves

Fixes: multi-line messages can leave the native composer with carriage returns and reach the Gateway without standard line-feed separators.

User Impact

User impact: line breaks typed in the native chat composer are sent as standard \n separators. Single-line messages and draft behavior remain unchanged.

Why This Change Was Made

WinUI TextBox drafts can contain lone \r characters. Normalize CRLF and lone CR once at the send boundary, preserving the native draft and every other Unicode character.

The maintainer update integrates current main after #1667 and removes a redundant foreground SendKeys test that was not behind the repository's required opt-in NativeKeyboard lane. The exact outgoing message remains covered at the runtime-port boundary, while current main retains the separate gated native keyboard proof.

Evidence

SendAsync_TextBoxLineBreaks_AreSentAsLineFeeds supplies a draft containing both lone CR and CRLF and verifies that the runtime port receives exactly:

line one\nline two\nline three

Two independent reviews found no production defect in the final two-file PR delta. The final integration with latest main and the clean-worktree build fix passed the complete local closeout.

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs or instructions
  • Tests or validation
  • Security hardening
  • Chore or infrastructure

Scope

  • Tray or WinUI UX
  • Windows node capability
  • Local MCP or winnode
  • Gateway, connection, or pairing
  • Setup or onboarding
  • Permissions, privacy, or security
  • Tests, CI, or docs

Required proof pools

  • none: the changed contract is the exact string passed from the composer controller to the runtime port. Foreground key injection is unchanged and remains covered by the existing opt-in NativeKeyboard lane.

Validation

Validation tree: PR head 2750135c3ef72cbe8bdc4cce465f42d786449e95, latest main 998e6b17f30ef545f0cc11f4392bad26216bc460, and #1727's clean-build fix. The two PR-changed files are byte-identical to the published PR head.

Command Result
.\build.ps1 Passed on native ARM64: all five build targets
dotnet test .\tests\OpenClaw.Shared.Tests\OpenClaw.Shared.Tests.csproj Passed: 4,310 passed, 37 skipped, 0 failed
dotnet test .\tests\OpenClaw.Tray.Tests\OpenClaw.Tray.Tests.csproj Passed: 4,010 passed, 0 skipped, 0 failed

The test commands omitted --no-restore because the proof used a new worktree with no test assets.

Real Behavior Proof

  • Environment tested: Windows 11 ARM64, .NET SDK 10.0.401, isolated integration worktree.
  • PR head or commit tested: PR files from 2750135c3ef72cbe8bdc4cce465f42d786449e95; full validation tree fc73630fc13a0ef73a8e2e9233ffe2ebf3c4090f.
  • Exact steps or command run: full build plus Shared and Tray suites; the Tray suite includes SendAsync_TextBoxLineBreaks_AreSentAsLineFeeds.
  • Evidence after fix: the fake runtime port received only LF separators for a mixed CR/CRLF draft.
  • Observed result: outgoing composer text is normalized without mutating the draft or interfering with fix(chat): a finished send erases text typed while it was in flight #1667's revision-based clear.
  • Screenshot or artifact links verified? (Yes/No/N/A): N/A, no visual output changes.
  • Not verified or blocked: no new live Gateway wire capture was collected. The contributor also reported manual Windows testing; the deterministic runtime-port assertion is the authoritative changed-output proof.

Security Impact

  • New permissions or capabilities? (Yes/No): No
  • Secrets or tokens handling changed? (Yes/No): No
  • New or changed network calls? (Yes/No): No
  • Command or tool execution surface changed? (Yes/No): No
  • Data access scope changed? (Yes/No): No
  • If any answer is Yes, explain the risk and mitigation: N/A.

Compatibility and Migration

  • Backward compatible? (Yes/No): Yes
  • Config or environment changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, list the exact upgrade steps: N/A.

Review Conversations

  • I replied to or resolved every bot review conversation addressed by this PR.
  • I left unresolved only conversations that still need maintainer judgment.

The multiline composer (Enter sends, Shift+Enter newline) stores WinUI
TextBox line breaks as carriage returns. SendAsync forwarded that raw
format, so multi-line messages left the tray as lone CR bytes.

Normalize CR and CRLF to LF at send time, keeping other Unicode line
separators in user content untouched. Add a controller unit test and a
real-process keyboard proof that Enter sends and Shift+Enter inserts a
newline without sending.
@clawsweeper

clawsweeper Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@larroy larroy added the status: 🚢 actively landing A maintainer or agent is actively driving this item through implementation, validation, or merge. label Oct 8, 2026
@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Oct 8, 2026
@clawsweeper

clawsweeper Bot commented Oct 8, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed October 8, 2026, 1:58 PM ET / 17:58 UTC.

ClawSweeper review

What this changes

The branch converts carriage-return and Windows-style line breaks to line feeds when sending composer messages and adds controller and native keyboard tests.

Example: The user sends a draft containing three lines.

  • Before: The outgoing text remains "line one\rline two\r\nline three".
  • After: The outgoing text becomes "line one\nline two\nline three".

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The repair is focused and correct, with useful unit coverage but no end-to-end observation of the changed outgoing text.
Proof confidence 🦐 gold shrimp (3/6) Not applicable: The collaborator-authored PR is exempt from the external-contributor proof gate. Its reported current-head Windows app run demonstrates Shift+Enter insertion and Enter clearing, while exact normalized output is proven only at the fake runtime port. No stored-data contract changes.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: Messages typed on multiple lines can arrive with carriage returns instead of usable line breaks.
Reason: Preserving typed line breaks completes the established multiline composer behavior with one focused production change.

Merge readiness

✅ Ready for maintainer review

This PR addresses a defect still present on current main. The focused normalization is correct; exact outgoing line endings have unit coverage, while the reported native-app run proves adjacent keyboard behavior.

Priority: P2
Reviewed head: 650c6e4da8b96d404f191949f9f0ceb9348f317c

Before merge

None.

Findings

None.

Tests

  • Missing end-to-end proof: The reported real-process keyboard run does not inspect submitted text: its synthetic provider completes without recording the message. Exact outgoing LF content is checked only by the unit test; the UI assertions would also pass on the base.
Agent review details

How this fits together

The native chat composer holds the user's draft and submits it through a shared controller. The chat provider queues that text and the gateway client sends it in a chat request.

flowchart LR
 A[Native chat input] --> B[Composer draft]
 B --> C[Send controller]
 C --> D[Normalize carriage returns]
 D --> E[Chat provider and queue]
 E --> F[Gateway chat request]
Loading

Technical review

Best possible solution:

Keep normalization at the composer submission boundary, preserving native draft text and existing send, queue and attachment ownership.

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

Yes, source establishes that a draft containing internal carriage returns reaches the outgoing message unchanged on main. No failing runtime run was performed during this read-only review.

Is this the best way to solve the issue?

Yes, converting CRLF and lone CR at submission is a narrow repair in the existing owner and avoids rewriting the controlled draft or caret.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 037c17dcb581.

Provenance checked

  • src/OpenClaw.Tray.WinUI/Chat/ChatComposerController.cs:SendAsync keeps the original intent (refactor(chat): extract composer ownership #1098: Extract composer workflows into a focused controller while preserving host-local drafts, accepted-send clearing and lifecycle fencing.)

Testing

Proof path: unit tests only. Added test files: 2.

Security

None.

Evidence

What I checked:

Likely related people:

  • Barbara Kudiess: Raw commit 686afdc adds src/OpenClaw.Tray.WinUI/Chat/ChatComposerController.cs:156 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 686afdc8d4a2; files: src/OpenClaw.Tray.WinUI/Chat/ChatComposerController.cs)
  • RomneyDa: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Review metrics

Metric Value Why it matters
Production versus test growth production +1/-1 lines; tests +85 lines Production remains a single focused replacement; most growth supplies regression and native-input coverage.

Labels

Label changes:

  • add P2: This repairs multiline chat formatting without changing connection, delivery admission or persisted settings.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Label justifications:

  • P2: This repairs multiline chat formatting without changing connection, delivery admission or persisted settings.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Capture the exact submitted message from the real composer path and verify LF separators without normalizing the observed output.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only; repair and merge need explicit maintainer commands such as @clawsweeper autofix or @clawsweeper automerge.

@karkarl

karkarl commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator

Global triage: NEEDS_HUMAN_TEST. Take confidence 55%; recommendation confidence 97%; effort small; risk medium.

Reviewed exact head 650c6e4da8b9. GitHub currently reports CLEAN with 28 passing, 0 failing, and 0 pending checks. proof pools are declared. Durable automated signal: Codex review: needs maintainer review before merge..

Maintainer or proof-pool owner: run every declared Windows proof pool at this exact head and replace blocked or historical evidence with current-head results.

This is a triage classification, not a merge action. No branch changes were made.

@larroy

larroy commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator Author

I tested myself by hand as well.

shanselman and others added 2 commits October 14, 2026 18:23
Integrate the multiline composer normalization after the queued-draft preservation fix.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a7c0b5c5-d1fc-468d-a9b1-e58c402f13e8
Keep the line-ending regression at the send boundary and rely on the existing opt-in NativeKeyboard coverage for foreground key injection.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a7c0b5c5-d1fc-468d-a9b1-e58c402f13e8
@shanselman
shanselman merged commit d5106cc into main Oct 15, 2026
26 checks passed
@shanselman
shanselman deleted the fix/chat-enter-send-line-endings branch October 15, 2026 02:21
@shanselman shanselman removed the status: 🚢 actively landing A maintainer or agent is actively driving this item through implementation, validation, or merge. label Oct 15, 2026
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. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. 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.

3 participants