- Archive all 10 shipped openspec changes to openspec/changes/archived/ - Update boocode_roadmap.md: date, shipped status for v1.14/v1.15/v2.0, add v2.1.0 section - Update README.md: 3-app monorepo, add services table, add What's shipped section - Remove stale active openspec folders (all work shipped)
117 KiB
BooCode v1.x — Roadmap
Last updated: 2026-05-25
Companion doc:
boocode_code_review.mdholds 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/chat, port9500,code.indifferentketchup.com) — read-only chat with file-inspection tools. The live thing. Pick a project, chat with a local LLM, get streaming responses over WebSocket. DB renamedboochat_dbat v2.0. - BooCoder (
apps/coder, port9502,coder.indifferentketchup.com) — write tools + external-CLI dispatch. Shipped v2.0.0–v2.0.4. In-process inference loop (withpending_changestable) AND ACP-dispatched external agents (opencode/goose) with PTY fallback (claude/pi/smallcode) — same surface, two execution paths. - BooTerm (
apps/booterm, port9501) — PTY/tmux/xterm.js. Live since May 2026. Node 20 Alpine + node-pty + tmux + xterm.js. Tmux session per pane (bc-<uuid>), SSH-out works (openssh-client + gosu in the image)./api/term/healthshares the existingboochat_db.
Caddy → Authelia → Tailscale → 100.114.205.53 → 9500/9501/9502. Three apps, one shared Postgres (boocode_db → boochat_db).
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 through v1.x. Write tools land in BooCoder at 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, goosegoose acp); PTY fallback retained for claude/pi/smallcode. - Strategic target: Paseo-equivalent dispatcher inside BooCode (2026-05-22 pivot). Paseo (
getpaseo/paseo) is AGPL-3.0 — incompatible with BooCode's MIT license and network-served deployment. Reproduce the architecture using only license-clean patterns. 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-23)
| Version | Theme | Tag |
|---|---|---|
| v1.0 | Initial scaffold | — |
| Batches 1–4.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.ts — includeUsage: 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:48 — hasText = 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:64 — buildMessagesPayload 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:11 — BUDGET_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.4–v1.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;streamCompletionadapter;messages_with_partsview; reasoning_parts end-to-endv1.13.1-cleanup-bundle—statement_timeout='30s', alpha-sorted tool registry, 60s stuck-row sweeper,experimental_repairToolCallpass-throughv1.13.2-compaction-prune— two-tier prune;message_parts.hidden_atcolumn + partial index;messages_with_partsview CASE refinementv1.13.3-truncate— opencodetruncate.tsport; opaquetr_<…>id,view_truncated_output(id)tool, tmpfs storagev1.13.4-reasoning-fix—<reasoning>prose-prefix in compaction head-assembly for tool-bearing turnsv1.13.5-stability-bundle—includeUsage: trueon provider,hasTexttrim guard,BUDGET_NO_AGENT15→30, trailing-empty-assistant filterv1.13.6-prefix-stability—buildSystemPromptWithFingerprintSHA-256 + per-session drift observerv1.13.7-compaction-trigger— overflow trigger lowered tofloor(0.85 × ctx_max)v1.13.8-tool-cost—tool_cost_statsSQL view + per-tool rolling 100-call mean in AgentPickerv1.13.9-agentlint— instruction-file AgentLint pass; identity-openers removed;CLAUDE.local.mdto .gitignorev1.13.10-openspec—openspec/changes/<slug>/{proposal,tasks,design}.mdshape; archived batch docs preserved viagit mvv1.13.11-tools— tiered tool loading viaBOOCODE_TOOLSenv (core | standard | all)v1.13.12-ws-schemas— Zod schemas for all 27 wire-format frames;publishFrame/publishUserFramewrappers; parity testv1.13.13-ws-publish— all ~80 publish sites converted to the typed wrappers; every WS frame now Zod-validated at boundaryv1.13.14-skills-audit— 26 skills vendored + audited via 5 parallel agent teams; 14 kept, 11 dropped, 1 migrated to BOOCHAT.md/BOOCODER.mdv1.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 likeread_file); xml-parser test coveragev1.13.17-cross-repo-reads—request_read_accesstool + per-sessionallowed_read_pathsgrants;pathGuardextended withextraRoots; pause/resume reuses theask_user_inputmechanismv1.13.18-codecontext-file-path—resolveProjectPathincodecontext_client.tsrealpath-resolvesfile_patharg the same waytarget_dirwas; closes the silent-fail path the sidecar exhibited on relative pathsv1.13.19-html-artifact-panes— pane-based artifact viewer with on-request HTML;<!DOCTYPE html>detection addsmessage_parts.kind='html_artifact'row; Markdown + HTML panes both open via "Open in pane" affordance; iframe sandboxallow-scripts allow-clipboard-write allow-downloads(noallow-same-origin,srcDoc); CSPconnect-src 'none'. Scope-revised mid-design from auto-bias-to-HTML to Markdown-default / HTML-on-requestv1.13.20-drop-legacy-cols— final strangler-fig step. Dropsmessages.tool_calls+tool_resultscolumns; 10 dual-write sites removed (recon caught 2 beyond the original roadmap inventory);messages_with_partsview simplified to parts-only subselects viaCREATE OR REPLACEbefore the column DROPs (Postgres ordering constraint). Adversarial-review catch:discard_stalehad aRETURNING tool_calls, tool_resultsclause; fixed via two-step UPDATE-then-SELECT-from-view.MessageAPI type retains the fields — view synthesizes them from parts so the wire shape is unchangedv1.13— umbrella 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:
- 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.
agent.steps ?? Infinityper-agent step cap. AGENTS.md gainssteps:field. Refactorersteps: 5, Architectsteps: 20, etc.- 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). - Doom-loop guards (v1.11.6) migrate from "abort recursion" to "raise within loop iteration." Same predicate, different control flow.
Lift sources:
anomalyco/opencodesession/prompt.tsrunLoop()outer agent loopanomalyco/opencodeagent.stepsper-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:
- 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/mcpwith optionalCONTEXT7_API_KEYheader. - Use the official
@modelcontextprotocol/sdkTypeScript client. No SSE transport yet (deferred to v1.15). Stdio transport not needed for Context7. - Tool discovery on startup:
tools/list. Tools surface in BooChat alongsideview_file/grep/etc., prefixedcontext7_*to avoid collisions. - Read-only invariant guard: the client must reject any MCP tool whose
annotations.readOnlyis false (or absent). Fail-closed. This is BooChat-specific defense-in-depth — v1.15 lifts this restriction for BooCoder. - Per-server
enabledflag inagents.ts. No glob patterns yet. - 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:
- 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.
- Detection at the BooChat backend. In
apps/chat/services/inference/stream-phase.tspost-processing: detect any assistant text part starting with<!DOCTYPE html>(case-insensitive, whitespace-trimmed) — or wrapped in a fenced```htmlblock — and tag it as an HTML artifact. Emit a new part kindhtml_artifactintomessage_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 nohtml_artifactpart is written. - 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 vianavigator.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.
- 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.
- Security stance for HTML pane — locked 2026-05-22: the iframe is sandboxed with
sandbox="allow-scripts allow-clipboard-write allow-downloads". Crucially, omitallow-same-originso the artifact has its own opaque origin and cannot read BooChat's cookies, Authelia session, or DOM. Backend serves the iframe content viasrcdoc=...inline (notsrc=) 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'. Theconnect-src 'none'is the key clause — artifacts can'tfetch(), 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. - 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 anhtml_artifactpart, 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|htmlwhich writes to disk (path-guarded) and returns the absolute path or pre-signed URL for the existing static-file serving route.
- 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. - 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. - No
web-artifacts-builderskill 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.kindCHECK 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:
- Wildcard rule matcher:
{ permission, pattern, action: 'allow' | 'deny' | 'ask' }. Last-match-wins. Per-agent rulesets layer under per-session rulesets. - Full MCP client implementation: stdio (local subprocess) + SSE (remote HTTP) transports,
tools/listdiscovery,tools/callinvocation, OAuth via Dynamic Client Registration (RFC 7591), per-server enabled flag, glob patterns for per-agent tool whitelisting (matching opencode'stoolsconfig shape). - codecontext sidecar gets re-pointed from static wrappers (v1.12) to real MCP. New connectors become a config-only addition.
- UI: permission-ask flow when a tool requires
askaction. Modal or inline card with Allow once / Allow always / Deny. Reuses v1.9.7 elicitation surface. - BooChat stays read-only by default — the read-only invariant guard from v1.14.x carries forward (defense-in-depth even with the ruleset).
- Config shape: match opencode's JSON schema near-verbatim so any opencode user can copy
mcpblocks from~/.opencode/config.jsoninto 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/opencodepermission/evaluate.tswildcard rulesetanomalyco/opencodemcp/index.tsMCP client (SSE transport, tools/list, tools/call, OAuth RFC 7591)cline/clineplan/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). Lands together with the boocode_db → boochat_db DB rename and the per-app subdomain split (code.indifferentketchup.com → BooChat, coder.indifferentketchup.com → BooCoder).
Shipped v2.0.0–v2.0.4. All 8 phases complete. See retrospective below.
Three protocol roles in one surface:
- MCP client (write-capable allowed). Inherits the v1.15 client unchanged. BooCoder can enable write-capable MCP servers (
@modelcontextprotocol/server-filesystemwrite tools, git commit MCP servers, etc.). All MCP writes route through the samepending_changesqueue as native writes. Per-task allow/deny means dispatched tasks can have a different MCP roster than the interactive shell. - MCP server (BooCoder's own primitives). New
apps/coder/services/mcp_server.tsexposesboocoder.create_task,boocoder.list_pending_changes,boocoder.apply,boocoder.reject,boocoder.dispatch_external_agent,boocoder.list_worktreesas MCP tools. Stdio transport for local consumers (Sam'sopencodein Termius), HTTP for remote (deferred until OAuth + secret storage). This is what makes external opencode-on-the-host BooCoder-aware. - ACP client (host). Replaces the raw-PTY dispatch path for ACP-capable agents. Spawns
opencode acpandgoose acpas 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_serversis 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:rwmount, 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, goosegoose acp). JSON-RPC over stdio. Native session/tool/file/terminal events. - Fallback path: raw PTY for claude/pi/smallcode via
node-ptywithcwd = /opt/<project>or agit worktree add /tmp/booworktrees/<session-id>worktree per dispatch. - Dispatch worker checks
available_agents.supports_acpat 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_agentitself.
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-xflag,paseo run/ls/attach/sendCLI verb shape,/handoff/loop/orchestratorskills concept.- Roo Code Boomerang Tasks pattern — orchestrator capability restriction + down-pass/up-pass context discipline (
new_taskmessage,attempt_completionresult, no implicit inheritance) + explicit precedence override clause. covibes/zeroshotblind-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_serversauto-forward pattern. Critical: one MCP config drives every dispatched agent.
Shared infrastructure between A and B
taskstable (id, project_id, template_id, parent_task_id, state, input, output_summary, dependencies, agent, model, worktree_path, cost, started_at, ended_at)task_templatestable (reusable spec → task instantiations)pipelinestable +pipeline_runs(ordered template invocations)available_agentstable (name, install_path, version, supports_acp, supports_mcp_client, last_probed_at) — populated by startup probe (which opencode && opencode --version, etc.)human_inboxview (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
boocodeCLI 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): All 8 phases shipped. v2.0.0-alpha through v2.0.4-hardening. The full BooCoder line 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). Runtime isolation (v2.1) 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 — 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:
- New ACP server entry point:
boocoder acpreads JSON-RPC over stdio, exposes BooCoder's task primitives as ACP sessions. - 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).
- 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 this is v2.2, 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.
Lift source: zed-industries/codex-acp (Apache-2.0) as a server-side ACP reference implementation.
Dependencies: v2.0 + v2.1 (recommended; ACP-driven sessions inside a sandbox are stronger).
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. ProviderPicker frontend component in workspace toolbar. 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.
v2.x — Optional / far future
- Verify gate above pending-changes —
augmentcode/augment-swebench-agentmajority-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 tool —
qodo-ai/qodo-skillsPR-resolver state machine (fetch issues → batch/interactive fix → inline reply). BooCoder v2.0+. - Record/replay LLM harness for tests —
qodo-ai/qodo-coverpattern (hashed prompt → fixture YAML). Re-implement in Vitest, don't vendor (AGPL). v1.13+ test infrastructure. - HMAC-chained audit log —
sipyourdrink-ltd/bernsteinpattern. Small lift, adds tamper-evident session history. v1.13+ optional. - Tiered tool loading —
eyaltoledano/claude-task-masterpattern (env var:core/standard/all). ~30 LoC inagents.ts. Pattern-only lift (claude-task-master is MIT + Commons Clause; reimplement). Shipped asv1.13.11-tools. - Spec directory structure —
Fission-AI/OpenSpecopenspec/changes/<name>/{proposal,specs,design,tasks}.mdshape for BooCode's own batch docs. Zero-dep documentation reformat, replaces ad-hocboocode_batchN.mdconvention. Shipped asv1.13.10-openspec. view_session_historyMCP tool —memovai/memovsnap/mem_history/validate_commitshape. Reference design for v1.13+ session-history feature.taste-skillanti-slop ban list — vendorLeonxlnx/taste-skillSKILL.md after diff against existingfrontend-designskill. 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 60–120 line sweet spot, SHA-pin Actions, ensure.env/CLAUDE.local.mdare gitignored. One-evening pass, immediate ROI. Shipped asv1.13.9-agentlint. budiinstall (Sam's host) —siropkin/budiClaude Code 5-hook observer (SessionStart/UserPromptSubmit/PostToolUse/SubagentStart/Stop). Local SQLite, sub-ms hook latency, dashboard atlocalhost: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-homelabaccessed 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 |
100.114.205.53:9502 |
/opt:/opt:rw (policy-gated) |
Write tools + ACP host + MCP client + MCP server + external-CLI dispatch | Shipped v2.0.0–v2.0.4 |
boochat_db (was boocode_db) |
127.0.0.1:5500 |
boocode_pgdata volume |
Postgres 16-alpine (shared by all three) | Live (renamed at v2.0) |
codecontext |
:8765 (internal) |
/opt/projects:/workspace:ro |
MCP server for architect 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 deprecatedsession_panestable; drop stalemessages_status_checkconstraint - v1.13.0-ai-sdk-v6:
message_parts (id, message_id, sequence, kind, payload jsonb, created_at)+ unique(message_id, sequence)+kindCHECK;messages_with_partsview with COALESCE fallbacks;ToolDef.categoryfield (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 TIMESTAMPTZcolumn + partial index(message_id) WHERE hidden_at IS NULL;messages_with_partsview 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;
CompactionMessageextended 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_cachetable 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_statsSQL view overmessages_with_parts(no new table — view + LATERALjsonb_array_elementsontool_calls); rolling 100-call window - v1.13.9-agentlint: none (instruction-file audit +
.gitignoreadd ofCLAUDE.local.md, no DB) - v1.13.10-openspec: none (docs reorganization,
git mvonly) - 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
.gitignorenegation patterns; no DB) - v1.13.15-codecontext-synth:
message_parts.kindCHECK constraint extended with'synthesis'value (DROP + DO $$ pg_constraint idempotency-guarded re-add) - v1.13.16-xml-parser: none (parser change + new
tool-suggestions.tshelper 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.kindCHECK 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_partsview rewritten to parts-only subselects viaCREATE OR REPLACE VIEWBEFORE the drops (Postgres ordering constraint). v1.12.1messages_status_check/messages_role_checkcleanup block removed (one-shot effective long ago) - v1.14:
agents.stepscolumn (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.kindCHECK constraint extended with'html_artifact'value - v1.15:
permissionstable,agent_permissionsjoin,session_permissionsjoin,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:
pending_changes (id, session_id, file_path, diff TEXT, status, created_at);tasks,task_templates,pipelines,pipeline_runs;available_agents (name, install_path, version, supports_acp, supports_mcp_client, last_probed_at);human_inboxview; DB renameboocode_db→boochat_db - v2.2: none (
boocoder acpis 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, three skills concept | v2.0 / v2.x |
agentclientprotocol.com spec + @zed-industries/agent-client-protocol SDK |
Apache-2.0 | ACP client (host) — replaces raw-PTY dispatch for opencode/goose | v2.0 |
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.2 |
| 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-compatibleincludeUsagedefaults to false —provider.tsnever asked llama-swap to emit usage, sotokens_used/ctx_usedhad been NULL in every assistant row since v1.13.1-A. The fix is one line atprovider.ts:18. No backfill for historical rows. (2) AI SDK v6 streaming emits a stray\ntext-delta on tool-call-only turns, which passedcontent.length > 0and rendered an empty bubble + ActionRow between each tool call. Trim inMessageList.flatten(hasText) and defensively inMessageBubble(hasContent). (3)buildMessagesPayloaddid not filter trailing empty or failed assistant rows — combined with (2), a Continue retry produced…summary-assistant, empty-assistant, failed-assistantpayloads and the upstream rejected with "Cannot have 2 or more assistant messages at the end of the list." Skip rules added atpayload.ts:64. (4)BUDGET_NO_AGENTbumped 15→30. Every tool inALL_TOOLSis read-only today; the cautious 15-cap was forward-looking for write tools that haven't landed. No-agent mode now matchesBUDGET_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.md100% present. General workflow rules (TDD, paraphrase-before-quote, security gotchas, "never git pull/commit/push", alpha-tool-ordering, codecontext-not-RAG) belong inAGENTS.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 toreferences// delete). Validation viamgechev/skills-best-practices4-step protocol +mgechev/skillgrade --smokeper skill. Anthropic'sagent-skills/best-practicespage becomes the canonical convention reference (500-line ceiling, gerund naming, MCPServerName:tool_nameformat, progressive disclosure one level deep, etc.). Documented inBOOCHAT.md/BOOCODER.mdto 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_artifactpart 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 noallow-same-origin, CSPconnect-src 'none',srcdoc=inline (notsrc=). 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 vendoranthropics/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
enabledflag. Hard rule: never enable a write-capable MCP server — the read-only invariant overrides protocol convenience. Defense-in-depth: client must reject any tool whoseannotations.readOnlyis 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_changesqueue. - MCP server role: BooCoder exposes its own task primitives (
boocoder.create_tasketc.) so externalopencodesessions 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_serversto the dispatched agent (per goose docs) — one MCP config drives every dispatched agent. - ACP agent role:
boocoder acpexposes BooCoder to Zed/JetBrains/Avante.nvim. Deferred to v2.2.
- MCP client role: inherits v1.15 client; write-capable servers allowed but writes route through
- 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 (BooCoder ACP agent for external editor drive).
- Reference materials: anthropics
mcp-builderskill (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 thecontext_serversauto-forward pattern,agentclientprotocol.comspec (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-app monorepo at
/opt/boocode/:apps/chat(read-only, currently the live thing at 9500),apps/coder(write tools + external CLI dispatch, 9502, v2.0 planned),apps/booterm(PTY terminal, live since May 2026 at 9501). Sharedapps/server(Fastify backend) andapps/web(React shell hosting the three surfaces as tabs). - Single shared database, rename
boocode_db→boochat_dbwhen BooCoder lands. All three surfaces in one Postgres. Cross-surface joins are valuable (coder task → originating chat → term debugging session). Separate databases would break this. - 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, orchild_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)
Sam wants 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: 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_serversauto-forward).
This is now the dominant roadmap direction, ahead of v1.13.x cleanup batches in importance but behind them in sequence (v1.13 finishing now; Paseo-equivalent work is v2.0+).
BooCoder execution: both Option A AND Option B, full-featured (2026-05-22)
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.14–v2.1 shipped (2026-05-25)
- v1.14.0-outer-loop ✅ — explicit
whileloop, per-agentsteps: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.4-hardening ✅ — full BooCoder line: write tools, dispatcher (ACP/PTY), MCP server (6 tools, stdio, 10-question eval passed), CLI client, human inbox, Boomerang
new_taskorchestration, path-guard fuzz suite (34 traversal-attack tests) - v2.1.0-provider-picker ✅ — 5-provider registry, model discovery,
/api/providersroute,ProviderPickerUI, agent-probe startup probe
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 25–30% 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
Panetype 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 aRETURNING tool_calls, tool_resultsclause 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.0 —
message_partstable + 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).streamCompletionrewritten asstreamTextadapter. Silent-abort bug caught and patched (explicitif (signal?.aborted) throw). Known regression: mid-stream tps gone — TODO for delta-cadence interpolation againstresult.usage. Latent regression discovered v1.13.7:includeUsagedefaults false on@ai-sdk/openai-compatible, soresult.usageresolved empty all along; tokens_used/ctx_used NULL in every row since this version. Fixed in v1.13.7. - v1.13.1-B —
messages_with_partsview with COALESCE fallbacks. Read sites switched. 1ms for 42-message chat verified. - v1.13.1-C —
ask_user_inputcorrelation ported to parts; reasoning end-to-end (361 chars reasoning at seq 0, 429 chars text at seq 1 in smoke).v1.13.1tagged onac1a71f. 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.usageresolved 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_cachetable" 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 on81d837c. - v1.13.7-compaction-trigger — 0.85×ctx_max early trigger (planned as v1.13.8 / v1.13.9).
- v1.13.8-tool-cost —
tool_cost_statsSQL 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-openspec —
openspec/changes/<slug>/{proposal,tasks,design}.mdbatch-doc structure adoption. - v1.13.11-tools — tiered tool loading via
BOOCODE_TOOLS=core|standard|allenv (~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/publishUserFramewrappers, ~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-reads —
request_read_accesstool + per-sessionallowed_read_pathsgrants;pathGuardextraRoots; reuses theask_user_inputpause/resume mechanism. - v1.13.18-codecontext-file-path —
resolveProjectPathincodecontext_client.tsrealpath-resolvesfile_paththe same waytarget_dirwas 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 addsmessage_parts.kind='html_artifact'row; iframe sandboxallow-scripts allow-clipboard-write allow-downloads(noallow-same-origin); CSPconnect-src 'none'+X-Content-Type-Options: nosniff+Content-Security-Policy: sandboxdefense-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_partssimplified to parts-only viaCREATE OR REPLACEbefore column DROPs (Postgres ordering constraint). Adversarial-review catch:discard_stalehadRETURNING tool_calls, tool_results— fixed via two-step UPDATE-then-SELECT-from-view.Messagetype retains the fields, populated by the view. v1.12.1 cleanup DO block removed. v1.13umbrella — 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, NOTsst/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.pyport dropped. Codecontext supersedes it. Aider contribution narrows to the.scmquery 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 nomodelfield. 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 jsonbwith WS broadcast for cross-device sync. Deprecatedsession_panestable dropped. Legacy localStorage migrates on first load. - v1.12.1 status indicator overhaul (2026-05-21). ChatStatusFrame expanded from
working|idle|errortostreaming|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 intoinference/directory before v1.13 so the AI SDK migration had clean seams.stream-phase.tsbecame the swap target forstreamText,tool-phase.tsgot the per-toolcategorytag (added in v1.13.0). Pure structural move, no behavior change. - AI SDK v6 silent-abort patched (v1.13.1-A).
fullStreamreturns normally on abort instead of throwing. Without explicitif (signal?.aborted) throwafter the stream drain, stop button writescompleteinstead ofcancelled. 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:
- Verify previous batch merged.
git log --oneline main -5. - Cut branch from main. Single-branch-per-dispatch convention.
- Dispatch via Paseo to Claude Code at
/opt/boocode. - Claude Code recon → blocking questions → implement → hand back.
- Compliance review in separate Claude chat (paste handback).
- Build:
docker compose build --no-cache <surface>where surface isboocode(chat) /booterm/boocoder(v2.0+). No-cache avoids the v1.11.2 stale-bundle trap. - Restart:
docker compose up -d <surface>. - Smoke test in browser (hard refresh).
- Sam commits and pushes. Never
git pull/git push/git commiton his behalf.
Sam reviews all diffs. Backups before any destructive step: cp file file.bak-$(date +%Y%m%d-%H%M%S).