From 0cfe8955a4fb2fed539ba9c71733e8aaaea0a00a Mon Sep 17 00:00:00 2001 From: "tao.chen" <93983997+taochen-ct@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:12:35 +0800 Subject: [PATCH] docs: drop ring buffer mentions from ws spec and plan Co-Authored-By: Claude --- .../plans/2026-07-03-terminal-websocket.md | 6 +++--- .../2026-07-03-terminal-websocket-design.md | 20 ++++++++----------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/superpowers/plans/2026-07-03-terminal-websocket.md b/docs/superpowers/plans/2026-07-03-terminal-websocket.md index 38a8f47..eb076c7 100644 --- a/docs/superpowers/plans/2026-07-03-terminal-websocket.md +++ b/docs/superpowers/plans/2026-07-03-terminal-websocket.md @@ -12,7 +12,7 @@ **Decisions (from spec, confirmed):** - WebSocket library: `gorilla/websocket` -- Replay buffer: not in v1 (deferred; ring buffer is implemented but not exposed) +- Replay buffer: not in v1 (deferred; a future task can add it behind a separate accessor) - Reconnect: up to 3 attempts with exponential backoff on transport error; never on exit banner ## File Structure Map @@ -88,7 +88,7 @@ 2. Modify internal/process/session.go: add Stdin io.WriteCloser, Subscribers []Subscription, Exit process.ExitInfo (Code int, Signal string), mu sync.Mutex. Use a slice, not a map. 3. Modify internal/process/manager.go: - In LocalManager.Start: open a pipe for cmd.Stdin, capture the write end. Open a pipe for cmd.Stdout AND cmd.Stderr, point both at the same reader, capture the reader. - - Spawn goroutine A: read from the merged pipe in a 4 KiB loop, append to a 64 KiB ring buffer (truncate oldest when full, do not expose the buffer in this task), and for each Subscription in session.Subscribers do a non-blocking send (select with default that drops the chunk). Stop when the pipe returns EOF. + - Spawn goroutine A: read from the merged pipe in a 4 KiB loop, and for each Subscription in session.Subscribers do a non-blocking send (select with default that drops the chunk). Stop when the pipe returns EOF. No replay buffer in v1. - Spawn goroutine B: call cmd.Wait(), populate session.Exit (use ProcessState.ExitCode() and Sys().(syscall.WaitStatus).Signal() if signal), then close every Subscription's channel exactly once. Do not delete the session from the map. - Add LocalManager.Subscribe(workspaceID) (Subscription, error): if no session, return util.New(util.CodeNotFound, "no running process"). Allocate a channel, append a subscription backed by it, return it. 4. Keep existing tests compiling. New Subscribe method is additive. @@ -472,7 +472,7 @@ Reconnect: 1s, 2s, 4s backoff. Stop after 3 failed attempts → "error". Reset o - Process model grows `Subscribe` and `Stdin`: Tasks 1, 2. - 409 when process not running: Task 3. - Exit banner on close: Task 3. -- Replay buffer: explicitly deferred (Task 1 implements it but does not expose it). +- Replay buffer: not in v1. The manager's reader goroutine reads and fans out directly; no history is kept. - Frontend hook with reconnect policy: Task 4. - TerminalPanel wired to live process: Task 5. - Backward compatibility: existing routes unchanged, Manager interface is additively grown, no wire format changes. diff --git a/docs/superpowers/specs/2026-07-03-terminal-websocket-design.md b/docs/superpowers/specs/2026-07-03-terminal-websocket-design.md index a0b3311..75365ff 100644 --- a/docs/superpowers/specs/2026-07-03-terminal-websocket-design.md +++ b/docs/superpowers/specs/2026-07-03-terminal-websocket-design.md @@ -25,9 +25,9 @@ path. directions are text frames carrying raw bytes (UTF-8). We do not use a custom envelope or JSON framing in v1. - Backend process model gets two new responsibilities: capture child - stdout/stderr into an in-memory ring buffer plus a list of subscriber - channels, and accept stdin writes. This stays inside `internal/process`; - the API layer is a thin WebSocket adapter. + stdout/stderr into a list of subscriber channels, and accept stdin + writes. This stays inside `internal/process`; the API layer is a thin + WebSocket adapter. - The existing `Manager` interface grows one method: `Subscribe(workspaceID) (Subscription, error)`. We also add an `Input` accessor on the service layer for stdin. The surface stays small. @@ -61,14 +61,13 @@ path. Buffered to a small constant (e.g. 32) to avoid blocking the producer. - `Close() error` — drops the subscription. Idempotent. - `LocalManager` keeps per-session: - - a `bytes.Buffer` of recent output (cap, e.g. 64 KiB) for any future - replay — implemented but **not exposed in v1**. - - a `[]chan []byte` of subscribers, guarded by `sync.Mutex`. + - a `map[Subscription]struct{}` of subscribers, guarded by `sync.Mutex`. - a `io.WriteCloser` for stdin (created via `cmd.Stdin = ...`). - On `Start`, set `cmd.Stdout` and `cmd.Stderr` to a pipe. A new goroutine - per session reads from the pipe, writes to the ring buffer, and fans out - to every subscriber channel. A non-blocking send (`select` with - `default`) drops output for slow clients. A 4 KiB read buffer is fine. + per session reads from the pipe and fans out to every subscriber channel. + A non-blocking send (`select` with `default`) drops output for slow + clients. A 4 KiB read buffer is fine. Replay of past output is **not** in + v1; a future task can add a ring buffer behind a separate accessor. - `Wait()` is called in a separate goroutine; on exit, the manager closes all subscriber channels and stores the exit status on the session. @@ -196,9 +195,6 @@ path. - The `Subscription` type and the `Input` writer are the two new capabilities the manager must grow. They are small and orthogonal. -- The ring buffer for replay is implemented but unused in v1. This is - dead weight. **Defer**: the spec keeps it for symmetry with future - replay work. Plan may drop it if not needed. - The exit banner format (`\r\n[process exited with code N]\r\n`) is a simple, debuggable string. We are not building a structured protocol. - All v1 design decisions aim to keep the backend diff small, the