# History ## 2026-09-17 — Ticket 0004: ACP web client (initial implementation) - **Goal**: implement `.tickets/issues/0004-specifications.yaml` — a webapp (Go backend + SPA) acting as an ACP client for a code agent (Claude Code first), UI following the principles of Zed's agent panel, designed to evolve. Driven with the `methodical-dev` skill, all checkpoints user-approved. - **Changes**: whole codebase created — `cmd/ori`, `cmd/ori-mock-agent`, `internal/{config,httpserver,agent,bridge,mockagent}`, `ui/` (Vite + React 19 + TS SPA with reducer/store/ws-client and panel components), `Makefile`, `biome.json`, `.qlty/qlty.toml`, bilingual `docs/` (en+fr, Diátaxis, 7 pages each), `docs/diagrams/packages.drawio`, root `README.md`. - **Decisions**: `coder/acp-go-sdk` for ACP (no official Go lib exists; the claude CLI has no native ACP mode — the user initially chose "native CLI", reversed after verification, and approved the `claude-code-acp` adapter); ACP payloads relayed verbatim to the browser; server as single source of truth with history replay + `user_message` echo; one shared session in v1. Mid-session the user corrected the Go module path from `github.com/bots-garden/ori` to `rickub.com/bots-garden/ori` (repo not on GitHub) — renamed everywhere, tests green. - **Tests**: 7 config + 6 httpserver + 10 agent (in-memory mock ACP agent) + 15 bridge (incl. 4 real-WebSocket integration) + 2 mockagent + 1 full e2e (compiled mock binary, real subprocess, real WS, permission round-trip, late-joiner replay) — all green incl. `-race`; 28 frontend tests (reducer, ws client with fake WebSocket, components) — green. One command: `make test`. - **Quality**: gate **PASS** (0 errors / 0 warnings / 0 smells, total complexity 145; started at 13/4/5/158). Config adjustments user-approved (see summary). Two environment fights: the 488MB `~/.qlty` volume (cache symlinked to main disk) and a partial JDK extraction. - **Docs**: `docs/en/` + `docs/fr/` complete four-quadrant sets; package diagram in draw.io; root README quick start. ## 2026-09-17 — Live thinking block + working spinner (same session, follow-up) - **Goal**: after testing against real Claude Code, the user asked for (1) the agent's thinking content visible live while it streams, (2) a spinner with rotating phrases while the agent works, Claude-style. - **Changes**: new `ui/src/components/WorkingIndicator.tsx` (spinner + `workingPhrases` rotating every 2.5s), `ChatThread` refactored with a `ThoughtEntry` component (live open block `💭 Thinking…` while streaming, collapsible `💭 Thought for a moment` once the turn ends), CSS spinner/thought-live, tutorials updated in both languages. - **Decisions**: live-vs-collapsed decided by the existing `closed` flag on thought items (no reducer change needed); phrases exported for deterministic tests. - **Tests**: +3 WorkingIndicator (fake timers: first phrase, rotation, wrap-around) and +2 App (thought open→collapsed across turn end; spinner present during a turn, gone after) — 33 frontend tests green, Go suite untouched. - **Quality**: gate PASS again (a first re-run flagged `ThreadEntry` at 6 returns; fixed by extracting `ThoughtEntry`). - **Also verified this session**: real Claude Code works end to end via `env -u CLAUDECODE ./bin/ori` (nested-session guard); adapter package renamed upstream to `@agentclientprotocol/claude-agent-acp` (default not changed yet). ## 2026-09-17 — Workspace panel: preview, editor, terminal, monospace command output (same session, follow-up 2) - **Goal**: user-requested evolutions — a preview panel (syntax-highlighted code + rendered Markdown/AsciiDoc), a terminal panel, a file editor panel (Monaco), and monospace rendering for system-command output in the chat. - **Changes**: backend `internal/files` (list/read/write file API, unrestricted access per user's explicit choice) and `internal/terminal` (PTY shell per WebSocket, creack/pty); frontend workspace layout (chat left + collapsible right panel: file tree with lazy directories, Preview/Editor/Terminal tabs kept mounted when inactive), `MonacoViewer` shared by read-only preview and editor (bundled workers, lazy chunk), AsciiDoc rendering via lazy `@asciidoctor/core`, xterm.js terminal, `tool-output` monospace block for `execute` tool calls. New deps: creack/pty, monaco-editor + @monaco-editor/react, @asciidoctor/core, @xterm/xterm + addon-fit. - **Decisions**: layout chat+workspace (user), interactive user terminal only in v1 (ACP terminals later, user), free filesystem/terminal access (user — sandbox assumed isolated), Monaco over CodeMirror (user); panes hidden not unmounted so the shell survives tab switches; monaco 0.56 import specifiers go through its exports map (no `esm/vs/` prefix). - **Tests**: +9 files API, +5 terminal PTY integration (real shell over real WS: command output, cwd, resize via stty, malformed frames, exit closes socket) → 46 Go tests incl. e2e; front +5 workspace store, +4 FileTree, +8 lang detection, +6 PreviewPane, +6 EditorPane, +4 TerminalPane (xterm/WS mocked), +2 ToolCallCard mono/markdown → 66 front tests. All green, `-race` clean. - **Quality**: gate PASS (0/0/0, total complexity 281). Iterations: biome 1.9 rejects the `noStaticElementInteractions` suppression (rule doesn't exist there — removed), the dangerouslySetInnerHTML suppression must sit between JSX attributes, EditorPane/ToolContent refactored (extracted EditorHeader/editorPlaceholder/TextToolContent) to clear return/complexity smells. - **Docs**: new how-to `use-the-workspace` + reference `workspace-api` in both languages; architecture pages and indexes updated; drawio diagram extended (files, terminal, pty, SPA runtime edges); root README. ## 2026-09-17 — Source code analysis document (new session) - **Goal**: analyse the source code and produce a summary as an AsciiDoc document. - **Changes**: new `docs/source-code-analysis.adoc` — overview, backend package table, frontend module/component inventory, HTTP/WS endpoints, bridge protocol summary, test counts (55 Go tests + 1 e2e, ~215 vitest cases), Makefile targets, design decisions, known limitations. No code changed. - **Method**: read `.memory/` first, then an Explore agent verified the memory picture against the sources (LOC: ~3,064 Go / ~3,252 TS-TSX). - **Tests / quality**: not run — documentation-only session, no code touched. ## 2026-09-17 — Workspace polish: AsciiDoc fix, VS Code-style tree with Material icons, dark terminal (same session, follow-up 3) - **Goal**: user feedback after trying the workspace — AsciiDoc preview broken in production ("m is not a function"), file tree should look like VS Code's with Material icons, terminal should always be dark. - **Changes**: AsciiDocView now uses @asciidoctor/core v4's real API — `convert` is a **named export and asynchronous** (the v3 `asciidoctor()` factory no longer exists; my CJS-default cast called `undefined()`); added a **non-mocked** AsciiDocView test rendering with the real converter — the mocked-only tests were exactly why the breakage reached production. FileTree restyled VS Code-like: `vscode-material-icons` (910 SVGs copied to `ui/public/material-icons/` by `ui/scripts/copy-icons.mjs`, npm pre-hooks on dev/build, gitignored), chevrons, indent guides, open-folder icons, active-file highlight. Terminal always dark (`#1e1e1e`) whatever the app theme. - **Tests**: +2 real AsciiDoc render, +2 FileTree (icons present, active highlight) → 70 front tests, all green; Go untouched (46). - **Quality**: gate PASS. ## 2026-09-17 — Inline monospace for backticked commands (same session, follow-up 4) - **Goal**: user feedback — tool titles like ``Run `ls .memory/` `` showed literal backticks in a proportional font. - **Changes**: new `InlineText` component (backtick spans → ``, no markdown pipeline), applied to tool call titles, permission titles and plan entries; `.inline-code` CSS. - **Tests**: +4 InlineText (spans, multiples, plain, unpaired backtick) and +1 ToolCallCard title assertion → 75 front tests green. ## 2026-09-17 — sbx template + kit for ori (same session, follow-up 5) - **Goal**: package ori as a Docker Sandboxes template (image `k33g/ori`, the user builds it under their handle) and add a kit to launch it easily from sbx. - **Changes**: `template/Dockerfile` (multi-stage: node builds the SPA, go builds the binaries, final layer = `docker/sandbox-templates:claude-code` + globally installed `@zed-industries/claude-code-acp` + `/usr/local/bin/{ori,ori-mock-agent}`, EXPOSE 8888, standalone CMD), `.dockerignore` (excludes the 635MB `sandboxes/` checkout, node_modules, .git…), `kits/ori/spec.yaml` (schemaVersion 2 mixin: idempotent `setup.startup` launching ori as the agent user on :8888, agentInstructions), `kits/ori/README.md`, `make template`, how-to `run-in-a-sandbox` in docs en+fr. - **Decisions**: no `sbx template build` exists — a template IS a container image passed via `sbx create claude --template k33g/ori`, and `--kit` accepts a mixin directory (verified in `sandboxes/docs/yml/sbx_create_claude.yaml`); the ori kit adds no network permissions (Anthropic hosts come from the claude agent kit, the adapter is baked into the image); kit startup guarded by `pgrep -x ori` because startup commands re-run on every container start. - **Tests/validation**: image built locally (2.81GB) and smoke-tested standalone with the mock agent (healthz + session OK); the kit's exact startup command exec'd in a sleeping container of the image → adapter session created, healthz OK; idempotency check OK; kit YAML parse-validated. ## 2026-09-17 — Kit refinements from the sbx source deep-dive (same session, follow-up 5b) - A thorough exploration of the sbx source confirmed the template/kit design and yielded three refinements applied to `kits/ori/spec.yaml`: `requires: {agent: claude}` (mixin-only field, enforces composition with the claude agent kit), a `ports:` block (container 8888 — kits auto-publish an ephemeral localhost port at create; Dockerfile EXPOSE is read by nothing), and dropping `user: "agent"` from the startup step (StartupCommand.user defaults to "1000", the agent uid; "agent" is non-standard). - Also learned and documented: sandboxd has its own image store — a never-pushed local image reaches it via `docker save` + `sbx template load`; kit references resolve fresh each create (no local kit registry); `sbx kit validate/pack/push` exist for distribution; an alternative shape (kind:sandbox + `extends: claude` + `sandbox.image: k33g/ori`) would allow `sbx create ./kits/ori .` in one argument — not adopted, the mixin matches the dev-toolkit pattern already in use. ## 2026-09-17 — build.sh, Hub publication at 0.0.0, parameterized kit port, quickstart (same session, follow-up 5c) - **Goal**: user wants to push `k33g/ori` to Docker Hub — a `template/build.sh` they run themselves, image tagged `0.0.0`; the kit's listen port made configurable; a `quickstart.md` with the sandbox-creation commands. - **Changes**: `template/build.sh` (multi-arch buildx --push, tags 0.0.0 + latest, VERSION as $1, PLATFORMS overridable); `kits/ori/spec.yaml` gained `args.port` (default "8888", pattern-validated) substituted into `ports.container` (unquoted `${{ kit.args.port }}` — the source's expand.go explicitly re-infers it as an integer), the startup `--addr`, and the agentInstructions; `quickstart.md` at the repo root; kit README and both run-in-a-sandbox pages now reference `k33g/ori:0.0.0` and `--kit-arg ori.port=`. - **Validation**: spec YAML parses with the arg refs; build.sh is the push path, `make template` stays the local-only path. The actual Hub push is the user's action (their Docker login). ## 2026-09-17 — Multi-arch build fix: no qemu in the template build (same session, follow-up 5d) - **Problem**: the user's `./template/build.sh` failed on the linux/amd64 half — on an arm64 host that stage runs under qemu, where Vite bundling Monaco exhausts Node's 2GB heap ("FATAL ERROR: Ineffective mark-compacts near heap limit", "qemu: uncaught target signal 6"). - **Fix**: every build stage now runs on `--platform=$BUILDPLATFORM`: the SPA build is platform-independent (built once, natively), Go cross-compiles with `GOOS=$TARGETOS GOARCH=$TARGETARCH`, and the ACP adapter is npm-installed in a native stage then COPY'd into the template's npm prefix (`/usr/local/share/npm-global`, agent-owned, on PATH). The final stage contains no RUN at all, so a multi-arch build never invokes qemu. - **Validation**: native rebuild green; smoke tests pass (healthz with mock agent; `claude-code-acp` resolves from the copied prefix and initializes a real ACP session). ## 2026-09-17 — Kit startup fix: su resets PATH (same session, follow-up 5e) - **Problem**: the user's real `sbx create … --kit ./kits/ori -p 5555:8888` came up with nothing listening. Root cause read from the sbx source (`sandboxlib/kit/durable_startup.go`): startup commands are rendered as `exec su -s /bin/sh -c '