Files
boocode/boocode_roadmap.md
indifferentketchup bbf9fac936 docs(roadmap): reconcile post-v1.6.1 + v1.6.2 in-flight
Update version summary: v1.6-mobile-pass and v1.6.1-cleanup are now
merged with SHAs; v1.6.2-mobile-ui-fixes added as in-flight with its
4-commit plan. v1.6.1-cleanup details rewritten to reflect what
actually shipped (B1) vs what was audited-only (secrets, panes,
unused exports, hand-rolled patterns, mount scope, etc.).

Closed two open items: session_renamed has a server publisher since
v1.4; PATCH /api/panes/:id is moot (endpoint never re-introduced).
Dependency graph updated with v1.6.2 node between v1.6.1 and v1.7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 06:37:27 +00:00

516 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BooCode v1.x — Roadmap
Last updated: 2026-05-16
## Overview
BooCode is a standalone code-chat tool at `/opt/boocode/`. Read-only by design in v1.x — pick a project, chat with a local LLM that has file-inspection tools, get streaming responses over WebSocket. Built May 2026 after the in-boolab BooCode mode stalled.
v1 shipped in a single Claude Code session. v1.1 onwards is a batched build-out. Original Batch 110 plan was reordered mid-stream — chats-inside-sessions, archive, and fork/delete work was prioritized over the mobile pass.
Live at `https://code.indifferentketchup.com` (Caddy → Authelia → Tailscale → `100.114.205.53:9500`).
-----
## Version summary
|Version |Theme |Status |Notes |
|----------------|---------------------------------------------------------------------------------------------------------------------------------------|---------------------------------|-----------------------------------------|
|v1.0 |Initial scaffold, read-only tools, WS streaming |✅ Done |Shipped in one Claude Code session |
|v1.1-batch1 |Markdown, Copy + Regen, tok/s + ctx, AI chat naming |✅ Merged |— |
|v1.1-batch2 |Sidebar restructure: projects → sessions, max 5 + “view all” |✅ Merged |— |
|v1.1-batch3 |Pane system, FileBrowserPane + Shiki, chat→file click, cross-tab |✅ Merged |— |
|v1.1-batch3.5 |Chip infrastructure, `@file` picker, line-select-attach |✅ Merged |— |
|v1.2 |Chats inside sessions refactor, right-rail, `/compact`, archive, force-send |✅ Merged |Replaced original “Batch 4 = mobile” plan|
|v1.2-project-ux |Project archive UX, sidebar context menu, full-bootstrap, Gitea API |✅ Merged |— |
|v1.3 |Tab-close + chat-archive |✅ Merged |— |
|v1.4 |Fork from message + delete message + header polish + housekeeping |✅ Merged |Was original “Batch 5” |
|v1.5 |Refactor splits, vitest harness (23 tests), error-log surfacing, `/opt:ro` + `BOOTSTRAP_ROOT`, persistent context-window tracker |✅ Merged |— |
|v1.5.1 |Bootstrap hotfix: git in container, SSH keypair, known_hosts, SSH URL rewrite, /opt/projects label |✅ Merged |`4a9f207` |
|v1.6-mobile-pass|Mobile pass: drawer, pane stacking, long-press, swipe-to-close, pull-to-refresh, IME safety, safe-area, tap targets + H1 path-guard fix|✅ Merged |`57c883b..943ae7d` (6 commits) |
|v1.6.1-cleanup |Mostly audit-only; one fix shipped: RightRail `max-md:hidden` wrapper. Audit reports for secrets, stale code, panes, mount scope, hand-rolled patterns deferred to follow-ups |✅ Merged |`6a9fe18` |
|v1.6.2-mobile-ui-fixes|Mobile UI polish from device testing: kill single-pane navigator chrome, header rework, “New chat” in long-press menu, RightRail as mobile drawer (reverts v1.6.1 wrapper) |🔄 Hand-back received, uncommitted|— |
|v1.7 |Drag-drop + paste-as-attachment (chip infra extension) |Planned |Was Batch 6 |
|v1.8 |Settings drawer (system prompt per project + session, web search toggle) |Planned |Was Batch 7 |
|v1.9 |Web search backend: SearXNG `web_search` + `web_fetch` tools |Planned |Was Batch 8 |
|v1.10 |Agents (Tier 2): `AGENTS.md`, per-agent model/temp/tools, picker |Planned |Was Batch 9 |
|v1.11 |BooTerm: separate container, xterm.js + node-pty + tmux, terminal pane |Planned |Was Batch 10 |
-----
## Version details
### v1.1-batch1 — Message polish ✅
Markdown (`react-markdown` + `remark-gfm`), Copy + Regenerate, tok/s + context counter, AI session naming.
**Key decisions:**
- `sessions.name` (not `title`).
- `enable_thinking: false` + `max_tokens: 30` for Qwen3 utility calls.
- `messages_deleted` WS frame added for multi-tab regen.
- In-app event bus (`sessionEvents.ts`, module-scope `Set<Listener>`).
**Schema:** `messages.tokens_used`, `messages.ctx_used`, `messages.ctx_max`, `messages.started_at`, `messages.finished_at`.
-----
### v1.1-batch2 — Sidebar restructure ✅
Projects as expandable groups, up to 5 recent sessions per project, “View all (N)”, `GET /api/sidebar`, `useSidebar` singleton hook.
**Key decisions:**
- `useSidebar` module-scope singleton.
- `localStorage['boocode.sidebar.expanded']`.
- `session_renamed` payload `{session_id, name}`.
-----
### v1.1-batch3 — Pane system ✅
`session_panes` table, pane CRUD with transactional position-shift, Workspace + tab strip + drag-to-reorder (native HTML5), ChatPane (extracted), FileBrowserPane (tree + Shiki + filter), chat→file click, PaneTab context menu, `file_ops` + `file_index` shared services, broker user channel + `/ws/user`, `session_updated`, `session_loaded`, idempotent default-Chat-pane backfill.
**Schema:** `session_panes` (id, session_id, position, kind CHECK, state JSONB).
-----
### v1.1-batch3.5 — Chips + @file + line-select ✅
`Attachment` type + `flattenToMessage` + LANG_MAP, `AttachmentChip`, `AttachmentPreviewModal`, ChatInput chip-row, hand-rolled `@file` mention popover, line-select-attach in FileBrowserPane via local `FileViewer`, FileBrowserPane filter upgrade (empty=tree, non-empty=flat).
-----
### v1.2 — Chats inside sessions ✅
Originally planned as the mobile pass. Reshuffled: structural refactor — chats inside sessions, right-rail, `/compact` (chats own model summarizes via `kind='compact'` system message), force-send.
**Schema:** `chats` table, `sessions.status`, `messages.chat_id`, `messages.kind` (regular | compact).
-----
### v1.2-project-ux ✅
Full new-project bootstrap (mkdir + git init + .gitignore + first commit + Gitea remote + push), sidebar context menu (Rename / Archive / Open in Gitea), project landing page archived-list, Gitea API integration. Option B taken: `BOOTSTRAP_ROOT` env var, `/opt` stays read-only mount, `/opt/projects` writable.
**Schema:** `projects.status`, `projects.archived_at`.
**Key decisions:**
- `execFile` only, no `exec` shell strings.
- DB INSERT last in bootstrap sequence.
- Soft-fail on Gitea steps.
- Project Delete endpoint exists but stays unexposed (re-add INSERTs fresh row → FK cascade nukes history; archive is the safe pattern).
-----
### v1.3 — Tab close + chat archive ✅
Tab close UX cleanup, chat-level archive (separate from session archive).
-----
### v1.4 — Fork + delete + header polish ✅
Was originally planned as Batch 5.
**Shipped:** `POST /api/sessions/:id/fork` (deep copy messages up to target, new session in same project), `DELETE /api/sessions/:id/messages/:id` (cascading via `messages_deleted` frame), header breadcrumb (Projects → Project → Session), inline-editable session name, file path shown when File Browser pane is active, `useActivePane` hook.
-----
### v1.5 — Refactor + tests + security scoping + context tracker ✅
5-commit sequence:
1. **Refactor:** FileBrowserPane (865 → split with FileViewer extracted), Workspace, inference split.
1. **Vitest harness:** 23 tests covering routes + resolveProjectPath. Pinned to v3 (Vite 5 / vitest 4 incompatibility).
1. **Error-log surfacing:** dead-code removal from earlier H1/H2 audit items, structured error logs to client.
1. **Mount scoping:** `/opt:/opt:ro` + `BOOTSTRAP_ROOT` writable subdir. Container loses write to `/opt` proper.
1. **Persistent context-window tracker:** floating popover above chat input right edge, source = latest `message_complete` frames `ctx_used` / `ctx_max`, color-coded (neutral <60%, amber 6085%, red 85%+), hides when `ctx_max` null.
**Carried bug:** `resolveProjectPath` whitelist-root bypass — discovered, asserted as “BEHAVIOR GAP” rather than silently patched. Fix landed in v1.6 (H1).
-----
### v1.5.1 — Bootstrap hotfix ✅ (`4a9f207`)
Dockerfile (git installed in container), docker-compose.yml, project_bootstrap.ts (SSH keypair, known_hosts, SSH URL Tailscale rewrite), CreateProjectModal.tsx, .gitignore. /opt/projects label clarified.
**Known issue carried forward:** dispatch used the in-repo `secrets/boocode_gitea` SSH key because the agent key was rejected. Key exposure flagged. Audit + rotation tracked in v1.6.1 below.
-----
### v1.6-mobile-pass ✅
**Merged via 6 commits `57c883b..943ae7d`** (5 functional + 1 docs):
1. `57c883b chore: fix resolveProjectPath whitelist-root bypass` (H1 — dropped `real !== whitelistReal` short-circuit; flipped the v1.5 BEHAVIOR GAP test; 23/23 pass).
1. `a643b5f feat(mobile): viewport hook + sidebar drawer + hamburger headers` (M1 + M2 + M6-header).
1. `cd897d6 feat(mobile): single-pane stack + long-press tab menu + swipe-to-close` (M3 + M4 + A2).
1. `273eeac feat(mobile): chat input keybinds + safe-area + tap targets + overflow safety` (M5 + M6-bottom + M7 + M8).
1. `4b5b9b2 feat(mobile): pull-to-refresh sidebar list` (A1).
1. `943ae7d docs: add v1.x roadmap snapshot` (this file).
**Decisions:**
- H2 (roadmap update) handled in this file rather than by Claude Code.
- M5: mobile = button-only send, Enter inserts newline. Desktop unchanged. `isComposing` guard for CJK IME.
- M6: kept `max-w-[1000px]` (mobile naturally full-width below cap).
- URL state: `?pane=<paneId>`. Bare URL resets activePaneIdx to 0.
- Long-press dispatches synthetic `contextmenu` on `[data-tab-id]`, opening Radix ContextMenuTrigger at touch coords. iOS callout suppressed.
- `SwipeablePaneTab`: 60px threshold, bails if vertical >30px, opacity 1→0.4.
- A2 bundled with M3 in Commit 3 (structural coupling).
- Home.tsx no hamburger.
**Deferred from v1.6 → rolled into v1.6.1-cleanup:**
- RightRail still renders on mobile (~32px column).
- Secrets hygiene audit.
- `ProjectSidebar.tsx` and `ChatTabBar.tsx` share content from two commits each — use `git add -p`.
-----
### v1.6.1-cleanup ✅ (`6a9fe18`)
**Shipped:** RightRail wrapped in `<div className="max-md:hidden contents">` so it's hidden entirely below the md breakpoint on mobile. (Note: v1.6.2 reverses this and replaces with a proper mobile drawer — see below.)
**Audited but not shipped (queued for follow-ups):**
- **Secrets hygiene:** `secrets/boocode_gitea` is NOT tracked; never committed to any branch; `.gitignore` already covers `secrets/`. Rotation is a Gitea-side action, no repo change needed.
- **`.bak` files:** 3 leftover from v1.5.1 (`docker-compose.yml.bak-20260516`, `Dockerfile.bak-20260516`, `apps/web/src/components/CreateProjectModal.tsx.bak-20260516`). Git-invisible via global `~/.gitignore_global` (`*.bak*`). Decide per file.
- **Unused exports:** neither `knip` nor `ts-prune` installed. Proposal pending.
- **Dead WS frames:** `session_renamed` HAS a server publisher (`routes/sessions.ts:140`, added in v1.4) — the roadmap's "no server publisher" open item is **STALE**, crossed off. The `InferenceFrame` union still declares `session_renamed` as a type variant but no code publishes it on the per-session channel; trivial 1-line cleanup deferred.
- **Unused imports:** web `tsc --noUnusedLocals --noUnusedParameters` returns 0 warnings.
- **`useSessionStream` refcount:** opportunity confirmed (~90 lines diff to apply the `useSidebar`-style module-scope singleton pattern). Risk LOW. Queued for v1.6.2 or later.
- **PATCH `/api/panes/:id` ownership:** **MOOT** — endpoint does not exist (the pane REST API was never re-introduced after pane state moved to client-side localStorage in v1.2). Crossed off open items.
- **Hand-rolled patterns vs library:** 5 hand-rolled hooks/components total 336 lines. None duplicates anything in existing deps; library swap (`@use-gesture`, `react-pull-to-refresh`) not worth the dep cost yet.
- **`/opt:/opt:ro` mount tightening:** Two-option plan documented for v1.6.2 — Option A (per-project bind-mounts) or Option B (deny `.env` pattern in `pathGuard`). Option B is the simpler short-term fix.
-----
### v1.6.2-mobile-ui-fixes 🔄
**Hand-back received, uncommitted on `v1.6.2-mobile-ui-fixes`.** 4-commit sequence proposed:
1. `fix(mobile): hide Split button + single-pane navigator chrome` (G1 — wrap the Workspace Split row in `!isMobile`).
1. `feat(mobile): rework Session and Project headers for narrow viewports` (G2 — breadcrumb `hidden sm:flex`, session name cap `max-w-[140px] sm:max-w-[280px]`, project page heading `text-base sm:text-lg`, “New session” icon-only on mobile).
1. `feat(mobile): add "New chat" to tab long-press context menu` (G3 — top of menu, separator, then existing items).
1. `feat(mobile): right-rail as drawer on mobile, header toggle button` (G4 option b — new `useRightRailDrawer` Context hook, `RightRail` renders as fixed `w-[85vw] max-w-sm` drawer on mobile, FolderTree button in Session header, **reverts v1.6.1's `max-md:hidden` wrapper**).
**Decisions:**
- G4 option b chosen: mobile file browsing IS useful; drawer pattern mirrors `useSidebarDrawer`.
- G2 single-row session-name+model layout (model picker right-aligned), per spec example.
- G3 "New chat" at top, separator, then Rename.
- G2 "New session" button: icon-only on mobile via `<span className="hidden sm:inline">New session</span>`.
**Adjacent uncommitted change (not part of v1.6.2):** `MAX_TOOL_LOOP_DEPTH 5 → 15` in `apps/server/src/services/inference.ts`. Sam-authored, sitting in working tree on `v1.6.2-mobile-ui-fixes`. **NOT on main as of this update.** Commit separately.
-----
### v1.7 — Drag-drop + paste (planned, was Batch 6)
**Depends on:** v1.6.1 merged.
**Scope (trimmed — chip infra exists from v1.1-batch3.5):**
- Drag-drop files onto ChatInput → chip via `addAttachment({kind: 'file', source: 'drop'})`.
- Paste >8 lines → chip via `addAttachment({kind: 'paste', source: 'paste'})`. ≤8 lines inline.
- Drop overlay (dashed border + “Drop to attach”).
- Client-side 5 MB cap + binary detection (null-byte check in first 8KB).
- Max 10 attachments shared cap.
- Folder drop rejected. Image paste rejected. Binary files rejected with toast.
**Frontend only.**
-----
### v1.8 — Settings drawer (planned, was Batch 7)
**Depends on:** header gear (already in v1.4).
**Scope:**
- Right-side drawer (hand-rolled, no shadcn Sheet). Tabbed: Session + Project.
- Session tab: system prompt, web search toggle, model picker, session name.
- Project tab: default system prompt, default web search, project name, root path (read-only), delete project (consider whether to expose given the cascade concern).
- Resolution: `session.system_prompt OR project.default_system_prompt OR ""`.
- Project defaults applied at session create (copied), not retroactively.
- Web search toggle persistent per session (`sessions.web_search_enabled`).
**Schema:** `sessions.web_search_enabled`, `projects.default_system_prompt`, `projects.default_web_search`.
-----
### v1.9 — Web search backend (planned, was Batch 8)
**Depends on:** v1.8.
**Scope:**
- `web_search` tool → SearXNG at `http://100.114.205.53:8888/search?format=json`, top-N `{title, url, snippet}`.
- `web_fetch` tool, regex HTML strip (no cheerio), 50KB cap.
- Tools conditionally included based on `session.web_search_enabled`.
- `ToolCallCard.tsx` renders results as clickable URL list, web_fetch as text preview.
- Env: `SEARXNG_URL`, `WEB_FETCH_TIMEOUT_MS`, `WEB_FETCH_MAX_BYTES`.
-----
### v1.10 — Agents (planned, was Batch 9)
**Depends on:** v1.8.
**Scope:**
- Tier 2 agents: system prompt + model + temperature + tools whitelist per agent.
- `AGENTS.md` (OpenCode-compatible): `## Agent Name` blocks with YAML frontmatter.
- Three builtin defaults (Investigator, Architect, Reviewer) when no `AGENTS.md`.
- If `AGENTS.md` exists, only its agents shown.
- Agent picker in ChatInput toolbar + SettingsDrawer.
- Tools whitelist enforced at inference layer. BooChat agents read-only.
- File parsed on demand with mtime cache.
- Mid-conversation agent switch allowed; old messages retain their tool history.
**Schema:** `sessions.agent_id TEXT`.
-----
### v1.11 — BooTerm (planned, was Batch 10)
**Depends on:** v1.1-batch3 (pane system), v1.8 (settings drawer pattern).
**Scope:**
- New container `booterm` at `100.114.205.53:9501`. Fastify + node-pty + tmux.
- Caddy path-based routing: `/api/term/*` + `/ws/term/*` → booterm.
- Shared `boocode_db`.
- Per-session tmux (`bc-<session_id>`), per-pane tmux window (`term-<pane_id>`).
- xterm.js terminal pane. New `kind = 'terminal'` in `session_panes` CHECK.
- PTY over binary WebSocket. Resize via `tmux resize-window`.
- Workspace mount: `/opt/repos:/opt/repos:rw`. BooCode chat container keeps `/opt:/opt:ro`.
- Send-to-terminal from chat: select text → right-click → “Send to terminal”.
- tmux persistence across WS reconnects, page refreshes, container restarts.
- No chroot/namespace isolation. Acceptable single-user homelab.
**New app:** `apps/booterm/`.
-----
## Architecture
### Containers (current + planned)
|Container |Port |Mount |Purpose |Status |
|------------|---------------------|-----------------------------------|----------------------------|---------|
|`boocode` |`100.114.205.53:9500`|`/opt:/opt:ro` + `/opt/projects:rw`|Chat + read-only tools + SPA|Live |
|`boocode_db`|`127.0.0.1:5500` |`boocode_pgdata` |Postgres 16-alpine (shared) |Live |
|`booterm` |`100.114.205.53:9501`|`/opt/repos:/opt/repos:rw` |Terminal sessions |v1.11 |
|`boocoder` |TBD |`/opt/repos:/opt/repos:rw` |Write tools |Post-v1.x|
### URL routing (target state after v1.11)
```
code.indifferentketchup.com
├── / → boocode (SPA)
├── /api/chat/*, /ws/chat/* → boocode :9500
├── /api/term/*, /ws/term/* → booterm :9501
├── /api/coder/*, /ws/coder/* → boocoder (future)
└── /ws/user → boocode :9500
```
### Database
Single Postgres `boocode_db`. All containers share. Projects shared. Sessions container-specific.
Current schema (post v1.5.1):
```
projects
├── id UUID PK
├── name TEXT
├── root_path TEXT
├── status TEXT (v1.2-project-ux: active | archived)
├── archived_at TIMESTAMPTZ (v1.2-project-ux)
├── default_system_prompt TEXT (v1.8)
├── default_web_search BOOLEAN (v1.8)
└── created_at TIMESTAMPTZ
sessions
├── id UUID PK
├── project_id UUID FK → projects
├── name TEXT
├── model TEXT
├── system_prompt TEXT
├── status TEXT (v1.2: active | archived)
├── web_search_enabled BOOLEAN (v1.8)
├── agent_id TEXT (v1.10)
├── created_at TIMESTAMPTZ
└── updated_at TIMESTAMPTZ
chats (v1.2)
├── id UUID PK
├── session_id UUID FK → sessions
├── name TEXT
├── status TEXT
├── created_at TIMESTAMPTZ
└── updated_at TIMESTAMPTZ
messages
├── id UUID PK
├── session_id UUID FK → sessions
├── chat_id UUID FK → chats (v1.2)
├── kind TEXT (v1.2: regular | compact)
├── role TEXT
├── content TEXT
├── tool_calls JSONB
├── tool_results JSONB
├── status TEXT
├── last_seq INTEGER
├── tokens_used INTEGER (v1.1-batch1)
├── ctx_used INTEGER (v1.1-batch1)
├── ctx_max INTEGER (v1.1-batch1)
├── started_at TIMESTAMPTZ (v1.1-batch1)
├── finished_at TIMESTAMPTZ (v1.1-batch1)
└── created_at TIMESTAMPTZ
session_panes (v1.1-batch3)
├── id UUID PK
├── session_id UUID FK → sessions (CASCADE)
├── position INTEGER
├── kind TEXT CHECK (chat | file_browser | terminal)
├── state JSONB
└── created_at TIMESTAMPTZ
settings
├── k TEXT PK
└── v TEXT
```
### Reusable patterns
|Pattern |Where |Used by |
|----------------------------|----------------------------------------|---------------------------------------------------------|
|In-app event bus |`sessionEvents.ts` |All batches. Module-scope `Set<Listener>`. |
|Singleton hooks |`useSidebar.ts` |Module-scope shared state. |
|User-channel WS broker |`broker.ts` + `useUserEvents.ts` |Cross-tab lifecycle. One WS per tab. |
|`clock_timestamp()` |All INSERT/UPDATE |Never `NOW()` in new code. |
|Additive schema only |`schema.sql` |`ADD COLUMN IF NOT EXISTS`, `CREATE TABLE IF NOT EXISTS`.|
|Idempotent backfills |`schema.sql` |`INSERT ... WHERE NOT EXISTS`. |
|`enable_thinking: false` |`auto_name.ts` |Required for Qwen3 utility calls. |
|`pathGuard` |`tools/*`, `file_ops.ts` |Realpath + project root enforcement. |
|Shared `file_ops.ts` |`tools.ts`, `routes/projects.ts` |Same core for inference tools and UI. |
|File index (`file_index.ts`)|`routes/projects.ts` |`rg --files` + mtime cache. |
|`useViewport` |`hooks/useViewport.ts` (v1.6) |matchMedia, SSR-safe. |
|`useSidebarDrawer` |`hooks/useSidebarDrawer.tsx` (v1.6) |Context + auto-close on route change. |
|Hand-rolled long-press |`hooks/useLongPress.ts` (v1.6) |500ms touchstart timer, dispatches synthetic contextmenu.|
|Hand-rolled pull-to-refresh |`hooks/usePullToRefresh.ts` (v1.6) |80px threshold, 600ms min hold. |
|Hand-rolled swipe |`components/SwipeablePaneTab.tsx` (v1.6)|60px threshold, vertical bail at 30px. |
-----
## Tech stack
|Layer |Tech |
|----------------|--------------------------------------------------------------------------|
|Backend |Node 20 + Fastify + `@fastify/websocket` + `@fastify/static` + zod + `pg` |
|Frontend |React + Vite + Tailwind v4 + shadcn nova preset |
|Inference |llama-swap `http://100.101.41.16:8401` (OpenAI-compatible) |
|Search |SearXNG `http://100.114.205.53:8888` (v1.9) |
|Syntax |Shiki (`github-dark`) |
|Terminal |xterm.js + node-pty + tmux (v1.11) |
|Auth |`Remote-User` from Authelia via Caddy `forward_auth` |
|Containerization|Docker Compose, Node 20-alpine, multi-stage, ripgrep apk, git apk (v1.5.1)|
|DB |Postgres 16-alpine, loopback `127.0.0.1:5500` |
|Networking |Tailscale mesh, Caddy (DO droplet), Authelia SSO |
|Code hosting |Gitea `git.indifferentketchup.com` |
|Tests |vitest v3 (pinned, Vite 5 / vitest 4 incompatible) |
-----
## Known open items
- **`useSessionStream` refcount.** Two ChatPanes = two WS. Apply singleton pattern. Audited in v1.6.1, queued.
- **`/opt:/opt:ro` mount exposes all `.env` files.** Whitelist scope before BooCoder. Two-option plan documented in v1.6.1 audit; ship in v1.6.2 or v1.7.
- **`secrets/boocode_gitea` in repo working tree.** Never committed (git-invisible via global ignore). Rotate the Gitea-side key when convenient; no repo action required.
- **Dormant in-boolab BooCode mode.** Reference only.
- **BooCoder container.** Post-v1.x.
**Closed since last update:**
- ~~`session_renamed` no server WS publisher~~ — server publishes via `broker.publishUser` from `routes/sessions.ts:140` (added in v1.4). Confirmed in v1.6.1 audit.
- ~~PATCH `/api/panes/:id` lacks session-ownership check~~ — endpoint does not exist; the pane REST API was never re-introduced after v1.2 moved pane state to localStorage.
-----
## Dependency graph
```
v1.0 (initial)
v1.1-batch1 (markdown)
v1.1-batch2 (sidebar)
v1.1-batch3 (panes) ────────────────────────┐
│ │
▼ │
v1.1-batch3.5 (chips) ──────┐ │
│ │ │
▼ │ │
v1.2 (chats-in-sessions) │ │
│ │ │
▼ │ │
v1.2-project-ux │ │
│ │ │
▼ │ │
v1.3 (tab-close) │ │
│ │ │
▼ │ │
v1.4 (fork+delete+header) ◄──┼────────────────┘
│ │
▼ │
v1.5 (refactor+tests+ctx) │
│ │
▼ │
v1.5.1 (bootstrap hotfix) │
│ │
▼ │
v1.6-mobile-pass │
│ │
▼ │
v1.6.1-cleanup │
│ │
▼ │
v1.6.2-mobile-ui-fixes ◄─────┘
v1.7 (drag-drop) ◄── v1.1-batch3.5
v1.8 (settings)
├──▶ v1.9 (web search)
├──▶ v1.10 (agents)
└──▶ v1.11 (BooTerm) ◄── v1.1-batch3
```
-----
## Workflow
1. Verify previous version merged to `main`.
1. Dispatch prompt via Paseo (Claude Code runs at `/opt/boocode`).
1. Claude Code recon → blocking questions → implement → hand back.
1. Review hand-back in separate Claude chat (spec compliance, code quality, drift, stale code).
1. Deploy: `docker compose up --build -d`.
1. Smoke test per the hand-backs plan.
1. Sam commits manually, pushes to Gitea, merges to `main`.
1. Next version.
Sam reviews all diffs. Sam commits. Never git pull/push/commit on his behalf.