Compare commits

...

15 Commits

Author SHA1 Message Date
Alexander Whitestone
7890bd4886 fix: [MEDIA] Veo/Flow flythrough prototypes for The Nexus and Timmy (closes #681)
Some checks failed
CI / test (pull_request) Failing after 8s
CI / validate (pull_request) Failing after 11s
Review Approval Gate / verify-review (pull_request) Failing after 2s
2026-04-10 20:17:12 -04:00
cc4af009c7 [claude] Mnemosyne session rooms — holographic chambers per session (#1171) (#1178)
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 13s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-10 22:45:10 +00:00
089b06b6f8 [claude] Mnemosyne category regions — spatial zones for fact types (#1168) (#1179)
Some checks failed
Deploy Nexus / deploy (push) Failing after 2s
Staging Verification Gate / verify-staging (push) Has been cancelled
2026-04-10 22:45:04 +00:00
8beae5ecc1 [claude] Mnemosyne holographic fact detail panel (#1172) (#1177)
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 6s
2026-04-10 22:29:17 +00:00
e2edfd3318 [claude] Mnemosyne gravity well clustering — related memories attract in 3D (#1175) (#1176)
Some checks failed
Deploy Nexus / deploy (push) Failing after 4s
Staging Verification Gate / verify-staging (push) Failing after 4s
2026-04-10 22:18:46 +00:00
8e18fa5311 Merge pull request 'fix: Missing Source Code Investigation — Classical AI Commits Disappearing' (#1163) from mimo/code/issue-1145 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 2s
Staging Verification Gate / verify-staging (push) Failing after 3s
Auto-merged by Timmy
2026-04-10 21:00:40 +00:00
1bf2af15a0 Merge pull request 'fix: [DEFERRED] Hermes Trismegistus — New Wizard Proposal' (#1162) from mimo/code/issue-1146 into main
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Staging Verification Gate / verify-staging (push) Has been cancelled
Auto-merged by Timmy
2026-04-10 21:00:37 +00:00
4095946749 Merge pull request '[Mnemosyne] Memory crystal click-to-inspect interaction' (#1161) from feat/mnemosyne-crystal-inspect into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
Auto-merged by Timmy
2026-04-10 21:00:24 +00:00
Alexander Whitestone
845e2f2ced fix: Missing Source Code Investigation — Classical AI Commits Disappearing (closes #1145)
Some checks failed
CI / test (pull_request) Failing after 8s
CI / validate (pull_request) Failing after 12s
Review Approval Gate / verify-review (pull_request) Failing after 3s
Root cause: duplicate agents wrote GOFAI code to public/nexus/app.js (wrong path)
instead of root app.js. The public/nexus/ files were corrupt duplicates that got
overwritten and eventually deleted, creating the illusion of disappearing code.

The classical AI code is fully present in root app.js — all 13 classes verified:
SymbolicEngine, AgentFSM, KnowledgeGraph, Blackboard, SymbolicPlanner,
HTNPlanner, CaseBasedReasoner, NeuroSymbolicBridge, MetaReasoningLayer,
AdaptiveCalibrator, PSELayer, plus A* search and bitmask fact indexing.

Prevention:
- Added public/nexus/ to .gitignore
- Added canonical file path documentation to CLAUDE.md
- Filed investigation report (INVESTIGATION_ISSUE_1145.md)
2026-04-10 16:25:57 -04:00
Mimo Swarm (mimo-code-1146)
60af11ec2f fix: [DEFERRED] Hermes Trismegistus — New Wizard Proposal (closes #1146)
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 13s
Review Approval Gate / verify-review (pull_request) Failing after 3s
- Added fleet/hermes-trismegistus/README.md with full proposal
- Added fleet/hermes-trismegistus/lane.md with routing definition
- Filled in acceptance criteria from issue #1146
- Status remains DEFERRED — unblock conditions documented

Automated by mimo-v2-pro swarm.
2026-04-10 16:14:13 -04:00
c387708892 feat(mnemosyne): add memory crystal inspection panel styles
Some checks failed
CI / test (pull_request) Failing after 10s
CI / validate (pull_request) Failing after 13s
Review Approval Gate / verify-review (pull_request) Failing after 2s
2026-04-10 19:38:57 +00:00
8694c0f5ad feat(mnemosyne): add memory crystal inspection panel HTML overlay 2026-04-10 19:38:55 +00:00
c3547196d8 feat(mnemosyne): memory crystal click-to-inspect — raycast crystals, show panel, dismiss on empty click 2026-04-10 19:38:54 +00:00
87bfe9b332 feat(mnemosyne): add crystal mesh query + highlight/select API for click-to-inspect 2026-04-10 19:38:53 +00:00
a0964a2fbf auto-merge PR #1159
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 2s
2026-04-10 19:03:41 +00:00
14 changed files with 2079 additions and 14 deletions

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@ nexus/__pycache__/
tests/__pycache__/
mempalace/__pycache__/
.aider*
# Prevent agents from writing to wrong path (see issue #1145)
public/nexus/

View File

@@ -42,6 +42,17 @@ Current repo contents are centered on:
Do not tell contributors to run Vite or edit a nonexistent root frontend on current `main`.
If browser/UI work is being restored, it must happen through the migration backlog and land back here.
## Canonical File Paths
**Frontend code lives at repo ROOT, NOT in `public/nexus/`:**
- `app.js` — main Three.js app (GOFAI, 3D world, all frontend logic)
- `index.html` — main HTML shell
- `style.css` — styles
- `server.py` — websocket bridge
- `gofai_worker.js` — web worker for off-thread reasoning
**DO NOT write to `public/nexus/`** — this path is gitignored. Agents historically wrote here by mistake, creating corrupt duplicates. See issue #1145 and `INVESTIGATION_ISSUE_1145.md`.
## Hard Rules
1. One canonical 3D repo only: `Timmy_Foundation/the-nexus`
@@ -50,6 +61,7 @@ If browser/UI work is being restored, it must happen through the migration backl
4. Telemetry and durable truth flow through Hermes harness
5. OpenClaw remains a sidecar, not the governing authority
6. Before claiming visual validation, prove the app being viewed actually comes from current `the-nexus`
7. **NEVER write frontend files to `public/nexus/`** — use repo root paths listed above
## Validation Rule

View File

@@ -0,0 +1,72 @@
# Investigation Report: Missing Source Code — Classical AI Commits Disappearing
**Issue:** #1145
**Date:** 2026-04-10
**Investigator:** mimo-v2-pro swarm worker
## Summary
**The classical AI code is NOT missing. It is fully present in root `app.js` (3302 lines).**
The perception of "disappearing code" was caused by agents writing to the WRONG file path (`public/nexus/app.js` instead of root `app.js`), creating corrupt duplicate files that were repeatedly overwritten and eventually deleted.
## Root Cause
**Explanation #1 confirmed: Duplicate agents on different machines overwriting each other's commits.**
Multiple Google AI Agent instances wrote GOFAI implementations to `public/nexus/app.js` — a path that does not correspond to the canonical app structure. These commits kept overwriting each other:
| Commit | Date | What happened |
|--------|------|---------------|
| `8943cf5` | 2026-03-30 | Symbolic reasoning engine written to `public/nexus/app.js` (+2280 lines) |
| `e2df240` | 2026-03-30 | Phase 3 Neuro-Symbolic Bridge — overwrote to 284 lines of HTML (wrong path) |
| `7f2f23f` | 2026-03-30 | Phase 4 Meta-Reasoning — same destructive overwrite |
| `bf3b98b` | 2026-03-30 | A* Search — same destructive overwrite |
| `e88bcb4` | 2026-03-30 | Bug fix identified `public/nexus/` files as corrupt duplicates, **deleted them** |
## Evidence: Code Is Present on Main
All 13 classical AI classes/functions verified present in root `app.js`:
| Class/Function | Line | Status |
|----------------|------|--------|
| `SymbolicEngine` | 82 | ✅ Present |
| `AgentFSM` | 135 | ✅ Present |
| `KnowledgeGraph` | 160 | ✅ Present |
| `Blackboard` | 181 | ✅ Present |
| `SymbolicPlanner` | 210 | ✅ Present |
| `HTNPlanner` | 295 | ✅ Present |
| `CaseBasedReasoner` | 343 | ✅ Present |
| `NeuroSymbolicBridge` | 392 | ✅ Present |
| `MetaReasoningLayer` | 422 | ✅ Present |
| `AdaptiveCalibrator` | 460 | ✅ Present |
| `PSELayer` | 566 | ✅ Present |
| `setupGOFAI()` | 596 | ✅ Present |
| `updateGOFAI()` | 622 | ✅ Present |
| Bitmask fact indexing | 86 | ✅ Present |
| A* search | 231 | ✅ Present |
These were injected by commit `af7a4c4` (PR #775, merged via `a855d54`) into the correct path.
## What Actually Happened
1. Google AI Agent wrote good GOFAI code to root `app.js` via the correct PR (#775)
2. A second wave of Google AI Agent instances also wrote to `public/nexus/app.js` (wrong path)
3. Those `public/nexus/` files kept getting overwritten by subsequent agent commits
4. Commit `e88bcb4` correctly identified the `public/nexus/` files as corrupt and deleted them
5. Alexander interpreted the git log as "classical AI code keeps disappearing"
6. The code was never actually gone — it just lived in root `app.js` the whole time
## Prevention Strategy
1. **Add `public/nexus/` to `.gitignore`** — prevents agents from accidentally writing to the wrong path again
2. **Add canonical path documentation to CLAUDE.md** — any agent reading this repo will know where frontend code lives
3. **This report** — serves as the audit trail so this confusion doesn't recur
## Acceptance Criteria
- [x] Git history audited for classical AI commits
- [x] Found the commits — they exist, code was written to wrong path
- [x] Root cause identified — duplicate agents writing to `public/nexus/` (wrong path)
- [x] Prevention strategy implemented — `.gitignore` + `CLAUDE.md` path guard
- [x] Report filed with findings (this document)

309
app.js
View File

@@ -4,6 +4,7 @@ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js';
import { SpatialMemory } from './nexus/components/spatial-memory.js';
import { SessionRooms } from './nexus/components/session-rooms.js';
// ═══════════════════════════════════════════
// NEXUS v1.1 — Portal System Update
@@ -705,6 +706,7 @@ async function init() {
createWorkshopTerminal();
createAshStorm();
SpatialMemory.init(scene);
SessionRooms.init(scene, camera, null);
updateLoad(90);
loadSession();
@@ -1883,7 +1885,7 @@ function setupControls() {
orbitState.lastX = e.clientX;
orbitState.lastY = e.clientY;
// Raycasting for portals
// Raycasting for portals and memory crystals
if (!portalOverlayActive) {
const mouse = new THREE.Vector2(
(e.clientX / window.innerWidth) * 2 - 1,
@@ -1891,12 +1893,43 @@ function setupControls() {
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(portals.map(p => p.ring));
if (intersects.length > 0) {
const clickedRing = intersects[0].object;
// Priority 1: Portals
const portalHits = raycaster.intersectObjects(portals.map(p => p.ring));
if (portalHits.length > 0) {
const clickedRing = portalHits[0].object;
const portal = portals.find(p => p.ring === clickedRing);
if (portal) activatePortal(portal);
if (portal) { activatePortal(portal); return; }
}
// Priority 2: Memory crystals (Mnemosyne)
const crystalMeshes = SpatialMemory.getCrystalMeshes();
if (crystalMeshes.length > 0) {
const crystalHits = raycaster.intersectObjects(crystalMeshes, false);
if (crystalHits.length > 0) {
const hitMesh = crystalHits[0].object;
const memInfo = SpatialMemory.getMemoryFromMesh(hitMesh);
if (memInfo) {
SpatialMemory.highlightMemory(memInfo.data.id);
showMemoryPanel(memInfo, e.clientX, e.clientY);
return;
}
}
}
// Priority 3: Session rooms (Mnemosyne #1171)
const roomMeshes = SessionRooms.getClickableMeshes();
if (roomMeshes.length > 0) {
const roomHits = raycaster.intersectObjects(roomMeshes, false);
if (roomHits.length > 0) {
const session = SessionRooms.handleRoomClick(roomHits[0].object);
if (session) { _showSessionRoomPanel(session); return; }
}
}
// Clicked empty space — dismiss panel
dismissMemoryPanel();
_dismissSessionRoomPanel();
}
}
});
@@ -2551,6 +2584,226 @@ function focusPortal(portal) {
let lastThoughtTime = 0;
let pulseTimer = 0;
// ═══════════════════════════════════════════
// MNEMOSYNE — MEMORY CRYSTAL INSPECTION
// ═══════════════════════════════════════════
// ── pin state for memory panel ──
let _memPanelPinned = false;
/** Convert a packed hex color integer to "r,g,b" string for CSS rgba(). */
function _hexToRgb(hex) {
return ((hex >> 16) & 255) + ',' + ((hex >> 8) & 255) + ',' + (hex & 255);
}
/**
* Position the panel near the screen click coordinates, keeping it on-screen.
*/
function _positionPanel(panel, clickX, clickY) {
const W = window.innerWidth;
const H = window.innerHeight;
const panelW = 356; // matches CSS width + padding
const panelH = 420; // generous estimate
const margin = 12;
let left = clickX + 24;
if (left + panelW > W - margin) left = clickX - panelW - 24;
left = Math.max(margin, Math.min(W - panelW - margin, left));
let top = clickY - 80;
top = Math.max(margin, Math.min(H - panelH - margin, top));
panel.style.right = 'auto';
panel.style.top = top + 'px';
panel.style.left = left + 'px';
panel.style.transform = 'none';
}
/**
* Navigate to (highlight + show panel for) a memory crystal by id.
*/
function _navigateToMemory(memId) {
SpatialMemory.highlightMemory(memId);
addChatMessage('system', `Focus: ${memId.replace(/_/g, ' ')}`);
const meshes = SpatialMemory.getCrystalMeshes();
for (const mesh of meshes) {
if (mesh.userData && mesh.userData.memId === memId) {
const memInfo = SpatialMemory.getMemoryFromMesh(mesh);
if (memInfo) { showMemoryPanel(memInfo); break; }
}
}
}
/**
* Show the holographic detail panel for a clicked crystal.
* @param {object} memInfo — { data, region } from SpatialMemory.getMemoryFromMesh()
* @param {number} [clickX] — screen X of the click (for panel positioning)
* @param {number} [clickY] — screen Y of the click
*/
function showMemoryPanel(memInfo, clickX, clickY) {
const panel = document.getElementById('memory-panel');
if (!panel) return;
const { data, region } = memInfo;
const regionDef = SpatialMemory.REGIONS[region] || SpatialMemory.REGIONS.working;
const colorHex = regionDef.color.toString(16).padStart(6, '0');
const colorRgb = _hexToRgb(regionDef.color);
// Header — region dot + label
document.getElementById('memory-panel-region').textContent = regionDef.label;
document.getElementById('memory-panel-region-dot').style.background = '#' + colorHex;
// Category badge
const badge = document.getElementById('memory-panel-category-badge');
if (badge) {
badge.textContent = (data.category || region || 'memory').toUpperCase();
badge.style.background = 'rgba(' + colorRgb + ',0.16)';
badge.style.color = '#' + colorHex;
badge.style.borderColor = 'rgba(' + colorRgb + ',0.4)';
}
// Entity name (humanised id)
const entityEl = document.getElementById('memory-panel-entity-name');
if (entityEl) entityEl.textContent = (data.id || '\u2014').replace(/_/g, ' ');
// Fact content
document.getElementById('memory-panel-content').textContent = data.content || '(empty)';
// Trust score bar
const strength = data.strength != null ? data.strength : 0.7;
const trustFill = document.getElementById('memory-panel-trust-fill');
const trustVal = document.getElementById('memory-panel-trust-value');
if (trustFill) {
trustFill.style.width = (strength * 100).toFixed(0) + '%';
trustFill.style.background = '#' + colorHex;
}
if (trustVal) trustVal.textContent = (strength * 100).toFixed(0) + '%';
// Meta rows
document.getElementById('memory-panel-id').textContent = data.id || '\u2014';
document.getElementById('memory-panel-source').textContent = data.source || 'unknown';
document.getElementById('memory-panel-time').textContent = data.timestamp ? new Date(data.timestamp).toLocaleString() : '\u2014';
// Related entities — clickable links
const connEl = document.getElementById('memory-panel-connections');
connEl.innerHTML = '';
if (data.connections && data.connections.length > 0) {
data.connections.forEach(cid => {
const btn = document.createElement('button');
btn.className = 'memory-conn-tag memory-conn-link';
btn.textContent = cid.replace(/_/g, ' ');
btn.title = 'Go to: ' + cid;
btn.addEventListener('click', (ev) => { ev.stopPropagation(); _navigateToMemory(cid); });
connEl.appendChild(btn);
});
} else {
connEl.innerHTML = '<span style="color:var(--color-text-muted)">None</span>';
}
// Pin button — reset on fresh open
_memPanelPinned = false;
const pinBtn = document.getElementById('memory-panel-pin');
if (pinBtn) {
pinBtn.classList.remove('pinned');
pinBtn.title = 'Pin panel';
pinBtn.onclick = () => {
_memPanelPinned = !_memPanelPinned;
pinBtn.classList.toggle('pinned', _memPanelPinned);
pinBtn.title = _memPanelPinned ? 'Unpin panel' : 'Pin panel';
};
}
// Positioning — near click if coords provided
if (clickX != null && clickY != null) {
_positionPanel(panel, clickX, clickY);
}
// Fade in
panel.classList.remove('memory-panel-fade-out');
panel.style.display = 'flex';
}
/**
* Dismiss the panel (respects pin). Called on empty-space click.
*/
function dismissMemoryPanel() {
if (_memPanelPinned) return;
_dismissMemoryPanelForce();
}
/**
* Force-dismiss the panel regardless of pin state. Used by the close button.
*/
function _dismissMemoryPanelForce() {
_memPanelPinned = false;
SpatialMemory.clearHighlight();
const panel = document.getElementById('memory-panel');
if (!panel || panel.style.display === 'none') return;
panel.classList.add('memory-panel-fade-out');
setTimeout(() => {
panel.style.display = 'none';
panel.classList.remove('memory-panel-fade-out');
}, 200);
}
/**
* Show the session room HUD panel when a chamber is entered.
* @param {object} session — { id, timestamp, facts[] }
*/
function _showSessionRoomPanel(session) {
const panel = document.getElementById('session-room-panel');
if (!panel) return;
const dt = session.timestamp ? new Date(session.timestamp) : new Date();
const tsEl = document.getElementById('session-room-timestamp');
if (tsEl) tsEl.textContent = isNaN(dt.getTime()) ? session.id : dt.toLocaleString();
const countEl = document.getElementById('session-room-fact-count');
const facts = session.facts || [];
if (countEl) countEl.textContent = facts.length + (facts.length === 1 ? ' fact' : ' facts') + ' in this chamber';
const listEl = document.getElementById('session-room-facts');
if (listEl) {
listEl.innerHTML = '';
facts.slice(0, 8).forEach(f => {
const item = document.createElement('div');
item.className = 'session-room-fact-item';
item.textContent = f.content || f.id || '(unknown)';
item.title = f.content || '';
listEl.appendChild(item);
});
if (facts.length > 8) {
const more = document.createElement('div');
more.className = 'session-room-fact-item';
more.style.color = 'rgba(200,180,255,0.4)';
more.textContent = '\u2026 ' + (facts.length - 8) + ' more';
listEl.appendChild(more);
}
}
// Close button
const closeBtn = document.getElementById('session-room-close');
if (closeBtn) closeBtn.onclick = () => _dismissSessionRoomPanel();
panel.classList.remove('session-panel-fade-out');
panel.style.display = 'block';
}
/**
* Dismiss the session room panel.
*/
function _dismissSessionRoomPanel() {
const panel = document.getElementById('session-room-panel');
if (!panel || panel.style.display === 'none') return;
panel.classList.add('session-panel-fade-out');
setTimeout(() => {
panel.style.display = 'none';
panel.classList.remove('session-panel-fade-out');
}, 200);
}
function gameLoop() {
requestAnimationFrame(gameLoop);
const delta = Math.min(clock.getDelta(), 0.1);
@@ -2581,6 +2834,9 @@ function gameLoop() {
animateMemoryOrbs(delta);
}
// Project Mnemosyne - Session Rooms (#1171)
SessionRooms.update(delta);
const mode = NAV_MODES[navModeIdx];
const chatActive = document.activeElement === document.getElementById('chat-input');
@@ -3104,9 +3360,52 @@ init().then(() => {
{ id: 'mem_hermes_chat', content: 'First conversation through the Hermes gateway', category: 'social', strength: 0.7, connections: [] },
{ id: 'mem_mnemosyne_start', content: 'Project Mnemosyne began — the living archive awakens', category: 'projects', strength: 0.9, connections: ['mem_nexus_birth', 'mem_spatial_schema'] },
{ id: 'mem_spatial_schema', content: 'Spatial Memory Schema defined — memories gain permanent homes', category: 'engineering', strength: 0.8, connections: ['mem_mnemosyne_start'] },
// MemPalace category zone demos — issue #1168
{ id: 'mem_pref_dark_mode', content: 'User prefers dark mode and monospace fonts', category: 'user_pref', strength: 0.9, connections: [] },
{ id: 'mem_pref_verbose_logs', content: 'User prefers verbose logging during debug sessions', category: 'user_pref', strength: 0.7, connections: [] },
{ id: 'mem_proj_nexus_goal', content: 'The Nexus goal: local-first 3D training ground for Timmy', category: 'project', strength: 0.95, connections: ['mem_proj_mnemosyne'] },
{ id: 'mem_proj_mnemosyne', content: 'Project Mnemosyne: holographic living archive of facts', category: 'project', strength: 0.85, connections: ['mem_proj_nexus_goal'] },
{ id: 'mem_tool_three_js', content: 'Three.js — 3D rendering library used for the Nexus world', category: 'tool', strength: 0.8, connections: [] },
{ id: 'mem_tool_gitea', content: 'Gitea API at forge.alexanderwhitestone.com for issue tracking', category: 'tool', strength: 0.75, connections: [] },
{ id: 'mem_gen_websocket', content: 'WebSocket bridge (server.py) connects Timmy cognition to the browser', category: 'general', strength: 0.7, connections: [] },
{ id: 'mem_gen_hermes', content: 'Hermes harness: telemetry and durable truth pipeline', category: 'general', strength: 0.65, connections: [] },
];
demoMemories.forEach(m => SpatialMemory.placeMemory(m));
// Gravity well clustering — attract related crystals, bake positions (issue #1175)
SpatialMemory.runGravityLayout();
// Project Mnemosyne — seed demo session rooms (#1171)
// Sessions group facts by conversation/work session with a timestamp.
const demoSessions = [
{
id: 'session_2026_03_01',
timestamp: '2026-03-01T10:00:00.000Z',
facts: [
{ id: 'mem_nexus_birth', content: 'The Nexus came online — first render of the 3D world', category: 'knowledge', strength: 0.95 },
{ id: 'mem_mnemosyne_start', content: 'Project Mnemosyne began — the living archive awakens', category: 'projects', strength: 0.9 },
]
},
{
id: 'session_2026_03_15',
timestamp: '2026-03-15T14:30:00.000Z',
facts: [
{ id: 'mem_first_portal', content: 'First portal deployed — connection to external service', category: 'engineering', strength: 0.85 },
{ id: 'mem_hermes_chat', content: 'First conversation through the Hermes gateway', category: 'social', strength: 0.7 },
{ id: 'mem_spatial_schema', content: 'Spatial Memory Schema defined — memories gain homes', category: 'engineering', strength: 0.8 },
]
},
{
id: 'session_2026_04_10',
timestamp: '2026-04-10T09:00:00.000Z',
facts: [
{ id: 'mem_session_rooms', content: 'Session rooms introduced — holographic chambers per session', category: 'projects', strength: 0.88 },
{ id: 'mem_gravity_wells', content: 'Gravity-well clustering bakes crystal positions on load', category: 'engineering', strength: 0.75 },
]
}
];
SessionRooms.updateSessions(demoSessions);
fetchGiteaData();
setInterval(fetchGiteaData, 30000);
runWeeklyAudit();

91
docs/media/README.md Normal file
View File

@@ -0,0 +1,91 @@
# Media Production — Veo/Flow Prototypes
Issue #681: [MEDIA] Veo/Flow flythrough prototypes for The Nexus and Timmy.
## Contents
- `veo-storyboard.md` — Full storyboard for 5 clips with shot sequences, prompts, and design focus areas
- `clip-metadata.json` — Durable metadata for each clip (prompts, model, outputs, insights)
## Clips Overview
| ID | Title | Audience | Purpose |
|----|-------|----------|---------|
| clip-001 | First Light | PUBLIC | The Nexus reveal teaser |
| clip-002 | Between Worlds | INTERNAL | Portal activation UX study |
| clip-003 | The Guardian's View | PUBLIC | Timmy's presence promo |
| clip-004 | The Void Between | INTERNAL | Ambient environment study |
| clip-005 | Command Center | INTERNAL | Terminal UI readability |
## How to Generate
### Via Flow (labs.google/flow)
1. Open `veo-storyboard.md`, copy the prompt for your clip
2. Go to labs.google/flow
3. Paste the prompt, select Veo 3.1
4. Generate (8-second clips)
5. Download output, update `clip-metadata.json` with output path and findings
### Via Gemini App
1. Type "generate a video of [prompt text]" in Gemini
2. Uses Veo 3.1 Fast (slightly lower quality, faster)
3. Good for quick iteration on prompts
### Via API (programmatic)
```python
from google import genai
client = genai.Client()
# See: ai.google.dev/gemini-api/docs/video
response = client.models.generate_content(
model="veo-3.1",
contents="[prompt from storyboard]"
)
```
## After Generation
For each clip:
1. Save output file to `outputs/clip-XXX.mp4`
2. Update `clip-metadata.json`:
- Add output file path to `output_files[]`
- Fill in `design_insights.findings` with observations
- Add `threejs_changes_suggested` if the clip reveals needed changes
3. Share internal clips with the team for design review
4. Use public clips in README, social media, project communication
## Design Insight Workflow
Each clip has specific questions it's designed to answer:
**clip-001 (First Light)**
- Scale perception: platform vs. portals vs. terminal
- Color hierarchy: teal primary, purple secondary, gold accent
- Camera movement: cinematic or disorienting?
**clip-002 (Between Worlds)**
- Activation distance: when does interaction become available?
- Transition feel: travel or teleportation?
- Overlay readability against portal glow
**clip-003 (The Guardian's View)**
- Agent presence: alive or decorative?
- Crystal hologram readability
- Wide shot: world or tech demo?
**clip-004 (The Void Between)**
- Void atmosphere: alive or empty?
- Particle systems: enhance or distract?
- Lighting hierarchy clarity
**clip-005 (Command Center)**
- Text readability at 1080p
- Color-coded panel hierarchy
- Scan-line effect: retro or futuristic?
## Constraints
- 8-second clips max (Veo/Flow limitation)
- Queued generation (not instant)
- Content policies apply
- Ultra tier gets highest rate limits

View File

@@ -0,0 +1,239 @@
{
"clips": [
{
"id": "clip-001",
"title": "First Light — The Nexus Reveal",
"purpose": "Public-facing teaser. Establishes the Nexus as a place worth visiting.",
"audience": "public",
"priority": "HIGH",
"duration_seconds": 8,
"shots": [
{
"shot": 1,
"timeframe": "0-2s",
"description": "Void Approach — camera drifts through nebula, hexagonal glow appears",
"design_focus": "isolation before connection"
},
{
"shot": 2,
"timeframe": "2-4s",
"description": "Platform Reveal — camera descends to hexagonal platform, grid pulses",
"design_focus": "structure emerges from chaos"
},
{
"shot": 3,
"timeframe": "4-6s",
"description": "Portal Array — sweep low showing multiple colored portals",
"design_focus": "infinite worlds, one home"
},
{
"shot": 4,
"timeframe": "6-8s",
"description": "Timmy's Terminal — rise to batcave terminal, holographic panels",
"design_focus": "someone is home"
}
],
"prompt": "Cinematic flythrough of a futuristic digital nexus hub. Start in deep space with a dark purple nebula, stars twinkling. Camera descends toward a glowing hexagonal platform with pulsing teal grid lines and a luminous ring border. Sweep low across the platform revealing multiple glowing portal archways in orange, teal, gold, and blue — each with flickering holographic labels. Rise toward a central command terminal with holographic data panels showing scrolling status text. Camera pushes into a teal light flare. Cyberpunk aesthetic, volumetric lighting, 8-second sequence, smooth camera movement, concept art quality.",
"prompt_variants": [],
"model_tool": "veo-3.1",
"access_point": "flow",
"output_files": [],
"design_insights": {
"questions": [
"Does the scale feel right? (platform vs. portals vs. terminal)",
"Does the color hierarchy work? (teal primary, purple secondary, gold accent)",
"Is the camera movement cinematic or disorienting?"
],
"findings": null,
"threejs_changes_suggested": []
},
"status": "pending",
"created_at": "2026-04-10T20:15:00Z"
},
{
"id": "clip-002",
"title": "Between Worlds — Portal Activation",
"purpose": "Internal design reference. Tests portal activation sequence and spatial relationships.",
"audience": "internal",
"priority": "HIGH",
"duration_seconds": 8,
"shots": [
{
"shot": 1,
"timeframe": "0-2.5s",
"description": "Approach — first-person walk toward Morrowind portal (orange, x:15, z:-10)",
"design_focus": "proximity feel, portal scale relative to player"
},
{
"shot": 2,
"timeframe": "2.5-5.5s",
"description": "Activation — portal brightens, energy vortex, particles accelerate, overlay text",
"design_focus": "activation UX, visual feedback timing"
},
{
"shot": 3,
"timeframe": "5.5-8s",
"description": "Stepping Through — camera pushes in, world dissolves, flash, 'VVARDENFELL' text",
"design_focus": "transition smoothness, immersion break points"
}
],
"prompt": "First-person perspective walking toward a glowing orange portal archway in a futuristic digital space. The portal ring has inner energy glow with rising particle effects. A holographic label \"MORROWIND\" flickers above. Camera stops, portal interior brightens into an energy vortex, particles accelerate inward. Camera pushes forward into the portal, world dissolves into an orange energy tunnel, flash to black with text \"VVARDENFELL\". Dark ambient environment with teal grid floor. Cyberpunk aesthetic, volumetric effects, smooth camera movement.",
"prompt_variants": [],
"model_tool": "veo-3.1",
"access_point": "flow",
"output_files": [],
"design_insights": {
"questions": [
"Is the activation distance clear? (when does interaction become available?)",
"Does the transition feel like travel or teleportation?",
"Is the overlay text readable against the portal glow?"
],
"findings": null,
"threejs_changes_suggested": []
},
"status": "pending",
"created_at": "2026-04-10T20:15:00Z"
},
{
"id": "clip-003",
"title": "The Guardian's View — Timmy's Perspective",
"purpose": "Public-facing. Establishes Timmy as the guardian/presence of the Nexus.",
"audience": "public",
"priority": "MEDIUM",
"duration_seconds": 8,
"shots": [
{
"shot": 1,
"timeframe": "0-2s",
"description": "Agent Presence — floating glowing orb with trailing particles",
"design_focus": "consciousness without body"
},
{
"shot": 2,
"timeframe": "2-4s",
"description": "Vision Crystal — rotating octahedron with holographic 'SOVEREIGNTY' text",
"design_focus": "values inscribed in space"
},
{
"shot": 3,
"timeframe": "4-6s",
"description": "Harness Pulse — thought stream ribbon, agent orbs drifting",
"design_focus": "the system breathes"
},
{
"shot": 4,
"timeframe": "6-8s",
"description": "Wide View — full Nexus visible, text overlay 'THE NEXUS — Timmy's Sovereign Home'",
"design_focus": "this is a world, not a page"
}
],
"prompt": "Cinematic sequence in a futuristic digital nexus. Start with eye-level view of a floating glowing orb (teal-gold light, trailing particles) pulsing gently — an AI agent presence. Shift to a rotating octahedron crystal refracting light, with holographic text \"SOVEREIGNTY — No masters, no chains\" and a ring of light pulsing beneath. Pull back to reveal flowing ribbons of light (thought streams) crossing a hexagonal platform, with agent orbs drifting. Rise to high orbit showing the full nexus: hexagonal platform, multiple colored portal archways, central command terminal, floating crystals, all framed by a dark purple nebula skybox. End with text overlay \"THE NEXUS — Timmy's Sovereign Home\". Cyberpunk aesthetic, volumetric lighting, contemplative pacing.",
"prompt_variants": [],
"model_tool": "veo-3.1",
"access_point": "flow",
"output_files": [],
"design_insights": {
"questions": [
"Do agent presences read as 'alive' or decorative?",
"Is the crystal-to-text hologram readable?",
"Does the wide shot communicate 'world' or 'tech demo'?"
],
"findings": null,
"threejs_changes_suggested": []
},
"status": "pending",
"created_at": "2026-04-10T20:15:00Z"
},
{
"id": "clip-004",
"title": "The Void Between — Ambient Environment Study",
"purpose": "Internal design reference. Tests ambient environment systems: particles, dust, lighting, skybox.",
"audience": "internal",
"priority": "MEDIUM",
"duration_seconds": 8,
"shots": [
{
"shot": 1,
"timeframe": "0-4s",
"description": "Particle Systems — static camera, view from platform edge into void, particles visible",
"design_focus": "does the void feel alive or empty?"
},
{
"shot": 2,
"timeframe": "4-8s",
"description": "Lighting Study — slow orbit showing teal/purple point lights on grid floor",
"design_focus": "lighting hierarchy, mood consistency"
}
],
"prompt": "Ambient environment study in a futuristic digital void. Static camera with slight drift, viewing from the edge of a hexagonal platform into deep space. Dark purple nebula with twinkling distant stars, subtle color shifts. Floating particles and dust drift slowly. No structures, no portals — pure atmosphere. Then camera slowly orbits showing teal and purple point lights casting volumetric glow on a dark hexagonal grid floor. Ambient lighting fills shadows. Contemplative, moody, atmospheric. Cyberpunk aesthetic, minimal movement, focus on light and particle behavior.",
"prompt_variants": [],
"model_tool": "veo-3.1",
"access_point": "flow",
"output_files": [],
"design_insights": {
"questions": [
"Is the void atmospheric or just dark?",
"Do the particle systems enhance or distract?",
"Is the lighting hierarchy (teal primary, purple secondary) clear?"
],
"findings": null,
"threejs_changes_suggested": []
},
"status": "pending",
"created_at": "2026-04-10T20:15:00Z"
},
{
"id": "clip-005",
"title": "Command Center — Batcave Terminal Focus",
"purpose": "Internal design reference. Tests readability and hierarchy of holographic terminal panels.",
"audience": "internal",
"priority": "LOW",
"duration_seconds": 8,
"shots": [
{
"shot": 1,
"timeframe": "0-2.5s",
"description": "Terminal Overview — 5 holographic panels in arc with distinct colors",
"design_focus": "panel arrangement, color distinction"
},
{
"shot": 2,
"timeframe": "2.5-5.5s",
"description": "Panel Detail — zoom into METRICS panel, scrolling text, scan lines",
"design_focus": "text readability, information density"
},
{
"shot": 3,
"timeframe": "5.5-8s",
"description": "Agent Status — shift to panel, pulsing green dots, pull back",
"design_focus": "status indication clarity"
}
],
"prompt": "Approach a futuristic holographic command terminal in a dark digital space. Five curved holographic panels float in an arc: \"NEXUS COMMAND\" (teal), \"DEV QUEUE\" (gold), \"METRICS\" (purple), \"SOVEREIGNTY\" (gold), \"AGENT STATUS\" (teal). Camera zooms into the METRICS panel showing scrolling data: \"CPU: 12%\", \"MEM: 4.2GB\", \"COMMITS: 842\" with scan lines and glow effects. Shift to AGENT STATUS panel showing \"TIMMY: ● RUNNING\", \"KIMI: ○ STANDBY\", \"CLAUDE: ● ACTIVE\" with pulsing green dots. Pull back to show full terminal context. Dark ambient environment, cyberpunk aesthetic, holographic UI focus.",
"prompt_variants": [],
"model_tool": "veo-3.1",
"access_point": "flow",
"output_files": [],
"design_insights": {
"questions": [
"Can you read the text at 1080p?",
"Do the color-coded panels communicate hierarchy?",
"Is the scan-line effect too retro or appropriately futuristic?"
],
"findings": null,
"threejs_changes_suggested": []
},
"status": "pending",
"created_at": "2026-04-10T20:15:00Z"
}
],
"metadata": {
"project": "Timmy_Foundation/the-nexus",
"issue": 681,
"source_plan": "~/google-ai-ultra-plan.md",
"tools_available": ["veo-3.1", "flow", "nano-banana-pro"],
"max_clip_duration": 8,
"created_by": "mimo-v2-pro swarm",
"created_at": "2026-04-10T20:15:00Z"
}
}

View File

View File

@@ -0,0 +1,237 @@
# Veo/Flow Flythrough Prototypes — Storyboard
## The Nexus & Timmy (Issue #681)
Source: `google-ai-ultra-plan.md` Veo/Flow section.
Purpose: Turn the current Nexus vision into short promo/concept clips for design leverage and communication.
---
## Clip 1: "First Light" — The Nexus Reveal (PUBLIC PROMO)
**Duration:** 8 seconds
**Purpose:** Public-facing teaser. Establishes the Nexus as a place worth visiting.
**Tone:** Awe. Discovery. "What is this?"
### Shot Sequence (4 shots, ~2s each)
1. **02s | Void Approach**
- Camera drifts through deep space nebula (dark purples, teals)
- Distant stars twinkle
- A faint hexagonal glow appears below
- *Narrative hook: isolation before connection*
2. **24s | Platform Reveal**
- Camera descends toward the hexagonal platform
- Grid lines pulse with teal energy
- The ring border glows at the edge
- *Narrative hook: structure emerges from chaos*
3. **46s | Portal Array**
- Camera sweeps low across the platform
- 34 portals visible: Morrowind (orange), Workshop (teal), Chapel (gold), Archive (blue)
- Each portal ring hums with colored light, holographic labels flicker
- *Narrative hook: infinite worlds, one home*
4. **68s | Timmy's Terminal**
- Camera rises to the batcave terminal
- Holographic panels glow: NEXUS COMMAND, METRICS, AGENT STATUS
- Text scrolls: "> STATUS: NOMINAL"
- Final frame: teal light floods the lens
- *Narrative hook: someone is home*
### Veo Prompt (text-to-video)
```
Cinematic flythrough of a futuristic digital nexus hub. Start in deep space with a dark purple nebula, stars twinkling. Camera descends toward a glowing hexagonal platform with pulsing teal grid lines and a luminous ring border. Sweep low across the platform revealing multiple glowing portal archways in orange, teal, gold, and blue — each with flickering holographic labels. Rise toward a central command terminal with holographic data panels showing scrolling status text. Camera pushes into a teal light flare. Cyberpunk aesthetic, volumetric lighting, 8-second sequence, smooth camera movement, concept art quality.
```
### Design Insight Target
- Does the scale feel right? (platform vs. portals vs. terminal)
- Does the color hierarchy work? (teal primary, purple secondary, gold accent)
- Is the camera movement cinematic or disorienting?
---
## Clip 2: "Between Worlds" — Portal Activation (INTERNAL DESIGN)
**Duration:** 8 seconds
**Purpose:** Internal design reference. Tests the portal activation sequence and spatial relationships.
**Tone:** Energy. Connection. "What happens when you step through?"
### Shot Sequence (3 shots, ~2.5s each)
1. **02.5s | Approach**
- First-person perspective walking toward the Morrowind portal (orange, position x:15, z:-10)
- Portal ring visible: inner glow, particle effects rising
- Holographic label "MORROWIND" flickers above
- *Design focus: proximity feel, portal scale relative to player*
2. **2.55.5s | Activation**
- Player stops at activation distance
- Portal interior brightens — energy vortex forms
- Camera tilts up to show the full portal height
- Particles accelerate into the portal center
- Overlay text appears: "ENTER MORROWIND?"
- *Design focus: activation UX, visual feedback timing*
3. **5.58s | Stepping Through**
- Camera pushes forward into the portal
- World dissolves into orange energy tunnel
- Brief flash — then fade to black with "VVARDENFELL" text
- *Design focus: transition smoothness, immersion break points*
### Veo Prompt (text-to-video)
```
First-person perspective walking toward a glowing orange portal archway in a futuristic digital space. The portal ring has inner energy glow with rising particle effects. A holographic label "MORROWIND" flickers above. Camera stops, portal interior brightens into an energy vortex, particles accelerate inward. Camera pushes forward into the portal, world dissolves into an orange energy tunnel, flash to black with text "VVARDENFELL". Dark ambient environment with teal grid floor. Cyberpunk aesthetic, volumetric effects, smooth camera movement.
```
### Design Insight Target
- Is the activation distance clear? (when does interaction become available?)
- Does the transition feel like travel or teleportation?
- Is the overlay text readable against the portal glow?
---
## Clip 3: "The Guardian's View" — Timmy's Perspective (PUBLIC PROMO)
**Duration:** 8 seconds
**Purpose:** Public-facing. Establishes Timmy as the guardian/presence of the Nexus.
**Tone:** Contemplative. Sovereign. "Who lives here?"
### Shot Sequence (4 shots, ~2s each)
1. **02s | Agent Presence**
- Camera at eye-level, looking at a floating agent presence (glowing orb with trailing particles)
- The orb pulses gently, teal-gold light
- Background: the Nexus platform, slightly out of focus
- *Narrative hook: consciousness without body*
2. **24s | Vision Crystal**
- Camera shifts to a floating octahedron crystal (Sovereignty vision point)
- Crystal rotates slowly, refracting light
- Text hologram appears: "SOVEREIGNTY — No masters, no chains"
- Ring of light pulses beneath
- *Narrative hook: values inscribed in space*
3. **46s | The Harness Pulse**
- Camera pulls back to show the thought stream — a flowing ribbon of light across the platform
- Harness pulse mesh glows at the center
- Agent orbs drift along the stream
- *Narrative hook: the system breathes*
4. **68s | Wide View**
- Camera rises to high orbit view
- Entire Nexus visible: platform, portals, terminal, crystals, agents
- Nebula skybox frames everything
- Final frame: "THE NEXUS — Timmy's Sovereign Home" text overlay
- *Narrative hook: this is a world, not a page*
### Veo Prompt (text-to-video)
```
Cinematic sequence in a futuristic digital nexus. Start with eye-level view of a floating glowing orb (teal-gold light, trailing particles) pulsing gently — an AI agent presence. Shift to a rotating octahedron crystal refracting light, with holographic text "SOVEREIGNTY — No masters, no chains" and a ring of light pulsing beneath. Pull back to reveal flowing ribbons of light (thought streams) crossing a hexagonal platform, with agent orbs drifting. Rise to high orbit showing the full nexus: hexagonal platform, multiple colored portal archways, central command terminal, floating crystals, all framed by a dark purple nebula skybox. End with text overlay "THE NEXUS — Timmy's Sovereign Home". Cyberpunk aesthetic, volumetric lighting, contemplative pacing.
```
### Design Insight Target
- Do agent presences read as "alive" or decorative?
- Is the crystal-to-text hologram readable?
- Does the wide shot communicate "world" or "tech demo"?
---
## Clip 4: "The Void Between" — Ambient Environment Study (INTERNAL DESIGN)
**Duration:** 8 seconds
**Purpose:** Internal design reference. Tests the ambient environment systems: particles, dust, lighting, skybox.
**Tone:** Atmosphere. Mood. "What does the Nexus feel like when nothing is happening?"
### Shot Sequence (2 shots, ~4s each)
1. **04s | Particle Systems**
- Static camera, slight drift
- View from platform edge, looking out into the void
- Particle systems visible: ambient particles, dust particles
- Nebula skybox: dark purples, distant stars, subtle color shifts
- No portals, no terminals — just the environment
- *Design focus: does the void feel alive or empty?*
2. **48s | Lighting Study**
- Camera slowly orbits a point on the platform
- Teal point light (position 0,1,-5) creates warm glow
- Purple point light (position -8,3,-8) adds depth
- Ambient light (0x1a1a3a) fills shadows
- Grid lines catch the light
- *Design focus: lighting hierarchy, mood consistency*
### Veo Prompt (text-to-video)
```
Ambient environment study in a futuristic digital void. Static camera with slight drift, viewing from the edge of a hexagonal platform into deep space. Dark purple nebula with twinkling distant stars, subtle color shifts. Floating particles and dust drift slowly. No structures, no portals — pure atmosphere. Then camera slowly orbits showing teal and purple point lights casting volumetric glow on a dark hexagonal grid floor. Ambient lighting fills shadows. Contemplative, moody, atmospheric. Cyberpunk aesthetic, minimal movement, focus on light and particle behavior.
```
### Design Insight Target
- Is the void atmospheric or just dark?
- Do the particle systems enhance or distract?
- Is the lighting hierarchy (teal primary, purple secondary) clear?
---
## Clip 5: "Command Center" — Batcave Terminal Focus (INTERNAL DESIGN)
**Duration:** 8 seconds
**Purpose:** Internal design reference. Tests readability and hierarchy of the holographic terminal panels.
**Tone:** Information density. Control. "What can you see from here?"
### Shot Sequence (3 shots, ~2.5s each)
1. **02.5s | Terminal Overview**
- Camera approaches the batcave terminal from the front
- 5 holographic panels visible in arc: NEXUS COMMAND, DEV QUEUE, METRICS, SOVEREIGNTY, AGENT STATUS
- Each panel has distinct color (teal, gold, purple, gold, teal)
- *Design focus: panel arrangement, color distinction*
2. **2.55.5s | Panel Detail**
- Camera zooms into METRICS panel
- Text scrolls: "> CPU: 12% [||....]", "> MEM: 4.2GB", "> COMMITS: 842"
- Panel background glows, scan lines visible
- *Design focus: text readability, information density*
3. **5.58s | Agent Status**
- Camera shifts to AGENT STATUS panel
- Text: "> TIMMY: ● RUNNING", "> KIMI: ○ STANDBY", "> CLAUDE: ● ACTIVE"
- Green dot pulses next to active agents
- Pull back to show panel in context
- *Design focus: status indication clarity*
### Veo Prompt (text-to-video)
```
Approach a futuristic holographic command terminal in a dark digital space. Five curved holographic panels float in an arc: "NEXUS COMMAND" (teal), "DEV QUEUE" (gold), "METRICS" (purple), "SOVEREIGNTY" (gold), "AGENT STATUS" (teal). Camera zooms into the METRICS panel showing scrolling data: "CPU: 12%", "MEM: 4.2GB", "COMMITS: 842" with scan lines and glow effects. Shift to AGENT STATUS panel showing "TIMMY: ● RUNNING", "KIMI: ○ STANDBY", "CLAUDE: ● ACTIVE" with pulsing green dots. Pull back to show full terminal context. Dark ambient environment, cyberpunk aesthetic, holographic UI focus.
```
### Design Insight Target
- Can you read the text at 1080p?
- Do the color-coded panels communicate hierarchy?
- Is the scan-line effect too retro or appropriately futuristic?
---
## Usage Matrix
| Clip | Title | Purpose | Audience | Priority |
|------|-------|---------|----------|----------|
| 1 | First Light | Public teaser | External | HIGH |
| 2 | Between Worlds | Portal UX design | Internal | HIGH |
| 3 | The Guardian's View | Public promo | External | MEDIUM |
| 4 | The Void Between | Environment design | Internal | MEDIUM |
| 5 | Command Center | Terminal UI design | Internal | LOW |
## Next Steps
1. Generate each clip using Veo/Flow (text-to-video prompts above)
2. Review outputs — update prompts based on what works
3. Record metadata in `docs/media/clip-metadata.json`
4. Iterate: refine prompts, regenerate, compare
5. Use internal design clips to inform Three.js implementation changes
6. Use public promo clips for README, social media, project communication
---
*Generated for Issue #681 — Timmy_Foundation/the-nexus*

View File

@@ -0,0 +1,72 @@
# Hermes Trismegistus — Wizard Proposal
> **Status:** 🟡 DEFERRED
> **Issue:** #1146
> **Created:** 2026-04-08
> **Author:** Alexander (KT Notes)
> **Mimo Worker:** mimo-code-1146-1775851759
---
## Identity
| Field | Value |
|-------|-------|
| **Name** | Hermes Trismegistus |
| **Nature** | Claude-native wizard. She knows she runs on Claude. She's "the daughter of Claude" and leans into that heritage. |
| **Purpose** | Dedicated reasoning and architecture wizard. Only handles tasks where Claude's reasoning capability genuinely adds value — planning, novel problem-solving, complex architecture decisions. |
| **Not** | A replacement for Timmy. Not competing for identity. Not doing monkey work. |
## Design Constraints
- **Free tier only from day one.** Alexander is not paying Anthropic beyond current subscription.
- **Degrades gracefully.** Full capability when free tier is generous, reduced scope when constrained.
- **Not locked to Claude.** If better free-tier providers emerge, she can route to them.
- **Multi-provider capable.** Welcome to become multifaceted if team finds better options.
## Hardware
- One of Alexander's shed laptops — minimum 4GB RAM, Ubuntu
- Dedicated machine, not shared with Timmy's Mac
- Runs in the Hermes harness
- Needs power at house first
## Constitutional Foundation
- The KT conversation and documents serve as her founding constitution
- Team (especially Timmy) has final say on whether she gets built
- Must justify her existence through useful work, same as every wizard
## Trigger to Unblock
All of the following must be true before implementation begins:
- [ ] Deadman switch wired and proven
- [ ] Config stable across fleet
- [ ] Fleet proven reliable for 1+ week
- [ ] Alexander provides a state-of-the-system KT to Claude for instantiation
## Acceptance Criteria
- [ ] Dedicated KT document written for Hermes instantiation
- [ ] Hardware provisioned (shed laptop with power)
- [ ] Hermes harness configured for Claude free tier
- [ ] Lazerus registry entry with health endpoints
- [ ] Fleet routing entry with role and routing verdict
- [ ] SOUL.md inscription drafted and reviewed by Timmy
- [ ] Smoke test: Hermes responds to a basic reasoning task
- [ ] Integration test: Hermes participates in a multi-wizard task alongside Timmy
## Proposed Lane
**Primary role:** Architecture reasoning
**Routing verdict:** ROUTE TO: complex architectural decisions, novel problem-solving, planning tasks that benefit from Claude's reasoning depth. Do NOT route to: code generation (use Timmy/Carnice), issue triage (use Fenrir), or operational tasks (use Bezalel).
## Dependencies
| Dependency | Status | Notes |
|------------|--------|-------|
| Deadman switch | 🔴 Not done | Must be proven before unblocking |
| Fleet stability | 🟡 In progress | 1+ week uptime needed |
| Shed laptop power | 🔴 Not done | Alexander needs to wire power |
| KT document | 🔴 Not drafted | Alexander provides to Claude at unblock time |

View File

@@ -0,0 +1,43 @@
# Hermes Trismegistus — Lane Definition
> **Status:** DEFERRED — do not instantiate until unblock conditions met
> **See:** fleet/hermes-trismegistus/README.md for full proposal
---
## Role
Dedicated reasoning and architecture wizard. Claude-native.
## Routing
Route to Hermes Trismegistus when:
- Task requires deep architectural reasoning
- Novel problem-solving that benefits from Claude's reasoning depth
- Planning and design decisions for the fleet
- Complex multi-step analysis that goes beyond code generation
Do NOT route to Hermes for:
- Code generation (use Timmy, Carnice, or Kimi)
- Issue triage (use Fenrir)
- Operational/DevOps tasks (use Bezalel)
- Anything that can be done with a cheaper model
## Provider
- **Primary:** anthropic/claude (free tier)
- **Fallback:** openrouter/free (Claude-class models)
- **Degraded:** ollama/gemma4:12b (when free tier exhausted)
## Hardware
- Shed laptop, Ubuntu, minimum 4GB RAM
- Dedicated machine, not shared
## Unblock Checklist
- [ ] Deadman switch operational
- [ ] Fleet config stable for 1+ week
- [ ] Shed laptop powered and networked
- [ ] KT document drafted by Alexander
- [ ] Timmy approves instantiation

View File

@@ -207,6 +207,50 @@
</div>
</div>
<!-- Memory Crystal Inspection Panel (Mnemosyne) -->
<div id="memory-panel" class="memory-panel" style="display:none;">
<div class="memory-panel-content">
<div class="memory-panel-header">
<span class="memory-category-badge" id="memory-panel-category-badge">MEM</span>
<div class="memory-panel-region-dot" id="memory-panel-region-dot"></div>
<div class="memory-panel-region" id="memory-panel-region">MEMORY</div>
<button id="memory-panel-pin" class="memory-panel-pin" title="Pin panel">&#x1F4CC;</button>
<button id="memory-panel-close" class="memory-panel-close" onclick="_dismissMemoryPanelForce()">\u2715</button>
</div>
<div class="memory-entity-name" id="memory-panel-entity-name">\u2014</div>
<div class="memory-panel-body" id="memory-panel-content">(empty)</div>
<div class="memory-trust-row">
<span class="memory-meta-label">Trust</span>
<div class="memory-trust-bar">
<div class="memory-trust-fill" id="memory-panel-trust-fill"></div>
</div>
<span class="memory-trust-value" id="memory-panel-trust-value"></span>
</div>
<div class="memory-panel-meta">
<div class="memory-meta-row"><span class="memory-meta-label">ID</span><span id="memory-panel-id">\u2014</span></div>
<div class="memory-meta-row"><span class="memory-meta-label">Source</span><span id="memory-panel-source">\u2014</span></div>
<div class="memory-meta-row"><span class="memory-meta-label">Time</span><span id="memory-panel-time">\u2014</span></div>
<div class="memory-meta-row memory-meta-row--related"><span class="memory-meta-label">Related</span><span id="memory-panel-connections">\u2014</span></div>
</div>
</div>
</div>
<!-- Session Room HUD Panel (Mnemosyne #1171) -->
<div id="session-room-panel" class="session-room-panel" style="display:none;">
<div class="session-room-panel-content">
<div class="session-room-header">
<span class="session-room-icon">&#x25A1;</span>
<div class="session-room-title">SESSION CHAMBER</div>
<button class="session-room-close" id="session-room-close" title="Close">&#x2715;</button>
</div>
<div class="session-room-timestamp" id="session-room-timestamp">&mdash;</div>
<div class="session-room-fact-count" id="session-room-fact-count">0 facts</div>
<div class="session-room-facts" id="session-room-facts"></div>
<div class="session-room-hint">Flying into chamber&hellip;</div>
</div>
</div>
<!-- Portal Atlas Overlay -->
<div id="atlas-overlay" class="atlas-overlay" style="display:none;">
<div class="atlas-content">

View File

@@ -0,0 +1,413 @@
// ═══════════════════════════════════════════════════════
// PROJECT MNEMOSYNE — SESSION ROOMS (Issue #1171)
// ═══════════════════════════════════════════════════════
//
// Groups memories by session into holographic chambers.
// Each session becomes a wireframe cube floating in space.
// Rooms are arranged chronologically along a spiral.
// Click a room to fly inside; distant rooms LOD to a point.
//
// Usage from app.js:
// SessionRooms.init(scene, camera, controls);
// SessionRooms.updateSessions(sessions); // [{id, timestamp, facts[]}]
// SessionRooms.update(delta); // call each frame
// SessionRooms.getClickableMeshes(); // for raycasting
// SessionRooms.handleRoomClick(mesh); // trigger fly-in
// ═══════════════════════════════════════════════════════
const SessionRooms = (() => {
// ─── CONSTANTS ───────────────────────────────────────
const MAX_ROOMS = 20;
const ROOM_SIZE = 9; // wireframe cube edge length
const ROOM_HALF = ROOM_SIZE / 2;
const LOD_THRESHOLD = 55; // distance: full → point
const LOD_HYSTERESIS = 5; // buffer to avoid flicker
const SPIRAL_BASE_R = 20; // spiral inner radius
const SPIRAL_R_STEP = 5; // radius growth per room
const SPIRAL_ANGLE_INC = 2.399; // golden angle (radians)
const SPIRAL_Y_STEP = 1.5; // vertical rise per room
const FLY_DURATION = 1.5; // seconds for fly-in tween
const FLY_TARGET_DEPTH = ROOM_HALF - 1.5; // how deep inside to stop
const ROOM_COLOR = 0x7b5cff; // violet — mnemosyne accent
const POINT_COLOR = 0x9b7cff;
const LABEL_COLOR = '#c8b4ff';
const STORAGE_KEY = 'mnemosyne_sessions_v1';
// ─── STATE ────────────────────────────────────────────
let _scene = null;
let _camera = null;
let _controls = null;
let _rooms = []; // array of room objects
let _sessionIndex = {}; // id → room object
// Fly-in tween state
let _flyActive = false;
let _flyElapsed = 0;
let _flyFrom = null;
let _flyTo = null;
let _flyLookFrom = null;
let _flyLookTo = null;
let _flyActiveRoom = null;
// ─── SPIRAL POSITION ──────────────────────────────────
function _spiralPos(index) {
const angle = index * SPIRAL_ANGLE_INC;
const r = SPIRAL_BASE_R + index * SPIRAL_R_STEP;
const y = index * SPIRAL_Y_STEP;
return new THREE.Vector3(
Math.cos(angle) * r,
y,
Math.sin(angle) * r
);
}
// ─── CREATE ROOM ──────────────────────────────────────
function _createRoom(session, index) {
const pos = _spiralPos(index);
const group = new THREE.Group();
group.position.copy(pos);
// Wireframe cube
const boxGeo = new THREE.BoxGeometry(ROOM_SIZE, ROOM_SIZE, ROOM_SIZE);
const edgesGeo = new THREE.EdgesGeometry(boxGeo);
const edgesMat = new THREE.LineBasicMaterial({
color: ROOM_COLOR,
transparent: true,
opacity: 0.55
});
const wireframe = new THREE.LineSegments(edgesGeo, edgesMat);
wireframe.userData = { type: 'session_room_wireframe', sessionId: session.id };
group.add(wireframe);
// Collision mesh (invisible, for raycasting)
const hitGeo = new THREE.BoxGeometry(ROOM_SIZE, ROOM_SIZE, ROOM_SIZE);
const hitMat = new THREE.MeshBasicMaterial({
visible: false,
transparent: true,
opacity: 0,
side: THREE.FrontSide
});
const hitMesh = new THREE.Mesh(hitGeo, hitMat);
hitMesh.userData = { type: 'session_room', sessionId: session.id, roomIndex: index };
group.add(hitMesh);
// LOD point (small sphere shown at distance)
const pointGeo = new THREE.SphereGeometry(0.5, 6, 4);
const pointMat = new THREE.MeshBasicMaterial({
color: POINT_COLOR,
transparent: true,
opacity: 0.7
});
const pointMesh = new THREE.Mesh(pointGeo, pointMat);
pointMesh.userData = { type: 'session_room_point', sessionId: session.id };
pointMesh.visible = false; // starts hidden; shown only at LOD distance
group.add(pointMesh);
// Timestamp billboard sprite
const sprite = _makeTimestampSprite(session.timestamp, session.facts.length);
sprite.position.set(0, ROOM_HALF + 1.2, 0);
group.add(sprite);
// Inner ambient glow
const glow = new THREE.PointLight(ROOM_COLOR, 0.4, ROOM_SIZE * 1.2);
group.add(glow);
_scene.add(group);
const room = {
session,
group,
wireframe,
hitMesh,
pointMesh,
sprite,
glow,
pos: pos.clone(),
index,
lodActive: false,
pulsePhase: Math.random() * Math.PI * 2
};
_rooms.push(room);
_sessionIndex[session.id] = room;
console.info('[SessionRooms] Created room for session', session.id, 'at index', index);
return room;
}
// ─── TIMESTAMP SPRITE ────────────────────────────────
function _makeTimestampSprite(isoTimestamp, factCount) {
const canvas = document.createElement('canvas');
canvas.width = 320;
canvas.height = 72;
const ctx = canvas.getContext('2d');
// Background pill
ctx.clearRect(0, 0, 320, 72);
ctx.fillStyle = 'rgba(20, 10, 40, 0.82)';
_roundRect(ctx, 4, 4, 312, 64, 14);
ctx.fill();
// Border
ctx.strokeStyle = 'rgba(123, 92, 255, 0.6)';
ctx.lineWidth = 1.5;
_roundRect(ctx, 4, 4, 312, 64, 14);
ctx.stroke();
// Timestamp text
const dt = isoTimestamp ? new Date(isoTimestamp) : new Date();
const label = _formatDate(dt);
ctx.fillStyle = LABEL_COLOR;
ctx.font = 'bold 15px monospace';
ctx.textAlign = 'center';
ctx.fillText(label, 160, 30);
// Fact count
ctx.fillStyle = 'rgba(200, 180, 255, 0.65)';
ctx.font = '12px monospace';
ctx.fillText(factCount + (factCount === 1 ? ' fact' : ' facts'), 160, 52);
const tex = new THREE.CanvasTexture(canvas);
const mat = new THREE.SpriteMaterial({ map: tex, transparent: true, opacity: 0.88 });
const sprite = new THREE.Sprite(mat);
sprite.scale.set(5, 1.1, 1);
sprite.userData = { type: 'session_room_label' };
return sprite;
}
// ─── HELPERS ──────────────────────────────────────────
function _roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
}
function _formatDate(dt) {
if (isNaN(dt.getTime())) return 'Unknown session';
const pad = n => String(n).padStart(2, '0');
return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())} ${pad(dt.getHours())}:${pad(dt.getMinutes())}`;
}
// ─── DISPOSE ROOM ────────────────────────────────────
function _disposeRoom(room) {
room.wireframe.geometry.dispose();
room.wireframe.material.dispose();
room.hitMesh.geometry.dispose();
room.hitMesh.material.dispose();
room.pointMesh.geometry.dispose();
room.pointMesh.material.dispose();
if (room.sprite.material.map) room.sprite.material.map.dispose();
room.sprite.material.dispose();
if (room.group.parent) room.group.parent.remove(room.group);
delete _sessionIndex[room.session.id];
}
// ─── PUBLIC: UPDATE SESSIONS ─────────────────────────
// sessions: [{id, timestamp, facts:[{id,content,category,strength,...}]}]
// Sorted chronologically oldest→newest; max MAX_ROOMS shown.
function updateSessions(sessions) {
if (!_scene) return;
const sorted = [...sessions]
.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
.slice(-MAX_ROOMS); // keep most recent MAX_ROOMS
// Remove rooms no longer present
const incoming = new Set(sorted.map(s => s.id));
for (let i = _rooms.length - 1; i >= 0; i--) {
const room = _rooms[i];
if (!incoming.has(room.session.id)) {
_disposeRoom(room);
_rooms.splice(i, 1);
}
}
// Add / update
sorted.forEach((session, idx) => {
if (_sessionIndex[session.id]) {
// Update position if index changed
const room = _sessionIndex[session.id];
if (room.index !== idx) {
room.index = idx;
const newPos = _spiralPos(idx);
room.group.position.copy(newPos);
room.pos.copy(newPos);
}
} else {
_createRoom(session, idx);
}
});
saveToStorage(sorted);
console.info('[SessionRooms] Updated:', _rooms.length, 'session rooms');
}
// ─── PUBLIC: INIT ─────────────────────────────────────
function init(scene, camera, controls) {
_scene = scene;
_camera = camera;
_controls = controls;
console.info('[SessionRooms] Initialized');
// Restore persisted sessions
const saved = loadFromStorage();
if (saved && saved.length > 0) {
updateSessions(saved);
}
}
// ─── PUBLIC: UPDATE (per-frame) ───────────────────────
function update(delta) {
if (!_scene || !_camera) return;
const camPos = _camera.position;
_rooms.forEach(room => {
const dist = camPos.distanceTo(room.pos);
// LOD toggle
const threshold = room.lodActive
? LOD_THRESHOLD + LOD_HYSTERESIS // must come closer to exit LOD
: LOD_THRESHOLD;
if (dist > threshold && !room.lodActive) {
room.lodActive = true;
room.wireframe.visible = false;
room.sprite.visible = false;
room.pointMesh.visible = true;
} else if (dist <= threshold && room.lodActive) {
room.lodActive = false;
room.wireframe.visible = true;
room.sprite.visible = true;
room.pointMesh.visible = false;
}
// Pulse wireframe opacity
room.pulsePhase += delta * 0.6;
if (!room.lodActive) {
room.wireframe.material.opacity = 0.3 + Math.sin(room.pulsePhase) * 0.2;
room.glow.intensity = 0.3 + Math.sin(room.pulsePhase * 1.4) * 0.15;
}
// Slowly rotate each room
room.group.rotation.y += delta * 0.04;
});
// Fly-in tween
if (_flyActive) {
_flyElapsed += delta;
const t = Math.min(_flyElapsed / FLY_DURATION, 1);
const ease = _easeInOut(t);
_camera.position.lerpVectors(_flyFrom, _flyTo, ease);
// Interpolate lookAt
const lookNow = new THREE.Vector3().lerpVectors(_flyLookFrom, _flyLookTo, ease);
_camera.lookAt(lookNow);
if (_controls && _controls.target) _controls.target.copy(lookNow);
if (t >= 1) {
_flyActive = false;
if (_controls && typeof _controls.update === 'function') _controls.update();
console.info('[SessionRooms] Fly-in complete for session', _flyActiveRoom && _flyActiveRoom.session.id);
_flyActiveRoom = null;
}
}
}
// ─── EASING ───────────────────────────────────────────
function _easeInOut(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
// ─── PUBLIC: GET CLICKABLE MESHES ─────────────────────
function getClickableMeshes() {
return _rooms.map(r => r.hitMesh);
}
// ─── PUBLIC: HANDLE ROOM CLICK ────────────────────────
function handleRoomClick(mesh) {
const { sessionId } = mesh.userData;
const room = _sessionIndex[sessionId];
if (!room || !_camera) return null;
// Fly into the room from the front face
_flyActive = true;
_flyElapsed = 0;
_flyActiveRoom = room;
_flyFrom = _camera.position.clone();
// Target: step inside the room toward its center
const dir = room.pos.clone().sub(_camera.position).normalize();
_flyTo = room.pos.clone().add(dir.multiplyScalar(FLY_TARGET_DEPTH));
_flyLookFrom = _controls && _controls.target
? _controls.target.clone()
: _camera.position.clone().add(_camera.getWorldDirection(new THREE.Vector3()));
_flyLookTo = room.pos.clone();
console.info('[SessionRooms] Flying into session room:', sessionId);
return room.session;
}
// ─── PERSISTENCE ──────────────────────────────────────
function saveToStorage(sessions) {
if (typeof localStorage === 'undefined') return;
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ v: 1, sessions }));
} catch (e) {
console.warn('[SessionRooms] Failed to save to localStorage:', e);
}
}
function loadFromStorage() {
if (typeof localStorage === 'undefined') return null;
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw);
if (!parsed || parsed.v !== 1 || !Array.isArray(parsed.sessions)) return null;
console.info('[SessionRooms] Restored', parsed.sessions.length, 'sessions from localStorage');
return parsed.sessions;
} catch (e) {
console.warn('[SessionRooms] Failed to load from localStorage:', e);
return null;
}
}
function clearStorage() {
if (typeof localStorage !== 'undefined') {
localStorage.removeItem(STORAGE_KEY);
console.info('[SessionRooms] Cleared localStorage');
}
}
// ─── PUBLIC API ───────────────────────────────────────
return {
init,
updateSessions,
update,
getClickableMeshes,
handleRoomClick,
clearStorage,
// For external inspection
getRooms: () => _rooms,
getSession: (id) => _sessionIndex[id] || null,
isFlyActive: () => _flyActive
};
})();
export { SessionRooms };

View File

@@ -8,12 +8,20 @@
// holographic archive.
//
// World layout (hex cylinder, radius 25):
// North (z-) → Documents & Knowledge
// South (z+) → Projects & Tasks
// East (x+) → Code & Engineering
// West (x-) → Conversations & Social
// Center → Active Working Memory
// Below (y-) → Archive (cold storage)
//
// Inner ring — original Mnemosyne taxonomy (radius 15):
// North (z-) → Documents & Knowledge
// South (z+) → Projects & Tasks
// East (x+) → Code & Engineering
// West (x-) → Conversations & Social
// Center → Active Working Memory
// Below (y-) → Archive (cold storage)
//
// Outer ring — MemPalace category zones (radius 20, issue #1168):
// North (z-) → User Preferences [golden]
// East (x+) → Project facts [blue]
// South (z+) → Tool knowledge [green]
// West (x-) → General facts [gray]
//
// Usage from app.js:
// SpatialMemory.init(scene);
@@ -73,6 +81,44 @@ const SpatialMemory = (() => {
color: 0x334455,
glyph: '\uD83D\uDDC4',
description: 'Cold storage — rarely accessed, aged-out memories'
},
// ── MemPalace category zones — outer ring, issue #1168 ────────────
user_pref: {
label: 'User Preferences',
center: [0, 0, -20],
radius: 10,
color: 0xffd700,
glyph: '\u2605',
description: 'Personal preferences, habits, user-specific settings',
labelY: 5
},
project: {
label: 'Project Facts',
center: [20, 0, 0],
radius: 10,
color: 0x4488ff,
glyph: '\uD83D\uDCC1',
description: 'Project-specific knowledge, goals, context',
labelY: 5
},
tool: {
label: 'Tool Knowledge',
center: [0, 0, 20],
radius: 10,
color: 0x44cc66,
glyph: '\uD83D\uDD27',
description: 'Tools, commands, APIs, and how to use them',
labelY: 5
},
general: {
label: 'General Facts',
center: [-20, 0, 0],
radius: 10,
color: 0x8899aa,
glyph: '\uD83D\uDCDD',
description: 'Miscellaneous facts not fitting other categories',
labelY: 5
}
};
@@ -99,6 +145,7 @@ const SpatialMemory = (() => {
const cx = region.center[0];
const cy = region.center[1] + 0.06;
const cz = region.center[2];
const labelY = region.labelY || 3;
const ringGeo = new THREE.RingGeometry(region.radius - 0.5, region.radius, 6);
const ringMat = new THREE.MeshBasicMaterial({
@@ -126,6 +173,22 @@ const SpatialMemory = (() => {
_scene.add(ring);
_scene.add(disc);
// Ground glow — brighter disc for MemPalace zones (labelY > 3 signals outer ring)
let glowDisc = null;
if (labelY > 3) {
const glowGeo = new THREE.CircleGeometry(region.radius, 32);
const glowMat = new THREE.MeshBasicMaterial({
color: region.color,
transparent: true,
opacity: 0.06,
side: THREE.DoubleSide
});
glowDisc = new THREE.Mesh(glowGeo, glowMat);
glowDisc.rotation.x = -Math.PI / 2;
glowDisc.position.set(cx, cy - 0.02, cz);
_scene.add(glowDisc);
}
// Floating label
const canvas = document.createElement('canvas');
canvas.width = 256;
@@ -139,11 +202,11 @@ const SpatialMemory = (() => {
const texture = new THREE.CanvasTexture(canvas);
const spriteMat = new THREE.SpriteMaterial({ map: texture, transparent: true, opacity: 0.6 });
const sprite = new THREE.Sprite(spriteMat);
sprite.position.set(cx, 3, cz);
sprite.position.set(cx, labelY, cz);
sprite.scale.set(4, 1, 1);
_scene.add(sprite);
return { ring, disc, sprite };
return { ring, disc, glowDisc, sprite };
}
// ─── PLACE A MEMORY ──────────────────────────────────
@@ -283,6 +346,9 @@ const SpatialMemory = (() => {
if (marker.ring && marker.ring.material) {
marker.ring.material.opacity = 0.1 + Math.sin(now * 0.001) * 0.05;
}
if (marker.glowDisc && marker.glowDisc.material) {
marker.glowDisc.material.opacity = 0.04 + Math.sin(now * 0.0008) * 0.02;
}
});
}
@@ -456,6 +522,81 @@ const SpatialMemory = (() => {
return count;
}
// ─── GRAVITY WELL CLUSTERING ──────────────────────────
// Force-directed layout: same-category crystals attract, unrelated repel.
// Run on load (bake positions, not per-frame). Spec from issue #1175.
const GRAVITY_ITERATIONS = 20;
const ATTRACT_FACTOR = 0.10; // 10% closer to same-category centroid per iteration
const REPEL_FACTOR = 0.05; // 5% away from nearest unrelated crystal
function runGravityLayout() {
const objs = Object.values(_memoryObjects);
if (objs.length < 2) {
console.info('[Mnemosyne] Gravity layout: fewer than 2 crystals, skipping');
return;
}
console.info('[Mnemosyne] Gravity layout starting —', objs.length, 'crystals,', GRAVITY_ITERATIONS, 'iterations');
for (let iter = 0; iter < GRAVITY_ITERATIONS; iter++) {
// Accumulate displacements before applying (avoids order-of-iteration bias)
const dx = new Float32Array(objs.length);
const dy = new Float32Array(objs.length);
const dz = new Float32Array(objs.length);
objs.forEach((obj, i) => {
const pos = obj.mesh.position;
const cat = obj.region;
// ── Attraction toward same-category centroid ──────────────
let sx = 0, sy = 0, sz = 0, sameCount = 0;
objs.forEach(o => {
if (o === obj || o.region !== cat) return;
sx += o.mesh.position.x;
sy += o.mesh.position.y;
sz += o.mesh.position.z;
sameCount++;
});
if (sameCount > 0) {
dx[i] += ((sx / sameCount) - pos.x) * ATTRACT_FACTOR;
dy[i] += ((sy / sameCount) - pos.y) * ATTRACT_FACTOR;
dz[i] += ((sz / sameCount) - pos.z) * ATTRACT_FACTOR;
}
// ── Repulsion from nearest unrelated crystal ───────────────
let nearestDist = Infinity;
let rnx = 0, rny = 0, rnz = 0;
objs.forEach(o => {
if (o === obj || o.region === cat) return;
const ex = pos.x - o.mesh.position.x;
const ey = pos.y - o.mesh.position.y;
const ez = pos.z - o.mesh.position.z;
const d = Math.sqrt(ex * ex + ey * ey + ez * ez);
if (d < nearestDist) {
nearestDist = d;
rnx = ex; rny = ey; rnz = ez;
}
});
if (nearestDist > 0.001 && nearestDist < Infinity) {
const len = Math.sqrt(rnx * rnx + rny * rny + rnz * rnz);
dx[i] += (rnx / len) * nearestDist * REPEL_FACTOR;
dy[i] += (rny / len) * nearestDist * REPEL_FACTOR;
dz[i] += (rnz / len) * nearestDist * REPEL_FACTOR;
}
});
// Apply displacements
objs.forEach((obj, i) => {
obj.mesh.position.x += dx[i];
obj.mesh.position.y += dy[i];
obj.mesh.position.z += dz[i];
});
}
// Bake final positions to localStorage
saveToStorage();
console.info('[Mnemosyne] Gravity layout complete — positions baked to localStorage');
}
// ─── SPATIAL SEARCH ──────────────────────────────────
function searchNearby(position, maxResults, maxDist) {
maxResults = maxResults || 10;
@@ -471,11 +612,53 @@ const SpatialMemory = (() => {
return results.slice(0, maxResults);
}
// ─── CRYSTAL MESH COLLECTION (for raycasting) ────────
function getCrystalMeshes() {
return Object.values(_memoryObjects).map(o => o.mesh);
}
// ─── MEMORY DATA FROM MESH ───────────────────────────
function getMemoryFromMesh(mesh) {
const entry = Object.values(_memoryObjects).find(o => o.mesh === mesh);
return entry ? { data: entry.data, region: entry.region } : null;
}
// ─── HIGHLIGHT / SELECT ──────────────────────────────
let _selectedId = null;
let _selectedOriginalEmissive = null;
function highlightMemory(memId) {
clearHighlight();
const obj = _memoryObjects[memId];
if (!obj) return;
_selectedId = memId;
_selectedOriginalEmissive = obj.mesh.material.emissiveIntensity;
obj.mesh.material.emissiveIntensity = 4.0;
obj.mesh.userData.selected = true;
}
function clearHighlight() {
if (_selectedId && _memoryObjects[_selectedId]) {
const obj = _memoryObjects[_selectedId];
obj.mesh.material.emissiveIntensity = _selectedOriginalEmissive || (obj.data.strength || 0.7) * 2.5;
obj.mesh.userData.selected = false;
}
_selectedId = null;
_selectedOriginalEmissive = null;
}
function getSelectedId() {
return _selectedId;
}
return {
init, placeMemory, removeMemory, update,
getMemoryAtPosition, getRegionAtPosition, getMemoriesInRegion, getAllMemories,
getCrystalMeshes, getMemoryFromMesh, highlightMemory, clearHighlight, getSelectedId,
exportIndex, importIndex, searchNearby, REGIONS,
saveToStorage, loadFromStorage, clearStorage
saveToStorage, loadFromStorage, clearStorage,
runGravityLayout
};
})();

357
style.css
View File

@@ -1223,3 +1223,360 @@ canvas#nexus-canvas {
.l402-msg { color: #fff; }
.pse-status { color: #4af0c0; font-weight: 600; }
/* ═══════════════════════════════════════════
MNEMOSYNE — MEMORY CRYSTAL INSPECTION PANEL
═══════════════════════════════════════════ */
.memory-panel {
position: fixed;
top: 50%;
right: 24px;
transform: translateY(-50%);
z-index: 120;
animation: memoryPanelIn 0.22s ease-out forwards;
}
.memory-panel-fade-out {
animation: memoryPanelOut 0.18s ease-in forwards !important;
}
@keyframes memoryPanelIn {
from { opacity: 0; transform: translateY(-50%) translateX(16px); }
to { opacity: 1; transform: translateY(-50%) translateX(0); }
}
@keyframes memoryPanelOut {
from { opacity: 1; }
to { opacity: 0; transform: translateY(-50%) translateX(12px); }
}
.memory-panel-content {
width: 340px;
background: rgba(8, 8, 24, 0.92);
backdrop-filter: blur(12px);
border: 1px solid rgba(74, 240, 192, 0.25);
border-radius: 12px;
padding: 16px;
box-shadow: 0 0 30px rgba(74, 240, 192, 0.08), 0 8px 32px rgba(0, 0, 0, 0.4);
}
.memory-panel-header {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.memory-panel-region-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.memory-panel-region {
font-family: var(--font-display, monospace);
font-size: 11px;
letter-spacing: 0.15em;
color: var(--color-primary, #4af0c0);
text-transform: uppercase;
flex: 1;
}
.memory-panel-close {
background: none;
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--color-text-muted, #888);
font-size: 14px;
cursor: pointer;
width: 24px;
height: 24px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
}
.memory-panel-close:hover {
background: rgba(255, 255, 255, 0.05);
color: #fff;
}
.memory-panel-body {
font-size: 14px;
line-height: 1.6;
color: var(--color-text, #ccc);
margin-bottom: 14px;
max-height: 120px;
overflow-y: auto;
word-break: break-word;
}
.memory-panel-meta {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 11px;
}
.memory-meta-row {
display: flex;
gap: 8px;
align-items: baseline;
}
.memory-meta-label {
color: var(--color-text-muted, #666);
text-transform: uppercase;
letter-spacing: 0.08em;
min-width: 50px;
flex-shrink: 0;
}
.memory-meta-row span:last-child {
color: var(--color-text, #aaa);
word-break: break-all;
}
.memory-conn-tag {
display: inline-block;
background: rgba(74, 240, 192, 0.1);
border: 1px solid rgba(74, 240, 192, 0.2);
border-radius: 4px;
padding: 1px 6px;
font-size: 10px;
font-family: var(--font-mono, monospace);
color: var(--color-primary, #4af0c0);
margin: 1px 2px;
}
.memory-conn-link {
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
.memory-conn-link:hover {
background: rgba(74, 240, 192, 0.22);
border-color: rgba(74, 240, 192, 0.5);
color: #fff;
}
/* Entity name — large heading inside panel */
.memory-entity-name {
font-family: var(--font-display, monospace);
font-size: 17px;
font-weight: 700;
color: #fff;
letter-spacing: 0.04em;
margin-bottom: 8px;
text-transform: capitalize;
word-break: break-word;
}
/* Category badge */
.memory-category-badge {
font-family: var(--font-display, monospace);
font-size: 9px;
letter-spacing: 0.12em;
font-weight: 700;
padding: 2px 6px;
border-radius: 4px;
border: 1px solid rgba(74, 240, 192, 0.3);
background: rgba(74, 240, 192, 0.12);
color: var(--color-primary, #4af0c0);
flex-shrink: 0;
}
/* Trust score bar */
.memory-trust-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
font-size: 11px;
}
.memory-trust-bar {
flex: 1;
height: 5px;
background: rgba(255, 255, 255, 0.08);
border-radius: 3px;
overflow: hidden;
}
.memory-trust-fill {
height: 100%;
border-radius: 3px;
background: var(--color-primary, #4af0c0);
transition: width 0.35s ease;
}
.memory-trust-value {
color: var(--color-text-muted, #888);
min-width: 32px;
text-align: right;
}
/* Pin button */
.memory-panel-pin {
background: none;
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--color-text-muted, #888);
font-size: 11px;
cursor: pointer;
width: 24px;
height: 24px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
flex-shrink: 0;
}
.memory-panel-pin:hover {
background: rgba(255, 255, 255, 0.05);
color: #fff;
}
.memory-panel-pin.pinned {
background: rgba(74, 240, 192, 0.15);
border-color: rgba(74, 240, 192, 0.4);
color: var(--color-primary, #4af0c0);
}
/* Related row — allow wrapping */
.memory-meta-row--related {
align-items: flex-start;
}
.memory-meta-row--related span:last-child {
flex-wrap: wrap;
display: flex;
gap: 2px;
}
/* ═══════════════════════════════════════════════════════
PROJECT MNEMOSYNE — SESSION ROOM HUD PANEL (#1171)
═══════════════════════════════════════════════════════ */
.session-room-panel {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
z-index: 125;
animation: sessionPanelIn 0.25s ease-out forwards;
}
.session-room-panel.session-panel-fade-out {
animation: sessionPanelOut 0.2s ease-in forwards !important;
}
@keyframes sessionPanelIn {
from { opacity: 0; transform: translateX(-50%) translateY(12px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes sessionPanelOut {
from { opacity: 1; }
to { opacity: 0; transform: translateX(-50%) translateY(10px); }
}
.session-room-panel-content {
min-width: 320px;
max-width: 480px;
background: rgba(8, 4, 28, 0.93);
backdrop-filter: blur(14px);
border: 1px solid rgba(123, 92, 255, 0.35);
border-radius: 12px;
padding: 14px 18px;
box-shadow: 0 0 32px rgba(123, 92, 255, 0.1), 0 8px 32px rgba(0, 0, 0, 0.45);
}
.session-room-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.session-room-icon {
font-size: 14px;
line-height: 1;
}
.session-room-title {
font-family: var(--font-display, monospace);
font-size: 11px;
letter-spacing: 0.18em;
color: #9b7cff;
text-transform: uppercase;
flex: 1;
}
.session-room-close {
background: none;
border: none;
color: rgba(255, 255, 255, 0.35);
cursor: pointer;
font-size: 14px;
padding: 0 2px;
line-height: 1;
transition: color 0.15s;
}
.session-room-close:hover {
color: rgba(255, 255, 255, 0.8);
}
.session-room-timestamp {
font-family: var(--font-display, monospace);
font-size: 13px;
color: #c8b4ff;
margin-bottom: 6px;
letter-spacing: 0.08em;
}
.session-room-fact-count {
font-size: 11px;
color: rgba(200, 180, 255, 0.55);
margin-bottom: 10px;
}
.session-room-facts {
display: flex;
flex-direction: column;
gap: 4px;
max-height: 140px;
overflow-y: auto;
}
.session-room-fact-item {
font-size: 11px;
color: rgba(220, 210, 255, 0.75);
padding: 4px 8px;
background: rgba(123, 92, 255, 0.07);
border-left: 2px solid rgba(123, 92, 255, 0.4);
border-radius: 0 4px 4px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.session-room-hint {
margin-top: 10px;
font-size: 10px;
color: rgba(200, 180, 255, 0.35);
text-align: center;
letter-spacing: 0.1em;
text-transform: uppercase;
}