forked from bots-garden/ori
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 themethodical-devskill, 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, bilingualdocs/(en+fr, Diátaxis, 7 pages each),docs/diagrams/packages.drawio, rootREADME.md. - Decisions:
coder/acp-go-sdkfor 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 theclaude-code-acpadapter); ACP payloads relayed verbatim to the browser; server as single source of truth with history replay +user_messageecho; one shared session in v1. Mid-session the user corrected the Go module path fromgithub.com/bots-garden/oritorickub.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
~/.qltyvolume (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 +workingPhrasesrotating every 2.5s),ChatThreadrefactored with aThoughtEntrycomponent (live open block💭 Thinking…while streaming, collapsible💭 Thought for a momentonce the turn ends), CSS spinner/thought-live, tutorials updated in both languages. - Decisions: live-vs-collapsed decided by the existing
closedflag 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
ThreadEntryat 6 returns; fixed by extractingThoughtEntry). - 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) andinternal/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),MonacoViewershared by read-only preview and editor (bundled workers, lazy chunk), AsciiDoc rendering via lazy@asciidoctor/core, xterm.js terminal,tool-outputmonospace block forexecutetool 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,
-raceclean. - Quality: gate PASS (0/0/0, total complexity 281). Iterations: biome 1.9 rejects the
noStaticElementInteractionssuppression (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+ referenceworkspace-apiin 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 —
convertis a named export and asynchronous (the v3asciidoctor()factory no longer exists; my CJS-default cast calledundefined()); 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 toui/public/material-icons/byui/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
InlineTextcomponent (backtick spans →<code class="inline-code">, no markdown pipeline), applied to tool call titles, permission titles and plan entries;.inline-codeCSS. - 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 635MBsandboxes/checkout, node_modules, .git…),kits/ori/spec.yaml(schemaVersion 2 mixin: idempotentsetup.startuplaunching ori as the agent user on :8888, agentInstructions),kits/ori/README.md,make template, how-torun-in-a-sandboxin docs en+fr. - Decisions: no
sbx template buildexists — a template IS a container image passed viasbx create claude --template k33g/ori, and--kitaccepts a mixin directory (verified insandboxes/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 bypgrep -x oribecause 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), aports:block (container 8888 — kits auto-publish an ephemeral localhost port at create; Dockerfile EXPOSE is read by nothing), and droppinguser: "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/pushexist for distribution; an alternative shape (kind:sandbox +extends: claude+sandbox.image: k33g/ori) would allowsbx 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/orito Docker Hub — atemplate/build.shthey run themselves, image tagged0.0.0; the kit's listen port made configurable; aquickstart.mdwith 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.yamlgainedargs.port(default "8888", pattern-validated) substituted intoports.container(unquoted${{ kit.args.port }}— the source's expand.go explicitly re-infers it as an integer), the startup--addr, and the agentInstructions;quickstart.mdat the repo root; kit README and both run-in-a-sandbox pages now referencek33g/ori:0.0.0and--kit-arg ori.port=. - Validation: spec YAML parses with the arg refs; build.sh is the push path,
make templatestays 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.shfailed 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 withGOOS=$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-acpresolves 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:8888came up with nothing listening. Root cause read from the sbx source (sandboxlib/kit/durable_startup.go): startup commands are rendered asexec su -s /bin/sh -c '<script>' agent, and util-linuxsualways 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 useddocker 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 exactsupath: healthz OK, ACP session created. - No image rebuild needed — kit-side only; the user must
sbx rmand 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:8888serves the webapp then goesstoppedby 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,holdSessioninsandboxd/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)insetupDockerNextBackend).sbx createholds a sentinel only for its own lifetime (executeCreateAndReleaseSessionincli-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 runtime30 s later fororiandori2). Port publishing and browser traffic hold no session. - Escape hatch: the create-time spec field
detached: true(openapiSandboxCreateRequest.detached) makesholdSessionnever auto-stop. Onlysbx run -d/--detachedsets it (create_daemon.go: Detached: opts.detached);sbx createhas no such flag; there is no update API, nosbx settingskey and no env var for the delay. - Recommendation given:
sbx rm orithensbx 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.mdnow usesbx 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. Nosbx createinvocation 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/healthwith 12 tests, EN+FR READMEs,make desktop/desktop-test, .gitignore, ticket 0005 filled). Verified: tests, vet, gofmt, Windows cross-build, rootmake build/make testunaffected. Not verifiable in the sandbox: running the window (no gcc/webkit2gtk). - riker:
GET /api/files/search,GET /api/raw, newinternal/skills+GET /api/skills;prompt.attachments→ ACPresource_linkblocks (Prompter.Prompt(blocks)replacesPromptText,Bridge.SetWorkspaceRoot); reducer storesavailable_commands_updatecommands;mentions.ts,useCompletion.ts,CompletionPopup, rewrittenPromptInput,ImageView,DrawioView(embed.diagrams.net + Source fallback),lang.tskindsimage/drawio; mock agent emits commands and echoes attachments; Vite dev proxy gained/api;biome format --writereformatted 11 files. Docs EN+FR: workspace-api, websocket-protocol, use-the-workspace, new how-tomention-files-and-skills, architecture sections, READMEs. Tickets 0008/0009/0011/0012 filled. - milo: ori calls
initialize,session/new,session/prompt,session/cancel; servessession/update,session/request_permission,fs/read_text_file,fs/write_text_file; refusesterminal/*; unused stable methods:authenticate,logout,session/load|resume|list|close|set_mode|set_config_option. "Agent commands" are not ACP methods:available_commands_update→/nametext 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 (
~/.qltymissing). 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), thenenv -u CLAUDECODE nohup ./bin/ori --addr 0.0.0.0:8888 > /tmp/ori.log 2>&1 &inside theclaude-orisandbox. Adapter@zed-industries/claude-code-acp@0.16.2resolved via npx (npm now flags it deprecated → renamed@agentclientprotocol/claude-agent-acp; still works). ACP session ready,/healthzok,/api/skillslists the skills. Port 8888 must be published on the host withsbx ports claude-ori --publish 8888:8888/tcp(not doable from inside the sandbox). - Trap hit: a
curlWebSocket handshake against/wsnever returns on success (101 keeps the socket open) — use-mor 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.2bundles@anthropic-ai/claude-agent-sdk@0.2.44, whose embedded Claude Code CLI is 2.1.44 (zero mentions of Fable), while~/.claude/settings.jsonsets"model": "fable[1m]"and the adapter forwards it verbatim. The system CLI (2.1.275) acceptsfable[1m]fine. Fix without touching the user's settings: the adapter honoursCLAUDE_CODE_EXECUTABLE→pathToClaudeCodeExecutable; ori relaunched withCLAUDE_CODE_EXECUTABLE=/home/agent/.local/share/claude/versions/2.1.275; a WS prompt round-trip returnedpong(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), newui/src/components/ResizeHandle.tsx(+ test),Workspace.tsx(handle between tree and tabs,--filetree-widthon the aside; newWorkspace.test.tsx),app.css(.filetreewidth from the variable,.resize-handlestyles). 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.jsbecame an ES module (type="module", IIFE and"use strict"dropped, optional chains, template literals,reloadFrame()instead ofsrc = src),internal/files/raw.goexplicit_ = file.Close()with rationale,internal/files/search.gowalk callback →searchWalker.visit/visitDir/collect,PreviewPane.tsxloading effect →usePreviewFilehook;qlty fmton 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-schemeper theme,.topbar-theme),App.tsx(☾/☀ button, aria-label "Switch to dark/light theme"),MonacoViewer.tsx(store instead ofmatchMedia),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-schemeis 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.mdEN + 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 |
|