After a codecontext overview-class tool call lands (get_codebase_overview, get_framework_analysis, get_semantic_neighborhoods), the pipeline runs a second inference pass that replaces the recursive runAssistantTurn. The synth pass auto-fetches the top-N source files referenced in the codecontext output plus project docs (BOOCHAT.md, AGENTS.md, *roadmap*.md, CONTEXT.md), applies a 32k-token budget with explicit drop-priority, and streams a structured response that grounds the model in real load-bearing code rather than relying on the codecontext summary alone. Smoke #1 (default) and #2 (Architect) both cite the correct inference/turn.ts + tool-phase.ts + stream-phase.ts files; smoke #6 (fault injection) verifies the fall-through path marks the synth message status='failed' and yields cleanly to the recursive turn. ## Truncation-aware extraction codecontext's wrapper inline-truncates results at 32k chars. Without the expansion step, the top-N file selection only saw the alphabetical head of the codebase (apps/booterm/dist/*) and auto-fetched the wrong sources. The pipeline now calls in-process readTruncation(outputPath) before extracting referenced files, so top-N selection sees the full 80k+ char output. The 32k truncated head still ships to the synth model — the expansion is reference-extraction-only, preserving the token-budget contract. Graceful degradation on readTruncation null/throw: log warn, fall back to the truncated head. ## Schema deviation from dispatch The dispatch claimed no schema migration was needed for the new 'synthesis' part kind. Reality: message_parts.kind has an explicit CHECK constraint (schema.sql:54) that would reject the new value. Added a DROP CONSTRAINT IF EXISTS + DO $$ pg_constraint idempotency-guarded re-add matching the CLAUDE.md migration pattern. The inline CREATE TABLE constraint also updated so fresh installs land with the extended enum. ## User-abort marks synth-message failed Deviation from review-time spec ("user-abort path does NOT mark the message failed"). The outer abort handler in error-handler.ts operates on the parent turn's assistantMessageId, not the new synth row that runSynthesisPass created. Without explicit marking, the synth row would sit in status='streaming' until the 5-min stale-streaming sweeper (v1.13.1-cleanup-bundle), tripping the frontend's 60s no-token-activity banner in the meantime — exactly the UX bug class the v1.13.1 sweeper was added to handle. Marking failed on every catch path (including user-abort) closes the gap. Cost: one extra DB write + one publish on the rare user-abort-during-synth path. ## Race-safe synth-tool capture tool-phase.ts uses synthEntries: Array<{tc, output, error?}> with per-callback push under Promise.all. find() picks the first non-error entry by call-order (toolCalls array index). Multiple synth-tools in one batch are uncommon but handled deterministically. ## Roadmap rebase Updated boocode_roadmap.md retrospective section + cleanup-order tracker + schema-changes summary to use the new vMAJOR.MINOR.PATCH-slug tag names per the 2026-05-22 retag (CHANGELOG.md is the canonical record). v1.13.15 listed as "this batch, tag pending"; a one-line follow-up commit will remove that qualifier after the tag lands.
19 KiB
Changelog
All notable changes per release tag. Most recent on top, ordered by tag creation date (which matches the git history). Tag names follow vMAJOR.MINOR.PATCH-slug — the slug describes what shipped, so the tag name alone is enough to recall the batch.
v1.13.15-codecontext-synth — 2026-05-22
Forced second-inference synthesis pass for codecontext overview-class tools (get_codebase_overview, get_framework_analysis, get_semantic_neighborhoods). After the tool result lands, the pipeline expands the truncated head via in-process readTruncation, extracts referenced file paths from the full content, auto-fetches top-N files + project docs (BOOCHAT.md, AGENTS.md, roadmap.md, CONTEXT.md) under a 32k-token budget with explicit drop-priority order, then streams a synthesis turn that replaces the recursive runAssistantTurn. The 32k truncated head still ships to the synth model (token-budget contract preserved); the expansion is reference-extraction-only. Falls through to recursion on timeout (90s), model error, or non-2xx; user-abort marks the synth message status='failed' and re-throws (the outer abort handler operates on the parent turn's message, not the new synth row — without explicit marking, the row would sit streaming until the 5-min sweeper, tripping the 60s stale-stream banner). Adds 'synthesis' to message_parts.kind CHECK constraint via DROP CONSTRAINT IF EXISTS + DO $$ pg_constraint idempotency-guarded re-add. Smokes #1, #2, #6 all clean; smokes #3–#5 are content-quality checks for UI review.
v1.13.14-skills-audit — 2026-05-22
Multi-topic batch. Skills audit (headline): vendored all 26 skills from /home/samkintop/opt/skills/ into repo-local data/skills/ (the /opt/skills:/data/skills override mount removed from docker-compose.yml so skills are auditable per-batch in git). Audited via 5 parallel Claude Code agent-teams running mgechev's 4-step protocol per skill — 14 survive with gerund-form names + refined triggers; 11 dropped (duplicates, BooCode-irrelevant patterns, Claude-already-does-natively); 1 (verification-before-completion) migrated to BOOCHAT.md/BOOCODER.md as an always-true rule. The Codeminer42 "rules vs recipes" split codified in those files. Token tracking + stale-stream banner fix: same root cause — IsoTimestamp = z.string() in ws-frames.ts was failing on postgres Date objects, silently dropping every message_complete / session_updated / chat_updated frame through the v1.13.13-ws-publish Zod gate; z.preprocess(v => v instanceof Date ? v.toISOString() : v, ...) applied to the primitive on both server + web (parity test still passes). Codecontext ignore: codecontext_client.ts auto-installs .codecontextignore.template into any project's root on first call (stops the upstream empty-source-file parser crash on foreign projects' node_modules). Budget bump: BUDGET_READ_ONLY + BUDGET_NO_AGENT 30 → 50 (real recon need ~27 + headroom for codecontext failure-retry turns; doom-loop guard catches the loop class anyway). UI: queued-message dropdown → edit / force-send / cancel buttons in ChatPane.tsx; ChatThroughput removed from desktop tab strip (mobile tab switcher keeps it). Audit decisions in openspec/changes/v1.13.12-skills-audit/audit-notes.md.
v1.13.13-ws-publish — 2026-05-22
Second half of the WebSocket-frame-typing batch. Converts the existing ~50 inference + auto_name publish sites (via the index.ts adapter) plus ~30 direct broker.publish* call sites in routes + compaction, so every server-emitted frame now goes through Zod validation at the broker boundary. Pairs with v1.13.12-ws-schemas.
v1.13.12-ws-schemas — 2026-05-22
First half of the WebSocket-frame-typing batch. Adds apps/server/src/types/ws-frames.ts with Zod schemas for all 27 wire-format frame types (discriminated union WsFrameSchema + KNOWN_FRAME_TYPES diagnostic lookup), duplicated byte-identical at apps/web/src/api/ws-frames.ts with a parity test. Introduces the publishFrame / publishUserFrame wrappers that fail-closed on schema mismatch.
v1.13.11-tools — 2026-05-22
Tiered tool loading via BOOCODE_TOOLS env var (core | standard | all). Core = 4 read-only fs tools (~2k token schema cost). Standard = +web + git + codecontext (~10k). All (default) = every tool in ALL_TOOLS (~21k). The var is a ceiling — narrows agent whitelists, never expands. Pattern lifted from eyaltoledano/claude-task-master.
v1.13.10-openspec — 2026-05-22
Adopt Fission-AI/OpenSpec's openspec/changes/<slug>/{proposal,tasks,design}.md shape for BooCode's own batch docs. Existing batch docs (boocode_batch10.md, handoff_v1.13.8_prefix_verify.md, handoff_v1.13.10_per_tool_cost.md) moved into openspec/changes/archived/ via git mv to preserve history. Zero-dep documentation reformat.
v1.13.9-agentlint — 2026-05-22
Manual audit of instruction files against 0xmariowu/AgentLint's 31-check standard. Removed identity-opener sections from BOOCHAT.md and BOOCODER.md (emphatic decoration the model doesn't need). Added CLAUDE.local.md to .gitignore — Claude Code's Glob ignores .gitignore by default, so local overrides were otherwise readable by any agent walking the workspace. CLAUDE.md passed all 10 checks unchanged.
v1.13.8-tool-cost — 2026-05-22
Per-tool prompt/completion-token rolling averages surfaced in AgentPicker as at-a-glance cost hints. Implementation is the tool_cost_stats SQL view over messages_with_parts (LATERAL jsonb_array_elements on tool_calls), plus a read endpoint and a tooltip extension. Equal-split attribution — multi-tool turn divides tokens N-ways; the 100-call rolling mean absorbs split noise. Filters out cap_hit / doom_loop sentinels. Source data already lands via existing UPDATEs that v1.13.5-stability-bundle's includeUsage: true fix made non-NULL.
v1.13.7-compaction-trigger — 2026-05-22
Compaction overflow trigger lowered to floor(0.85 × ctx_max), replacing the v1.11.0-era ctx_max − 20_000 formula. Old formula gave only 7.6% headroom at 262k context and 0 budget for ≤20k contexts (never fired). New formula gives consistent 15% summarizer headroom across all model sizes. Opencode pattern lift from session/overflow.ts.
v1.13.6-prefix-stability — 2026-05-22
System-prompt prefix stability verify-and-measure. Recon during planning disproved the original DB-cache premise: buildSystemPrompt already runs over inputs mtime-cached at the file layer (BOOCHAT.md, AGENTS.md global+per-project), and DB scalars are byte-stable until edited. This batch closes the verification gap with instrumentation, not implementation — buildSystemPromptWithFingerprint computes SHA-256 over the assembled prefix and a per-session Map observer fires prefix-drift (warn) on hash change with field-level changed_inputs diff.
v1.13.5-stability-bundle — 2026-05-22
Five fixes for latent regressions surfaced during the cosmetic-revert investigation. (1) provider.ts — includeUsage: true on createOpenAICompatible (default false omitted stream_options.include_usage; llama-swap never emitted usage; tokens_used / ctx_used were NULL on every assistant row since v1.13.0-ai-sdk-v6). (2) MessageList.tsx — hasText = m.content.trim().length > 0 to skip whitespace-only tool-call-only turns rendering empty bubbles. (3) BUDGET_NO_AGENT raised 15 → 30 to match read-only agent cap. (4) payload.ts skips status='failed' + complete-but-empty assistant rows so cap-hit + Continue doesn't upstream-reject. (5) Misc UI sanitization.
v1.13.4-reasoning-fix — 2026-05-22
Compaction head-assembly audit caught one fix: reasoning was omitted from the summarizer's view of tool-bearing turns, silently degrading summary quality for reasoning-channel models (qwen3.6). v1.13.0-ai-sdk-v6 had wired reasoning end-to-end into inference but missed this one read site. CompactionMessage extended with reasoning_parts; buildHeadPayload embeds it as a <reasoning>...</reasoning> prose prefix on the assistant content (OpenAI wire shape has no structured reasoning field).
v1.13.3-truncate — 2026-05-22
Port of opencode's truncate.ts. Full tool output retrievable via opaque tr_<12 base32 chars> id (~60 bits entropy) and a new view_truncated_output(id) tool. Tmpfs storage at /tmp/boocode-truncations/ (overridable via BOOCODE_TRUNCATION_DIR), 5MB cap, 7-day TTL, orphan-reap on the periodic 60s sweeper. Wired through four tools: view_file, list_dir, web_fetch, codecontext_client. Each returns the existing sliced view plus an outputPath field when truncation fires.
v1.13.2-compaction-prune — 2026-05-22
Two-tier compaction prune — opencode pattern that was half-shipped in v1.11.0. New message_parts.hidden_at column with partial index on WHERE hidden_at IS NULL. messages_with_parts view changed from COALESCE(parts, legacy) to a CASE that distinguishes "no parts at all → fall back to legacy column for pre-v1.13.0 history" from "all parts hidden → drop the row from the model payload" (smoke caught the COALESCE leaking hidden parts back via legacy fallback). prune.ts scans tool_result parts newest-first, protects the last 40k tokens, marks older candidates hidden once the combined estimate clears 20k.
v1.13.1-cleanup-bundle — 2026-05-22
Four independent items owed from prior dispatches. (1) statement_timeout = '30s' at the database level (documented in schema.sql but applied operationally — ALTER DATABASE can't run inside a DO block). (2) Tool registry alpha-sorted at module load — llama.cpp's prompt cache hits on byte-identical prefixes; reordering tools near the top of the system prompt would invalidate every cached turn. (3) Periodic 60s stuck-row sweeper. (4) experimental_repairToolCall to keep streams alive on malformed qwen3.6 tool args (pass-through implementation — logs and forwards unmodified; existing zod-reject path routes back to the model).
v1.13.0-ai-sdk-v6 — 2026-05-22
Major migration to AI SDK v6. Introduces the streamCompletion adapter (services/inference/stream-phase.ts) over streamText, with five known gotchas the LSP can't catch — abort signals swallowed by fullStream (post-iteration throw required), usage lands only at stream end via await result.usage, tools have no execute field (BooCode dispatches in tool-phase.ts), and tool-call-only turns may emit a leading \n text-delta. Also ships the messages_with_parts view (parts-merge read path) and wires reasoning_parts end-to-end via a ReasoningPart in the v6 ModelMessage. Ports ask_user_input correlation queries from JSON columns to message_parts JOINs.
v1.12.4-inference-split — 2026-05-21
Complete inference.ts split into services/inference/. Pieces: turn.ts (orchestration — runAssistantTurn / runInference / createInferenceRunner), sentinel-summaries.ts (runCapHitSummary, runDoomLoopSummary), stream-phase.ts, tool-phase.ts, provider.ts, payload.ts, prune.ts, budget.ts, xml-parser.ts, error-handler.ts, sentinels.ts, parts.ts, types.ts. Public surface re-exported via inference/index.ts; callers import from ./services/inference/index.js explicitly (NodeNext doesn't honor directory-index resolution).
v1.12.3-stale-banner — 2026-05-21
Stale-stream banner with Retry/Discard. When an assistant message sits status='streaming' with no token activity for 60+ seconds, the chat shows a banner above the input. Both actions clear the stale row via new POST /api/chats/:id/discard_stale (updates status='failed', publishes chat_status='idle'). Closes the UX gap from the 2026-05-21 debugging spiral — slow streams and dead streams now look different.
v1.12.2-live-toks — 2026-05-21
Live tok/s + ctx display next to the status indicator. ChatThroughput renders inline beside StatusDot while streaming or tool_running. Subscribes to existing 'usage' WS frames (500ms-throttled, carrying completion_tokens + ctx_used + ctx_max) via sessionEvents. Hides when status drops to idle/error or data is older than 10s. Addresses the same UX gap as v1.12.3-stale-banner — gives users a live token velocity readout that immediately distinguishes slow from dead.
v1.12.1-stop-handler — 2026-05-21
handleAbortOrError now writes status='cancelled' on user stop; rows no longer stuck streaming forever. Drops stale messages_status_check constraint (only messages_status_chk remains, allowing 'cancelled' via TS MESSAGE_STATUSES). Removes detectSameNameLoop and DOOM_LOOP_SAME_NAME_THRESHOLD (added during the 2026-05-21 debugging spike, never fired in any real run) plus 12 verbose ctx.log.info diagnostic markers from the same spike. Bundles workspace pane sync + status indicator overhaul + startup hung-row sweep that landed earlier in v1.12.1 work.
v1.12.0-codecontext — 2026-05-21
Adds the codecontext sidecar (Go-based code-graph indexer at codecontext:8080/v1/<tool_name> over boocode_net) plus container guidance and skills runtime updates. Introduces the chat_status WS frame (streaming | tool_running | waiting_for_input | idle | error, widened from working|idle|error). Drops the deprecated session_panes table — workspace pane state moves to sessions.workspace_panes jsonb for cross-device sync via PATCH /api/sessions/:id/workspace.
v1.11.1-consolidation — 2026-05-21
Rollup of v1.11.0–v1.11.10 work that was shipped piecemeal. Covers anchored rolling compaction (single summary=true row per chat that supersedes itself), doom-loop guard via detectDoomLoop, path_guard secret-filename deny list, web tools (web_search against SearXNG + web_fetch with SSRF/private-IP block), and the 5MB stream-cap on response bodies with abort-on-overflow.
v1.11.0-context-bar — 2026-05-20
Persistent context-window tracker in ChatPane + ctx_max capture via ${LLAMA_SWAP_URL}/upstream/<model>/props. First inferences after a boocode boot may have ctx_max=NULL if llama-swap hasn't loaded the model yet — 60s negative cache TTL recovers on next turn. Replaced an earlier dead read of parsed.timings.n_ctx which never carried n_ctx.
v1.10.1-booterm-user — 2026-05-19
Per-user shell privilege drop in the booterm container via gosu in tmux.conf default-command. Shells launched in browser terminal panes drop privs to samkintop rather than running as root inside the container.
v1.10.0-booterm — 2026-05-18
Second container (apps/booterm, port 9501, bookworm-slim+glibc). Fastify + node-pty + tmux. Browser terminal panes connect via WS to /ws/term/sessions/:sid/panes/:pid; per-session tmux session bc-<sid>, per-pane window term-<pid>. xterm-addon-webgl with document.fonts.load(...)-gated init (Canvas2D doesn't honor font-display: block) and iOS-friendly visibility-change context recreation.
v1.9.2-ask-user-input — 2026-05-18
ask_user_input elicitation tool. Pauses the inference loop and surfaces a prompt to the user; their response routes back as the tool result. Correlation initially via messages.tool_calls / tool_results JSON columns (later ported to message_parts in v1.13.0-ai-sdk-v6).
v1.9.1-skills — 2026-05-18
Skills runtime + /skill slash command with autocomplete. Server-side parser, tools, /api/skills, and mount. Hardens .dockerignore to exclude secrets/ and data/. Drops the type-to-confirm gate on chat delete (plain Cancel/Confirm only — per workspace convention).
v1.9.0-themes-settings — 2026-05-17
Settings pane + per-project defaults + bulk archive + themes lift. themes-v1 (18 preset palettes) ships in the same batch with a Settings picker for live theme switching.
v1.8.2-cap-hit — 2026-05-17
Tool-loop cap-hit summary — when an assistant exceeds the per-turn tool budget, a sentinel role='system' row with metadata.kind='cap_hit' is inserted and a summary turn runs to give the user a coherent endpoint. Also compacts the tool-call UI rendering.
v1.8.1-agents-global — 2026-05-16
Global agents (data/AGENTS.md bind-mounted at /data/AGENTS.md) + parser robustness + WS reconnect toast. Per-project AGENTS.md mechanism (getAgentsForProject) remains for other projects; the BooCode repo itself uses global-only to eliminate two-files-must-stay-in-sync drift.
v1.8.0-agents — 2026-05-16
Tier 2 agents — AGENTS.md registry + per-session agent picker. Also lands mobile tab switcher, branch indicator, and the git_status tool.
v1.7.0-drag-drop — 2026-05-16
Drag-drop + paste-as-attachment for long text in the chat input.
v1.6.0-mobile — 2026-05-16
Full mobile suite. Adds useViewport (matchMedia breakpoints mobile <768 / tablet 768–1023 / desktop ≥1024), useSidebarDrawer / useRightRailDrawer (Context + auto-close on useLocation().pathname change), useLongPress (500ms timer, synthetic contextmenu), usePullToRefresh (80px threshold, 600ms hold), SwipeablePaneTab (60px close, 30px vertical bail). Mobile headers with safe-area padding, hamburger left, FolderTree right. Tap targets at max-md:min-h-[44px] max-md:min-w-[44px]. Raises MAX_TOOL_LOOP_DEPTH 5 → 15. Right-rail becomes a drawer on mobile.
v1.5.1-bootstrap — 2026-05-16
Bootstrap fixes — git + ssh installed in the boocode container, Tailscale host rewrite, /opt/projects label correction for the create-new-project bootstrap flow.
v1.5.0-refactor-tests — 2026-05-16
Refactor split (FileBrowserPane / Workspace / runAssistantTurn) + vitest harness + unit tests for security-critical pure functions. Scopes the /opt mount to /opt/projects (writable) plus PROJECT_ROOT_WHITELIST=/opt (read-only resolution for add-existing). Surfaces swallowed errors and removes dead session_renamed paths.
v1.4.0-fork-header — 2026-05-16
Fork from message + delete message + header polish + general housekeeping.
v1.3.0-chats-projects — 2026-05-16
Chats-in-sessions era. Adds force-send, /compact, right-rail file browser, archive/rename/Open-in-Gitea sidebar context menu, archived projects landing page, create-project bootstrap with Gitea remote setup, landing-card buttons, 1000px content cap. Dedup audit and chat archive/delete from the sidebar.
v1.2.0-multi-pane — 2026-05-15
Multi-pane workspace (batch 3, T1–T8). session_panes schema (later replaced by sessions.workspace_panes jsonb in v1.12.0), Pane discriminated union, broker user channel + /api/ws/user, file_ops + file_index services, PaneShell / ChatPane / FileBrowserPane / PaneTab / Workspace components, usePanes hook, Shiki integration in CodeBlock. Up to 5 panes per session; default chat pane created on POST /api/sessions.
v1.1.0-markdown-sidebar — 2026-05-15
Markdown rendering, message actions, tok/s + ctx display, AI session naming. Sidebar restructure — chats nested under projects (max 5 + view-all), live updates via WS.
v1.0.0-initial — 2026-05-14
Initial commit. Skeleton of the monorepo: apps/server (Fastify + postgres), apps/web (React + Vite), basic chat loop against llama-swap.