Skip to content

fix(opencode): drain stdout/stderr via StdoutPipe to capture full discovery output - #11

Open
inola-ww wants to merge 2 commits into
aytzey:mainfrom
inola-ww:fix/opencode-discovery-pipe-drain
Open

fix(opencode): drain stdout/stderr via StdoutPipe to capture full discovery output#11
inola-ww wants to merge 2 commits into
aytzey:mainfrom
inola-ww:fix/opencode-discovery-pipe-drain

Conversation

@inola-ww

@inola-ww inola-ww commented Jul 16, 2026

Copy link
Copy Markdown

Fixes #10.

Summary

internal/session/opencode.go:runOpenCode set command.Stdout = &buf and
let command.Run() drain the pipes itself. When the opencode db discovery
query produced multi-megabyte JSON (a user with many sessions and
non-trivial first/last user-message previews), the captured buffer held
~2-4 MB instead of ~8 MB. discoverOpenCode then hit a truncated-JSON
parse error, deliberately swallowed it (so discovery is best-effort), and
returned zero opencode rows. The picker showed every other provider and
not the opencode ones.

The fix drains stdout and stderr via command.StdoutPipe() /
StderrPipe() and copies them into the existing cappedBuffer / a plain
bytes.Buffer from goroutines, waiting on both before command.Wait().
This is the same pattern the codebase already uses (and the only call
site that did not). cappedBuffer's enforced maxOpenCodeOutput cap is
preserved so a runaway CLI still gets rejected.

Verification

  • go build ./... clean
  • go vet ./... clean
  • go test ./internal/session/ -run "OpenCode|TestRunOpenCode|TestCappedBuffer|TestDiscoverFindsOpenCode|TestDecodeOpenCode" passes (12/12)
  • Manual: against the local opencode 1.18.2 store (137 root sessions,
    ~8.3 MB discovery JSON), showagent list --json returned 0 opencode
    rows before the fix and 137 after. showagent list now shows the
    sessions grouped under /home/ww.

The new TestRunOpenCodeDrainsLargeOutput is a smoke test that pipes
96 KiB through a fake opencode and asserts the full payload is
captured. The exact race in os/exec is timing-dependent and not
deterministically reproducible in a unit test at this scale; the PR's
manual verification covers the failure mode that issue #10 reported.

Risk

Lowest possible: the helper is private and only called from the opencode
provider. The new path preserves the timeout, the maxOpenCodeOutput
cap, the stderr-detail-on-error rendering, and the partial-output
behavior when the child exits with a non-zero status. No other provider
is touched and no public surface changes.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of large command output during session discovery.
    • Prevented output from being truncated or causing command execution to stall.
    • Error reporting now more reliably includes relevant command output.

…covery output

runOpenCode set command.Stdout = &buf and let exec.Run() drain the
pipes itself. For the opencode db discovery query this returned a
truncated buffer: when the user's SQLite store has many sessions with
multi-KB first/last user-message previews, the JSON output crosses
~4 MB and os/exec's copy goroutine loses the tail after the child
exits. discoverOpenCode then sees a parse error, deliberately swallows
it (so discovery is best-effort), and returns zero opencode rows. The
picker ends up showing only the providers that read their stores
directly off disk (claude/pi/codex/gemini/jcode).

Switch the helper to command.StdoutPipe() + StderrPipe() and copy
both into cappedBuffer / bytes.Buffer from goroutines, waiting on the
drain before command.Wait() so the buffer holds the full payload.

Fixes aytzey#10
@inola-ww
inola-ww requested a review from aytzey as a code owner July 16, 2026 09:37
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@inola-ww, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a66432df-71ce-4736-986b-25d1c6a4bcf2

📥 Commits

Reviewing files that changed from the base of the PR and between d37395e and 2419d69.

📒 Files selected for processing (1)
  • internal/session/opencode.go
📝 Walkthrough

Walkthrough

runOpenCode now drains stdout and stderr through explicit pipes before waiting for the subprocess, preventing incomplete large-output capture. A regression test verifies that a large stdout payload and trailing marker are fully retained.

Changes

OpenCode subprocess output

Layer / File(s) Summary
Pipe-based subprocess draining
internal/session/opencode.go
runOpenCode uses concurrent pipe readers, retains the stdout cap, waits for process completion, and selects trimmed stderr or stdout for errors.
Large-output regression coverage
internal/session/opencode_test.go
A fake opencode executable emits a large payload and tail marker, and the test verifies complete output capture.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: aytzey

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main fix: draining opencode stdout/stderr to capture full discovery output.
Description check ✅ Passed The description is detailed and covers summary, verification, and risk, though it doesn't match the template format exactly.
Linked Issues check ✅ Passed The fix directly addresses issue #10 by fully draining opencode stdout/stderr, preserving caps and adding a regression test.
Out of Scope Changes check ✅ Passed The diff is narrowly scoped to runOpenCode and its regression test, with no unrelated functional changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/session/opencode.go`:
- Line 466: Replace the unbounded stderr bytes.Buffer in the opencode command
execution flow with cappedBuffer, configured using the existing maxOpenCodeError
limit, matching the stdout safeguard and preserving the existing stderr capture
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6b5b216c-158f-4640-8e7a-c27de5ce86a3

📥 Commits

Reviewing files that changed from the base of the PR and between ee8e035 and d37395e.

📒 Files selected for processing (2)
  • internal/session/opencode.go
  • internal/session/opencode_test.go

Comment thread internal/session/opencode.go Outdated
…eError limit

The pipe-drain conversion in the previous commit dropped the cappedBuffer
on stderr in favor of an unbounded bytes.Buffer, so a misbehaving opencode
CLI could grow stderr without bound (and allocate until OOM). Reuse the
existing maxOpenCodeError cap, matching the stdout safeguard and the
original pre-fix code path.

Caught by CodeRabbit review on PR aytzey#11.
@inola-ww

Copy link
Copy Markdown
Author

The 66.67% docstring coverage is computed across internal/session/opencode.go
as a whole, not against my diff.

Functions/methods in my PR diff:

  • runOpenCode (internal/session/opencode.go) — docstring present and
    extended in this PR (covers the new pipe-drain rationale).
  • TestRunOpenCodeDrainsLargeOutput (internal/session/opencode_test.go)
    — leading comment block, Go godoc-convention prefix.

The remaining ~32% are pre-existing symbols this PR did not touch:

  • opencodeProvider.Name, .DisplayName, .CommandName, .Home
    (interface-implementation boilerplate on every provider in the repo)
  • cappedBuffer.Write
  • opencodeTitle, opencodeUserText, opencodeSessionEntry

Adding docstrings to those here would expand the PR scope beyond the
issue #10 fix. Happy to do it as a follow-up commit on this same
branch (or in a separate PR) if you want — let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: showagent's opencode discovery silently returns 0 rows when the SQLite store has many sessions

1 participant