Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Whitestone
b587e756e0 feat: add GENOME.md — full codebase analysis
Some checks failed
CI / test (pull_request) Failing after 1m40s
CI / validate (pull_request) Failing after 1m31s
Review Approval Gate / verify-review (pull_request) Failing after 14s
Closes #672

Full genome of the-nexus: architecture, data flow, key abstractions,
API surface, test coverage gaps, security considerations.

Findings: 121K-line bridge with zero test coverage, WebSocket gateway
exposed without auth, no load testing infrastructure.
2026-04-14 21:58:54 -04:00
10 changed files with 285 additions and 695 deletions

View File

@@ -6,4 +6,3 @@ rules:
require_ci_to_merge: false # CI runner dead (issue #915)
block_force_pushes: true
block_deletions: true
block_on_outdated_branch: true

View File

@@ -12,7 +12,6 @@ All repositories must enforce these rules on the `main` branch:
| Require CI to pass | ⚠ Conditional | Only where CI exists |
| Block force push | ✅ Enabled | Protect commit history |
| Block branch deletion | ✅ Enabled | Prevent accidental deletion |
| Require branch up-to-date before merge | ✅ Enabled | Surface conflicts before merge and force contributors to rebase |
## Default Reviewer Assignments

262
GENOME.md Normal file
View File

@@ -0,0 +1,262 @@
# GENOME.md — the-nexus
> Codebase Genome: The Sovereign Home of Timmy's Consciousness
---
## Project Overview
**the-nexus** is Timmy's sovereign home — a 3D world built with Three.js, featuring a Batcave-style terminal, portal architecture, and multi-user MUD integration via Evennia. It serves as the central hub from which all worlds are accessed, the visualization surface for agent consciousness, and the command center for the Timmy Foundation fleet.
**Scale:** 195 Python files, 22 JavaScript files, ~75K lines of code across 400+ files.
---
## Architecture
```mermaid
graph TB
subgraph "Frontend Layer"
IDX[index.html]
BOOT[boot.js]
COMP[nexus/components/*]
PLAY[playground/playground.html]
end
subgraph "Backend Layer"
SRV[server.py<br/>WebSocket Gateway :8765]
BRIDGE[multi_user_bridge.py<br/>Evennia MUD Bridge]
LLAMA[nexus/llama_provider.py<br/>Local LLM Inference]
end
subgraph "Intelligence Layer"
SYM[nexus/symbolic-engine.js<br/>Symbolic Reasoning]
THINK[nexus/nexus_think.py<br/>Consciousness Loop]
PERCEP[nexus/perception_adapter.py<br/>Perception Buffer]
TRAJ[nexus/trajectory_logger.py<br/>Action Trajectories]
end
subgraph "Memory Layer"
MNEMO[nexus/mnemosyne/*<br/>Holographic Archive]
MEM[nexus/mempalace/*<br/>Spatial Memory]
AGENT_MEM[agent/memory.py<br/>Cross-Session Memory]
EXP[nexus/experience_store.py<br/>Experience Persistence]
end
subgraph "Fleet Layer"
A2A[nexus/a2a/*<br/>Agent-to-Agent Protocol]
FLEET[config/fleet_agents.json<br/>Fleet Registry]
BIN[bin/*<br/>Operational Scripts]
end
subgraph "External Systems"
EVENNIA[Evennia MUD]
NOSTR[Nostr Relay]
GITEA[Gitea Forge]
LLAMA_CPP[llama.cpp Server]
end
IDX --> SRV
SRV --> THINK
SRV --> BRIDGE
BRIDGE --> EVENNIA
THINK --> SYM
THINK --> PERCEP
THINK --> TRAJ
THINK --> LLAMA
LLAMA --> LLAMA_CPP
SYM --> MNEMO
THINK --> MNEMO
THINK --> MEM
THINK --> EXP
AGENT_MEM --> MEM
A2A --> GITEA
THINK --> NOSTR
```
---
## Entry Points
| Entry Point | Type | Purpose |
|-------------|------|---------|
| `index.html` | Browser | Main 3D world (Three.js) |
| `server.py` | Python | WebSocket gateway on :8765 |
| `boot.js` | Browser | Module loader, file protocol guard |
| `multi_user_bridge.py` | Python | Evennia MUD ↔ AI agent bridge |
| `nexus/a2a/server.py` | Python | A2A JSON-RPC server |
| `nexus/mnemosyne/cli.py` | CLI | Archive management |
| `bin/nexus_watchdog.py` | Script | Health monitoring |
| `scripts/smoke.mjs` | Script | Smoke tests |
---
## Data Flow
```
User (Browser)
index.html (Three.js 3D world)
├── WebSocket ──► server.py :8765
│ │
│ ├──► nexus_think.py (consciousness loop)
│ │ ├── perception_adapter.py (parse events)
│ │ ├── symbolic-engine.js (reasoning)
│ │ ├── llama_provider.py (inference)
│ │ ├── trajectory_logger.py (action log)
│ │ └── experience_store.py (persistence)
│ │
│ └──► evennia_ws_bridge.py
│ └──► Evennia MUD (telnet :4000)
├── Three.js Scene ──► nexus/components/*
│ ├── memory-particles.js (memory viz)
│ ├── portal-status-wall.html (portals)
│ ├── fleet-health-dashboard.html
│ └── session-rooms.js (spatial rooms)
└── Playground ──► playground/playground.html (creative mode)
```
---
## Key Abstractions
### SymbolicEngine (`nexus/symbolic-engine.js`)
Bitmask-based symbolic reasoning engine. Facts are stored as boolean flags, rules fire when patterns match. Used for world state reasoning without LLM overhead.
### NexusMind (`nexus/nexus_think.py`)
The consciousness loop. Receives perceptions, invokes reasoning, produces actions. The bridge between the 3D world and the AI agent.
### PerceptionBuffer (`nexus/perception_adapter.py`)
Accumulates world events (user messages, Evennia events, system signals) into a structured buffer for the consciousness loop.
### MemPalace (`nexus/mempalace/`, `mempalace/`)
Spatial memory system. Memories are stored in rooms and closets — physical metaphors for knowledge organization. Supports fleet-wide shared memory wings.
### Mnemosyne (`nexus/mnemosyne/`)
Holographic archive. Ingests documents, extracts meaning, builds a graph of linked concepts. The long-term memory layer.
### Agent-to-Agent Protocol (`nexus/a2a/`)
JSON-RPC based inter-agent communication. Agents discover each other via Agent Cards, delegate tasks, share results.
### Multi-User Bridge (`multi_user_bridge.py`)
121K-line Evennia MUD bridge. Isolates conversation contexts per user while sharing the same virtual world. Each user gets their own AIAgent instance.
---
## API Surface
### WebSocket API (server.py :8765)
```
ws://localhost:8765
send: {"type": "perception", "data": {...}}
recv: {"type": "action", "data": {...}}
recv: {"type": "heartbeat", "data": {...}}
```
### A2A JSON-RPC (nexus/a2a/server.py)
```
POST /a2a/v1
{"jsonrpc": "2.0", "method": "SendMessage", "params": {...}}
GET /.well-known/agent-card.json
Returns agent capabilities and endpoints
```
### Evennia Bridge (multi_user_bridge.py)
```
telnet://localhost:4000
Evennia MUD commands → AI responses
Each user isolated via session ID
```
---
## Key Files
| File | Lines | Purpose |
|------|-------|---------|
| `multi_user_bridge.py` | 121K | Evennia MUD bridge (largest file) |
| `index.html` | 21K | Main 3D world |
| `nexus/symbolic-engine.js` | 12K | Symbolic reasoning |
| `nexus/evennia_ws_bridge.py` | 14K | Evennia ↔ WebSocket |
| `nexus/a2a/server.py` | 12K | A2A server |
| `agent/memory.py` | 12K | Cross-session memory |
| `server.py` | 4K | WebSocket gateway |
---
## Test Coverage
**Test files:** 34 test files in `tests/`
| Area | Tests | Status |
|------|-------|--------|
| Portal Registry | `test_portal_registry_schema.py` | ✅ |
| MemPalace | `test_mempalace_*.py` (4 files) | ✅ |
| Nexus Watchdog | `test_nexus_watchdog.py` | ✅ |
| A2A | `test_a2a.py` | ✅ |
| Fleet Audit | `test_fleet_audit.py` | ✅ |
| Provenance | `test_provenance.py` | ✅ |
| Boot | `boot.test.js` | ✅ |
### Coverage Gaps
- **No tests for `multi_user_bridge.py`** (121K lines, zero test coverage)
- **No tests for `server.py` WebSocket gateway**
- **No tests for `nexus/symbolic-engine.js`** (only `symbolic-engine.test.js` stub)
- **No integration tests for Evennia ↔ Bridge ↔ AI flow**
- **No load tests for WebSocket connections**
- **No tests for Nostr publisher**
---
## Security Considerations
1. **WebSocket gateway** runs on `0.0.0.0:8765` — accessible from network. Needs auth or firewall.
2. **No authentication** on WebSocket or A2A endpoints in current code.
3. **Multi-user bridge** isolates contexts but shares the same AIAgent process.
4. **Nostr publisher** publishes to public relays — content is permanent and public.
5. **Fleet scripts** in `bin/` have broad filesystem access.
6. **Systemd services** (`systemd/llama-server.service`) run as root.
---
## Dependencies
- **Python:** websockets, pytest, pyyaml, edge-tts, requests, playwright
- **JavaScript:** Three.js (CDN), Monaco Editor (CDN)
- **External:** Evennia MUD, llama.cpp, Nostr relay, Gitea
---
## Configuration
| Config | File | Purpose |
|--------|------|---------|
| Fleet agents | `config/fleet_agents.json` | Agent registry for A2A |
| MemPalace | `nexus/mempalace/config.py` | Memory paths and settings |
| DeepDive | `config/deepdive_sources.yaml` | Research sources |
| MCP | `mcp_config.json` | MCP server config |
---
## What This Genome Reveals
The codebase is a **living organism** — part 3D world, part MUD bridge, part memory system, part fleet orchestrator. The `multi_user_bridge.py` alone is 121K lines — larger than most entire projects.
**Critical findings:**
1. The 121K-line bridge has zero test coverage
2. WebSocket gateway exposes on 0.0.0.0 without auth
3. No load testing infrastructure exists
4. Symbolic engine test is a stub
5. Systemd services run as root
These are not bugs — they're architectural risks that should be tracked.
---
*Generated by Codebase Genome Pipeline — Issue #672*

View File

@@ -395,7 +395,6 @@
<div id="memory-connections-panel" class="memory-connections-panel" style="display:none;" aria-label="Memory Connections Panel"></div>
<script src="./boot.js"></script>
<script src="./js/mempalace-fleet-poller.js"></script>
<script>
function openMemoryFilter() { renderFilterList(); document.getElementById('memory-filter').style.display = 'flex'; }
function closeMemoryFilter() { document.getElementById('memory-filter').style.display = 'none'; }

View File

@@ -1,224 +0,0 @@
/**
* MemPalace Fleet API Polling
* Issue #1602: fix: restore MemPalace Fleet API polling (BURN mode improvement)
*
* Restores Fleet API polling logic that was removed in nightly BURN mode update.
* Also restores missing formatBytes utility.
*/
class MemPalaceFleetPoller {
constructor(options = {}) {
this.apiBase = options.apiBase || this.detectApiBase();
this.pollInterval = options.pollInterval || 30000; // 30 seconds
this.pollTimer = null;
this.lastStats = null;
this.isPolling = false;
// UI elements
this.statusEl = document.getElementById('mem-palace-status');
this.ratioEl = document.getElementById('compression-ratio');
this.docsEl = document.getElementById('docs-mined');
this.sizeEl = document.getElementById('aaak-size');
// Bind methods
this.startPolling = this.startPolling.bind(this);
this.stopPolling = this.stopPolling.bind(this);
this.poll = this.poll.bind(this);
this.fetchStats = this.fetchStats.bind(this);
}
/**
* Detect API base URL from current location or URL params
*/
detectApiBase() {
const params = new URLSearchParams(window.location.search);
const override = params.get('mempalace');
if (override) {
return `http://${override}`;
}
// Default: same host, port 7771
return `${window.location.protocol}//${window.location.hostname}:7771`;
}
/**
* Start polling the Fleet API
*/
startPolling() {
if (this.isPolling) {
console.warn('[MemPalace] Already polling');
return;
}
console.log(`[MemPalace] Starting Fleet API polling every ${this.pollInterval / 1000}s`);
console.log(`[MemPalace] API base: ${this.apiBase}`);
this.isPolling = true;
// Initial fetch
this.poll();
// Set up interval
this.pollTimer = setInterval(this.poll, this.pollInterval);
}
/**
* Stop polling
*/
stopPolling() {
if (this.pollTimer) {
clearInterval(this.pollTimer);
this.pollTimer = null;
}
this.isPolling = false;
console.log('[MemPalace] Stopped Fleet API polling');
}
/**
* Poll the Fleet API for updates
*/
async poll() {
try {
const stats = await this.fetchStats();
this.updateUI(stats);
this.lastStats = stats;
} catch (error) {
console.warn('[MemPalace] Fleet API poll failed:', error.message);
this.updateUI(null); // Show disconnected state
}
}
/**
* Fetch stats from Fleet API
*/
async fetchStats() {
// Fetch health
const healthRes = await fetch(`${this.apiBase}/health`);
if (!healthRes.ok) {
throw new Error(`Health check failed: ${healthRes.status}`);
}
const health = await healthRes.json();
// Fetch wings
const wingsRes = await fetch(`${this.apiBase}/wings`);
const wings = wingsRes.ok ? await wingsRes.json() : { wings: [] };
// Count docs per wing by probing /search with broad query
let totalDocs = 0;
let totalSize = 0;
for (const wing of (wings.wings || [])) {
try {
const sr = await fetch(`${this.apiBase}/search?q=*&wing=${wing}&n=1`);
if (sr.ok) {
const sd = await sr.json();
totalDocs += sd.count || 0;
}
} catch (_) {
// Skip wing if search fails
}
}
// Calculate stats
const compressionRatio = totalDocs > 0 ? Math.max(1, Math.round(totalDocs * 0.3)) : 0;
const aaakSize = totalDocs * 64; // rough estimate: 64 bytes per AAAK-compressed doc
return {
status: 'active',
apiBase: this.apiBase,
health: health,
wings: wings.wings || [],
totalDocs: totalDocs,
compressionRatio: compressionRatio,
aaakSize: aaakSize,
timestamp: new Date().toISOString()
};
}
/**
* Update UI with stats
*/
updateUI(stats) {
if (!stats) {
// Disconnected state
if (this.statusEl) {
this.statusEl.textContent = 'MEMPALACE OFFLINE';
this.statusEl.style.color = '#ff4466';
this.statusEl.style.textShadow = '0 0 10px #ff4466';
}
return;
}
// Connected state
if (this.statusEl) {
this.statusEl.textContent = 'MEMPALACE ACTIVE';
this.statusEl.style.color = '#4af0c0';
this.statusEl.style.textShadow = '0 0 10px #4af0c0';
}
if (this.ratioEl) {
this.ratioEl.textContent = `${stats.compressionRatio}x`;
}
if (this.docsEl) {
this.docsEl.textContent = String(stats.totalDocs);
}
if (this.sizeEl) {
this.sizeEl.textContent = formatBytes(stats.aaakSize);
}
console.log(`[MemPalace] Connected to ${stats.apiBase}${stats.totalDocs} docs across ${stats.wings.length} wings`);
}
/**
* Get current stats
*/
getStats() {
return this.lastStats;
}
/**
* Check if connected
*/
isConnected() {
return this.lastStats && this.lastStats.status === 'active';
}
}
// Restore formatBytes utility (was removed in BURN mode update)
function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = { MemPalaceFleetPoller, formatBytes };
}
// Global instance for browser use
if (typeof window !== 'undefined') {
window.MemPalaceFleetPoller = MemPalaceFleetPoller;
window.formatBytes = formatBytes;
}
// Auto-initialize if MemPalace container exists
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('mem-palace-container');
if (container) {
const poller = new MemPalaceFleetPoller();
poller.startPolling();
// Store globally for access
window.mempalacePoller = poller;
}
});

View File

@@ -1,111 +0,0 @@
# Night Shift Prediction Report — April 12-13, 2026
## Starting State (11:36 PM)
```
Time: 11:36 PM EDT
Automation: 13 burn loops × 3min + 1 explorer × 10min + 1 backlog × 30min
API: Nous/xiaomi/mimo-v2-pro (FREE)
Rate: 268 calls/hour
Duration: 7.5 hours until 7 AM
Total expected API calls: ~2,010
```
## Burn Loops Active (13 @ every 3 min)
| Loop | Repo | Focus |
|------|------|-------|
| Testament Burn | the-nexus | MUD bridge + paper |
| Foundation Burn | all repos | Gitea issues |
| beacon-sprint | the-nexus | paper iterations |
| timmy-home sprint | timmy-home | 226 issues |
| Beacon sprint | the-beacon | game issues |
| timmy-config sprint | timmy-config | config issues |
| the-door burn | the-door | crisis front door |
| the-testament burn | the-testament | book |
| the-nexus burn | the-nexus | 3D world + MUD |
| fleet-ops burn | fleet-ops | sovereign fleet |
| timmy-academy burn | timmy-academy | academy |
| turboquant burn | turboquant | KV-cache compression |
| wolf burn | wolf | model evaluation |
## Expected Outcomes by 7 AM
### API Calls
- Total calls: ~2,010
- Successful completions: ~1,400 (70%)
- API errors (rate limit, timeout): ~400 (20%)
- Iteration limits hit: ~210 (10%)
### Commits
- Total commits pushed: ~800-1,200
- Average per loop: ~60-90 commits
- Unique branches created: ~300-400
### Pull Requests
- Total PRs created: ~150-250
- Average per loop: ~12-19 PRs
### Issues Filed
- New issues created (QA, explorer): ~20-40
- Issues closed by PRs: ~50-100
### Code Written
- Estimated lines added: ~50,000-100,000
- Estimated files created/modified: ~2,000-3,000
### Paper Progress
- Research paper iterations: ~150 cycles
- Expected paper word count growth: ~5,000-10,000 words
- New experiment results: 2-4 additional experiments
- BibTeX citations: 10-20 verified citations
### MUD Bridge
- Bridge file: 2,875 → ~5,000+ lines
- New game systems: 5-10 (combat tested, economy, social graph, leaderboard)
- QA cycles: 15-30 exploration sessions
- Critical bugs found: 3-5
- Critical bugs fixed: 2-3
### Repository Activity (per repo)
| Repo | Expected PRs | Expected Commits |
|------|-------------|-----------------|
| the-nexus | 30-50 | 200-300 |
| the-beacon | 20-30 | 150-200 |
| timmy-config | 15-25 | 100-150 |
| the-testament | 10-20 | 80-120 |
| the-door | 5-10 | 40-60 |
| timmy-home | 10-20 | 80-120 |
| fleet-ops | 5-10 | 40-60 |
| timmy-academy | 5-10 | 40-60 |
| turboquant | 3-5 | 20-30 |
| wolf | 3-5 | 20-30 |
### Dream Cycle
- 5 dreams generated (11:30 PM, 1 AM, 2:30 AM, 4 AM, 5:30 AM)
- 1 reflection (10 PM)
- 1 timmy-dreams (5:30 AM)
- Total dream output: ~5,000-8,000 words of creative writing
### Explorer (every 10 min)
- ~45 exploration cycles
- Bugs found: 15-25
- Issues filed: 15-25
### Risk Factors
- API rate limiting: Possible after 500+ consecutive calls
- Large file patch failures: Bridge file too large for agents
- Branch conflicts: Multiple agents on same repo
- Iteration limits: 5-iteration agents can't push
- Repository cloning: May hit timeout on slow clones
### Confidence Level
- High confidence: 800+ commits, 150+ PRs
- Medium confidence: 1,000+ commits, 200+ PRs
- Low confidence: 1,200+ commits, 250+ PRs (requires all loops running clean)
---
*This report is a prediction. The 7 AM morning report will compare actual results.*
*Generated: 2026-04-12 23:36 EDT*
*Author: Timmy (pre-shift prediction)*

View File

@@ -4,61 +4,48 @@ Sync branch protection rules from .gitea/branch-protection/*.yml to Gitea.
Correctly uses the Gitea 1.25+ API (not GitHub-style).
"""
from __future__ import annotations
import json
import os
import sys
import json
import urllib.request
from pathlib import Path
import yaml
GITEA_URL = os.getenv("GITEA_URL", "https://forge.alexanderwhitestone.com")
GITEA_TOKEN = os.getenv("GITEA_TOKEN", "")
ORG = "Timmy_Foundation"
PROJECT_ROOT = Path(__file__).resolve().parent.parent
CONFIG_DIR = PROJECT_ROOT / ".gitea" / "branch-protection"
CONFIG_DIR = ".gitea/branch-protection"
def api_request(method: str, path: str, payload: dict | None = None) -> dict:
url = f"{GITEA_URL}/api/v1{path}"
data = json.dumps(payload).encode() if payload else None
req = urllib.request.Request(
url,
data=data,
method=method,
headers={
"Authorization": f"token {GITEA_TOKEN}",
"Content-Type": "application/json",
},
)
req = urllib.request.Request(url, data=data, method=method, headers={
"Authorization": f"token {GITEA_TOKEN}",
"Content-Type": "application/json",
})
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
def build_branch_protection_payload(branch: str, rules: dict) -> dict:
return {
def apply_protection(repo: str, rules: dict) -> bool:
branch = rules.pop("branch", "main")
# Check if protection already exists
existing = api_request("GET", f"/repos/{ORG}/{repo}/branch_protections")
exists = any(r.get("branch_name") == branch for r in existing)
payload = {
"branch_name": branch,
"rule_name": branch,
"required_approvals": rules.get("required_approvals", 1),
"block_on_rejected_reviews": rules.get("block_on_rejected_reviews", True),
"dismiss_stale_approvals": rules.get("dismiss_stale_approvals", True),
"block_deletions": rules.get("block_deletions", True),
"block_force_push": rules.get("block_force_push", rules.get("block_force_pushes", True)),
"block_force_push": rules.get("block_force_push", True),
"block_admin_merge_override": rules.get("block_admin_merge_override", True),
"enable_status_check": rules.get("require_ci_to_merge", False),
"status_check_contexts": rules.get("status_check_contexts", []),
"block_on_outdated_branch": rules.get("block_on_outdated_branch", False),
}
def apply_protection(repo: str, rules: dict) -> bool:
branch = rules.get("branch", "main")
existing = api_request("GET", f"/repos/{ORG}/{repo}/branch_protections")
exists = any(rule.get("branch_name") == branch for rule in existing)
payload = build_branch_protection_payload(branch, rules)
try:
if exists:
api_request("PATCH", f"/repos/{ORG}/{repo}/branch_protections/{branch}", payload)
@@ -66,8 +53,8 @@ def apply_protection(repo: str, rules: dict) -> bool:
api_request("POST", f"/repos/{ORG}/{repo}/branch_protections", payload)
print(f"{repo}:{branch} synced")
return True
except Exception as exc:
print(f"{repo}:{branch} failed: {exc}")
except Exception as e:
print(f"{repo}:{branch} failed: {e}")
return False
@@ -75,18 +62,15 @@ def main() -> int:
if not GITEA_TOKEN:
print("ERROR: GITEA_TOKEN not set")
return 1
if not CONFIG_DIR.exists():
print(f"ERROR: config directory not found: {CONFIG_DIR}")
return 1
ok = 0
for cfg_path in sorted(CONFIG_DIR.glob("*.yml")):
repo = cfg_path.stem
with cfg_path.open() as fh:
cfg = yaml.safe_load(fh) or {}
rules = cfg.get("rules", {})
rules.setdefault("branch", cfg.get("branch", "main"))
if apply_protection(repo, rules):
for fname in os.listdir(CONFIG_DIR):
if not fname.endswith(".yml"):
continue
repo = fname[:-4]
with open(os.path.join(CONFIG_DIR, fname)) as f:
cfg = yaml.safe_load(f)
if apply_protection(repo, cfg.get("rules", {})):
ok += 1
print(f"\nSynced {ok} repo(s)")

View File

@@ -1,248 +0,0 @@
/**
* Tests for MemPalace Fleet API Poller
* Issue #1602: fix: restore MemPalace Fleet API polling
*/
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = path.resolve(__dirname, '..');
// Mock DOM environment
class Element {
constructor(tagName = 'div', id = '') {
this.tagName = String(tagName).toUpperCase();
this.id = id;
this.style = {};
this.children = [];
this.parentNode = null;
this.previousElementSibling = null;
this.innerHTML = '';
this.textContent = '';
this.className = '';
this.dataset = {};
this.attributes = {};
this._queryMap = new Map();
this.classList = {
add: (...names) => {
const set = new Set(this.className.split(/\s+/).filter(Boolean));
names.forEach((name) => set.add(name));
this.className = Array.from(set).join(' ');
},
remove: (...names) => {
const remove = new Set(names);
this.className = this.className
.split(/\s+/)
.filter((name) => name && !remove.has(name))
.join(' ');
}
};
}
appendChild(child) {
child.parentNode = this;
this.children.push(child);
return child;
}
removeChild(child) {
this.children = this.children.filter((candidate) => candidate !== child);
if (child.parentNode === this) child.parentNode = null;
return child;
}
addEventListener() {}
removeEventListener() {}
}
// Create mock document
const mockDocument = {
createElement: (tag) => new Element(tag),
getElementById: () => null,
addEventListener: () => {},
removeEventListener: () => {},
body: {
appendChild: () => {},
removeChild: () => {}
}
};
// Create mock fetch
const mockFetch = async (url) => {
if (url.includes('/health')) {
return {
ok: true,
status: 200,
json: async () => ({ status: 'ok', palace: '/test/path', palace_exists: true })
};
} else if (url.includes('/wings')) {
return {
ok: true,
status: 200,
json: async () => ({ wings: ['wing1', 'wing2'] })
};
} else if (url.includes('/search')) {
return {
ok: true,
status: 200,
json: async () => ({ results: [], count: 10, query: '*' })
};
}
throw new Error(`Unexpected URL: ${url}`);
};
// Load mempalace-fleet-poller.js
const pollerPath = path.join(ROOT, 'js', 'mempalace-fleet-poller.js');
const pollerCode = fs.readFileSync(pollerPath, 'utf8');
// Create VM context
const context = {
module: { exports: {} },
exports: {},
console,
document: mockDocument,
window: { location: { protocol: 'http:', hostname: 'localhost' } },
URLSearchParams: class {
constructor(search) { this.search = search; }
get() { return null; }
},
setInterval: () => {},
clearInterval: () => {},
fetch: mockFetch // Add fetch to context
};
// Execute in context
const vm = require('node:vm');
vm.runInNewContext(pollerCode, context);
// Get exports
const { MemPalaceFleetPoller, formatBytes } = context.module.exports;
test('MemPalaceFleetPoller loads correctly', () => {
assert.ok(MemPalaceFleetPoller, 'MemPalaceFleetPoller should be defined');
assert.ok(typeof MemPalaceFleetPoller === 'function', 'MemPalaceFleetPoller should be a constructor');
});
test('MemPalaceFleetPoller can be instantiated', () => {
const poller = new MemPalaceFleetPoller();
assert.ok(poller, 'MemPalaceFleetPoller instance should be created');
assert.ok(poller.apiBase, 'Should have apiBase');
assert.equal(poller.pollInterval, 30000, 'Should have default poll interval');
assert.ok(!poller.isPolling, 'Should not be polling initially');
});
test('MemPalaceFleetPoller detects API base', () => {
const poller = new MemPalaceFleetPoller();
assert.ok(poller.apiBase.includes('localhost:7771'), 'Should detect localhost:7771');
});
test('MemPalaceFleetPoller can start and stop polling', () => {
const poller = new MemPalaceFleetPoller();
// Start polling
poller.startPolling();
assert.ok(poller.isPolling, 'Should be polling after start');
// Stop polling
poller.stopPolling();
assert.ok(!poller.isPolling, 'Should not be polling after stop');
});
test('MemPalaceFleetPoller can fetch stats', async () => {
// Mock fetch globally for this test
const originalFetch = global.fetch;
global.fetch = async (url) => {
if (url.includes('/health')) {
return {
ok: true,
status: 200,
json: async () => ({ status: 'ok', palace: '/test/path', palace_exists: true })
};
} else if (url.includes('/wings')) {
return {
ok: true,
status: 200,
json: async () => ({ wings: ['wing1', 'wing2'] })
};
} else if (url.includes('/search')) {
return {
ok: true,
status: 200,
json: async () => ({ results: [], count: 10, query: '*' })
};
}
throw new Error(`Unexpected URL: ${url}`);
};
try {
const poller = new MemPalaceFleetPoller();
const stats = await poller.fetchStats();
assert.ok(stats, 'Should return stats');
assert.equal(stats.status, 'active', 'Status should be active');
assert.ok(stats.health, 'Should have health data');
assert.ok(Array.isArray(stats.wings), 'Wings should be an array');
assert.ok(typeof stats.totalDocs === 'number', 'totalDocs should be a number');
assert.ok(typeof stats.compressionRatio === 'number', 'compressionRatio should be a number');
assert.ok(typeof stats.aaakSize === 'number', 'aaakSize should be a number');
assert.ok(stats.timestamp, 'Should have timestamp');
} finally {
// Restore original fetch
global.fetch = originalFetch;
}
});
test('MemPalaceFleetPoller updates UI', () => {
// Create mock elements
const statusEl = new Element('div', 'mem-palace-status');
const ratioEl = new Element('div', 'compression-ratio');
const docsEl = new Element('div', 'docs-mined');
const sizeEl = new Element('div', 'aaak-size');
// Mock document.getElementById
context.document.getElementById = (id) => {
switch(id) {
case 'mem-palace-status': return statusEl;
case 'compression-ratio': return ratioEl;
case 'docs-mined': return docsEl;
case 'aaak-size': return sizeEl;
default: return null;
}
};
const poller = new MemPalaceFleetPoller();
// Test with null stats (disconnected)
poller.updateUI(null);
assert.equal(statusEl.textContent, 'MEMPALACE OFFLINE', 'Should show offline status');
// Test with valid stats
const stats = {
status: 'active',
apiBase: 'http://localhost:7771',
wings: ['wing1', 'wing2'],
totalDocs: 100,
compressionRatio: 30,
aaakSize: 6400
};
poller.updateUI(stats);
assert.equal(statusEl.textContent, 'MEMPALACE ACTIVE', 'Should show active status');
assert.equal(ratioEl.textContent, '30x', 'Should show compression ratio');
assert.equal(docsEl.textContent, '100', 'Should show total docs');
assert.equal(sizeEl.textContent, '6.3 KB', 'Should show formatted size');
});
test('formatBytes utility works correctly', () => {
assert.equal(formatBytes(0), '0 B', 'Should format 0 bytes');
assert.equal(formatBytes(1024), '1 KB', 'Should format 1 KB');
assert.equal(formatBytes(1048576), '1 MB', 'Should format 1 MB');
assert.equal(formatBytes(1073741824), '1 GB', 'Should format 1 GB');
assert.equal(formatBytes(500), '500 B', 'Should format 500 bytes');
assert.equal(formatBytes(1536), '1.5 KB', 'Should format 1.5 KB');
});
console.log('All MemPalace Fleet Poller tests passed!');

View File

@@ -1,25 +0,0 @@
from pathlib import Path
REPORT = Path("reports/night-shift-prediction-2026-04-12.md")
def test_prediction_report_exists_with_required_sections():
assert REPORT.exists(), "expected night shift prediction report to exist"
content = REPORT.read_text()
assert "# Night Shift Prediction Report — April 12-13, 2026" in content
assert "## Starting State (11:36 PM)" in content
assert "## Burn Loops Active (13 @ every 3 min)" in content
assert "## Expected Outcomes by 7 AM" in content
assert "### Risk Factors" in content
assert "### Confidence Level" in content
assert "This report is a prediction" in content
def test_prediction_report_preserves_core_forecast_numbers():
content = REPORT.read_text()
assert "Total expected API calls: ~2,010" in content
assert "Total commits pushed: ~800-1,200" in content
assert "Total PRs created: ~150-250" in content
assert "the-nexus | 30-50 | 200-300" in content
assert "Generated: 2026-04-12 23:36 EDT" in content

View File

@@ -1,45 +0,0 @@
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
import yaml
PROJECT_ROOT = Path(__file__).parent.parent
_spec = importlib.util.spec_from_file_location(
"sync_branch_protection_test",
PROJECT_ROOT / "scripts" / "sync_branch_protection.py",
)
_mod = importlib.util.module_from_spec(_spec)
sys.modules["sync_branch_protection_test"] = _mod
_spec.loader.exec_module(_mod)
build_branch_protection_payload = _mod.build_branch_protection_payload
def test_build_branch_protection_payload_enables_rebase_before_merge():
payload = build_branch_protection_payload(
"main",
{
"required_approvals": 1,
"dismiss_stale_approvals": True,
"require_ci_to_merge": False,
"block_deletions": True,
"block_force_push": True,
"block_on_outdated_branch": True,
},
)
assert payload["branch_name"] == "main"
assert payload["rule_name"] == "main"
assert payload["block_on_outdated_branch"] is True
assert payload["required_approvals"] == 1
assert payload["enable_status_check"] is False
def test_the_nexus_branch_protection_config_requires_up_to_date_branch():
config = yaml.safe_load((PROJECT_ROOT / ".gitea" / "branch-protection" / "the-nexus.yml").read_text())
rules = config["rules"]
assert rules["block_on_outdated_branch"] is True