# oc-tree — resumption guide Open this file first when picking the work back up. ## What this project is A Python TUI sidecar that subscribes to `opencode serve`'s SSE event stream and renders a live tree of sessions → messages → tool calls in a terminal pane next to the opencode TUI. Subagents nest under their parent via `session.created.info.parentID`. Roadmap entry: `localgenai/Roadmap.md` → "Layer 0: Cross-cutting capabilities" → "Observability — partial" + prioritized step #12. Phoenix (`opencode/.opencode/plugin/phoenix-bridge.js`) already handles the *external* deep-trace store. oc-tree is the glanceable "what's it doing right now" pane that lives in-harness (well, in tmux next to it). ## Where we are - Plan agreed: Python + textual, lives at `localgenai/oc-tree/`. - Phases: M0 → M1 → M2 → M3 → M4. See descriptions in this repo's task list (`TaskList`) or the roadmap entry. - **M0 — DONE** (skeleton). `uv sync` installs cleanly; `oc-tree` and `oc-tree-probe` entry points resolve. Imports verified. - **M0 — AWAITING USER ACTION** (schema verification). Probe is built but hasn't been run against a live `opencode serve`. Three open schema questions still unanswered. ## What blocks progress User needs to run the probe against a real opencode session and report back. Without these answers, M2's reducer design is guesswork. Run in a tmux pane while `opencode serve` is up: ```sh cd ~/Documents/obsidian/localgenai/oc-tree uv run oc-tree-probe ``` Drive opencode through a session that hits **all three** triggers: - Spawn a Task-tool subagent - Trigger at least one permission prompt - Make at least one regular tool call (Read/Bash/etc.) Ctrl-C the probe. Then run: ```sh # Q1: does session.created.info.parentID populate for subagents? jq -r 'select(.type=="session.created") | .raw.properties.info.parentID' \ /tmp/oc-tree-probe.jsonl # Q2: does message.part.updated carry full part or delta? jq -c 'select(.type=="message.part.updated") | .raw.properties.part' \ /tmp/oc-tree-probe.jsonl | head # Q3: what permission.* events actually fire? jq -r '.type' /tmp/oc-tree-probe.jsonl | grep -i permission | sort -u ``` Paste the output (or the JSONL file path) into the next session. ## What happens next Once probe answers are in: 1. Mark M0 complete, start **M1 (flat session list)** — textual app, live-updating list of sessions with status, no nesting yet. Proves the reducer + render loop. Independent of the schema answers, so could start in parallel. 2. **M2 (tree view)** — needs probe answers to know: - Whether to nest by `parentID` directly (Q1 yes) or fall back to inferring subagents from `Task` tool-part response payloads. - Whether the part-update reducer replaces by `partID` (Q2 = full part) or merges a delta (Q2 = delta). - What permission events to render (Q3). 3. **M3 (reconnect + state rebuild)** — heartbeat watchdog, REST replay on disconnect. Driven by sst/opencode#15149/#22198 known leaks. 4. **M4 (polish)** — keybindings, theme, tmux layout doc. ## File layout ``` localgenai/oc-tree/ ├── pyproject.toml uv project (textual, httpx, httpx-sse) ├── README.md user-facing readme ├── NEXT_STEPS.md this file ├── .python-version 3.11 └── src/oc_tree/ ├── client.py OpenCodeClient: REST + SSE ├── probe.py schema-verification CLI ├── __main__.py stub for `oc-tree` (real TUI in M1) └── widgets/ empty (populated in M1+) ``` ## Key references - opencode server docs: - Authoritative schema: `GET /doc` on a running `opencode serve` (do not hardcode — fetch per-version). - sst/opencode#7451 — no per-session SSE endpoint; we filter `/event` client-side. - sst/opencode#6573 — Task subagent over `opencode serve` may have bugs; this is what Q1 verifies. - sst/opencode#11424 — `message.part.updated` sometimes replays full state; this is what Q2 verifies. - sst/opencode#15149, #22198 — SSE disconnect leaks; informs M3 shutdown discipline. ## Decisions worth not relitigating - **Python + textual** chosen over Go+Bubbletea (faster iteration, matches stack — uvx already in use) and Node+ink (worse SSE/UI ergonomics; phoenix-bridge.js doesn't justify matching). - **Read-only v1.** No sending messages, no editing. Just visibility. - **Lives in `localgenai/oc-tree/`** rather than its own repo; can be extracted later if it warrants a standalone release. - **State rebuild via REST on every (re)connect** rather than trusting SSE catchup or `Last-Event-ID` (server doesn't honor it).