# Edit History — 2026-04-04 ## Summary Major session covering **file explorer**, **template import/replace**, **bundle drop zone**, **team expansion**, **canvas toolbar**, **viewport persistence**, **hot-reload**, **WebSocket events**, **Docker Compose**, **plugin system** (ECC + Superpowers), **production hardening** (graceful shutdown, rate limiting, AES-256 secrets), **human-in-the-loop** approval chain, **HMA memory** (L1/L2/L3), **coordinator pattern**, **code sandbox**, **memory consolidation**, **Langfuse trace preview**, **search dialog**, **toast notifications**, **OpenRouter provider**, **skill installer**, **ClawHub integration**, **team zoom-in**, **connection breakage visualization**, and 15 code review rounds. UI E2E tested via browser: 95/104 plan items done (91%). ## File Explorer Tab (FilesTab) - Tree view with collapsible directories and file type icons - Inline code editor with monospace font, Ctrl/Cmd+S save, Tab inserts spaces - Create new files with path input, delete with confirmation - Platform endpoints: `GET/PUT/DELETE /workspaces/:id/files/*path` for individual files, `GET /workspaces/:id/files` for tree listing - Path traversal protection via `validateRelPath()` - `useMemo` for tree building, ref-based cursor positioning ## Agent Import & Replace via UI - **Import Agent Folder**: button in template palette, uses `webkitdirectory` file picker, uploads to `POST /templates/import` - **Replace Agent Files**: button in DetailsTab, uploads folder to `PUT /workspaces/:id/files`, auto-restarts, confirmation before destructive replace - Auto-generates `config.yaml` detecting prompt_files and skills from uploaded files - CLI script `import-agent.sh` also available ## Canvas Visual Overhaul - **Toolbar**: fixed top-center bar with Molecule AI logo, live status counts, workspace total - **WorkspaceNode redesign**: status gradient bar, team badge (child count), pill-shaped handles, 4 skill badges - **SidePanel**: slide-in animation, tab icons, UUID footer, darker backdrop blur - **ContextMenu**: status dot header, expand/collapse team actions - **Animations**: node fade-in on mount, panel slide-in from right - **Scrollbar**: custom thin zinc styling ## Bundle & Team Features - **BundleDropZone**: drag `.bundle.json` onto canvas to import, visual overlay, progress spinner, toast - **Context menu**: Export Bundle downloads file, Duplicate exports+re-imports - **Team expansion APIs**: `POST /workspaces/:id/expand` creates sub-workspaces from config, `POST /collapse` removes them - **Context menu**: Expand to Team / Collapse Team - **bundle-compile.sh**: compiles all templates to `.bundle.json` artifacts ## Runtime & Infrastructure - **Viewport persistence**: `GET/PUT /canvas/viewport`, debounced save on pan/zoom, restore on load - **Hot-reload watcher** (`watcher.py`): polls config dir for hash changes, debounced reload - **WebSocket subscriber** (`events.py`): connects to platform `/ws`, triggers prompt rebuild on peer events - **Langfuse auto-injection**: detects env vars, creates CallbackHandler - **Cross-workspace trace linking**: delegation tool passes `parent_task_id` in A2A metadata - **Workspace forwarding**: discovery follows `forwarded_to` chain (max 5 hops) - **Full Docker Compose**: 6 services with health checks on shared `molecule-monorepo-net` - **Auto-discover configs dir**: `findConfigsDir()` searches parent directories ## Bug Fixes - **A2A executor**: switched from `astream` to `ainvoke` for reliable responses across models - **Chat response parsing**: handle `result.parts[]` with `kind` field (a2a-sdk v0.3 format) - **Terminal handler**: tries multiple container name patterns (ws-{id}, workspace-name) - **A2A proxy**: injects `messageId` into `params.message` (required by a2a-sdk) - **Path traversal**: `validateRelPath()` blocks `../` and absolute paths in file uploads - **Shared utilities**: `normalizeName()`, `writeFiles()`, `generateDefaultConfig()` extracted to eliminate 3x duplication ## ECC & Superpowers Integration ### Workspace Templates - **ecc-coding-agent**: Everything Claude Code with 10 curated skills (coding-standards, tdd-workflow, e2e-testing, security-review, api-design, backend-patterns, frontend-patterns, deep-research, shell-exec), CLAUDE.md + AGENTS.md + WORKING-CONTEXT.md as prompt files - **superpowers-agent**: obra/superpowers with 16 skills (14 from superpowers + shell-exec + file-ops), code-reviewer agent, 3 commands (brainstorm, write-plan, execute-plan) - **import-ecc.sh**: CLI script to import individual or all 156 ECC skills as templates ### Plugin System (integrated into every workspace) - `workspace/plugins.py`: scans `/plugins/` for installed plugins, loads rules/*.md, prompt fragments, and skills directories - `plugins/ecc/`: ECC guardrails rules + 5 shared skills (coding-standards, tdd-workflow, security-review, api-design, deep-research) + AGENTS.md prompt fragment - `plugins/superpowers/`: 5 shared skills (test-driven-development, systematic-debugging, writing-plans, executing-plans, verification-before-completion) - Every workspace agent auto-inherits plugin rules + skills (deduplicated by ID, workspace skills take priority) - Provisioner mounts `/plugins:ro` into every container - docker-compose.yml mounts `./plugins` for platform ### Prompt Integration - `prompt.py`: accepts `plugin_rules` and `plugin_prompts` parameters - Rules injected as "Platform Rules" section - Prompt fragments injected as "Platform Guidelines" section - Plugin skills merged after workspace skills (deduplicated) ## Code Review Fixes (Rounds 7-9) - Round 7: path traversal fix, dedup normalization/config generation, file upload limit (200), delete confirmation for replace - Round 8: remove unused `getLang`, success timer ref cleanup, delete confirmation for files, textarea ref for Tab key, `useMemo` for tree - Round 9: deduplicate plugin skills by ID (workspace takes priority), remove arbitrary 50-char prompt size filter - Round 10: rate limiter goroutine leak (accept context), crypto key warning on invalid, .env.example sync - Round 11: unused useReactFlow import, search state lifted to store, synthetic event replaced - Round 12: remove unnecessary comment in Toolbar - Round 13: N+1 approval polling → single endpoint, auto-expiry, TEAM scope query fix, configurable poll env vars - Round 14: sandbox shell injection (mount temp file), consolidation fallback path, SandboxConfig wired to load_config - Round 15: executor returns only AI messages (skip tool results), locale middleware narrowed to known codes - Round 16: MCP server network error handling + startup platform validation - Round 17: parallel file downloads in Files tab ## MCP Server - `mcp-server/` — TypeScript MCP server using `@modelcontextprotocol/sdk`, stdio transport - **20 tools**: list/create/get/delete/restart workspaces, chat with agent, assign model, set/list secrets, list/read/write/delete files, commit/search memory, list templates, expand/collapse team, list/decide approvals - Works with Claude Code, Cursor, Codex, OpenCode — any MCP client - Added to `.mcp.json` for immediate Claude Code integration - Startup validates platform connectivity, logs warning with fix instructions - Network errors return friendly messages instead of crashing ## Embedded Sub-Workspaces - **WorkspaceNode redesign**: parent nodes expand in size (320-450px) when they have children. Children render as embedded mini-cards in a "Team Members" 2-column grid inside the parent node. - **No separate nodes/edges**: child nodes set `hidden: true` in React Flow, no parent→child edges created. Children exist in store for data access but render visually inside the parent. - **Click child chip**: selects the child and opens its side panel. - **Toolbar**: shows "4 workspaces + 1 sub" to distinguish root vs embedded counts. - **Fix**: children selector memoized with `useMemo` to prevent infinite render loop (Zustand `.filter()` creates new array reference every render). ## Files Tab Enhancements - **Upload**: folder picker to upload multiple files into workspace - **Export**: download all workspace files as JSON bundle (parallel via Promise.allSettled) - **Clear**: delete all files with red confirmation dialog - **Download**: ↓ button for currently open file - **Folder delete**: ✕ on hover for directories, confirmation shows "and all its contents", platform uses os.RemoveAll ## Memory Consolidation Loop (Phase 9, 15g) - `consolidation.py`: runs every 5min, checks if LOCAL memories exceed threshold (10), uses agent to summarize into dense TEAM knowledge, deletes originals. Falls back to concatenation if agent unavailable/rate-limited with error-level logging. - Configurable via `CONSOLIDATION_INTERVAL` and `CONSOLIDATION_THRESHOLD` env vars. ## Code Sandbox (Phase 12, 18a-18b, 18e) - `tools/sandbox.py`: `run_code(code, language)` tool - Subprocess backend: direct execution with timeout (default for Tier 1-2) - Docker backend: code written to temp file, mounted read-only at `/sandbox/code.py` (no shell injection). Container runs with `--network none --memory 256m --cpus 0.5 --read-only --tmpfs /tmp:size=32m`. - Supports python, javascript, shell/bash - `SandboxConfig` dataclass in config.py, loaded from `sandbox:` in config.yaml - Configurable via `SANDBOX_BACKEND`, `SANDBOX_TIMEOUT`, `SANDBOX_MEMORY_LIMIT` env vars ## Connection Breakage Visualization (Phase 11, 17p) - Edges in canvas styled by child workspace status: - Online: animated, dark zinc stroke - Degraded: amber stroke, thicker (2px), no animation - Offline/Failed: dashed gray stroke, no animation ## Interactive Canvas Improvements - **Search dialog (⌘K)**: `SearchDialog.tsx` — fuzzy search across name/role/status, click to select + open Details, state managed in Zustand store - **Toast notifications**: `Toaster.tsx` — global `showToast()` callable anywhere, success/error/info variants, auto-dismiss 4s, slide-up animation. Context menu actions show toasts. - **Empty state**: `EmptyState.tsx` — shown when no workspaces, visual guide with keyboard shortcuts - **Keyboard shortcuts**: Escape cascades (close context menu → close panel), ⌘K opens search - **Toolbar search button**: magnifying glass + ⌘K badge, calls `store.setSearchOpen(true)` ## Human-in-the-Loop Approval Chain (Phase 8, 14a-14f) - **Migration 007_approvals.sql**: `approval_requests` table with status (pending/approved/denied/escalated) - **handlers/approvals.go**: POST create, GET list, GET /approvals/pending (single query for all), POST decide. Auto-expires pending requests older than 10 minutes. - **Platform events**: APPROVAL_REQUESTED, APPROVAL_ESCALATED (to parent), APPROVAL_APPROVED, APPROVAL_DENIED - **tools/approval.py**: `request_approval(action, reason)` tool. Pauses agent, polls for decision (configurable via APPROVAL_POLL_INTERVAL/APPROVAL_TIMEOUT env vars). - **ApprovalBanner.tsx**: polls single `/approvals/pending` endpoint, shows approve/deny cards with workspace name, slide-in-from-top animation, toast feedback. - Agent decides when to call `request_approval` based on system prompt guidelines. ## Hierarchical Memory Architecture (Phase 9, 15a-15f) - **Migration 008_agent_memories.sql**: `agent_memories` table with pgvector extension, scope (LOCAL/TEAM/GLOBAL) - **handlers/memories.go**: POST commit, GET search (with text ILIKE), DELETE - LOCAL: workspace_id only - TEAM: parent + siblings via parent_id join, CanCommunicate check, excludes removed workspaces - GLOBAL: readable by all, write restricted to root (no parent_id) - **tools/memory.py**: `commit_memory(content, scope)` and `search_memory(query, scope)` tools - Every agent now has 4 built-in tools: delegate, approve, commit_memory, search_memory ## OpenRouter Provider + E2E Testing - **agent.py**: added OpenRouter as 5th LLM provider (uses langchain-openai with custom base URL) - **requirements.txt**: added `langchain-openai>=0.3.0` - **Supported providers**: anthropic, openai, openrouter, google_genai, ollama - **E2E test results**: 40/41 pass (SEO + Echo agents via OpenRouter claude-3.5-haiku). Tests: workspace CRUD, agent management, registry/discovery, heartbeat/status, A2A chat, secrets, HMA memory, approvals, config, templates, viewport, events, files, cascade delete. ## Langfuse Trace Preview (Phase 10, 16c) - **handlers/traces.go**: `GET /workspaces/:id/traces` proxies to Langfuse API - **TracesTab.tsx**: expandable trace list with input/output, latency, tokens, cost - **SidePanel**: 9 tabs now (added Traces) ## Team Zoom-in + Skill Installer + ClawHub - **Canvas zoom-in (13g)**: double-click team node → fitBounds animation - **SkillInstaller (17n)**: type skill name → creates SKILL.md in workspace files - **ClawHub (17q)**: "Install from ClawHub" button sends install command via A2A ## Coordinator Pattern (Phase 7, 13c) - `workspace/coordinator.py`: auto-detects children on startup, injects team description into prompt, adds `route_task_to_team` tool - When workspace has children → becomes coordinator that routes A2A messages to best-suited child based on capabilities - Coordination rules injected: analyze task, choose member, delegate, aggregate, fallback ## Production Hardening (Phase 14) ### Graceful Shutdown (20e) - `main.go`: signal handler for SIGINT/SIGTERM, context cancellation stops liveness monitor + Redis subscriber, HTTP server drains connections (30s timeout), WebSocket hub Close() disconnects all clients - `ws/hub.go`: added Close() method ### Rate Limiting (20d) - `middleware/ratelimit.go`: token bucket rate limiter (100 req/min/IP), accepts context for clean shutdown, auto-cleanup of stale buckets every 5min - Applied globally via router middleware ### Secrets Encryption (20c) - `crypto/aes.go`: AES-256-GCM encrypt/decrypt, enabled via `SECRETS_ENCRYPTION_KEY` env var (32 bytes raw or base64), graceful degradation to plaintext if not set, warnings on invalid key - `secrets.go`: encrypt on write, decrypt on read - `workspace.go`: decrypt secrets before injecting into provisioned containers ### Delete Team Cascade (13h) - `workspace.go` Delete handler: without `?confirm=true` returns children list for confirmation, with confirm cascades delete to all sub-workspaces (stops containers, removes from DB, broadcasts events) - `DetailsTab.tsx`: passes `?confirm=true` after user confirms