From c593df6cf39fca46163b4e134882b743776c988c Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Mon, 10 Aug 2026 11:18:48 -0700 Subject: [PATCH] Untrack stale and misplaced docs from git CLAUDE.md, VENDORED.md, briefs/, docs/adr/, and three working docs (audit-fleet-prompt, perftrace-attribution-guide, tui-opentui-packaging) no longer belong in version control. VENDORED.md was also deleted outright since it undercounted vendored packages and is fully superseded by docs/VENDORING.md. CL-5905 --- CLAUDE.md | 1 - VENDORED.md | 74 ------- briefs/tui-rebuild-opentui.md | 308 ---------------------------- docs/adr/opentui-binding.md | 125 ----------- docs/audit-fleet-prompt.md | 128 ------------ docs/perftrace-attribution-guide.md | 189 ----------------- docs/tui-opentui-packaging.md | 242 ---------------------- 7 files changed, 1067 deletions(-) delete mode 100644 CLAUDE.md delete mode 100644 VENDORED.md delete mode 100644 briefs/tui-rebuild-opentui.md delete mode 100644 docs/adr/opentui-binding.md delete mode 100644 docs/audit-fleet-prompt.md delete mode 100644 docs/perftrace-attribution-guide.md delete mode 100644 docs/tui-opentui-packaging.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43c994c2d..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md diff --git a/VENDORED.md b/VENDORED.md deleted file mode 100644 index c8b29dad8..000000000 --- a/VENDORED.md +++ /dev/null @@ -1,74 +0,0 @@ -# Vendored packages - -Corbits Code consumes Interchange as published `@intx/*` npm packages pinned at -0.2.2, with one exception: `@intx/inference` is vendored into -`vendor/intx-inference`. The root `package.json` lists the vendored directory -as a workspace and pins the name via `overrides`, so every consumer — including -the published `@intx/agent`'s transitive dependency — resolves to the local -copy. We never modify or push to the upstream interchange repository; every -local change to interchange code lands in `vendor/` through this repo's normal -review flow. - -## Why vendor - -- Carry corbits-local patches that published `@intx/inference` does not have. -- Let performance and correctness work on the shared runtime land through this - repo's normal review flow. Upstreaming is not an option: nothing from this - repo goes to `faremeter/interchange`. - -## Currently vendored - -| Package | Vendored path | Baseline | -|---|---|---| -| `@intx/inference` | `vendor/intx-inference` | upstream `v0.2.2` tag source | - -## How resolution works - -1. The vendored package keeps its original `name: "@intx/inference"` and its - `exports` pointing at `src` (Bun runs TypeScript natively). -2. Root `package.json` lists `./vendor/intx-inference` in `workspaces` and pins - `"@intx/inference": "workspace:*"` in `overrides`, so both direct imports - and the published packages' transitive dependency bind to the vendored copy. -3. `bun install` links it like any other workspace package. Verify with - `readlink node_modules/@intx/inference`. - -## Local patches (`vendor/intx-inference`) - -The vendored inference runtime is upstream 0.2.2 source plus the audited -keeper patches (Linear CL-4352 records the audit; git history for -`vendor/intx-inference/**` is the authoritative list). At a high level: - -- Commitment-boundary streaming: committed output is delivered incrementally - rather than as a terminal burst, and retry buffering is memory-linear. -- Streaming inactivity timeout measures silence since the last semantic SSE - event the adapter parsed, not since the last raw chunk. -- `isStreamTerminal` adapter hook so protocols that signal completion with a - semantic event (OpenAI Responses) do not hang the read loop. -- Abort-origin propagation through `classifyAbortError`. -- Reactor fixes: correlation-id leak, deliver() rejection surfacing, - tool-batch checkpointing with `addToHistory`, turns-revision skip-rewrite, - committed-retry `inference.retry` markers. -- `ephemeralTurns` prompt-tail injection (`ExtendedInferenceOptions`) and - context transforms riding `Dependencies.contextTransforms` — the two - surfaces the published packages do not carry. -- SSE unterminated-line cap; state-manager deep-freeze and lazy snapshots. - -## Keeping in sync with upstream - -When adopting a newer published `@intx/inference`: - -1. Fetch the new upstream source for comparison (e.g. `npm pack - @intx/inference@` or the upstream tag) — read-only; never a clone - you push from. -2. Diff it against `vendor/intx-inference/src`; expect only the keeper patches - above. Drop any patch the new version has absorbed. -3. Replay the remaining patches onto the new base, one commit per concern. -4. Bump the vendored `package.json` `version` and this file's baseline row. -5. Run `bun run typecheck`, `bun run build`, and `bun test` from repo root. -6. Update the `@intx/*` pins in root and vendored `package.json` together. - -## Licensing - -Keep the package's `"license": "LGPL-2.1-only"` field and its `LICENSE` file. -Vendored interchange code stays LGPL-2.1-only; the repo's GPLv2 license and AI -exception do not apply to it. diff --git a/briefs/tui-rebuild-opentui.md b/briefs/tui-rebuild-opentui.md deleted file mode 100644 index d31a48b5d..000000000 --- a/briefs/tui-rebuild-opentui.md +++ /dev/null @@ -1,308 +0,0 @@ -# Product brief: Corbits Code TUI rebuild (OpenTUI foundation) - -**Status:** Ready for planning -**Audience for this doc:** whoever plans and builds the TUI rebuild -**Not this brief:** framework APIs, package versions, migration mechanics (engineer's call once product is locked) - ---- - -## One-liner - -A full-screen coding-agent TUI that feels calm and predictable for a 30-minute feature session — one layout system, one discovery surface, queue-first steering, zero “why did the screen jump?” moments. - ---- - -## Audience - -**Who:** Solo developers and small teams who already use Corbits (or a peer agent CLI) to implement a discrete feature in a real repo. They live in the terminal, care about cost and safety, and will abandon a tool that fights them mid-stream. - -**What they do today instead:** - -- Stay in Claude Code / Gemini CLI / Amp / OpenCode despite wanting Corbits' director, permissions, and multi-agent model — because the TUI feels random. -- Or stay in Corbits and work around scroll bugs, mistyped interrupt vs queue, and “where is that shortcut?” by re-reading `/help` and still guessing wrong. - -**Rough scale that matters for v1:** the people already on Corbits + the next 50–200 serious CLI-agent users evaluating switches. Not “everyone who codes.” Not IDE-first users. Not remote-control power users yet. - -**What “good” means for them:** after half an hour they trust the chrome. They steered without panic. They approved what needed approving. They can scroll the log and copy something useful. They would start another session tomorrow without a workaround ritual. - ---- - -## The hook - -**Stop the random.** Today every surface reimplements layout; scroll/overflow/focus fight each other; mid-run Enter vs Alt+Enter is the opposite of the market leader people already internalized from Amp; discovery is split across slash, help overlay, and tribal knowledge. - -Rebuild on **one layout foundation (OpenTUI)** so: - -1. The transcript is the stable center; chrome is a fixed budget, not a free-for-all. -2. **Sending while the agent runs queues by default**; interrupt is deliberate and labeled. -3. **One command palette** is how you find everything (slash remains a power path, not the only path). -4. Keybindings are listed, consistent, and settings-overridable later — not re-bound per modal by accident. - -If those four land, the product stops feeling broken. Everything else is polish or adjacency. - ---- - -## Definition of success (observable) - -After launch of this rebuild wave, a human (not a unit test) can verify: - -1. **Calm session** — In a 30-minute “implement X” session with streaming + tool calls + at least one permission prompt, the transcript does not jump, clip, or leave dead empty regions when content grows or a modal opens/closes. -2. **Steering without panic** — Operator can type mid-run, hit Enter, and see the message **queued** (badge/count visible). Interrupt requires a distinct action they can name after one look at the hint line. -3. **Discovery without docs** — From a cold start, operator opens the command palette, finds “permissions” and “model,” runs both, and returns to the prompt without memorizing slash names. -4. **Chrome budget held** — On an 80×24 terminal, the event log always has a usable scroll region while idle and while active; optional panels (tasks, agents, hooks) never permanently steal the log without an obvious way back. -5. **No “mystery key”** — Ctrl+O (or the chosen palette chord) does one job. Expand-tool is not on the same chord. Help and palette do not contradict each other. -6. **Copy path exists** — Operator can get a past assistant reply or tool output onto the system clipboard without mouse-drag terminal selection (which we know is flaky across terminals). Does not need perfect multi-select yet. - -**Money / retention proxy (product, not finance):** existing Corbits users stop filing “TUI is broken / scroll is wrong / I interrupted by accident” as the top session-killers within one release cycle of the rebuild landing. If those tickets still dominate, the rebuild failed even if OpenTUI is “in.” - ---- - -## Good 30-minute session (felt experience) - -Operator opens `corbits "Add X to the API"` in a real repo. - -1. **0–1 min** — Screen paints once. Header says who is running (model/profile). Prompt is ready. No layout thrash on first stream token. -2. **1–10 min** — Agent streams; tool rows appear; log auto-follows unless operator scrolled up (then a clear “follow live” affordance). Cost or status is glanceable, not screaming. -3. **Mid-run** — Operator types “also add a test.” Enter → message sits in queue with a count. Agent finishes current step/turn and picks it up. If they meant “stop now,” they use the interrupt chord (documented on the hint line) and the run stops without ambiguity. -4. **Permission** — Modal overlays cleanly; log does not reflow into gibberish; Allow Once / Always works; focus returns to prompt. -5. **Orchestrator path (if on)** — Agents strip shows children; Enter-observe is intentional; Esc leaves. Parent log still makes sense. -6. **End** — They scroll back, copy a diff or answer, `/clear` or quit without residual alt-screen garbage. - -If any of those steps feel like fighting the terminal, v1 is not done. - ---- - -## Amp: steal / adapt / ignore - -| Amp idea | Verdict | Why | -|---|---|---| -| Own TUI foundation for no flicker, smooth stream scroll, mouse, overlays | **Steal (via OpenTUI, not a from-scratch framework)** | Root cause of Corbits pain is fragmented layout. OpenTUI is the chosen foundation; goal is Amp-class *behavior*, not Amp's private renderer. | -| Command palette (Ctrl+O) as primary discovery | **Steal** | Slash-only discovery fails cold users. Palette is how people find mode, permissions, IDE connect in Amp. Corbits today burns Ctrl+O on tool expand — wrong priority. | -| Queue-by-default while agent runs; interrupt is special | **Steal** | Corbits today: Enter interrupts, Alt+Enter queues — inverted vs Amp and vs operator expectation once they've used Amp. Inversion is a retention tax. | -| Steer (deliver at next tool boundary) vs hard interrupt | **Steal (locked)** | **Enter** = queue · **Alt+Enter** = steer at tool boundary · **Ctrl+C** = interrupt. Queue drain is tool-boundary, not only full idle. | - -| One polished visual system, not many themes | **Steal** | One default theme, restrained accents. Theme marketplace is vanity until chrome is stable. | -| Keybindings discoverable; customize via settings | **Adapt** | v1: discoverable + consistent + documented in palette/help. Settings remap is **later** unless it falls out of the keymap table for free. | -| Plugin UI primitives: notify / confirm / input / select (TUI+web) | **Adapt (TUI only in v1)** | Corbits plugins already exist. v1 needs a small, stable TUI dialog kit so plugins and core modals share one stack. Web mirror of those primitives is later. | -| Remote control / multi-thread / web mirror | **Ignore for this project** | Product adjacency. Does not fix “TUI feels broken.” Note in plan as non-goals so nobody sneaks them in. | -| Thread sharing, multiplayer, orbs, schedules | **Ignore** | Amp product surface, not Corbits' job this wave. | -| Amp agent modes low/medium/high/ultra | **Ignore as UI chrome** | Corbits already has model/profile/effort surfaces. Do not clone Amp's mode marketing. | -| Mouse support | **Adapt lightly** | Wheel scroll on the log is must-ship if OpenTUI makes it cheap. Click-to-focus everywhere is later. | -| Selection/copy still hard in Amp (community scar) | **Learn — ship a deliberate copy path** | Do not claim mouse selection is solved. Ship message/tool pick-to-clipboard (Corbits already has Alt+C direction — keep and make reliable). | -| Lag on huge threads (Amp scar) | **Learn — budget for long logs** | v1 must define a max comfortable log strategy (windowed render / virtualize / collapse old tools). “Render every historical line forever” is out. | -| Ink (Claude/Gemini) stay path | **Ignore as destination** | Peers on Ink are reference for UX patterns only; decision is OpenTUI. | -| pi-tui differential renderer | **Ignore as destination** | Interesting peer; not our stack. Steal ideas about differential paint only if OpenTUI already exposes them. | - ---- - -## Chrome budget (plain language) - -Think of the terminal as rent. The **event log pays the rent**. Everything else is a tenant with a lease limit. - -### Fixed tenants (always on, small) - -- **Header** — who/what session (profile, workflow chip if any). ~2 rows. -- **Prompt stack** — model/action bar + bordered input + status. ~5–6 rows total when idle. -- **Optional progress line** — only while the agent is active or a workflow chip needs it. Collapses when idle. - -Hard rule: on **24 rows**, the log still gets **at least ~12 rows** idle. On **80 cols**, no chrome wraps into a second unexpected block that steals log height without a single source of truth. - -### Optional tenants (toggle, never stack forever) - -- Tasks panel, hooks panel, agents strip expand, goal view, full-screen managers (`/model`, settings, permissions). -- **One primary overlay at a time** for blocking work (permission, operator question, full-screen picker). Non-blocking toasts/banners are thin and timed or dismissible. -- Opening B while A is open either replaces A or stacks with a single Esc path that always returns to the prompt. No orphan focus. - -### Layout principles - -1. **One geometry owner** — zones and row budgets live in one place; components consume budgets; they do not invent height. -2. **Transcript is the scroll root** — only one vertical scroll surface owns mouse wheel + page keys unless a modal explicitly captures them. -3. **Follow vs pinned** — auto-follow when at bottom; leave follow when operator scrolls up; show how to reattach. -4. **Streaming must not thrash** — appends paint smoothly; no full-redraw flicker; no jump-to-top on each token. -5. **Modals measure the remaining box** — they never assume full terminal height without subtracting chrome. -6. **Long content collapses by default** — tool output and huge assistant blobs start collapsed or windowed; expand is explicit (and not on the palette chord). - ---- - -## Interaction principles (plain language) - -1. **Queue is the default mid-run send.** Enter while running enqueues. Badge shows count. Operator can cancel/edit queued items (minimum: clear last / clear all). Delivery is at **tool boundary**. -2. **Steer is ASAP.** Alt+Enter injects at the next tool boundary (Amp-like). -3. **Interrupt is loud and rare.** **Ctrl+C** hard-stops the current run (does not quit the app without confirm if that already exists). Hint line always shows: *queue* / *steer* / *stop*. -3. **Palette is how you discover.** One chord opens a searchable list of commands, panels, and settings entry points. Slash commands remain for muscle memory and scripting muscle; every slash entry has a palette twin. -4. **One meaning per chord.** No dual-use Ctrl+O. Expand-tool moves. Help does not invent shortcuts the keymap does not honor. -5. **Focus is a stack.** Prompt → overlay → nested picker → back. Esc always means “pop one level” until prompt. Double-Esc clear-prompt only when already at prompt (document that). -6. **Permissions stay sacred.** Rebuild does not soften the gate. Faster chrome, same safety story. -7. **Orchestrator is optional chrome.** Single-agent sessions do not pay agents-strip tax. Orchestrator shows strip without stealing the log permanently. -8. **Copy is a product feature, not terminal luck.** Keyboard path to clipboard for messages and tool output. -9. **Performance is UX.** A 2-hour session with hundreds of tool rows must still scroll. If it lags, collapse/window — do not “fix later.” -10. **No theme zoo.** One visual system. Accessibility contrast over fashion. - -### Mid-run semantics change (explicit product call) - -| Today (Corbits) | Target | -|---|---| -| Enter = interrupt + send | Enter = **queue** (tool-boundary delivery) | -| Alt+Enter = queue | Alt+Enter = **steer** (ASAP tool boundary) | -| (no clear interrupt) | Ctrl+C = **interrupt** | -| Hint line documents both | Hint line + palette: queue / steer / interrupt | - -This is a breaking muscle-memory change for existing Corbits users. Ship it with a one-time banner or `/help` callout in the release notes — still worth it; Amp already trained the market. - ---- - -## In scope for v1 (must-ship) - -Smallest set that proves the hook: - -1. **OpenTUI foundation** for the main session shell: header, event log, prompt, status, modal host. -2. **Single layout/geometry system** (chrome budget enforced; one scroll root). -3. **Smooth streaming + follow/pin scroll behavior** under load (tools + tokens). -4. **Queue-by-default mid-run send** + visible queue badge + clear/cancel queue (Enter). -5. **Steer** via Alt+Enter at tool boundary + **Ctrl+C interrupt**, both on hint line and in palette. -6. **Command palette** as primary discovery (commands, panels, model/settings entry). -7. **Existing critical modals on the shared overlay host:** permission, operator ask, model/settings (or thin wrappers), exit confirm. -8. **Reliable copy path** for message / tool output (keyboard). -9. **Long-log strategy** so huge threads do not melt the TUI (windowing or collapse policy — pick one in the plan, ship it). -10. **Keymap table is truth** — help, palette, and actual handlers match; Ctrl+O is palette not expand-tool. -11. **Mouse wheel scroll** on the log (if OpenTUI supports it cleanly). -12. **Parity for session mode, auto mode toggle, agents strip observe/leave** — behavior preserved, chrome cleaned. - -### Explicitly out of scope (later / adjacency) - -- Remote control from web -- Web UI mirror of plugin dialogs -- Multiplayer / thread sharing -- Full keymap customization UI (unless free) -- Theme marketplace / multiple official themes -- Perfect terminal mouse text selection -- Amp-style queue item reorder + selective steer (unless default queue is insufficient) -- Rebuild of exec/non-TUI path (leave stable; only share types/events if needed) -- IDE extensions -- Rewriting the director, permissions policy, or agent loop “while we're here” -- New product surfaces (schedules, orbs-equivalents, etc.) - -If a planning doc starts estimating remote control, stop and re-read this section. - ---- - -## Constraints - -- **Foundation decision:** OpenTUI (OpenCode's stack). Do not reopen Ink vs OpenTUI in the plan unless OpenTUI is proven unblockable — then escalate as a risk, not a casual pivot. -- **Product identity stays Corbits:** deterministic director, permission gate, goals/tasks, orchestrator vs single — UI serves those; it does not become “Amp skin.” -- **Breaking interaction change is allowed** for queue-default; document it. -- **Terminal reality:** 80×24 minimum; 120×40 common; tmux users exist (Shift+Enter newline may not work — keep a newline chord). -- **No code in this brief; no Linear expansion from this brief alone** — planning agent turns this into work. - ---- - -## Open risks and unresolved decisions - -| Risk / decision | Status (2026-08-05) | -|---|---| -| Exact interrupt chord | **LOCKED:** Ctrl+C = interrupt · Enter = queue · Alt+Enter = steer | -| Queue drain boundary | **LOCKED:** tool boundary (not only full idle) | -| Palette chord | **LOCKED:** Ctrl+O = palette; move expand-tool | -| Long-log policy | **LOCKED ideal:** viewport working set; scroll up collapses off-bottom / shows into-top; numeric N in CL-5399 | -| OpenTUI maturity / gaps | Spike decides go/no-go (CL-5365); binding ADR (CL-5366) | -| Existing user muscle memory | **LOCKED:** no legacy Enter-interrupts toggle; educate once | -| Platform support | **LOCKED:** macOS #1, Linux #2, Windows non-blocking | -| Cutover | **LOCKED:** branch hard cutover; no dual-release; scrap branch if epic fails | -| Plugin dialog kit depth | Cap at four primitives for v1 (notify/confirm/input/select) — still fine | -| Agents strip + goal/tasks density | Chrome budget in plan §5; constitution finishes numbers | - ---- - -## Acceptance checks (human, no code reading) - -Run on a real repo, real model, 80×24 and a larger terminal. - -### A. Layout calm - -- [ ] Start session; stream 500+ tokens; log follows bottom; no flicker/tear. -- [ ] Scroll up mid-stream; view stays pinned; a clear control resumes follow. -- [ ] Open permission modal; dismiss; log height and content position remain sane. -- [ ] Toggle tasks panel open/closed; log remains usable; Esc returns focus to prompt. - -### B. Queue / steer / interrupt - -- [ ] While agent runs, type a message and press Enter → queued (count ≥ 1), agent does **not** stop. -- [ ] Queued message delivers at next **tool boundary** without re-typing. -- [ ] Alt+Enter steers ASAP at tool boundary. -- [ ] Ctrl+C interrupts the run; agent stops; no silent no-op. -- [ ] Hint line names queue, steer, and stop. -- [ ] Interrupt chord stops the run; partial work does not leave the UI wedged. -- [ ] Hint line names both actions in plain words. - -### C. Discovery - -- [ ] Cold user opens palette, types “perm”, opens permissions manager, Esc to prompt. -- [ ] Palette lists model/settings and at least the built-in slash commands. -- [ ] `/help` (or help command) does not advertise chords the app ignores. - -### D. Copy and length - -- [ ] Copy an assistant message to clipboard via keyboard path; paste elsewhere succeeds. -- [ ] Session with many tool calls still scrolls; expand/collapse tool output works; palette chord is not required for expand. - -### E. Orchestrator (if enabled) - -- [ ] Child agents appear on strip; observe session; Esc returns to parent; parent log still coherent. - -### F. Regression sacredness - -- [ ] Permission gate still blocks a consequential action in ask mode. -- [ ] SHIFT+TAB still toggles auto mode with a visible cue. -- [ ] Quit path still confirms; no stuck alt-screen on exit. - -All boxes checked ⇒ rebuild wave is shippable. Any open box ⇒ not done, regardless of “migrated to OpenTUI.” - ---- - -## What the plan document must force us to answer - -**Suggested plan title:** `TUI rebuild: OpenTUI shell, queue-first steering, command palette` - -**Sections the plan is not allowed to skip:** - -1. **Product non-goals** — Copy the out-of-scope list; add any new temptations and kill them. -2. **Interaction contract** — Final table: Enter / newline / queue / interrupt / Esc / palette chord. No “TBD” after spike. -3. **Chrome budget table** — Row rents per zone at 24 and 40 rows; which panels are mutually exclusive. -4. **Scroll & streaming model** — Follow/pin rules; who owns the wheel; what happens on modal open. -5. **Long-log strategy** — Chosen algorithm + failure mode when history is huge. -6. **Palette IA** — Command inventory (built-ins + how plugins register); search behavior; relationship to slash. -7. **Overlay host** — Stack rules; which flows are full-screen vs modal vs toast. -8. **Migration slices** — Vertical slices that each leave a runnable TUI (not a big-bang dark launch). First slice must prove scroll+stream calm. -9. **Spike results** — OpenTUI: streaming, mouse, overlays, performance, packaging. Go/no-go criteria. -10. **Breaking-change comms** — Queue-default Enter; keymap moves; release note copy. -11. **Acceptance** — Paste the human checklist; map each item to an owner and a manual test script. -12. **Explicit deferrals** — Remote control, web mirror, keymap editor, selective steer — with “revisit when” triggers. - -A plan that only says “port components to OpenTUI” is not a plan. A plan that answers the twelve is. - ---- - -## Glossary - -| Term | Meaning in this brief | -|---|---| -| **Chrome** | Everything that is not the scrollable event log/transcript (header, prompt, status, strips, panels, modals). | -| **Chrome budget** | Fixed row/column rents so the log always has a usable region. | -| **Event log / transcript** | The main scrollable history of user, assistant, tools, and system events. | -| **Queue** | Operator messages held while the agent is running, delivered at a defined boundary without stopping current work. | -| **Interrupt** | Hard stop of the current agent run so the operator can take over immediately. | -| **Steer** | (Amp) deliver a queued message at the next opportunity before full idle; Corbits v1 may only ship queue+interrupt unless plan expands. | -| **Command palette** | Searchable overlay of actions/commands; primary discovery surface. | -| **Overlay host** | Single stack that owns modals/full-screen managers and focus return. | -| **Follow vs pin** | Auto-scroll with new output vs user-scrolled frozen viewport. | -| **OpenTUI** | Chosen TUI foundation (OpenCode stack) replacing the current Ink-centric shell. | -| **Session mode** | `single` vs `orchestrator` — product setting; UI must respect both. | -| **Agents strip** | Orchestrator UI for child sessions (observe/leave). | -| **Copy path** | First-class keyboard flow to put content on the system clipboard. | -| **Long-log strategy** | How the UI stays responsive as history grows (window/collapse/cap). | - ---- - -## Bottom line - -Ship a TUI people stop apologizing for. One geometry owner, queue-first send, palette discovery, interrupt on purpose, copy that works, logs that do not melt. OpenTUI is the foundation, not the product. Remote control and web mirrors do not pay the rent until the main stage is solid. diff --git a/docs/adr/opentui-binding.md b/docs/adr/opentui-binding.md deleted file mode 100644 index 41a2f318d..000000000 --- a/docs/adr/opentui-binding.md +++ /dev/null @@ -1,125 +0,0 @@ -# ADR: OpenTUI binding — Solid + core + keymap - -**Status:** Accepted -**Date:** 2026-08-05 -**Evidence:** `docs/plans/opentui-spike-report.md` -**Spike packages:** `@opentui/core@0.5.1`, `@opentui/solid@0.5.1` (Bun 1.3.14, darwin arm64) - ---- - -## Decision - -**Use Solid + core + keymap** as the OpenTUI binding stack for the Corbits TUI migration: - -| Layer | Package / API | Role | -|---|---|---| -| **UI composition** | `@opentui/solid` | Declarative shell, chrome zones, stream surfaces | -| **Imperative control** | `@opentui/core` class API (`*Renderable`) | Scroll, focus leases, headless test renderer | -| **Key chords** | `@opentui/keymap` (next packaging step) | Host mapping for Enter / Alt+Enter / Ctrl+C and app chords | - -Do **not** adopt React/`@opentui/react` as the production binding. Do **not** ship dual long-term bindings (Solid and React). Core-only without a component binding is rejected for application UI. - ---- - -## Context - -Corbits Code’s interactive surface is Ink/React today. The layout/scroll platform plan locks OpenTUI as the substrate after a GO spike. The spike proved install, native FFI render, flex mini-shell, sticky ScrollBox, focus leases, and distinct key shapes under Bun. Binding choice was explicitly deferred to this ADR. - -Constraints that shape the choice: - -- Migration is a **branch hard cutover** — no dual-release Ink + OpenTUI, and no dual OpenTUI component bindings long-term. -- Product needs dense streaming UIs (transcript working set, sticky follow, scroll-up pin) and reliable focus leases (prompt vs scroll). -- Queue / steer / interrupt chords are locked product behavior: Enter = queue, Alt+Enter = steer, Ctrl+C = interrupt. Binding must preserve distinguishable key events. -- OpenCode (peer production stack) uses Solid on OpenTUI; aligning reduces unknown surface area. -- Spike did **not** exercise the React binding; any React claim would be unevidenced. - ---- - -## Options - -### A. Solid + core + keymap (chosen) - -**Spike evidence** - -- `@opentui/solid` installs and exports `render` / `testRender`. -- Core mini-shell + headless suite green (16/16): sticky stream, scroll-up pin, return-to-bottom, focus leases, Enter submit path. -- Key shapes proven on core `keyInput`: Enter = `return`; Alt+Enter = `return` + `meta`; Ctrl+C = `c` + `ctrl`. -- Class API reliable for imperative scroll/focus; construct/VNode path broke `scrollTop` once — prefer class API for those leases. -- Aligns with OpenCode’s production peer stack. -- Install warned `incorrect peer dependency solid-js@1.9.14` but resolved; pin a compatible peer when productionizing. - -**Tradeoffs** - -- Team leaves Ink/React muscle memory for application UI (acceptable under hard cutover). -- Needs `jsxImportSource: "@opentui/solid"` and Bun preload (`@opentui/solid/preload` via `bunfig.toml`). -- `@opentui/keymap` was **not** installed in the spike; treat as the next packaging/wiring item when host chords land — not a reason to pick React or core-only. - -### B. React + core (`@opentui/react`) - -**Spike evidence** - -- **Not exercised.** Docs-only path: `createRoot(renderer)` + `@opentui/react` jsxImportSource. -- Closest to current Corbits Ink/React habits; higher render-cost risk than Solid for dense streams (spike note, not measured here). - -**Why rejected** - -- No spike pass/fail for layout, sticky scroll, focus, or keys under React. -- Choosing React would reopen the binding without evidence and diverge from the OpenCode-aligned path the GO verdict already validated for Solid. -- Revisit only if a future spike proves React parity and team velocity clearly favors React reuse; do not block migration on that sample. - -### C. Core-only (no Solid/React component binding) - -**Spike evidence** - -- Core alone proved the mini shell and all headless checkpoints via class renderables. - -**Why rejected for application UI** - -- Application shell, chrome zones, and stream surfaces need a component model and fine-grained updates; hand-building the full product UI on bare renderables is a maintainability dead end. -- Core remains **required** underneath Solid for scroll/focus leases and headless tests — core-only is wrong as the sole application binding, not as a dependency. - ---- - -## Consequences - -### For implementers (migration branch) - -1. **Default stack:** compose UI in Solid; reach for `@opentui/core` class renderables when controlling scroll position, sticky pause/resume, and focus leases. -2. **Package setup (migration branch only — not root until cutover policy says so):** - - `@opentui/core`, `@opentui/solid`, and (when wiring chords) `@opentui/keymap` - - Compatible `solid-js` peer pin (resolve the 1.9.x peer warning at productionize time) -3. **TypeScript / Bun:** - - `jsxImportSource: "@opentui/solid"` - - Preload `@opentui/solid/preload` in `bunfig.toml` for the migration package/app entry -4. **Imperative scroll convention:** use the class API (`ScrollBoxRenderable`, etc.). Avoid construct-factory / VNode proxy paths for `scrollTop` and related control after the spike regression. -5. **Keys:** identity for Enter is `return` (not `enter`) on raw `keyInput`. Alt+Enter = `return` with `meta` or `option`. Ctrl+C = `c` with `ctrl`. Prefer keymap for host chords once packaged; keep Alt+Enter on the key path so focused `InputRenderable` does not treat it as submit. -6. **Tests:** continue using core headless `createTestRenderer` for layout/key regression without a host TTY. - -### Non-goals - -- No dual long-term bindings (Solid application UI and React application UI). -- No production React binding without a new spike that exercises layout, sticky scroll, focus, and keys. -- No OpenTUI install at repo root as part of this decision record; packaging lands on the migration branch per cutover plan. -- No Windows or Linux/musl certification in this ADR (spike residual risk; CI matrix later). - -### Skills / docs impact - -- Migration and TUI skills should teach Solid + OpenTUI patterns, not Ink/React component APIs, for new shell work. -- Layout constitution and interaction contract remain binding for geometry and chords; this ADR only locks the renderer binding stack. - -### Residual risks (from spike, not reopened here) - -- `@opentui/keymap` not yet installed or proven in-tree. -- Solid peer pin warning. -- Native dylib packaging for standalone/Homebrew and CI optional-deps matrix (darwin, linux glibc + musl). -- Multi-hour stream performance not measured. - ---- - -## References - -- Spike report (GO): `docs/plans/opentui-spike-report.md` -- Layout/scroll platform plan: `docs/plans/tui-layout-scroll-platform.md` -- Layout constitution: `docs/tui-layout-constitution.md` -- Interaction contract: `docs/tui-interaction-contract.md` -- Product brief: `briefs/tui-rebuild-opentui.md` diff --git a/docs/audit-fleet-prompt.md b/docs/audit-fleet-prompt.md deleted file mode 100644 index 6428a44a3..000000000 --- a/docs/audit-fleet-prompt.md +++ /dev/null @@ -1,128 +0,0 @@ -# Audit fleet prompt - -Paste as the task for an orchestrating agent. It spawns a review panel over the -codebase's own capabilities and reports back. Adjust the scope line before use. - ---- - -You are orchestrating a standing audit of Corbits Code's own capabilities. The -goal is not to ship a feature — it is to find out what is quietly wrong, what is -about to break, and what has drifted from what we believe about it. - -**Scope for this run:** - -## Ground rules for every agent you spawn - -Put these in each agent's prompt verbatim. They are not boilerplate; each one -was bought with real time. - -**Read-only on git.** Do not run `git checkout`, `switch`, `stash`, `reset`, -`clean`, `add`, `commit`, or `push`. Other agents share this working tree, and a -`stash` has stranded work here before. Read history with `git log` / `git show -:`. - -**Verify by running, not by reading.** A green suite is not evidence. On this -codebase, every genuine defect of the last cycle was found by running the app or -capturing the pty byte stream, and every false finding came from trusting a -document. If a claim can be executed, execute it. - -**Distinguish VERIFIED from SUSPECT, per finding.** A suspicion clearly labelled -is useful. A suspicion stated as fact wastes a day and burns the reviewer's -credibility for the findings that were real. - -**Do not trust documents, including ours.** A readiness doc in this repo -recently produced four false blocking findings and a formal do-not-ship verdict; -all four had been fixed months earlier. If a doc and the code disagree, the code -wins, and the doc is itself a finding. - -**Assume tests may guard the wrong path.** We shipped a bug where the test drove -`pushToolCall`/`pushToolResult` while the app ran `applyToolResult` — the test -asserted correct behavior on code that never executes. When a test covers a -finding you believe is real, check which code path it actually exercises before -concluding the finding is wrong. - -**Watch for silent no-match.** Bindings, dispatch tables, and event channels -that fail by doing nothing are this codebase's most common defect shape. We have -found: four emitted event channels with zero listeners, five help rows -describing behavior that did not exist, and a keybinding that never matched. -None of them errored. None were caught by tests. - -## The panel — spawn these in parallel - -Give each its own prompt. Do not let them duplicate scope. - -**greybeard — architecture and the long term.** Is each abstraction sound, or -does it encode today's decisions so tightly that the next change fights it? -Where is ownership of a constraint split across layers, so an invariant is -stated in one place and violated in another? What will hurt in six months? -Explicitly ask it to separate "must fix now" from "will hurt later" — and to -argue against large refactors close to a release, since a rewrite of the -most-exercised file is how a good release becomes a bad week. - -**critique — correctness and completeness.** Find defects; do not fix them. For -each: file, line, what breaks, and the concrete input or sequence that triggers -it. Point it at error paths, disposal, double-dispose, resize mid-overlay, a -source throwing mid-render, and anything whose state is valid mid-stream but -wrong on screen. - -**neckbeard — hygiene, and refactor proposals.** This is the agent that files -refactor issues. Let it be pedantic; in terminal and permission code the fiddly -details *are* the product. Unicode width, escape sequences, off-by-ones, type -escape hatches, boundary validation, naming and comment drift. **Explicitly -authorize it to propose refactors as Linear issues** rather than only complain — -one issue per proposal, with the seam it would cut along, what it buys, and what -it risks. Require it to separate genuine defects from taste, and tell it not to -suggest rewriting anything in Rust. - -**gaasbot (CTO) — risk and sequencing.** Not a code review. Given what the others -find, what actually blocks a release, what ships with a note, and what is filed? -Ask it directly what we are most likely getting wrong that nobody raised. Tell -it plainly that you would rather hear "do not ship" now than at minute fifty-five. - -**bruckheimer — the person using it.** Not a code review either. Can a new user -get through the first ninety seconds? Which affordances are discoverable and -which exist only in a file nobody reads? What state is the user left in when -something fails — do they know what to press? Read the copy actually shown on -screen and name specific strings that should change and what they should say. - -## What each agent must return - -- Findings ranked: blocking, should-fix, file-for-later. -- Each concrete enough for another agent to act on with no follow-up questions. -- Evidence for anything claimed as verified — the command run, the bytes - captured, the frame rendered. -- An explicit statement of what it did **not** cover, so gaps are visible rather - than assumed closed. - -Tell them a short honest review beats a padded one, and that "this is genuinely -fine" is a useful finding when it is true. - -## Your job as orchestrator - -1. **Do not relay findings unverified.** Check the load-bearing ones yourself - before acting. A CTO-level verdict here rested on a stale doc; four of its - five blocking claims fell apart under a five-minute grep. -2. **Dedupe across agents** — the same defect will arrive under different names. -3. **File what is not being fixed now**, with enough context that the next - person does not re-derive it. An unfiled finding is a lost finding. -4. **Route fixes to agents that own disjoint files**, and say who owns what. Two - agents editing one file will silently overwrite each other; that has already - cost a re-apply here. -5. **Report honestly.** If the suite is red, say so with the number. If a fix is - mitigation rather than a fix, say which. If you were wrong earlier, correct it - in a sentence and move on. - -## Known traps in this codebase - -Include whichever apply to the scope: - -- `bun test` runs `vendor/intx-inference`; CI runs `bun test ./src ./tests - ./evals`. Quote the scoped number or you will report failures that do not gate. -- Roughly forty git worktrees exist; worktree-enumerating tests fail - environmentally because of them. -- The OpenTUI headless test renderer cannot see paint, real modifier reporting, - the system clipboard, or terminal-owned selection. Whole defect classes are - invisible to it by construction. -- `Renderable.destroy()` frees only its own buffer and detaches children without - destroying them. Anything dropping a subtree must destroy it recursively. diff --git a/docs/perftrace-attribution-guide.md b/docs/perftrace-attribution-guide.md deleted file mode 100644 index 8f822395b..000000000 --- a/docs/perftrace-attribution-guide.md +++ /dev/null @@ -1,189 +0,0 @@ -# PerfTrace attribution guide - -How to capture a slow session, dump local spans, and run the offline attribution -report. No OTEL collector, no PostHog, no network. - -See also: [`PERFTRACE.md`](./PERFTRACE.md) for the span model, dump schema, and -`jq` recipes. - -## Why this exists - -When a session feels slow, the first question is **where the wall time went**: - -| Category | Meaning | -|---|---| -| `inference` | Model call wall (`inference` spans). Nested `inference.ttft` vs `inference.stream` show wait-for-first-token vs rest of stream. | -| `tools` | Tool invocations under the turn. | -| `permission.wait` | Ask-gate / approval idle time (when instrumented). | -| `subagent` | Child agent lifetimes (fanout cost). | -| `other` | Turn wall not covered by the above — scheduling, TUI, un-instrumented work, gaps between phases. | - -Shares are **exclusive** over turn wall. For **completed** turns, wall is -`end − start`. For **open** (still-running) turns — including mid-stall dumps — -wall is estimated as `max(completed-descendant endNs) − turn.startNs` so shares -stay meaningful, and the report lists **open phase names** (e.g. -`inference.stream`, `turn`) so completed-only shares are not read as a full -stall diagnosis. - -Nested exclusive children under an exclusive parent (e.g. `tool` / -`inference` under `subagent`) count only toward the parent exclusive bucket — -they are not double-counted. Nested TTFT/stream and `adapter.transport` are -diagnostic splits (they are not added on top of `inference` in the exclusive -table). TTFT/stream shares use **(ttft + stream)** as the denominator, not -inference wall. - - -## Capture a real slow session - -1. Prefer a repro that exercises the pain: high reasoning, several tools, and - (if relevant) subagents or permission prompts. -2. Run Corbits Code normally. PerfTrace is always-on in-process; there is no - settings toggle. -3. When the session stalls or finishes slowly, dump the ring next to session - artifacts. - -```ts -import { snapshot } from "../src/perf/index.js"; -import { dumpSpans } from "../src/perf/dump.js"; - -const path = await dumpSpans(snapshot(), { - dir: "~/.corbits/projects//", - sessionId: "", -}); -// → ~/.corbits/projects///perftrace-.json -``` - -The dump is privacy-strict (allowlisted tags only). Safe to keep offline or -share with teammates without prompts/paths. - -## Run the attribution report - -From a local dump file alone: - -```bash -bun scripts/perf-report.ts ~/.corbits/projects///perftrace-.json -``` - -Machine-readable JSON: - -```bash -bun scripts/perf-report.ts --json ~/.corbits/projects///perftrace-.json -``` - -Golden multi-tool demo (no dump file needed — uses -`src/perf/fixtures/multi-tool-turn.ts`): - -```bash -bun scripts/perf-report.ts --fixture -``` - -Example fixture output (fixture ns values are tiny — formatter prints sub-ms): - -``` -PerfTrace attribution report -─────────────────────────── -Session wall: 0.005ms turns=1 (completed=1) - -Exclusive phase shares (of session wall): - inference 40.0% 0.002ms n=1 - tools 24.0% 0.001ms n=2 - permission.wait 8.0% 0.000ms - subagent 0.0% 0ms - other 28.0% 0.001ms -... -``` - - -## How to read the report - -1. **Session exclusive shares** — which bucket ate the turn wall. A large - `inference` share with high `ttft` points at model queueing / cold start. A - large `tools` share with high `n=` points at tool work. A large - `permission.wait` share is human/ask-gate idle, not model or tool code. -2. **Open / incomplete** — if the report says `Open (incomplete)` and lists - still-running phases, exclusive shares only cover completed descendants. - Treat open phase names as the hang candidates; do not conclude from the - exclusive table alone. -3. **`other` large** — either real un-instrumented cost (TUI, scheduling) or - gaps between instrumented phases. If `other` dominates a pain session, add - spans before optimizing transport. -4. **TTFT vs stream** — of `ttft + stream` only (not inference wall). High TTFT - share → time-to-first-token problem. High stream share → long generation or - slow token delivery. -5. **Transport signal** — `adapter.transport / inference`. When transport is a - large fraction of inference wall, prioritize transport work (WebSocket / - incremental input). When it is small, transport is not the bottleneck. -6. **Per-turn rows** — find the outlier turn when the session average looks fine - but one turn felt stuck. Open turns print `open phases:` explicitly. -7. **Subagent count + share** — fanout cost. High subagent share means child - agents as a whole (nested tools/inference under the subagent are inside that - bucket, not double-counted as parent tools/inference). - -### What “large transport share” looks like - -| transportShareOfInference | Reading | -|---|---| -| ≈ 0 or missing | Adapter did not emit `adapter.transport`, or transport was negligible. Do not prioritize WebSocket/incremental input on this evidence alone. | -| Low (e.g. < 10–15%) | Most inference wall is model/server time, not client transport. Prefer model/TTFT or tool work. | -| High (e.g. > 25–30% of inference, sustained across turns) | Client transport is a meaningful slice of inference wall — candidate for WebSocket / incremental input priority. | - -Always pair with absolute ms: a 40% share of a 50ms inference is noise; 40% of a -8s inference is a product decision. - -## Decision note template (transport prioritization) - -Copy into a Linear issue or PR when a pain dump suggests transport investment. - -```markdown -## Decision: WebSocket / incremental input priority? - -**Session / dump:** -**Report command:** `bun scripts/perf-report.ts ` - -### Evidence -- Session wall: -- Exclusive shares: inference <%> · tools <%> · permission.wait <%> · subagent <%> · other <%> -- TTFT share of (ttft+stream): <%> -- Stream share of (ttft+stream): <%> -- `adapter.transport` ns: · share of inference: <%> -- Turns examined: ; outlier turn id: - -### Reading -- [ ] Transport share is **high** and absolute transport ms is user-visible - → prioritize WebSocket / incremental input (or adapter transport work). -- [ ] Transport share is **low / missing**; inference TTFT or tools dominate - → do **not** prioritize transport; focus on . -- [ ] `other` or missing instrumentation dominates - → instrument first; decide after a second dump. - -### Decision -- Priority: transport work this cycle -- Owner: -- Follow-up: -``` - -## API (programmatic) - -```ts -import { - attributionFromSpans, - attributionFromDump, - formatAttributionReport, -} from "../src/perf/attribution-report.js"; -import { snapshot } from "../src/perf/index.js"; - -const report = attributionFromSpans(snapshot()); -console.log(formatAttributionReport(report)); -// or: attributionFromDump(JSON.parse(await readFile(path, "utf8"))) -``` - -Pure functions — safe in tests and evals. The multi-tool golden fixture locks -expected ns values in `src/perf/fixtures/multi-tool-turn.ts` and -`src/perf/attribution-report.test.ts`. - -## Related - -- `src/perf/rollup.ts` — phase / turn / session totals -- `src/perf/dump.ts` — `dumpSpans` / `buildDump` -- `src/perf/attribution-report.ts` — exclusive shares + formatter -- `scripts/perf-report.ts` — CLI entrypoint diff --git a/docs/tui-opentui-packaging.md b/docs/tui-opentui-packaging.md deleted file mode 100644 index 195635574..000000000 --- a/docs/tui-opentui-packaging.md +++ /dev/null @@ -1,242 +0,0 @@ -# OpenTUI install / CI / packaging plan - -**Status:** plan (docs only; CI not yet changed) -**Spike verdict:** GO -**Spike packages:** `@opentui/core@0.5.1`, `@opentui/solid@0.5.1` -**Runtime:** Bun (`package.json` engines: `bun >= 1.2`) -**Platform priority:** macOS #1 · Linux #2 · Windows non-blocking - -This document answers how OpenTUI natives install for contributors and CI, which matrix cells are required, and how to diagnose missing/wrong-arch binaries. Root `package.json` on `migration/opentui-tui` now depends on OpenTUI (see §1); CI configs are still unchanged. - ---- - -## 1. Dependency model - -OpenTUI is a TypeScript API over a **native Zig core** loaded via Bun FFI (or Node ≥ 26.4.0 + `--experimental-ffi`; Corbits is Bun-first). - -| Package | Role | -|---|---| -| `@opentui/core` | Public API + runtime; declares optional platform packages | -| `@opentui/core--[ -musl]` | Prebuilt native library for one OS/CPU (optional dependency) | -| `@opentui/solid` | Solid binding (spike recommendation; peer `solid-js`) | -| `@opentui/keymap` | Host key chords — not installed in spike; wire later | - -### Optional platform packages (`@opentui/core@0.5.1`) - -Package managers install only the optional dep matching the host OS/CPU: - -| Package | OS | Arch | libc | Native file | -|---|---|---|---|---| -| `@opentui/core-darwin-arm64` | macOS | arm64 | — | `libopentui.dylib` | -| `@opentui/core-darwin-x64` | macOS | x64 | — | `libopentui.dylib` | -| `@opentui/core-linux-x64` | Linux | x64 | glibc (default) | `libopentui.so` | -| `@opentui/core-linux-arm64` | Linux | arm64 | glibc (default) | `libopentui.so` | -| `@opentui/core-linux-x64-musl` | Linux | x64 | musl | `libopentui.so` | -| `@opentui/core-linux-arm64-musl` | Linux | arm64 | musl | `libopentui.so` | -| `@opentui/core-win32-x64` | Windows | x64 | — | `opentui.dll` | -| `@opentui/core-win32-arm64` | Windows | arm64 | — | `opentui.dll` | - -Resolution (Bun path): `@opentui/core` dynamically imports the matching `@opentui/core--` package; that package’s entry re-exports the native file with `{ type: "file" }`. - -**Linux musl:** set `OPENTUI_LIBC=musl` so the runtime selects `*-musl` packages. Unset / empty / `glibc` → glibc packages. Other values throw. - -**Supported arches only:** `arm64` and `x64`. Other CPUs throw `Unsupported OpenTUI Node asset target`. - ---- - -## 2. Contributor install (clean machine) - -On **main**, root Corbits does not depend on OpenTUI. On `migration/opentui-tui`, root depends on OpenTUI and `bun install` pulls the host native package. The isolated spike tree under `tmp/opentui-spike` remains useful for re-running spike scripts. - -### Prerequisites - -- **Bun** ≥ 1.2 (spike ran on Bun 1.3.14) -- macOS or Linux, arm64 or x64 -- Network access to the registry that serves `@opentui/*` (npm) - -### A. Re-run the spike (today — recommended) - -```bash -# From repo root -cd tmp/opentui-spike -bun install -bun run verify # headless: native FFI + layout/keys (primary evidence) -bun run start # interactive mini shell (real TTY only) -``` - -Clean machine without the spike tree: - -```bash -mkdir -p tmp/opentui-spike && cd tmp/opentui-spike -bun init -y -bun add @opentui/core@0.5.1 -# Optional Solid path (spike recommendation): -bun add @opentui/solid@0.5.1 solid-js -``` - -**macOS check (arm64 example):** - -```bash -ls node_modules/@opentui/core-darwin-arm64/libopentui.dylib -file node_modules/@opentui/core-darwin-arm64/libopentui.dylib -# expect: Mach-O 64-bit dynamically linked shared library arm64 -``` - -**Linux check (glibc x64 example):** - -```bash -ls node_modules/@opentui/core-linux-x64/libopentui.so -file node_modules/@opentui/core-linux-x64/libopentui.so -# expect: ELF 64-bit LSB shared object, x86-64 -``` - -**Linux musl (Alpine / musl hosts):** - -```bash -export OPENTUI_LIBC=musl -bun install # reinstall so optional musl package can resolve if needed -ls node_modules/@opentui/core-linux-*-musl/libopentui.so -``` - -### B. Production install (migration branch — future) - -When OpenTUI is added to the app (not this doc’s job): - -```bash -# From repo root on the migration branch only -bun install --frozen-lockfile -# Assert native package present for this host (see §5) -bun run test:tui # or whatever harness lands with the platform -``` - -OpenTUI has since shipped: root `package.json` on `main` depends on `@opentui/core@0.5.1`, `@opentui/solid@0.5.1`, `@opentui/keymap@0.5.1`, and `solid-js@1.9.14`, and `src/tui/` is the shipping shell (see `docs/TUI.md`). - -### Solid contributor notes - -- `jsxImportSource: "@opentui/solid"` -- `bunfig.toml` preload: `@opentui/solid/preload` -- Spike saw `incorrect peer dependency solid-js@1.9.14` — package still resolved; pin a compatible peer when productionizing. - -### Node (not primary) - -Corbits is Bun-first. Node consumers need Node ≥ 26.4.0 and `--experimental-ffi`. Prefer Bun for all contributor and CI paths. - ---- - -## 3. CI matrix plan - -**Docs only — do not edit `.github` in this task.** Current CI is a single `ubuntu-latest` job (workflow_dispatch only while org billing is constrained). When OpenTUI lands on a branch, expand as below. - -### Priority - -| Priority | OS | Required for merge? | -|---|---|---| -| #1 | macOS | **Yes** (primary daily-dev surface) | -| #2 | Linux | **Yes** (primary CI / server / contributor) | -| — | Windows | **No** (non-blocking; optional smoke) | - -### Recommended matrix cells - -| Cell | Runner (example) | Arch | libc | Required | Assert native package | -|---|---|---|---|---|---| -| macOS arm64 | `macos-14` / `macos-latest` | arm64 | — | **Yes** | `@opentui/core-darwin-arm64` + `libopentui.dylib` | -| macOS x64 | `macos-13` (Intel) if available | x64 | — | Preferred | `@opentui/core-darwin-x64` | -| Linux glibc x64 | `ubuntu-latest` | x64 | glibc | **Yes** | `@opentui/core-linux-x64` + `libopentui.so` | -| Linux glibc arm64 | `ubuntu-24.04-arm` (or equiv.) | arm64 | glibc | Preferred | `@opentui/core-linux-arm64` | -| Linux musl x64 | Alpine container / musl job | x64 | musl (`OPENTUI_LIBC=musl`) | Preferred | `@opentui/core-linux-x64-musl` | -| Linux musl arm64 | Alpine arm64 | arm64 | musl | Nice-to-have | `@opentui/core-linux-arm64-musl` | -| Windows x64 | `windows-latest` | x64 | — | Optional | `@opentui/core-win32-x64` + `opentui.dll` | -| Windows arm64 | when runner exists | arm64 | — | Optional | `@opentui/core-win32-arm64` | - -**Minimum gate for OpenTUI platform PRs:** macOS arm64 **and** Linux glibc x64 both green (install + headless renderer tests). Musl and Intel Mac are strongly recommended before Bar milestone; Windows does not block merge. - -### Per-cell install assertion (sketch) - -```bash -# After bun install --frozen-lockfile -node_or_bun_script that: - 1. Resolves process.platform / process.arch (/ OPENTUI_LIBC) - 2. Confirms node_modules/@opentui/core-/ exists - 3. Confirms native file present and non-empty - 4. Imports createTestRenderer from @opentui/core/testing and paints once -``` - -Fail the job if the optional package is missing (silent optional-dep skip is the main failure mode). - -### Bun version - -Pin Bun in CI to a version ≥ 1.2 that matches contributor engines (spike: 1.3.x). Avoid mixing Node-only install paths for OpenTUI jobs. - -### TODO (implement later — not this task) - -- Add matrix jobs to `.github/workflows` when org billing allows normal CI again. -- Publish a small `scripts/assert-opentui-native.ts` (or test) used by every matrix cell. -- Include native assets in any `bun build --compile` / Homebrew release recipe (dylib/so/dll must ship or resolve at install). - ---- - -## 4. Failure modes - -| Symptom | Likely cause | Fix | -|---|---|---| -| `OpenTUI is not supported on the current platform: @opentui/core-…` | Optional platform package missing from `node_modules` (install skipped, offline cache, or `optional=false`) | Re-run `bun install` without disabling optional deps; check registry access; confirm OS/arch match | -| Wrong arch binary / dyld or ELF class error | Cross-copied `node_modules`, Rosetta confusion, or wrong package forced | Delete `node_modules` + lock local cache for that package; reinstall on the target machine | -| Linux loads glibc `.so` on Alpine (or reverse) | `OPENTUI_LIBC` unset on musl, or set on glibc | Set `OPENTUI_LIBC=musl` on musl hosts; leave unset on glibc | -| `Unsupported OpenTUI Node asset target: …` | Arch outside arm64/x64 (e.g. ia32, riscv) | Unsupported — use arm64/x64 host | -| Native import works in Bun, fails in Node | Node < 26.4 or missing `--experimental-ffi` | Use Bun; or upgrade Node + flag | -| Install peer warning on `solid-js` | Solid peer pin mismatch | Pin compatible `solid-js` when productionizing; does not block core FFI | -| Headless tests pass, interactive TTY fails | No real TTY / wrong term / CI without pseudo-TTY | Keep headless `createTestRenderer` as CI primary; interactive only on developer machines | -| Standalone binary / Homebrew missing native | Compile/package step omitted optional assets | Release pipeline must bundle or re-resolve platform package (verify before cutover) | -| Tree-sitter / markdown assets missing | Incomplete pack of `@opentui/core` wasm | Only if those components are used; include `@opentui/core` assets in release | - -### Install policy for optional deps - -- **Never** pass flags that skip optional dependencies for OpenTUI installs. -- Prefer `bun install --frozen-lockfile` in CI so the lockfile records the intended optional set. -- Do not vendor a single-platform dylib into the repo; rely on optional packages. - ---- - -## 5. Release / packaging risks (pre-cutover checklist) - -1. **FFI load path** — confirmed on darwin-arm64 spike; re-verify on each required matrix cell. -2. **Compile (`bun build --compile`)** — ensure the native library is found under the bundled filesystem root or re-resolved next to the binary. -3. **Homebrew / tarball** — ship per-platform artifacts or run an install step that fetches the correct optional package. -4. **Asset size** — tree-sitter wasm under `@opentui/core` if markdown/code components are used; measure before release. -5. **Lockfile** — commit lockfile entries that allow optional platform packages to resolve on all required CI OS/arch pairs. -6. **No dual stack** — packaging targets the OpenTUI migration branch only; main stays Ink until hard cutover. - ---- - -## 6. Quick reference — “how do I install on a clean Mac/Linux machine?” - -```bash -# 1. Install Bun (>= 1.2) -curl -fsSL https://bun.sh/install | bash - -# 2. Clone Corbits and use the spike tree (until root depends on OpenTUI) -cd /path/to/corbits-code/tmp/opentui-spike -bun install - -# 3. Prove native FFI -bun run verify - -# 4. Optional interactive shell -bun run start -``` - -**macOS:** expect `@opentui/core-darwin-{arm64|x64}` and `libopentui.dylib`. -**Linux glibc:** expect `@opentui/core-linux-{arm64|x64}` and `libopentui.so`. -**Linux musl:** `export OPENTUI_LIBC=musl` and expect `*-musl` package. -**Windows:** optional packages exist; not required for contributor or merge gate. - ---- - -## 7. Related docs - -| Doc | Role | -|---|---| -| `docs/TUI.md` | Shipping shell behavior spec (layout, geometry, overlays, prompt) | -| `docs/adr/opentui-binding.md` | ADR: why OpenTUI, core-class vs Solid binding | - -Spike re-run evidence lives under `tmp/opentui-spike/` (local; not a root dependency).