From 3eae3a237ca6bf556deef35842c3359a4f617fb1 Mon Sep 17 00:00:00 2001 From: Tony Powell Date: Wed, 14 Jan 2026 13:24:34 -0500 Subject: [PATCH 01/11] chore: Publishing tasks --- todo/LEARNINGS.md | 163 ---------------------------------------------- todo/PROMPT.md | 1 + todo/TASKS.md | 104 +++++++++-------------------- 3 files changed, 31 insertions(+), 237 deletions(-) diff --git a/todo/LEARNINGS.md b/todo/LEARNINGS.md index 025a602..3a573c3 100644 --- a/todo/LEARNINGS.md +++ b/todo/LEARNINGS.md @@ -16,166 +16,3 @@ Use this knowledge to avoid repeating mistakes and build on what works. - Pattern that worked well - Anything the next agent should know --> - -## mock-loop-interface - -- Created `src/agent.ts` with an `Agent` interface that defines `run()` and `isAvailable()` methods -- The interface uses typed events (`onLog`, `onOutput`) for streaming updates to consumers -- `LogEntry` has categories: info, success, warning, error - matches the existing loop.ts color scheme -- `AgentOutput` is raw text with timestamps for agent stdout/stderr -- `OpenCodeAgent` wraps the real CLI using `Bun.spawn()` to capture output streams -- `MockAgent` is fully configurable: logs, output, exitCode, delay, and availability -- For tests, use `!` non-null assertions when accessing array elements after verifying length with `toHaveLength()` -- The mock can be reconfigured mid-test using `configure()` method for testing different scenarios -- Keep test mocks simple - just arrays of strings and basic config objects, no complex simulation - -## loop-dry-run - -- Added `dryRun` and `agent` options to `LoopOptions` interface -- When `dryRun: true`, the loop skips git branch creation and uses MockAgent instead of OpenCodeAgent -- The `agent` option allows injecting any Agent implementation for testing or custom behavior -- Replaced `process.exit(1)` calls with `throw new Error()` for better testability -- Tests need `pauseSeconds: 0` to avoid the 3-second default pause between iterations -- TASKS.md format uses `###` (h3) for task IDs, not `##` (h2) - important for test fixtures -- When testing agent invocation, need pending tasks - if all tasks complete, loop exits before calling agent -- Event callbacks (onLog, onOutput) forward agent events to the loop's console.log and stdout - -## output-buffer - -- Created `src/ui/buffer.ts` as a shared module for storing loop logs and agent output separately -- Reused the `LogCategory` type from `src/agent.ts` to keep categories consistent (info, success, warning, error) -- Used callback-based subscriptions with `Set` for efficient add/remove operations -- Subscription functions return an unsubscribe function (closure pattern) for clean cleanup -- `getLogs()` and `getOutput()` return copies of arrays (`[...array]`) to prevent external mutation -- The `clear()` method was added for buffer reset while keeping subscriptions intact -- Tests verify that subscriptions continue working after clear() - important for reconnection scenarios -- Kept the module simple with no dependencies beyond the LogCategory type - YAGNI principle - -## stream-capture - -- Added `buffer?: OutputBuffer` to `LoopOptions` - optional so non-UI mode continues to work unchanged -- Used a factory function pattern `createLoggers(buffer?)` to create log functions that write to both console and buffer -- The loggers are created at the start of `runLoop` and passed to `createWorkingBranch` via a `Loggers` interface -- Agent output is captured in the `onOutput` event handler: writes to both `process.stdout` and `buffer?.appendOutput()` -- The optional chaining (`buffer?.appendLog`) ensures graceful fallback when no buffer is provided -- Console.log calls continue working for non-UI mode - the buffer is purely additive -- Tests mock both `console.log` and `process.stdout.write` to verify output goes to both destinations -- Buffer subscriptions work in real-time - subscribers receive entries as they are appended during loop execution - -## bun-server - -- Bun.serve() returns a server object with inferred type - no need to import `Server` type explicitly (it requires a generic argument anyway) -- For WebSocket upgrade, use `server.upgrade(req, { data })` inside fetch handler - if successful returns truthy and you return `undefined` -- `routes` object handles static routes, `fetch` function handles dynamic routes and WebSocket upgrades -- WebSocket handlers receive `ServerWebSocket` where T is the data type attached during upgrade -- For tests, use different ports per test to avoid conflicts (8315, 8316, etc.) since tests may run in parallel -- `afterEach` with `server.stop()` ensures clean teardown between tests -- WebSocket tests need proper timeout handling with Promise wrappers around event callbacks -- Placeholder responses are simple - just return `new Response()` with appropriate headers/status - -## websocket-streaming - -- WebSocket data can hold unsubscribe functions directly - `{ id, unsubscribeLogs: (() => void) | null, unsubscribeOutput: (() => void) | null }` -- When a client connects: (1) send connected message, (2) send history, (3) subscribe to buffer updates -- History message includes both logs and output in a single `{ type: "history", logs: [], output: [] }` message -- New entries are sent as individual `{ type: "log", entry }` or `{ type: "output", entry }` messages -- On disconnect, must call the unsubscribe functions and set them to null to avoid memory leaks -- Tests for WebSocket message timing can be flaky - avoid sequential `await receiveMessage()` calls across multiple sockets -- Better pattern: collect all messages in arrays via `onmessage` handlers, then filter and assert after a small delay -- Exported `WebSocketMessage` type for type-safe parsing in tests and future frontend code -- The `BufferLogEntry` and `BufferAgentOutput` types needed to be imported from buffer.ts for the message types - -## html-shell - -- Bun's HTML imports allow `./app.tsx` to be referenced directly in script tags - Bun handles the transpilation automatically -- Used `