|
| 1 | +# Cortex Brain Map — Implementation Plan |
| 2 | + |
| 3 | +**Spec:** `specs/2026-05-09-cortex-brain-map-redesign.md` |
| 4 | +**Date:** 2026-05-09 |
| 5 | +**Status:** Ready for execution |
| 6 | +**Approach:** Phased delivery, ≤ 5 files per phase, verify after each, commit per phase. |
| 7 | + |
| 8 | +## Phase ordering |
| 9 | + |
| 10 | +``` |
| 11 | +P1 Geometry foundation → P2 Render pipeline & layers |
| 12 | +P2 → P3 Edge shader + merged geometry |
| 13 | +P3 → P4 Ripple engine (BFS + DataTexture) |
| 14 | +P4 → P5 Click flow + camera ease + dim/restore |
| 15 | +P5 → P6 HUD overlay + SPLIT toggle + ripple path |
| 16 | +P6 → P7 Tests (unit + perf + snapshot) |
| 17 | +P7 → P8 Cleanup + flag wiring + removals |
| 18 | +``` |
| 19 | + |
| 20 | +Each phase is independently buildable and visually verifiable in the running desktop app. Verify gate: type-check, lint, run tests, manually open Brain tab, smoke-test the phase's added behavior. Commit on green. |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +## Phase 1 — Geometry foundation |
| 25 | + |
| 26 | +**Goal:** Replace anatomical hemisphere layout with shell-based positions. App renders nodes on outer/inner geodesic shells with orbital rings, no anatomy, no ripple yet. |
| 27 | + |
| 28 | +**Files (≤ 5):** |
| 29 | +1. `desktop/cortex-control-center/src/brain/ShellGeometry.js` — NEW. Builds outer icosphere (R=140, subdivision 2), inner icosphere (R=80), 3 ellipse rings (reuse current `ellipseRing` math), reticle ring, center crosshair. Exports `createConstellationShells()` returning a single `THREE.Group` named `cortex-constellation-shell`. |
| 30 | +2. `desktop/cortex-control-center/src/brain/ShellLayout.js` — NEW. Exports `applyShellLayout(nodes, { useShellSplit })` projecting each node to its shell surface using a deterministic seeded hash. Memory → outer if `useShellSplit`, else outer for both. Decisions → inner if `useShellSplit`, else outer. Returns nodes with `{ x, y, z, shellRadius, brainRegion: 'outer'|'inner' }`. |
| 31 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. Replace `applyBrainLayout` import with `applyShellLayout`. Replace shell injection useEffect to use `createConstellationShells` instead of `createJarvisBrainShell`. Drop the `createBrainShapeForce` registration; add `createShellProjectionForce` that pulls each node's `(x,y,z)` toward `shellRadius * normalize(node.pos)`. Keep `useShellSplit` as `useState(true)`. |
| 32 | +4. *(unused this phase)* |
| 33 | +5. *(unused this phase)* |
| 34 | + |
| 35 | +**Anatomical removal scope (this phase):** delete `BRAIN_REGIONS`, `brainRegionForNode`, hemisphere math in `brainLayoutPoint`, `cortexPath`, `hemisphereOutline`, `createJarvisBrainShell`. Keep `seededUnit`, `hashString` (reused by ShellLayout). |
| 36 | + |
| 37 | +**Verify:** |
| 38 | +- `pnpm tsc --noEmit` clean. |
| 39 | +- `pnpm lint` clean. |
| 40 | +- Open desktop app → Brain tab → see two faceted wireframe shells + 3 rings + reticle. Nodes scattered on shell surfaces. No two-blob brain shape at any zoom. |
| 41 | +- HUD copy and stats unchanged. |
| 42 | + |
| 43 | +**Commit:** `refactor(brain): replace anatomical layout with shell-based constellation geometry` |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## Phase 2 — Render pipeline & layers |
| 48 | + |
| 49 | +**Goal:** Selective bloom on emissive elements only, layer assignment, tonemapping, draw call ceiling enforced. |
| 50 | + |
| 51 | +**Files:** |
| 52 | +1. `desktop/cortex-control-center/src/brain/RenderLayers.js` — NEW. Exports `BRAIN_LAYERS = { BASE: 0, BLOOM: 1 }` and helper `assignLayer(object3d, layer)` walking children. |
| 53 | +2. `desktop/cortex-control-center/src/brain/PostFx.jsx` — NEW. React component wrapping `<EffectComposer>` + selective `<Bloom>` from `@react-three/postprocessing`. Props: `enabled`, `intensity`, `threshold`, `smoothing`. Manages a 1-second rolling frame-time window via `useFrame`; calls `onAutoDegrade(disabled: boolean)` when median crosses thresholds (≥33.3 ms disable, ≤22 ms re-enable sustained 3 s). |
| 54 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. Add `bloomEnabled` state (default `true`). Mount `<PostFx>` inside the graph wrapper. Set `gl.toneMapping = ACESFilmicToneMapping` and `gl.toneMappingExposure = 1.0` via `ForceGraph3D` ref. Assign shells/rings/reticle to layer 0; nodes (instanced material) to layer 1. |
| 55 | +4. `desktop/cortex-control-center/package.json` — EDIT. Add `@react-three/postprocessing` and `@react-three/fiber` (peer) if missing. Run `pnpm install`. |
| 56 | +5. *(unused this phase)* |
| 57 | + |
| 58 | +**Verify:** |
| 59 | +- Bloom visible: nodes glow softly, shell wireframes stay crisp (no halo bleed). |
| 60 | +- Open Chrome devtools perf → record 5 s of orbiting → median frame time < 22 ms on reference machine. |
| 61 | +- Inject artificial 50 ms `setTimeout` stall in a scratch hook for 1.5 s → bloom disables; remove → bloom re-enables ≤ 4 s. (Manual smoke; perf test added later in P7.) |
| 62 | +- `renderer.info.render.calls < 50` logged once after first paint (temporary `console.debug`, removed in P8). |
| 63 | + |
| 64 | +**Commit:** `feat(brain): add selective bloom + auto-degrade + layer assignment` |
| 65 | + |
| 66 | +--- |
| 67 | + |
| 68 | +## Phase 3 — Edge shader + merged geometry |
| 69 | + |
| 70 | +**Goal:** Replace `react-force-graph-3d` per-link rendering with a single merged `BufferGeometry` driven by a custom `ShaderMaterial`. Static look only — no activation animation yet. |
| 71 | + |
| 72 | +**Files:** |
| 73 | +1. `desktop/cortex-control-center/src/brain/PulseShader.js` — NEW. Exports `createPulseMaterial({ baseColor, pulseColor })` returning a `THREE.ShaderMaterial` with uniforms `uTime`, `uActivation` (`DataTexture`, placeholder this phase), `uHeadPos`. Vertex shader passes `aEdgeId`, `aProgress` (0..1 along edge), `aActivation` from texture sample. Fragment shader: `gl_FragColor = mix(base, pulse, smoothstep(uHeadPos-0.05, uHeadPos, vProgress) * (1.0 - smoothstep(uHeadPos, uHeadPos+0.15, vProgress))) * (vActivation + 0.05)`. |
| 74 | +2. `desktop/cortex-control-center/src/brain/EdgeMesh.js` — NEW. Builds a single merged `BufferGeometry` from `graphData.links`. Each link contributes 16 quad segments along its straight or arched path; attributes: `aPosition`, `aProgress`, `aEdgeId`. Exports `buildEdgeMesh(links, nodesById)` and `disposeEdgeMesh(mesh)`. |
| 75 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. Disable `react-force-graph-3d`'s built-in link rendering (`linkVisibility={() => false}`). Add the merged edge mesh to the scene as a sibling of the InstancedMesh. Rebuild only when `graphData.links` reference changes. |
| 76 | +4. *(unused this phase)* |
| 77 | +5. *(unused this phase)* |
| 78 | + |
| 79 | +**Verify:** |
| 80 | +- Edges still render, indistinguishable from prior look at static rest (just constant color). |
| 81 | +- Draw call count drops measurably (shells 2 + rings 3 + nodes 1 + edges 1 + reticle 1 + crosshair 1 + HUD ≤ 6 = ≤ 14 < 50). |
| 82 | +- No regression in hover/click selection (edges aren't clickable yet — not a regression; was already low-priority). |
| 83 | +- Type-check, lint, existing tests pass. |
| 84 | + |
| 85 | +**Commit:** `feat(brain): replace per-link rendering with merged geometry + pulse shader scaffold` |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## Phase 4 — Ripple engine (BFS + DataTexture) |
| 90 | + |
| 91 | +**Goal:** When a node is clicked, BFS runs and writes activation values to the edge `DataTexture`. Shader reads them and animates the traveling pulse along activated edges. |
| 92 | + |
| 93 | +**Files:** |
| 94 | +1. `desktop/cortex-control-center/src/brain/RippleEngine.js` — NEW. Class `RippleEngine` with: |
| 95 | + - `buildAdjacency(links)` — Map<nodeId, Array<{ neighborId, edgeIndex }>>. |
| 96 | + - `fire(nodeId, now)` — runs BFS depth ≤ 2, schedules activations into a Float32Array indexed by edge index, with `firstSeen[edgeIndex] = clickTime + depth × 110ms`. |
| 97 | + - `tick(now, gl)` — single rAF entry point: for each edge, computes `t = now - firstSeen[i]`; if `t >= 0`, writes `min(activation + risePart, 1.0) × exp(-(t-riseMs)/280)` into the DataTexture. Multiple ripples accumulate in `Float32Array activations` then clamp to 1.0 on flush. |
| 98 | + - Owns `THREE.DataTexture(activations, edgeCount, 1, RedFormat, FloatType)`; `texture.needsUpdate = true` on flush. |
| 99 | +2. `desktop/cortex-control-center/src/brain/easing.js` — NEW. Exports `easeOutCubic(t)`, `expDecay(t, tau)`, `clamp01(x)`. |
| 100 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. Instantiate `RippleEngine` once on `graphData` change. Wire its `DataTexture` into `PulseShader`'s `uActivation`. Run `engine.tick(performance.now(), gl)` inside an `useFrame` hook (added via `<RippleTicker>` child component because `ForceGraph3D` doesn't expose `useFrame`). |
| 101 | +4. `desktop/cortex-control-center/src/brain/RippleTicker.jsx` — NEW. Tiny component using `useFrame` to call `engine.tick()` each frame. Wired into the post-fx tree from P2. |
| 102 | +5. *(unused this phase)* |
| 103 | + |
| 104 | +**Hook into click:** keep this phase shader-only. Click already exists (`selectGraphNode`); add `engineRef.current.fire(node.id, performance.now())` inside it. Visible result: clicked node's outgoing edges glow with a traveling head. |
| 105 | + |
| 106 | +**Verify:** |
| 107 | +- Click a node → its outgoing edges show a pulse traveling outward, decaying within ~500 ms. |
| 108 | +- Two rapid clicks on adjacent nodes → both ripples visible, edges shared by both glow brighter (additive, clamped at 1.0). |
| 109 | +- No memory leak after 100 clicks (`renderer.info.memory.geometries` and `.textures` stable). |
| 110 | +- Type-check, lint, existing tests pass. |
| 111 | + |
| 112 | +**Commit:** `feat(brain): ripple engine — BFS-timed activation drives pulse shader` |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## Phase 5 — Click flow polish: source pulse, secondary pop-in, camera ease, dim/restore |
| 117 | + |
| 118 | +**Goal:** Round out the click-fire experience to match §5 of the spec. |
| 119 | + |
| 120 | +**Files:** |
| 121 | +1. `desktop/cortex-control-center/src/brain/NodeFx.jsx` — NEW. React component rendering source-node halo (additive sprite, scale pulse 1.0→1.4→1.0, 600 ms) and secondary ripple rings (additive sprites at depth-1/depth-2 nodes, 400 ms expand+fade). Driven by `RippleEngine` callbacks. |
| 122 | +2. `desktop/cortex-control-center/src/brain/RippleEngine.js` — EDIT. Add observer pattern: `onSourceFire(cb)`, `onNodeReached(cb)`. `NodeFx` subscribes. |
| 123 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. |
| 124 | + - Replace `focusGraphNode` (full dolly) with `easeCameraToward(camera, target, 0.15)`. |
| 125 | + - Add dim/restore: when ripple active, drive `nodeOpacity` per-instance to 0.25 for non-affected nodes, 1.0 for affected, ease 200 ms in / 400 ms out. |
| 126 | + - Mount `<NodeFx />` inside the graph tree. |
| 127 | +4. `desktop/cortex-control-center/src/brain/cameraEase.js` — NEW. Exports `easeCameraToward(camera, target, fraction)`. |
| 128 | +5. *(unused this phase)* |
| 129 | + |
| 130 | +**Verify:** |
| 131 | +- Click node with several neighbors: |
| 132 | + - Source pulses 1.0→1.4→1.0 over 600 ms. |
| 133 | + - Edges show traveling glow. |
| 134 | + - Depth-1 nodes pop ripple ring on packet arrival; depth-2 nodes do too. |
| 135 | + - Non-affected nodes fade to ~25 % opacity, restore within 400 ms after ripple completes. |
| 136 | + - Camera eases ~15 % toward node, no re-center, user can still orbit. |
| 137 | +- Total ripple visibly complete within ~900 ms. |
| 138 | + |
| 139 | +**Commit:** `feat(brain): source pulse, secondary ripple, camera ease, dim/restore` |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +## Phase 6 — HUD overlay + SPLIT toggle + ripple path trace |
| 144 | + |
| 145 | +**Goal:** Add Jarvis HUD chrome (corner glyph clusters, radar reticle), wire `useShellSplit` toggle, populate "RIPPLE PATH" mini-trace in the selection panel. |
| 146 | + |
| 147 | +**Files:** |
| 148 | +1. `desktop/cortex-control-center/src/brain/HudOverlay.jsx` — NEW. Four corner glyph clusters (DOM, absolutely positioned over canvas) with tick dials + status readouts: TL = nodes/links, TR = mem/dec/agents/last-fire timestamp, BL = bearing/zoom/depth, BR = small radar reticle (SVG arc + bearing needle reading from camera azimuth). |
| 149 | +2. `desktop/cortex-control-center/src/styles.css` — EDIT. Add `.brain-hud-corner`, `.brain-hud-radar`, `.brain-hud-tick`, `.brain-split-toggle`. Keep existing brain HUD classes intact. |
| 150 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. |
| 151 | + - Mount `<HudOverlay>` inside `.brain-container`. |
| 152 | + - Add SPLIT pill button next to AUTO/MANUAL toggle; flips `useShellSplit`. Passes through to `applyShellLayout` on next layout pass. |
| 153 | + - Extend selection panel: render `selectedFlow.flowLinks` (already computed) as a labeled "RIPPLE PATH" tree showing depth-1 hop names + depth-2 hop counts. |
| 154 | +4. *(unused this phase)* |
| 155 | +5. *(unused this phase)* |
| 156 | + |
| 157 | +**Verify:** |
| 158 | +- Four corners populated, radar needle rotates with camera bearing. |
| 159 | +- SPLIT pill toggles outer/inner-shell vs single-shell-color-only modes; layout re-applies cleanly without losing the camera. |
| 160 | +- Selection panel shows RIPPLE PATH listing actual neighbors when a node is selected. |
| 161 | +- Existing top-left card and top-right stats unchanged. |
| 162 | + |
| 163 | +**Commit:** `feat(brain): jarvis HUD overlay, SPLIT toggle, ripple path trace` |
| 164 | + |
| 165 | +--- |
| 166 | + |
| 167 | +## Phase 7 — Tests |
| 168 | + |
| 169 | +**Goal:** Cover every §14 acceptance bullet with automated tests where possible. |
| 170 | + |
| 171 | +**Files:** |
| 172 | +1. `desktop/cortex-control-center/src/brain/__tests__/ShellLayout.test.js` — NEW. Deterministic positions across runs and across `graphData` reference changes that don't touch node ids. |
| 173 | +2. `desktop/cortex-control-center/src/brain/__tests__/RippleEngine.test.js` — NEW. BFS depth cap = 2, additivity (clamp at 1.0), camera ease (15 %, no re-center), dim/restore (0.25 ± 0.01, 400 ms restore). |
| 174 | +3. `desktop/cortex-control-center/src/brain/__tests__/easing.test.js` — NEW. `easeOutCubic` boundaries + monotonic, `expDecay(τ × ln(6)) ≈ 0.16`. |
| 175 | +4. `desktop/cortex-control-center/src/brain/__tests__/perf.test.js` — NEW. Headless WebGL via `gl` npm package: render 1000 nodes / 300 links, assert `renderer.info.render.calls < 50`. Bloom auto-degrade trigger: inject 50 ms stalls for 1.5 s, assert disable; remove, assert re-enable within 4 s. |
| 176 | +5. `desktop/cortex-control-center/src/brain/__tests__/snapshot.test.js` — NEW. Puppeteer launches the desktop app dev build, navigates to Brain tab, seeds fixed data, **disables bloom** in the harness, captures PNG, compares via `pixelmatch` 0.1 against `__snapshots__/constellation.png`. First run writes the baseline (manual approval gate in PR). |
| 177 | + |
| 178 | +**Verify:** |
| 179 | +- `pnpm test --run` green. |
| 180 | +- `pnpm test perf.test.js` green on reference machine. |
| 181 | +- Snapshot baseline reviewed visually before being checked in. |
| 182 | + |
| 183 | +**Commit:** `test(brain): unit + perf + snapshot coverage for constellation lattice` |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +## Phase 8 — Cleanup + flag wiring + removals |
| 188 | + |
| 189 | +**Goal:** Wire build-time flag, delete dead code, remove temporary debug logs. |
| 190 | + |
| 191 | +**Files:** |
| 192 | +1. `desktop/cortex-control-center/vite.config.js` — EDIT. Wire `CORTEX_BRAIN_LATTICE` env var; default `1`. |
| 193 | +2. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. |
| 194 | + - At entry: if `import.meta.env.VITE_CORTEX_BRAIN_LATTICE === '0'`, render the legacy renderer for the QA overlap window. (Else: lattice path.) Note: legacy renderer is already retained on disk only if Phase 1 deletion was deferred — given the spec says "delete in same PR", we delete now and ship lattice unconditionally; this Phase 8 file edit then becomes a no-op flag plumb that we keep for one release before removing. |
| 195 | + - Decision based on user signal at PR time: keep flag plumb or drop it. Default plan: drop, since spec says no shim. |
| 196 | + - Remove temporary `console.debug` for draw call count from P2. |
| 197 | +3. `desktop/cortex-control-center/src/BrainVisualizer.jsx` — EDIT. Final removals if any anatomy code still lingered (audit pass). |
| 198 | +4. `CHANGELOG.md` — EDIT. Add `## v0.6.x — Brain Map: Constellation Lattice` entry summarizing the redesign. |
| 199 | +5. *(unused this phase)* |
| 200 | + |
| 201 | +**Verify:** |
| 202 | +- `rg -n "BRAIN_REGIONS|brainRegionForNode|createJarvisBrainShell|cortexPath|hemisphereOutline|applyBrainLayout" desktop/cortex-control-center/src/` returns zero hits. |
| 203 | +- Build passes: `pnpm build`. |
| 204 | +- Final manual smoke pass on reference machine: 1000-node load < 2 s, click-to-fire latency < 100 ms, ripple within 900 ms, 60 fps median, RIPPLE PATH HUD populated, SPLIT toggle works, 2D fallback still triggers when WebGL is forced off. |
| 205 | +- All P1–P7 commits land cleanly on top of master. |
| 206 | + |
| 207 | +**Commit:** `chore(brain): remove anatomy code, wire flag, changelog` |
| 208 | + |
| 209 | +--- |
| 210 | + |
| 211 | +## Cross-cutting rules |
| 212 | + |
| 213 | +- **No `git add .`** — only stage files listed per phase. |
| 214 | +- **Re-read before editing.** Max 3 edits per file between verification reads. |
| 215 | +- **Verify gate after each phase** — type-check, lint, tests, manual smoke. No "deferred fixes" between phases. |
| 216 | +- **Commit per phase.** New commits, never amend. |
| 217 | +- **Run on reference machine** for any fps claim. |
| 218 | +- **Failures escalate after 3 attempts** — stop, report, do not loop silently. |
| 219 | + |
| 220 | +## Risk reserves |
| 221 | + |
| 222 | +- If P3 merged-geometry path causes hover flicker (the existing `nodeLabel` hover relies on per-link raycast that no longer exists): build a minimal raycaster against the InstancedMesh nodes only — links don't need hover. Spec doesn't require link hover. Budget: 2 hours. |
| 223 | +- If P4 shader compile fails on a target driver: fall back to 2D grid (already specified in §13). Budget: 30 min decision. |
| 224 | +- If P7 perf test in headless `gl` is too flaky: gate the draw-call assertion only; manual fps measurement on reference machine remains canonical. |
| 225 | + |
| 226 | +## Out of scope (carry forward) |
| 227 | + |
| 228 | +- Audio cues (Tone.js sub-pluck per hop). |
| 229 | +- Mobile/touch ripple gestures. |
| 230 | +- Saving/replaying ripple sequences. |
| 231 | +- Any change to `/dump` endpoint or downstream Cortex APIs. |
0 commit comments