diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea10bbe --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Dependencies +node_modules/ + +# Environment & secrets +.env +.env.* +.env.local +.env.production +*.key +*.pem +*.p12 +*.pfx + +# Claude Code +CLAUDE.md +.claude/ + +# CAR Brain local data +.car-brain/ + +# IDE & editors +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store +Thumbs.db + +# Build output +dist/ +build/ +out/ + +# OS files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes + +# Logs +*.log +npm-debug.log* + +# Temporary files +tmp/ +.tmp/ +*.bak +*.orig +.gstack/ diff --git a/.superpowers/brainstorm/55156-1775215990/content/car-brain-mockup.html b/.superpowers/brainstorm/55156-1775215990/content/car-brain-mockup.html new file mode 100644 index 0000000..c108df3 --- /dev/null +++ b/.superpowers/brainstorm/55156-1775215990/content/car-brain-mockup.html @@ -0,0 +1,841 @@ + + + + + +CAR Brain Simulator — Layout Mockup + + + +
+ + +
+ + +
+ + +
+ +
+ + +
+ +
+ + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + 100% + +
+
+ + +
+
+ + Select +
+
+ + Pan / Orbit +
+
+
+ + Add Chunk +
+
+ + Add Link +
+
+ ? + Add Question +
+
+ + Add Trigger +
+
+ + +
+
+ +
+ Step 5 Multi-Query Retrieval — sending 4 sub-queries into the memory graph +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SCHEMA + + + PATTERN + + + + + + + + + + T2 SUMMARY + + + T2 COMPRESSED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QUERY + + + + + + + + + + + + + + + + + + Spain + margins + + + + + Logistics + change + + + + Korea + similar + + + + Partner + renegotiation + + + + + + + + + + + + + TRG + + + 0.92 + 0.78 + 0.41 + +
+ + +
+
+
Overview
+
Meta
+
Retrieval
+
Score
+
+ +
+
+

Selected Chunk

+
+ Tier 1 + chunk-456 +
+
+ On March 15, Valentina said Spain margins dropped to 18% after the logistics partner change in February. +
+
+ +
+

Relevance Score

+
+ 0.92 + HIGH +
+
+
+
+
+ +
+

Metadata

+
+ source + user_stated +
+
+ reliability + high +
+
+ emotional_tone + concern +
+
+ intensity + 0.85 +
+
+ stakes + high +
+
+ status + open +
+
+ access_count + 3 +
+
+ decision_made + true +
+
+ +
+

Topic Tags

+
+ spain + margins + EU-ops +
+
+ +
+

Entity Tags

+
+ Spain account + logistics partner +
+
+ +
+

Retrieval Cues

+
+ spain margins + EU cost pressure + logistics change impact +
+
+ +
+

Linked Chunks

+
+
chunk-789 — Korea similar pattern
+
chunk-234 — Partner renegotiation
+
+
+
+
+ + +
+
+
Retrieval Log
+
Consolidation
+
Contradictions 1
+
Questions 3
+
Metamemory
+
+ +
+
+ 14:32:01 + S1 + Session primer loaded — 3 open threads, 1 trigger matched +
+
+ 14:32:01 + S2 + Context constructed — user in investigation mode, drilling into Spain account +
+
+ 14:32:02 + S3 + Query decomposed into 4 sub-questions: existence, recency, baseline, causation +
+
+ 14:32:02 + S4 + Metamemory: Spain account coverage DEEP, Q2 projections NOT DISCUSSED +
+
+ 14:32:03 + S5 + Multi-query retrieval R1 — 4 queries sent, 11 chunks activated + conf: 0.78 +
+
+ + S6 + Cluster formation — waiting... +
+
+
+ +
+ + \ No newline at end of file diff --git a/.superpowers/brainstorm/55156-1775215990/content/visual-direction.html b/.superpowers/brainstorm/55156-1775215990/content/visual-direction.html new file mode 100644 index 0000000..45a8344 --- /dev/null +++ b/.superpowers/brainstorm/55156-1775215990/content/visual-direction.html @@ -0,0 +1,131 @@ +

Visual Direction for the Brain Simulator

+

Which aesthetic feels right for a CAR Protocol memory system?

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + T1 + + T1 + + CLUSTER + relevance: 0.87 + + T3 + + Q + +
+
+
+

A. Neural Dark

+

Deep dark background with glowing neon connections. Nodes pulse when activated. Edges glow cyan/purple during retrieval. Brain-scan aesthetic — feels like watching synapses fire.

+

Tier 1: cyan | Tier 2: purple | Tier 3: pink | Questions: gold | Clusters: white glow

+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + raw + + + raw + + + cluster + 0.87 + + schema + + Q? + +
+
+
+

B. Organic Brain

+

Deep blue-to-purple gradients with soft radiating glows around nodes. Connections feel organic, like dendrites. Warm reds for hot memories, cool purples for cold. Feels biological.

+

Hot memory: warm red | Cold memory: deep purple | Schemas: muted blue | Questions: violet

+
+
+ +
+
+
+ + + + + + + + + Tier 1 + episode + + Tier 1 + episode + + Cluster + score: 0.87 + + Tier 3 + schema + + Q? + L3 + +
+
+
+

C. Clean Clinical

+

Light background, crisp borders, clear typography. Like a medical/scientific visualization tool. Professional, data-dense, highly readable. Focus on information density over atmosphere.

+

Blue: episodes | Purple: clusters | Pink: schemas | Amber: questions

+
+
+
\ No newline at end of file diff --git a/.superpowers/brainstorm/55156-1775215990/content/waiting.html b/.superpowers/brainstorm/55156-1775215990/content/waiting.html new file mode 100644 index 0000000..f92c257 --- /dev/null +++ b/.superpowers/brainstorm/55156-1775215990/content/waiting.html @@ -0,0 +1,3 @@ +
+

Continuing in terminal...

+
\ No newline at end of file diff --git a/.superpowers/brainstorm/55156-1775215990/state/server-info b/.superpowers/brainstorm/55156-1775215990/state/server-info new file mode 100644 index 0000000..9ba2544 --- /dev/null +++ b/.superpowers/brainstorm/55156-1775215990/state/server-info @@ -0,0 +1 @@ +{"type":"server-started","port":64272,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:64272","screen_dir":"/Users/amitkolton/Projects/cargraph/carmindgraph/.superpowers/brainstorm/55156-1775215990/content","state_dir":"/Users/amitkolton/Projects/cargraph/carmindgraph/.superpowers/brainstorm/55156-1775215990/state"} diff --git a/.superpowers/brainstorm/55156-1775215990/state/server.pid b/.superpowers/brainstorm/55156-1775215990/state/server.pid new file mode 100644 index 0000000..08942f0 --- /dev/null +++ b/.superpowers/brainstorm/55156-1775215990/state/server.pid @@ -0,0 +1 @@ +55165 diff --git a/CAR_INGESTION_PROTOCOL.md b/CAR_INGESTION_PROTOCOL.md new file mode 100644 index 0000000..97deaf0 --- /dev/null +++ b/CAR_INGESTION_PROTOCOL.md @@ -0,0 +1,177 @@ +# CAR Ingestion Protocol + +Instructions for ingesting knowledge into a CAR Brain graph. Use this protocol +with Claude Code or any AI runner to convert raw information into structured +brain data. + +## Pipeline + +``` +1. SCAN — Read all source material (any domain) +2. CHUNK — Break into atomic units (one fact/decision/event per chunk) +3. CLASSIFY — Assign tier (1=raw, 2=summary, 3=pattern), tags, entities +4. QUESTION — Generate questions at 5 levels per chunk (L1-L5) +5. LINK — Identify cross-links, contradictions, temporal sequences +6. SCORE — Set initial relevance weights (emotional, stakes, consequence) +7. CLUSTER — Group related chunks by topic/entity/time +8. EXPORT — Output as JSON matching the graph document schema +``` + +## Step Details + +### 1. SCAN + +Read the input material. Accept any format: +- Text (conversations, notes, documents) +- Code files (source, config, docs) +- Structured data (JSON, YAML, CSV) + +### 2. CHUNK + +Break into atomic memory chunks. Each chunk contains exactly ONE: +- Fact or data point +- Decision or action item +- Event or observation +- Relationship or connection + +Rule: if a sentence contains two independent facts, split it into two chunks. + +### 3. CLASSIFY + +For each chunk, assign: + +```json +{ + "tier": 1, + "source": "user_stated | agent_generated | imported", + "source_reliability": "high | medium | low", + "emotional_tone": "neutral | positive | negative | concerned | ...", + "emotional_intensity": 0.0-1.0, + "stakes_level": "high | medium | low", + "topic_tags": ["tag1", "tag2"], + "entity_tags": ["Entity1", "Entity2"], + "decision_made": true/false, + "status": "open | resolved" +} +``` + +Tier assignment: +- **Tier 1**: Raw episodic memory (individual facts, events, observations) +- **Tier 2**: Consolidated summary (synthesis of 3+ related T1 chunks) +- **Tier 3**: Schema/pattern (cross-domain insight, permanent knowledge) + +### 4. QUESTION + +Generate questions at 5 levels for each chunk: + +| Level | Type | Example | +|-------|------|---------| +| L1 | Bare Fact | "What is the key fact stated here?" | +| L2 | Explanation | "Why did this happen?" | +| L3 | Implication | "What are the consequences?" | +| L4 | Counterfactual | "What if this hadn't happened?" | +| L5 | Conditional | "Under what conditions would this change?" | + +### 5. LINK + +Identify relationships between chunks: + +| Edge Type | When | +|-----------|------| +| `linked_to` | Chunks share topic/entity/context | +| `amends` | One chunk corrects another | +| `contradicts` | Chunks contain conflicting information | +| `preceded_by` | Temporal sequence | +| `promotes_to` | T1 chunk is part of a T2 summary | +| `answers` | A chunk answers a question | + +### 6. SCORE + +Set initial relevance weights: +- `access_count`: 1 (new chunk) +- `emotional_intensity`: based on content analysis +- `stakes_level`: "high" if involves decisions, money, deadlines +- `decision_made`: true if the chunk records a decision + +### 7. CLUSTER + +Group chunks that share 2+ tags or entities. Each cluster gets: +- `member_ids`: list of chunk IDs +- `shared_tags`: tags all members share +- `shared_entities`: entities all members share +- `cluster_strength`: member count / total chunks + +### 8. EXPORT + +Output format (JSON): + +```json +{ + "id": "brain_", + "title": "CAR Brain - ", + "version": "1.0.0", + "nodes": [ + { + "id": "chunk_", + "type": "chunk", + "label": "", + "description": "", + "position": { "x": 0, "y": 0, "z": -200 }, + "data": { ... full chunk metadata ... } + } + ], + "edges": [ + { + "id": "edge_", + "type": "linked_to", + "source": "chunk_a", + "target": "chunk_b", + "label": "linked_to" + } + ], + "clusters": [ + { + "id": "cluster_", + "label": "Cluster: ", + "member_ids": ["chunk_a", "chunk_b"], + "shared_tags": ["topic"], + "shared_entities": ["Entity"], + "cluster_strength": 0.5 + } + ], + "viewport": { "x": 0, "y": 0, "z": 0, "zoom": 1 }, + "metadata": { + "createdBy": "car-ingestion", + "description": "" + } +} +``` + +## Position Guidelines + +Place nodes in 3D space by tier: +- **Tier 1 chunks**: z = -200 (mid plane) +- **Tier 2 summaries**: z = -400 (back plane) +- **Tier 3 patterns**: z = -600 (deep plane) +- **Questions**: z = -100 (front-mid) +- **Triggers**: z = -200 (mid plane) + +Spread x/y positions to avoid overlap. Related chunks should be near each other. + +## Usage + +### With Claude Code CLI + +```bash +claude -p "Read the file at ./notes.md and process it through the CAR +Ingestion Protocol. Output the result as a JSON file matching the CAR +Brain graph document schema." > brain.json +``` + +### Manual Import + +1. Generate the JSON file using any AI tool +2. Open CAR Brain (http://localhost:4173) +3. Click Import in the toolbar +4. Select the JSON file +5. Watch the brain materialize diff --git a/README.md b/README.md index 22767e2..48c74cc 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,146 @@ -# MindGraph AI +# CAR Brain Simulator -MindGraph AI is a lightweight, framework-free visual graph workbench for modeling AI workflows in the browser. +A 3D neural memory workbench implementing the [Clustered Associative Recall (CAR) Protocol](docs/superpowers/specs/2026-04-03-car-brain-simulator-design.md) for multi-agent memory systems. Built as a reconstructed fork of [MindGraph AI](https://github.com/chrisrobison/mindgraph). -It keeps a browser-native stack: -- custom elements (Web Components) -- plain ES modules -- PAN event bus for app-wide coordination -- centralized graph state in `graph-store` +Watch your memories form as glowing neurons in 3D space. Add knowledge, ask questions, and observe the 13-step retrieval sequence animate across a galaxy of interconnected memory chunks. ## Run Locally -Use any static HTTP server (do not open `index.html` with `file://`). - ```bash -cd /Users/cdr/Projects/mindgraph -python3 -m http.server 4173 +node server.js +# Open http://127.0.0.1:4173 +``` + +No build step. No npm install. Just Node.js and a browser with WebGL. + +## What It Does + +**Add memories** — type facts, decisions, events into the Memory input. Each becomes a Tier 1 chunk (glowing cyan sphere) with auto-generated questions and cross-links. + +**Ask questions** — type a query and watch the brain think. The 13-step retrieval sequence fires: session priming, context construction, question decomposition, multi-query retrieval, cluster formation, interference checking, confidence grading, and synthesis. All visualized in real-time on the 3D canvas. + +**Ingest projects** — point it at a directory and Claude processes every file through the CAR protocol, building a knowledge graph of your entire project. + +## Architecture + +Two-layer local application: + ``` +Browser (localhost:4173) Local Server (server.js) ++------------------------+ +---------------------------+ +| Three.js 3D Canvas | | Static file serving | +| Web Component panels |<--REST--->| /api/brain (persistence) | +| PAN event bus | | /api/process (Claude CLI) | +| graph-store (state) | | /api/config (settings) | ++------------------------+ +---------------------------+ + | + Claude Code CLI (default) + or Anthropic API fallback +``` + +**Frontend:** Vanilla JS, Web Components, Three.js via CDN. Zero build step. +**Backend:** Single `server.js`, zero npm dependencies. Node.js `http` module only. +**AI Runner:** Claude Code CLI (uses your Claude subscription). Falls back to template-based processing when Claude isn't available. + +## The 3D Brain + +Nodes are 3D geometries rendered with Three.js: + +| Node Type | Shape | Color | What It Represents | +|-----------|-------|-------|-------------------| +| Chunk (Tier 1) | Sphere | Cyan `#00f5ff` | Raw episodes, recent memories | +| Chunk (Tier 2) | Icosahedron | Purple `#7b61ff` | Compressed summaries | +| Chunk (Tier 3) | Octahedron | Pink `#ff2d78` | Permanent schemas/patterns | +| Question | Tetrahedron | Gold `#ffd93d` | Retrieval keys (L1-L5) | +| Trigger | Diamond | Amber `#ff9f1c` | Prospective memory triggers | +| Cluster | Translucent shell | White 20% | Groups of related chunks | + +**Atmosphere:** 400 ambient particles, 800 background stars, 6 nebula gradient clouds, ambient light breathing. Post-processing bloom gives every node a radiating glow. + +**Edges** connect memories with semantic relationships: `linked_to`, `amends`, `contradicts` (red pulse), `promotes_to`, `answers` (gold), `decomposes_to`, `clusters_with`, `preceded_by`, `triggers`. + +## The 13-Step Retrieval Sequence + +When you ask a question, the CAR engine runs the full retrieval pipeline: + +1. **Session Primer** — check recent context, scan triggers +2. **Context Construction** — build a picture of why the question exists +3. **Question Decomposition** — split into 3-5 sub-questions +4. **Metamemory Check** — what do I know? what are my gaps? +5. **Multi-Query Retrieval R1** — score all chunks, top-3 per sub-query +6. **Cluster Formation** — group retrieved chunks +7. **Cluster Expansion R2** — pull in neighbors +8. **Tiered Retrieval** — search across all three memory tiers +9. **Interference Check** — detect contradictions +10. **Confidence Grading** — grade each sub-question +11. **Thinking Profile** — detect session mode +12. **Synthesis + Response** — reconstruct answer from fragments +13. **Post-Retrieval Update** — re-index, strengthen retrieval paths + +Two modes: **Play** (auto-runs ~8.5 seconds) and **Step** (advance manually). + +## CAR Protocol + +Based on the Clustered Associative Recall Protocol (v1.0, March 2026) — a cognitive science-informed implementation guide for human-like memory recall in multi-agent systems. + +Core principles: +- Memory is reconstruction from fragments, not file retrieval +- Never suppress or delete memory — build better retrieval cues instead +- Never present confident answers without grading confidence +- Questions are retrieval-ready keys; statements are passive data +- Every retrieval modifies the memory (read-write, not read-only) +- Forgetting is functional — relevance decay keeps the system fast + +Relevance scoring uses Ebbinghaus decay curves, access frequency, emotional weight, consequence weight, connection density, and the Zeigarnik effect (open threads get a 1.5x retrieval bonus). + +## Project Ingestion + +The `CAR_INGESTION_PROTOCOL.md` describes how to feed any project into the brain: + +1. SCAN — read all source material +2. CHUNK — break into atomic memory units +3. CLASSIFY — assign tier, tags, entities +4. QUESTION — generate questions at 5 levels per chunk +5. LINK — identify cross-links, contradictions, temporal sequences +6. SCORE — set initial relevance weights +7. CLUSTER — group related chunks +8. EXPORT — output as JSON for import + +## Persistence + +Brain data persists to `~/.car-brain/brains/` on disk (via the server API) with localStorage as a fallback for instant reload. Export/Import JSON from the toolbar for manual backup. + +## Tech Stack + +- **3D rendering:** Three.js v0.170.0 (CDN, ES modules) +- **UI framework:** None. Vanilla Web Components. +- **State management:** Custom PAN event bus (publish/subscribe on EventTarget) +- **Persistence:** Node.js server → disk JSON + browser localStorage +- **AI integration:** Claude Code CLI (subscription auth) +- **Build step:** None. Serve static files. +- **Dependencies:** Zero npm packages. Three.js from CDN only. + +## Keyboard Shortcuts + +| Key | Action | +|-----|--------| +| `V` | Select tool | +| `H` | Pan/Orbit tool | +| `C` | Add Chunk tool | +| `L` | Add Link tool | +| `Q` | Add Question tool | +| `T` | Add Trigger tool | +| `M` | Focus memory input | +| `Space` | Play/pause retrieval | +| `Right Arrow` | Step forward in retrieval | +| `Esc` | Cancel / clear selection | +| `Cmd+Z` | Undo | +| `1/2/3/0` | Filter by tier / show all | + +## Origins + +Reconstructed from [chrisrobison/mindgraph](https://github.com/chrisrobison/mindgraph), a framework-free graph workbench. The original 2D agent-workflow editor was rebuilt into a 3D brain simulator with the CAR protocol as its cognitive engine. + +## License -Open: -- `http://127.0.0.1:4173` - -## Core Architecture - -### State Ownership Rules - -MindGraph now follows strict ownership rules: -- `graph-store` is the single source of truth for graph document mutations. -- PAN carries intent events (`*.requested`) and state/result events (`*.changed`, `*.created`, `*.updated`, etc). -- UI components render from graph-store snapshots and store-driven events. -- UI components keep only ephemeral interaction state (drag, pan, marquee, connect-mode context). - -### Responsibilities - -- `graph-store` - - owns nodes, edges, viewport, selection, undo/redo history - - consumes graph intent events and applies canonical mutations - - publishes state-change events + `graph.document.changed` -- `ui-store` - - owns UI-only state (tool/tab/dev-console/runtime panels) - - emits graph intent events for selection/zoom actions -- `persistence-store` - - listens to `graph.document.changed` and save/load events - - handles autosave/restore to `localStorage` -- runtime modules (`mock-agent-runtime`, `data-connectors`) - - read graph data from `graph-store` - - request graph updates via `graph.node.update.requested` - - publish runtime lifecycle and activity events -- components - - publish intent events - - subscribe to state/result events - - render based on store state - -## Event Model - -### Intent / Request Events - -Examples: -- `graph.node.select.requested` -- `graph.node.update.requested` -- `graph.node.move.requested` -- `graph.node.create.requested` -- `graph.node.delete.requested` -- `graph.edge.create.requested` -- `graph.selection.clear.requested` -- `graph.selection.set.requested` -- `graph.viewport.update.requested` -- `graph.document.load.requested` -- `graph.document.save.requested` -- `graph.document.undo.requested` -- `graph.document.redo.requested` -- `runtime.agent.run.requested` -- `runtime.subtree.run.requested` -- `runtime.all.run.requested` - -### State / Result Events - -Examples: -- `graph.node.selected` -- `graph.selection.set` -- `graph.selection.cleared` -- `graph.node.updated` -- `graph.node.created` -- `graph.node.deleted` -- `graph.edge.created` -- `graph.viewport.changed` -- `graph.document.loaded` -- `graph.document.saved` -- `graph.document.changed` -- `runtime.agent.run.started` -- `runtime.agent.run.completed` -- `runtime.agent.run.failed` - -## Graph Mutation Flow - -Canonical mutation flow: -1. Component/runtime publishes `*.requested` intent. -2. `graph-store` validates and applies mutation. -3. `graph-store` emits specific result event (`graph.node.updated`, `graph.edge.created`, etc). -4. `graph-store` emits `graph.document.changed` with reason metadata. -5. UI re-renders from store snapshots; persistence/autosave reacts to changed document events. - -## Canvas Architecture - -`graph-canvas` has been decomposed into focused modules: -- `js/components/graph-canvas/canvas-renderer.js` -- `js/components/graph-canvas/canvas-edges.js` -- `js/components/graph-canvas/canvas-viewport.js` -- `js/components/graph-canvas/canvas-selection.js` - -The custom element now owns only ephemeral interaction state: -- active drag preview -- pan gesture -- marquee gesture -- connect-mode source -- edge-popover state - -It no longer keeps a shadow graph document as a competing source of truth. - -## Core User Loop Improvements - -Implemented tightening of the core loop: -- create/select/edit nodes through request/state event flow -- connect edges with in-app popover (no `window.prompt`) -- shift-click additive/toggle selection -- marquee selection on empty-space drag in select tool -- `Esc` behavior: - - close edge popover - - clear connect source - - reset tool to Select, or clear selection -- delete supports multi-selection - -## Component Overview - -Top-level shell: -- `app-shell` composes toolbar, palette, canvas, inspector, and bottom activity panel - -Core UI modules: -- `top-toolbar` runtime actions, save/load, undo/redo, autosave toggle, zoom -- `left-tool-palette` tool selection and keyboard shortcut guide -- `graph-canvas` pan/zoom/drag/select/create/connect interactions -- `inspector-panel` tabbed node editing views -- `bottom-activity-panel` logs, task queue, run history, errors, PAN console - -State + services: -- `graph-store` graph document state + undo/redo + canonical mutations -- `ui-store` UI tab/tool/runtime panel state -- `persistence-store` localStorage autosave/restore -- `mock-agent-runtime` mock agent execution and activity publishing - -## Graph Document Model - -Graph documents are JSON objects with: -- `id`, `title`, `version` -- `nodes[]` -- `edges[]` -- `viewport` (`x`, `y`, `zoom`) -- `metadata` (includes persisted selection) - -Node shape: -- `id`, `type`, `label`, `description`, `position`, `data`, `metadata` - -Edge shape: -- `id`, `type`, `source`, `target`, `label`, `metadata` - -Validation and normalization are handled in: -- `js/core/graph-document.js` - -## Persistence Behavior - -- Manual export/import via toolbar (`Save JSON`, `Load JSON`) -- Optional localStorage autosave toggle in toolbar -- Last session restore on startup when autosave data exists -- Autosave watches `graph.document.changed` -- Viewport and selection are persisted in the graph document - -## Interaction Shortcuts - -- `Delete` / `Backspace`: delete selected node(s) -- `Cmd/Ctrl + D`: duplicate selected node -- `Cmd/Ctrl + Z`: undo -- `Shift + Cmd/Ctrl + Z` or `Ctrl + Y`: redo -- `Esc`: close connect popover / clear connect source / reset tool / clear selection -- `Shift + Click`: additive/toggle node selection - -## Current Limitations - -- Marquee uses top-left node position hit testing (not full node-bounds overlap yet) -- Undo/redo remains snapshot-based -- No collaborative sync or server persistence -- Mock runtime is intentionally local and simulated - -## Acceptance Checklist - -Current build supports: -1. Seeded graph loads on startup (or autosaved last session) -2. Pan/zoom/select works -3. Node creation, editing, deletion, and duplication work -4. In-app edge creation flow (type + label) works -5. Save/load JSON graph documents -6. Data node refresh behavior and inspector previews -7. Agent runs through mock runtime -8. Live activity panel updates -9. Visible PAN event console with filtering/clear -10. Lightweight framework-free browser app with disciplined store ownership +See upstream repository for license terms. diff --git a/css/app.css b/css/app.css index 7380897..e74eaa8 100644 --- a/css/app.css +++ b/css/app.css @@ -1,27 +1,60 @@ -@import url("https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap"); :root { - --bg: #edf2f8; - --bg-2: #dce7f6; - --surface: #ffffff; - --surface-muted: #f4f8fe; - --line: #d7e1ef; - --line-strong: #b5c4d8; - --text: #172235; - --text-soft: #5f728c; - --accent: #245e9b; - --accent-soft: #e4eefb; - --danger: #a0303f; - --success: #1f7040; - --shadow-soft: 0 10px 28px -20px rgba(11, 28, 53, 0.45); - --shadow-panel: 0 18px 40px -26px rgba(17, 38, 70, 0.36); - --toolbar-height: 72px; - --bottom-height: 228px; - --left-width: 238px; - --right-width: 370px; - --radius: 14px; - --font: "Manrope", "Segoe UI", "SF Pro Text", Arial, sans-serif; - --font-mono: "IBM Plex Mono", "SF Mono", Menlo, monospace; + /* Background */ + --bg-canvas: #080c14; + --bg-panel: #0d1117; + --bg-input: #161b22; + --bg-border: #21262d; + --bg-hover: #161b22; + + /* Data colors (tier-mapped, never decorative) */ + --color-t1: #00f5ff; + --color-t2: #7b61ff; + --color-t3: #ff2d78; + --color-question: #ffd93d; + --color-trigger: #ff9f1c; + --color-contradiction: #ff3333; + --color-cluster: rgba(255, 255, 255, 0.2); + + /* Text */ + --text-primary: #e6edf3; + --text-secondary: #c9d1d9; + --text-muted: #484f58; + --text-accent: #ffffff; + + /* Typography */ + --font-sans: "IBM Plex Sans", sans-serif; + --font-mono: "IBM Plex Mono", monospace; + --font-size-xs: 10px; + --font-size-sm: 11px; + --font-size-base: 12px; + --font-size-md: 13px; + --font-size-lg: 14px; + + /* Spacing (4px base) */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-6: 24px; + --space-8: 32px; + + /* Radii */ + --radius-sm: 3px; + --radius-md: 4px; + --radius-lg: 6px; + + /* Transitions */ + --transition-fast: 0.15s ease; + --transition-normal: 0.3s ease; + --transition-slow: 0.6s ease-in-out; + + /* Layout */ + --toolbar-height: 44px; + --palette-width: 56px; + --inspector-width: 300px; + --bottom-panel-height: 180px; } * { @@ -34,17 +67,14 @@ body { padding: 0; width: 100%; height: 100%; - color: var(--text); - font-family: var(--font); - background: - radial-gradient(circle at 7% 3%, rgba(255, 255, 255, 0.88) 0%, rgba(255, 255, 255, 0) 42%), - radial-gradient(circle at 92% 84%, rgba(196, 218, 244, 0.55) 0%, rgba(196, 218, 244, 0) 38%), - linear-gradient(160deg, var(--bg) 0%, var(--bg-2) 100%); + color: var(--text-secondary); + font-family: var(--font-sans); + background: var(--bg-canvas); } body { min-height: 100vh; - font-size: 14px; + font-size: var(--font-size-base); letter-spacing: 0.01em; } @@ -58,10 +88,12 @@ input, select, textarea { font: inherit; + color: var(--text-secondary); } button { - transition: background-color 140ms ease, border-color 140ms ease, box-shadow 140ms ease, color 140ms ease; + transition: background-color var(--transition-fast), border-color var(--transition-fast), + box-shadow var(--transition-fast), color var(--transition-fast); } button:focus-visible, @@ -69,7 +101,7 @@ input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 0; - box-shadow: 0 0 0 3px rgba(36, 94, 155, 0.22); + box-shadow: 0 0 0 2px rgba(0, 245, 255, 0.25); } kbd { @@ -78,15 +110,37 @@ kbd { justify-content: center; min-width: 1.4rem; padding: 0.06rem 0.3rem; - border-radius: 6px; - border: 1px solid var(--line-strong); - background: #fbfdff; - color: #324b6c; + border-radius: var(--radius-md); + border: 1px solid var(--bg-border); + background: var(--bg-input); + color: var(--text-muted); font-family: var(--font-mono); - font-size: 0.68rem; + font-size: var(--font-size-xs); line-height: 1.4; } +::selection { + background: rgba(0, 245, 255, 0.2); +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--bg-border); + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + @media (min-width: 961px) { html, body { diff --git a/css/layout.css b/css/layout.css index 1936a32..4de1264 100644 --- a/css/layout.css +++ b/css/layout.css @@ -3,14 +3,15 @@ height: 100vh; display: grid; gap: 0; - padding: 0.4rem 0.45rem 0.5rem; - grid-template-rows: auto 1fr var(--bottom-height); - grid-template-columns: minmax(200px, var(--left-width)) minmax(0, 1fr) minmax(300px, var(--right-width)); + padding: 0; + grid-template-rows: var(--toolbar-height) 1fr var(--bottom-panel-height); + grid-template-columns: var(--palette-width) minmax(0, 1fr) var(--inspector-width); grid-template-areas: "toolbar toolbar toolbar" "palette canvas inspector" "activity activity activity"; overflow: hidden; + background: var(--bg-canvas); } .mg-toolbar { @@ -46,20 +47,7 @@ @media (max-width: 1200px) { :root { - --left-width: 210px; - --right-width: 294px; - } -} - -@media (max-width: 1380px) { - .mg-shell { - grid-template-columns: minmax(188px, var(--left-width)) minmax(0, 1fr); - grid-template-rows: auto minmax(370px, 1fr) auto var(--bottom-height); - grid-template-areas: - "toolbar toolbar" - "palette canvas" - "inspector inspector" - "activity activity"; + --inspector-width: 260px; } } @@ -68,7 +56,6 @@ height: auto; min-height: 100vh; overflow: visible; - padding: 0.35rem; grid-template-columns: 1fr; grid-template-rows: auto auto minmax(300px, 1fr) auto auto; grid-template-areas: diff --git a/css/nodes.css b/css/nodes.css index e046954..cde6875 100644 --- a/css/nodes.css +++ b/css/nodes.css @@ -1,229 +1,61 @@ -.mg-node { - position: absolute; - display: block; - border: 1px solid #d0dff1; - border-radius: 14px; - background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%); - box-shadow: 0 16px 34px -24px rgba(12, 30, 64, 0.52); - padding: 0.84rem 0.94rem; +/* ── CAR Brain: 3D nodes are rendered in Three.js, not DOM ── */ +/* This file contains styles for the 2D overlay elements only */ + +/* ── Floating node labels (CSS2DRenderer) ── */ + +.car-node-label { + font-family: var(--font-mono, "IBM Plex Mono", monospace); + font-size: 10px; + color: rgba(255, 255, 255, 0.75); + text-shadow: 0 0 6px rgba(0, 245, 255, 0.4), 0 1px 3px rgba(0, 0, 0, 0.8); + white-space: nowrap; + pointer-events: none; user-select: none; - cursor: grab; - transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease; -} - -.mg-node-instance { - position: absolute; -} - -.mg-node:hover { - border-color: #a7bfdc; - box-shadow: 0 18px 34px -24px rgba(24, 50, 84, 0.56); -} - -.mg-node-instance.is-selected .mg-node { - border-color: #2d629c; - box-shadow: 0 0 0 2px rgba(45, 98, 156, 0.2), 0 20px 34px -26px rgba(20, 45, 86, 0.62); - transform: translateY(-1px); -} - -.mg-node-instance.is-connect-source .mg-node { - border-color: #a56518; - box-shadow: 0 0 0 2px rgba(165, 101, 24, 0.18), 0 20px 34px -26px rgba(95, 58, 13, 0.52); -} - -.mg-node-instance.is-connect-target .mg-node { - border-color: #2f6ba8; - box-shadow: 0 0 0 2px rgba(47, 107, 168, 0.2), 0 20px 34px -26px rgba(24, 56, 98, 0.56); -} - -.mg-node h4 { - margin: 0; - font-size: 0.95rem; - line-height: 1.24; -} - -.mg-node p { - margin: 0.44rem 0 0; - color: var(--text-soft); - font-size: 0.8rem; - line-height: 1.33; -} - -.node-title-row { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 0.6rem; - padding-right: 1rem; -} - -.node-meta-grid { - margin-top: 0.66rem; - display: grid; - grid-template-columns: auto 1fr; - gap: 0.3rem 0.52rem; - font-size: 0.74rem; -} - -.node-meta-grid span { - color: #6e82a0; -} - -.node-meta-grid strong { - color: #25334b; - font-weight: 700; - justify-self: end; -} - -.node-meta-inline { - margin-top: 0.65rem; - font-size: 0.76rem; - color: #607393; -} - -.status-badge, -.readonly-badge { - padding: 0.12rem 0.48rem; - border-radius: 999px; - font-size: 0.66rem; - line-height: 1.5; - letter-spacing: 0.03em; - border: 1px solid transparent; - text-transform: uppercase; - font-weight: 700; -} - -.status-badge { - background: #eff4fc; - border-color: #d4e0f0; - color: #37557d; -} - -.status-active { - background: #e9f8ef; - border-color: #b9dfc8; - color: #1f7040; + transform: translateY(-8px); + letter-spacing: 0.3px; } -.status-ready { - background: #edf3fd; - border-color: #c6d7f2; - color: #2e5688; -} - -.status-idle { - background: #f3f5f9; - border-color: #d5dde9; - color: #50617b; -} - -.status-running { - background: #fff6e8; - border-color: #f0d3a2; - color: #8d4f00; -} - -.status-completed { - background: #e9f8ef; - border-color: #b9dfc8; - color: #1f7040; -} - -.status-failed { - background: #fdeeee; - border-color: #f1c4c6; - color: #9a2d33; -} - -.readonly-badge { - background: #eff8f5; - border-color: #c6e9dc; - color: #1d6a56; -} - -.compact-title { - margin-bottom: 0.32rem; - font-size: 0.66rem; - text-transform: uppercase; - letter-spacing: 0.08em; - color: #607a9f; - font-weight: 800; -} - -.mg-node.note { - width: 250px; - border-left: 4px solid #5f6f88; -} +/* Node tooltip is styled in panels.css (.node-tooltip) */ +/* All 3D node rendering is in three-nodes.js */ -.mg-node.agent { - width: 290px; - border-left: 4px solid #2e5b94; -} +/* ── Context menu (right-click on 3D node) ── */ -.mg-node.agent.agent-status-running { - border-left-color: #b26c10; +.node-context-menu { + position: fixed; + z-index: 100; + background: var(--bg-panel); + border: 1px solid var(--bg-border); + border-radius: var(--radius-md); + padding: var(--space-1) 0; + min-width: 160px; + font-family: var(--font-mono); + font-size: var(--font-size-sm); } -.mg-node.agent.agent-status-completed { - border-left-color: #1f7040; -} - -.mg-node.agent.agent-status-failed { - border-left-color: #a1363c; -} - -.node-runtime-summary { - margin-top: 0.62rem; - padding-top: 0.55rem; - border-top: 1px dashed #d5dfed; - font-size: 0.74rem; - color: #42556f; -} - -.mg-node.data { - width: 260px; - border-left: 4px solid #0f8a72; -} - -.mg-node.compact { - width: 210px; - min-height: 104px; - padding-top: 0.68rem; -} - -.mg-node.transformer { - border-left: 4px solid #9b5c13; -} - -.mg-node.view { - border-left: 4px solid #6f54a0; -} - -.mg-node.action { - border-left: 4px solid #b23f43; +.node-context-menu button { + display: block; + width: 100%; + background: none; + border: none; + padding: var(--space-1) var(--space-3); + text-align: left; + color: var(--text-secondary); + cursor: pointer; + font-family: var(--font-mono); + font-size: var(--font-size-sm); } -.node-connect-handle { - position: absolute; - top: 10px; - right: 10px; - width: 12px; - height: 12px; - border-radius: 999px; - border: 1px solid #7f98b8; - background: #345f8f; - box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.9); - cursor: crosshair; - padding: 0; - z-index: 2; +.node-context-menu button:hover { + background: var(--bg-hover); + color: var(--text-primary); } -.node-connect-handle:hover { - background: #1f4a7b; - border-color: #567da8; +.node-context-menu .menu-divider { + height: 1px; + background: var(--bg-border); + margin: var(--space-1) 0; } -.node-connect-handle:focus-visible { - outline: 2px solid #7fa6d3; - outline-offset: 1px; +.node-context-menu .menu-danger { + color: var(--color-contradiction); } diff --git a/css/panels.css b/css/panels.css index 2ba1812..6eefcfa 100644 --- a/css/panels.css +++ b/css/panels.css @@ -1,306 +1,378 @@ +/* ── Panel base ── */ + .mg-panel { - background: color-mix(in srgb, var(--surface) 96%, #f1f7ff 4%); - border: 1px solid var(--line); - border-radius: var(--radius); - margin: 0.4rem; + background: var(--bg-panel); + border: 1px solid var(--bg-border); + border-radius: 0; overflow: hidden; - box-shadow: var(--shadow-panel); } .mg-panel header { - background: linear-gradient(180deg, #f8fbff 0%, #eef4fd 100%); - border-bottom: 1px solid var(--line); - padding: 0.72rem 1rem; - font-weight: 700; - font-size: 0.82rem; + background: var(--bg-panel); + border-bottom: 1px solid var(--bg-border); + padding: var(--space-2) var(--space-3); + font-weight: 600; + font-size: var(--font-size-xs); text-transform: uppercase; - letter-spacing: 0.06em; - color: #3b5474; + letter-spacing: 0.08em; + color: var(--text-muted); + font-family: var(--font-mono); } .mg-panel .content { - padding: 0.85rem; - font-size: 0.88rem; + padding: var(--space-2) var(--space-3); + font-size: var(--font-size-base); min-height: 0; } -.toolbar-actions { - display: flex; - gap: 0.42rem; - align-items: center; - flex-wrap: wrap; -} +/* ── Top toolbar ── */ .top-toolbar-panel { - margin-bottom: 0.25rem; + border-bottom: 1px solid var(--bg-border); + border-left: 0; + border-right: 0; + border-top: 0; + display: flex; + align-items: center; + height: var(--toolbar-height); } .top-toolbar-content { - display: grid; - grid-template-columns: auto auto auto auto auto; - gap: 0.68rem; + display: flex; align-items: center; - padding: 0.68rem 0.8rem; + gap: var(--space-3); + padding: 0 var(--space-3); + width: 100%; + height: 100%; } .toolbar-brand { display: flex; align-items: center; - gap: 0.66rem; - min-width: 240px; + gap: var(--space-2); + min-width: 140px; } .brand-glyph { - width: 36px; - height: 36px; - border-radius: 11px; - background: linear-gradient(145deg, #1f4e86 0%, #3f79bd 100%); - box-shadow: 0 8px 24px -16px rgba(35, 82, 140, 0.7); - color: #fff; + width: 28px; + height: 28px; + border-radius: var(--radius-md); + background: var(--bg-input); + border: 1px solid var(--bg-border); + color: var(--color-t1); display: grid; place-items: center; - font-weight: 800; - letter-spacing: 0.04em; - font-size: 0.78rem; + font-weight: 700; + font-family: var(--font-mono); + font-size: var(--font-size-xs); } .brand-text { - display: grid; - gap: 0.12rem; + display: flex; + flex-direction: column; + gap: 1px; } .brand-text strong { - font-size: 0.98rem; + font-size: var(--font-size-md); line-height: 1.1; + color: var(--text-accent); } .brand-text span { - font-size: 0.72rem; - color: var(--text-soft); - font-weight: 600; + font-size: var(--font-size-xs); + color: var(--text-muted); + font-family: var(--font-mono); } -.toolbar-action-group { - padding-right: 0.5rem; - border-right: 1px solid #d6e2f1; +.toolbar-section { + display: flex; + align-items: center; + gap: var(--space-2); + padding: 0 var(--space-2); + border-left: 1px solid var(--bg-border); + height: 100%; } -.toolbar-action-group:last-child { - border-right: 0; - padding-right: 0; -} - -.toolbar-actions button, -.bottom-panel-toolbar .toolbar-actions button, -.bottom-dev-toggle, -.pan-console-toolbar button, -.inspector-inline-row button { - border: 1px solid #c8d7ea; - background: #fdfefe; - color: #1f3550; - border-radius: 10px; - padding: 0.4rem 0.72rem; +.toolbar-section:first-child { + border-left: 0; + padding-left: 0; +} + +.toolbar-input { + background: var(--bg-input); + border: 1px solid var(--bg-border); + border-radius: var(--radius-md); + padding: var(--space-1) var(--space-2); + color: var(--text-primary); + font-family: var(--font-mono); + font-size: var(--font-size-sm); + min-width: 180px; + height: 28px; + transition: border-color var(--transition-fast); +} + +.toolbar-input:focus { + outline: none; +} + +.toolbar-input.memory-input:focus { + border-color: rgba(0, 245, 255, 0.3); +} + +.toolbar-input.query-input:focus { + border-color: rgba(255, 217, 61, 0.3); +} + +.toolbar-input::placeholder { + color: var(--text-muted); +} + +.toolbar-btn { + background: var(--bg-input); + border: 1px solid var(--bg-border); + border-radius: var(--radius-md); + color: var(--text-secondary); + padding: var(--space-1) var(--space-2); cursor: pointer; - font-weight: 600; + font-family: var(--font-mono); + font-size: var(--font-size-xs); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.04em; white-space: nowrap; - box-shadow: var(--shadow-soft); + height: 28px; + display: flex; + align-items: center; + gap: var(--space-1); } -.toolbar-actions button:hover, -.bottom-panel-toolbar .toolbar-actions button:hover, -.bottom-dev-toggle:hover, -.pan-console-toolbar button:hover, -.inspector-inline-row button:hover { - border-color: #afc4de; - background: #f5f9ff; +.toolbar-btn:hover { + background: var(--bg-hover); + border-color: var(--text-muted); } -.toolbar-actions button:disabled, -.inspector-inline-row button:disabled { - opacity: 0.56; +.toolbar-btn:disabled { + opacity: 0.4; cursor: not-allowed; - box-shadow: none; } -.toolbar-actions button[aria-pressed="true"], -.bottom-panel-toolbar .toolbar-actions button[aria-pressed="true"], -.bottom-dev-toggle[aria-pressed="true"] { - border-color: #8fb2da; - color: #194f87; - background: #eaf2fe; +.toolbar-btn[aria-pressed="true"], +.toolbar-btn.active { + border-color: var(--color-t1); + color: var(--color-t1); } -.toolbar-zoom button { - min-width: 42px; - text-align: center; - padding-left: 0.48rem; - padding-right: 0.48rem; -} +/* ── Step dots ── */ -.palette-tools, -.inspector-tabs { +.step-dots { display: flex; - flex-wrap: wrap; - gap: 0.45rem; + gap: 3px; + align-items: center; } -.palette-panel { - height: calc(100% - 0.8rem); +.step-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--bg-border); + transition: background var(--transition-fast); } -.palette-content { - display: grid; - gap: 0.82rem; - padding: 0.68rem !important; - height: 100%; - overflow: auto; +.step-dot.done { + background: var(--color-t1); } -.palette-group { - border: 1px solid #d5e2f1; - border-radius: 12px; - background: #f8fbff; - padding: 0.58rem; +.step-dot.active { + background: var(--color-question); + animation: dot-pulse 0.8s ease-in-out infinite alternate; } -.palette-group h4, -.palette-shortcuts h4 { - margin: 0 0 0.5rem; - font-size: 0.68rem; - letter-spacing: 0.08em; - text-transform: uppercase; - color: #4a678b; - font-weight: 800; +@keyframes dot-pulse { + from { + opacity: 0.6; + } + to { + opacity: 1; + } } -.palette-group .palette-tools { - display: grid; - gap: 0.38rem; - grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); +/* ── Left palette ── */ + +.palette-panel { + height: 100%; + border-top: 0; + border-bottom: 0; + border-left: 0; + display: flex; + flex-direction: column; +} + +.palette-content { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding: var(--space-2) !important; + flex: 1; + overflow: auto; } .palette-tool-btn { - width: 100%; + width: 40px; height: 40px; - border: 1px solid #cfdded; - background: #ffffff; - color: #1e334d; - border-radius: 9px; + border: 1px solid transparent; + background: transparent; + color: var(--text-muted); + border-radius: var(--radius-md); padding: 0; cursor: pointer; display: grid; place-items: center; + font-family: var(--font-mono); + font-size: var(--font-size-xs); + font-weight: 600; + transition: all var(--transition-fast); } .palette-tool-btn:hover { - border-color: #b6cbdf; - background: #f2f8ff; + background: var(--bg-hover); + color: var(--text-secondary); } .palette-tool-btn[aria-pressed="true"] { - border-color: #8fb2da; - background: #e8f2ff; - color: #174f86; + background: var(--bg-input); + border-color: var(--color-t1); + color: var(--color-t1); } -.palette-tool-icon { - width: 20px; - height: 20px; - object-fit: contain; - pointer-events: none; +.palette-shortcut-hint { + font-family: var(--font-mono); + font-size: var(--font-size-xs); + color: var(--text-muted); + padding: var(--space-1) var(--space-2); + line-height: 1.5; } -.palette-shortcuts { - border: 1px dashed #c6d7ea; - border-radius: 12px; - background: linear-gradient(180deg, #fcfeff 0%, #f7fbff 100%); - padding: 0.6rem; - display: grid; - gap: 0.32rem; -} - -.palette-shortcuts p { - margin: 0; - font-size: 0.74rem; - color: #45617f; - line-height: 1.4; -} +/* ── Inspector panel ── */ .mg-inspector-panel { - height: calc(100% - 0.8rem); + height: 100%; + border-top: 0; + border-bottom: 0; + border-right: 0; + display: flex; + flex-direction: column; } .inspector-layout { - display: grid; - gap: 0.76rem; + display: flex; + flex-direction: column; + gap: var(--space-2); + flex: 1; + overflow: auto; } .inspector-summary { - background: #f5f9ff; - border: 1px solid #d5e3f4; - border-radius: 12px; - padding: 0.68rem; + padding: var(--space-2) 0; } .inspector-node-title { margin: 0; - font-size: 0.95rem; - font-weight: 700; + font-size: var(--font-size-lg); + font-weight: 600; + color: var(--text-accent); } .inspector-node-meta { - margin: 0.24rem 0 0; - color: var(--text-soft); - font-size: 0.77rem; - text-transform: capitalize; + margin: var(--space-1) 0 0; + color: var(--text-muted); + font-size: var(--font-size-sm); + font-family: var(--font-mono); + text-transform: uppercase; +} + +/* ── Inspector tabs: text-only, underline-active, anti-slop ── */ + +.inspector-tabs { + display: flex; + gap: var(--space-3); + border-bottom: 1px solid var(--bg-border); + padding-bottom: 0; } .inspector-tabs button { - border: 1px solid #cbdbef; - background: #f4f8ff; - color: #20466c; - border-radius: 999px; - padding: 0.3rem 0.62rem; - font-size: 0.75rem; + background: none; + border: none; + border-bottom: 2px solid transparent; + border-radius: 0; + color: var(--text-muted); + padding: var(--space-1) 0 var(--space-2); + font-family: var(--font-mono); + font-size: var(--font-size-xs); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.06em; cursor: pointer; - font-weight: 600; + white-space: nowrap; + transition: color var(--transition-fast), border-color var(--transition-fast); } .inspector-tabs button:hover { - background: #edf4ff; + color: var(--text-secondary); } -.inspector-tabs button[aria-pressed="true"] { - background: #2d619b; - border-color: #2d619b; - color: #ffffff; +.inspector-tabs button[aria-pressed="true"], +.inspector-tabs button[aria-selected="true"] { + color: var(--color-t1); + border-bottom-color: var(--color-t1); } .inspector-tab-content { - display: grid; - gap: 0.68rem; + display: flex; + flex-direction: column; + gap: var(--space-2); min-height: 0; overflow: auto; - padding-right: 0.08rem; + flex: 1; +} + +/* ── Inspector key-value rows (anti-slop: no cards) ── */ + +.inspector-kv { + display: grid; + grid-template-columns: auto 1fr; + gap: var(--space-1) var(--space-3); + font-size: var(--font-size-sm); } -.inspector-group { - border: 1px solid var(--line); - border-radius: 12px; - padding: 0.68rem; - background: #fcfdff; +.inspector-kv-label { + color: var(--text-muted); + font-family: var(--font-sans); + font-weight: 500; + white-space: nowrap; } -.inspector-group h4 { - margin: 0 0 0.56rem; - font-size: 0.8rem; - color: #3d5878; +.inspector-kv-value { + color: var(--text-primary); + font-family: var(--font-mono); + word-break: break-word; +} + +.inspector-section-title { + margin: var(--space-2) 0 var(--space-1); + font-size: var(--font-size-xs); + font-family: var(--font-mono); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-muted); } .inspector-field { - display: grid; - gap: 0.26rem; - margin-bottom: 0.54rem; + display: flex; + flex-direction: column; + gap: var(--space-1); + margin-bottom: var(--space-2); } .inspector-field:last-child { @@ -308,155 +380,106 @@ } .inspector-field > span { - font-size: 0.72rem; - color: var(--text-soft); - font-weight: 600; + font-size: var(--font-size-xs); + color: var(--text-muted); + font-weight: 500; + font-family: var(--font-sans); } .inspector-field input[type="text"], .inspector-field input[type="url"], .inspector-field input[type="number"], .inspector-field select, -.inspector-field textarea, -.pan-console-toolbar input { +.inspector-field textarea { width: 100%; - border: 1px solid #c9d8eb; - border-radius: 9px; - padding: 0.44rem 0.5rem; - font: inherit; - font-size: 0.8rem; - color: var(--text); - background: #ffffff; + border: 1px solid var(--bg-border); + border-radius: var(--radius-md); + padding: var(--space-1) var(--space-2); + font-family: var(--font-mono); + font-size: var(--font-size-sm); + color: var(--text-primary); + background: var(--bg-input); } .inspector-field textarea { resize: vertical; - min-height: 72px; - font-family: var(--font-mono); - line-height: 1.35; -} - -.inspector-field input[type="color"] { - width: 100%; - height: 2rem; - border: 1px solid #cdd9ea; - border-radius: 9px; - padding: 0.16rem; - background: #ffffff; -} - -.inspector-field.checkbox { - display: flex; - align-items: center; - gap: 0.45rem; + min-height: 60px; + line-height: 1.4; } -.inspector-field.checkbox span { - font-size: 0.8rem; - color: var(--text); +.inspector-field select { + appearance: none; } -.inspector-help, .inspector-empty { margin: 0; - color: var(--text-soft); - font-size: 0.8rem; + color: var(--text-muted); + font-size: var(--font-size-sm); + line-height: 1.5; + padding: var(--space-4) var(--space-2); } -.inspector-source-list { - display: grid; - gap: 0.35rem; - margin-bottom: 0.6rem; -} +/* ── Tags ── */ -.inspector-source-item { - display: flex; - gap: 0.45rem; - align-items: flex-start; - font-size: 0.79rem; - color: #2f415d; +.tag { + display: inline-flex; + padding: 1px var(--space-2); + border: 1px solid; + border-radius: var(--radius-sm); + font-family: var(--font-mono); + font-size: var(--font-size-xs); + line-height: 1.6; } -.inspector-source-item code { - font-size: 0.67rem; - color: #516784; +.tag-t1 { + border-color: rgba(0, 245, 255, 0.4); + color: var(--color-t1); } -.inspector-inline-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.65rem; - margin-top: 0.42rem; +.tag-t2 { + border-color: rgba(123, 97, 255, 0.4); + color: var(--color-t2); } -.inspector-last-updated { - font-size: 0.72rem; - color: #5f7391; +.tag-t3 { + border-color: rgba(255, 45, 120, 0.4); + color: var(--color-t3); } -.inspector-preview { - margin: 0; - max-height: 220px; - overflow: auto; - padding: 0.5rem 0.55rem; - border: 1px solid #d7e2f1; - border-radius: 9px; - background: #ffffff; - font-family: var(--font-mono); - font-size: 0.75rem; - line-height: 1.4; - white-space: pre-wrap; - word-break: break-word; +.tag-question { + border-color: rgba(255, 217, 61, 0.4); + color: var(--color-question); } -.inspector-list { - margin: 0; - padding-left: 1.1rem; - display: grid; - gap: 0.35rem; - font-size: 0.78rem; - color: #304766; +.tag-trigger { + border-color: rgba(255, 159, 28, 0.4); + color: var(--color-trigger); } -.log-list { - list-style: none; - padding: 0; - margin: 0; - display: grid; - gap: 0.4rem; -} +/* ── Relevance gauge ── */ -.log-item { - padding: 0.48rem 0.6rem; - background: #f8fbff; - border: 1px solid #d9e6f5; - border-radius: 9px; - font-family: var(--font-mono); - font-size: 0.74rem; -} - -.mg-canvas { - min-height: 0; +.relevance-gauge { + height: 4px; + background: var(--bg-border); + border-radius: 2px; + overflow: hidden; } -.mg-graph-panel { - display: flex; - flex-direction: column; - height: calc(100% - 0.8rem); +.relevance-gauge-fill { + height: 100%; + border-radius: 2px; + transition: width var(--transition-normal); } -.mg-graph-content { - flex: 1; - min-height: 380px; - min-width: 0; - padding: 0.72rem !important; -} +/* ── Bottom panel ── */ .mg-activity .mg-panel { - height: calc(100% - 0.8rem); + height: 100%; display: flex; flex-direction: column; + border-bottom: 0; + border-left: 0; + border-right: 0; } .mg-activity .mg-panel .content { @@ -466,292 +489,323 @@ } .bottom-panel-content { - display: grid; - grid-template-rows: auto minmax(120px, 1fr) auto; - gap: 0.72rem; + display: flex; + flex-direction: column; + gap: var(--space-2); + height: 100%; } .bottom-panel-toolbar { display: flex; align-items: center; justify-content: space-between; - gap: 0.64rem; - flex-wrap: wrap; + gap: var(--space-3); } -.bottom-panel-tab-content { - min-height: 0; -} +/* ── Bottom panel tabs: text-only, underline-active, anti-slop ── */ -.bottom-panel-dev { - border-top: 1px solid #dae5f2; - padding-top: 0.56rem; +.bottom-tabs { + display: flex; + gap: var(--space-3); } -.panel-empty { - margin: 0; - color: var(--text-soft); +.bottom-tabs button { + background: none; + border: none; + border-bottom: 2px solid transparent; + border-radius: 0; + color: var(--text-muted); + padding: var(--space-1) 0 var(--space-2); + font-family: var(--font-mono); + font-size: var(--font-size-xs); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.06em; + cursor: pointer; + white-space: nowrap; + transition: color var(--transition-fast), border-color var(--transition-fast); + position: relative; } -.panel-split { - display: grid; - gap: 0.42rem; - margin-bottom: 0.66rem; +.bottom-tabs button:hover { + color: var(--text-secondary); } -.panel-split:last-child { - margin-bottom: 0; +.bottom-tabs button[aria-pressed="true"] { + color: var(--color-t1); + border-bottom-color: var(--color-t1); } -.panel-split h4 { - margin: 0; - font-size: 0.76rem; - letter-spacing: 0.02em; - color: #36567a; +.tab-badge { + position: absolute; + top: -2px; + right: -10px; + min-width: 14px; + height: 14px; + border-radius: 7px; + background: var(--color-contradiction); + color: var(--text-accent); + font-size: 9px; + font-weight: 700; + display: grid; + place-items: center; + padding: 0 3px; } -.row-meta { - color: #607793; - margin-right: 0.42rem; +.bottom-panel-tab-content { + flex: 1; + min-height: 0; + overflow: auto; } -.level-info { - color: #2b5f95; +/* ── Log rows (anti-slop: no cards) ── */ + +.log-row { + display: flex; + align-items: baseline; + gap: var(--space-2); + padding: var(--space-1) 0; + border-bottom: 1px solid rgba(33, 38, 45, 0.5); + font-size: var(--font-size-sm); } -.level-warn { - color: #8d5f1a; +.log-row:last-child { + border-bottom: 0; } -.level-error { - color: #8a2f38; +.log-timestamp { + color: var(--text-muted); + font-family: var(--font-mono); + font-size: var(--font-size-xs); + flex-shrink: 0; } -.panel-rows { - margin: 0; - padding: 0; - list-style: none; - display: grid; - gap: 0.42rem; +.log-step { + color: var(--color-t1); + font-family: var(--font-mono); + font-size: var(--font-size-xs); + font-weight: 600; + flex-shrink: 0; + min-width: 24px; } -.panel-row { - border: 1px solid #dbe6f3; - border-radius: 9px; - background: #f9fcff; - padding: 0.46rem 0.56rem; - display: grid; - gap: 0.3rem; +.log-message { + color: var(--text-secondary); + font-family: var(--font-mono); } -.panel-row-error { - border-color: #edcfd3; - background: #fff8f9; +.log-detail { + color: var(--text-muted); + font-family: var(--font-mono); + font-size: var(--font-size-xs); } -.panel-row-main { - display: flex; - align-items: center; - gap: 0.45rem; - flex-wrap: wrap; +.log-confidence { + font-family: var(--font-mono); + font-size: var(--font-size-xs); + font-weight: 600; } -.panel-row-main code, -.pan-event-row code { - padding: 0.12rem 0.28rem; - border: 1px solid #d8e3f0; - border-radius: 6px; - background: #ffffff; - color: #3b4f67; - font-size: 0.7rem; +.confidence-high { + color: #3fb950; } -.chip { - border: 1px solid #cddbed; - border-radius: 999px; - padding: 0.1rem 0.4rem; - font-size: 0.68rem; - background: #edf4ff; - color: #34587d; +.confidence-medium { + color: var(--color-question); } -.panel-row-meta { - display: flex; - gap: 0.6rem; - flex-wrap: wrap; - font-size: 0.72rem; - color: #5f7691; +.confidence-low { + color: var(--color-contradiction); } -.panel-row-detail { - font-size: 0.76rem; - color: #2d425d; +.panel-empty { + margin: 0; + color: var(--text-muted); + font-size: var(--font-size-sm); + font-family: var(--font-sans); + padding: var(--space-4) var(--space-2); + line-height: 1.5; } +/* ── PAN console ── */ + .pan-console { - display: grid; - gap: 0.46rem; + display: flex; + flex-direction: column; + gap: var(--space-1); } .pan-console-toolbar { display: flex; align-items: center; - gap: 0.45rem; + gap: var(--space-2); } .pan-console-toolbar input { flex: 1; - min-width: 180px; - font-size: 0.76rem; - background: #fbfdff; + min-width: 140px; + font-size: var(--font-size-sm); + background: var(--bg-input); + border: 1px solid var(--bg-border); + border-radius: var(--radius-md); + padding: var(--space-1) var(--space-2); + color: var(--text-primary); + font-family: var(--font-mono); } .pan-event-row { - display: grid; - gap: 0.2rem; + display: flex; + gap: var(--space-2); + align-items: baseline; + font-size: var(--font-size-xs); + font-family: var(--font-mono); + color: var(--text-muted); + padding: 2px 0; } -.graph-workspace { - height: 100%; - min-height: 360px; - position: relative; - overflow: hidden; - border-radius: 14px; - border: 1px solid #cedbeb; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.65); - background-color: #f6faff; - background-image: - linear-gradient(to right, rgba(153, 176, 204, 0.24) 1px, transparent 1px), - linear-gradient(to bottom, rgba(153, 176, 204, 0.24) 1px, transparent 1px); - background-size: 24px 24px; - cursor: grab; - outline: none; - touch-action: none; +.pan-event-row code { + padding: 1px var(--space-1); + border: 1px solid var(--bg-border); + border-radius: var(--radius-sm); + background: var(--bg-input); + color: var(--text-secondary); + font-size: var(--font-size-xs); } -.graph-workspace.is-dragging-node { - cursor: grabbing; -} +/* ── Canvas container ── */ -.graph-workspace[data-tool="pan"] { - cursor: grab; +.mg-canvas { + min-height: 0; } -.graph-workspace[data-tool^="create:"] { - cursor: copy; +.mg-graph-panel { + display: flex; + flex-direction: column; + height: 100%; + border: 0; + background: transparent; } -.graph-workspace[data-tool="connect"] { - cursor: crosshair; +.mg-graph-content { + flex: 1; + min-height: 0; + min-width: 0; + padding: 0 !important; + position: relative; } -.graph-scene { - position: absolute; - inset: 0; - width: 3200px; - height: 2200px; - transform-origin: 0 0; -} +/* ── Three.js canvas container ── */ -.graph-edges-layer { - position: absolute; - inset: 0; - width: 3200px; - height: 2200px; - overflow: visible; - pointer-events: auto; +.three-canvas-container { + width: 100%; + height: 100%; + position: relative; + background: var(--bg-canvas); + cursor: grab; + outline: none; } -.graph-node-layer { - position: absolute; - inset: 0; - pointer-events: none; +.three-canvas-container.is-orbiting { + cursor: grabbing; } -.graph-node-layer [data-node-id] { - pointer-events: auto; - touch-action: none; +.three-canvas-container canvas { + display: block; + width: 100% !important; + height: 100% !important; } -.graph-marquee { - position: absolute; - border: 1px solid #3a6ea6; - background: rgba(58, 110, 166, 0.14); - border-radius: 6px; - pointer-events: none; - z-index: 7; -} +/* ── Tooltip (CSS2D overlay) ── */ -.graph-edge-path { - fill: none; - stroke: #90a5c0; - stroke-width: 1.5; - stroke-linecap: round; +.node-tooltip { + background: #161b22; + opacity: 0.95; + border: 1px solid var(--bg-border); + border-radius: var(--radius-md); + padding: var(--space-1) var(--space-2); + max-width: 240px; pointer-events: none; + font-family: var(--font-mono); + font-size: var(--font-size-sm); + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.graph-edge-path.is-selected { - stroke-width: 2.4; - stroke: #2c6ca8; +.node-tooltip-meta { + color: var(--text-muted); + font-size: var(--font-size-xs); + margin-top: 2px; } -.graph-edge-hit-area { - fill: none; - stroke: transparent; - stroke-width: 14; - pointer-events: stroke; - cursor: pointer; -} +/* ── Canvas overlays ── */ -.graph-edge-draft { - fill: none; - stroke: #335f90; - stroke-width: 2; - stroke-dasharray: 6 6; - stroke-linecap: round; +.canvas-empty-state { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + color: var(--text-muted); + font-family: var(--font-sans); + font-size: var(--font-size-lg); pointer-events: none; + z-index: 2; } -.graph-edge-parent_of { - stroke: #2f6aaa; -} - -.graph-edge-reports_to { - stroke: #3e6ea5; - stroke-dasharray: 5 4; -} - -.graph-edge-reads_from { - stroke: #28846f; -} - -.graph-edge-arrow { - fill: #6f88a7; -} - -.graph-edge-label-group { +.canvas-loading-state { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + color: var(--text-muted); + font-family: var(--font-mono); + font-size: var(--font-size-sm); pointer-events: none; + z-index: 2; + animation: pulse-text 2s ease-in-out infinite; } -.graph-edge-label-bg { - fill: rgba(244, 249, 255, 0.92); - stroke: #c7d9ee; +@keyframes pulse-text { + 0%, + 100% { + opacity: 0.4; + } + 50% { + opacity: 1; + } } -.graph-edge-label { - font-size: 10.6px; - fill: #395677; - text-anchor: middle; - font-weight: 700; +.canvas-prompt { + position: absolute; + top: var(--space-4); + left: 50%; + transform: translateX(-50%); + color: var(--text-muted); + font-family: var(--font-sans); + font-size: var(--font-size-sm); + pointer-events: none; + z-index: 2; + opacity: 0; + animation: fade-in-prompt 1s 3s ease forwards; } -@media (max-width: 1200px) { - .top-toolbar-content { - grid-template-columns: 1fr 1fr; - gap: 0.5rem; +@keyframes fade-in-prompt { + to { + opacity: 1; } +} - .toolbar-brand { - grid-column: 1 / -1; - } +.webgl-error-overlay { + position: absolute; + inset: 0; + display: grid; + place-items: center; + background: var(--bg-panel); + z-index: 10; + color: var(--text-secondary); + font-family: var(--font-sans); + font-size: var(--font-size-lg); } diff --git a/data/sample/ads_api_mock.json b/data/sample/ads_api_mock.json deleted file mode 100644 index 6adb22d..0000000 --- a/data/sample/ads_api_mock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "campaigns": [ - { "id": "cmp_1", "name": "Spring Launch", "status": "active" }, - { "id": "cmp_2", "name": "Retargeting", "status": "paused" } - ] -} diff --git a/data/sample/market_data.json b/data/sample/market_data.json deleted file mode 100644 index f189f5d..0000000 --- a/data/sample/market_data.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "snapshot": "2026-03-31", - "segments": [ - { "name": "SMB", "growth": 0.12 }, - { "name": "MidMarket", "growth": 0.09 }, - { "name": "Enterprise", "growth": 0.07 } - ] -} diff --git a/data/sample/site_config.json b/data/sample/site_config.json deleted file mode 100644 index 609a1e7..0000000 --- a/data/sample/site_config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "siteName": "MindGraph Demo Site", - "theme": "light", - "regions": ["hero", "features", "footer"] -} diff --git a/data/sample/support_db.json b/data/sample/support_db.json deleted file mode 100644 index 36264d2..0000000 --- a/data/sample/support_db.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "tickets": [ - { "id": "t-1001", "priority": "high", "state": "open" }, - { "id": "t-1002", "priority": "medium", "state": "open" } - ] -} diff --git a/docs/superpowers/specs/2026-04-03-car-brain-simulator-design.md b/docs/superpowers/specs/2026-04-03-car-brain-simulator-design.md new file mode 100644 index 0000000..eeb4ce6 --- /dev/null +++ b/docs/superpowers/specs/2026-04-03-car-brain-simulator-design.md @@ -0,0 +1,939 @@ +# CAR Brain Simulator — Design Spec + +**Date:** 2026-04-03 +**Status:** Approved +**Repo:** amitko-pixel/carmindgraph (fork of chrisrobison/mindgraph) + +## Overview + +Reconstruct the MindGraph AI app into a visual brain simulator implementing the +Clustered Associative Recall (CAR) Protocol. The app becomes a working CAR engine +with a 3D neural visualization — you add memories, ask questions, and watch the +brain process them through the full 13-step retrieval sequence. + +### What it is + +- A CAR Protocol workbench + living memory system +- **Standalone product** that also slides into existing projects/file systems +- **Runs locally** on your machine, never phones home (except to the AI runner) +- Domain-agnostic: ingests any knowledge (code, business, research, conversations) +- Four input modes: text memory input, query panel, JSON file import, **project directory scan** +- Ships with a Claude instruction manual for full project ingestion +- Visual: Neural dark 3D aesthetic using Three.js for the graph canvas +- **AI-powered:** Claude Code (Anthropic API) runs the CAR engine. Swappable to local models later. + +### What it is NOT + +- Not a static visualization or demo. The brain is powered by real AI. +- Not a cloud service. Everything runs on your machine. +- Not a chat interface. It's a graph workbench with memory processing. +- Not locked to one AI provider. Claude Code is the default, local models are planned. + +## Architecture + +### Two-Layer Architecture + +**Layer 1: Local Server** (`server.js` or `server.py`) +A lightweight local server that: +- Serves the static frontend (HTML/CSS/JS) +- Proxies AI requests to the Anthropic API (keeps API key server-side, never in browser) +- Reads the local file system for project ingestion (scans directories, reads files) +- Persists brain data to disk as JSON (not just localStorage) +- Exposes a simple REST API for the frontend + +**Layer 2: Browser Frontend** (Three.js + Web Components) +The 3D brain visualization and all panel UI. Talks only to the local server. + +``` + Browser (localhost:4173) Local Server (localhost:4174) + ┌──────────────────────┐ ┌──────────────────────────┐ + │ Three.js 3D Canvas │ │ Static file serving │ + │ Web Component panels│◄────────►│ /api/ingest (fs scan) │ + │ PAN event bus │ REST │ /api/process (AI call) │ + │ graph-store (state) │ │ /api/brain (persist) │ + └──────────────────────┘ │ /api/config (settings)│ + └─────────┬────────────────┘ + │ + ┌─────────▼────────────────┐ + │ AI Runner (pluggable) │ + │ ├─ Claude (Anthropic API)│ ← default + │ ├─ Ollama (local) │ ← future + │ └─ OpenAI (fallback) │ ← future + └──────────────────────────┘ +``` + +### Running Modes + +**Standalone mode** (default): +```bash +cd carmindgraph +node server.js +# or: python3 server.py +# Opens http://localhost:4173 +``` +Empty brain. Add memories manually, query, import JSON. + +**Project mode** (slide into an existing project): +```bash +cd ~/Projects/my-app +car-brain --project . +# or: car-brain --project /path/to/project +``` +The server scans the project directory, ingests files through Claude, and builds +the initial brain graph. You see your project's knowledge materialize as neurons. + +### AI Runner Interface + +The runner is pluggable. Every CAR operation goes through a simple interface: + +```javascript +// runner API (server-side) +runner.generateQuestions(chunk) // → Question[] at 5 levels +runner.decompose(query) // → sub-questions for retrieval +runner.consolidate(chunks) // → Tier 2 summaries +runner.detectPatterns(summaries) // → Tier 3 schemas +runner.gradeConfidence(subAnswers) // → overall confidence + gaps +runner.synthesize(fragments, query)// → reconstructed answer +runner.detectContradictions(a, b) // → contradiction analysis +runner.buildRetrievalCues(chunk) // → retrieval cues +``` + +**Default runner: Claude Code (Claude subscription)** +The default user has a Claude subscription (Pro/Team/Enterprise), not a raw API key. +The server integrates via Claude Code CLI (`claude` command), which handles auth +through the user's existing subscription. No separate API key needed. + +- Routine operations (questions, cues, consolidation): uses Claude Code with default model +- Heavy operations (synthesis, pattern detection, contradictions): uses Claude Code with `--model opus` flag +- Prompts follow the CAR protocol sections exactly +- Falls back to Anthropic API (`ANTHROPIC_API_KEY`) if Claude Code CLI is not installed + +**Future runners:** +- Ollama (local models, no API key needed, fully offline) +- OpenAI (GPT-4o fallback) +- Raw Anthropic API (for users with API keys instead of subscriptions) +- Custom (user implements the runner interface) + +### What stays from existing codebase +- PAN event bus (`js/core/pan.js`) +- Store architecture (graph-store, ui-store, persistence-store) +- Web Components for all panels +- Undo/redo (snapshot-based) + +### What changes +- `graph-canvas` → Three.js WebGL scene (replaces DOM/SVG rendering) +- Node types → CAR protocol entities (chunk, cluster, question, pattern, trigger) +- Edge types → CAR relationships (linked_to, amends, contradicts, promotes_to, etc.) +- Inspector tabs → CAR metadata views +- Bottom panel tabs → Retrieval log, Consolidation, Contradictions, Questions, Metamemory +- Runtime → real AI-powered CAR engine replaces mock-agent-runtime +- Persistence → disk-based JSON files (alongside localStorage for session state) +- Seed data → Spain margins demo from the protocol +- New: lightweight local server for API proxying and file system access +- New dependency: Three.js via CDN (` -