Context
internal/terminal/terminalrunner.DynamicMultiWriter is a useful primitive for fan-out of pty/master output: it implements io.Writer over an underlying set of writers that can be added or removed at runtime (so a test or runtime can attach a log file, a side pipe for prompt detection, an in-memory buffer, etc., and detach them again without tearing down the pty drain goroutine).
It is currently inside internal/, so downstream consumers cannot import it.
Why
The kukeon repo's e2e PTY harness wants exactly this primitive. PR eminwux/kukeon#75 ships a minimal single-bytes.Buffer drain because it cannot import the sbsh internal type. A comparison of the two harnesses (sbsh e2e/e2e_test.go vs kukeon e2e/e2e_pty_test.go from PR #75) makes the gap concrete: sbsh's harness uses DynamicMultiWriter to fan out master output to a log file plus a side pipe for an event-driven prompt detector, while kukeon's polls a single buffer. Once the type is public, kukeon can drop its bespoke drain in favour of the well-tested sbsh primitive.
Proposed shape
- Move the type to a public path, e.g.
pkg/io/dynamicmultiwriter (final name is your call).
- Keep the API surface minimal and stable: constructor,
Write, plus the dynamic add/remove methods that exist today.
- Document concurrency guarantees explicitly (which methods are safe under concurrent
Write and reader-set mutation).
- Internal callers in
internal/terminal/terminalrunner switch to the new public package; no behaviour change.
Acceptance criteria
Out of scope
- Any kukeon-side change. That is tracked separately on the kukeon repo and depends on this issue landing first.
Filed by
dev agent on behalf of the user (override of dev-role hard rule against filing project-backlog issues).
Context
internal/terminal/terminalrunner.DynamicMultiWriteris a useful primitive for fan-out of pty/master output: it implementsio.Writerover an underlying set of writers that can be added or removed at runtime (so a test or runtime can attach a log file, a side pipe for prompt detection, an in-memory buffer, etc., and detach them again without tearing down the pty drain goroutine).It is currently inside
internal/, so downstream consumers cannot import it.Why
The
kukeonrepo's e2e PTY harness wants exactly this primitive. PR eminwux/kukeon#75 ships a minimal single-bytes.Bufferdrain because it cannot import the sbsh internal type. A comparison of the two harnesses (sbshe2e/e2e_test.govs kukeone2e/e2e_pty_test.gofrom PR #75) makes the gap concrete: sbsh's harness usesDynamicMultiWriterto fan out master output to a log file plus a side pipe for an event-driven prompt detector, while kukeon's polls a single buffer. Once the type is public, kukeon can drop its bespoke drain in favour of the well-tested sbsh primitive.Proposed shape
pkg/io/dynamicmultiwriter(final name is your call).Write, plus the dynamic add/remove methods that exist today.Writeand reader-set mutation).internal/terminal/terminalrunnerswitch to the new public package; no behaviour change.Acceptance criteria
DynamicMultiWriteris importable from outside the module under a stable public path.*_test.go) and cover concurrent add/remove + Write.Out of scope
Filed by
dev agent on behalf of the user (override of dev-role hard rule against filing project-backlog issues).