Skip to content

fix(adapters): pipe psmux pane logs through a positional sidecar sink#218

Merged
pbean merged 1 commit into
bmad-code-org:mainfrom
dracic:fix/psmux-pipe-pane-positional-sink-217
Jul 21, 2026
Merged

fix(adapters): pipe psmux pane logs through a positional sidecar sink#218
pbean merged 1 commit into
bmad-code-org:mainfrom
dracic:fix/psmux-pipe-pane-positional-sink-217

Conversation

@dracic

@dracic dracic commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

On psmux 3.3.7 PsmuxMultiplexer.pipe_pane always produced a 0-byte run log, for two stacked reasons (diagnosed in the #185 live smoke, full evidence in #217 and psmux/psmux#482):

Fix (verified end-to-end on 3.3.7)

Write the sink source to a sidecar <log>.sink.ps1 next to the run log and hand psmux a flagless positional command (pwsh "<sidecar>"); the sink runs a chunked read/write loop that flushes per chunk, so pane bytes land incrementally and byte-exact (multibyte UTF-8 included). Sidecar paths containing PowerShell interpolation syntax ($, backtick) are refused up front with the usual best-effort warning, which now also covers the sidecar write itself.

Known accepted costs (per #217): the sink loads the user profile (-NoProfile cannot survive the flag stripping) and a sidecar .ps1 lives next to each run log. Known ceilings are named inline in pipe_pane: spaced/$-bearing paths through psmux's re-parse are untested upstream territory, an AllSigned/Restricted execution policy refuses the unsigned .ps1, and an rc-0 spawn race still yields a silent empty log.

Testing

  • tests/test_psmux_backend.py: 24 passed — pins the flagless positional command shape, the per-chunk flush loop, the interpolation-path rejection, and the warn-don't-raise contract for a failed sidecar write.
  • tests/test_multiplexer.py + tests/test_tui_data.py: no regressions.

Closes #217

Summary by CodeRabbit

  • Bug Fixes

    • Improved PowerShell-based pane capture reliability in psmux environments.
    • Added safeguards to skip capture and show a warning when the required sidecar script cannot be safely created or accessed.
    • Improved handling of capture failures and unsupported script paths.
  • Tests

    • Expanded coverage for sidecar script execution, write behavior, failure handling, and unsafe paths.

…bmad-code-org#217)

psmux pipe-pane strips every dash-flag token from the piped command, so
the pwsh -EncodedCommand sink died on spawn; and the CopyTo sink parked
bytes in a 4 KB buffer with unreliable pipe EOF, so run logs stayed
empty. Write the sink source to a sidecar <log>.sink.ps1 and invoke it
positionally with a chunked read/write loop that flushes per chunk, so
the live-tailed log receives pane bytes incrementally and byte-exact.
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a7ed9489-6211-4078-904f-f447580c2b29

📥 Commits

Reviewing files that changed from the base of the PR and between 555584a and ba3d327.

📒 Files selected for processing (2)
  • src/bmad_loop/adapters/psmux_backend.py
  • tests/test_psmux_backend.py

Walkthrough

PsmuxMultiplexer.pipe_pane now writes a positional PowerShell sidecar script, validates its path, launches it through psmux, and handles additional I/O failures. Tests cover invocation, incremental byte flushing, unwritable destinations, and unsafe interpolation syntax.

Changes

psmux sink capture

Layer / File(s) Summary
Sidecar sink implementation
src/bmad_loop/adapters/psmux_backend.py
The backend documents psmux’s flag stripping, derives and validates a .sink.ps1 path, writes a UTF-8 buffered byte-copy script, invokes it positionally, and catches broader I/O and encoding errors.
Sidecar behavior validation
tests/test_psmux_backend.py
Tests verify positional pwsh invocation, incremental byte writes and flushes, unwritable-path handling, and rejection of PowerShell interpolation syntax.

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

Sequence Diagram(s)

sequenceDiagram
  participant PsmuxMultiplexer
  participant psmux
  participant pwsh
  participant LogFile
  PsmuxMultiplexer->>PsmuxMultiplexer: Write <log>.sink.ps1
  PsmuxMultiplexer->>psmux: Start pipe-pane with positional script path
  psmux->>pwsh: Launch sidecar script
  pwsh->>LogFile: Append and flush pane bytes
Loading

Possibly related PRs

Poem

I’m a rabbit with a sidecar script,
Catching pane bytes bit by bit.
Flush them fast, keep paths safe,
No lost dashes in the chase.
Hop, hop—logs now grow bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately describes the main change: switching psmux pane logging to a positional sidecar sink.
Linked Issues check ✅ Passed The changes implement the issue's required positional sidecar sink with per-chunk flushing, byte-exact writes, path rejection, and best-effort warnings.
Out of Scope Changes check ✅ Passed The modified code and tests stay focused on psmux pane log capture and its edge cases, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

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.

psmux pipe_pane sink captures nothing on 3.3.7: -EncodedCommand transport is stripped upstream, and the CopyTo sink can't flush incrementally

2 participants