Applied review findings from the simplify pass:
1. Extract fromBunStream(name, get) and forkStderrDrain(stream, into)
helpers — 4 duplicated Stream.fromReadableStream call sites collapse
to one factory, and the identical stderr drain across serve/acp is
now a single helper. Error messages now include the underlying cause
instead of swallowing it.
2. acp.send awaits proc.stdin.write's backpressure promise. The bare
Effect.sync was discarding the Promise<number> form, which can
reorder ndjson lines under pipe-buffer-full conditions and corrupt
framing.
3. acp.close drops the try/catch around proc.stdin.end() — idempotent
in Bun, the bare catch only masked future regressions.
4. Effect.ignore on stream drains is now Effect.ignore({ log: true })
so a real protocol or decode error surfaces in test debug output
instead of disappearing silently.
5. Help-snapshots replaces the manual failures[] accumulator + continue
with Effect.partition. Same behavior, no mutable state, declarative.
324/324 CLI tests stay green; typecheck clean.
Replaces raw Promise.race + setTimeout in the acp builder's release
finalizer with an Effect.gen that expresses the graceful-then-SIGTERM
race using Effect.timeoutOrElse(Duration.seconds(2)). Same behavior,
no leaked timer, consistent with the serve builder.
Adds the second long-lived-command builder to the cli-process harness:
`opencode.acp(opts)` spawns the real CLI in JSON-RPC-over-stdio mode
and returns a duplex handle (`send`/`receive`/`close`/`exited`) scoped
to the test's lifetime. Stdin EOF triggers a clean shutdown; the scope
finalizer also falls back to SIGTERM after 2s for a hung child.
ACP frames each JSON-RPC message as one ndjson line on stdout. The
builder forks a scope-bound `Stream.fromReadableStream` + `splitLines`
pipeline that feeds parsed responses into a `Queue.unbounded`, so tests
can `yield* acp.receive` without worrying about backpressure or framing.
Two smoke tests:
- `initialize` round-trip — sends the protocol handshake from the ACP
README and asserts the response advertises the same protocolVersion
and a non-empty agentCapabilities block.
- Clean shutdown on stdin EOF — proves the scope finalizer's stdin.end()
triggers a graceful exit, not a SIGTERM-fallback exit.