Skip to content

feat(pkg): extract DynamicMultiWriter to a public package #145

Description

@eminwux

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

  • DynamicMultiWriter is importable from outside the module under a stable public path.
  • Existing sbsh callers compile against the new path with no behavioural change.
  • Unit tests for the public type live next to it (*_test.go) and cover concurrent add/remove + Write.
  • A short godoc on the type explaining the contract (when writers are added/removed, error semantics, ordering guarantees).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions