Splits the previous /opt:/opt:rw bind into two mounts to narrow the
writable scope of the container:
- /opt:/opt:ro — read-only mount for legacy/existing project
add-existing flow. resolveProjectPath still uses
PROJECT_ROOT_WHITELIST (/opt by default) so existing projects under
/opt/<name> (analytics, boolab, boocode itself) continue to resolve
and serve their file-tree via the read-only tools.
- /opt/projects:/opt/projects:rw — writable mount targeted at the
create-new-project bootstrap path.
Picked Option B from the spec (simpler than two scan roots):
PROJECT_ROOT_WHITELIST stays /opt, new BOOTSTRAP_ROOT env var defaults
to /opt/projects and is used by project_bootstrap.ts as the mkdir
target. Bootstrap path-escape check now compares against
BOOTSTRAP_ROOT.
Prereq: host must `mkdir -p /opt/projects` before next container
restart. Documented in CLAUDE.md and .env.example.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fastify global empty-JSON-body parser fixes archive/unarchive/stop 400s
- Removed redundant local sessionEvents.emit at all 5+2 sites with server-side WS publishers; added dedupe guards in useSidebar/Workspace/Project handlers
- Sidebar session right-click adds Delete (destructive) with confirm Dialog
- Session.tsx navigates away on session_deleted/session_archived for the active session
- SessionLandingPage chat rows show message_count, effective_context_tokens, last_message_preview via LATERAL joins on GET /api/sessions/:id/chats
- Workspace.tsx pane drag-to-reorder using native HTML5 events (no new deps)
- CompactCard: Copy toast, Send-to-chat with target chat name, empty-state in share popover, Re-run button
- auto_name.ts: filter count gate and assistant-fetch by content <> '' so tool-call assistant rows don't trip the once-and-only-once guard
- Adds CLAUDE.md and apps/web/src/lib/format.ts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>