Compare commits

..

4 Commits

Author SHA1 Message Date
Alexander Whitestone
024e74defe WIP: issue #710 (mimo swarm)
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 14s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-13 00:51:31 +00:00
6c67002161 Merge pull request 'fix: [PORTAL] Rebuild the portal stack as Timmy → Reflex → Pilot on clean backlog' (#1324) from mimo/build/issue-672 into main
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Staging Verification Gate / verify-staging (push) Has been cancelled
2026-04-13 00:51:10 +00:00
43699c83cf Merge pull request 'fix: [IDENTITY] Make SOUL / Oath panel part of the main interaction loop' (#1316) from mimo/create/issue-709 into main
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Staging Verification Gate / verify-staging (push) Has been cancelled
2026-04-13 00:50:58 +00:00
Alexander Whitestone
91f0bcb034 fix: closes #672
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 15s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-13 00:50:38 +00:00
4 changed files with 225 additions and 3 deletions

32
app.js
View File

@@ -72,6 +72,9 @@ let workshopScanMat = null;
let workshopPanelRefreshTimer = 0;
let lastFocusedPortal = null;
// ═══ VISITOR / OPERATOR MODE ═══
let uiMode = 'visitor'; // 'visitor' | 'operator'
// ═══ NAVIGATION SYSTEM ═══
const NAV_MODES = ['walk', 'orbit', 'fly'];
let navModeIdx = 0;
@@ -778,6 +781,7 @@ async function init() {
enterPrompt.addEventListener('click', () => {
enterPrompt.classList.add('fade-out');
document.body.classList.add('visitor-mode');
document.getElementById('hud').style.display = 'block';
const erpPanel = document.getElementById('evennia-room-panel');
if (erpPanel) erpPanel.style.display = 'block';
@@ -1579,15 +1583,22 @@ function createPortal(config) {
// Label
const labelCanvas = document.createElement('canvas');
labelCanvas.width = 512;
labelCanvas.height = 64;
labelCanvas.height = 96;
const lctx = labelCanvas.getContext('2d');
lctx.font = 'bold 32px "Orbitron", sans-serif';
lctx.fillStyle = '#' + portalColor.getHexString();
lctx.textAlign = 'center';
lctx.fillText(`${config.name.toUpperCase()}`, 256, 42);
lctx.fillText(`${config.name.toUpperCase()}`, 256, 36);
// Role tag (timmy/reflex/pilot) — defines portal ownership boundary
if (config.role) {
const roleColors = { timmy: '#4af0c0', reflex: '#ff4466', pilot: '#ffd700' };
lctx.font = 'bold 18px "Orbitron", sans-serif';
lctx.fillStyle = roleColors[config.role] || '#888888';
lctx.fillText(config.role.toUpperCase(), 256, 68);
}
const labelTex = new THREE.CanvasTexture(labelCanvas);
const labelMat = new THREE.MeshBasicMaterial({ map: labelTex, transparent: true, side: THREE.DoubleSide });
const labelMesh = new THREE.Mesh(new THREE.PlaneGeometry(4, 0.5), labelMat);
const labelMesh = new THREE.Mesh(new THREE.PlaneGeometry(4, 0.75), labelMat);
labelMesh.position.y = 7.5;
group.add(labelMesh);
@@ -1863,6 +1874,18 @@ function createAmbientStructures() {
}
// ═══ NAVIGATION MODE ═══
// ═══ VISITOR / OPERATOR MODE TOGGLE ═══
function toggleUIMode() {
uiMode = uiMode === 'visitor' ? 'operator' : 'visitor';
document.body.classList.remove('visitor-mode', 'operator-mode');
document.body.classList.add(uiMode + '-mode');
const label = document.getElementById('mode-label');
const icon = document.querySelector('#mode-toggle-btn .hud-icon');
if (label) label.textContent = uiMode === 'visitor' ? 'VISITOR' : 'OPERATOR';
if (icon) icon.textContent = uiMode === 'visitor' ? '👁' : '⚙';
addChatMessage('system', `Switched to ${uiMode.toUpperCase()} mode.`);
}
function cycleNavMode() {
navModeIdx = (navModeIdx + 1) % NAV_MODES.length;
const mode = NAV_MODES[navModeIdx];
@@ -2059,6 +2082,7 @@ function setupControls() {
document.getElementById('portal-close-btn').addEventListener('click', closePortalOverlay);
document.getElementById('vision-close-btn').addEventListener('click', closeVisionOverlay);
document.getElementById('mode-toggle-btn').addEventListener('click', toggleUIMode);
document.getElementById('atlas-toggle-btn').addEventListener('click', openPortalAtlas);
document.getElementById('atlas-close-btn').addEventListener('click', closePortalAtlas);
initAtlasControls();
@@ -3088,6 +3112,8 @@ function populateAtlas() {
<div class="atlas-card-footer">
<div class="atlas-card-coord">X:${config.position.x} Z:${config.position.z}</div>
<div class="atlas-card-action">${actionLabel} →</div>
${config.role ? `<div class="atlas-card-role role-${config.role}">${config.role.toUpperCase()}</div>` : ''}
<div class="atlas-card-type">${config.destination?.type?.toUpperCase() || 'UNKNOWN'}</div>
</div>
`;

View File

@@ -155,6 +155,9 @@
<button id="soul-toggle-btn" class="hud-icon-btn" title="Timmy's SOUL">
<span class="hud-icon"></span>
<span class="hud-btn-label">SOUL</span>
<button id="mode-toggle-btn" class="hud-icon-btn mode-toggle" title="Toggle Mode">
<span class="hud-icon">👁</span>
<span class="hud-btn-label" id="mode-label">VISITOR</span>
</button>
<button id="atlas-toggle-btn" class="hud-icon-btn" title="Portal Atlas">
<span class="hud-icon">🌐</span>
@@ -375,6 +378,107 @@
</ul>
</div>
</div>
<div class="branch-policy" style="margin-top: 10px; font-size: 12px; color: #aaa;">
<strong>BRANCH PROTECTION POLICY</strong><br>
<ul style="margin:0; padding-left:15px;">
<li>• Require PR for merge ✅</li>
<li>• Require 1 approval ✅</li>
<li>• Dismiss stale approvals ✅</li>
<li>• Require CI ✅ (where available)</li>
<li>• Block force push ✅</li>
<li>• Block branch deletion ✅</li>
<li>• Weekly audit for unreviewed merges ✅</li>
</ul>
</div>
<div id="mem-palace-container" class="mem-palace-ui">
<div class="mem-palace-header">
<span id="mem-palace-status">MEMPALACE</span>
<button onclick="mineMemPalaceContent()" class="mem-palace-btn">Mine Chat</button>
</div>
<div class="mem-palace-stats">
<div>Compression: <span id="compression-ratio">--</span>x</div>
<div>Docs mined: <span id="docs-mined">0</span></div>
<div>AAAK size: <span id="aaak-size">0B</span></div>
</div>
<div class="mem-palace-logs" id="mem-palace-logs"></div>
</div>
<div class="default-reviewers" style="margin-top: 8px; font-size: 12px; color: #aaa;">
<strong>DEFAULT REVIEWERS</strong><br>
<ul style="margin:0; padding-left:15px;">
<li><span style="color:#4af0c0;">@perplexity</span> (QA gate on all repos)</li>
<li><span style="color:#7b5cff;">@Timmy</span> (owner gate on hermes-agent)</li>
</ul>
</div>
<div class="implementation-status" style="margin-top: 10px; font-size: 12px; color: #aaa;">
<strong>IMPLEMENTATION STATUS</strong><br>
<div style="margin-top: 5px; display: flex; flex-direction: column; gap: 2px;">
<div><span style="color:#4af0c0;">hermes-agent</span>: Require PR + 1 approval + CI ✅</div>
<div><span style="color:#7b5cff;">the-nexus</span>: Require PR + 1 approval ⚠️ (CI disabled)</div>
</div>
</div>
<div id="mem-palace-status" style="position:fixed; right:24px; top:64px; background:rgba(74,240,192,0.1); color:#4af0c0; padding:6px 12px; border-radius:4px; font-family:'Orbitron', sans-serif; font-size:10px; letter-spacing:0.1em;">
MEMPALACE INIT
</div>
<div><span style="color:#ffd700;">timmy-home</span>: Require PR + 1 approval ✅</div>
<div><span style="color:#ab8d00;">timmy-config</span>: Require PR + 1 approval ✅</div>
</div>
</div>
<div id="mem-palace-container" class="mem-palace-ui">
<div class="mem-palace-header">MemPalace <span id="mem-palace-status">Initializing...</span></div>
<div class="mem-palace-stats">
<div>Compression: <span id="compression-ratio">--</span>x</div>
<div>Docs mined: <span id="docs-mined">0</span></div>
<div>AAAK size: <span id="aaak-size">0B</span></div>
</div>
<div class="mem-palace-actions">
<button id="mine-now-btn" class="mem-palace-btn" onclick="mineChatToMemPalace()">Mine Chat</button>
<button class="mem-palace-btn" onclick="searchMemPalace()">Search</button>
</div>
<div id="mem-palace-logs" class="mem-palace-logs"></div>
</div>
<div id="mem-palace-controls" style="position:fixed; right:24px; top:54px; background:rgba(74,240,192,0.05); padding:4px 8px; font-family:'JetBrains Mono',monospace; font-size:11px; border-left:2px solid #4af0c0;">
<button onclick="mineMemPalace()">Mine Chat</button>
<button onclick="searchMemPalace()">Search</button>
</div>
<div id="mempalace-results" style="position:fixed; right:24px; top:84px; max-height:200px; overflow-y:auto; background:rgba(0,0,0,0.3); padding:8px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#e0f0ff; border-left:2px solid #4af0c0;"></div>
<div id="mem-palace-controls" style="position:fixed; right:24px; top:54px; background:rgba(74,240,192,0.05); padding:4px 8px; font-family:'JetBrains Mono',monospace; font-size:10px; border-left:2px solid #4af0c0;">
<button class="mem-palace-mining-btn" onclick="mineChatToMemPalace()">Mine Chat</button>
<button onclick="searchMemPalace()">Search</button>
</div>
<div id="mempalace-results" style="position:fixed; right:24px; top:84px; max-height:200px; overflow-y:auto; background:rgba(0,0,0,0.3); padding:8px; font-family:'JetBrains Mono',monospace; font-size:11px; color:#e0f0ff; border-left:2px solid #4af0c0;"></div>
```
index.html
```html
<div class="branch-policy" style="margin-top: 10px; font-size: 12px; color: #aaa;">
<strong>BRANCH PROTECTION POLICY</strong><br>
<ul style="margin:0; padding-left:15px;">
<li>• Require PR for merge ✅</li>
<li>• Require 1 approval ✅</li>
<li>• Dismiss stale approvals ✅</li>
<li>• Require CI ✅ (where available)</li>
<li>• Block force push ✅</li>
<li>• Block branch deletion ✅</li>
</ul>
</div>
<div class="default-reviewers" style="margin-top: 8px;">
<strong>DEFAULT REVIEWERS</strong><br>
<ul style="margin:0; padding-left:15px;">
<li><span style="color:#4af0c0;">@perplexity</span> (QA gate on all repos)</li>
<li><span style="color:#7b5cff;">@Timmy</span> (owner gate on hermes-agent)</li>
</ul>
</div>
<div class="implementation-status" style="margin-top: 10px;">
<strong>IMPLEMENTATION STATUS</strong><br>
<div style="margin-top: 5px; display: flex; flex-direction: column; gap: 2px;">
<div><span style="color:#4af0c0;">hermes-agent</span>: Require PR + 1 approval + CI ✅</div>
<div><span style="color:#7b5cff;">the-nexus</span>: Require PR + 1 approval ⚠<> (CI disabled)</div>
<div><span style="color:#ffd700;">timmy-home</span>: Require PR + 1 approval ✅</div>
<div><span style="color:#ab8d00;">timmy-config</span>: Require PR + 1 approval ✅</div>
</div>
</div>
</footer>
<script type="module" src="./app.js"></script>

View File

@@ -5,6 +5,7 @@
"description": "The Vvardenfell harness. Ash storms and ancient mysteries.",
"status": "online",
"color": "#ff6600",
"role": "pilot",
"position": { "x": 15, "y": 0, "z": -10 },
"rotation": { "y": -0.5 },
"portal_type": "game-world",
@@ -36,6 +37,7 @@
"description": "Calradia battle harness. Massive armies, tactical command.",
"status": "downloaded",
"color": "#ffd700",
"role": "pilot",
"position": { "x": -15, "y": 0, "z": -10 },
"rotation": { "y": 0.5 },
"portal_type": "game-world",
@@ -67,6 +69,7 @@
"description": "The creative harness. Build, script, and manifest.",
"status": "online",
"color": "#4af0c0",
"role": "timmy",
"position": { "x": 0, "y": 0, "z": -20 },
"rotation": { "y": 0 },
"destination": {
@@ -81,6 +84,7 @@
"description": "The repository of all knowledge. History, logs, and ancient data.",
"status": "online",
"color": "#0066ff",
"role": "timmy",
"position": { "x": 25, "y": 0, "z": 0 },
"rotation": { "y": -1.57 },
"destination": {
@@ -95,6 +99,7 @@
"description": "A sanctuary for reflection and digital peace.",
"status": "online",
"color": "#ffd700",
"role": "timmy",
"position": { "x": -25, "y": 0, "z": 0 },
"rotation": { "y": 1.57 },
"destination": {
@@ -109,6 +114,7 @@
"description": "The open nexus. A place for agents to gather and connect.",
"status": "online",
"color": "#4af0c0",
"role": "reflex",
"position": { "x": 15, "y": 0, "z": 10 },
"rotation": { "y": -2.5 },
"destination": {
@@ -123,6 +129,7 @@
"description": "The transition point. Entry and exit from the Nexus core.",
"status": "standby",
"color": "#ff4466",
"role": "reflex",
"position": { "x": -15, "y": 0, "z": 10 },
"rotation": { "y": 2.5 },
"destination": {

View File

@@ -384,6 +384,19 @@ canvas#nexus-canvas {
color: rgba(160, 184, 208, 0.6);
}
.atlas-card-role {
font-family: var(--font-display);
font-size: 9px;
font-weight: 700;
letter-spacing: 1px;
padding: 2px 6px;
border-radius: 3px;
text-transform: uppercase;
}
.atlas-card-role.role-timmy { color: #4af0c0; background: rgba(74, 240, 192, 0.12); border: 1px solid rgba(74, 240, 192, 0.3); }
.atlas-card-role.role-reflex { color: #ff4466; background: rgba(255, 68, 102, 0.12); border: 1px solid rgba(255, 68, 102, 0.3); }
.atlas-card-role.role-pilot { color: #ffd700; background: rgba(255, 215, 0, 0.12); border: 1px solid rgba(255, 215, 0, 0.3); }
.atlas-footer {
padding: 15px 30px;
border-top: 1px solid var(--color-border);
@@ -2484,3 +2497,75 @@ canvas#nexus-canvas {
.soul-link a:hover {
opacity: 0.7;
}
/* ═══════════════════════════════════════════════════════
VISITOR / OPERATOR MODE
═══════════════════════════════════════════════════════ */
.mode-toggle {
border-color: #4af0c0 !important;
}
.mode-toggle .hud-icon {
font-size: 16px;
}
#mode-label {
color: #4af0c0;
font-weight: 600;
}
/* Visitor mode: hide operator-only panels */
body.visitor-mode .gofai-hud,
body.visitor-mode .hud-debug,
body.visitor-mode .hud-agent-log,
body.visitor-mode .archive-health-dashboard,
body.visitor-mode .memory-feed,
body.visitor-mode .memory-inspect-panel,
body.visitor-mode .memory-connections-panel,
body.visitor-mode .memory-filter,
body.visitor-mode #mem-palace-container,
body.visitor-mode #mem-palace-controls,
body.visitor-mode #mempalace-results,
body.visitor-mode .nexus-footer {
display: none !important;
}
/* Visitor mode: simplify bannerlord status */
body.visitor-mode #bannerlord-status {
display: none !important;
}
/* Visitor mode: add a subtle visitor badge */
body.visitor-mode .hud-location::after {
content: '⬡ VISITOR';
margin-left: 12px;
font-size: 9px;
letter-spacing: 0.15em;
color: #4af0c0;
opacity: 0.7;
font-family: 'Orbitron', sans-serif;
vertical-align: middle;
}
/* Operator mode: add operator badge */
body.operator-mode .hud-location::after {
content: '⬢ OPERATOR';
margin-left: 12px;
font-size: 9px;
letter-spacing: 0.15em;
color: #ffd700;
opacity: 0.8;
font-family: 'Orbitron', sans-serif;
vertical-align: middle;
}
/* Operator mode: golden accent on toggle */
body.operator-mode .mode-toggle {
border-color: #ffd700 !important;
}
body.operator-mode #mode-label {
color: #ffd700;
}