Skip to content

fix(capture): retain valid UTF8 output at the byte limit - #1006

Merged
steipete merged 1 commit into
openclaw:mainfrom
rudycelekli:fix/capture-action-utf8-prefix
Oct 10, 2026
Merged

steipete merged 1 commit into
openclaw:mainfrom
rudycelekli:fix/capture-action-utf8-prefix

Conversation

@rudycelekli

Copy link
Copy Markdown
Contributor

Summary

Preserve capture-action child stdout/stderr when the existing 64 KiB retention limit splits a UTF8 scalar. Previously, strict decoding of the incomplete retained bytes discarded the entire valid prefix. The command result lost the text and its manifest stream receipt recorded byteCount 0 with the empty-content digest.

Recover only a valid prefix whose incomplete trailing scalar could be completed, and only when bytes were actually discarded. Keep the byte budget and truncation flags. Exact-limit true EOF, incomplete true EOF, interior binary data, overlong encodings, surrogate prefixes and out-of-range prefixes retain strict behavior. Process dispatch, generation identity, signal handling, cleanup, capture permissions and artifact custody are unchanged.

Native production evidence

A complete unchanged CLI build at main 159412696 passed. The native consumer links 1,853 actual production CLI/dependency objects and calls the real process runner plus CaptureActionManifestWriter.stream; it contains no copied producer or manifest implementation. Six actual owned Perl child processes exit0 with real generation identity and process-group cleanup. Both stream receipts are serialized and checked against independently hashed retained text.

Before: valid split 2/3/4-byte output loses both stdout and stderr and produces byteCount 0/empty-content digest. ASCII and aligned multibyte controls retain 65,536 bytes. After: all six controls pass; the split cases retain 65,535, 65,535 and 65,533 bytes with the correct nonempty digests and truncation flags.

This executes the actual process/manifest graph without a desktop capture. Host macOS 26.0 is below the documented 26.1 UI baseline; no UI, Bridge compatibility, screen capture or permission escalation is claimed.

Verification

  • Full existing owner test file plus the regressions compiled against the complete unchanged graph and executed through Swift Testing: 28 tests, two failing definitions/33 assertion issues before the fix; lifecycle/cancellation/cleanup and malformed/EOF controls pass.
  • After adding an additional interior-invalid control, all 29 owner tests pass in a quiet replay. An earlier concurrent replay hit one existing effective-budget timing assertion; its failure is preserved and no timing threshold was changed.
  • Complete fixed CLI build passes; both before/after native production consumers retained with source/binary hashes.
  • Declared repository pnpm run lint --no-cache passes: 112 existing warnings, zero serious violations. The changed paths pass strict lint with zero violations. An earlier extra full --strict check promoted those unchanged warnings to errors; it is preserved separately and is not the declared gate.
  • Full SwiftFormat check passes: 0/2,088 files require formatting, 153 files skipped. Changed files formatted using root and CLI configurations; whitespace check passes.
  • Actual pinned pnpm 11.28.4 through owned Corepack cache, Node 24.5.0 within the declared node >=22 range. Exact matching submodule gitlinks and checkout-local Swift workspace mapping. No dependency or lock changes.
  • Required unchanged hosted acceptance passed at signed/DCO b328b49925ddf086348a7717f26861192cc2138c: ordinary macOS CI all six jobs; CodeQL all three languages, including actual Swift builds/analysis; supplemental release validation all four groups. The full-safe job actually ran unchanged pnpm run test:safe, reporting 110 core tests and 2,266 CLI tests passed, and verified canonical workspace locks. The other groups ran their complete package suites. No workflows or permissions were changed.

AI assistance was used; the frozen two-file source patch and native evidence received independent review before signed/DCO preparation. No release or installed binary promotion.

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
@clawsweeper

clawsweeper Bot commented Oct 7, 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.

@cursor

cursor Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Localized change to bounded pipe UTF-8 decoding on truncation only; extensive new tests cover edge cases without altering process lifecycle or capture permissions.

Overview
When capture-action child stdout/stderr hit the 64 KiB retention cap mid–UTF-8 character, strict decoding used to fail and drop the entire retained buffer (empty text, manifest byteCount 0). BoundedPipeOutput.finish() now, only when output was actually truncated, tries to strip a partial trailing scalar (1–3 bytes) if that suffix could belong to a valid multibyte sequence; the decodable prefix is returned and truncation stays true.

Unchanged: exact cap at true EOF, incomplete bytes at EOF without further discard, interior invalid UTF-8, overlong/surrogate/out-of-range prefixes still yield empty output with the prior strict rules. The 64 KiB budget, truncation flags, and process/manifest plumbing are untouched.

Adds Swift Testing regressions for aligned caps, split writes, multibyte prefixes (é/€/🙂), invalid sequences, and manifest stream byteCount alignment.

Reviewed by Cursor Bugbot for commit b328b49. Bugbot is set up for automated code reviews on this repo. Configure here.

@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 Oct 7, 2026
@clawsweeper

clawsweeper Bot commented Oct 7, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed October 7, 2026, 7:24 PM ET / 23:24 UTC.

ClawSweeper review

What this changes

Preserve valid capture-action stdout and stderr when the 64 KiB retention limit splits a UTF-8 character, with regression coverage for truncation and malformed input.

Merge readiness

✅ Ready for maintainer review

This PR remains necessary: current main and v4.9.0 still exhibit the decoding defect. The focused repair has relevant native before/after evidence, and no actionable correctness finding remains.

Priority: P2
Reviewed head: b328b49925ddf086348a7717f26861192cc2138c

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused, well-covered repair with relevant native observations and no blocking findings.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (live_output): The captured body reports macOS before/after runs through the actual process runner and manifest stream owner using real child processes, showing recovered UTF-8 prefixes and correct receipts. This directly covers the changed path; desktop capture is unnecessary. No stored-data schema or migration contract changes.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (live_output): The captured body reports macOS before/after runs through the actual process runner and manifest stream owner using real child processes, showing recovered UTF-8 prefixes and correct receipts. This directly covers the changed path; desktop capture is unnecessary. No stored-data schema or migration contract changes.
Evidence reviewed 9 items Introduced patch: The pinned introduction changes only output decoding and its tests. Recovery requires actual truncation, a legally completable trailing scalar, and a strictly decodable preceding buffer.
Current main still needs the fix: Main still strictly decodes the entire retained buffer and substitutes empty text on failure. Inspection of v4.9.0 shows the same implementation.
Released implementation: The latest supplied release, v4.9.0, retains the defective decoding behavior; this is not an already-shipped repair.
Findings None None.
Security None None.

How this fits together

Peekaboo’s capture-action process runner collects bounded output from a child command. That text feeds the command result and the manifest’s stream byte counts and hashes.

flowchart LR
  A[Child command] --> B[Stdout and stderr pipes]
  B --> C[64 KiB retention limit]
  C --> D[Strict UTF-8 decoding]
  D --> E[Recover incomplete trailing character when truncated]
  E --> F[Command result]
  F --> G[Manifest stream receipts]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test growth production +29/-1; tests +110/-0 Production growth is confined to trailing-scalar recovery and supported by focused boundary regressions.

Technical review

Best possible solution:

Retain the complete valid UTF-8 prefix within the existing byte budget while preserving strict malformed-input handling and accurate stream receipts.

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

Yes: current main retains the first 65,536 bytes and strictly decodes them, so splitting a multibyte character discards the valid prefix. Source inspection establishes the path; the contributor also reports native before/after reproduction.

Is this the best way to solve the issue?

Yes: the repair is confined to truncated output and validates both the incomplete suffix and retained prefix, preserving existing malformed-input and true-EOF behavior.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 60b541e8f340.

Labels

Label changes:

  • add P2: This fixes loss of retained child output at a specific UTF-8 byte boundary with limited scope.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The captured body reports macOS before/after runs through the actual process runner and manifest stream owner using real child processes, showing recovered UTF-8 prefixes and correct receipts. This directly covers the changed path; desktop capture is unnecessary. No stored-data schema or migration contract changes.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The captured body reports macOS before/after runs through the actual process runner and manifest stream owner using real child processes, showing recovered UTF-8 prefixes and correct receipts. This directly covers the changed path; desktop capture is unnecessary. No stored-data schema or migration contract changes.

Label justifications:

  • P2: This fixes loss of retained child output at a specific UTF-8 byte boundary with limited scope.
  • 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 captured body reports macOS before/after runs through the actual process runner and manifest stream owner using real child processes, showing recovered UTF-8 prefixes and correct receipts. This directly covers the changed path; desktop capture is unnecessary. No stored-data schema or migration contract changes.
  • proof: sufficient: Contributor real behavior proof is sufficient. The captured body reports macOS before/after runs through the actual process runner and manifest stream owner using real child processes, showing recovered UTF-8 prefixes and correct receipts. This directly covers the changed path; desktop capture is unnecessary. No stored-data schema or migration contract changes.

Evidence

What I checked:

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • SebTardif: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

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.

@steipete
steipete merged commit 1bf9581 into openclaw:main Oct 10, 2026
12 checks passed
@steipete

Copy link
Copy Markdown
Collaborator

Landed as 1bf9581. Thanks @rudycelekli!

Verification: I read the recovery logic against the edge cases. Recovery only runs when bytes were actually truncated; the trailing scalar is completed with the minimal valid continuation (E0 needs A0, F0 needs 90), so overlong, surrogate and out-of-range prefixes still fail strictly; and the valid prefix must decode on its own. Codex autoreview (P0–P2) was scoped-clean at b328b49. Hosted CI was green on that head (CLI, Core, Tachikoma, macOS app builds, SwiftFormat, SwiftLint, CodeQL). The new CaptureActionProcessRunnerTests cover the 2/3/4-byte splits, invalid prefixes, interior corruption and the truncation flags. I'm adding the changelog entry with credit in a follow-up.

steipete added a commit that referenced this pull request Oct 10, 2026
A Peekaboo CLI binary invoked under any name not ending in "peekaboo" (a copy such as peekaboo-before, or a symlink such as pb -> peekaboo) rejected every command with "Unknown command '<its own path>'", because CommanderRuntimeRouter dropped argv[0] only when it hasSuffix("peekaboo"). The CLI entry points now take full argv and always drop exactly the executable element; the same heuristic is removed from agent default-subcommand normalization and result-envelope classification. A caller audit found only two tail-only callers (both tests), now passing an executable name.

Also adds the changelog credits for #1006 and #1007.

Tests: CommanderRuntimeArgvTests plus expanded resolution/classification tests cover absolute paths, pb, peekaboo-4.9, peekaboo-before and the peekaboo forms (141 assertion failures against the old code); focused swift test 78 tests in 7 suites; manual copied-binary and real pb symlink --version/see --help; Codex autoreview scoped-clean; hosted CI and CodeQL green.
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