nandi/oripublic Fork 0
f5c963af3c1597c0274ce4a99705dbd92a7d1cba
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

🛟 Updated. f5c963a · on f5c963af3c1597c0274ce4a99705dbd92a7d1cba · k33g · 21h ago
history.md · 154 lines · 28.2 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.

2026-09-17 — Commit and merge into main

  • User asked to merge feat/acp-webapp into main. Everything since 2434cc7 committed as 76d62ac (203 files, +21 595 lines), excluding hello.md (test artefact) and .claude/settings.local.json (local permissions). main fast-forwarded (merge base was 4edda86, no divergence). Not pushed to origin (git.rickub.com) — not asked. This memory update itself is a follow-up commit on main.

2026-09-18 — Diagnose git push --set-upstream origin main hanging

  • Asked: why the first push to origin never finishes. Diagnostic only; no code or config changed.
  • Finding: origin is https://git.rickub.com/bots-garden/ori.git; the server answers 401 on git-receive-pack, the macOS keychain has no entry for that host, and the session (launched from VSCodium's terminal) exports GIT_ASKPASS → VSCodium's askpass.sh. Git therefore hands the password prompt to VSCodium's input box, which is easy to miss, so the push waits forever. Confirmed with ps: git-remote-httpsaskpass.sh "Password for 'https://git.rickub.com'" blocked.
  • Also verified: network fine (DNS, 443, 22 all open); anonymous read over HTTPS works; SSH key ~/.ssh/id_ed25519 is accepted by git@git.rickub.com (server replies "serves git only"); git ls-remote over SSH succeeds and shows the remote repo is empty (no branches yet). Local main = 34e69b5.
  • Recommendation left to the user: switch origin to git@git.rickub.com:bots-garden/ori.git (no password, key already trusted), or run the push in a plain terminal with GIT_ASKPASS unset so osxkeychain stores the credentials. Not applied — awaiting the user's choice.

2026-09-18 — AppleScript launcher for the ori sandbox + desktop app

  • Asked: an AppleScript that runs sbx run -d claude . --template k33g/ori:0.0.1 --kit ./kits/ori --name ori -p 5555:8888 then launches ori-desktop/build/bin/ori-desktop.app.
  • Changes: new scripts/launch-ori.applescript; one row in quickstart.md (everyday commands); pointer in summary.md.
  • Decisions: (1) if sbx ls -q already lists ori, restart it with sbx run -d --name ori instead of re-running the create command (which would collide on the name); errors on that path only notify, the health check decides. (2) Poll /healthz on :5555 (2 s × 45) before opening the app, because the kit starts the server asynchronously and the desktop app auto-connects to the saved URL (~/Library/Application Support/ori-desktop/settings.json already holds http://localhost:5555). (3) PATH exported explicitly (Finder launches lack /opt/homebrew/bin). (4) Repo root derived from path to me so the compiled .app works from scripts/ too. Rejected: sbx rm + recreate on every launch (destroys the sandbox's Claude session/state).
  • Tests: osacompile OK; handlers exercised via load script: sandboxExists → false with no sandbox, waitForServer → false on a closed port, true against a throw-away Python /healthz on :5555. Not run end to end (would create a real sandbox and start Claude in it). Quality gate not re-run (no Go/TS touched).

2026-09-18 — Ticket 0005 back to the IssueSpec task schema; launcher compiled as an app

  • Asked: "le ticket 0005 n'est plus au bon format"; then how to make the AppleScript launcher double-clickable.
  • Cause: the 0005 checklist had been hand-written as - text: … / done: true|false, which is not IssueSpec's task shape (id, title, state, priority, author, createdAt, optional updatedAt/closedAt). IssueSpec's parser rejects the file: "tasks[0].id must be a positive integer".
  • Changes: .tickets/issues/0005-….yaml — 8 tasks rewritten (7 closed with updatedAt/closedAt = 2026-09-17T22:23:39Z, 1 open; all priority: none, author k33g); issue updatedAt bumped. scripts/Launch Ori.app built with osacompile, ignored via .gitignore (scripts/*.app/). summary.md gains a .tickets paragraph.
  • Verification: bundled IssueSpec's parseIssueDocument (from the local clone ~/CodeBerg/issuespec, esbuild --format=cjs) and ran it over all 15 tickets: all OK; the committed 0005 fails as expected.
  • Follow-up: scripts/README.md + scripts/README.fr.md written (what the launcher does, run vs. double-click, design choices, handler test recipe); quickstart row now points to it.
  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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# 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.

## 2026-09-17 — Commit and merge into main

- User asked to merge `feat/acp-webapp` into `main`. Everything since 2434cc7 committed as `76d62ac` (203 files, +21 595 lines), excluding `hello.md` (test artefact) and `.claude/settings.local.json` (local permissions). `main` fast-forwarded (merge base was 4edda86, no divergence). Not pushed to `origin` (git.rickub.com) — not asked. This memory update itself is a follow-up commit on main.

## 2026-09-18 — Diagnose `git push --set-upstream origin main` hanging

- **Asked**: why the first push to `origin` never finishes. Diagnostic only; no code or config changed.
- **Finding**: `origin` is `https://git.rickub.com/bots-garden/ori.git`; the server answers 401 on `git-receive-pack`, the macOS keychain has no entry for that host, and the session (launched from VSCodium's terminal) exports `GIT_ASKPASS` → VSCodium's `askpass.sh`. Git therefore hands the password prompt to VSCodium's input box, which is easy to miss, so the push waits forever. Confirmed with `ps`: `git-remote-https``askpass.sh "Password for 'https://git.rickub.com'"` blocked.
- **Also verified**: network fine (DNS, 443, 22 all open); anonymous read over HTTPS works; SSH key `~/.ssh/id_ed25519` is accepted by `git@git.rickub.com` (server replies "serves git only"); `git ls-remote` over SSH succeeds and shows the remote repo is **empty** (no branches yet). Local `main` = 34e69b5.
- **Recommendation left to the user**: switch `origin` to `git@git.rickub.com:bots-garden/ori.git` (no password, key already trusted), or run the push in a plain terminal with `GIT_ASKPASS` unset so osxkeychain stores the credentials. Not applied — awaiting the user's choice.

## 2026-09-18 — AppleScript launcher for the ori sandbox + desktop app

- **Asked**: an AppleScript that runs `sbx run -d claude . --template k33g/ori:0.0.1 --kit ./kits/ori --name ori -p 5555:8888` then launches `ori-desktop/build/bin/ori-desktop.app`.
- **Changes**: new `scripts/launch-ori.applescript`; one row in `quickstart.md` (everyday commands); pointer in `summary.md`.
- **Decisions**: (1) if `sbx ls -q` already lists `ori`, restart it with `sbx run -d --name ori` instead of re-running the create command (which would collide on the name); errors on that path only notify, the health check decides. (2) Poll `/healthz` on :5555 (2 s × 45) before opening the app, because the kit starts the server asynchronously and the desktop app auto-connects to the saved URL (`~/Library/Application Support/ori-desktop/settings.json` already holds `http://localhost:5555`). (3) `PATH` exported explicitly (Finder launches lack `/opt/homebrew/bin`). (4) Repo root derived from `path to me` so the compiled `.app` works from `scripts/` too. Rejected: `sbx rm` + recreate on every launch (destroys the sandbox's Claude session/state).
- **Tests**: `osacompile` OK; handlers exercised via `load script`: `sandboxExists` → false with no sandbox, `waitForServer` → false on a closed port, true against a throw-away Python `/healthz` on :5555. Not run end to end (would create a real sandbox and start Claude in it). Quality gate not re-run (no Go/TS touched).

## 2026-09-18 — Ticket 0005 back to the IssueSpec task schema; launcher compiled as an app

- **Asked**: "le ticket 0005 n'est plus au bon format"; then how to make the AppleScript launcher double-clickable.
- **Cause**: the 0005 checklist had been hand-written as `- text: … / done: true|false`, which is not IssueSpec's task shape (`id`, `title`, `state`, `priority`, `author`, `createdAt`, optional `updatedAt`/`closedAt`). IssueSpec's parser rejects the file: "tasks[0].id must be a positive integer".
- **Changes**: `.tickets/issues/0005-….yaml` — 8 tasks rewritten (7 `closed` with `updatedAt`/`closedAt` = 2026-09-17T22:23:39Z, 1 `open`; all `priority: none`, author k33g); issue `updatedAt` bumped. `scripts/Launch Ori.app` built with `osacompile`, ignored via `.gitignore` (`scripts/*.app/`). `summary.md` gains a `.tickets` paragraph.
- **Verification**: bundled IssueSpec's `parseIssueDocument` (from the local clone `~/CodeBerg/issuespec`, esbuild `--format=cjs`) and ran it over all 15 tickets: all OK; the committed 0005 fails as expected.
- **Follow-up**: `scripts/README.md` + `scripts/README.fr.md` written (what the launcher does, run vs. double-click, design choices, handler test recipe); quickstart row now points to it.