Files
boocode/boocode_roadmap.md
indifferentketchup 314adaae48 docs: reconcile roadmap, README, and deferred work for v2.2 ship state
Mark v2.2/v2.2.1 shipped and v2.3 planned in roadmap and README; fix
DEFERRED-WORK §2 (ACP probe skip is planned, not resolved).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 15:27:16 +00:00

120 KiB
Raw Blame History

BooCode roadmap (v1.xv2.x)

Last updated: 2026-05-26

Companion doc: boocode_code_review.md holds the full external-repo inventory, lift rationale, and license analysis. This document is the canonical source for shipping state, version ordering, and what's planned vs. shipped.

Overview

BooCode is a 3-app monorepo at /opt/boocode/ (locked 2026-05-22):

  • BooChat (apps/server + apps/web, port 9500, code.indifferentketchup.com) — read-only chat with file-inspection tools. Backend in apps/server, SPA in apps/web. Database boochat (renamed from boocode at v2.0).
  • BooCoder (apps/coder, port 9502, coder.indifferentketchup.com) — write tools + external-CLI dispatch. Shipped v2.0.0v2.2.1. Host systemd service (not Docker since v2.1.0). In-process inference (with pending_changes table) AND Paseo-style ACP dispatch for seven providers (cursor, opencode, goose, claude, qwen, copilot + native boocode) with PTY fallback where ACP is unavailable.
  • BooTerm (apps/booterm, port 9501) — PTY/tmux/xterm.js. Live since May 2026. bookworm-slim + node-pty + tmux + xterm.js. Tmux session per pane (bc-<uuid>), SSH-out works (openssh-client + gosu in the image). Shares Postgres database boochat.

Caddy → Authelia → Tailscale → 100.114.205.53 → 9500/9501/9502. Three apps, one shared Postgres (Docker service boocode_db, database name boochat).

Architectural commitments:

  • No embeddings. Model uses file-view tools (view_file, list_dir, grep, find_files) + sidecar analyzers (codecontext, future codesight) + codecontext MCP tools. Walked away from the RAG pipeline May 2026.
  • BooChat is read-only. Write tools live in BooCoder (shipped v2.0).
  • Mount strategy: blanket /opt:rw, permission gating at the write-tool layer. Per-project scoping is policy, not mount. Path-guard correctness is the #1 test target for v2.0.
  • External CLI agents (opencode/claude/goose/pi) live on the host, not in containers. BooCoder shells out via local-exec PTY or ACP subprocess. Host install inherits Sam's existing ~/.opencode/, ~/.claude/, ~/.config/goose/ configs.
  • Protocol roles locked (2026-05-22): BooChat = MCP client only (read-only tool consumer, never enables write-capable MCP servers). BooCoder = MCP client + MCP server + ACP client (host) + ACP agent (driveable) — full matrix. BooCoder's ACP-client role replaces raw-PTY dispatch for ACP-capable agents (opencode opencode acp, goose goose acp); PTY fallback retained for claude/pi/smallcode.
  • Paseo-equivalent dispatcher inside BooCode (2026-05-22 pivot, shipped v2.2). Paseo (getpaseo/paseo) is AGPL-3.0 — incompatible with BooCode's MIT license and network-served deployment. BooCode reproduces the architecture using license-clean patterns only (provider-snapshot.ts, ACP merge/stream/persist, AgentComposerBar). Primary architectural template: Dominic789654/agent-hub (Apache-2.0). Critical context-management primitive: Roo Code Boomerang Tasks pattern. Observation pattern: Claude Code hooks (siropkin/budi reference).

External code lifted from / referenced in: see boocode_code_review.md for full inventory.


Shipped (status as of 2026-05-26)

Version Theme Tag
v1.0 Initial scaffold
Batches 14.4 Markdown, sidebar, panes, chats-inside-sessions, archive, fork/delete, header polish, settings drawer
v1.5 resolveProjectPath, BOOTSTRAP_ROOT, vitest pin
v1.6, v1.6.1, v1.6.2 Mobile pass + RightRail mobile drawer
v1.7 Drag-drop file + paste-as-attachment
v1.8, v1.8.1, v1.8.2 Settings drawer, git_status tool, WS reconnect, per-turn budget reset + Continue affordance + CapHitSentinel
v1.9.1 Skills system (/opt/skills/ + skill_find / skill_use / skill_resource + /skill slash command) v1.9.1
v1.9.7 ask_user_input elicitation tool v1.9.7
Batch 9 (Agents Tier 2) AGENTS.md + 6 builtin agents + AgentPicker in ChatInput toolbar + sessions.agent_id folded into v1.9.1/v1.9.7
v1.10.0 BooTerm: separate container, xterm.js + node-pty + tmux v1.10.0
v1.10.1 BooTerm-user (spawn as samkintop, login bash, Claude Code/opencode PATH) v1.10.1
v1.10.4, v1.10.5 Mobile terminal + XML tool-call fallback parser
v1.11.0 opencode-style compaction port (auto-overflow, anchored summary, tail preservation)
v1.11.1 Compaction follow-up (working indicator during compaction, unit tests, .bak cleanup)
v1.11.2 ContextBar (persistent context-usage indicator above MessageList)
v1.11.3 ctx_max capture via /upstream/<model>/props (replaces dead timings.n_ctx read) v1.11.3
v1.11.5 ContextBar inline next to agent picker; remove ChatContextPopover; default new sessions to no agent
v1.11.6 Doom-loop guard from opencode (3 identical tool calls → sentinel, abort recursion)
v1.11.7 pathGuard secrets filter (continue.dev DEFAULT_SECURITY_IGNORE_FILETYPES)
v1.11.8 web_search + web_fetch tools via SearXNG
v1.11.9 Manual redirect handling — re-run URL guard on each hop (SSRF hardening)
v1.11.10 Stream-cap response body at 5MB, abort on overflow v1.11.x
v1.12.0 codecontext sidecar (Go HTTP shim, NDJSON MCP framing, child.Wait supervisor) + container guidance (BOOCHAT.md/BOOCODER.md) + 7 vendored skills + system-prompt.ts extraction + mtime-watch cache + 8 codecontext tool wrappers + per-agent tool whitelists + .codecontextignore template + agents.ts ALL_TOOL_NAMES single-source-of-truth fix v1.12.0
v1.12.1 Server-side workspace pane sync (sessions.workspace_panes jsonb) + 5-state status indicator overhaul (streaming/tool_running/waiting_for_input/idle/error) + startup hung-row sweep + stale messages_status_check constraint dropped + detectSameNameLoop reverted (dead code) + stop-handler writes cancelled status v1.12.1
v1.12.2 Live tok/s + ctx_used display next to status indicator while streaming (frontend-only) v1.12.2
v1.12.3 Stale-stream banner — "Previous response didn't complete. [Retry] [Discard]" when streaming row > ~60s with no new tokens. POST /api/chats/:id/discard_stale backend endpoint v1.12.3
v1.12.4 Refactor only — inference.ts (1700 LoC) split into inference/ directory: turn.ts, stream-phase.ts, tool-phase.ts, error-handler.ts, sentinel-summaries.ts, payload.ts, xml-parser.ts, sentinels.ts, budget.ts, types.ts, index.ts. Shipped as rc1/rc2/rc3 → final. No behavior change. Lined up stream-phase.ts as the swap target for v1.13 AI SDK migration v1.12.4
v1.13.0 message_parts table (id, message_id, sequence, kind, payload jsonb, created_at) with kinds text/tool_call/tool_result/reasoning/step_start. CHECK constraint, (message_id, sequence) unique + index. Dual-write at every site that wrote tool_calls/tool_results JSON (stream-phase finalize, skills × 2, messages.ts answer flow, chats.ts × 2). ToolDef<T> gained `category: 'read_only' 'write'`. v1.x registry rejects write. Old JSON columns remain authoritative for reads. Strangler-fig phase 1
v1.13.1-A AI SDK v6 install + streamCompletion adapter. ai@^6, @ai-sdk/openai-compatible@^2. provider.ts wraps createOpenAICompatible against config.LLAMA_SWAP_URL. streamCompletion rewritten as adapter over streamText. XML fallback parser preserved for qwen3.6's inline <tool_call> emissions. Patched mid-flight: AI SDK v6 swallows abort signals silently — explicit if (signal?.aborted) throw after stream drain. Without it, stop button writes complete instead of cancelled. reasoning-delta counted + dropped (re-captured in -C). Known regression flagged: live mid-stream tps gone (single trailing publish; TODO for delta-cadence interpolation against result.usage) (umbrella tag)
v1.13.1-B messages_with_parts view with COALESCE fallbacks against legacy JSON columns. Read sites switched: chats.ts:427, messages.ts:95, ws.ts:27, payload.ts, compaction.ts. Perf verified at 1ms for 42-message chat. reasoning_parts column added to the view (consumed in -C). API contract preserved. Parts become source of truth at read; JSON columns kept by dual-write only (umbrella tag)
v1.13.1-C ask_user_input correlation ported to parts. messages.ts:478/549 now JOINs message_parts on payload->>'id' and payload->>'tool_call_id'. Downstream call sites updated to {message_id, payload} shape. 404 fallback for pre-v1.13.0 history (acceptable scope). Reasoning end-to-end: reasoning-delta accumulated in stream-phase.ts adapter via StreamResult.reasoning (simpler than the brief's StreamPhaseState approach); partsFromAssistantMessage accepts optional reasoning, emits at seq 0; finalizeCompletion + executeToolPhase dual-write reasoning parts; payload.ts reads reasoning_parts from view, collapses into OpenAiMessage.reasoning; toModelMessages emits AI SDK ReasoningPart in assistant content array. Smoke: 361 chars reasoning at seq 0, 429 chars text at seq 1 v1.13.1 (ac1a71f)
v1.13.3 Cleanup bundle, 4 independent items. (1) ALTER DATABASE boocode SET statement_timeout = '30s' — caps damage from query-plan regression on the view's nested subselects; documented in schema.sql since ALTER DATABASE can't run inside a DO block. (2) Alpha-sorted tool registry — .sort((a, b) => a.name.localeCompare(b.name)) at ALL_TOOLS export; llama.cpp prompt cache hits on byte-identical prefixes, tool-order drift killed hit rate every turn. (3) Periodic 60s in-process sweeper marks streaming rows older than 5 min as failed and publishes chat_status='idle' so the UI dot drops — closes mid-session crash UX gap that the startup sweep (v1.12.1) only handled at boot. (4) experimental_repairToolCall wired through AI SDK v6 streamText — routes malformed tool calls to a logged passthrough instead of crashing the stream. Owed since v1.13.1-A. 173/173 tests pass (+1 alpha-ordering test) v1.13.3 (a08d809)
v1.13.4 Two-tier compaction prune. services/inference/prune.ts with pure selectPruneTargets decision helper. Tier 1 hides stale tool_result parts via message_parts.hidden_at at the 20k-freed threshold (cheap, no inference call); tier 2 falls back to anchored summarize when prune alone isn't enough. Schema additions: message_parts.hidden_at column + partial index ON (message_id) WHERE hidden_at IS NULL. messages_with_parts view filters hidden parts so payload assembly never sees them. Avoids burning an inference round on every overflow. opencode-pattern half-shipped in v1.11.0 — this closes it. v1.13.4 (ec8593c)
v1.13.5 opencode truncate.ts port — full tool output retrievable via opaque id. New services/truncate.ts with tr_<12 base32> ids on tmpfs (/tmp/boocode-truncations, 0o700, 5MB cap matching view_file's MAX_FILE_BYTES, 7-day TTL). Three exports: storeTruncation, readTruncation, truncateIfNeeded (wrap-or-passthrough helper). New view_truncated_output(id) tool retrieves the full content; model never sees the truncation dir (resolved server-side). Wired through 5 of 7 tool sites: view_file, list_dir, web_fetch, codecontext_client, plus alpha-sorted into ALL_TOOLS (count 19→20). cleanupTruncations piggybacks on the v1.13.3 60s sweeper (TTL pass + orphan reap via parts query on payload->'output'->>'outputPath'). grep and find_files deferred (need file_ops refactor to expose uncapped output). 186 tests (was 179, +7 in truncate.test.ts). v1.13.5 (f8fc5db)
v1.13.6 Compaction head-assembly audit + reasoning fix. Audit traced compaction's summary path post-v1.13.1-B read flip across three quadrants — Q1 view read (clean), Q2 parts shape (clean), Q3 reasoning render (FIX NEEDED). v1.13.1-C wired reasoning end-to-end into inference/payload.ts but missed the compaction read site, silently degrading summary quality for reasoning-channel models (qwen3.6) since -C shipped. Fix: CompactionMessage extended with reasoning_parts field; SELECT pulls reasoning_parts from messages_with_parts; buildHeadPayload (now exported for tests) prefixes assistant content with <reasoning>...</reasoning>\n\n<content> when reasoning is present; standalone <reasoning> tag for tool-call-only turns; omits tag when reasoning is null or empty. 4 new render-branch tests (190 total). v1.13.6 (81d837c)
v1.13.7 (uncommitted) Stability bundle, 5 fixes from production observability gap. (1) provider.tsincludeUsage: true on createOpenAICompatible. @ai-sdk/openai-compatible defaults this false, omitting stream_options.include_usage from request body; llama-swap never emitted the usage block, so result.usage.inputTokens/outputTokens resolved undefined and tokens_used/ctx_used landed NULL in every assistant row since v1.13.1-A. Surfaces tokens in StatsLine + persisted DB rows going forward (no backfill). (2) MessageList.tsx:48hasText = m.content.trim().length > 0. AI SDK v6 streaming occasionally emits a leading \n text-delta on tool-call-only turns; the literal newline passed length > 0 and rendered an empty bubble + ActionRow between each tool call. (3) MessageBubble.tsx:654 — same trim on hasContent (defensive, no-tool-calls path). (4) payload.ts:64buildMessagesPayload skips assistant rows with status='failed' AND status='complete' && empty content && no tool_calls. Without this, a trailing empty/failed assistant + the next attempt's placeholder produced "Cannot have 2 or more assistant messages at the end of the list" rejections from the upstream API. (5) budget.ts:11BUDGET_NO_AGENT = 30 (was 15). No-agent mode shares the read-only-agent toolset at runtime; the cautious 15-cap was forward-looking for write tools that haven't landed. 190/190 tests still pass.

v1.13.2 deliberately deferred — keep the dual-write through v1.13.4v1.13.11 as rollback insurance. Drop legacy columns last.


Shipped (v1.13.x — strangler-fig closed 2026-05-23)

All v1.13.x batches use the vMAJOR.MINOR.PATCH-slug tag scheme adopted 2026-05-22. CHANGELOG.md is the canonical per-tag record (slug describes what shipped; tag name alone recalls the batch). The v1.13.x line ran 21 batches over a single intense window; the umbrella v1.13 tag sits on 211e903 (same commit as v1.13.20-drop-legacy-cols), marking the strangler-fig closed. Tags in chronological order:

  • v1.13.0-ai-sdk-v6 — AI SDK v6 migration; streamCompletion adapter; messages_with_parts view; reasoning_parts end-to-end
  • v1.13.1-cleanup-bundlestatement_timeout='30s', alpha-sorted tool registry, 60s stuck-row sweeper, experimental_repairToolCall pass-through
  • v1.13.2-compaction-prune — two-tier prune; message_parts.hidden_at column + partial index; messages_with_parts view CASE refinement
  • v1.13.3-truncate — opencode truncate.ts port; opaque tr_<…> id, view_truncated_output(id) tool, tmpfs storage
  • v1.13.4-reasoning-fix<reasoning> prose-prefix in compaction head-assembly for tool-bearing turns
  • v1.13.5-stability-bundleincludeUsage: true on provider, hasText trim guard, BUDGET_NO_AGENT 15→30, trailing-empty-assistant filter
  • v1.13.6-prefix-stabilitybuildSystemPromptWithFingerprint SHA-256 + per-session drift observer
  • v1.13.7-compaction-trigger — overflow trigger lowered to floor(0.85 × ctx_max)
  • v1.13.8-tool-costtool_cost_stats SQL view + per-tool rolling 100-call mean in AgentPicker
  • v1.13.9-agentlint — instruction-file AgentLint pass; identity-openers removed; CLAUDE.local.md to .gitignore
  • v1.13.10-openspecopenspec/changes/<slug>/{proposal,tasks,design}.md shape; archived batch docs preserved via git mv
  • v1.13.11-tools — tiered tool loading via BOOCODE_TOOLS env (core | standard | all)
  • v1.13.12-ws-schemas — Zod schemas for all 27 wire-format frames; publishFrame / publishUserFrame wrappers; parity test
  • v1.13.13-ws-publish — all ~80 publish sites converted to the typed wrappers; every WS frame now Zod-validated at boundary
  • v1.13.14-skills-audit — 26 skills vendored + audited via 5 parallel agent teams; 14 kept, 11 dropped, 1 migrated to BOOCHAT.md/BOOCODER.md
  • v1.13.15-codecontext-synth — forced second-inference synthesis pass for codecontext overview tools (truncation-aware extraction; auto-fetched top-N files + project docs; 32k payload-budget contract preserved)
  • v1.13.16-xml-parser — Anthropic <invoke> parser support + Levenshtein-based unknown-tool recovery hints (qwen3.6 drift to Claude Code-style tool names like read_file); xml-parser test coverage
  • v1.13.17-cross-repo-readsrequest_read_access tool + per-session allowed_read_paths grants; pathGuard extended with extraRoots; pause/resume reuses the ask_user_input mechanism
  • v1.13.18-codecontext-file-pathresolveProjectPath in codecontext_client.ts realpath-resolves file_path arg the same way target_dir was; closes the silent-fail path the sidecar exhibited on relative paths
  • v1.13.19-html-artifact-panes — pane-based artifact viewer with on-request HTML; <!DOCTYPE html> detection adds message_parts.kind='html_artifact' row; Markdown + HTML panes both open via "Open in pane" affordance; iframe sandbox allow-scripts allow-clipboard-write allow-downloads (no allow-same-origin, srcDoc); CSP connect-src 'none'. Scope-revised mid-design from auto-bias-to-HTML to Markdown-default / HTML-on-request
  • v1.13.20-drop-legacy-cols — final strangler-fig step. Drops messages.tool_calls + tool_results columns; 10 dual-write sites removed (recon caught 2 beyond the original roadmap inventory); messages_with_parts view simplified to parts-only subselects via CREATE OR REPLACE before the column DROPs (Postgres ordering constraint). Adversarial-review catch: discard_stale had a RETURNING tool_calls, tool_results clause; fixed via two-step UPDATE-then-SELECT-from-view. Message API type retains the fields — view synthesizes them from parts so the wire shape is unchanged
  • v1.13umbrella tag on the same commit as v1.13.20. Marks the AI SDK v6 + parts-table migration complete

The v1.13.x line is closed. Three batches still sit in the In flight column conceptually but none of them are v1.13.x scope: live-smoke of v1.13.19 (manual browser exercise of the artifact panes — five minutes, independent), and the two v1.14 branches below. Independent siblings (v1.14.x-mcp, v1.14.x-html, v1.16) can ship in any order relative to v1.14 itself.


v1.14 — Phase C: outer agent loop

Goal: explicit multi-step loop per opencode prompt.ts runLoop(). Replace the current ad-hoc tool-call recursion.

Scope:

  1. Outer loop continues until model returns non-tool finish OR step cap hit. Step ≠ tool call: one step can contain multiple tool calls in parallel.
  2. agent.steps ?? Infinity per-agent step cap. AGENTS.md gains steps: field. Refactorer steps: 5, Architect steps: 20, etc.
  3. Step-boundary events (step_start, step_finish) explicit in the parts stream. Per-step snapshot for revert (planned for BooCoder; backend-only in v1.14).
  4. Doom-loop guards (v1.11.6) migrate from "abort recursion" to "raise within loop iteration." Same predicate, different control flow.

Lift sources:

  • anomalyco/opencode session/prompt.ts runLoop() outer agent loop
  • anomalyco/opencode agent.steps per-agent step cap
  • AGENTS.md extensions for steps, output_schema (Qodo agent.toml pattern), exit_expression (Qodo pattern), execution_strategy (Qodo plan/act)
  • Reference: RA.Aid three-stage Research/Planning/Implementation as AGENTS.md design principle; expert-tool escape hatch pattern (most subtasks on routine model, escalate to qwopus27b only when needed)
  • Reference: Roo Code Boomerang Tasks — orchestrator-with-capability-restriction pattern. Adopt as AGENTS.md design principle (orchestrator role can call only dispatch tools, no file reads / MCP / shell).

Dependencies: v1.13 merged.

Estimated: ~800 LoC.

Shipped as v1.14.0-outer-loop. Explicit while (stepNumber < effectiveCap) loop in turn.ts, per-agent steps: field from AGENTS.md frontmatter, MAX_STEPS=200 ceiling, doom-loop guard migrated to loop-iteration style.


v1.14.x-mcp — single-server MCP-client proof-of-concept (NEW, 2026-05-22)

Goal: validate the MCP-client loop end-to-end against one real MCP server before committing to the full opencode mcp/index.ts port at v1.15. Small, throwaway-if-needed, slots between v1.14 and v1.15 without disrupting either.

Scope:

  1. Add a hardcoded MCP client (single server) to BooChat. Initial target: Context7 (Sam already uses it via opencode, so the config is known to work). Remote HTTP transport at https://mcp.context7.com/mcp with optional CONTEXT7_API_KEY header.
  2. Use the official @modelcontextprotocol/sdk TypeScript client. No SSE transport yet (deferred to v1.15). Stdio transport not needed for Context7.
  3. Tool discovery on startup: tools/list. Tools surface in BooChat alongside view_file/grep/etc., prefixed context7_* to avoid collisions.
  4. Read-only invariant guard: the client must reject any MCP tool whose annotations.readOnly is false (or absent). Fail-closed. This is BooChat-specific defense-in-depth — v1.15 lifts this restriction for BooCoder.
  5. Per-server enabled flag in agents.ts. No glob patterns yet.
  6. No OAuth. Context7 supports an API key header; that's it for v1.14.x. OAuth lands in v1.15.

What this proves:

  • MCP protocol loop works end-to-end against a real server in BooCode's Fastify backend.
  • Tool-discovery → tool-list → tool-call → result-render → context-budget accounting all hold.
  • Read-only enforcement at the client layer is sound.
  • Config schema shape is right before v1.15 commits to the opencode-compatible JSON config.

What this does NOT do:

  • No SSE transport. (v1.15.)
  • No OAuth flow. (v1.15.)
  • No multiple servers. (v1.15.)
  • No per-agent server allow/deny. (v1.15.)

Dependencies: v1.13 merged (parts table for tool-call/tool-result emission).

Estimated: ~150 LoC.

Skip-condition: if v1.14 finishes and Sam wants to leap straight to v1.15, fold this into the early steps of v1.15.

Shipped as v1.14.1-mcp-poc. Context7 MCP client validated end-to-end.


v1.14.x-html — pane-based artifact viewer with Markdown + HTML (REVISED, 2026-05-23)

Goal: every assistant message gets an "Open in pane" affordance that renders it as an artifact — Markdown by default (the model's normal output), HTML only when the user explicitly asks for it (e.g. "render this as HTML", "make me a dashboard", "build an interactive diagram"). Both artifact types open in BooChat's existing workspace splitter. Markdown panes have Copy (raw source) + Download (.md); HTML panes have Download (.html) only. No inline iframe preview — artifacts are pane-only.

Inspired by Thariq Shihipar's "HTML > Markdown at length" pattern (claude.com/blog/using-claude-code-the-unreasonable-effectiveness-of-html, May 20 2026), but scoped down from that post's "auto-bias to HTML for >100 lines" recommendation: Markdown stays the default everywhere, HTML is an on-request rendering target for cases where interactive controls / diagrams / side-by-side layouts pay off.

Scope:

  1. Model-side prompting (no code change, just AGENTS.md guidance):
  • Add HTML-on-request rule to global AGENTS.md: "Stay in Markdown by default for all outputs, short or long. Switch to a self-contained <!DOCTYPE html>...</html> artifact only when the user explicitly asks (e.g. 'render this as HTML', 'make a dashboard', 'build a diagram')."
  • Inline the web-artifacts-builder "avoid AI slop" design principles for when HTML is requested: no excessive centered layouts, no purple gradients, no uniform rounded corners, no Inter font, no generic AI aesthetics.
  • Cite Thariq's blog post in the rule comment so future audit passes know where the design conventions came from.
  1. Detection at the BooChat backend. In apps/server/src/services/inference/stream-phase.ts post-processing: detect any assistant text part starting with <!DOCTYPE html> (case-insensitive, whitespace-trimmed) — or wrapped in a fenced ```html block — and tag it as an HTML artifact. Emit a new part kind html_artifact into message_parts (CHECK constraint update). Payload: {html_content, char_count, title}. Title pulled from <title> tag or first <h1> if available. Detection is opportunistic — when the model produces HTML (because the user asked), the tag fires; otherwise the message stays plain-Markdown and no html_artifact part is written.
  2. Pane-only render surface. Every assistant message in the chat stream gets an "Open in pane" affordance (icon button in the message footer, alongside the existing copy/regenerate controls). Clicking it opens the message as an artifact pane in BooChat's existing workspace splitter, alongside the file viewer and BooTerm. Pane is dismissible. Pane state persisted via sessions.workspace_panes jsonb (the v1.12.1 schema already supports this).
  • Markdown pane — renders via the same Markdown component used inline in MessageBubble (so syntax highlighting, fenced code blocks, tables, etc. all work). Header carries Copy (writes raw Markdown source to clipboard via navigator.clipboard.writeText) and Download (.md) buttons.
  • HTML pane — renders the artifact in a sandboxed iframe at full pane height. Header carries Download (.html) only. No Copy button — HTML source isn't useful clipboard content; if the user wants the source they can Download and inspect.
  1. Download path & filename slug. Both formats write to /opt/<project>/.boocode/artifacts/<slug>-<unix-timestamp>.<ext> (path-guarded same as native write tools), and surface an OS download link via the existing file-serving path.
  • Markdown slug: derived from the message's first heading (# ...) if present, else the first 6 words of the message body, lowercased + hyphenated.
  • HTML slug: derived from the artifact's <title> tag if present, else first <h1>, else first 6 words of the inner text. Same lowercase-hyphen treatment.
  1. Security stance for HTML pane — locked 2026-05-22: the iframe is sandboxed with sandbox="allow-scripts allow-clipboard-write allow-downloads". Crucially, omit allow-same-origin so the artifact has its own opaque origin and cannot read BooChat's cookies, Authelia session, or DOM. Backend serves the iframe content via srcdoc=... inline (not src=) so no separate URL exists to disclose. CSP header on the iframe response: default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; font-src data:; connect-src 'none'. The connect-src 'none' is the key clause — artifacts can't fetch(), can't open WebSockets, can't ping a tracking pixel, can't exfiltrate. JS runs (so interactive knobs/sliders/copy-as-prompt buttons work) but nothing else network-touching does.
  2. Frontend components:
  • apps/web/src/components/MarkdownArtifactPane.tsx — pane shell + header (Copy + Download) + Markdown render reusing the existing component.
  • apps/web/src/components/HtmlArtifactPane.tsx — pane shell + header (Download only) + <iframe srcdoc={html_content} sandbox="allow-scripts allow-clipboard-write allow-downloads" />.
  • MessageBubble.tsx — add "Open in pane" affordance to every assistant message footer. Dispatches workspace-pane action {type: 'markdown_artifact' | 'html_artifact', message_id, html_content?}. When the message has an html_artifact part, the affordance opens as an HTML pane; otherwise it opens as a Markdown pane.
  • Download button → POST to new endpoint /api/chats/:id/messages/:msg_id/artifacts/download?fmt=md|html which writes to disk (path-guarded) and returns the absolute path or pre-signed URL for the existing static-file serving route.
  1. No artifact persistence beyond the chat. Artifacts live in message_parts.payload->>'html_content' (for HTML) or are derived on-demand from the assistant message's content (for Markdown). Downloads go to /opt/<project>/.boocode/artifacts/ and are user-managed from there. No separate artifacts table.
  2. Token-budget guard. Single HTML artifact can be at most 1MB of HTML in message_parts.payload. Larger triggers a streaming abort with a friendly error: "Artifact exceeded 1MB; consider splitting into multiple files or reducing inline assets." Markdown artifacts have no separate cap — they're bounded by the existing message-size envelope.
  3. No web-artifacts-builder skill vendor. That skill (anthropics/skills/web-artifacts-builder) is built for Claude.ai's runtime with Vite + Parcel + tspaths + html-inline toolchain. BooChat has no shell execution surface. The pattern transplants; the toolchain doesn't. Treat the skill's "avoid AI slop" design principles as conventions inlined in the HTML-on-request AGENTS.md rule. The init/bundle scripts are out of scope.

Lift sources:

  • claude.com/blog/using-claude-code-the-unreasonable-effectiveness-of-html (Thariq Shihipar, May 20 2026) — design conventions and use-case taxonomy (specs/code-review/design/reports/custom editors). The "auto-bias for >100 lines" recommendation is deliberately NOT lifted.
  • HTML iframe sandbox spec (web platform standard, no license issues).
  • anthropics/skills/web-artifacts-builder — design-principle reference only ("avoid AI slop" rules). Do not vendor the toolchain.

Dependencies: v1.13 merged (message_parts table is where HTML artifacts live). Independent of v1.14 (outer loop) and v1.14.x-mcp (MCP PoC). Can ship in any order relative to those.

Estimated: ~400 LoC. Roughly half backend (HTML detection + part-kind extension + download endpoint + path-guard integration + Markdown slug derivation) and half frontend (two artifact-pane components + MessageBubble affordance + pane integration + download wiring).

Schema addition:

  • message_parts.kind CHECK constraint adds 'html_artifact' to the allowed set.

Skip-condition: none — independent batch, ships clean any time after v1.13. Pane-based artifact view is a structural UX improvement (full-height read surface for long replies, durable download path) on top of the HTML-on-request rendering capability.

Shipped as v1.13.19-html-artifact-panes on 2026-05-23. Two scope-revisions during impl: (a) the HTML-on-request rule landed in BOOCHAT.md (always-true rules layer), not data/AGENTS.md (per-agent registry) — per BOOCHAT.md's own convention block. (b) Pane state stayed reference-only — {chat_id, message_id, title} — content fetched on mount via the existing chat-messages endpoint (Markdown) and a new GET /api/chats/:id/messages/:msg_id/html_artifact (HTML). Storing content in pane state would have ridden 1MB blobs through the session_workspace_updated WS frame and bloated the jsonb column on multi-pane sessions. Defense-in-depth additions beyond the original proposal: X-Content-Type-Options: nosniff + Content-Security-Policy: sandbox on the GET serve route, and assertArtifactsDirSafe realpaths the artifacts dir after mkdir to close a symlink-escape gap that would otherwise let a planted symlink under .boocode/artifacts/ route writes outside the project root. Smoke not run pre-tag; first deploy is the smoke.


v1.15 — Phase D: permission ruleset + full MCP client

Goal: wildcard permission ruleset (opencode evaluate.ts pattern) and a proper MCP client implementation. Foundation for BooCoder to gate writes; immediate value for codecontext to be re-wired as a real MCP server.

Scope:

  1. Wildcard rule matcher: { permission, pattern, action: 'allow' | 'deny' | 'ask' }. Last-match-wins. Per-agent rulesets layer under per-session rulesets.
  2. Full MCP client implementation: stdio (local subprocess) + SSE (remote HTTP) transports, tools/list discovery, tools/call invocation, OAuth via Dynamic Client Registration (RFC 7591), per-server enabled flag, glob patterns for per-agent tool whitelisting (matching opencode's tools config shape).
  3. codecontext sidecar gets re-pointed from static wrappers (v1.12) to real MCP. New connectors become a config-only addition.
  4. UI: permission-ask flow when a tool requires ask action. Modal or inline card with Allow once / Allow always / Deny. Reuses v1.9.7 elicitation surface.
  5. BooChat stays read-only by default — the read-only invariant guard from v1.14.x carries forward (defense-in-depth even with the ruleset).
  6. Config shape: match opencode's JSON schema near-verbatim so any opencode user can copy mcp blocks from ~/.opencode/config.json into BooCode unchanged. Schema is not copyrightable; matching it is pure interoperability.

v1 MCP scope limit (security): local-stdio MCP servers and Context7-style API-key remote servers only. Remote MCP servers requiring OAuth tokens are deferred until BooCode has a real secret-storage primitive (sops-encrypted entries, Vault sidecar, or OS keyring). Reason: MCP OAuth tokens are bearer credentials for third-party services; storing them in plaintext PostgreSQL inside the BooCode DB widens the attack surface significantly if Authelia is bypassed. v1.15 ships the OAuth code path but the config schema rejects OAuth servers until secret storage lands.

Absorbs: Original Batch 12 (tool approval + plan/act mode) — same outcome via permission rules instead of mode enum.

Lift sources:

  • anomalyco/opencode permission/evaluate.ts wildcard ruleset
  • anomalyco/opencode mcp/index.ts MCP client (SSE transport, tools/list, tools/call, OAuth RFC 7591)
  • cline/cline plan/act invariant — read-only mode pattern (absorbed)

Dependencies: v1.13 merged (parts table for permission events). Independent of v1.14.

Estimated: ~600 LoC.

Shipped as v1.15.0-mcp-multi. Multi-server MCP client with stdio transport + config file, per-agent tool glob patterns in AGENTS.md frontmatter.


v1.16 — codesight repo_health

Call graph, circular dependency detection, dead code flagging. Port analyze.mjs from spirituslab/codesight. New tool repo_health(project_id). In-process Node (not sidecar). Cache results keyed by (project_id, file_hashes_sig) in new repo_health_cache table.

Independent batch — ships clean any time after v1.13. Low leverage unless Sam actually uses the dead-code / circular-dep output.

Lift source: spirituslab/codesight analyze.mjs. Drop VS Code wrapper.

Dependencies: v1.12 merged (can reuse codecontext parse output where overlapping).

Estimated: ~400 LoC.


v2.0 — BooCoder: pending changes + dual execution paths + ACP host + MCP server

Major version bump. New app apps/coder/ inside the existing monorepo (not a separate repo). Shipped with database rename boocodeboochat and subdomain split (code.indifferentketchup.com → BooChat, coder.indifferentketchup.com → BooCoder).

Shipped v2.0.0v2.0.5. All 8 phases complete plus v2.0.5 (Arena, FAST_MODEL, Qwen dispatch). See retrospective below.

Three protocol roles in one surface:

  1. MCP client (write-capable allowed). Inherits the v1.15 client unchanged. BooCoder can enable write-capable MCP servers (@modelcontextprotocol/server-filesystem write tools, git commit MCP servers, etc.). All MCP writes route through the same pending_changes queue as native writes. Per-task allow/deny means dispatched tasks can have a different MCP roster than the interactive shell.
  2. MCP server (BooCoder's own primitives). New apps/coder/services/mcp_server.ts exposes boocoder.create_task, boocoder.list_pending_changes, boocoder.apply, boocoder.reject, boocoder.dispatch_external_agent, boocoder.list_worktrees as MCP tools. Stdio transport for local consumers (Sam's opencode in Termius), HTTP for remote (deferred until OAuth + secret storage). This is what makes external opencode-on-the-host BooCoder-aware.
  3. ACP client (host). Replaces the raw-PTY dispatch path for ACP-capable agents. Spawns opencode acp and goose acp as JSON-RPC stdio subprocesses. Native session lifecycle, mid-session model/mode switching, file-operation events surfaced as diffs in the BooCoder UI, terminal events that route into BooTerm, permission prompts answered via real dialogs. MCP servers configured in BooCoder are auto-forwarded to the dispatched ACP agent (per goose docs — context_servers is the field name). One MCP config drives every dispatched agent.

Two execution paths, same surface (the answer to the May 18 "1 and 2 full featured" question):

Path A — in-process write-tool inference loop (Option B / native)

  • New write tools: edit_file, create_file, delete_file, apply_pending, rewind.
  • Edits queue in pending_changes (id, session_id, file_path, diff TEXT, status, created_at). Nothing touches disk until /apply.
  • Per-pane diff UI with Approve/Reject.
  • Path-guard layer (apps/coder/services/path_guard.ts) enforces per-project scoping using the v1.15 permission wildcard ruleset. Blanket /opt:rw mount, policy at the tool layer. Highest-priority test target: fuzz the path-guard against every traversal-attack pattern, including MCP-served filesystem writes.

Lift source: plandex-ai/plandex pending-changes data model and diff/apply/rewind UX vocabulary.

Path B — ACP/PTY dispatch to external CLI agents (Option A / dispatch)

  • New tool dispatch_external_agent(agent: 'opencode'|'claude'|'goose'|'pi', model: string, task: string, worktree: string).
  • Primary path: ACP subprocess for agents that support it (opencode opencode acp, goose goose acp). JSON-RPC over stdio. Native session/tool/file/terminal events.
  • Fallback path: raw PTY for claude/pi/smallcode via node-pty with cwd = /opt/<project> or a git worktree add /tmp/booworktrees/<session-id> worktree per dispatch.
  • Dispatch worker checks available_agents.supports_acp at runtime and picks the right transport. Same task table, same project registry, same pending-changes flow.
  • Captures stdout/stderr/exit-code into PostgreSQL stream tables (PTY path) or maps ACP events to the parts taxonomy (ACP path). WebSocket events surface to all three React surfaces.
  • One worktree per active dispatched session.
  • User picks per task via UI dropdown at task creation, or the in-process loop calls dispatch_external_agent itself.

Lift sources:

  • Dominic789654/agent-hub (Apache-2.0) — task DAG schema, dispatcher worker, project registry, human inbox. Primary architectural template.
  • getpaseo/paseo (AGPL-3.0, design only — no code lift) — daemon+clients architecture, --worktree feature-x flag, paseo run/ls/attach/send CLI verb shape, /handoff /loop /orchestrator skills concept.
  • Roo Code Boomerang Tasks pattern — orchestrator capability restriction + down-pass/up-pass context discipline (new_task message, attempt_completion result, no implicit inheritance) + explicit precedence override clause.
  • covibes/zeroshot blind-validation invariant — verify gate runs in separate agent context that only sees the diff and acceptance criteria, not the producing conversation.
  • ACP spec (agentclientprotocol.com) — local-subprocess ACP via stdio JSON-RPC. Remote ACP (HTTP/WS) is still work-in-progress per the spec maintainers; v2.0 uses stdio only.
  • Goose ACP docs (goose-docs.ai/docs/guides/acp-clients/) — context_servers auto-forward pattern. Critical: one MCP config drives every dispatched agent.

Shared infrastructure between A and B

  • tasks table (id, project_id, template_id, parent_task_id, state, input, output_summary, dependencies, agent, model, worktree_path, cost, started_at, ended_at)
  • task_templates table (reusable spec → task instantiations)
  • pipelines table + pipeline_runs (ordered template invocations)
  • available_agents table (name, install_path, version, supports_acp, supports_mcp_client, last_probed_at) — populated by startup probe (which opencode && opencode --version, etc.)
  • human_inbox view (state IN ('blocked', 'failed', 'needs_human'))
  • Worker process boocoder-dispatcher (systemd unit alongside Fastify): picks ready tasks, dispatches via A or B (and within B, ACP or PTY), captures output, marks state.
  • New boocode CLI as a thin WebSocket/HTTP client against the BooCoder API. Verbs: boocode run, boocode ls, boocode attach <id>, boocode send <id>. Mirrors Paseo's UX, license-clean implementation.
  • BooCoder-internal MCP server (see role 2 above) registered on the Fastify server alongside the existing HTTP/WS endpoints. Stdio transport for opencode-in-Termius; HTTP transport gated on OAuth + secret storage.

MCP server eval requirement: run BooCoder's internal MCP server through the anthropics mcp-builder skill's 10-question evaluation framework before shipping. Ten independent, read-only, complex questions with verifiable answers in XML format. If the eval doesn't pass, the MCP server isn't shippable.

Dependencies: v1.13 (parts table) + v1.14 (outer loop + step boundaries for revert snapshots) + v1.14.x (MCP-client PoC) + v1.15 (full MCP client + permissions for path-guard policy).

Estimated: ~1500 LoC for Path A + Path B + shared schema, plus ~400 LoC for the MCP-server role, plus ~300 LoC for the ACP-client role. Multiple sub-versions: v2.0.0 native + ACP, v2.0.1 MCP server, v2.0.2 polish.

Retrospective (2026-05-25, updated 2026-05-26): All 8 phases shipped. v2.0.0-alpha through v2.0.5. The full BooCoder foundation is complete: write tools with pending-changes queue, dispatcher with ACP/PTY dual paths, MCP server (6 tools, stdio transport, 10-question eval passed), CLI client, human inbox, Boomerang new_task orchestration, and path-guard fuzz suite (34 traversal-attack tests). v2.1 added host systemd + provider picker; v2.2 shipped the Paseo provider/dispatch stack (v2.2-paseo-providers + v2.2.1-pane-scoped-chats). Runtime isolation (v2.1 optional batch) remains optional pending production bake.


v2.1 — BooCoder runtime isolation (optional)

Per-session Docker sandbox spawned by BooCoder on first write. Only project path mounted, not /opt. Idle-timeout 30 min. Standard OpenHands runtime contract: HTTP API inside container, BooCoder calls in.

Skip-condition: if the v2.0 path-guard layer holds up under fuzzing + a few months of production use, runtime isolation becomes optional hardening rather than necessary defense. Track but don't commit.

Lift source: OpenHands/OpenHands V1 runtime pattern.

Dependencies: v2.0.

Estimated: ~600 LoC.

Status: Still optional. v2.0 path-guard fuzz suite (34 traversal-attack tests) passed. No production pressure to containerize yet.



v2.2 — Paseo-style providers

Shipped v2.2-paseo-providers (2026-05-26). Paseo-equivalent provider stack for BooCoder. Seven providers (boocode, cursor, claude, opencode, goose, qwen, copilot) with snapshot API (provider-snapshot.ts, ACP cold probe, per-provider model merge, cursor models from ACP). Frontend AgentComposerBar replaces v2.1's ProviderPicker — provider / mode / model / thinking in the coder composer; SlashCommandPicker + useProviderSnapshot. ACP dispatch rewritten (acp-dispatch.ts, acp-stream.ts, acp-spawn.ts, agent-turn-persist.ts, acp-tool-snapshot.ts) with Paseo merge/stream/persist, inline PermissionCard prompts, and reasoning_delta WS frames. Agent slash-command hints via ACP available_commands_update + AgentCommandsHint. Arena and MCP entry points accept mode_id / thinking_option_id. SSH helpers removed; host exec via host-exec.ts. Openspec archived to openspec/changes/archived/v2.2-paseo-providers.md.

Shipped v2.2.1-pane-scoped-chats (2026-05-26, same commit). Follow-up fixes: pane-scoped chat resolution (resolveChatId requires pane_id; client seeds dedicated chats per coder/terminal pane), CoderMessageList BooChat-style tool timeline, WS user-delta replace-not-append, inference buildMessagesPayload orphan tool_calls stripping for shared chats with ACP history.


v2.3 — Provider lifecycle (Paseo-style registry)

Planned. Config-backed provider registry (/data/coder-providers.json), merged built-ins + overrides, enable/disable toggles, two-tier probe (fast binary vs slow ACP session), generic ACP spawn from config without new code paths. Depends on v2.2 snapshot wire shape. Openspec: openspec/changes/v2-3-provider-lifecycle/. See CURRENT.md.

Lift source: Paseo provider docs (design only — no AGPL code lift).

Dependencies: v2.2.


v2.4 — BooCoder as ACP agent (driveable from external editors)

Goal: expose boocoder acp so Zed, JetBrains, Avante.nvim, CodeCompanion.nvim can drive BooCoder as their agent. Outbound exposure of the BooCoder write-tool surface to ACP-compatible editors.

Scope:

  1. New ACP server entry point: boocoder acp reads JSON-RPC over stdio, exposes BooCoder's task primitives as ACP sessions.
  2. BooCoder UI features remain optional: editor drives session via ACP; pending-changes queue still gates writes; user can approve/reject from either BooCoder's web UI or the editor's permission dialog (whichever responds first).
  3. Same auth model as the rest of BooCoder — editor must be reachable on the Tailscale mesh, or BooCoder is invoked with a short-lived token.

Why v2.4, not v2.0: outbound ACP-agent role is cheap once the inbound ACP-client side is implemented (same protocol library, server side), but it's a different product surface — driving BooCoder from external editors. Ship it after BooCoder's own surface stabilizes. (The v2.2 version number was used for the Paseo provider/dispatch batch shipped 2026-05-26.)

Lift source: zed-industries/codex-acp (Apache-2.0) as a server-side ACP reference implementation.

Dependencies: v2.0 + v2.2 (recommended; v2.1 runtime isolation optional).

Estimated: ~400 LoC.


v2.1.0 — Provider picker + model discovery

Shipped v2.1.0-provider-picker. Provider registry with 5 providers (boocode, opencode, goose, claude, qwen). Model discovery via LLAMA_SWAP_URL/upstream/<model>/props. /api/providers route returns installed providers with models. v2.1 ProviderPicker UI superseded by AgentComposerBar in v2.2. Agent-probe startup probe discovers installed agents on host, their versions, ACP support, and models. Booterm SSH host configurable via BOOTERM_SSH_HOST/BOOTERM_SSH_USER env vars.

Shipped v2.1.1-roadmap-cleanup. Roadmap reconciliation, README updates, openspec archive housekeeping. No runtime behavior changes.


v2.x — Optional / far future

  • Verify gate above pending-changesaugmentcode/augment-swebench-agent majority-vote ensembler pattern (K candidate diffs → ranker model picks winner). JSONL schema only, no code lift. Combine with zeroshot blind-validation invariant. v2.0+ optional batch.
  • PR-resolver toolqodo-ai/qodo-skills PR-resolver state machine (fetch issues → batch/interactive fix → inline reply). BooCoder v2.0+.
  • Record/replay LLM harness for testsqodo-ai/qodo-cover pattern (hashed prompt → fixture YAML). Re-implement in Vitest, don't vendor (AGPL). v1.13+ test infrastructure.
  • HMAC-chained audit logsipyourdrink-ltd/bernstein pattern. Small lift, adds tamper-evident session history. v1.13+ optional.
  • Tiered tool loadingeyaltoledano/claude-task-master pattern (env var: core / standard / all). ~30 LoC in agents.ts. Pattern-only lift (claude-task-master is MIT + Commons Clause; reimplement). Shipped as v1.13.11-tools.
  • Spec directory structureFission-AI/OpenSpec openspec/changes/<name>/{proposal,specs,design,tasks}.md shape for BooCode's own batch docs. Zero-dep documentation reformat, replaces ad-hoc boocode_batchN.md convention. Shipped as v1.13.10-openspec.
  • view_session_history MCP toolmemovai/memov snap/mem_history/validate_commit shape. Reference design for v1.13+ session-history feature.
  • taste-skill anti-slop ban list — vendor Leonxlnx/taste-skill SKILL.md after diff against existing frontend-design skill. Real value at v2.0+ when BooCoder generates frontend code (DubDrive, BooLab, Fathom).
  • AgentLint audit pass — manual review of BooCode's own CLAUDE.md/AGENTS.md/BOOCHAT.md/BOOCODER.md using 0xmariowu/AgentLint's 31 evidence-backed checks. Trim emphasis-keyword density, hit 60120 line sweet spot, SHA-pin Actions, ensure .env/CLAUDE.local.md are gitignored. One-evening pass, immediate ROI. Shipped as v1.13.9-agentlint.
  • budi install (Sam's host)siropkin/budi Claude Code 5-hook observer (SessionStart/UserPromptSubmit/PostToolUse/SubagentStart/Stop). Local SQLite, sub-ms hook latency, dashboard at localhost:7878. Not a BooCode lift — install globally for Claude Code session observability.
  • Multi-provider LLM (pi-ai pattern): Only if a concrete need for Anthropic / OpenAI / Mistral direct surfaces. llama-swap covers everything today.
  • Workflow graphs (microsoft/agent-framework concepts): Multi-agent coordination. Conceptual reference only. Realistically a v3.x topic.
  • Secret storage primitive (prerequisite for remote OAuth MCP servers). Pick between: sops-encrypted entries in PostgreSQL, HashiCorp Vault sidecar, or OS-level keyring on ubuntu-homelab accessed via a thin service. Unblocks remote OAuth MCP servers in BooCode generally. v2.x or earlier if a remote OAuth server (Sentry, Atlassian, etc.) becomes urgent.

Architecture target state

Containers (post-v2.0)

Container Port Mount Purpose Status
boochat (was boocode) 100.114.205.53:9500 /opt:/opt:ro Read-only chat + SPA host + MCP client Live (renames at v2.0)
booterm 100.114.205.53:9501 /opt:/opt PTY/tmux terminal sessions Live (May 2026)
boocoder (host systemd) 100.114.205.53:9502 full host FS (policy-gated) Write tools + ACP client + MCP client + MCP server + external-CLI dispatch Shipped v2.0.0v2.2.1 (systemd since v2.1.0)
boochat (Docker service boocode_db) 127.0.0.1:5500 boocode_pgdata volume Postgres 16-alpine (shared by all three) Live (DB renamed from boocode at v2.0)
codecontext :8080 (internal, Docker network) /opt:/opt:ro Go HTTP sidecar for code graph tools Live (v1.12.0)

Caddy routing target (post-v2.0)

code.indifferentketchup.com         → boochat   :9500   (SPA + chat API + MCP client)
coder.indifferentketchup.com        → boocoder  :9502   (SPA + write API + MCP client + MCP server HTTP)
coder.indifferentketchup.com/mcp    → boocoder  :9502   (BooCoder MCP server endpoint, when remote-MCP unlocked)
term.indifferentketchup.com         → booterm   :9501   (or routed under code.*/term/)

Schema additions by version

  • v1.11.0: messages.compacted_at, messages.summary, messages.tail_start_id, chats.needs_compaction
  • v1.11.7: none (pathGuard logic, no DB)
  • v1.12.0: none (codecontext stateless; truncation in-memory id-map with TTL cleanup)
  • v1.12.1: sessions.workspace_panes jsonb (workspace sync); drop deprecated session_panes table; drop stale messages_status_check constraint
  • v1.13.0-ai-sdk-v6: message_parts (id, message_id, sequence, kind, payload jsonb, created_at) + unique (message_id, sequence) + kind CHECK; messages_with_parts view with COALESCE fallbacks; ToolDef.category field (TS type, not DB)
  • v1.13.1-cleanup-bundle: ALTER DATABASE boocode SET statement_timeout = '30s' (op step, documented in schema.sql; doesn't survive volume reset)
  • v1.13.2-compaction-prune: message_parts.hidden_at TIMESTAMPTZ column + partial index (message_id) WHERE hidden_at IS NULL; messages_with_parts view filters hidden parts
  • v1.13.3-truncate: none (tmpfs id-map stored on disk under BOOCODE_TRUNCATION_DIR; no schema)
  • v1.13.4-reasoning-fix: none (compaction read-side change; CompactionMessage extended in TS, not DB)
  • v1.13.5-stability-bundle: none (provider config + 4 frontend/payload guards + budget constant, no schema change)
  • v1.13.6-prefix-stability: none — verify-and-measure batch, instrumentation only; drops the originally-planned system_prompt_cache table since recon proved input-layer mtime caches already achieve prefix stability
  • v1.13.7-compaction-trigger: none (compaction overflow trigger is a constant change in services/compaction.ts, no DB)
  • v1.13.8-tool-cost: tool_cost_stats SQL view over messages_with_parts (no new table — view + LATERAL jsonb_array_elements on tool_calls); rolling 100-call window
  • v1.13.9-agentlint: none (instruction-file audit + .gitignore add of CLAUDE.local.md, no DB)
  • v1.13.10-openspec: none (docs reorganization, git mv only)
  • v1.13.11-tools: none (env-var tier filter at request time, no DB)
  • v1.13.12-ws-schemas: none (Zod schemas + wrappers in TS, no DB)
  • v1.13.13-ws-publish: none (publish-site conversion + protocol-drift fix in compaction.ts, no DB)
  • v1.13.14-skills-audit: none (skills + AGENTS.md migration into git via .gitignore negation patterns; no DB)
  • v1.13.15-codecontext-synth: message_parts.kind CHECK constraint extended with 'synthesis' value (DROP + DO $$ pg_constraint idempotency-guarded re-add)
  • v1.13.16-xml-parser: none (parser change + new tool-suggestions.ts helper in TS, no DB)
  • v1.13.17-cross-repo-reads: sessions.allowed_read_paths text[] NOT NULL DEFAULT ARRAY[]::text[] (per-session cross-repo read grants)
  • v1.13.18-codecontext-file-path: none (path resolver in codecontext_client.ts, no DB)
  • v1.13.19-html-artifact-panes: message_parts.kind CHECK constraint extended with 'html_artifact' value (same v1.13.15 pattern)
  • v1.13.20-drop-legacy-cols: ALTER TABLE messages DROP COLUMN tool_calls, DROP COLUMN tool_results (the strangler-fig's final phase). messages_with_parts view rewritten to parts-only subselects via CREATE OR REPLACE VIEW BEFORE the drops (Postgres ordering constraint). v1.12.1 messages_status_check/messages_role_check cleanup block removed (one-shot effective long ago)
  • v1.14: agents.steps column (or AGENTS.md parser extension; no DB if file-only)
  • v1.14.x-mcp: none — single-server MCP-client PoC is config-only at first, no schema change
  • v1.14.x-html: message_parts.kind CHECK constraint extended with 'html_artifact' value
  • v1.15: permissions table, agent_permissions join, session_permissions join, mcp_servers (name, type, transport, url_or_command, enabled, config_hash, last_probed_at) registry
  • v1.16: repo_health_cache (project_id, file_hashes_sig, payload JSONB, created_at)
  • v2.0 (shipped): pending_changes, tasks, available_agents, human_inbox view; database renamed boocodeboochat
  • v2.2 (shipped): none (provider snapshot + ACP dispatch are runtime/services; pane chat scoping uses existing sessions.workspace_panes + chats)
  • v2.4: none (boocoder acp is a new entry point, not a schema change)

Lift sources (headline table)

Full inventory and rationale in boocode_code_review.md. Headline items below; anomalyco/opencode is canonical (not sst/opencode — correction 2026-05-22).

Source License Used for Where
anomalyco/opencode MIT, TS Compaction algorithms (session/compaction.ts + session/overflow.ts) v1.11.0
anomalyco/opencode MIT, TS Doom-loop guard (session/processor.ts DOOM_LOOP_THRESHOLD=3) v1.11.6
continuedev/continue Apache-2.0 DEFAULT_SECURITY_IGNORE_FILETYPES v1.11.7
nmakod/codecontext MIT, Go Architect: codebase map sidecar (8 MCP-shaped tools, static-wrapped) v1.12.0
anomalyco/opencode MIT, TS AI SDK v6 adoption + streamText swap + ReasoningPart shape v1.13.1
anomalyco/opencode MIT, TS Parts-message taxonomy (text/tool_call/tool_result/reasoning/step_start) v1.13.0
anomalyco/opencode MIT, TS experimental_repairToolCall via AI SDK v6 v1.13.3
anomalyco/opencode MIT, TS Two-tier compaction prune (message_parts.hidden_at + tier logic) v1.13.4
anomalyco/opencode MIT, TS tool/truncate.ts truncation + outputPath pattern (adapted: opaque id) v1.13.5
anomalyco/opencode MIT, TS 0.85×ctx_max overflow trigger formula v1.13.7-compaction-trigger
anomalyco/opencode MIT, TS session/prompt.ts runLoop() outer agent loop + agent.steps cap v1.14.0-outer-loop
Anthropic MCP SDK (TypeScript) MIT MCP client, single-server PoC v1.14.1-mcp-poc
claude.com/blog/using-claude-code-the-unreasonable-effectiveness-of-html (blog, pattern only) HTML-output bias rule + use-case taxonomy v1.14.x-html
anthropics/skills/web-artifacts-builder MIT (design-principle reference) "Avoid AI slop" conventions inline in AGENTS.md v1.14.x-html
mgechev/skills-best-practices MIT (pattern) 4-step skill validation protocol with paste-ready prompts v1.13.12 (skills audit)
mgechev/skillgrade MIT Agent-agnostic skill eval framework (eval.yaml + smoke/reliable/regression presets) v1.13.12 (skills audit) + ongoing
blog.codeminer42.com/stop-putting-best-practices-in-skills/ (blog, pattern only) Rules→recipes split: skills 6% invoke vs AGENTS.md 100% present v1.13.12 (skills audit)
platform.claude.com/docs/.../agent-skills/best-practices (docs, canonical) 500-line ceiling, gerund naming, progressive-disclosure patterns, MCP ServerName:tool_name format v1.13.12 + all future skills
anomalyco/opencode MIT, TS permission/evaluate.ts wildcard ruleset v1.15.0-mcp-multi (planned, not shipped)
anomalyco/opencode MIT, TS mcp/index.ts MCP client (stdio + SSE, tools/list, tools/call, OAuth RFC 7591) v1.15.0-mcp-multi
Aider-AI/aider Apache-2.0 Fallback aider/queries/tree-sitter-*.scm grammars v1.12 (fallback)
cline/cline Apache-2.0 Plan/Act invariant (absorbed into v1.15 permissions) v1.15
spirituslab/codesight MIT-ish Repo health analyzer (analyze.mjs) v1.16
plandex-ai/plandex MIT Pending-changes data model + diff/apply/rewind UX v2.0
Dominic789654/agent-hub Apache-2.0 Task DAG schema, dispatcher worker, project registry, human inbox — primary architectural template for v2.0 dispatcher v2.0
getpaseo/paseo AGPL-3.0 (design only, no code lift) Daemon+clients arch, CLI verb shape, worktree flag, provider snapshot/dispatch patterns v2.2 (shipped) / v2.x
agentclientprotocol.com spec + @zed-industries/agent-client-protocol SDK Apache-2.0 ACP client (host) — replaces raw-PTY dispatch for opencode/goose/cursor v2.0 → v2.2
anthropics/skills mcp-builder MIT MCP server build workflow + 10-question evaluation framework v2.0 (BooCoder MCP server)
zed-industries/codex-acp Apache-2.0 ACP server-side reference for boocoder acp v2.4
Roo Code: Boomerang Tasks Apache-2.0 (pattern only) Orchestrator capability restriction + down-pass/up-pass context discipline v1.14 (AGENTS.md) → v2.0 (real delegation)
covibes/zeroshot MIT (pattern only) Blind-validation invariant + complexity-classification conductor v1.14 (AGENTS.md) → v2.0 (verify gate)
OpenHands/OpenHands MIT Sandbox runtime contract v2.1
qodo-ai/agents MIT agent.toml schema (output_schema, exit_expression, execution_strategy) v1.14
qodo-ai/qodo-cover AGPL-3.0 (re-implement, don't vendor) Record/replay LLM response harness v1.13+ tests
qodo-ai/qodo-skills MIT PR-resolver state machine + provider-CLI adapter pattern v2.0+
augmentcode/augment-swebench-agent MIT Majority-vote ensembler (K diffs → ranker → winner) + JSONL schema v2.0+ optional
eyaltoledano/claude-task-master MIT+Commons Clause (pattern only) Tiered tool loading via env var + three model roles v1.13.x / v1.14
Fission-AI/OpenSpec permissive (verify) openspec/changes/<name>/{proposal,specs,design,tasks}.md structure for batch docs v1.13.x / v1.14
0xmariowu/AgentLint MIT 31 evidence-backed checks for CLAUDE.md/AGENTS.md quality Immediate manual pass; v1.12.x optional plugin
Leonxlnx/taste-skill MIT Anti-slop ban list + 3-dial parameterization pattern v2.0+ (BooCoder frontend output)
RA.Aid (ai-christianson) Apache-2.0 (pattern only) Three-stage Research/Planning/Implementation + expert-tool escape hatch v1.14 (AGENTS.md)
memovai/memov MIT (pattern only) .mem shadow timeline + snap/validate_commit MCP tool shape v1.13+ history tool design; v2.0+ drift gate
sipyourdrink-ltd/bernstein (verify) HMAC-chained audit log primitive v1.13+ optional
aimasteracc/tree-sitter-analyzer MIT Outline-first patterns (trace_impact tool) v1.12 (alt) / unscheduled
earendil-works/pi MIT Multi-provider LLM (pi-ai) v2.x (optional)
siropkin/budi (tooling, not lift) MIT Claude Code 5-hook observer for Sam's host workflow Immediate (install globally)
aaif-goose/goose Apache-2.0 ACP agent (goose acp) — dispatched alongside opencode in v2.0 Path B v2.0 (host install)

Decisions log

  • v1.13.7 stability bundle (2026-05-22, uncommitted). Five-fix sweep during the cosmetic-revert investigation surfaced two production-affecting regressions latent since v1.13.1-A. (1) @ai-sdk/openai-compatible includeUsage defaults to falseprovider.ts never asked llama-swap to emit usage, so tokens_used/ctx_used had been NULL in every assistant row since v1.13.1-A. The fix is one line at provider.ts:18. No backfill for historical rows. (2) AI SDK v6 streaming emits a stray \n text-delta on tool-call-only turns, which passed content.length > 0 and rendered an empty bubble + ActionRow between each tool call. Trim in MessageList.flatten (hasText) and defensively in MessageBubble (hasContent). (3) buildMessagesPayload did not filter trailing empty or failed assistant rows — combined with (2), a Continue retry produced …summary-assistant, empty-assistant, failed-assistant payloads and the upstream rejected with "Cannot have 2 or more assistant messages at the end of the list." Skip rules added at payload.ts:64. (4) BUDGET_NO_AGENT bumped 15→30. Every tool in ALL_TOOLS is read-only today; the cautious 15-cap was forward-looking for write tools that haven't landed. No-agent mode now matches BUDGET_READ_ONLY. None of the five changes touch schema or compaction — they're cleanup against a "v1.13.1-A regression that hadn't been caught yet" surface.
  • Skills taxonomy locked: AGENTS.md = rules, skills = recipes (2026-05-22). Codeminer42's multi-turn eval showed plain skills invoke 6% in clean runs vs CLAUDE.md/AGENTS.md 100% present. General workflow rules (TDD, paraphrase-before-quote, security gotchas, "never git pull/commit/push", alpha-tool-ordering, codecontext-not-RAG) belong in AGENTS.md; specific on-demand procedures (/skill scaffold-component, /skill run-release-checklist) belong in skills. Hooks are for automation, not instruction delivery. The 7 vendored v1.12 skills get an audit pass in v1.13.12 to sort each into the 4-way split (move to AGENTS.md / keep as recipe / move bulky context to references/ / delete). Validation via mgechev/skills-best-practices 4-step protocol + mgechev/skillgrade --smoke per skill. Anthropic's agent-skills/best-practices page becomes the canonical convention reference (500-line ceiling, gerund naming, MCP ServerName:tool_name format, progressive disclosure one level deep, etc.). Documented in BOOCHAT.md / BOOCODER.md to future-proof against re-adding workflow rules as skills.
  • HTML artifacts in BooChat locked (2026-05-22). Adopt Thariq Shihipar's "HTML > Markdown for outputs >100 lines" pattern. AGENTS.md gets the HTML-bias rule. Backend detection emits new html_artifact part kind. Frontend renders in three places: inline iframe preview in chat stream, "open in pane" workspace splitter integration, and download to /opt/<project>/.boocode/artifacts/<slug>-<timestamp>.html. Security: sandbox="allow-scripts allow-clipboard-write allow-downloads" with no allow-same-origin, CSP connect-src 'none', srcdoc= inline (not src=). All of Thariq's interactive examples (sliders/knobs/SVG diagrams/copy-as-JSON) work under this sandbox because they're entirely client-side. Don't vendor anthropics/skills/web-artifacts-builder — its Vite + Parcel toolchain can't run in BooChat (no shell). Treat the skill's "avoid AI slop" rules as design conventions inlined in AGENTS.md.

MCP and ACP protocol roles per surface (2026-05-22, locked)

  • BooChat = MCP client only. Read-only tool consumer. Per-server enabled flag. Hard rule: never enable a write-capable MCP server — the read-only invariant overrides protocol convenience. Defense-in-depth: client must reject any tool whose annotations.readOnly is false or absent.
  • BooCoder = MCP client + MCP server + ACP client (host) + ACP agent (driveable). Full matrix.
    • MCP client role: inherits v1.15 client; write-capable servers allowed but writes route through pending_changes queue.
    • MCP server role: BooCoder exposes its own task primitives (boocoder.create_task etc.) so external opencode sessions in Termius become BooCoder-aware. Stdio for local, HTTP gated on OAuth+secret storage.
    • ACP client (host) role: replaces raw-PTY dispatch for ACP-capable agents (opencode, goose). PTY retained as fallback for claude/pi/smallcode. Critical pattern: ACP clients auto-forward MCP context_servers to the dispatched agent (per goose docs) — one MCP config drives every dispatched agent.
    • ACP agent role: boocoder acp exposes BooCoder to Zed/JetBrains/Avante.nvim. Deferred to v2.4.
  • Why BooChat doesn't get ACP: ACP standardizes the editor→agent direction. BooChat doesn't drive agents; it is the chat. Adding ACP-agent to BooChat would convert it into an opencode-equivalent — different product. Skip.
  • MCP/ACP integration phasing: v1.14.x (single-server MCP-client PoC against Context7) → v1.15 (full MCP client + permissions) → v2.0 (BooCoder full matrix: write-capable MCP client + MCP server + ACP client) → v2.2 (Paseo provider snapshot + ACP dispatch — shipped) → v2.4 (BooCoder ACP agent for external editor drive).
  • Reference materials: anthropics mcp-builder skill (4-phase build workflow + 10-question eval framework — required for BooCoder's MCP server before shipping), opencode MCP/ACP docs as JSON-schema interop reference, goose ACP docs for the context_servers auto-forward pattern, agentclientprotocol.com spec (note: remote ACP via HTTP/WS still WIP, v2.0 uses stdio only).
  • v1 MCP scope limit (security): local-stdio MCP servers + Context7-style API-key remote only. Remote OAuth MCP servers (Sentry, Atlassian, etc.) deferred until BooCode has a real secret-storage primitive — token leakage from a PostgreSQL dump or Authelia bypass is a real attack surface that doesn't exist with local-stdio MCP.

Monorepo / multi-app structure (2026-05-22, locked)

  • BooCode is a 3-surface monorepo at /opt/boocode/: BooChat (apps/server + apps/web, :9500), BooCoder (apps/coder, :9502, shipped v2.0v2.2.1, host systemd), BooTerm (apps/booterm, :9501, live since May 2026). One React SPA hosts chat, coder, and terminal panes.
  • Single shared database boochat. Docker service boocode_db, all three surfaces connect to the same Postgres. Cross-surface joins are valuable (coder task → originating chat → term debugging session).
  • Mount strategy: blanket /opt:rw, policy enforcement at the write-tool layer. Per-project scoping is logic, not mount. Path-guard correctness becomes the highest-priority test target for v2.0 — fuzz it, property-test it, every traversal-attack pattern (including MCP-served filesystem writes).
  • External CLI agents on the host, not in containers. BooCoder shells out via local-exec PTY or ACP subprocess (node-pty, host shell, or child_process.spawn('opencode', ['acp'])). Host install inherits Sam's existing ~/.opencode/, ~/.claude/, ~/.config/goose/ configs without re-mounting. Containerize later only if a concrete reason emerges.

Strategic pivot: Paseo-equivalent dispatcher (2026-05-22, shipped v2.2)

Sam wanted BooCode to function like Paseo without using Paseo itself. Paseo is AGPL-3.0 — incompatible with BooCode's MIT license and its network-served deployment at code.indifferentketchup.com. Solution: reproduce the architecture in BooCode's existing Fastify + TS + PostgreSQL + React stack, using only license-clean patterns.

  • Primary architectural template: Dominic789654/agent-hub (Apache-2.0) — three-process model (board server + dispatcher + assistant terminal) and schema (tasks/projects/templates/pipelines/human_inbox).
  • Critical context-management primitive: Roo Code Boomerang Tasks pattern — orchestrator with intentional capability restriction, down-pass/up-pass context discipline, no implicit inheritance.
  • Observation pattern: Claude Code hooks (siropkin/budi reference) — register BooCode as the hook receiver for SessionStart/UserPromptSubmit/PostToolUse/SubagentStart/Stop.
  • Protocol-level Paseo equivalence (shipped v2.2): the ACP client + MCP server combination in BooCoder is the protocol-spelled version of Paseo's daemon. ACP gives multi-agent dispatch with structured events instead of free-form PTY output. MCP server gives BooCoder-as-task-board, callable from any MCP client (Termius-based opencode, future editors). One MCP config feeds every dispatched agent (via context_servers auto-forward). v2.2 added provider snapshot, mode/thinking, permission prompts, and Paseo-style stream/persist.

Next on this track: v2.3 provider lifecycle (config-backed registry, enable/disable, two-tier probe). See openspec v2-3-provider-lifecycle.

Earlier May 18 chat recommended Option A (thin orchestration shell over OpenCode) but explicitly called the choice not-locked. Sam's call this session: ship both paths in the same BooCoder surface. Option B / in-process loop handles interactive write work with native tools + pending-changes UI (v2.0 plandex pattern). Option A / PTY-or-ACP dispatch handles parallel/batch work where Sam wants to A/B opencode vs claude vs goose vs pi against the same task in separate worktrees. User picks per task. ACP replaces raw PTY wherever the agent supports it (opencode, goose); PTY fallback retained for claude/pi/smallcode.

v1.13.x cleanup line locked (2026-05-22)

The v1.13.x cleanup line shipped 21 batches over a single intense window in vMAJOR.MINOR.PATCH-slug form: v1.13.0-ai-sdk-v6 → v1.13.1-cleanup-bundle → v1.13.2-compaction-prune → v1.13.3-truncate → v1.13.4-reasoning-fix → v1.13.5-stability-bundle → v1.13.6-prefix-stability → v1.13.7-compaction-trigger → v1.13.8-tool-cost → v1.13.9-agentlint → v1.13.10-openspec → v1.13.11-tools → v1.13.12-ws-schemas → v1.13.13-ws-publish → v1.13.14-skills-audit → v1.13.15-codecontext-synth → v1.13.16-xml-parser → v1.13.17-cross-repo-reads → v1.13.18-codecontext-file-path → v1.13.19-html-artifact-panes → v1.13.20-drop-legacy-cols → umbrella v1.13 . Do not fold was the discipline — each batch has a distinct rollback surface, and bisecting a 750-LoC merge across four unrelated changes is worse than four separate dispatches. Held throughout; CHANGELOG.md is the per-tag canonical record.

v1.14v2.2 shipped (2026-05-26)

  • v1.14.0-outer-loop — explicit while loop, per-agent steps: cap, doom-loop migration
  • v1.14.1-mcp-poc — Context7 MCP client validated
  • v1.15.0-mcp-multi — multi-server MCP client, stdio transport, per-agent tool globs
  • v2.0.0-alpha through v2.0.5 — full BooCoder line: write tools, dispatcher (ACP/PTY), MCP server (6 tools, stdio, 10-question eval passed), CLI client, human inbox, Boomerang new_task orchestration, path-guard fuzz suite (34 traversal-attack tests), Arena, FAST_MODEL
  • v2.1.0-provider-picker — 5-provider registry, model discovery, /api/providers route, agent-probe startup probe, BooCoder host systemd migration
  • v2.1.1-roadmap-cleanup — roadmap/README/openspec archive housekeeping
  • v2.2-paseo-providers — 7-provider snapshot, AgentComposerBar, ACP dispatch rewrite, permission prompts, agent commands, cursor/copilot providers
  • v2.2.1-pane-scoped-chats — pane-scoped chat resolution, CoderMessageList tool UI, WS user-delta fix, inference orphan tool_call stripping

In flight

  • v2.3-provider-lifecycle — config-backed provider registry, enable/disable, two-tier probe (openspec drafted; not started). See CURRENT.md.

Numbering and scope-revision discipline during v1.13.x (2026-05-23)

The v1.13.x line ran 21 batches; planned-vs-shipped numbering diverged for half of them, and three batches had material scope revisions mid-design. Pattern that emerged and is worth carrying forward:

  • Patch numbers are assigned at ship time, not in planning. The proposal/openspec folder uses a planning slug (e.g. v1.14.x-html-artifact-panes); the final tag uses a concrete patch monotonic-per-minor (e.g. v1.13.19-html-artifact-panes). Avoids the "we said v1.13.8 but actually shipped seventh" confusion that ate two retrospective passes on the roadmap.
  • Scope-revise the proposal before dispatching. v1.13.19-html-artifact-panes flipped mid-design from "auto-bias to HTML for >100 lines" to "Markdown default, HTML on request" — the proposal got rewritten before recon. Far cheaper than discovering the wrong approach in implementation. The "brainstorm before code" discipline.
  • Recon-first dispatch finds 2530% more sites than the roadmap inventory. v1.13.20 recon caught 2 extra dual-write sites (chats.ts fork-clone + 2 in tool-phase.ts) and an extra fixture file. v1.13.19 recon corrected which Pane type to extend. Skipping recon to save a step doesn't.
  • Adversarial reviews catch what test suites miss. v1.13.19 reviewer caught silent error-promotion in openInPane; v1.13.20 reviewer caught a RETURNING tool_calls, tool_results clause that crashes in production but slips past green tests. Both are routine code-reviewer dispatches; both saved a same-day hotfix. Two-stage review (spec then quality) is non-negotiable when shipping fast.
  • Calendar-gated waits are production-safety hedges that don't apply here. v1.13.20 originally said "wait one week of production traffic on v1.13.1 before dropping columns." Sam called it out: single-user self-hosted, no rollback constraint, code-level audit + DB COUNT query is the actual safety check. Dropped the wait. Don't ritualize production-grade hedges in a single-user codebase.

v1.13 retrospective (what shipped)

  • v1.13.0message_parts table + dual-write at every JSON-write site. Old columns authoritative for reads. Reversible.
  • v1.13.1-A — AI SDK v6 (ai@^6, @ai-sdk/openai-compatible@^2). streamCompletion rewritten as streamText adapter. Silent-abort bug caught and patched (explicit if (signal?.aborted) throw). Known regression: mid-stream tps gone — TODO for delta-cadence interpolation against result.usage. Latent regression discovered v1.13.7: includeUsage defaults false on @ai-sdk/openai-compatible, so result.usage resolved empty all along; tokens_used/ctx_used NULL in every row since this version. Fixed in v1.13.7.
  • v1.13.1-Bmessages_with_parts view with COALESCE fallbacks. Read sites switched. 1ms for 42-message chat verified.
  • v1.13.1-Cask_user_input correlation ported to parts; reasoning end-to-end (361 chars reasoning at seq 0, 429 chars text at seq 1 in smoke). v1.13.1 tagged on ac1a71f. Latent regression discovered v1.13.6: reasoning was wired into the inference payload but NOT into compaction's head-assembly payload — summarizer model couldn't see reasoning for tool-bearing turns, degrading qwen3.6 summary quality. Fixed in v1.13.6.
  • v1.13.3 — bundle: statement_timeout=30s, alpha tool ordering, periodic stuck-row sweeper, repairToolCall wiring. Tagged on a08d809.
  • v1.13.4 — two-tier compaction prune. Tagged on ec8593c.
  • v1.13.5 — opencode truncate.ts port + view_truncated_output tool. Tagged on f8fc5db.
  • v1.13.6 — compaction head-assembly audit + reasoning fix. Closed the Q3 reasoning gap from v1.13.1-C. Tagged on 81d837c.
  • v1.13.7 — stability bundle: includeUsage fix + trim guards + payload filter + budget bump. Surfaces tokens (closes a v1.13.1-A latent regression where result.usage resolved empty), kills the empty-bubble + ActionRow noise between tool calls on single-tool-call turns, and unblocks Continue after cap-hit on chats that have trailing empty/failed assistants.
  • v1.13.6-prefix-stability — system-prompt prefix verify-and-measure batch (originally numbered v1.13.8 in the planning doc). Reframed mid-design from "add a system_prompt_cache table" to "instrument-and-prove" after recon showed input-layer mtime caches already achieve byte-stable prefixes. Smoke confirmed zero drift across 5 turns; dropped the planned DB table. Tagged on 81d837c.
  • v1.13.7-compaction-trigger — 0.85×ctx_max early trigger (planned as v1.13.8 / v1.13.9).
  • v1.13.8-tool-costtool_cost_stats SQL view + AgentPicker tooltip surfacing (planned as v1.13.9 / v1.13.10).
  • v1.13.9-agentlint — instruction-file AgentLint pass (planned as part of v1.13.11 skills audit; split into its own batch when it grew larger than fitting).
  • v1.13.10-openspecopenspec/changes/<slug>/{proposal,tasks,design}.md batch-doc structure adoption.
  • v1.13.11-tools — tiered tool loading via BOOCODE_TOOLS=core|standard|all env (~30 LoC; was a far-future optional item, slotted in).
  • v1.13.12-ws-schemas + v1.13.13-ws-publish — Zod schemas for all 27 wire-format frames, publishFrame/publishUserFrame wrappers, ~80 publish sites converted (planned as v1.13.10 / v1.13.11).
  • v1.13.14-skills-audit — 26 skills vendored + audited via 5 parallel agent teams; 14 kept, 11 dropped, 1 migrated to BOOCHAT.md/BOOCODER.md. Codeminer42 rules-vs-recipes framing applied.
  • v1.13.15-codecontext-synth — forced second-inference synthesis pass for codecontext overview tools (truncation-aware extraction; auto-fetched top-N files + project docs under 32k payload budget).
  • v1.13.16-xml-parser — Anthropic <invoke> parser support + Levenshtein unknown-tool recovery hints (qwen3.6 drift to Claude Code-style tool names).
  • v1.13.17-cross-repo-readsrequest_read_access tool + per-session allowed_read_paths grants; pathGuard extraRoots; reuses the ask_user_input pause/resume mechanism.
  • v1.13.18-codecontext-file-pathresolveProjectPath in codecontext_client.ts realpath-resolves file_path the same way target_dir was already resolved.
  • v1.13.19-html-artifact-panes — pane-based artifact viewer (Markdown default + HTML on request). Scope-revised mid-design from auto-bias-HTML to Markdown-default. <!DOCTYPE html> detection adds message_parts.kind='html_artifact' row; iframe sandbox allow-scripts allow-clipboard-write allow-downloads (no allow-same-origin); CSP connect-src 'none' + X-Content-Type-Options: nosniff + Content-Security-Policy: sandbox defense-in-depth. Pane state is reference-only — content fetched on mount to keep jsonb small.
  • v1.13.20-drop-legacy-cols — final strangler-fig step. 10 dual-write sites stripped (recon caught 2 beyond the original v1.13.2 inventory). messages_with_parts simplified to parts-only via CREATE OR REPLACE before column DROPs (Postgres ordering constraint). Adversarial-review catch: discard_stale had RETURNING tool_calls, tool_results — fixed via two-step UPDATE-then-SELECT-from-view. Message type retains the fields, populated by the view. v1.12.1 cleanup DO block removed.
  • v1.13 umbrella — tagged on the same commit as v1.13.20 (211e903). AI SDK v6 + parts-table migration complete.

Pre-v1.13 architectural decisions (still load-bearing)

  • Embeddings dropped from BooCode (May 2026). Replaced RAG with file-view tools + sidecar analyzers.
  • opencode promoted to Tier A (2026-05-20). Five algorithms identified for lift (compaction, doom-loop, repairToolCall, runLoop, permission evaluate) plus truncate.ts and MCP client.
  • OpenCode canonical repo: anomalyco/opencode, NOT sst/opencode (correction 2026-05-22). Development moved to anomalyco; sst/opencode is the predecessor lineage. All 15 catalog references rewritten.
  • Original Batch 11 (aider PageRank port) replaced by codecontext sidecar approach.
  • Original Batch 12 (codebase indexer w/ Harrier) removed. No embedding infrastructure.
  • Original Batch 13 (OpenHands event log) replaced by v1.13 parts table (opencode pattern).
  • Original Batch 12 (cline plan/act mode) absorbed into v1.15 (opencode permission ruleset).
  • Aider's repomap.py port dropped. Codecontext supersedes it. Aider contribution narrows to the .scm query files only.
  • Globstar parked — not an architect tool. Future verify-before-commit candidate only.
  • codeprysm rejected — embedding-based. Node/edge taxonomy noted as reference if we ever build our own graph.
  • Batch 9 decoupled from Batch 7 (2026-05-16); shipped in 92bd3b1. Builtin defaults: six agents (Code Reviewer, Debugger, Refactorer, Architect, Security Auditor, Prompt Builder) with no model field. Session model wins by default.
  • AI SDK adoption deferred to v1.13 — and shipped as v1.13.1-A. v6 chosen (not v5) for native typed parts model and top-level experimental_repairToolCall.
  • tool_choice='required' confirmed supported by llama-swap (qwen3.6-35b-a3b-mxfp4, 2026-05-20).
  • v1.12.0 shipped 2026-05-21. codecontext sidecar Track B + container guidance Track A. v1.12 truncation and repairToolCall deferred into v1.13.
  • v1.12.1 workspace pane sync (2026-05-21). Moved pane state from per-device localStorage to sessions.workspace_panes jsonb with WS broadcast for cross-device sync. Deprecated session_panes table dropped. Legacy localStorage migrates on first load.
  • v1.12.1 status indicator overhaul (2026-05-21). ChatStatusFrame expanded from working|idle|error to streaming|tool_running|waiting_for_input|idle|error. StatusDot rewritten with distinct animations per state.
  • detectSameNameLoop reverted in v1.12.1. Added during the 2026-05-21 debugging spike, never fired in any real run. Dead code.
  • The 2026-05-21 "freeze" debugging spike taught one lesson: BooCode had no UI signal for the difference between a slow stream and a dead stream. v1.12.2 (live tok/s) and v1.12.3 (stale-stream banner) directly closed that gap. v1.13's typed parts table made the inference state machine visible by construction — the structural fix the spike pointed to.
  • v1.12.4 refactor shipped 2026-05-21/22. inference.ts (1700 LoC) split into inference/ directory before v1.13 so the AI SDK migration had clean seams. stream-phase.ts became the swap target for streamText, tool-phase.ts got the per-tool category tag (added in v1.13.0). Pure structural move, no behavior change.
  • AI SDK v6 silent-abort patched (v1.13.1-A). fullStream returns normally on abort instead of throwing. Without explicit if (signal?.aborted) throw after the stream drain, stop button writes complete instead of cancelled. One-liner comment at the site so it survives future refactors.

Catalog growth (2026-05-22 deep review pass)

The session-of-the-day catalog review added 50+ new entries to boocode_code_review.md. Decisions worth carrying into roadmap planning:

  • Tier A active lifts unchanged: opencode, codecontext, tree-sitter-analyzer, codesight, aider.
  • Tier B / Tier C reviewed and triaged. Most consequential additions: agent-hub (#48, primary v2.0 architectural template), Roo Boomerang Tasks (#46, v1.14 AGENTS.md pattern), zeroshot (#37, blind-validation invariant), AgentLint (#39, immediate manual audit pass), RA.Aid (#44, three-stage routing), OpenSpec (#36, batch-doc structure), bernstein (#49, HMAC audit log), memov (#42, session-history tool design), siropkin/budi (#51, install for Claude Code observability).
  • Rejected as code sources: kilocode, costrict, prompt-tower, mycoder, reviewcerberus (closed Docker), Junie (closed), Cody (parked), VS Code extensions broadly, all Web Builders, LynxPrompt (GPL-3.0), claude-task-master code (Commons Clause), Paseo source (AGPL).
  • No additional code lifts promoted to a current version. All catalog adds are either patterns (license-clean), references (for v2.0+), or one-off audit-pass items (AgentLint, budi install).

Workflow

Each batch:

  1. Verify previous batch merged. git log --oneline main -5.
  2. Cut branch from main. Single-branch-per-dispatch convention.
  3. Dispatch via Paseo to Claude Code at /opt/boocode.
  4. Claude Code recon → blocking questions → implement → hand back.
  5. Compliance review in separate Claude chat (paste handback).
  6. Build: docker compose build --no-cache <surface> where surface is boocode (chat) / booterm / boocoder (v2.0+). No-cache avoids the v1.11.2 stale-bundle trap.
  7. Restart: docker compose up -d <surface>.
  8. Smoke test in browser (hard refresh).
  9. Sam commits and pushes. Never git pull / git push / git commit on his behalf.

Sam reviews all diffs. Backups before any destructive step: cp file file.bak-$(date +%Y%m%d-%H%M%S).