Files
browser/openspec/changes/improve-server-terminal-log-window/design.md
T

35 lines
2.9 KiB
Markdown

## Context
The server log SSE route currently replays `historyLimit` entries independently for every stream, then sends live events. The management terminal stores a bounded client buffer but does not own a scroll container or follow state, so the browser viewport remains at its initial top position after history is appended. Durable log bodies are stored by the platform `LogBodyStore` as memory indexes backed by per-stream JSONL segment files; this change does not rewrite or delete that durable history.
## Goals / Non-Goals
**Goals:**
- Make initial server log replay bounded across all streams and limited to the newest entries.
- Keep replay chronological and preserve live SSE delivery.
- Keep at most 10,000 terminal lines in the browser and automatically follow the newest line until the operator scrolls away.
- Make returning to the bottom restore follow mode.
**Non-Goals:**
- No full-history fetch, log retention deletion, database migration, or change to Run ingest.
- No browser-to-Run transport or game-specific log selection.
## Decisions
- Treat `historyLimit` as a server-wide budget for the SSE endpoint, with a platform cap of 10,000. This prevents stream-count multiplication while retaining the existing query parameter and compatibility for existing clients.
- Read bounded tails from each stream using its latest sequence, merge by timestamp/sequence/stream ID, and emit only the newest budgeted entries in chronological order. This keeps the UI output coherent without adding a new cross-stream database query API.
- Give the terminal output element a ref and track `followLatest` from scroll position. Initial history and live events call a bottom-scroll helper only while locked; a user scroll above a small bottom threshold unlocks, and a later scroll to the threshold locks again.
- Keep the rendered buffer capped at 10,000 through the existing merge helper. System and command-result lines use the same cap, so browser memory remains bounded even when the stream is noisy.
## Risks / Trade-offs
- [Risk] Reading a tail from every stream still does bounded work proportional to stream count. -> Mitigation: each stream read is capped by the global budget and the final response is capped at 10,000; existing per-stream cursor storage remains unchanged.
- [Risk] Timestamp ties across streams can reorder entries relative to ingest order. -> Mitigation: use sequence and stream ID tie-breakers and retain each stream's sequence ordering.
- [Risk] Scroll events can race with React rendering. -> Mitigation: defer bottom scrolling with `requestAnimationFrame` and re-check the element's current scroll metrics.
## Migration Plan
Deploy the backend and frontend together. Existing clients sending `historyLimit` continue to work, but receive a server-wide bounded replay. Rollback is code-only: reverting the endpoint selection and terminal follow logic restores the prior behavior without data migration.