Skip to content

fix: terminal proof rejects soft-wrapped output - #1285

Merged
steipete merged 1 commit into
mainfrom
fix/terminal-soft-wrap-proof
Aug 28, 2026
Merged

fix: terminal proof rejects soft-wrapped output#1285
steipete merged 1 commit into
mainfrom
fix/terminal-soft-wrap-proof

Conversation

@steipete

@steipete steipete commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Resolves a problem where the retained terminal verifier rejects successful output when an expected phrase crosses the terminal's 160-column soft-wrap boundary. The historical symptom was peerD / ependencies appearing on adjacent screen rows in this review, despite the command succeeding. That link is context, not a claim that the original OpenClaw tests were rerun here.

Why This Change Was Made

Use tmux's wrap metadata (capture-pane -J) for assertion history and the initial presentAtStart snapshot, retaining literal includes matching, hard newline boundaries, whitespace, exit-status checks, and command isolation. Final viewport capture deliberately keeps physical screen rows.

This repairs the retained verifier only. Automatic generation remains retired by #1280; the intervening historical-proof classification and helper-retirement changes are included in the base. No automatic lane, policy gate, timeout/retry, dependency, schema, or OpenClaw product code changes.

User Impact

Operators using retained terminal tooling can match phrases split only by terminal layout without accepting a true newline inside the phrase, a nonzero exit, or a marker left by a previous command. Visual output remains wrapped as rendered. Ordinary automatic reviews do not resume proof execution.

OpenClaw Bay Impact

Unaffected: no observer fields, projections, routes, controls, lifecycle policy, or proof-credit rules change. Historical receipts remain diagnostic; this patch does not turn terminal PASS into behavioral-proof sufficiency.

Documentation Impact

Reviewed AGENTS.md, CONTRIBUTING.md, VISION.md, README.md, and docs/README.md. Updated the compatibility-only docs/live-proof.md to distinguish logical assertion captures from visual viewport rows. Its existing maintainer owner and update triggers remain accurate. No changelog change.

Evidence

Tested committed head: c0eb630aa15632640695ba6d8497ca9d8f8470df.
Base: ccac077fea2f63570bd7d91969ee69abcca310e0.
Diff: production +3/-2 (two flags and one comment), tests +82/-9, docs +4/-0; three files only.

  • Final-head build and documentation checks passed: pnpm run build; pnpm run check:docs; git diff origin/main..HEAD --check.
  • Final-head real-tmux regression checks: 5 passed, 0 failed, 0 skipped (82.5 seconds), covering entry/subsequent commands and initial presence, actual hard newlines and exact whitespace, both completion modes with descendant cleanup, prior-command rejection, and the original package-manager-echo safeguard unchanged.
  • Earlier wider focused run on f72ea010c1d7fd134b1bd0826b3a707778c312bc plus this patch: 173 passed, 0 failed, 0 skipped for node --test test/live-proof.test.ts test/live-proof-review-environment.test.ts test/live-proof-report.test.ts. This is supporting pre-rebase evidence, not an exact-head full-suite claim.
  • Codex autoreview: pre-commit uncommitted review passed; committed branch against origin/main passed. Both named gates used the helper's requested default P0 reporting scope, with no reported findings. No review transcripts are attached.

Full-check limitation: the earlier local pnpm run check passed static/docs, all builds, lint, and changed-surface coverage, then stopped making progress in the existing test/action-ledger-runtime.test.ts import-bindings pause subprocess. The owned run was stopped after more than 17 minutes. This does not establish a patch regression; the two corresponding main CI runs were green. No ledger changes or gate bypasses are included. Fresh exact-head hosted CI now passed: CI run 33193117080 completed successfully, including pnpm check, sparse repair build smoke, and Windows Codex launcher. CodeQL run 33193117034 also passed. These results close the local full-check validation gap without changing or bypassing the ledger tests.

Review disposition: the first ClawSweeper review assessed the real behavior proof as sufficient and found no actionable correctness or security issue. Its two remaining checklist entries both concerned the then-pending broad CI check; the exact-head success above resolves them. No Rank-up moves were requested. The short three-case real-runtime proof was refreshed on the same unchanged head before requesting the updated review.

Real Behavior Proof

Claim and exercised surface: compiled driveTerminal → real mediaProofCommandRunner/tmux PTY → buildLiveVerificationResultparseLiveVerificationResult. This directly exercises the changed capture boundary and downstream PASS/FAIL interpretation, rather than substituting a mocked capture or treating a generic command exit as proof.

Environment: local macOS arm64, Node 24.20.0, pnpm 11.10.0, tmux 3.7c; native local execution, no Crabbox provider/image/lease. Media recording disabled. The tmux manual specifies that -J preserves trailing spaces and joins wrapped lines.

Fixture: prepares pruned esm output with an actual peerDependencies host, padded so peerD ends at column 160. The baseline real-PTY regression on pre-fix production code failed despite exit 0 and showed peerD\nependencies; the same test after the fix passed. The regression and negative controls are public in test/live-proof-review-environment.test.ts.

Run from the repository root with Node 24 and tmux installed:

pnpm run build
node --test --test-name-pattern='terminal proof (supervises consecutive commands in the same pane|preserves hard newlines and exact whitespace|does not satisfy a command from the previous pane state|cleanup terminates a signal-resistant descendant in a distinct process group|cannot pass from package-manager echo before a nonzero exit)' test/live-proof-review-environment.test.ts
node /tmp/terminal-soft-wrap-runtime.mjs

The standalone script is included below so the runtime reproduction does not depend on inaccessible local files. It records the actual Git HEAD in each receipt and asserts all three outcomes; only its generated fixture directories are removed.

Observed final-head runtime trace (all three expected outcomes verified):

soft-wrap-exit-zero: overallPass=true, driveStatus=completed, satisfied=true, visualSoftWrap=true
hard-newline-exit-zero: overallPass=false, driveStatus=failed, satisfied=false; assertion mismatch despite successful command exit
soft-wrap-exit-seven: overallPass=false, driveStatus=failed, satisfied=false; terminal command failed with exit status 7

The passing case's returned viewport still contains peerD\nependencies host and does not contain the contiguous marker, while its assertion is satisfied. The hard-newline fixture separately proves exact whitespace can match without allowing the contiguous phrase. The package-manager test still uses an actual pnpm run echo followed by exit 7.

Artifacts and limits: the trace above and public regression source provide inspectable evidence; the standalone script writes timestamped, exact-head verification receipts to /tmp/terminal-soft-wrap-runtime-receipts.json. This is a controlled synthetic controller proof, not a rerun of the original OpenClaw 18 tests, a video, a live-channel run, or evidence of automatic proof execution. The fixture's 18 passed line is synthetic text only. No foreign PR was mutated, and no historical receipt is being used to grant proof credit.

Standalone runtime reproduction — save as /tmp/terminal-soft-wrap-runtime.mjs
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';

const repo = process.cwd();
const { driveTerminal } = await import(pathToFileURL(join(repo, 'dist/live-proof/drivers.js')));
const { mediaProofCommandRunner } = await import(pathToFileURL(join(repo, 'dist/clawsweeper-media-proof.js')));
const { buildLiveVerificationResult, parseLiveVerificationResult } = await import(pathToFileURL(join(repo, 'dist/live-proof/verification.js')));
const headSha = execFileSync('git', ['-C', repo, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
const marker = 'prepares pruned esm output with an actual peerDependencies host';
const line = ' '.repeat(160 - marker.indexOf('ependencies')) + marker;
const cases = [
  { name: 'soft-wrap-exit-zero', text: line + '\n18 passed\n', exit: 0, expectedPass: true },
  { name: 'hard-newline-exit-zero', text: line.replace('peerD', 'peerD\n') + '\n', exit: 0, expectedPass: false },
  { name: 'soft-wrap-exit-seven', text: line + '\n', exit: 7, expectedPass: false },
];
const receipts = [];
for (const scenario of cases) {
  const root = mkdtempSync('/tmp/clawsweeper-wrap-proof-');
  try {
    writeFileSync(join(root, 'emit.mjs'), `process.stdout.write(${JSON.stringify(scenario.text)}); process.exitCode = ${scenario.exit};\n`);
    const plan = {
      status: 'recommended', surface: 'terminal', terminalCompletion: 'exit_zero',
      reason: 'Local synthetic terminal capture verification; not an OpenClaw test rerun.',
      payoff: { kind: 'static_text', justification: 'Observe literal matching and physical screen rows without media.' },
      entry: 'node emit.mjs', steps: [{ action: 'expect_output', text: marker }],
    };
    const drive = driveTerminal({ plan, checkout: root, rawVideoPath: join(root, 'proof.webm'), maxRecordingSeconds: 90, recordMedia: false, runner: mediaProofCommandRunner });
    const result = parseLiveVerificationResult(buildLiveVerificationResult({ repo: 'example/terminal-soft-wrap', item: 1, headSha, plan, driveStatus: drive.status, stepLog: drive.steps, output: drive.output, verifiedAt: new Date().toISOString() }));
    assert.equal(result.overall_pass, scenario.expectedPass, JSON.stringify(result));
    if (scenario.expectedPass) {
      assert.ok(result.output.includes('peerD\nependencies host'), 'visual viewport lost soft wrap');
      assert.equal(result.output.includes(marker), false, 'viewport was incorrectly joined');
    } else {
      assert.match(result.failure.reason, scenario.exit === 7 ? /exit status 7/ : /assertion mismatch/);
    }
    receipts.push({ scenario: scenario.name, sourceHead: headSha, uncommittedPatch: false, synthetic: true, ...result });
    console.log(JSON.stringify({ scenario: scenario.name, overallPass: result.overall_pass, driveStatus: result.drive_status, satisfied: result.steps[0].satisfied, failure: result.failure?.reason, visualSoftWrap: result.output.includes('peerD\nependencies host') }));
  } finally {
    rmSync(root, { recursive: true, force: true });
  }
}
writeFileSync('/tmp/terminal-soft-wrap-runtime-receipts.json', JSON.stringify(receipts, null, 2) + '\n', { mode: 0o600 });
console.log('runtime-proof: all three expected outcomes verified; no publication');

@clawsweeper

clawsweeper Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

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

@steipete
steipete marked this pull request as ready for review August 28, 2026 17:06
@clawsweeper clawsweeper Bot added 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. labels Aug 28, 2026
@clawsweeper

clawsweeper Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

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

ClawSweeper review

What this changes

The PR makes retained tmux terminal assertions join soft-wrapped rows while preserving hard newlines, whitespace, and visual screen-row output.

Merge readiness

⚠️ Ready for maintainer review - 1 item remains

This PR remains necessary because current main still captures terminal assertion text without tmux soft-wrap joining. The exact introduced change is narrow, preserves the separate visual viewport, and has sufficient real-tmux proof with no actionable correctness or security finding.

Priority: P2
Reviewed head: c0eb630aa15632640695ba6d8497ca9d8f8470df

Review scores

Measure Result What it means
Overall readiness 🦞 diamond lobster (5/6) A compact implementation, direct real-tmux proof, focused negative controls, and successful exact-head hosted checks provide strong merge-readiness evidence.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (live_output): The PR body provides an exact-head real-tmux trace showing successful soft-wrap matching while separately rejecting a hard newline and a nonzero exit.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (live_output): The PR body provides an exact-head real-tmux trace showing successful soft-wrap matching while separately rejecting a hard newline and a nonzero exit.
Evidence reviewed 6 items Current-main behavior remains unfixed: The pinned main/base capture helpers omit tmux -J, so wrapped screen rows remain separated when used for assertion snapshots.
Introduced capture boundary: The PR adds -J only to initial and history assertion captures, while the final viewport capture intentionally keeps physical screen rows.
Assertion semantics are preserved: Observed expectations are evaluated against the retained history capture, while final reporting captures the separate non-joined viewport.
Findings None None.
Security None None.

How this fits together

ClawSweeper’s retained terminal verifier captures tmux pane output to evaluate historical terminal assertions while separately retaining a visual terminal viewport. Command output flows through these captures into assertion results and diagnostic verification receipts.

flowchart LR
  A[Terminal command output] --> B[tmux pane]
  B --> C[Assertion capture]
  B --> D[Visual viewport capture]
  C --> E[Literal assertion check]
  E --> F[Verification receipt]
  D --> F
Loading

Before merge

  • Complete next step (P2) - No repair-lane work is needed because the current PR has no identified actionable defect; it is ready for ordinary maintainer review.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Diff scope 3 files; 89 additions, 11 deletions The implementation is confined to two terminal-capture flags, focused regression coverage, and matching documentation.
Production versus coverage production +3/-2; tests +82/-9; docs +4/-0 Most of the patch is focused real-tmux regression coverage rather than new production surface.

Technical review

Best possible solution:

Keep the logical assertion capture joined only for tmux soft wraps and retain the physical-row viewport, then land the focused fix through normal maintainer review.

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

Yes: current main’s assertion captures omit -J, and the supplied exact-head real-tmux fixture exercises the 160-column soft-wrap case plus hard-newline and nonzero-exit controls. This review inspected the source and submitted trace but did not execute the fixture.

Is this the best way to solve the issue?

Yes: adding -J only to logical assertion snapshots fixes layout-only wrapping without weakening literal matching or changing the separately captured visual viewport.

AGENTS.md: found and applied where relevant.

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

Labels

Label changes:

  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.

Label justifications:

  • P2: This corrects a retained terminal-verifier false negative with limited operator-facing blast radius.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body provides an exact-head real-tmux trace showing successful soft-wrap matching while separately rejecting a hard newline and a nonzero exit.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body provides an exact-head real-tmux trace showing successful soft-wrap matching while separately rejecting a hard newline and a nonzero exit.

Evidence

What I checked:

  • Current-main behavior remains unfixed: The pinned main/base capture helpers omit tmux -J, so wrapped screen rows remain separated when used for assertion snapshots. (src/live-proof/drivers.ts:1829, ccac077fea2f)
  • Introduced capture boundary: The PR adds -J only to initial and history assertion captures, while the final viewport capture intentionally keeps physical screen rows. (src/live-proof/drivers.ts:1829, c0eb630aa156)
  • Assertion semantics are preserved: Observed expectations are evaluated against the retained history capture, while final reporting captures the separate non-joined viewport. (src/live-proof/drivers.ts:1497, c0eb630aa156)
  • Focused regression coverage: The real-tmux tests cover a 160-column soft wrap, consecutive commands, stale prior-command rejection, and a hard newline plus exact-whitespace negative control. (test/live-proof-review-environment.test.ts:1103, c0eb630aa156)
  • Feature-history routing: Recent history shows Peter Steinberger repeatedly maintaining this terminal-proof driver, with Vincent Koc contributing the authoritative-result path immediately before this fix. (src/live-proof/drivers.ts:1829, c0eb630aa156)
  • Exact-head proof and hosted validation: The PR body records a real tmux run on the exact head with passing soft-wrap behavior and hard-newline/nonzero-exit rejection; it also links successful exact-head pnpm check and CodeQL runs. (test/live-proof-review-environment.test.ts:1171, c0eb630aa156)

Likely related people:

  • Peter Steinberger: Authored this focused change and the preceding finite-command, cleanup, and successful-terminal-verification work in the same driver. (role: recent terminal-proof feature owner; confidence: high; commits: c0eb630aa156, d56daf07f0c3, 2a5c59f9d5e5; files: src/live-proof/drivers.ts, test/live-proof-review-environment.test.ts)
  • Vincent Koc: Recent history attributes the authoritative terminal-results work to Vincent Koc in the same live-proof driver. (role: adjacent result-path contributor; confidence: medium; commits: d103c0cf5c98; files: src/live-proof/drivers.ts)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • 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.

History

Review history (1 earlier review cycle)
  • reviewed 2026-08-28T17:12:41.958Z sha c0eb630 :: needs maintainer review before merge. :: none

@steipete

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

The main PR body now records successful exact-head CI (including pnpm check) and CodeQL, resolving both pending-CI checklist entries from the prior review. The three-case real-tmux runtime proof was refreshed on unchanged head c0eb630aa15632640695ba6d8497ca9d8f8470df; all expected outcomes still hold. No source, policy, or gate changes.

@clawsweeper

clawsweeper Bot commented Aug 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 exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Aug 28, 2026
@steipete
steipete merged commit 8804099 into main Aug 28, 2026
24 of 25 checks passed
@steipete
steipete deleted the fix/terminal-soft-wrap-proof branch August 28, 2026 17:27
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: 🦞 diamond lobster Very strong PR readiness with only minor 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.

1 participant