Compare commits
1 Commits
fix/1602
...
burn/672-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b587e756e0 |
@@ -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
|
||||
|
||||
1
.github/BRANCH_PROTECTION.md
vendored
1
.github/BRANCH_PROTECTION.md
vendored
@@ -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
262
GENOME.md
Normal 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*
|
||||
@@ -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'; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
@@ -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)*
|
||||
@@ -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)")
|
||||
|
||||
@@ -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!');
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user