nandi/oripublic Fork 0
76d62ac0da1600e1c208017584c3fa22e54feaa3
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

history.md · 128 lines · 23.8 KBmarkdown
Blame HistoryOpen raw

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 → <code class="inline-code">, 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 '<script>' agent, and util-linux su always resets PATH to the login.defs default — which lacks /usr/local/share/npm-global/bin, so ori died instantly with "claude-code-acp: executable file not found". Reproduced in the image (su -c 'command -v claude-code-acp' agent → missing); my earlier validation had used docker exec, which preserves image ENV — that's why it passed.
  • Fix: the kit's startup script now exports PATH="/usr/local/share/npm-global/bin:/home/agent/.local/bin:$PATH" first (comment marks it load-bearing). Re-validated through the exact su path: healthz OK, ACP session created.
  • No image rebuild needed — kit-side only; the user must sbx rm and recreate the sandbox for the new kit to apply. Debug window for kit startup: /var/log/sbx-kit-startup.log (dispatcher log) + /home/agent/.ori.log.

2026-09-17 — Session end inside the sandbox: WS-offline investigation in flight (follow-up 5f)

  • After the su-PATH fix, the user's sandbox serves the SPA on localhost:5555 but the UI shows "offline" (the WebSocket to /ws does not stay up). Established before stopping: ori's WS handshake is correct against browser-like Origin headers (101 for localhost:* origins, 403 for foreign — tested with curl against the image); sbx port publishing goes through gvisor's port mapper (raw L4, so the upgrade should pass). Three discriminating diagnostics were handed to the user (server log; WS handshake from inside the sandbox; WS handshake through the forward) — answers not yet received. The session ends here: the user is relaunching the assistant outside sbx.
  • Everything since commit 2434cc7 is still uncommitted (user's call, repeatedly deferred).

2026-09-17 — Why the ori sandbox stops on its own: sbx auto-stop investigation (follow-up 6, read-only)

  • Asked: the sandbox created with sbx create claude . --template k33g/ori:0.0.0 --kit ./kits/ori --name ori -p 8888:8888 serves the webapp then goes stopped by itself; find the auto-stop mode in the sbx source and a way to disable it.
  • Found (in sandboxes/, the vendored sbx checkout, v0.43.0 daemon on this Mac): sandboxd counts "sentinel" long-poll sessions per sandbox (GET /sandbox/{name}/session, holdSession in sandboxd/pkg/server/backend_dockernext.go); when the count hits zero it stops the container after a hard-coded 30 s grace (WithAutoStopDelay(30*time.Second) in setupDockerNextBackend). sbx create holds a sentinel only for its own lifetime (executeCreateAndReleaseSession in cli-plugin/commands/create.go), so the stop fires 30 s after the command returns — confirmed by the daemon log (session disconnected, deferring auto-stopauto-stopped runtime 30 s later for ori and ori2). Port publishing and browser traffic hold no session.
  • Escape hatch: the create-time spec field detached: true (openapi SandboxCreateRequest.detached) makes holdSession never auto-stop. Only sbx run -d/--detached sets it (create_daemon.go: Detached: opts.detached); sbx create has no such flag; there is no update API, no sbx settings key and no env var for the delay.
  • Recommendation given: sbx rm ori then sbx run -d claude . --template k33g/ori:0.0.0 --kit ./kits/ori --name ori -p 8888:8888.
  • Docs switched (same session, at the user's request while they tested): quickstart.md, kits/ori/README.md, docs/en|fr/how-to/run-in-a-sandbox.md now use sbx run -d, explain the 30 s auto-stop and why detached is the fix, and add a restart line (sbx run -d --name <name>) plus a daemon-log diagnostic hint. No sbx create invocation remains in project docs. No code change.
  • Also observed: the WS-"offline" symptom from follow-up 5f is very plausibly this same stop (the page was already loaded when the container died), still to be confirmed by the user.

2026-09-17 — Three parallel agents: ori-desktop (Wails), @/skill selectors + draw.io/image previews, ACP command inventory

  • Asked: (bob) create ori-desktop/, a Wails app connecting to the ori webapp; (riker) add an @ file selector, a skill selector, draw.io and image previews; (milo, read-only) list the ACP commands exposed. Then the user built ori-desktop on their Mac: Check OK, Connect "did nothing".
  • bob: new module ori-desktop/ (Wails v2.16.0, plain template, iframe embed of the ori SPA, settings JSON, bound methods, internal/settings + internal/health with 12 tests, EN+FR READMEs, make desktop/desktop-test, .gitignore, ticket 0005 filled). Verified: tests, vet, gofmt, Windows cross-build, root make build/make test unaffected. Not verifiable in the sandbox: running the window (no gcc/webkit2gtk).
  • riker: GET /api/files/search, GET /api/raw, new internal/skills + GET /api/skills; prompt.attachments → ACP resource_link blocks (Prompter.Prompt(blocks) replaces PromptText, Bridge.SetWorkspaceRoot); reducer stores available_commands_update commands; mentions.ts, useCompletion.ts, CompletionPopup, rewritten PromptInput, ImageView, DrawioView (embed.diagrams.net + Source fallback), lang.ts kinds image/drawio; mock agent emits commands and echoes attachments; Vite dev proxy gained /api; biome format --write reformatted 11 files. Docs EN+FR: workspace-api, websocket-protocol, use-the-workspace, new how-to mention-files-and-skills, architecture sections, READMEs. Tickets 0008/0009/0011/0012 filled.
  • milo: ori calls initialize, session/new, session/prompt, session/cancel; serves session/update, session/request_permission, fs/read_text_file, fs/write_text_file; refuses terminal/*; unused stable methods: authenticate, logout, session/load|resume|list|close|set_mode|set_config_option. "Agent commands" are not ACP methods: available_commands_update/name text in the prompt. Relayed to riker, who implemented it (ticket 0008).
  • Bug fixed after the user's test: ori-desktop/frontend/src/main.css#connect-screen {display:grid} outranked .screen[hidden], so Connect (and the startup auto-connect) rendered the viewer below the fold; now class .connect + .screen[hidden] {display:none !important}. Rebuild needed (wails build); iframe content in WKWebView still unconfirmed.
  • Tests: make test → 9 Go packages ok, vitest 16 files / 102 tests (was 66); go test -race ./... ok (riker installed gcc + libc6-dev via apt in the sandbox for it).
  • Quality gate: NOT run — qlty absent from this sandbox (~/.qlty missing). gofmt / go vet / biome check clean.
  • Nothing committed (still everything since 2434cc7).

2026-09-17 — Launch ori for manual testing of the new features (evening session)

  • Asked: "lance ori que je puisse tester les nouveautés". No code change: make build (SPA + binaries), then env -u CLAUDECODE nohup ./bin/ori --addr 0.0.0.0:8888 > /tmp/ori.log 2>&1 & inside the claude-ori sandbox. Adapter @zed-industries/claude-code-acp@0.16.2 resolved via npx (npm now flags it deprecated → renamed @agentclientprotocol/claude-agent-acp; still works). ACP session ready, /healthz ok, /api/skills lists the skills. Port 8888 must be published on the host with sbx ports claude-ori --publish 8888:8888/tcp (not doable from inside the sandbox).
  • Trap hit: a curl WebSocket handshake against /ws never returns on success (101 keeps the socket open) — use -m or check the log instead.
  • Follow-up (same session): first real prompt failed with -32603 There's an issue with the selected model (fable[1m]). Root cause: @zed-industries/claude-code-acp@0.16.2 bundles @anthropic-ai/claude-agent-sdk@0.2.44, whose embedded Claude Code CLI is 2.1.44 (zero mentions of Fable), while ~/.claude/settings.json sets "model": "fable[1m]" and the adapter forwards it verbatim. The system CLI (2.1.275) accepts fable[1m] fine. Fix without touching the user's settings: the adapter honours CLAUDE_CODE_EXECUTABLEpathToClaudeCodeExecutable; ori relaunched with CLAUDE_CODE_EXECUTABLE=/home/agent/.local/share/claude/versions/2.1.275; a WS prompt round-trip returned pong (turn_ended end_turn). No code change.

2026-09-17 — Resizable file explorer column (ticket 0015), via methodical-dev

  • Goal: make the workspace file tree column resizable with the mouse; user chose localStorage persistence and the file-tree divider only (not the chat/workspace split).
  • Changes: ui/src/workspace.ts (fileTreeWidth, setFileTreeWidth, resetFileTreeWidth, clampFileTreeWidth, constants, guarded localStorage), new ui/src/components/ResizeHandle.tsx (+ test), Workspace.tsx (handle between tree and tabs, --filetree-width on the aside; new Workspace.test.tsx), app.css (.filetree width from the variable, .resize-handle styles). Ticket 0015 closed.
  • Decisions: width in px (drag produces px; comparable with bounds); FileTree untouched — width reaches CSS via a custom property; handle owns no width, the store clamps. jsdom 26 has no PointerEvent → test-local MouseEvent polyfill.
  • Quality gate (first run of qlty on the tree since the parallel-agents session): 12 errors / 1 warning / 2 smells, all pre-existing — fixed without weakening anything: ori-desktop/frontend/src/main.js became an ES module (type="module", IIFE and "use strict" dropped, optional chains, template literals, reloadFrame() instead of src = src), internal/files/raw.go explicit _ = file.Close() with rationale, internal/files/search.go walk callback → searchWalker.visit/visitDir/collect, PreviewPane.tsx loading effect → usePreviewFile hook; qlty fmt on main.css / main.js / wails.json. Run #15 PASS (0/0/0, complex 507).
  • Tests: 118 vitest (18 files), 9 Go packages, ori-desktop 2 packages — green. make test.
  • Docs: how-to use-the-workspace (step 5 + keyboard/persistence variant) and architecture explanation, EN + FR; root README; ori-desktop READMEs (ES module).
  • Not verifiable here: the ori-desktop frontend change (ES module) needs a wails build + launch on the user's Mac.

2026-09-17 — Light / dark theme selector, light by default (ticket 0013)

  • Goal: user request received mid-session; choices made via AskUserQuestion: two themes only (no "system" option), single review at the end.
  • Changes: new ui/src/theme.ts (+ theme.test.ts), app.css (dark palette on :root[data-theme="dark"], color-scheme per theme, .topbar-theme), App.tsx (☾/☀ button, aria-label "Switch to dark/light theme"), MonacoViewer.tsx (store instead of matchMedia), main.tsx (side-effect import so the theme is stamped before the first render), App.test.tsx (+1). Ticket 0013 closed.
  • Decisions: dropping prefers-color-scheme is a behaviour change (dark-OS users now get light until they toggle) — accepted by the user ("light par défaut"); documented in the architecture explanation. Terminal keeps its dark background (pre-existing decision).
  • Tests: 124 vitest / 19 files green; Go untouched. Quality: run #16 PASS (0/0/0, complex 514).
  • Docs: new how-to switch-the-theme.md EN + FR, both indexes, architecture explanation paragraph EN + FR, root README.
  • ori rebuilt and restarted on :8888 for the user's review.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# 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 → `<code class="inline-code">`, 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 '<script>' agent`, and util-linux `su` always resets PATH to the login.defs default — which lacks `/usr/local/share/npm-global/bin`, so ori died instantly with "claude-code-acp: executable file not found". Reproduced in the image (`su -c 'command -v claude-code-acp' agent` → missing); my earlier validation had used `docker exec`, which preserves image ENV — that's why it passed.
- **Fix**: the kit's startup script now exports `PATH="/usr/local/share/npm-global/bin:/home/agent/.local/bin:$PATH"` first (comment marks it load-bearing). Re-validated through the exact `su` path: healthz OK, ACP session created.
- **No image rebuild needed** — kit-side only; the user must `sbx rm` and recreate the sandbox for the new kit to apply. Debug window for kit startup: `/var/log/sbx-kit-startup.log` (dispatcher log) + `/home/agent/.ori.log`.

## 2026-09-17 — Session end inside the sandbox: WS-offline investigation in flight (follow-up 5f)

- After the su-PATH fix, the user's sandbox serves the SPA on localhost:5555 but the UI shows "offline" (the WebSocket to /ws does not stay up). Established before stopping: ori's WS handshake is correct against browser-like Origin headers (101 for localhost:* origins, 403 for foreign — tested with curl against the image); sbx port publishing goes through gvisor's port mapper (raw L4, so the upgrade should pass). Three discriminating diagnostics were handed to the user (server log; WS handshake from inside the sandbox; WS handshake through the forward) — answers not yet received. The session ends here: the user is relaunching the assistant outside sbx.
- Everything since commit 2434cc7 is still uncommitted (user's call, repeatedly deferred).

## 2026-09-17 — Why the ori sandbox stops on its own: sbx auto-stop investigation (follow-up 6, read-only)

- **Asked**: the sandbox created with `sbx create claude . --template k33g/ori:0.0.0 --kit ./kits/ori --name ori -p 8888:8888` serves the webapp then goes `stopped` by itself; find the auto-stop mode in the sbx source and a way to disable it.
- **Found** (in `sandboxes/`, the vendored sbx checkout, v0.43.0 daemon on this Mac): sandboxd counts "sentinel" long-poll sessions per sandbox (`GET /sandbox/{name}/session`, `holdSession` in `sandboxd/pkg/server/backend_dockernext.go`); when the count hits zero it stops the container after a hard-coded 30 s grace (`WithAutoStopDelay(30*time.Second)` in `setupDockerNextBackend`). `sbx create` holds a sentinel only for its own lifetime (`executeCreateAndReleaseSession` in `cli-plugin/commands/create.go`), so the stop fires 30 s after the command returns — confirmed by the daemon log (`session disconnected, deferring auto-stop``auto-stopped runtime` 30 s later for `ori` and `ori2`). Port publishing and browser traffic hold no session.
- **Escape hatch**: the create-time spec field `detached: true` (openapi `SandboxCreateRequest.detached`) makes `holdSession` never auto-stop. Only `sbx run -d/--detached` sets it (`create_daemon.go: Detached: opts.detached`); `sbx create` has no such flag; there is no update API, no `sbx settings` key and no env var for the delay.
- **Recommendation given**: `sbx rm ori` then `sbx run -d claude . --template k33g/ori:0.0.0 --kit ./kits/ori --name ori -p 8888:8888`.
- **Docs switched (same session, at the user's request while they tested)**: `quickstart.md`, `kits/ori/README.md`, `docs/en|fr/how-to/run-in-a-sandbox.md` now use `sbx run -d`, explain the 30 s auto-stop and why detached is the fix, and add a restart line (`sbx run -d --name <name>`) plus a daemon-log diagnostic hint. No `sbx create` invocation remains in project docs. No code change.
- **Also observed**: the WS-"offline" symptom from follow-up 5f is very plausibly this same stop (the page was already loaded when the container died), still to be confirmed by the user.

## 2026-09-17 — Three parallel agents: ori-desktop (Wails), @/skill selectors + draw.io/image previews, ACP command inventory

- **Asked**: (bob) create `ori-desktop/`, a Wails app connecting to the ori webapp; (riker) add an `@` file selector, a skill selector, draw.io and image previews; (milo, read-only) list the ACP commands exposed. Then the user built ori-desktop on their Mac: Check OK, Connect "did nothing".
- **bob**: new module `ori-desktop/` (Wails v2.16.0, plain template, iframe embed of the ori SPA, settings JSON, bound methods, `internal/settings` + `internal/health` with 12 tests, EN+FR READMEs, `make desktop`/`desktop-test`, .gitignore, ticket 0005 filled). Verified: tests, vet, gofmt, Windows cross-build, root `make build`/`make test` unaffected. Not verifiable in the sandbox: running the window (no gcc/webkit2gtk).
- **riker**: `GET /api/files/search`, `GET /api/raw`, new `internal/skills` + `GET /api/skills`; `prompt.attachments` → ACP `resource_link` blocks (`Prompter.Prompt(blocks)` replaces `PromptText`, `Bridge.SetWorkspaceRoot`); reducer stores `available_commands_update` commands; `mentions.ts`, `useCompletion.ts`, `CompletionPopup`, rewritten `PromptInput`, `ImageView`, `DrawioView` (embed.diagrams.net + Source fallback), `lang.ts` kinds `image`/`drawio`; mock agent emits commands and echoes attachments; Vite dev proxy gained `/api`; `biome format --write` reformatted 11 files. Docs EN+FR: workspace-api, websocket-protocol, use-the-workspace, new how-to `mention-files-and-skills`, architecture sections, READMEs. Tickets 0008/0009/0011/0012 filled.
- **milo**: ori calls `initialize`, `session/new`, `session/prompt`, `session/cancel`; serves `session/update`, `session/request_permission`, `fs/read_text_file`, `fs/write_text_file`; refuses `terminal/*`; unused stable methods: `authenticate`, `logout`, `session/load|resume|list|close|set_mode|set_config_option`. "Agent commands" are not ACP methods: `available_commands_update``/name` text in the prompt. Relayed to riker, who implemented it (ticket 0008).
- **Bug fixed after the user's test**: `ori-desktop/frontend/src/main.css``#connect-screen {display:grid}` outranked `.screen[hidden]`, so Connect (and the startup auto-connect) rendered the viewer below the fold; now class `.connect` + `.screen[hidden] {display:none !important}`. Rebuild needed (`wails build`); iframe content in WKWebView still unconfirmed.
- **Tests**: `make test` → 9 Go packages ok, vitest 16 files / 102 tests (was 66); `go test -race ./...` ok (riker installed gcc + libc6-dev via apt in the sandbox for it).
- **Quality gate**: NOT run — qlty absent from this sandbox (`~/.qlty` missing). gofmt / go vet / biome check clean.
- Nothing committed (still everything since 2434cc7).

## 2026-09-17 — Launch ori for manual testing of the new features (evening session)

- **Asked**: "lance ori que je puisse tester les nouveautés". No code change: `make build` (SPA + binaries), then `env -u CLAUDECODE nohup ./bin/ori --addr 0.0.0.0:8888 > /tmp/ori.log 2>&1 &` inside the `claude-ori` sandbox. Adapter `@zed-industries/claude-code-acp@0.16.2` resolved via npx (npm now flags it deprecated → renamed `@agentclientprotocol/claude-agent-acp`; still works). ACP session ready, `/healthz` ok, `/api/skills` lists the skills. Port 8888 must be published on the host with `sbx ports claude-ori --publish 8888:8888/tcp` (not doable from inside the sandbox).
- Trap hit: a `curl` WebSocket handshake against `/ws` never returns on success (101 keeps the socket open) — use `-m` or check the log instead.
- **Follow-up (same session)**: first real prompt failed with `-32603 There's an issue with the selected model (fable[1m])`. Root cause: `@zed-industries/claude-code-acp@0.16.2` bundles `@anthropic-ai/claude-agent-sdk@0.2.44`, whose embedded Claude Code CLI is **2.1.44** (zero mentions of Fable), while `~/.claude/settings.json` sets `"model": "fable[1m]"` and the adapter forwards it verbatim. The system CLI (2.1.275) accepts `fable[1m]` fine. Fix without touching the user's settings: the adapter honours `CLAUDE_CODE_EXECUTABLE``pathToClaudeCodeExecutable`; ori relaunched with `CLAUDE_CODE_EXECUTABLE=/home/agent/.local/share/claude/versions/2.1.275`; a WS prompt round-trip returned `pong` (`turn_ended end_turn`). No code change.

## 2026-09-17 — Resizable file explorer column (ticket 0015), via methodical-dev

- **Goal**: make the workspace file tree column resizable with the mouse; user chose localStorage persistence and the file-tree divider only (not the chat/workspace split).
- **Changes**: `ui/src/workspace.ts` (`fileTreeWidth`, `setFileTreeWidth`, `resetFileTreeWidth`, `clampFileTreeWidth`, constants, guarded localStorage), new `ui/src/components/ResizeHandle.tsx` (+ test), `Workspace.tsx` (handle between tree and tabs, `--filetree-width` on the aside; new `Workspace.test.tsx`), `app.css` (`.filetree` width from the variable, `.resize-handle` styles). Ticket 0015 closed.
- **Decisions**: width in px (drag produces px; comparable with bounds); FileTree untouched — width reaches CSS via a custom property; handle owns no width, the store clamps. jsdom 26 has no PointerEvent → test-local MouseEvent polyfill.
- **Quality gate** (first run of qlty on the tree since the parallel-agents session): 12 errors / 1 warning / 2 smells, all pre-existing — fixed without weakening anything: `ori-desktop/frontend/src/main.js` became an ES module (`type="module"`, IIFE and `"use strict"` dropped, optional chains, template literals, `reloadFrame()` instead of `src = src`), `internal/files/raw.go` explicit `_ = file.Close()` with rationale, `internal/files/search.go` walk callback → `searchWalker.visit/visitDir/collect`, `PreviewPane.tsx` loading effect → `usePreviewFile` hook; `qlty fmt` on main.css / main.js / wails.json. Run #15 PASS (0/0/0, complex 507).
- **Tests**: 118 vitest (18 files), 9 Go packages, ori-desktop 2 packages — green. `make test`.
- **Docs**: how-to `use-the-workspace` (step 5 + keyboard/persistence variant) and architecture explanation, EN + FR; root README; ori-desktop READMEs (ES module).
- **Not verifiable here**: the ori-desktop frontend change (ES module) needs a `wails build` + launch on the user's Mac.

## 2026-09-17 — Light / dark theme selector, light by default (ticket 0013)

- **Goal**: user request received mid-session; choices made via AskUserQuestion: two themes only (no "system" option), single review at the end.
- **Changes**: new `ui/src/theme.ts` (+ `theme.test.ts`), `app.css` (dark palette on `:root[data-theme="dark"]`, `color-scheme` per theme, `.topbar-theme`), `App.tsx` (☾/☀ button, aria-label "Switch to dark/light theme"), `MonacoViewer.tsx` (store instead of `matchMedia`), `main.tsx` (side-effect import so the theme is stamped before the first render), `App.test.tsx` (+1). Ticket 0013 closed.
- **Decisions**: dropping `prefers-color-scheme` is a behaviour change (dark-OS users now get light until they toggle) — accepted by the user ("light par défaut"); documented in the architecture explanation. Terminal keeps its dark background (pre-existing decision).
- **Tests**: 124 vitest / 19 files green; Go untouched. **Quality**: run #16 PASS (0/0/0, complex 514).
- **Docs**: new how-to `switch-the-theme.md` EN + FR, both indexes, architecture explanation paragraph EN + FR, root README.
- ori rebuilt and restarted on :8888 for the user's review.