Skip to content

fix(note): prevent clack from re-breaking copy-sensitive tokens#94746

Merged
clawsweeper[bot] merged 2 commits into
openclaw:mainfrom
xzh-icenter:fix/issue-94730-clean
Jun 19, 2026
Merged

fix(note): prevent clack from re-breaking copy-sensitive tokens#94746
clawsweeper[bot] merged 2 commits into
openclaw:mainfrom
xzh-icenter:fix/issue-94730-clean

Conversation

@xzh-icenter

Copy link
Copy Markdown
Contributor

Fixes #94730

Summary

  • Problem: openclaw doctor mangles long file paths inside note(...) boxes. wrapNoteMessage correctly preserves copy-sensitive tokens as unbroken lines, but clack's note() applies its own internal wrap after our format callback, re-breaking the line at the terminal width.
  • Why now: This affects every user running openclaw doctor in a terminal narrower than the longest path/URL in the output. The mangled path cannot be copy-pasted, defeating the purpose of surfacing it.
  • What changed: Use a wide virtual stream (columns = max(80, longestLine + 12)) in the note() function so clack's internal wrap never triggers, letting wrapNoteMessage's output pass through to the terminal intact.
  • What did NOT change: No changes to wrapNoteMessage, wrapLine, isCopySensitiveToken, or splitLongWord. The fix is in the note() function only.
  • Outcome: Long paths and URLs in note() boxes are now displayed as a single line that can be copy-pasted, regardless of terminal width.
  • Reviewer focus: The 5-line change in packages/terminal-core/src/note.ts lines 209-215.

Verification

# Reproduce the bug: clack re-breaks copy-sensitive tokens
node -e "
const { note } = require('@clack/prompts');
const longPath = '~/.openclaw/agents/main/sessions/9c2acae5-841f-4aea-936b-fdb513b60202.jsonl.lock';
const message = 'Session lock: ' + longPath;
const output = [];
note(message, 'Test', {
  output: { columns: 80, write: (c) => { output.push(c); return true; }, isTTY: true },
  format: (line) => line,
});
const result = output.join('');
console.log('Path intact:', result.includes(longPath));
"

Real behavior proof

Behavior addressed: Long file paths in openclaw doctor note boxes are split across multiple lines, making them impossible to copy-paste.

Environment tested: Linux 6.18.33.1-microsoft-standard-WSL2 (x64), Node.js v24.16.0.

Steps run after the patch:

cd /mnt/g/code/openclaw-worktrees/issue-94730

# Test 1: Old behavior (80 col) - path gets broken
node -e "
const { note } = require('@clack/prompts');
const longPath = '~/.openclaw/agents/main/sessions/9c2acae5-841f-4aea-936b-fdb513b60202.jsonl.lock';
const message = 'Session lock: ' + longPath;
const output1 = [];
note(message, 'Old (80 col)', {
  output: { columns: 80, write: (c) => { output1.push(c); return true; }, isTTY: true },
  format: (line) => line,
});
const output2 = [];
const wideColumns = Math.max(80, message.length + 12);
note(message, 'New (wide)', {
  output: { columns: wideColumns, write: (c) => { output2.push(c); return true; }, isTTY: true },
  format: (line) => line,
});
console.log('Old (80 col) - path intact:', output1.join('').includes(longPath));
console.log('New (wide)   - path intact:', output2.join('').includes(longPath));
"

Evidence after fix (console output):

Old (80 col) - path intact: false
New (wide)   - path intact: true

Observed result: With the old 80-column stream, clack's internal wrap breaks the path at character 74. With the wide virtual stream, the path remains intact as a single line.

Not tested: Live openclaw doctor run (requires full gateway setup). The fix only changes the virtual stream width passed to clack's note().

@openclaw-barnacle openclaw-barnacle Bot added size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: passed. Reviewed June 18, 2026, 11:58 PM ET / 03:58 UTC.

Summary
The PR widens the virtual Clack output columns for wrapped terminal notes and adds a rendered-output regression test for copy-sensitive session-lock paths.

PR surface: Source +8, Tests +28. Total +36 across 2 files.

Reproducibility: yes. Current source routes session lock paths through note(), and the pinned Clack note renderer hard-wraps final content from getColumns(output) - 6 after OpenClaw's first wrapping pass.

Review metrics: none identified.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #94730
Summary: This PR is a proof-positive implementation candidate for the canonical issue about Clack re-wrapping copy-sensitive doctor note paths.

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 ClawSweeper repair is needed; the maintainer-armed automerge lane can gate this exact head on checks and mergeability.

Security
Cleared: The diff is limited to terminal note width calculation and a Vitest regression test, with no dependency, workflow, secret, network, package-publishing, or install-script change.

Review details

Best possible solution:

Land this shared terminal-core fix or an equivalent narrow implementation, then close the linked issue and retire duplicate candidate PRs after merge.

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

Yes. Current source routes session lock paths through note(), and the pinned Clack note renderer hard-wraps final content from getColumns(output) - 6 after OpenClaw's first wrapping pass.

Is this the best way to solve the issue?

Yes. The shared terminal-core note helper is the right layer because many doctor/setup note surfaces use it, and widening the virtual Clack stream is narrower than replacing Clack's full box renderer for this bug.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a normal-priority CLI doctor rendering bug fix with a limited terminal-output blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 🚀 automerge armed: This PR is in ClawSweeper's automerge lane. Sufficient (terminal): The PR body includes copied terminal output from an after-patch script comparing old 80-column Clack behavior with widened output and showing the long path remains intact.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied terminal output from an after-patch script comparing old 80-column Clack behavior with widened output and showing the long path remains intact.
Evidence reviewed

PR surface:

Source +8, Tests +28. Total +36 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 10 2 +8
Tests 1 29 1 +28
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 39 3 +36

What I checked:

  • Current main still delegates final wrapping to Clack: Current main resolves terminal columns, wraps the note message once, then calls Clack note with output created at the same column width, so OpenClaw's copy-sensitive wrapping is not the final render pass. (packages/terminal-core/src/note.ts:210, 508e3bf41353)
  • Affected doctor caller reaches shared note helper: Session lock diagnostics format lock paths and emit the joined lines through the shared note helper, matching the reported openclaw doctor surface. (src/commands/doctor-session-locks.ts:97, 508e3bf41353)
  • Pinned dependency performs a hard final wrap: The pinned Clack note implementation calls wrapWithFormat(message, getColumns(output) - 6, format), and wrapWithFormat uses wrapAnsi with hard: true, so the virtual output columns control whether Clack re-breaks the line. (npm:@clack/[email protected] package/dist/index.mjs.map:26)
  • PR changes the shared terminal note handoff: The PR adds resolveNoteOutputColumns, computes the widest already-wrapped line, and passes a virtual output width of at least widestLine + 6 to Clack. (packages/terminal-core/src/note.ts:189, b17a4ff5711c)
  • Regression test covers final Clack output: The new test renders the wrapped message through Clack using the adjusted output columns and asserts the session lock path remains intact in the rendered output. (packages/terminal-core/src/table.test.ts:352, b17a4ff5711c)
  • Latest release still has the old call shape: Release v2026.6.8 still calls clackNote(wrapNoteMessage(...)) with the original note output columns, so the fix is not already shipped on the latest release. (packages/terminal-core/src/note.ts:210, 844f405ac1be)

Likely related people:

  • steipete: GitHub path history shows steipete moved terminal core into packages/terminal-core and previously shared terminal note wrapping behavior. (role: terminal-core package history contributor; confidence: medium; commits: de1dfab03ef0, f7f5c24786ad, 4df95d3c3fd6; files: packages/terminal-core/src/note.ts, packages/terminal-core/src/table.test.ts, src/terminal/note.ts)
  • vignesh07: History for the old terminal note path includes Vignesh Natarajan's copy-sensitive token wrapping work, which is the invariant this PR preserves through Clack rendering. (role: adjacent copy-sensitive wrapping contributor; confidence: medium; commits: 69418cca2091; files: src/terminal/note.ts)
  • Grynn: The stale session lock doctor diagnostic that surfaces these .jsonl.lock paths traces to Grynn's merged session-lock watchdog and doctor work. (role: session-lock feature contributor; confidence: medium; commits: e91a5b021648; files: src/commands/doctor-session-locks.ts)
  • amknight: Recent GitHub path history shows amknight touched the session-lock doctor surface while narrowing embedded session write-lock behavior. (role: recent session-lock contributor; confidence: low; commits: 8a060b2904d4; files: src/commands/doctor-session-locks.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: 🦐 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. P2 Normal backlog priority with limited blast radius. labels Jun 19, 2026
wrapNoteMessage correctly preserves copy-sensitive tokens (paths, URLs)
as unbroken lines, but clack's note() applies its own internal wrap
after our format callback, re-breaking the line at the terminal width.

Use a wide virtual stream (columns = max(80, longestLine + 12)) so
clack's internal wrap never triggers, letting wrapNoteMessage's output
pass through to the terminal intact.

Fixes openclaw#94730
…ken preservation

Add a focused test in table.test.ts that verifies clack's note() rendering
does not re-break copy-sensitive tokens (session lock paths) after
wrapNoteMessage preserves them. Uses resolveNoteOutputColumns to widen
the virtual output stream, matching the fix in note.ts.

Refs openclaw#94730
@xzh-icenter
xzh-icenter force-pushed the fix/issue-94730-clean branch from c408319 to b17a4ff Compare June 19, 2026 02:05
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 19, 2026
@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. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jun 19, 2026
@vincentkoc

Copy link
Copy Markdown
Member

/clownfish automerge

@vincentkoc vincentkoc added the clownfish:automerge Maintainer opted this Clownfish PR into bounded ClawSweeper-reviewed automerge label Jun 19, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Clownfish is on the reef for this PR. 🐠

I tagged clownfish:automerge and sent ClawSweeper over this exact head. If the sweep finds rough coral, failing checks, or needs-human, I will take another bounded repair lap and ask for a fresh review.

A maintainer can call /clownfish stop any time and I will drift this back to human review.

@vincentkoc

Copy link
Copy Markdown
Member

/clownfish automerge

@vincentkoc vincentkoc added the clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge label Jun 19, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Clownfish is on the reef for this PR. 🐠

I tagged clownfish:automerge and sent ClawSweeper over this exact head. If the sweep finds rough coral, failing checks, or needs-human, I will take another bounded repair lap and ask for a fresh review.

A maintainer can call /clownfish stop any time and I will drift this back to human review.

@clawsweeper clawsweeper Bot added status: 🚀 automerge armed This PR is in ClawSweeper's automerge lane. and removed status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🦞✅
ClawSweeper merged this PR after the passing review.

Source: clawsweeper[bot]
Feedback: structured ClawSweeper verdict: pass (sha=b17a4ff5711c1d7607ffbc06b7f4f13ff3373afe)
Merge status: merged by ClawSweeper automerge
Merged at: 2026-06-19T03:59:10Z
Merge commit: 650423790034

What merged:

  • The PR widens the virtual Clack output columns for wrapped terminal notes and adds a rendered-output regression test for copy-sensitive session-lock paths.
  • PR surface: Source +8, Tests +28. Total +36 across 2 files.
  • Reproducibility: yes. Current source routes session lock paths through note(), and the pinned Clack note renderer hard-wraps final content from getColumns(output) - 6 after OpenClaw's first wrapping pass.

Automerge notes:

  • PR branch already contained follow-up commit before automerge: test(note): add rendered-output regression test for copy-sensitive to…

The automerge loop is complete.

Automerge progress:

  • 2026-06-19 03:58:58 UTC review passed b17a4ff5711c (structured ClawSweeper verdict: pass (sha=b17a4ff5711c1d7607ffbc06b7f4f13ff3373...)
  • 2026-06-19 03:59:12 UTC merged b17a4ff5711c (merged by ClawSweeper automerge)

@clawsweeper
clawsweeper Bot merged commit 6504237 into openclaw:main Jun 19, 2026
198 of 204 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 19, 2026
…claw#94746)

Summary:
- The PR widens the virtual Clack output columns for wrapped terminal notes and adds a rendered-output regression test for copy-sensitive session-lock paths.
- PR surface: Source +8, Tests +28. Total +36 across 2 files.
- Reproducibility: yes. Current source routes session lock paths through `note()`, and the pinned Clack note renderer hard-wraps final content from `getColumns(output) - 6` after OpenClaw's first wrapping pass.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(note): add rendered-output regression test for copy-sensitive to…

Validation:
- ClawSweeper review passed for head b17a4ff.
- Required merge gates passed before the squash merge.

Prepared head SHA: b17a4ff
Review: openclaw#94746 (comment)

Co-authored-by: Dirk <[email protected]>
cxbAsDev pushed a commit to cxbAsDev/openclaw that referenced this pull request Jun 23, 2026
…claw#94746)

Summary:
- The PR widens the virtual Clack output columns for wrapped terminal notes and adds a rendered-output regression test for copy-sensitive session-lock paths.
- PR surface: Source +8, Tests +28. Total +36 across 2 files.
- Reproducibility: yes. Current source routes session lock paths through `note()`, and the pinned Clack note renderer hard-wraps final content from `getColumns(output) - 6` after OpenClaw's first wrapping pass.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(note): add rendered-output regression test for copy-sensitive to…

Validation:
- ClawSweeper review passed for head b17a4ff.
- Required merge gates passed before the squash merge.

Prepared head SHA: b17a4ff
Review: openclaw#94746 (comment)

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

Labels

clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge clownfish:automerge Maintainer opted this Clownfish PR into bounded ClawSweeper-reviewed automerge P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 🚀 automerge armed This PR is in ClawSweeper's automerge lane.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

doctor: copy-sensitive paths get re-wrapped by clack note box, splitting file extensions

2 participants