Old hardcoded MAX_TOOL_LOOP_DEPTH=15 replaced by per-agent max_tool_calls (1-100, AGENTS.md frontmatter) with defaults: 30 for read-only-only agents, 10 for agents that include any non-read-only tool, 15 for raw chat. When the loop hits cap, fire one final summary call with tools disabled, stream the wrap-up into the in-flight assistant message, then insert a system sentinel with metadata.kind='cap_hit'. The sentinel renders an amber bubble with a Continue button (latest sentinel only) that POSTs to a new /api/chats/:id/continue route to extend. Hard ceiling: 3 cap-hits per chat (2 continues max) — third sentinel reports can_continue=false. Error frames carry a machine-readable reason code alongside human error text. Failed messages persist the reason via metadata.kind='error' so the bubble renders specifics on reload (WS error frame is one-shot). Tool call UI rewired: ToolCallLine renders inline (↳ name args spinner/check/✗, expand-on-tap for args+result); ToolCallGroup collapses 3+ consecutive same-tool runs into a compact card. MessageList owns a three-pass pre-render (flatten + fold tool results onto matching runs by id + group same-tool runs + number sentinels). MessageBubble drops tool rendering and adds the sentinel / error-reason branches. ToolCallCard deleted. Roadmap follow-up logged: add explicit max_tool_calls: 30 to the 6 agents in /data/AGENTS.md and /opt/boocode/AGENTS.md post-ship for discoverability (defaults handle behavior identically). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 KiB
BooCode v1.x — Roadmap
Last updated: 2026-05-16
Overview
BooCode is a standalone code-chat tool at /opt/boocode/. Read-only by design — pick a project, chat with a local LLM that has file-inspection tools, get streaming responses over WebSocket.
Live at https://code.indifferentketchup.com (Caddy → Authelia → Tailscale → 100.114.205.53:9500).
Architectural commitments:
- No embeddings. The model uses file-view tools (
view_file,list_dir,grep,find_files) + sidecar analyzers (codecontext, codesight). Walked away from the RAG pipeline May 2026. - Read-only in v1.x. Write tools land in BooCoder (separate container, post-v1.x).
- One Postgres (
boocode_db), one frontend SPA, container-per-service for new capabilities.
External code lifted from / referenced in: see boocode_code_review.md for full inventory.
Batch summary
| Batch | Theme | Status | Branch / Notes |
|---|---|---|---|
| 1 | Markdown, Copy + Regen, tok/s + ctx, AI naming | ✅ Done | v1.1-batch1 merged |
| 2 | Sidebar restructure | ✅ Done | v1.1-batch2 merged |
| 3 | Pane system, FileBrowserPane + Shiki, cross-tab | ✅ Done | v1.1-batch3 merged |
| 3.5 | Chip infrastructure, @file, line-select |
✅ Done | merged |
| 4 (v1.2) | Chats inside sessions, right-rail, /compact, archive, force-send |
✅ Done | merged |
| 4.1–4.4 | Project archive, sidebar context, Gitea API, bootstrap | ✅ Done | merged |
| v1.5 cleanup | resolveProjectPath, BOOTSTRAP_ROOT, vitest pin | ✅ Done | merged |
| v1.6 mobile | Drawer, single-pane, long-press, IME-safe, pull-to-refresh, swipe-close | ✅ Done | merged |
| v1.6.1 | RightRail mobile wrapper fix | ✅ Done | merged |
| Tool-loop bump | MAX_TOOL_LOOP_DEPTH 5→15 | ✅ Done | merged |
| v1.6.2 | Workspace + Session+Project headers + ChatTabBar new-chat + RightRail mobile drawer | 🔄 In flight | v1.6.2-mobile-ui-fixes |
| v1.8 mobile tabs | Bottom-sheet pane switcher + cross-tab pane_status WS sync + StatusDot on tabs |
Next up | v1.8-mobile-tabs; hand-rolled sheet |
| 9 (REORDERED, DECOUPLED) | Agents (Tier 2): AGENTS.md, per-agent temp/tools, picker in ChatInput toolbar |
✅ Implemented, uncommitted | six builtins; on main awaiting commit |
| 5 | Fork message, delete message, header polish | Planned | |
| 6 | Drag-drop file + paste-as-attachment | Planned | thin extension of 3.5 chips |
| 7 | Settings drawer: system prompt, web search toggle, agent entry | Planned | adds SettingsDrawer agent entry (Batch 9 deferred half) |
| 8 | Web search backend: SearXNG web_search + web_fetch |
Planned | |
| 10 | BooTerm: separate container, xterm.js + node-pty + tmux | Planned | |
| 11 — Architect: codebase map | codecontext sidecar + MCP tool wiring | Planned | from nmakod/codecontext |
| 11b — Architect: repo health | call graph, circular deps, dead code | Planned | from spirituslab/codesight |
| 12 — Tool approval + plan/act mode | Read-only invariant, per-tool gating | Planned | from cline |
| 13 — Append-only event log | Replace messages-table semantics | Planned | from OpenHands V1 |
| 14 — BooCoder: pending changes | Sandboxed edit queue, atomic apply | Post-v1.x | from plandex |
| 15 — BooCoder runtime isolation | Per-session Docker sandbox | Post-v1.x | from OpenHands |
| 16 — Multi-provider LLM | Optional litellm-style abstraction | Optional | from pi-ai |
| 17 — Workflow graphs | Multi-agent coordination | Far future | from microsoft/agent-framework concepts |
Old Batch 12 (codebase indexer w/ Harrier embeddings) — REMOVED. Replaced by Batch 11/11b sidecar approach. See boocode_code_review.md decisions log.
Batch 9 reordered ahead of 5–8, 10. Picker mounts in ChatInput.tsx toolbar only. SettingsDrawer agent entry rolled into Batch 7 when it lands. No UI dependency on Batches 5/6/7, so it can ship anytime after v1.6.2.
Batch details (planned / new)
Batch 9 — Agents (Tier 2, DECOUPLED)
Spec: boocode_batch9.md with the deltas below.
Status: Next up after v1.6.2 merges. Decoupled from Batch 7.
Deltas from boocode_batch9.md:
- Builtin defaults in
agents.tsOMIT themodelfield. Resolution order makessession.modelwin whenagent.modelis null. Spec line 30 example is misleading — do not hardcode any model in builtins. - Builtin defaults are the six agents shipped in
/opt/boocode/AGENTS.md: Code Reviewer, Debugger, Refactorer, Architect, Security Auditor, Prompt Builder. If project rootAGENTS.mdexists, only its agents show. If absent, show the six builtins. - AgentPicker mounts in
ChatInput.tsxtoolbar between ModelPicker and the+button. NoSettingsDrawer.tsxorHeader.tsxchanges in this batch. - SettingsDrawer agent entry + Header active-agent badge moved to Batch 7.
Files to create:
apps/server/src/services/agents.ts— parser, six builtin defaults, mtime-keyed cache.apps/server/src/routes/agents.ts—GET /api/projects/:id/agents.apps/web/src/components/AgentPicker.tsx— dropdown, matches ModelPicker pattern.
Files to modify:
apps/server/src/schema.sql—ALTER TABLE sessions ADD COLUMN IF NOT EXISTS agent_id TEXT;apps/server/src/services/inference.ts— resolution order:effective_system_prompt,effective_model,effective_temperature,effective_toolsfrom session + agent + project. Filter tools array against agent whitelist before sending to llama-swap.apps/server/src/routes/sessions.ts— PATCH acceptsagent_id.apps/server/src/types/api.ts— Agent type, extend Session withagent_id.apps/web/src/api/client.ts,apps/web/src/api/types.ts— Agent type,api.agents.list(projectId).apps/web/src/components/ChatInput.tsx— mount AgentPicker.
Testing plan (manual, before locking temps):
- Drop
/opt/boocode/AGENTS.md(six agents, nomodelfield on any). - For each of the 7 keeper models, switch session model and run the same target prompt against each agent. Log tok/s, instruction-following quality.
- Adjust per-agent temperature in
AGENTS.mdbased on results. - A/B candidates: qwen3.6-35b-a3b-mxfp4 (daily), qwopus3.6-35b-a3b-q4 (reasoning), qwopus3.5-27b-q4, qwen3.6-27b-ud-q4-xl, nemotron-3-nano-30b, gemma-4-26b-a4b-mxfp4, qwen3-coder-30b-apex.
Dependencies: v1.6.2 merged.
Batch 11 — Architect: codebase map (REVISED)
Inspiration / lift: nmakod/codecontext (MIT, Go binary).
What it gives BooCode: an architect-grade codebase overview without embeddings. Codecontext parses the repo with tree-sitter, extracts symbols, builds import/dependency relationships, and exposes the result via an MCP server with 8 tools. The model gets a structural map of any codebase on demand.
Why this replaces the original Batch 11 (aider PageRank port): codecontext is a finished binary in our stack language (Go), with watch mode, incremental updates, framework detection, and git-co-change-based semantic neighborhoods (no embeddings). The aider port would be reimplementing what codecontext already ships.
Scope:
- Add
codecontextsidecar container todocker-compose.yml. Mount the project root read-only. One sidecar per BooCode instance — projects are addressed by absolute path. - Wire each codecontext MCP tool into BooCode’s
inference/tools.tsas a native tool the model can call: repo_overview(project_id)→ codecontextget_codebase_overviewrepo_file_analysis(project_id, path)→get_file_analysisrepo_symbol_info(project_id, symbol)→get_symbol_inforepo_search_symbols(project_id, query)→search_symbolsrepo_dependencies(project_id, path)→get_dependenciesrepo_semantic_neighborhoods(project_id, path)→get_semantic_neighborhoods(git co-change)repo_framework_analysis(project_id)→get_framework_analysispath_guard.tsextension: incorporatecontinuedev/continueDEFAULT_SECURITY_IGNORE_FILETYPESso codecontext can’t surface.env,.pem, keys, etc.- Fallback grammars: drop
Aider-AI/aider‘saider/queries/tree-sitter-*.scmfiles for any language codecontext doesn’t cover. Use them via an in-process tree-sitter wrapper only if a project needs an unsupported language. Defer wrapper build until that’s an actual gap.
Where it goes: new apps/server/src/architect/ directory. No new tables — codecontext maintains its own state on disk. New env: CODECONTEXT_URL=http://codecontext:8765 (MCP endpoint).
Decisions to make at recon time:
- Bundle the binary directly in the BooCode Dockerfile, or run codecontext as its own service? Sidecar is cleaner. Bundle is one less container.
- How does the model discover codecontext tools — register them statically in the tools registry, or proxy MCP
tools/listat startup?
Dependencies: none. Can ship before Batches 5–10.
Batch 11b — Architect: repo health (NEW)
Inspiration / lift: spirituslab/codesight (MIT-ish, TS/Node).
What it gives BooCode: complement to Batch 11. Where codecontext answers “what is this codebase,” repo health answers “what’s wrong with this codebase.” Call graph, circular dependency detection, dead code flagging.
Scope:
- Port codesight’s
analyze.mjsanalyzer core intoapps/server/src/architect/repo_health.ts. Drop the VS Code extension shell. Keep: - Symbol extraction (already overlaps codecontext — call codecontext where possible, only redo what’s needed for graph edges).
- Call graph builder (function-to-function edges).
- Circular dependency detector.
- Dead code detector (exported symbols never imported or called).
- New tool:
repo_health(project_id)returning{ circular_dependencies: [...], dead_code: [...] }. Output respects codesight’s documented false-positive caveats (customElements.define, framework entry points, dynamic imports) — surface those in the tool description so the model doesn’t trust dead-code flags blindly. - Cache results in
boocode_dbkeyed by(project_id, file_hashes). Invalidate on file change via file-index hash check.
Decisions:
- Build it in-process (Node) vs spawn a CLI? In-process is simpler. Spawn matches codecontext sidecar pattern but adds latency.
Dependencies: Batch 11 merged (so we can reuse codecontext’s parse output where possible). Can be deferred until after Batches 5–10.
Batch 12 — Tool approval gating + plan/act mode
Inspiration / lift: cline/cline (Apache-2.0).
What it gives BooCode: per-session control over which tools the model can call. Lays the groundwork for BooCoder by building the gating mechanism before there are any write tools to gate.
Scope:
- New column
sessions.tool_approval_mode TEXT— values:read_only(v1.x default),plan,act_auto,act_approve. - New column
sessions.approved_tools JSONB— per-session whitelist foract_approvemode. - Tool registry refactor: tools tagged
read_onlyorwrite. Inread_onlymode (v1.x), write tools never appear in the model’s tools array. Inplanmode, same — write tools hidden, model produces a plan only.act_*modes unlock writes (post-v1.x). - UI: mode picker in SettingsDrawer (Batch 7 dependency). Inline indicator in chat header.
Dependencies: Batch 7 (SettingsDrawer).
Batch 13 — Append-only event log
Inspiration / lift: OpenHands/OpenHands V1 (MIT).
What it gives BooCode: replaces the ad-hoc messages table semantics with a typed event stream. Unlocks rewind, time-travel, and clean handoff semantics for multi-agent flows.
Scope:
- New
session_eventstable:(id, session_id, ts, kind, payload JSONB, parent_id). Event kinds:user_message,assistant_message,tool_call,tool_result,pane_action,mode_change,system. - Existing
messagestable becomes a derived view oversession_eventsfor backward compatibility, then deprecated over a release. - Inference loop emits events instead of mutating message rows.
- Frontend
useSessionStreamreducer rewritten to consume events.
Migration is non-trivial. Plan in a dedicated batch with explicit cutover window.
Dependencies: Batches 5 (fork/delete) and 7 (settings) merged. Must not be in flight with other backend work.
Batch 14 — BooCoder: pending changes
Inspiration / lift: plandex-ai/plandex (MIT).
What it gives BooCode: safe write tools. Edits queue in a virtual layer; nothing touches the filesystem until explicit /apply.
Scope:
- New container
boocoderat100.114.205.53:9502. Owns write tools (edit_file,create_file,delete_file,apply_pending,rewind). - New table
pending_changes (id, session_id, file_path, diff TEXT, status, created_at). Status:pending,applied,rejected. - Tools execute against the pending-changes layer, not the filesystem.
apply_pendingis the only path that touches disk.rewindrolls back apending-id back to disk state. - BooCode chat container stays read-only (
/opt:/opt:ro). BooCoder mounts/opt/repos:/opt/repos:rwand uses git worktree pattern from paseo for isolation. - Frontend: new pane kind
pending_diffshows the queued diff inline with Approve/Reject per chunk.
Dependencies: Batches 12 (gating) + 13 (events). Don’t start until both are live.
Batch 15 — BooCoder runtime isolation
Inspiration / lift: OpenHands/OpenHands (MIT).
What it gives BooCode: per-session Docker sandbox for BooCoder writes. Closes the /opt:ro mount risk identified in v1.x open items.
Scope:
- Per-session container spawned by BooCoder on first write. Container has only the project’s path mounted, not
/opt. - Container lifecycle: spawn on first write call, idle-timeout after 30 min, recreate on resume.
- Action execution server pattern: HTTP API inside the container, BooCoder calls in. Standard OpenHands runtime contract.
Dependencies: Batch 14.
Batch 16 — Multi-provider LLM abstraction
Inspiration / lift: earendil-works/pi pi-ai (MIT).
What it gives BooCode: optional non-llama-swap inference paths (Anthropic, OpenAI, Mistral direct). Currently we have one provider (llama-swap) and the existing streamCompletion is hardcoded to OpenAI-compatible at that endpoint.
Scope:
- Provider abstraction:
interface LLMProvider { stream(req): AsyncIterator<Frame> }. - Built-in: llama-swap (current), Anthropic, OpenAI (Codex-style).
- Per-session
provider_idcolumn.
Status: Optional. Skip unless a concrete need surfaces. llama-swap covers daily driver work.
Batch 17 — Workflow graphs
Inspiration / lift: microsoft/agent-framework (MIT) — concepts only.
What it gives BooCode: multi-agent coordination. Architect → Coder → Reviewer → Verifier handoffs orchestrated by a YAML-defined workflow.
Status: Far future. Read agent-framework’s docs/decisions/ ADRs. Don’t port code — Azure/.NET-heavy.
Dependencies: Batches 12 (modes), 13 (events). Realistically a v2.x topic.
Order of operations
Two tracks. Pick one to drive next.
Track A — Finish v1.x mobile + polish then agents:
- v1.6.2 ships (in flight)
- Batch 9 (agents) — decoupled, can land next; no UI dependency on 5/6/7
- Batches 5, 6, 7, 8 in order. Each is small, frontend-heavy, no architecture risk. Batch 7 absorbs SettingsDrawer agent entry.
Track B — Begin architect capabilities in parallel:
- Batch 11 (codecontext sidecar) — biggest single capability jump. Frontend stays the same; new tools appear to the model.
- Batch 11b (repo health) — follow-up.
- Batch 12 (gating) — sets up everything post-v1.x.
Recommendation: ship v1.6.2, then Batch 9 (agents) next so the test bed exists before Track A continues. Then Track A through Batch 7. Batch 11 can run in parallel with Batches 8–10 since 11 has no UI dependency.
Architecture target state
Containers
| Container | Port | Mount | Purpose | Status |
|---|---|---|---|---|
boocode |
100.114.205.53:9500 |
/opt:/opt:ro |
Chat + read-only tools + SPA | Live |
boocode_db |
127.0.0.1:5500 |
boocode_pgdata volume |
Postgres 16-alpine | Live |
codecontext |
100.114.205.53:8765 (internal) |
project root :ro | MCP server for architect tools | Batch 11 |
booterm |
100.114.205.53:9501 |
/opt/repos:/opt/repos:rw |
Terminals (tmux + node-pty) | Batch 10 |
boocoder |
100.114.205.53:9502 |
per-session sandbox | Write tools | Batch 14 |
Schema additions
Batch 9: sessions.agent_id TEXT (nullable; references AGENTS.md by slug).
Batch 11: none (codecontext stateless on disk).
Batch 11b: repo_health_cache (project_id, file_hashes_sig, payload JSONB, created_at).
Batch 12: sessions.tool_approval_mode, sessions.approved_tools.
Batch 13: session_events; deprecate messages long-tail.
Batch 14: pending_changes.
Lift sources (summary)
Full inventory in boocode_code_review.md. Headline items:
| Source | Used for | Where |
|---|---|---|
| nmakod/codecontext (MIT, Go) | Architect: codebase map sidecar | Batch 11 |
| spirituslab/codesight (MIT-ish, TS) | Architect: repo health analyzer | Batch 11b |
| Aider-AI/aider (Apache-2.0) | Fallback .scm grammars (60+ languages) |
Batch 11 (fallback) |
| continuedev/continue (Apache-2.0) | DEFAULT_SECURITY_IGNORE_FILETYPES | Batch 11 prep |
| cline/cline (Apache-2.0) | Plan/Act mode pattern | Batch 12 |
| plandex-ai/plandex (MIT) | Pending-changes data model | Batch 14 |
| OpenHands/OpenHands (MIT) | Event log + sandbox runtime | Batches 13, 15 |
| aimasteracc/tree-sitter-analyzer (MIT) | Outline-first response patterns | Reference |
| earendil-works/pi (MIT) | Multi-provider LLM | Batch 16 (optional) |
| rshah515/claude-code-subagents (MIT) | Reference for builtin agent prompts | Batch 9 (six builtins) |
| microsoft/agent-framework (MIT) | Workflow concepts | Batch 17 (far future) |
Decisions log
- Embeddings dropped from BooCode. Replaced RAG with file-view tools + sidecar analyzers.
- Original Batch 11 (aider PageRank port) replaced by codecontext sidecar approach.
- Original Batch 12 (codebase indexer w/ Harrier) removed entirely. No embedding infrastructure in BooCode v1.x.
- 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). AgentPicker mounts in
ChatInput.tsxtoolbar only. SettingsDrawer agent entry and Header active-agent badge moved to Batch 7. Builtin defaults shipped: six agents (Code Reviewer, Debugger, Refactorer, Architect, Security Auditor, Prompt Builder) with nomodelfield — session model wins by default.
Follow-ups (post-ship docs / cleanup)
- After v1.8.2 ships: Add explicit
max_tool_calls: 30to all 6 agents in/data/AGENTS.mdand/opt/boocode/AGENTS.md. Purely for documentation/discoverability — defaults handle behavior identically (all 6 agents use only read-only tools, default is already 30).
Workflow
Each batch:
- Verify previous batch merged.
- Dispatch via Paseo to Claude Code at
/opt/boocode. - Claude Code recon → blocking questions → implement → hand back.
- Compliance review in separate Claude chat.
- Deploy:
docker compose up --build -d. - Smoke test.
- Sam commits and pushes.
Sam reviews all diffs. Sam commits. Never git pull/push/commit on his behalf.