Files
timmy-home/research/big-brain/the-nexus-context-bundle.md
Alexander Whitestone 303ae44411
Some checks failed
Smoke Test / smoke (push) Failing after 11s
feat: add big-brain nexus audit artifact (#656)
Merge PR #656
2026-04-14 22:18:20 +00:00

115 KiB
Raw Blame History

Audit Context Bundle — Timmy_Foundation/the-nexus

Generated: 2026-04-14T01:27:12.408470+00:00 Repo root: /private/tmp/the-nexus-audit Text files indexed: 403

File manifest

  • .gitea.yml — 17 lines, 462 bytes
  • .gitea/branch-protection.yml — 55 lines, 1313 bytes
  • .gitea/branch-protection/hermes-agent.yml — 8 lines, 184 bytes
  • .gitea/branch-protection/the-nexus.yml — 8 lines, 215 bytes
  • .gitea/branch-protection/timmy-config.yml — 8 lines, 198 bytes
  • .gitea/branch-protection/timmy-home.yml — 8 lines, 204 bytes
  • .gitea/branch_protection.yml — 72 lines, 2044 bytes
  • .gitea/branch_protections.yml — 35 lines, 758 bytes
  • .gitea/protected_branches.yaml — 8 lines, 199 bytes
  • .gitea/workflows/ci.yml — 99 lines, 3052 bytes
  • .gitea/workflows/deploy.yml — 34 lines, 1055 bytes
  • .gitea/workflows/review_gate.yml — 21 lines, 553 bytes
  • .gitea/workflows/staging_gate.yml — 20 lines, 517 bytes
  • .gitea/workflows/weekly-audit.yml — 34 lines, 1094 bytes
  • .githooks/stale-pr-closer.sh — 201 lines, 7597 bytes
  • .github/BRANCH_PROTECTION.md — 42 lines, 1414 bytes
  • .github/ISSUE_TEMPLATE.md — 26 lines, 476 bytes
  • .github/pull_request_template.md — 65 lines, 1608 bytes
  • .github/workflows/ci.yml — 19 lines, 349 bytes
  • .github/workflows/enforce-branch-policy.yml — 49 lines, 1657 bytes
  • BROWSER_CONTRACT.md — 83 lines, 3518 bytes
  • CLAUDE.md — 92 lines, 3390 bytes
  • CONTRIBUTING.md — 155 lines, 4701 bytes
  • DEVELOPMENT.md — 23 lines, 664 bytes
  • Dockerfile — 21 lines, 407 bytes
  • EVENNIA_NEXUS_EVENT_PROTOCOL.md — 107 lines, 2677 bytes
  • FINDINGS-issue-1047.md — 203 lines, 11283 bytes
  • FINDINGS-issue-801.md — 305 lines, 11756 bytes
  • FIRST_LIGHT_REPORT.md — 81 lines, 4354 bytes
  • FIRST_LIGHT_REPORT_EVENNIA_BRIDGE.md — 49 lines, 1829 bytes
  • GAMEPORTAL_PROTOCOL.md — 208 lines, 7319 bytes
  • INVESTIGATION_ISSUE_1145.md — 72 lines, 3501 bytes
  • LEGACY_MATRIX_AUDIT.md — 178 lines, 12584 bytes
  • POLICY.md — 94 lines, 3278 bytes
  • README.md — 167 lines, 5323 bytes
  • SOUL.md — 22 lines, 823 bytes
  • app.js — 4079 lines, 140499 bytes
  • assets/audio/README.md — 53 lines, 1465 bytes
  • audits/2026-04-06-formalization-audit.md — 463 lines, 20753 bytes
  • audits/2026-04-07-perplexity-audit-3-response.md — 9 lines, 645 bytes
  • bin/a2a_delegate.py — 241 lines, 7356 bytes
  • bin/apply_branch_protections.py — 42 lines, 1286 bytes
  • bin/bezalel_heartbeat_check.py — 326 lines, 10589 bytes
  • bin/browser_smoke.sh — 69 lines, 2328 bytes
  • bin/check_cron_heartbeats.py — 449 lines, 15864 bytes
  • bin/deepdive_aggregator.py — 116 lines, 3421 bytes
  • bin/deepdive_delivery.py — 186 lines, 6745 bytes
  • bin/deepdive_filter.py — 246 lines, 8386 bytes
  • bin/deepdive_orchestrator.py — 266 lines, 10159 bytes
  • bin/deepdive_synthesis.py — 173 lines, 6216 bytes
  • bin/deepdive_tts.py — 273 lines, 8825 bytes
  • bin/enforce_branch_protection.py — 46 lines, 1803 bytes
  • bin/fleet_audit.py — 463 lines, 17640 bytes
  • bin/generate_provenance.py — 131 lines, 3876 bytes
  • bin/nexus_watchdog.py — 704 lines, 23875 bytes
  • bin/night_watch.py — 301 lines, 10453 bytes
  • bin/setup_gitea_protections.py — 43 lines, 1221 bytes
  • bin/swarm_governor.py — 141 lines, 5027 bytes
  • bin/webhook_health_dashboard.py — 275 lines, 9294 bytes
  • boot.js — 49 lines, 1332 bytes
  • bootstrap.mjs — 100 lines, 4564 bytes
  • commands/timmy_commands.py — 97 lines, 2797 bytes
  • concept-packs/genie-nano-banana/README.md — 53 lines, 2189 bytes
  • concept-packs/genie-nano-banana/pipeline.md — 107 lines, 3857 bytes
  • concept-packs/genie-nano-banana/prompts/environments.yaml — 129 lines, 6169 bytes
  • concept-packs/genie-nano-banana/prompts/landmarks.yaml — 80 lines, 3755 bytes
  • concept-packs/genie-nano-banana/prompts/portals.yaml — 80 lines, 3417 bytes
  • concept-packs/genie-nano-banana/prompts/skyboxes.yaml — 63 lines, 2861 bytes
  • concept-packs/genie-nano-banana/prompts/textures.yaml — 81 lines, 3609 bytes
  • concept-packs/genie-nano-banana/references/palette.md — 78 lines, 3015 bytes
  • concept-packs/genie-nano-banana/shot-list.yaml — 143 lines, 5301 bytes
  • concept-packs/genie-nano-banana/storage-policy.md — 65 lines, 2709 bytes
  • config/agent_card.example.yaml — 57 lines, 1470 bytes
  • config/deepdive_keywords.yaml — 149 lines, 2455 bytes
  • config/deepdive_requirements.txt — 31 lines, 496 bytes
  • config/deepdive_sources.yaml — 115 lines, 2786 bytes
  • config/fleet_agents.json — 153 lines, 4582 bytes
  • deploy.sh — 17 lines, 540 bytes
  • docker-compose.desktop.yml — 46 lines, 1334 bytes
  • docker-compose.yml — 15 lines, 265 bytes
  • docs/A2A_PROTOCOL.md — 241 lines, 7298 bytes
  • docs/BANNERLORD_HARNESS_PROOF.md — 424 lines, 16348 bytes
  • docs/BANNERLORD_RUNTIME.md — 174 lines, 5609 bytes
  • docs/CANONICAL_INDEX_DEEPDIVE.md — 152 lines, 7326 bytes
  • docs/DEEPSDIVE_ARCHITECTURE.md — 88 lines, 3705 bytes
  • docs/DEEPSDIVE_EXECUTION.md — 167 lines, 6652 bytes
  • docs/DEEPSDIVE_QUICKSTART.md — 98 lines, 3280 bytes
  • docs/FLEET_VOCABULARY.md — 239 lines, 12757 bytes
  • docs/GHOST_WIZARD_AUDIT.md — 93 lines, 3806 bytes
  • docs/GOOGLE_AI_ULTRA_INTEGRATION.md — 127 lines, 4753 bytes
  • docs/QUARANTINE_PROCESS.md — 168 lines, 4344 bytes
  • docs/agent-review-log.md — 88 lines, 5154 bytes
  • docs/ai-tools-org-assessment.md — 66 lines, 2529 bytes
  • docs/bezalel/evennia/cmd_palace.py — 246 lines, 7935 bytes
  • docs/bezalel/evennia/cmd_record.py — 166 lines, 5226 bytes
  • docs/bezalel/evennia/cmd_steward.py — 105 lines, 3241 bytes
  • docs/bezalel/evennia/hall_of_wings.py — 83 lines, 3008 bytes
  • docs/bezalel/evennia/palace_room.py — 87 lines, 3297 bytes
  • docs/bezalel/evennia/steward_npc.py — 70 lines, 2349 bytes
  • docs/branch_protection.md — 33 lines, 1156 bytes
  • docs/branch_protection_policy.md — 26 lines, 547 bytes
  • docs/burn-mode-fleet-manual.md — 214 lines, 7059 bytes
  • docs/computer-use.md — 174 lines, 4915 bytes
  • docs/deep-dive-architecture.md — 284 lines, 11220 bytes
  • docs/deep-dive/ARCHITECTURE.md — 80 lines, 2772 bytes
  • docs/deep-dive/TTS_INTEGRATION_PROOF.md — 285 lines, 9437 bytes
  • docs/hermes-v2.0-architecture.md — 237 lines, 14073 bytes
  • docs/media/README.md — 91 lines, 2891 bytes
  • docs/media/clip-metadata.json — 239 lines, 11474 bytes
  • docs/media/veo-storyboard.md — 237 lines, 11534 bytes
  • docs/mempalace/bezalel_example.yaml — 22 lines, 438 bytes
  • docs/mempalace/rooms.yaml — 183 lines, 5350 bytes
  • docs/mempalace_taxonomy.yaml — 145 lines, 2756 bytes
  • docs/offload-826-audit.md — 57 lines, 2633 bytes
  • docs/pr-reviewer-policy.md — 42 lines, 1585 bytes
  • docs/sovereign-ordinal-archive.json — 19 lines, 552 bytes
  • docs/sovereign-ordinal-archive.md — 163 lines, 10382 bytes
  • docs/successor-fork-spec.md — 167 lines, 6625 bytes
  • docs/voice-output.md — 135 lines, 4047 bytes
  • electron-main.js — 12 lines, 358 bytes
  • evolution/network_simulator.py — 4 lines, 122 bytes
  • evolution/quantum_hardener.py — 4 lines, 104 bytes
  • evolution/tirith_hardener.py — 4 lines, 87 bytes
  • evolution/world_modeler.py — 4 lines, 103 bytes
  • examples/harness_demo.py — 385 lines, 13762 bytes
  • fleet/allegro/allegro-cycle-state.json — 36 lines, 1570 bytes
  • fleet/allegro/allegro-failure-log.md — 42 lines, 1664 bytes
  • fleet/allegro/allegro-handoff-template.md — 56 lines, 1632 bytes
  • fleet/allegro/allegro-hands-off-registry.json — 18 lines, 465 bytes
  • fleet/allegro/allegro-lane.md — 53 lines, 1841 bytes
  • fleet/allegro/allegro-wake-checklist.md — 52 lines, 1567 bytes
  • fleet/allegro/archived-scripts/README.md — 26 lines, 1056 bytes
  • fleet/allegro/burn-mode-validator.py — 130 lines, 4252 bytes
  • fleet/allegro/install.sh — 31 lines, 1222 bytes
  • fleet/fleet-routing.json — 266 lines, 9622 bytes
  • fleet/fleet.sh — 121 lines, 3786 bytes
  • fleet/hermes-trismegistus/README.md — 72 lines, 3006 bytes
  • fleet/hermes-trismegistus/lane.md — 43 lines, 1189 bytes
  • fleet/identity-registry.yaml — 121 lines, 3037 bytes
  • gitea-branch-protection.js — 75 lines, 2420 bytes
  • gitea-branch-protection.sh — 6 lines, 190 bytes
  • gitea_api/branch_protection.py — 36 lines, 1119 bytes
  • gofai_worker.js — 35 lines, 1925 bytes
  • help.html — 489 lines, 14637 bytes
  • index.html — 420 lines, 20989 bytes
  • intelligence/deepdive/Dockerfile — 42 lines, 1283 bytes
  • intelligence/deepdive/GEMINI_HANDOFF.md — 199 lines, 8289 bytes
  • intelligence/deepdive/Makefile — 67 lines, 2314 bytes
  • intelligence/deepdive/OPERATIONAL_READINESS.md — 265 lines, 6535 bytes
  • intelligence/deepdive/PRODUCTION_READINESS_REVIEW.md — 112 lines, 6744 bytes
  • intelligence/deepdive/PROOF_OF_EXECUTION.md — 72 lines, 2551 bytes
  • intelligence/deepdive/PROOF_OF_LIFE.md — 112 lines, 4927 bytes
  • intelligence/deepdive/QUALITY_FRAMEWORK.md — 212 lines, 7247 bytes
  • intelligence/deepdive/QUICKSTART.md — 79 lines, 2186 bytes
  • intelligence/deepdive/README.md — 73 lines, 3702 bytes
  • intelligence/deepdive/architecture.md — 277 lines, 7926 bytes
  • intelligence/deepdive/config.yaml — 133 lines, 3944 bytes
  • intelligence/deepdive/dedup_index.py — 372 lines, 12757 bytes
  • intelligence/deepdive/deploy.sh — 124 lines, 3368 bytes
  • intelligence/deepdive/docker-compose.yml — 54 lines, 1605 bytes
  • intelligence/deepdive/dpo_generator.py — 441 lines, 16799 bytes
  • intelligence/deepdive/dpo_quality.py — 533 lines, 20706 bytes
  • intelligence/deepdive/fleet_context.py — 205 lines, 7108 bytes
  • intelligence/deepdive/pipeline.py — 823 lines, 30779 bytes
  • intelligence/deepdive/prompts/PROMPT_ENGINEERING_KT.md — 151 lines, 5827 bytes
  • intelligence/deepdive/prompts/production_briefing_v1.txt — 59 lines, 3552 bytes
  • intelligence/deepdive/quality_eval.py — 335 lines, 12328 bytes
  • intelligence/deepdive/requirements.txt — 26 lines, 453 bytes
  • intelligence/deepdive/telegram_command.py — 133 lines, 4330 bytes
  • intelligence/deepdive/tests/test_aggregator.py — 64 lines, 2142 bytes
  • intelligence/deepdive/tests/test_e2e.py — 84 lines, 2669 bytes
  • intelligence/deepdive/tests/test_fleet_context.py — 62 lines, 2219 bytes
  • intelligence/deepdive/tests/test_relevance.py — 83 lines, 3036 bytes
  • intelligence/deepdive/tts_engine.py — 267 lines, 8782 bytes
  • l402_server.py — 35 lines, 1203 bytes
  • lazarus-registry.yaml — 128 lines, 3068 bytes
  • manifest.json — 21 lines, 495 bytes
  • mcp_config.json — 12 lines, 247 bytes
  • mcp_servers/README.md — 94 lines, 2852 bytes
  • mcp_servers/desktop_control_server.py — 412 lines, 14483 bytes
  • mcp_servers/steam_info_server.py — 480 lines, 17799 bytes
  • mcp_servers/test_servers.py — 239 lines, 8298 bytes
  • mempalace.js — 44 lines, 1327 bytes
  • mempalace/init.py — 5 lines, 146 bytes
  • mempalace/audit_privacy.py — 177 lines, 5198 bytes
  • mempalace/export_closets.sh — 104 lines, 3677 bytes
  • mempalace/fleet_api.py — 248 lines, 7941 bytes
  • mempalace/retain_closets.py — 163 lines, 4716 bytes
  • mempalace/rooms.yaml — 125 lines, 3993 bytes
  • mempalace/tunnel_sync.py — 308 lines, 10041 bytes
  • mempalace/validate_rooms.py — 119 lines, 3383 bytes
  • mimo-swarm/scripts/auto-merger.py — 142 lines, 4175 bytes
  • mimo-swarm/scripts/auto-reviewer.py — 232 lines, 7284 bytes
  • mimo-swarm/scripts/mimo-dispatcher.py — 542 lines, 19915 bytes
  • mimo-swarm/scripts/mimo-worker.sh — 157 lines, 5276 bytes
  • mimo-swarm/scripts/worker-runner.py — 234 lines, 8336 bytes
  • multi_user_bridge.py — 2888 lines, 123949 bytes
  • nexus/BIRTH.md — 71 lines, 2639 bytes
  • nexus/README.md — 48 lines, 2153 bytes
  • nexus/init.py — 32 lines, 667 bytes
  • nexus/a2a/init.py — 98 lines, 2251 bytes
  • nexus/a2a/card.py — 167 lines, 5079 bytes
  • nexus/a2a/client.py — 392 lines, 12556 bytes
  • nexus/a2a/registry.py — 264 lines, 8698 bytes
  • nexus/a2a/server.py — 386 lines, 12233 bytes
  • nexus/a2a/types.py — 524 lines, 16880 bytes
  • nexus/adaptive_calibrator.py — 97 lines, 3503 bytes
  • nexus/bannerlord_harness.py — 927 lines, 35736 bytes
  • nexus/bannerlord_runtime.py — 263 lines, 9595 bytes
  • nexus/bannerlord_trace.py — 234 lines, 7710 bytes
  • nexus/components/VIBE_CODE_EVALUATION.md — 97 lines, 3527 bytes
  • nexus/components/agent-presence-panel.html — 432 lines, 11704 bytes
  • nexus/components/fleet-health-dashboard.html — 118 lines, 4995 bytes
  • nexus/components/fleet-pulse.html — 101 lines, 3082 bytes
  • nexus/components/heartbeat-briefing-panel.html — 394 lines, 11124 bytes
  • nexus/components/memory-birth.js — 263 lines, 9680 bytes
  • nexus/components/memory-connections.js — 291 lines, 11156 bytes
  • nexus/components/memory-inspect.js — 180 lines, 7328 bytes
  • nexus/components/memory-optimizer.js — 28 lines, 931 bytes
  • nexus/components/memory-particles.js — 404 lines, 14216 bytes
  • nexus/components/memory-pulse.js — 160 lines, 5202 bytes
  • nexus/components/portal-status-wall.html — 478 lines, 14025 bytes
  • nexus/components/reasoning-trace.js — 451 lines, 14603 bytes
  • nexus/components/resonance-visualizer.js — 16 lines, 529 bytes
  • nexus/components/session-rooms.js — 413 lines, 15113 bytes
  • nexus/components/spatial-audio.js — 242 lines, 8952 bytes
  • nexus/components/spatial-memory.js — 1042 lines, 38272 bytes
  • nexus/components/timeline-scrubber.js — 205 lines, 7177 bytes
  • nexus/computer_use.py — 313 lines, 9739 bytes
  • nexus/computer_use_demo.py — 118 lines, 3872 bytes
  • nexus/cron_heartbeat.py — 136 lines, 4175 bytes
  • nexus/evennia_event_adapter.py — 127 lines, 4593 bytes
  • nexus/evennia_mempalace/init.py — 49 lines, 1570 bytes
  • nexus/evennia_mempalace/commands/init.py — 15 lines, 349 bytes
  • nexus/evennia_mempalace/commands/recall.py — 267 lines, 8719 bytes
  • nexus/evennia_mempalace/commands/write.py — 124 lines, 3264 bytes
  • nexus/evennia_mempalace/typeclasses/init.py — 1 lines, 37 bytes
  • nexus/evennia_mempalace/typeclasses/npcs.py — 138 lines, 4719 bytes
  • nexus/evennia_mempalace/typeclasses/rooms.py — 99 lines, 4147 bytes
  • nexus/evennia_ws_bridge.py — 353 lines, 13937 bytes
  • nexus/experience_store.py — 298 lines, 11354 bytes
  • nexus/gemini_harness.py — 896 lines, 33177 bytes
  • nexus/groq_worker.py — 79 lines, 2344 bytes
  • nexus/heartbeat.py — 79 lines, 2167 bytes
  • nexus/mempalace/init.py — 23 lines, 719 bytes
  • nexus/mempalace/config.py — 46 lines, 2410 bytes
  • nexus/mempalace/searcher.py — 200 lines, 5569 bytes
  • nexus/mnemosyne/FEATURES.yaml — 209 lines, 7271 bytes
  • nexus/mnemosyne/init.py — 34 lines, 1016 bytes
  • nexus/mnemosyne/archive.py — 1444 lines, 53822 bytes
  • nexus/mnemosyne/cli.py — 577 lines, 21495 bytes
  • nexus/mnemosyne/embeddings.py — 170 lines, 6099 bytes
  • nexus/mnemosyne/entry.py — 63 lines, 2366 bytes
  • nexus/mnemosyne/ingest.py — 182 lines, 5644 bytes
  • nexus/mnemosyne/linker.py — 106 lines, 3831 bytes
  • nexus/mnemosyne/reasoner.py — 14 lines, 533 bytes
  • nexus/mnemosyne/resonance_linker.py — 22 lines, 906 bytes
  • nexus/mnemosyne/rules.json — 6 lines, 75 bytes
  • nexus/mnemosyne/snapshot.py — 31 lines, 1479 bytes
  • nexus/mnemosyne/tests/init.py — 1 lines, 0 bytes
  • nexus/mnemosyne/tests/test_archive.py — 855 lines, 34514 bytes
  • nexus/mnemosyne/tests/test_cli_commands.py — 138 lines, 4936 bytes
  • nexus/mnemosyne/tests/test_consolidation.py — 176 lines, 7465 bytes
  • nexus/mnemosyne/tests/test_discover.py — 1 lines, 16 bytes
  • nexus/mnemosyne/tests/test_embeddings.py — 112 lines, 3744 bytes
  • nexus/mnemosyne/tests/test_graph_clusters.py — 271 lines, 10395 bytes
  • nexus/mnemosyne/tests/test_ingest_file.py — 241 lines, 8025 bytes
  • nexus/mnemosyne/tests/test_memory_decay.py — 278 lines, 10028 bytes
  • nexus/mnemosyne/tests/test_path.py — 106 lines, 3764 bytes
  • nexus/mnemosyne/tests/test_resonance.py — 1 lines, 17 bytes
  • nexus/mnemosyne/tests/test_snapshot.py — 1 lines, 16 bytes
  • nexus/mnemosyne/tests/test_snapshots.py — 240 lines, 9298 bytes
  • nexus/morning_report.py — 177 lines, 6811 bytes
  • nexus/morrowind_harness.py — 888 lines, 34327 bytes
  • nexus/nexus_think.py — 519 lines, 16579 bytes
  • nexus/nostr_identity.py — 102 lines, 3203 bytes
  • nexus/nostr_publisher.py — 55 lines, 1770 bytes
  • nexus/perception_adapter.py — 540 lines, 17692 bytes
  • nexus/retry_helper.py — 114 lines, 3695 bytes
  • nexus/setup_gitea.py — 1 lines, 0 bytes
  • nexus/symbolic-engine.js — 386 lines, 11545 bytes
  • nexus/symbolic-engine.test.js — 61 lines, 1904 bytes
  • nexus/traces/bannerlord/REPLAY.md — 97 lines, 2819 bytes
  • nexus/traces/bannerlord/sample_manifest.json — 18 lines, 873 bytes
  • nexus/trajectory_logger.py — 143 lines, 5194 bytes
  • operation-get-a-job/README.md — 141 lines, 4996 bytes
  • operation-get-a-job/entity-setup.md — 203 lines, 6079 bytes
  • operation-get-a-job/outreach-templates.md — 216 lines, 8391 bytes
  • operation-get-a-job/portfolio.md — 202 lines, 6680 bytes
  • operation-get-a-job/proposal-template.md — 237 lines, 7201 bytes
  • operation-get-a-job/rate-card.md — 216 lines, 7596 bytes
  • operation-get-a-job/service-offerings.md — 184 lines, 6075 bytes
  • operations/fleet-topology.md — 105 lines, 3356 bytes
  • org/README.md — 14 lines, 430 bytes
  • paper/autoreason-mud-paper.md — 172 lines, 7561 bytes
  • paper/experiment1_results.md — 40 lines, 2380 bytes
  • paper/experiment3_results.md — 52 lines, 1989 bytes
  • paper/results_section.md — 69 lines, 4293 bytes
  • playground/README.md — 95 lines, 2604 bytes
  • playground/playground.html — 692 lines, 28636 bytes
  • portals.json — 295 lines, 6399 bytes
  • provenance.json — 62 lines, 2037 bytes
  • pytest.ini — 14 lines, 384 bytes
  • reports/bezalel/2026-04-06-bezalel-review-allegro-deliverables.md — 162 lines, 9386 bytes
  • reports/bezalel/2026-04-06-test-count-verification.md — 36 lines, 1425 bytes
  • reports/bezalel/2026-04-07-mempalace-field-report.md — 62 lines, 2868 bytes
  • reports/bezalel/nightly/2026-04-07.md — 15 lines, 443 bytes
  • requirements.txt — 7 lines, 115 bytes
  • reviews/2026-04-06-formalization-audit-review.md — 211 lines, 11754 bytes
  • reviews/2026-04-06-greptard-report-review.md — 164 lines, 12621 bytes
  • reviews/2026-04-06-operation-get-a-job-review.md — 426 lines, 29107 bytes
  • robots.txt — 8 lines, 154 bytes
  • scaffold/deep-dive/aggregator/init.py — 1 lines, 0 bytes
  • scaffold/deep-dive/aggregator/arxiv_fetcher.py — 105 lines, 3168 bytes
  • scaffold/deep-dive/aggregator/blog_fetcher.py — 112 lines, 3221 bytes
  • scaffold/deep-dive/cron.yaml — 13 lines, 426 bytes
  • scaffold/deep-dive/delivery/init.py — 1 lines, 0 bytes
  • scaffold/deep-dive/delivery/delivery_pipeline.py — 100 lines, 3115 bytes
  • scaffold/deep-dive/orchestrator.py — 108 lines, 3472 bytes
  • scaffold/deep-dive/relevance/init.py — 1 lines, 0 bytes
  • scaffold/deep-dive/relevance/relevance_engine.py — 98 lines, 3485 bytes
  • scaffold/deep-dive/requirements.txt — 7 lines, 154 bytes
  • scaffold/deep-dive/synthesis/init.py — 1 lines, 0 bytes
  • scaffold/deep-dive/synthesis/synthesis_engine.py — 85 lines, 2365 bytes
  • scaffold/deep-dive/synthesis/synthesis_prompt.txt — 62 lines, 2275 bytes
  • scaffold/deep-dive/tts/init.py — 1 lines, 0 bytes
  • scaffold/deep-dive/tts/tts_pipeline.py — 99 lines, 3396 bytes
  • scaffold/deepdive/README.md — 61 lines, 2016 bytes
  • scaffold/deepdive/phase1/arxiv_aggregator.py — 176 lines, 5702 bytes
  • scaffold/deepdive/phase1/config.yaml — 43 lines, 879 bytes
  • scaffold/deepdive/phase2/relevance_engine.py — 21 lines, 640 bytes
  • scaffold/deepdive/phase3/synthesis.py — 20 lines, 616 bytes
  • scaffold/deepdive/phase4/tts_pipeline.py — 20 lines, 625 bytes
  • scaffold/deepdive/phase5/telegram_delivery.py — 19 lines, 571 bytes
  • scripts/audit_mempalace_privacy.py — 95 lines, 2665 bytes
  • scripts/audit_merge_reviews.py — 167 lines, 5402 bytes
  • scripts/backup_databases.sh — 50 lines, 2027 bytes
  • scripts/bannerlord_runtime_setup.sh — 126 lines, 5297 bytes
  • scripts/bannerlord_verify_runtime.sh — 117 lines, 5129 bytes
  • scripts/ci_auto_revert.py — 135 lines, 4755 bytes
  • scripts/cron-heartbeat-write.sh — 115 lines, 5130 bytes
  • scripts/flake_detector.py — 256 lines, 8347 bytes
  • scripts/fleet.sh — 114 lines, 3784 bytes
  • scripts/guardrails.sh — 5 lines, 133 bytes
  • scripts/lazarus_checkpoint.py — 140 lines, 4283 bytes
  • scripts/lazarus_watchdog.py — 252 lines, 8835 bytes
  • scripts/mempalace-incremental-mine.sh — 96 lines, 3061 bytes
  • scripts/mempalace_export.py — 75 lines, 2283 bytes
  • scripts/mempalace_nightly.sh — 24 lines, 1052 bytes
  • scripts/meta_heartbeat.sh — 53 lines, 1376 bytes
  • scripts/provision-runner.sh — 229 lines, 9684 bytes
  • scripts/reassign_fenrir.py — 184 lines, 6702 bytes
  • scripts/repo_truth_guard.py — 113 lines, 3158 bytes
  • scripts/review_gate.py — 70 lines, 2052 bytes
  • scripts/runner-health-probe.sh — 190 lines, 8211 bytes
  • scripts/runner_health_probe.sh — 46 lines, 1745 bytes
  • scripts/secret_guard.sh — 50 lines, 1627 bytes
  • scripts/smoke.mjs — 4 lines, 147 bytes
  • scripts/staging_gate.py — 77 lines, 2465 bytes
  • scripts/sync_branch_protection.py — 81 lines, 2731 bytes
  • scripts/sync_fleet_to_alpha.sh — 30 lines, 887 bytes
  • scripts/validate_mempalace_taxonomy.py — 123 lines, 3412 bytes
  • server.py — 123 lines, 4389 bytes
  • service-worker.js — 26 lines, 542 bytes
  • style.css — 2936 lines, 60727 bytes
  • tests/boot.test.js — 20 lines, 1241 bytes
  • tests/bootstrap.test.mjs — 28 lines, 4647 bytes
  • tests/conftest.py — 124 lines, 4409 bytes
  • tests/fixtures/fleet_palace/bezalel/forge.closet.json — 16 lines, 366 bytes
  • tests/fixtures/fleet_palace/bezalel/hermes.closet.json — 11 lines, 233 bytes
  • tests/fixtures/fleet_palace/bezalel/issues.closet.json — 11 lines, 241 bytes
  • tests/quarantine/README.md — 28 lines, 883 bytes
  • tests/quarantine/init.py — 2 lines, 98 bytes
  • tests/test_a2a.py — 763 lines, 23763 bytes
  • tests/test_bannerlord_harness.py — 690 lines, 27273 bytes
  • tests/test_bezalel_heartbeat.py — 334 lines, 12741 bytes
  • tests/test_browser_smoke.py — 293 lines, 11281 bytes
  • tests/test_computer_use.py — 362 lines, 13762 bytes
  • tests/test_cron_heartbeats.py — 341 lines, 12529 bytes
  • tests/test_edge_tts.py — 420 lines, 15077 bytes
  • tests/test_evennia_event_adapter.py — 56 lines, 2204 bytes
  • tests/test_evennia_mempalace_commands.py — 303 lines, 11379 bytes
  • tests/test_evennia_ws_bridge.py — 36 lines, 1732 bytes
  • tests/test_fleet_audit.py — 143 lines, 6363 bytes
  • tests/test_gemini_harness.py — 566 lines, 25697 bytes
  • tests/test_help_page.py — 42 lines, 1299 bytes
  • tests/test_index_html_integrity.py — 10 lines, 551 bytes
  • tests/test_manifest.py — 39 lines, 1553 bytes
  • tests/test_mempalace_audit_privacy.py — 129 lines, 4201 bytes
  • tests/test_mempalace_fleet_api.py — 239 lines, 8141 bytes
  • tests/test_mempalace_retain_closets.py — 139 lines, 4393 bytes
  • tests/test_mempalace_searcher.py — 190 lines, 6996 bytes
  • tests/test_mempalace_tunnel_sync.py — 205 lines, 7040 bytes
  • tests/test_mempalace_validate_rooms.py — 160 lines, 4624 bytes
  • tests/test_nexus_watchdog.py — 311 lines, 12317 bytes
  • tests/test_portal_registry_schema.py — 45 lines, 1121 bytes
  • tests/test_provenance.py — 73 lines, 2715 bytes
  • tests/test_repo_truth.py — 35 lines, 1087 bytes
  • tests/test_syntax_fixes.py — 111 lines, 4378 bytes
  • tests/test_webhook_health_dashboard.py — 120 lines, 4144 bytes
  • vision.json — 37 lines, 1658 bytes
  • world/multi_user_bridge.py — 289 lines, 11125 bytes
  • world_state.json — 208 lines, 5257 bytes

Selected file excerpts

tests/test_provenance.py

1|"""
2|Provenance tests — verify the Nexus browser surface comes from
3|a clean Timmy_Foundation/the-nexus checkout, not stale sources.
4|
5|Refs: #686
6|"""
7|import json
8|import hashlib
9|from pathlib import Path
10|
11|REPO_ROOT = Path(__file__).resolve().parent.parent
12|
13|
14|def test_provenance_manifest_exists() -> None:
15|    """provenance.json must exist and be valid JSON."""
16|    p = REPO_ROOT / "provenance.json"
17|    assert p.exists(), "provenance.json missing — run bin/generate_provenance.py"
18|    data = json.loads(p.read_text())
19|    assert "files" in data
20|    assert "repo" in data
21|
22|
23|def test_provenance_repo_identity() -> None:
24|    """Manifest must claim Timmy_Foundation/the-nexus."""
25|    data = json.loads((REPO_ROOT / "provenance.json").read_text())
26|    assert data["repo"] == "Timmy_Foundation/the-nexus"
27|
28|
29|def test_provenance_all_contract_files_present() -> None:
30|    """Every file listed in the provenance manifest must exist on disk."""
31|    data = json.loads((REPO_ROOT / "provenance.json").read_text())
32|    missing = []
33|    for rel in data["files"]:
34|        if not (REPO_ROOT / rel).exists():
35|            missing.append(rel)
36|    assert not missing, f"Contract files missing: {missing}"
37|
38|
39|def test_provenance_hashes_match() -> None:
40|    """File hashes must match the stored manifest (no stale/modified files)."""
41|    data = json.loads((REPO_ROOT / "provenance.json").read_text())
42|    mismatches = []
43|    for rel, meta in data["files"].items():
44|        p = REPO_ROOT / rel
45|        if not p.exists():
46|            mismatches.append(f"MISSING: {rel}")
47|            continue
48|        actual = hashlib.sha256(p.read_bytes()).hexdigest()
49|        if actual != meta["sha256"]:
50|            mismatches.append(f"CHANGED: {rel}")
51|    assert not mismatches, f"Provenance mismatch:\n" + "\n".join(mismatches)
52|
53|
54|def test_no_legacy_matrix_references_in_frontend() -> None:
55|    """Frontend files must not reference /Users/apayne/the-matrix as a source."""
56|    forbidden_paths = ["/Users/apayne/the-matrix"]
57|    offenders = []
58|    for rel in ["index.html", "app.js", "style.css"]:
59|        p = REPO_ROOT / rel
60|        if p.exists():
61|            content = p.read_text()
62|            for bad in forbidden_paths:
63|                if bad in content:
64|                    offenders.append(f"{rel} references {bad}")
65|    assert not offenders, f"Legacy matrix references found: {offenders}"
66|
67|
68|def test_no_stale_perplexity_computer_references_in_critical_files() -> None:
69|    """Verify the provenance generator script itself is canonical."""
70|    script = REPO_ROOT / "bin" / "generate_provenance.py"
71|    assert script.exists(), "bin/generate_provenance.py must exist"
72|    content = script.read_text()
73|    assert "Timmy_Foundation/the-nexus" in content

README.md

1|# The Nexus Project
2|
3|## Branch Protection & Review Policy
4|
5|**All repositories enforce these rules on the `main` branch:**
6|
7|| Rule | Status | Rationale |
8||------|--------|-----------|
9|| Require PR for merge | ✅ Enabled | Prevent direct commits |
10|| Required approvals | 1+ | Minimum review threshold |
11|| Dismiss stale approvals | ✅ Enabled | Re-review after new commits |
12|| Require CI to pass | ⚠️ Conditional | Only where CI exists |
13|| Block force push | ✅ Enabled | Protect commit history |
14|| Block branch deletion | ✅ Enabled | Prevent accidental deletion |
15|
16|**Default Reviewers:**
17|- @perplexity (all repositories)
18|- @Timmy (hermes-agent only)
19|
20|**CI Enforcement:**
21|- hermes-agent: Full CI enforcement
22|- the-nexus: CI pending runner restoration (#915)
23|- timmy-home: No CI enforcement
24|- timmy-config: Limited CI
25|
26|**Implementation Status:**
27|- [x] hermes-agent protection enabled
28|- [x] the-nexus protection enabled
29|- [x] timmy-home protection enabled
30|- [x] timmy-config protection enabled
31|
32|> This policy replaces all previous ad-hoc workflows. Any exceptions require written approval from @Timmy and @perplexity.
33|
34|---
35|
36|It is meant to become two things at once:
37|- a local-first training ground for Timmy
38|- a wizardly visualization surface for the living system
39|
40|## Current Truth
41|
42|As of current `main`, this repo does **not** ship a browser 3D world.
43|In plain language: current `main` does not ship a browser 3D world.
44|
45|A clean checkout of `Timmy_Foundation/the-nexus` on `main` currently contains:
46|- Python heartbeat / cognition files under `nexus/`
47|- `server.py`
48|- protocol, report, and deployment docs
49|- JSON configuration files like `portals.json` and `vision.json`
50|
51|It does **not** currently contain an active root frontend such as:
52|- `index.html`
53|- `app.js`
54|- `style.css`
55|- `package.json`
56|
57|Serving the repo root today shows a directory listing, not a rendered world.
58|
59|## One Canonical 3D Repo
60|
61|`Timmy_Foundation/the-nexus` is the only canonical 3D repo.
62|In plain language: Timmy_Foundation/the-nexus is the only canonical 3D repo.
63|
64|The old local browser app at:
65|- `/Users/apayne/the-matrix`
66|
67|is legacy source material, not a second repo to keep evolving in parallel.
68|Useful work from it must be audited and migrated here.
69|
70|See:
71|- `LEGACY_MATRIX_AUDIT.md`
72|
73|## Why this matters
74|
75|We do not want to lose real quality work.
76|We also do not want to keep two drifting 3D repos alive by accident.
77|
78|The rule is:
79|- rescue good work from legacy Matrix
80|- rebuild inside `the-nexus`
81|- keep telemetry and durable truth flowing through the Hermes harness
82|- Hermes is the sole harness — no external gateway dependencies
83|
84|## Verified historical browser-world snapshot
85|
86|The commit the user pointed at:
87|- `0518a1c3ae3c1d0afeb24dea9772102f5a3d9a66`
88|
89|still contains the old root browser files (`index.html`, `app.js`, `style.css`, `package.json`, tests/), so it is a useful in-repo reference point for what existed before the later deletions.
90|
91|## Active migration backlog
92|
93|- `#684` sync docs to repo truth
94|- `#685` preserve legacy Matrix quality work before rewrite
95|- `#686` rebuild browser smoke / visual validation for the real Nexus repo
96|- `#687` restore a wizardly local-first visual shell from audited Matrix components
97|- `#672` rebuild the portal stack as Timmy → Reflex → Pilot
98|- `#673` deterministic Morrowind pilot loop with world-state proof
99|- `#674` reflex tactical layer and semantic trajectory logging
100|- `#675` deterministic context compaction for long local sessions
101|
102|## What gets preserved from legacy Matrix
103|
104|High-value candidates include:
105|- visitor movement / embodiment
106|- chat, bark, and presence systems
107|- transcript logging
108|- ambient / visual atmosphere systems
109|- economy / satflow visualizations
110|- smoke and browser validation discipline
111|
112|Those pieces should be carried forward only if they serve the mission and are re-tethered to real local system state.
113|
114|## Running Locally
115|
116|### Current repo truth
117|
118|There is no root browser app on current `main`.
119|Do not tell people to static-serve the repo root and expect a world.
120|
121|### Branch Protection & Review Policy
122|
123|**All repositories enforce:**
124|- PRs required for all changes
125|- Minimum 1 approval required
126|- CI/CD must pass
127|- No force pushes
128|- No direct pushes to main
129|
130|**Default reviewers:**
131|- `@perplexity` for all repositories
132|- `@Timmy` for nexus/ and hermes-agent/
133|
134|**Enforced by Gitea branch protection rules**
135|
136|### What you can run now
137|
138|- `python3 server.py` for the local websocket bridge
139|- Python modules under `nexus/` for heartbeat / cognition work
140|
141|### Browser world restoration path
142|
143|The browser-facing Nexus must be rebuilt deliberately through the migration backlog above, using audited Matrix components and truthful validation.
144|
145|---
146|
147|*One 3D repo. One migration path. No more ghost worlds.*
148|
149|## Running Locally
150|
151|### Current repo truth
152|
153|There is no root browser app on current `main`.
154|Do not tell people to static-serve the repo root and expect a world.
155|
156|### What you can run now
157|
158|- `python3 server.py` for the local websocket bridge
159|- Python modules under `nexus/` for heartbeat / cognition work
160|
161|### Browser world restoration path
162|
163|The browser-facing Nexus must be rebuilt deliberately through the migration backlog above, using audited Matrix components and truthful validation.
164|
165|---
166|
167|*One 3D repo. One migration path. No more ghost worlds.*

app.js

1|import ResonanceVisualizer from './nexus/components/resonance-visualizer.js';\nimport * as THREE from 'three';
2|import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
3|import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
4|import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
5|import { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js';
6|import { SpatialMemory } from './nexus/components/spatial-memory.js';
7|import { SpatialAudio } from './nexus/components/spatial-audio.js';
8|import { MemoryBirth } from './nexus/components/memory-birth.js';
9|import { MemoryOptimizer } from './nexus/components/memory-optimizer.js';
10|import { MemoryInspect } from './nexus/components/memory-inspect.js';
11|import { MemoryPulse } from './nexus/components/memory-pulse.js';
12|import { ReasoningTrace } from './nexus/components/reasoning-trace.js';
13|
14|// ═══════════════════════════════════════════
15|// NEXUS v1.1 — Portal System Update
16|// ═══════════════════════════════════════════
17|
18|const NEXUS = {
19|  colors: {
20|    primary:    0x4af0c0,
21|    secondary:  0x7b5cff,
22|    bg:         0x050510,
23|    panelBg:    0x0a0f28,
24|    nebula1:    0x1a0a3e,
25|    nebula2:    0x0a1a3e,
26|    gold:       0xffd700,
27|    danger:     0xff4466,
28|    gridLine:   0x1a2a4a,
29|  }
30|};
31|
32|// ═══ STATE ═══
33|let camera, scene, renderer, composer;
34|let clock, playerPos, playerRot;
35|let keys = {};
36|let mouseDown = false;
37|let batcaveTerminals = [];
38|let portals = []; // Registry of active portals
39|let visionPoints = []; // Registry of vision points
40|let agents = []; // Registry of agent presences
41|let activePortal = null; // Portal currently in proximity
42|let activeVisionPoint = null; // Vision point currently in proximity
43|let portalOverlayActive = false;
44|let visionOverlayActive = false;
45|let atlasOverlayActive = false;
46|let thoughtStreamMesh;
47|let harnessPulseMesh;
48|let powerMeterBars = [];
49|let particles, dustParticles;
50|let debugOverlay;
51|let frameCount = 0, lastFPSTime = 0, fps = 0;
52|let chatOpen = true;
53|let memoryFeedEntries = [];  // Mnemosyne: recent memory events for feed panel
54|let _memoryFilterOpen = false;  // Mnemosyne: filter panel state
55|let _clickStartX = 0, _clickStartY = 0;  // Mnemosyne: click-vs-drag detection
56|let loadProgress = 0;
57|let performanceTier = 'high';
58|
59|/** Escape HTML entities for safe innerHTML insertion. */
60|function escHtml(s) {
61|  return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
62|}
63|
64|// ═══ HERMES WS STATE ═══
65|let hermesWs = null;
66|let wsReconnectTimer = null;
67|let wsConnected = false;
68|// ═══ EVENNIA ROOM STATE ═══
69|let evenniaRoom = null;   // {title, desc, exits[], objects[], occupants[], timestamp, roomKey}
70|let evenniaConnected = false;
71|let evenniaStaleTimer = null;
72|const EVENNIA_STALE_MS = 60000; // mark stale after 60s without update
73|let recentToolOutputs = [];
74|let actionStreamEntries = [];  // Evennia command/result flow for action stream panel
75|let actionStreamRoom = '';      // Current room from movement events
76|let workshopPanelCtx = null;
77|let workshopPanelTexture = null;
78|let workshopPanelCanvas = null;
79|let workshopScanMat = null;
80|let workshopPanelRefreshTimer = 0;
81|let lastFocusedPortal = null;
82|
83|// ═══ VISITOR / OPERATOR MODE ═══
84|let uiMode = 'visitor'; // 'visitor' | 'operator'
85|
86|// ═══ NAVIGATION SYSTEM ═══
87|const NAV_MODES = ['walk', 'orbit', 'fly'];
88|let navModeIdx = 0;
89|
90|const orbitState = {
91|  target:  new THREE.Vector3(0, 2, 0),
92|  radius:  14,
93|  theta:   Math.PI,
94|  phi:     Math.PI / 6,
95|  minR:    3,
96|  maxR:    40,
97|  lastX:   0,
98|  lastY:   0,
99|};
100|
101|let flyY = 2;
102|
103|// ═══ INIT ═══
104|
105|import { 
106|  SymbolicEngine, AgentFSM, KnowledgeGraph, Blackboard, 
107|  SymbolicPlanner, HTNPlanner, CaseBasedReasoner, 
108|  NeuroSymbolicBridge, MetaReasoningLayer 
109|} from './nexus/symbolic-engine.js';
110|// ═══ SOVEREIGN SYMBOLIC ENGINE (GOFAI) ═══
111|class SymbolicEngine {
112|  constructor() {
113|    this.facts = new Map();
114|    this.factIndices = new Map();
115|    this.factMask = 0n;
116|    this.rules = [];
117|    this.reasoningLog = [];
118|  }
119|
120|  addFact(key, value) {
121|    this.facts.set(key, value);
122|    if (!this.factIndices.has(key)) {
123|      this.factIndices.set(key, BigInt(this.factIndices.size));
124|    }
125|    const bitIndex = this.factIndices.get(key);
126|    if (value) {
127|      this.factMask |= (1n << bitIndex);
128|    } else {
129|      this.factMask &= ~(1n << bitIndex);
130|    }
131|  }
132|
133|  addRule(condition, action, description, triggerFacts = []) {
134|    this.rules.push({ condition, action, description, triggerFacts });
135|  }
136|
137|  reason() {
138|    this.rules.forEach(rule => {
139|      if (rule.condition(this.facts)) {
140|        const result = rule.action(this.facts);
141|        if (result) {
142|          this.logReasoning(rule.description, result);
143|        }
144|      }
145|    });
146|  }
147|
148|  logReasoning(ruleDesc, outcome) {
149|    const entry = { timestamp: Date.now(), rule: ruleDesc, outcome: outcome };
150|    this.reasoningLog.unshift(entry);
151|    if (this.reasoningLog.length > 5) this.reasoningLog.pop();
152|    
153|    const container = document.getElementById('symbolic-log-content');
154|    if (container) {
155|      const logDiv = document.createElement('div');
156|      logDiv.className = 'symbolic-log-entry';
157|      logDiv.innerHTML = `<span class="symbolic-rule">[RULE] ${ruleDesc}</span><span class="symbolic-outcome">→ ${outcome}</span>`;
158|      container.prepend(logDiv);
159|      if (container.children.length > 5) container.lastElementChild.remove();
160|    }
161|  }
162|}
163|
164|class AgentFSM {
...[truncated]...

CLAUDE.md

1|# CLAUDE.md — The Nexus (Timmy_Foundation/the-nexus)
2|
3|## Project Overview
4|
5|The Nexus is Timmy's canonical 3D/home-world repo.
6|Its intended role is:
7|- local-first training ground for Timmy
8|- wizardly visualization surface for the system
9|
10|## Current Repo Truth
11|
12|Do not describe this repo as a live browser app on `main`.
13|
14|Current `main` does not ship the old root frontend files:
15|- `index.html`
16|- `app.js`
17|- `style.css`
18|- `package.json`
19|
20|A clean checkout of current `main` serves a directory listing if you static-serve the repo root.
21|That is world-state truth.
22|
23|The live browser shell people remember exists in legacy form at:
24|- `/Users/apayne/the-matrix`
25|
26|That legacy app is source material for migration, not a second canonical repo.
27|
28|Timmy_Foundation/the-nexus is the only canonical 3D repo.
29|
30|See:
31|- `LEGACY_MATRIX_AUDIT.md`
32|- issues `#684`, `#685`, `#686`, `#687`
33|
34|## Architecture (current main)
35|
36|Current repo contents are centered on:
37|- `nexus/` — Python cognition / heartbeat components
38|- `server.py` — local websocket bridge
39|- `portals.json`, `vision.json` — data/config artifacts
40|- deployment/docs files
41|
42|Do not tell contributors to run Vite or edit a nonexistent root frontend on current `main`.
43|If browser/UI work is being restored, it must happen through the migration backlog and land back here.
44|
45|## Canonical File Paths
46|
47|**Frontend code lives at repo ROOT, NOT in `public/nexus/`:**
48|- `app.js` — main Three.js app (GOFAI, 3D world, all frontend logic)
49|- `index.html` — main HTML shell
50|- `style.css` — styles
51|- `server.py` — websocket bridge
52|- `gofai_worker.js` — web worker for off-thread reasoning
53|
54|**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`.
55|
56|## Hard Rules
57|
58|1. One canonical 3D repo only: `Timmy_Foundation/the-nexus`
59|2. No parallel evolution of `/Users/apayne/the-matrix` as if it were the product
60|3. Rescue useful legacy Matrix work by auditing and migrating it here
61|4. Telemetry and durable truth flow through Hermes harness
62|5. OpenClaw remains a sidecar, not the governing authority
63|6. Before claiming visual validation, prove the app being viewed actually comes from current `the-nexus`
64|7. **NEVER write frontend files to `public/nexus/`** — use repo root paths listed above
65|
66|## Validation Rule
67|
68|If you are asked to visually validate Nexus:
69|- prove the tested app comes from a clean checkout/worktree of `Timmy_Foundation/the-nexus`
70|- if current `main` only serves a directory listing or otherwise lacks the browser world, stop calling it visually validated
71|- pivot to migration audit and issue triage instead of pretending the world still exists
72|
73|## Migration Priorities
74|
75|1. `#684` — docs truth
76|2. `#685` — legacy Matrix preservation audit
77|3. `#686` — browser smoke / visual validation rebuild
78|4. `#687` — restore wizardly local-first visual shell
79|5. then continue portal/gameplay work (`#672`, `#673`, `#674`, `#675`)
80|
81|## Legacy Matrix rescue targets
82|
83|The old Matrix contains real quality work worth auditing:
84|- visitor movement and embodiment
85|- agent presence / bark / chat systems
86|- transcript logging
87|- ambient world systems
88|- satflow / economy visualization
89|- browser smoke tests and production build discipline
90|
91|Preserve the good work.
92|Do not preserve stale assumptions or fake architecture.

POLICY.md

1|# Branch Protection & Review Policy
2|
3|## 🛡️ Enforced Branch Protection Rules
4|
5|All repositories must apply the following branch protection rules to the `main` branch:
6|
7|| Rule | Setting | Rationale |
8||------|---------|-----------|
9|| Require PR for merge | ✅ Required | Prevent direct pushes to `main` |
10|| Required approvals | ✅ 1 approval | Ensure at least one reviewer approve before merge |
11|| Dismiss stale approvals | ✅ Auto-dismiss | Require re-approval after new commits |
12|| Require CI to pass | ✅ Where CI exist | Prevent merging of failing builds |
13|| Block force push | ✅ Enabled | Protect commit history |
14|| Block branch deletion | ✅ Enabled | Prevent accidental deletion of `main` |
15|
16|> ⚠️ Note: CI enforcement is optional for repositories where CI is not yet configured.
17|
18|---
19|
20|### 👤 Default Reviewer Assignment
21|
22|All repositories must define default reviewers using CODEOWNERS-style configuration:
23|
24|- `@perplexity` is the **default reviewer** for all repositories.
25|- `@Timmy` is a **required reviewer** for `hermes-agent`.
26|- Repository-specific owners may be added for specialized areas.
27|
28|---
29|
30|### <20> Affected Repositories
31|
32|| Repository | Status | Notes |
33||-------------|--------|-------|
34|| `hermes-agent` | ✅ Protected | CI is active |
35|| `the-nexus` | ✅ Protected | CI is pending |
36|| `timmy-home` | ✅ Protected | No CI |
37|| `timmy-config` | ✅ Protected | Limited CI |
38|
39|---
40|
41|### ✅ Acceptance Criteria
42|
43|- [ ] Branch protection enabled on `hermes-agent` main
44|- [ ] Branch protection enabled on `the-nexus` main
45|- [ ] Branch protection enabled on `timmy-home` main
46|- [ ] Branch protection enabled on `timmy-config` main
47|- [ ] `@perplexity` set as default reviewer org-wide
48|- [ ] Policy documented in this file
49|
50|---
51|
52|### <20> Blocks
53|
54|- Blocks #916, #917
55|- cc @Timmy @Rockachopa
56|
57|— @perplexity, Integration Architect + QA
58|
59|## 🛡️ Branch Protection Rules
60|
61|These rules must be applied to the `main` branch of all repositories:
62|- [R] **Require Pull Request for Merge**  No direct pushes to `main`
63|- [x] **Require 1 Approval**  At least one reviewer must approve
64|- [R] **Dismiss Stale Approvals**  Re-review after new commits
65|- [x] **Require CI to Pass**  Only allow merges with passing CI (where CI exists)
66|- [x] **Block Force Push**  Prevent rewrite history
67|- [x] **Block Branch Deletion**  Prevent accidental deletion of `main`
68|
69|## 👤 Default Reviewer
70|
71|- `@perplexity`  Default reviewer for all repositories
72|- `@Timmy`  Required reviewer for `hermes-agent` (owner gate)
73|
74|## 🚧 Enforcement
75|
76|- All repositories must have these rules applied in the Gitea UI under **Settings > Branches > Branch Protection**.
77|- CI must be configured and enforced for repositories with CI pipelines.
78|- Reviewers assignments must be set via CODEOWNERS or manually in the UI.
79|
80|## 📌 Acceptance Criteria
81|
82|- [ ] Branch protection rules applied to `main` in:
83|  - `hermes-agent`
84|  - `the-nexus`
85|  - `timmy-home`
86|  - `timmy-config`
87|- [ ] `@perplexity` set as default reviewer
88|- [ ] `@Timmy` set as required reviewer for `hermes-agent`
89|- [ ] This policy documented in each repository's root
90|
91|## 🧠 Notes
92|
93|- For repositories without CI, the "Require CI to Pass" rule is optional.
94|- This policy is versioned and must be updated as needed.

server.py

1|#!/usr/bin/env python3
2|"""
3|The Nexus WebSocket Gateway — Robust broadcast bridge for Timmy's consciousness.
4|This server acts as the central hub for the-nexus, connecting the mind (nexus_think.py),
5|the body (Evennia/Morrowind), and the visualization surface.
6|"""
7|import asyncio
8|import json
9|import logging
10|import signal
11|import sys
12|from typing import Set
13|
14|# Branch protected file - see POLICY.md
15|import websockets
16|
17|# Configuration
18|PORT = 8765
19|HOST = "0.0.0.0"  # Allow external connections if needed
20|
21|# Logging setup
22|logging.basicConfig(
23|    level=logging.INFO,
24|    format='%(asctime)s [%(levelname)s] %(message)s',
25|    datefmt='%Y-%m-%d %H:%M:%S'
26|)
27|logger = logging.getLogger("nexus-gateway")
28|
29|# State
30|clients: Set[websockets.WebSocketServerProtocol] = set()
31|
32|async def broadcast_handler(websocket: websockets.WebSocketServerProtocol):
33|    """Handles individual client connections and message broadcasting."""
34|    clients.add(websocket)
35|    addr = websocket.remote_address
36|    logger.info(f"Client connected from {addr}. Total clients: {len(clients)}")
37|    
38|    try:
39|        async for message in websocket:
40|            # Parse for logging/validation if it's JSON
41|            try:
42|                data = json.loads(message)
43|                msg_type = data.get("type", "unknown")
44|                # Optional: log specific important message types
45|                if msg_type in ["agent_register", "thought", "action"]:
46|                    logger.debug(f"Received {msg_type} from {addr}")
47|            except (json.JSONDecodeError, TypeError):
48|                pass
49|
50|            # Broadcast to all OTHER clients
51|            if not clients:
52|                continue
53|                
54|            disconnected = set()
55|            # Create broadcast tasks, tracking which client each task targets
56|            task_client_pairs = []
57|            for client in clients:
58|                if client != websocket and client.open:
59|                    task = asyncio.create_task(client.send(message))
60|                    task_client_pairs.append((task, client))
61|
62|            if task_client_pairs:
63|                tasks = [pair[0] for pair in task_client_pairs]
64|                results = await asyncio.gather(*tasks, return_exceptions=True)
65|                for i, result in enumerate(results):
66|                    if isinstance(result, Exception):
67|                        target_client = task_client_pairs[i][1]
68|                        logger.error(f"Failed to send to client {target_client.remote_address}: {result}")
69|                        disconnected.add(target_client)
70|            
71|            if disconnected:
72|                clients.difference_update(disconnected)
73|                
74|    except websockets.exceptions.ConnectionClosed:
75|        logger.debug(f"Connection closed by client {addr}")
76|    except Exception as e:
77|        logger.error(f"Error handling client {addr}: {e}")
78|    finally:
79|        clients.discard(websocket)
80|        logger.info(f"Client disconnected {addr}. Total clients: {len(clients)}")
81|
82|async def main():
83|    """Main server loop with graceful shutdown."""
84|    logger.info(f"Starting Nexus WS gateway on ws://{HOST}:{PORT}")
85|    
86|    # Set up signal handlers for graceful shutdown
87|    loop = asyncio.get_running_loop()
88|    stop = loop.create_future()
89|    
90|    def shutdown():
91|        if not stop.done():
92|            stop.set_result(None)
93|
94|    for sig in (signal.SIGINT, signal.SIGTERM):
95|        try:
96|            loop.add_signal_handler(sig, shutdown)
97|        except NotImplementedError:
98|            # Signal handlers not supported on Windows
99|            pass
100|
101|    async with websockets.serve(broadcast_handler, HOST, PORT):
102|        logger.info("Gateway is ready and listening.")
103|        await stop
104|        
105|    logger.info("Shutting down Nexus WS gateway...")
106|    # Close any remaining client connections (handlers may have already cleaned up)
107|    remaining = {c for c in clients if c.open}
108|    if remaining:
109|        logger.info(f"Closing {len(remaining)} active connections...")
110|        close_tasks = [client.close() for client in remaining]
111|        await asyncio.gather(*close_tasks, return_exceptions=True)
112|    clients.clear()
113|    
114|    logger.info("Shutdown complete.")
115|
116|if __name__ == "__main__":
117|    try:
118|        asyncio.run(main())
119|    except KeyboardInterrupt:
120|        pass
121|    except Exception as e:
122|        logger.critical(f"Fatal server error: {e}")
123|        sys.exit(1)

style.css

1|/* === NEXUS DESIGN SYSTEM === */
2|:root {
3|  --font-display: 'Orbitron', sans-serif;
4|  --font-body: 'JetBrains Mono', monospace;
5|
6|  --color-bg: #050510;
7|  --color-surface: rgba(10, 15, 40, 0.85);
8|  --color-border: rgba(74, 240, 192, 0.2);
9|  --color-border-bright: rgba(74, 240, 192, 0.5);
10|
11|  --color-text: #e0f0ff;
12|  --color-text-muted: #8a9ab8;
13|  --color-text-bright: #ffffff;
14|
15|  --color-primary: #4af0c0;
16|  --color-primary-dim: rgba(74, 240, 192, 0.3);
17|  --color-secondary: #7b5cff;
18|  --color-danger: #ff4466;
19|  --color-warning: #ffaa22;
20|  --color-gold: #ffd700;
21|
22|  --text-xs: 11px;
23|  --text-sm: 13px;
24|  --text-base: 15px;
25|  --text-lg: 18px;
26|  --text-xl: 24px;
27|  --text-2xl: 36px;
28|
29|  --space-1: 4px;
30|  --space-2: 8px;
31|  --space-3: 12px;
32|  --space-4: 16px;
33|  --space-6: 24px;
34|  --space-8: 32px;
35|
36|  --panel-blur: 16px;
37|  --panel-radius: 8px;
38|  --transition-ui: 200ms cubic-bezier(0.16, 1, 0.3, 1);
39|}
40|
41|*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
42|
43|html, body {
44|  width: 100%;
45|  height: 100%;
46|  overflow: hidden;
47|  background: var(--color-bg);
48|  font-family: var(--font-body);
49|  color: var(--color-text);
50|  -webkit-font-smoothing: antialiased;
51|}
52|
53|canvas#nexus-canvas {
54|  display: block;
55|  width: 100vw;
56|  height: 100vh;
57|  position: fixed;
58|  top: 0;
59|  left: 0;
60|}
61|
62|/* === LOADING SCREEN === */
63|#loading-screen {
64|  position: fixed;
65|  inset: 0;
66|  z-index: 1000;
67|  background: var(--color-bg);
68|  display: flex;
69|  align-items: center;
70|  justify-content: center;
71|  transition: opacity 0.8s ease;
72|}
73|#loading-screen.fade-out {
74|  opacity: 0;
75|  pointer-events: none;
76|}
77|.loader-content {
78|  text-align: center;
79|}
80|.loader-sigil {
81|  margin-bottom: var(--space-6);
82|}
83|.loader-title {
84|  font-family: var(--font-display);
85|  font-size: var(--text-2xl);
86|  font-weight: 700;
87|  letter-spacing: 0.3em;
88|  color: var(--color-primary);
89|  text-shadow: 0 0 30px rgba(74, 240, 192, 0.4);
90|  margin-bottom: var(--space-2);
91|}
92|.loader-subtitle {
93|  font-size: var(--text-sm);
94|  color: var(--color-text-muted);
95|  letter-spacing: 0.1em;
96|  margin-bottom: var(--space-6);
97|}
98|.loader-bar {
99|  width: 200px;
100|  height: 2px;
101|  background: rgba(74, 240, 192, 0.15);
102|  border-radius: 1px;
103|  margin: 0 auto;
104|  overflow: hidden;
105|}
106|.loader-fill {
107|  height: 100%;
108|  width: 0%;
109|  background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
110|  border-radius: 1px;
111|  transition: width 0.3s ease;
112|}
113|
114|/* === ENTER PROMPT === */
115|#enter-prompt {
116|  position: fixed;
117|  inset: 0;
118|  z-index: 500;
119|  background: rgba(5, 5, 16, 0.7);
120|  display: flex;
121|  align-items: center;
122|  justify-content: center;
123|  cursor: pointer;
124|  transition: opacity 0.5s ease;
125|}
126|#enter-prompt.fade-out {
127|  opacity: 0;
128|  pointer-events: none;
129|}
130|.enter-content {
131|  text-align: center;
132|}
133|.enter-content h2 {
134|  font-family: var(--font-display);
135|  font-size: var(--text-xl);
136|  color: var(--color-primary);
137|  letter-spacing: 0.2em;
138|  text-shadow: 0 0 20px rgba(74, 240, 192, 0.3);
139|  margin-bottom: var(--space-2);
140|}
141|.enter-content p {
142|  font-size: var(--text-sm);
143|  color: var(--color-text-muted);
144|  animation: pulse-text 2s ease-in-out infinite;
145|}
146|@keyframes pulse-text {
147|  0%, 100% { opacity: 0.5; }
148|  50% { opacity: 1; }
149|}
150|
151|/* === GAME UI (HUD) === */
152|.game-ui {
153|  position: fixed;
154|  inset: 0;
155|  pointer-events: none;
156|  z-index: 10;
157|  font-family: var(--font-body);
158|  color: var(--color-text);
159|}
160|.game-ui button, .game-ui input, .game-ui [data-interactive] {
161|  pointer-events: auto;
162|}
163|
164|/* Top Right Container */
165|.hud-top-right {
166|  position: absolute;
167|  top: var(--space-3);
168|  right: var(--space-3);
169|  display: flex;
170|  flex-direction: column;
171|  align-items: flex-end;
172|  gap: var(--space-3);
173|  pointer-events: none;
174|}
175|.hud-top-right > * {
176|  pointer-events: auto;
177|}
178|
179|.hud-icon-btn {
180|  background: rgba(10, 15, 40, 0.7);
181|  border: 1px solid var(--color-primary);
182|  color: var(--color-primary);
183|  padding: 8px 12px;
184|  font-family: var(--font-display);
185|  font-size: 11px;
186|  font-weight: 600;
187|  cursor: pointer;
188|  display: flex;
189|  align-items: center;
190|  gap: 8px;
191|  transition: all var(--transition-ui);
192|  backdrop-filter: blur(5px);
193|  box-shadow: 0 0 10px rgba(74, 240, 192, 0.2);
194|  letter-spacing: 0.1em;
195|}
196|
197|.hud-icon-btn:hover {
198|  background: var(--color-primary);
199|  color: var(--color-bg);
200|  box-shadow: 0 0 20px var(--color-primary);
201|}
202|
203|.hud-status-item {
204|  display: flex;
205|  align-items: center;
206|  gap: 8px;
207|  background: rgba(0, 0, 0, 0.5);
208|  padding: 4px 12px;
209|  border-radius: 20px;
210|  border: 1px solid rgba(255, 255, 255, 0.1);
211|  font-family: var(--font-body);
212|  font-size: 10px;
213|  letter-spacing: 0.1em;
214|  color: var(--color-text-muted);
215|  margin-bottom: 8px;
216|  pointer-events: auto;
217|}
218|
219|.hud-status-item .status-dot {
220|  width: 6px;
221|  height: 6px;
222|  border-radius: 50%;
223|  background: var(--color-danger);
224|}
225|
226|.hud-status-item.online .status-dot {
227|  background: var(--color-primary);
228|  box-shadow: 0 0 5px var(--color-primary);
229|}
230|
231|.hud-status-item.standby .status-dot {
232|  background: var(--color-gold);
233|  box-shadow: 0 0 5px var(--color-gold);
234|}
235|
236|.hud-status-item.online .status-label {
237|  color: #fff;
238|}
239|
240|.hud-icon {
241|  font-size: 16px;
242|}
243|
244|/* Portal Atlas Overlay */
...[truncated]...

index.html

1|<!DOCTYPE html>
2|<html lang="en" data-theme="dark">
3|<head>
4|<!--
5|   ______                            __
6|  / ____/___  ____ ___  ____  __  __/ /____  _____
7| / /   / __ \/ __ `__ \/ __ \/ / / / __/ _ \/ ___/
8|/ /___/ /_/ / / / / / / /_/ / /_/ / /_/  __/ /
9|\____/\____/_/ /_/ /_/ .___/\__,_/\__/\___/_/
10|                    /_/
11|        Created with Perplexity Computer
12|        https://www.perplexity.ai/computer
13|-->
14|<meta name="generator" content="Perplexity Computer">
15|<meta name="author" content="Perplexity Computer">
16|<meta property="og:see_also" content="https://www.perplexity.ai/computer">
17|<link rel="author" href="https://www.perplexity.ai/computer">
18|
19|<meta charset="UTF-8">
20|<meta name="viewport" content="width=device-width, initial-scale=1.0">
21|<title>The Nexus — Timmy's Sovereign Home</title>
22|<link rel="preconnect" href="https://fonts.googleapis.com">
23|<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
24|<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=Orbitron:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
25|<link rel="stylesheet" href="./style.css">
26|<link rel="manifest" href="./manifest.json">
27|<script type="importmap">
28|{
29|  "imports": {
30|    "three": "https://cdn.jsdelivr.net/npm/three@0.183.0/build/three.module.js",
31|    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.183.0/examples/jsm/"
32|  }
33|}
34|</script>
35|</head>
36|<body>
37|<!-- Loading Screen -->
38|<div id="loading-screen">
39|  <div class="loader-content">
40|    <div class="loader-sigil">
41|      <svg viewBox="0 0 120 120" width="120" height="120">
42|        <defs>
43|          <linearGradient id="sigil-grad" x1="0%" y1="0%" x2="100%" y2="100%">
44|            <stop offset="0%" stop-color="#4af0c0"/>
45|            <stop offset="100%" stop-color="#7b5cff"/>
46|          </linearGradient>
47|        </defs>
48|        <circle cx="60" cy="60" r="55" fill="none" stroke="url(#sigil-grad)" stroke-width="1.5" opacity="0.4"/>
49|        <circle cx="60" cy="60" r="45" fill="none" stroke="url(#sigil-grad)" stroke-width="1" opacity="0.3">
50|          <animateTransform attributeName="transform" type="rotate" from="0 60 60" to="360 60 60" dur="8s" repeatCount="indefinite"/>
51|        </circle>
52|        <polygon points="60,15 95,80 25,80" fill="none" stroke="#4af0c0" stroke-width="1.5" opacity="0.6">
53|          <animateTransform attributeName="transform" type="rotate" from="0 60 60" to="-360 60 60" dur="12s" repeatCount="indefinite"/>
54|        </polygon>
55|        <circle cx="60" cy="60" r="8" fill="#4af0c0" opacity="0.8">
56|          <animate attributeName="r" values="6;10;6" dur="2s" repeatCount="indefinite"/>
57|          <animate attributeName="opacity" values="0.5;1;0.5" dur="2s" repeatCount="indefinite"/>
58|        </circle>
59|      </svg>
60|    </div>
61|    <h1 class="loader-title">THE NEXUS</h1>
62|    <p class="loader-subtitle">Initializing Sovereign Space...</p>
63|    <div id="boot-message" style="display:none; margin-top:12px; max-width:420px; color:#d9f7ff; font-family:'JetBrains Mono', monospace; font-size:13px; line-height:1.6; text-align:center;"></div>
64|    <div class="loader-bar"><div class="loader-fill" id="load-progress"></div></div>
65|  </div>
66|</div>
67|
68|<!-- HUD Overlay -->
69|<div id="hud" class="game-ui" style="display:none;">
70|  <!-- GOFAI HUD Panels -->
71|  <div class="gofai-hud">
72|    <div class="hud-panel" id="symbolic-log">
73|      <div class="panel-header">SYMBOLIC ENGINE</div>
74|      <div id="symbolic-log-content" class="panel-content"></div>
75|    </div>
76|    <div class="hud-panel" id="blackboard-log">
77|      <div class="panel-header">BLACKBOARD</div>
78|      <div id="blackboard-log-content" class="panel-content"></div>
79|    </div>
80|    <div class="hud-panel" id="planner-log">
81|      <div class="panel-header">SYMBOLIC PLANNER</div>
82|      <div id="planner-log-content" class="panel-content"></div>
83|    </div>
84|    <div class="hud-panel" id="cbr-log">
85|      <div class="panel-header">CASE-BASED REASONER</div>
86|      <div id="cbr-log-content" class="panel-content"></div>
87|    </div>
88|    <div class="hud-panel" id="neuro-bridge-log">
89|      <div class="panel-header">NEURO-SYMBOLIC BRIDGE</div>
90|      <div id="neuro-bridge-log-content" class="panel-content"></div>
91|    </div>
92|    <div class="hud-panel" id="meta-log">
93|      <div class="panel-header">META-REASONING</div>
94|      <div id="meta-log-content" class="panel-content"></div>
95|    </div>
96|    <div class="hud-panel" id="sovereign-health-log">
97|      <div class="panel-header">SOVEREIGN HEALTH</div>
98|      <div id="sovereign-health-content" class="panel-content"></div>
99|    </div>
100|    <div class="hud-panel" id="calibrator-log">
101|      <div class="panel-header">ADAPTIVE CALIBRATOR</div>
102|      <div id="calibrator-log-content" class="panel-content"></div>
103|    </div>
104|    <div class="hud-panel" id="reasoning-trace">
105|      <div class="trace-header-container">
106|        <div class="panel-header"><span class="trace-icon">🧠</span> REASONING TRACE</div>
107|        <div class="trace-controls">
108|          <button class="trace-btn" id="trace-clear" title="Clear trace">🗑️</button>
109|          <button class="trace-btn" id="trace-toggle" title="Toggle visibility">👁️</button>
110|          <button class="trace-btn" id="trace-export" title="Export trace">📤</button>
111|        </div>
112|      </div>
113|      <div class="trace-task" id="trace-task">No active task</div>
114|      <div class="trace-counter" id="trace-counter">0 steps</div>
115|      <div id="reasoning-trace-content" class="panel-content trace-content"></div>
116|    </div>
117|  </div>
118|
119|  <!-- Evennia Room Snapshot Panel -->
120|  <div id="evennia-room-panel" class="evennia-room-panel" style="display:none;">
...[truncated]...

DEVELOPMENT.md

1|# Development Workflow
2|
3|## Branching Strategy
4|- Feature branches: `feature/your-name/feature-name`
5|- Hotfix branches: `hotfix/issue-number`
6|- Release branches: `release/x.y.z`
7|
8|## Local Development
9|1. Clone repo: `git clone https://forge.alexanderwhitestone.com/Timmy_Foundation/the-nexus.git`
10|2. Create branch: `git checkout -b feature/your-feature`
11|3. Commit changes: `git commit -m "Fix: your change"`
12|4. Push branch: `git push origin feature/your-feature`
13|5. Create PR via Gitea UI
14|
15|## Testing
16|- Unit tests: `npm test`
17|- Linting: `npm run lint`
18|- CI/CD: `npm run ci`
19|
20|## Code Quality
21|- ✅ 100% test coverage
22|- ✅ Prettier formatting
23|- ✅ No eslint warnings

gofai_worker.js

1|const heuristic = (state, goal) => Object.keys(goal).reduce((h, key) => h + (state[key] === goal[key] ? 0 : Math.abs((state[key] || 0) - (goal[key] || 0))), 0), preconditionsMet = (state, preconditions = {}) => Object.entries(preconditions).every(([key, value]) => (typeof value === 'number' ? (state[key] || 0) >= value : state[key] === value));
2|const findPlan = (initialState, goalState, actions = []) => {
3|  const openSet = [{ state: initialState, plan: [], g: 0, h: heuristic(initialState, goalState) }];
4|  const visited = new Map([[JSON.stringify(initialState), 0]]);
5|  while (openSet.length) {
6|    openSet.sort((a, b) => (a.g + a.h) - (b.g + b.h));
7|    const { state, plan, g } = openSet.shift();
8|    if (heuristic(state, goalState) === 0) return plan;
9|    actions.forEach((action) => {
10|      if (!preconditionsMet(state, action.preconditions)) return;
11|      const nextState = { ...state, ...(action.effects || {}) };
12|      const key = JSON.stringify(nextState);
13|      const nextG = g + 1;
14|      if (!visited.has(key) || nextG < visited.get(key)) {
15|        visited.set(key, nextG);
16|        openSet.push({ state: nextState, plan: [...plan, action.name], g: nextG, h: heuristic(nextState, goalState) });
17|      }
18|    });
19|  }
20|  return [];
21|};
22|
23|self.onmessage = function(e) {
24|  const { type, data } = e.data;
25|  if (type === 'REASON') {
26|    const factMap = new Map(data.facts || []);
27|    const results = (data.rules || []).filter((rule) => (rule.triggerFacts || []).every((fact) => factMap.get(fact))).map((rule) => ({ rule: rule.description, outcome: rule.workerOutcome || 'OFF-THREAD MATCH', triggerFacts: rule.triggerFacts || [], confidence: rule.confidence ?? 0.5 }));
28|    self.postMessage({ type: 'REASON_RESULT', results });
29|    return;
30|  }
31|  if (type === 'PLAN') {
32|    const plan = findPlan(data.initialState || {}, data.goalState || {}, data.actions || []);
33|    self.postMessage({ type: 'PLAN_RESULT', plan });
34|  }
35|};

provenance.json

1|{
2|  "generated_at": "2026-04-11T01:14:54.632326+00:00",
3|  "repo": "Timmy_Foundation/the-nexus",
4|  "git": {
5|    "commit": "d408d2c365a9efc0c1e3a9b38b9cc4eed75695c5",
6|    "branch": "mimo/build/issue-686",
7|    "remote": "https://forge.alexanderwhitestone.com/Timmy_Foundation/the-nexus.git",
8|    "dirty": true
9|  },
10|  "files": {
11|    "index.html": {
12|      "sha256": "71ba27afe8b6b42a09efe09d2b3017599392ddc3bc02543b31c2277dfb0b82cc",
13|      "size": 25933
14|    },
15|    "app.js": {
16|      "sha256": "2b765a724a0fcda29abd40ba921bc621d2699f11d0ba14cf1579cbbdafdc5cd5",
17|      "size": 132902
18|    },
19|    "style.css": {
20|      "sha256": "cd3068d03eed6f52a00bbc32cfae8fba4739b8b3cb194b3ec09fd747a075056d",
21|      "size": 44198
22|    },
23|    "gofai_worker.js": {
24|      "sha256": "d292f110aa12a8aa2b16b0c2d48e5b4ce24ee15b1cffb409ab846b1a05a91de2",
25|      "size": 969
26|    },
27|    "server.py": {
28|      "sha256": "e963cc9715accfc8814e3fe5c44af836185d66740d5a65fd0365e9c629d38e05",
29|      "size": 4185
30|    },
31|    "portals.json": {
32|      "sha256": "889a5e0f724eb73a95f960bca44bca232150bddff7c1b11f253bd056f3683a08",
33|      "size": 3442
34|    },
35|    "vision.json": {
36|      "sha256": "0e3b5c06af98486bbcb2fc2dc627dc8b7b08aed4c3a4f9e10b57f91e1e8ca6ad",
37|      "size": 1658
38|    },
39|    "manifest.json": {
40|      "sha256": "352304c4f7746f5d31cbc223636769969dd263c52800645c01024a3a8489d8c9",
41|      "size": 495
42|    },
43|    "nexus/components/spatial-memory.js": {
44|      "sha256": "60170f6490ddd743acd6d285d3a1af6cad61fbf8aaef3f679ff4049108eac160",
45|      "size": 32782
46|    },
47|    "nexus/components/session-rooms.js": {
48|      "sha256": "9997a60dda256e38cb4645508bf9e98c15c3d963b696e0080e3170a9a7fa7cf1",
49|      "size": 15113
50|    },
51|    "nexus/components/timeline-scrubber.js": {
52|      "sha256": "f8a17762c2735be283dc5074b13eb00e1e3b2b04feb15996c2cf0323b46b6014",
53|      "size": 7177
54|    },
55|    "nexus/components/memory-particles.js": {
56|      "sha256": "1be5567a3ebb229f9e1a072c08a25387ade87cb4a1df6a624e5c5254d3bef8fa",
57|      "size": 14216
58|    }
59|  },
60|  "missing": [],
61|  "file_count": 12
62|}

BROWSER_CONTRACT.md

1|# Browser Contract — The Nexus
2|
3|The minimal set of guarantees a working Nexus browser surface must satisfy.
4|This is the target the smoke suite validates against.
5|
6|## 1. Static Assets
7|
8|The following files MUST exist at the repo root and be serveable:
9|
10|| File              | Purpose                          |
11||-------------------|----------------------------------|
12|| `index.html`      | Entry point HTML shell           |
13|| `app.js`          | Main Three.js application        |
14|| `style.css`       | Visual styling                   |
15|| `portals.json`    | Portal registry data             |
16|| `vision.json`     | Vision points data               |
17|| `manifest.json`   | PWA manifest                     |
18|| `gofai_worker.js` | GOFAI web worker                 |
19|| `server.py`       | WebSocket bridge                 |
20|
21|## 2. DOM Contract
22|
23|The following elements MUST exist after the page loads:
24|
25|| ID                    | Type     | Purpose                            |
26||-----------------------|----------|------------------------------------|
27|| `nexus-canvas`        | canvas   | Three.js render target             |
28|| `loading-screen`      | div      | Initial loading overlay            |
29|| `hud`                 | div      | Main HUD container                 |
30|| `chat-panel`          | div      | Chat interface panel               |
31|| `chat-input`          | input    | Chat text input                    |
32|| `chat-messages`       | div      | Chat message history               |
33|| `chat-send`           | button   | Send message button                |
34|| `chat-toggle`         | button   | Collapse/expand chat               |
35|| `debug-overlay`       | div      | Debug info overlay                 |
36|| `nav-mode-label`      | span     | Current navigation mode display    |
37|| `ws-status-dot`       | span     | Hermes WS connection indicator     |
38|| `hud-location-text`   | span     | Current location label             |
39|| `portal-hint`         | div      | Portal proximity hint              |
40|| `spatial-search`      | div      | Spatial memory search overlay      |
41|| `enter-prompt`        | div      | Click-to-enter overlay (transient) |
42|
43|## 3. Three.js Contract
44|
45|After initialization completes:
46|
47|- `window` has a THREE renderer created from `#nexus-canvas`
48|- The canvas has a WebGL rendering context
49|- `scene` is a `THREE.Scene` with fog
50|- `camera` is a `THREE.PerspectiveCamera`
51|- `portals` array is populated from `portals.json`
52|- At least one portal mesh exists in the scene
53|- The render loop is running (`requestAnimationFrame` active)
54|
55|## 4. Loading Contract
56|
57|1. Page loads → loading screen visible
58|2. Progress bar fills to 100%
59|3. Loading screen fades out
60|4. Enter prompt appears
61|5. User clicks → enter prompt fades → HUD appears
62|
63|## 5. Provenance Contract
64|
65|A validation run MUST prove:
66|
67|- The served files match a known hash manifest from `Timmy_Foundation/the-nexus` main
68|- No file is served from `/Users/apayne/the-matrix` or other stale source
69|- The hash manifest is generated from a clean git checkout
70|- Screenshot evidence is captured and timestamped
71|
72|## 6. Data Contract
73|
74|- `portals.json` MUST parse as valid JSON array
75|- Each portal MUST have: `id`, `name`, `status`, `destination`
76|- `vision.json` MUST parse as valid JSON
77|- `manifest.json` MUST have `name`, `start_url`, `theme_color`
78|
79|## 7. WebSocket Contract
80|
81|- `server.py` starts without error on port 8765
82|- A browser client can connect to `ws://localhost:8765`
83|- The connection status indicator reflects connected state

tests/quarantine/README.md

1|# tests/quarantine/
2|
3|This directory holds tests that have been **quarantined** because they are
4|flaky or temporarily broken.  Quarantine keeps the main test suite green while
5|ensuring no test is silently deleted or forgotten.
6|
7|## Rules
8|
9|1. Every file here must correspond to an open issue.
10|2. Every test here must carry `@pytest.mark.quarantine(reason="#NNN")`.
11|3. Quarantined tests are **excluded from the default CI run** but are included
12|   when you pass `--run-quarantine`.
13|4. The quarantine is a **temporary holding area**, not a graveyard.  If a
14|   quarantined test's issue has been open for more than 30 days with no
15|   progress, escalate it.
16|
17|## Adding a test
18|
19|```python
20|# tests/quarantine/test_my_flaky_thing.py
21|import pytest
22|
23|@pytest.mark.quarantine(reason="Flaky: #1234")
24|def test_my_flaky_thing():
25|    ...
26|```
27|
28|See `docs/QUARANTINE_PROCESS.md` for the complete workflow.

nexus/README.md

1|# Nexus Symbolic Engine (Layer 4)
2|
3|This directory contains the core symbolic reasoning and agent state management components for the Nexus. These modules implement a **Layer 4 Cognitive Architecture**, bridging raw perception with high-level planning and decision-making.
4|
5|## Architecture Overview
6|
7|The system follows a **Blackboard Architecture**, where a central shared memory space allows decoupled modules to communicate and synchronize state.
8|
9|### Core Components
10|
11|- **`SymbolicEngine`**: A GOFAI (Good Old Fashioned AI) engine that manages facts and rules. It uses bitmasking for fast fact-checking and maintains a reasoning log.
12|- **`AgentFSM`v*: A Finite State Machine for agents. It transitions between states (e.g., `IDLE`, `ANALYZING`, `STABILIZING`) based on symbolic facts and publishes state changes to the Blackboard.
13|- **`Blackboard`**: The central communication hub. It allows modules to `write` and `read` state, and `subscribe` to changes.
14|- **`SymbolicPlanner` (A*)**: A heuristic search planner that generates action sequences to reach a goal state.
15|- **`HTNPlanner`**: A Hierarchical Task Network planner for complex, multi-step task decomposition.
16|- **`CaseBasedReasoner`**: A memory-based reasoning module that retrieves and adapts past solutions to similar situations.
17|- **`NeuroSymbolicBridge`**: Translates raw perception data (e.g., energy levels, stability) into symbolic concepts (e.g., `CRITICAL_DRAIN_PATTERN`).
18|- **`MetaReasoningLayer`**: Monitors performance, caches plans, and reflects on the system's own reasoning processes.
19|
20|## Usage
21|
22|[```javascript
23|import { SymbolicEngine, Blackboard, AgentFSM } from './symbolic-engine.js';
24|
25|const blackboard = new Blackboard();
26|const engine = new SymbolicEngine();
27|const fsm = new AgentFSM('Timmy', 'IDLE', blackboard);
28|
29|// Add facts and rules
30|engine.addFact('activePortals', 3);
31|engine.addRule(
32|  (facts) => facts.get('activePortals') > 2,
33|  () => 'STABILIZE_PORTALS',
34|  'High portal activity detected'
35|f);
36|
37|// Run reasoning loop
38|engine.reason();
39|fsm.update(engine.facts);
40|```
41|Z
42|## Testing
43|
44|Run the symbolic engine tests using:
45|[```bash
46|node nexus/symbolic-engine.test.js
47|```
48|Z

tests/conftest.py

1|"""Pytest configuration for the test suite."""
2|import re
3|import pytest
4|
5|# Configure pytest-asyncio mode
6|pytest_plugins = ["pytest_asyncio"]
7|
8|# Pattern that constitutes a valid issue link in a skip reason.
9|# Accepts: #NNN, https?://..., or JIRA-NNN style keys.
10|_ISSUE_LINK_RE = re.compile(
11|    r"(#\d+|https?://\S+|[A-Z]+-\d+)",
12|    re.IGNORECASE,
13|)
14|
15|
16|def _has_issue_link(reason: str) -> bool:
17|    """Return True if *reason* contains a recognisable issue reference."""
18|    return bool(_ISSUE_LINK_RE.search(reason or ""))
19|
20|
21|def _skip_reason(report) -> str:
22|    """Extract the human-readable skip reason from a pytest report."""
23|    longrepr = getattr(report, "longrepr", None)
24|    if longrepr is None:
25|        return ""
26|    if isinstance(longrepr, tuple) and len(longrepr) >= 3:
27|        # (filename, lineno, "Skipped: <reason>")
28|        return str(longrepr[2])
29|    return str(longrepr)
30|
31|
32|def pytest_configure(config):
33|    """Configure pytest."""
34|    config.addinivalue_line(
35|        "markers", "integration: mark test as integration test (requires MCP servers)"
36|    )
37|    config.addinivalue_line(
38|        "markers",
39|        "quarantine: mark test as quarantined (flaky/broken, tracked by issue)",
40|    )
41|
42|
43|def pytest_addoption(parser):
44|    """Add custom command-line options."""
45|    parser.addoption(
46|        "--run-integration",
47|        action="store_true",
48|        default=False,
49|        help="Run integration tests that require MCP servers",
50|    )
51|    parser.addoption(
52|        "--no-skip-enforcement",
53|        action="store_true",
54|        default=False,
55|        help="Disable poka-yoke enforcement of issue-linked skip reasons (CI escape hatch)",
56|    )
57|
58|
59|def pytest_collection_modifyitems(config, items):
60|    """Modify test collection based on options."""
61|    if not config.getoption("--run-integration"):
62|        skip_integration = pytest.mark.skip(
63|            reason="Integration tests require --run-integration and MCP servers running"
64|        )
65|        for item in items:
66|            if "integration" in item.keywords:
67|                item.add_marker(skip_integration)
68|
69|
70|# ---------------------------------------------------------------------------
71|# POKA-YOKE: Treat skipped tests as failures unless they carry an issue link.
72|# ---------------------------------------------------------------------------
73|
74|@pytest.hookimpl(hookwrapper=True)
75|def pytest_runtest_makereport(item, call):
76|    """Intercept skipped reports and fail them if they lack an issue link.
77|
78|    Exceptions:
79|    * Tests in tests/quarantine/ — explicitly quarantined, issue link required
80|      on the quarantine marker, not the skip marker.
81|    * Tests using environment-variable-based ``skipif`` conditions — these are
82|      legitimate CI gates (RUN_INTEGRATION_TESTS, RUN_LIVE_TESTS, etc.) where
83|      the *condition* is the gate, not a developer opt-out.  We allow these
84|      only when the skip reason mentions a recognised env-var pattern.
85|    * --no-skip-enforcement flag set (emergency escape hatch).
86|    """
87|    outcome = yield
88|    report = outcome.get_result()
89|
90|    if not report.skipped:
91|        return
92|
93|    # Escape hatch for emergency use.
94|    if item.config.getoption("--no-skip-enforcement", default=False):
95|        return
96|
97|    reason = _skip_reason(report)
98|
99|    # Allow quarantined tests — they are tracked by their quarantine marker.
100|    if "quarantine" in item.keywords:
101|        return
102|
103|    # Allow env-var-gated skipif conditions.  These come from the
104|    # pytest_collection_modifyitems integration gate above, or from
105|    # explicit @pytest.mark.skipif(..., reason="... requires ENV=1 ...")
106|    _ENV_GATE_RE = re.compile(r"(require|needs|set)\s+\w+=[^\s]+", re.IGNORECASE)
107|    if _ENV_GATE_RE.search(reason):
108|        return
109|
110|    # Allow skips added by the integration gate in this very conftest.
111|    if "require --run-integration" in reason:
112|        return
113|
114|    # Anything else needs an issue link.
115|    if not _has_issue_link(reason):
116|        report.outcome = "failed"
117|        report.longrepr = (
118|            "[POKA-YOKE] Skip without issue link is not allowed.\n"
119|            f"  Reason given: {reason!r}\n"
120|            "  Fix: add an issue reference to the skip reason, e.g.:\n"
121|            "    @pytest.mark.skip(reason='Broken until #NNN is resolved')\n"
122|            "  Or quarantine the test: move it to tests/quarantine/ and\n"
123|            "  file an issue — see docs/QUARANTINE_PROCESS.md"
124|        )

tests/test_a2a.py

1|"""
2|Tests for A2A Protocol implementation.
3|
4|Covers:
5|- Type serialization roundtrips (Agent Card, Task, Message, Artifact, Part)
6|- JSON-RPC envelope
7|- Agent Card building from YAML config
8|- Registry operations (register, list, filter)
9|- Client/server integration (end-to-end task delegation)
10|"""
11|
12|from __future__ import annotations
13|
14|import asyncio
15|import json
16|import pytest
17|from pathlib import Path
18|from unittest.mock import AsyncMock, patch, MagicMock
19|
20|from nexus.a2a.types import (
21|    A2AError,
22|    AgentCard,
23|    AgentCapabilities,
24|    AgentInterface,
25|    AgentSkill,
26|    Artifact,
27|    DataPart,
28|    FilePart,
29|    JSONRPCError,
30|    JSONRPCRequest,
31|    JSONRPCResponse,
32|    Message,
33|    Role,
34|    Task,
35|    TaskState,
36|    TaskStatus,
37|    TextPart,
38|    part_from_dict,
39|    part_to_dict,
40|)
41|from nexus.a2a.card import build_card, load_card_config
42|from nexus.a2a.registry import LocalFileRegistry
43|
44|
45|# === Type Serialization Roundtrips ===
46|
47|
48|class TestTextPart:
49|    def test_roundtrip(self):
50|        p = TextPart(text="hello world")
51|        d = p.to_dict()
52|        assert d == {"text": "hello world"}
53|        p2 = part_from_dict(d)
54|        assert isinstance(p2, TextPart)
55|        assert p2.text == "hello world"
56|
57|    def test_custom_media_type(self):
58|        p = TextPart(text="data", media_type="text/markdown")
59|        d = p.to_dict()
60|        assert d["mediaType"] == "text/markdown"
61|        p2 = part_from_dict(d)
62|        assert p2.media_type == "text/markdown"
63|
64|
65|class TestFilePart:
66|    def test_inline_roundtrip(self):
67|        p = FilePart(media_type="image/png", raw="base64data", filename="img.png")
68|        d = p.to_dict()
69|        assert d["raw"] == "base64data"
70|        assert d["filename"] == "img.png"
71|        p2 = part_from_dict(d)
72|        assert isinstance(p2, FilePart)
73|        assert p2.raw == "base64data"
74|
75|    def test_url_roundtrip(self):
76|        p = FilePart(media_type="application/pdf", url="https://example.com/doc.pdf")
77|        d = p.to_dict()
78|        assert d["url"] == "https://example.com/doc.pdf"
79|        p2 = part_from_dict(d)
80|        assert isinstance(p2, FilePart)
81|        assert p2.url == "https://example.com/doc.pdf"
82|
83|
84|class TestDataPart:
85|    def test_roundtrip(self):
86|        p = DataPart(data={"key": "value", "count": 42})
87|        d = p.to_dict()
88|        assert d["data"] == {"key": "value", "count": 42}
89|        p2 = part_from_dict(d)
90|        assert isinstance(p2, DataPart)
91|        assert p2.data["count"] == 42
92|
93|
94|class TestMessage:
95|    def test_roundtrip(self):
96|        msg = Message(
97|            role=Role.USER,
98|            parts=[TextPart(text="Hello agent")],
99|            metadata={"priority": "high"},
100|        )
101|        d = msg.to_dict()
102|        assert d["role"] == "ROLE_USER"
103|        assert d["parts"] == [{"text": "Hello agent"}]
104|        assert d["metadata"]["priority"] == "high"
105|
106|        msg2 = Message.from_dict(d)
107|        assert msg2.role == Role.USER
108|        assert isinstance(msg2.parts[0], TextPart)
109|        assert msg2.parts[0].text == "Hello agent"
110|        assert msg2.metadata["priority"] == "high"
111|
112|    def test_multi_part(self):
113|        msg = Message(
114|            role=Role.AGENT,
115|            parts=[
116|                TextPart(text="Here's the report"),
117|                DataPart(data={"status": "healthy"}),
118|            ],
119|        )
120|        d = msg.to_dict()
121|        assert len(d["parts"]) == 2
122|        msg2 = Message.from_dict(d)
123|        assert len(msg2.parts) == 2
124|        assert isinstance(msg2.parts[0], TextPart)
125|        assert isinstance(msg2.parts[1], DataPart)
126|
127|
128|class TestArtifact:
129|    def test_roundtrip(self):
130|        art = Artifact(
131|            parts=[TextPart(text="result data")],
132|            name="report",
133|            description="CI health report",
134|        )
135|        d = art.to_dict()
136|        assert d["name"] == "report"
137|        assert d["description"] == "CI health report"
138|
139|        art2 = Artifact.from_dict(d)
140|        assert art2.name == "report"
141|        assert isinstance(art2.parts[0], TextPart)
142|        assert art2.parts[0].text == "result data"
143|
144|
145|class TestTask:
146|    def test_roundtrip(self):
147|        task = Task(
148|            id="test-123",
149|            status=TaskStatus(state=TaskState.WORKING),
150|            history=[
151|                Message(role=Role.USER, parts=[TextPart(text="Do X")]),
152|            ],
153|        )
154|        d = task.to_dict()
155|        assert d["id"] == "test-123"
156|        assert d["status"]["state"] == "TASK_STATE_WORKING"
157|
158|        task2 = Task.from_dict(d)
159|        assert task2.id == "test-123"
160|        assert task2.status.state == TaskState.WORKING
161|        assert len(task2.history) == 1
162|
163|    def test_with_artifacts(self):
164|        task = Task(
165|            id="art-task",
166|            status=TaskStatus(state=TaskState.COMPLETED),
167|            artifacts=[
168|                Artifact(
169|                    parts=[TextPart(text="42")],
170|                    name="answer",
171|                )
172|            ],
173|        )
174|        d = task.to_dict()
175|        assert len(d["artifacts"]) == 1
176|        task2 = Task.from_dict(d)
177|        assert task2.artifacts[0].name == "answer"
178|
179|    def test_terminal_states(self):
180|        for state in [
181|            TaskState.COMPLETED,
182|            TaskState.FAILED,
183|            TaskState.CANCELED,
184|            TaskState.REJECTED,
185|        ]:
186|            assert state.terminal is True
187|
188|        for state in [
...[truncated]...

tests/boot.test.js

1|const { test } = require('node:test');
2|const assert = require('node:assert/strict');
3|const { bootPage } = require('../boot.js');
4|const el = (tagName = 'div') => ({ tagName, textContent: '', innerHTML: '', style: {}, children: [], type: '', src: '', appendChild(child) { this.children.push(child); } });
5|
6|test('bootPage handles file and http origins', () => {
7|  const loaderSubtitle = el(), bootMessage = el(), body = el('body');
8|  const doc = { body, querySelector: s => s === '.loader-subtitle' ? loaderSubtitle : null, getElementById: id => id === 'boot-message' ? bootMessage : null, createElement: tag => el(tag) };
9|  const fileResult = bootPage({ location: { protocol: 'file:' } }, doc);
10|  assert.equal(fileResult.mode, 'file');
11|  assert.equal(body.children.length, 0);
12|  assert.match(loaderSubtitle.textContent, /serve this world over http/i);
13|  assert.match(bootMessage.innerHTML, /python3 -m http\.server 8888/i);
14|  const httpResult = bootPage({ location: { protocol: 'http:' } }, doc);
15|  assert.equal(httpResult.mode, 'module');
16|  assert.equal(body.children.length, 1);
17|  assert.equal(body.children[0].tagName, 'script');
18|  assert.equal(body.children[0].type, 'module');
19|  assert.equal(body.children[0].src, './bootstrap.mjs');
20|});

docs/media/README.md

1|# Media Production — Veo/Flow Prototypes
2|
3|Issue #681: [MEDIA] Veo/Flow flythrough prototypes for The Nexus and Timmy.
4|
5|## Contents
6|
7|- `veo-storyboard.md` — Full storyboard for 5 clips with shot sequences, prompts, and design focus areas
8|- `clip-metadata.json` — Durable metadata for each clip (prompts, model, outputs, insights)
9|
10|## Clips Overview
11|
12|| ID | Title | Audience | Purpose |
13||----|-------|----------|---------|
14|| clip-001 | First Light | PUBLIC | The Nexus reveal teaser |
15|| clip-002 | Between Worlds | INTERNAL | Portal activation UX study |
16|| clip-003 | The Guardian's View | PUBLIC | Timmy's presence promo |
17|| clip-004 | The Void Between | INTERNAL | Ambient environment study |
18|| clip-005 | Command Center | INTERNAL | Terminal UI readability |
19|
20|## How to Generate
21|
22|### Via Flow (labs.google/flow)
23|1. Open `veo-storyboard.md`, copy the prompt for your clip
24|2. Go to labs.google/flow
25|3. Paste the prompt, select Veo 3.1
26|4. Generate (8-second clips)
27|5. Download output, update `clip-metadata.json` with output path and findings
28|
29|### Via Gemini App
30|1. Type "generate a video of [prompt text]" in Gemini
31|2. Uses Veo 3.1 Fast (slightly lower quality, faster)
32|3. Good for quick iteration on prompts
33|
34|### Via API (programmatic)
35|```python
36|from google import genai
37|client = genai.Client()
38|
39|# See: ai.google.dev/gemini-api/docs/video
40|response = client.models.generate_content(
41|    model="veo-3.1",
42|    contents="[prompt from storyboard]"
43|)
44|```
45|
46|## After Generation
47|
48|For each clip:
49|1. Save output file to `outputs/clip-XXX.mp4`
50|2. Update `clip-metadata.json`:
51|   - Add output file path to `output_files[]`
52|   - Fill in `design_insights.findings` with observations
53|   - Add `threejs_changes_suggested` if the clip reveals needed changes
54|3. Share internal clips with the team for design review
55|4. Use public clips in README, social media, project communication
56|
57|## Design Insight Workflow
58|
59|Each clip has specific questions it's designed to answer:
60|
61|**clip-001 (First Light)**
62|- Scale perception: platform vs. portals vs. terminal
63|- Color hierarchy: teal primary, purple secondary, gold accent
64|- Camera movement: cinematic or disorienting?
65|
66|**clip-002 (Between Worlds)**
67|- Activation distance: when does interaction become available?
68|- Transition feel: travel or teleportation?
69|- Overlay readability against portal glow
70|
71|**clip-003 (The Guardian's View)**
72|- Agent presence: alive or decorative?
73|- Crystal hologram readability
74|- Wide shot: world or tech demo?
75|
76|**clip-004 (The Void Between)**
77|- Void atmosphere: alive or empty?
78|- Particle systems: enhance or distract?
79|- Lighting hierarchy clarity
80|
81|**clip-005 (Command Center)**
82|- Text readability at 1080p
83|- Color-coded panel hierarchy
84|- Scan-line effect: retro or futuristic?
85|
86|## Constraints
87|
88|- 8-second clips max (Veo/Flow limitation)
89|- Queued generation (not instant)
90|- Content policies apply
91|- Ultra tier gets highest rate limits

tests/test_edge_tts.py

1|"""Tests for the edge-tts voice provider integration.
2|
3|Issue: #1126 — edge-tts voice provider
4|"""
5|from __future__ import annotations
6|
7|import asyncio
8|import sys
9|import types
10|from pathlib import Path
11|from unittest.mock import AsyncMock, MagicMock, patch
12|
13|import pytest
14|
15|
16|# ---------------------------------------------------------------------------
17|# Helpers — build a minimal fake edge_tts module so tests don't need the
18|# real package installed.
19|# ---------------------------------------------------------------------------
20|
21|def _make_fake_edge_tts():
22|    """Return a fake edge_tts module with a mock Communicate class."""
23|    fake = types.ModuleType("edge_tts")
24|
25|    class FakeCommunicate:
26|        def __init__(self, text, voice):
27|            self.text = text
28|            self.voice = voice
29|
30|        async def save(self, path: str):
31|            # Write a tiny stub so file-existence checks pass.
32|            Path(path).write_bytes(b"FAKE_MP3")
33|
34|    fake.Communicate = FakeCommunicate
35|    return fake
36|
37|
38|# ---------------------------------------------------------------------------
39|# Tests for EdgeTTSAdapter (bin/deepdive_tts.py)
40|# ---------------------------------------------------------------------------
41|
42|class TestEdgeTTSAdapter:
43|    """Tests for EdgeTTSAdapter in bin/deepdive_tts.py."""
44|
45|    def _import_adapter(self, fake_edge_tts=None):
46|        """Import EdgeTTSAdapter with optional fake edge_tts module."""
47|        # Ensure fresh import by temporarily inserting into sys.modules.
48|        if fake_edge_tts is not None:
49|            sys.modules["edge_tts"] = fake_edge_tts
50|        # Reload to pick up the injected module.
51|        import importlib
52|        import bin.deepdive_tts as mod
53|        importlib.reload(mod)
54|        return mod.EdgeTTSAdapter, mod.TTSConfig
55|
56|    def test_default_voice(self, tmp_path):
57|        """EdgeTTSAdapter uses en-US-GuyNeural when no voice_id is set."""
58|        fake = _make_fake_edge_tts()
59|        sys.modules["edge_tts"] = fake
60|
61|        import importlib
62|        import bin.deepdive_tts as mod
63|        importlib.reload(mod)
64|
65|        config = mod.TTSConfig(
66|            provider="edge-tts",
67|            voice_id="",
68|            output_dir=tmp_path,
69|        )
70|        adapter = mod.EdgeTTSAdapter(config)
71|        assert adapter.voice == mod.EdgeTTSAdapter.DEFAULT_VOICE
72|
73|    def test_custom_voice(self, tmp_path):
74|        """EdgeTTSAdapter respects explicit voice_id."""
75|        fake = _make_fake_edge_tts()
76|        sys.modules["edge_tts"] = fake
77|
78|        import importlib
79|        import bin.deepdive_tts as mod
80|        importlib.reload(mod)
81|
82|        config = mod.TTSConfig(
83|            provider="edge-tts",
84|            voice_id="en-US-JennyNeural",
85|            output_dir=tmp_path,
86|        )
87|        adapter = mod.EdgeTTSAdapter(config)
88|        assert adapter.voice == "en-US-JennyNeural"
89|
90|    def test_synthesize_returns_mp3(self, tmp_path):
91|        """synthesize() returns a .mp3 path and creates the file."""
92|        fake = _make_fake_edge_tts()
93|        sys.modules["edge_tts"] = fake
94|
95|        import importlib
96|        import bin.deepdive_tts as mod
97|        importlib.reload(mod)
98|
99|        config = mod.TTSConfig(
100|            provider="edge-tts",
101|            voice_id="",
102|            output_dir=tmp_path,
103|        )
104|        adapter = mod.EdgeTTSAdapter(config)
105|        output = tmp_path / "test_output"
106|        result = adapter.synthesize("Hello world", output)
107|
108|        assert result.suffix == ".mp3"
109|        assert result.exists()
110|
111|    def test_synthesize_passes_text_and_voice(self, tmp_path):
112|        """synthesize() passes the correct text and voice to Communicate."""
113|        fake = _make_fake_edge_tts()
114|        communicate_calls = []
115|
116|        class TrackingCommunicate:
117|            def __init__(self, text, voice):
118|                communicate_calls.append((text, voice))
119|
120|            async def save(self, path):
121|                Path(path).write_bytes(b"FAKE")
122|
123|        fake.Communicate = TrackingCommunicate
124|        sys.modules["edge_tts"] = fake
125|
126|        import importlib
127|        import bin.deepdive_tts as mod
128|        importlib.reload(mod)
129|
130|        config = mod.TTSConfig(
131|            provider="edge-tts",
132|            voice_id="en-GB-RyanNeural",
133|            output_dir=tmp_path,
134|        )
135|        adapter = mod.EdgeTTSAdapter(config)
136|        adapter.synthesize("Test sentence.", tmp_path / "out")
137|
138|        assert len(communicate_calls) == 1
139|        assert communicate_calls[0] == ("Test sentence.", "en-GB-RyanNeural")
140|
141|    def test_missing_package_raises(self, tmp_path):
142|        """synthesize() raises RuntimeError when edge-tts is not installed."""
143|        # Remove edge_tts from sys.modules to simulate missing package.
144|        sys.modules.pop("edge_tts", None)
145|
146|        import importlib
147|        import bin.deepdive_tts as mod
148|        importlib.reload(mod)
149|
150|        # Patch the import inside synthesize to raise ImportError.
151|        original_import = __builtins__.__import__ if hasattr(__builtins__, "__import__") else __import__
152|
153|        config = mod.TTSConfig(
154|            provider="edge-tts",
155|            voice_id="",
156|            output_dir=tmp_path,
157|        )
158|        adapter = mod.EdgeTTSAdapter(config)
159|
160|        with patch.dict(sys.modules, {"edge_tts": None}):
161|            with pytest.raises((RuntimeError, ImportError)):
162|                adapter.synthesize("Hello", tmp_path / "out")
163|
164|    def test_adapters_dict_includes_edge_tts(self):
...[truncated]...

tests/test_manifest.py

1|"""Tests for manifest.json PWA support. Fixes #832 (Missing manifest.json)."""
2|import json
3|from pathlib import Path
4|
5|
6|def test_manifest_exists() -> None:
7|    assert Path("manifest.json").exists(), "manifest.json must exist for PWA support"
8|
9|
10|def test_manifest_is_valid_json() -> None:
11|    content = Path("manifest.json").read_text()
12|    data = json.loads(content)
13|    assert isinstance(data, dict)
14|
15|
16|def test_manifest_has_required_pwa_fields() -> None:
17|    data = json.loads(Path("manifest.json").read_text())
18|    assert "name" in data, "manifest.json must have 'name'"
19|    assert "short_name" in data, "manifest.json must have 'short_name'"
20|    assert "start_url" in data, "manifest.json must have 'start_url'"
21|    assert "display" in data, "manifest.json must have 'display'"
22|    assert "icons" in data, "manifest.json must have 'icons'"
23|
24|
25|def test_manifest_icons_non_empty() -> None:
26|    data = json.loads(Path("manifest.json").read_text())
27|    assert len(data["icons"]) > 0, "manifest.json must define at least one icon"
28|
29|
30|def test_index_html_references_manifest() -> None:
31|    content = Path("index.html").read_text()
32|    assert 'rel="manifest"' in content, "index.html must have <link rel=\"manifest\">"
33|    assert "manifest.json" in content, "index.html must reference manifest.json"
34|
35|
36|def test_help_html_references_manifest() -> None:
37|    content = Path("help.html").read_text()
38|    assert 'rel="manifest"' in content, "help.html must have <link rel=\"manifest\">"
39|    assert "manifest.json" in content, "help.html must reference manifest.json"

tests/test_help_page.py

1|"""Tests for the /help page. Refs: #833 (Missing /help page)."""
2|from pathlib import Path
3|
4|
5|def test_help_html_exists() -> None:
6|    assert Path("help.html").exists(), "help.html must exist to resolve /help 404"
7|
8|
9|def test_help_html_is_valid_html() -> None:
10|    content = Path("help.html").read_text()
11|    assert "<!DOCTYPE html>" in content
12|    assert "<html" in content
13|    assert "</html>" in content
14|
15|
16|def test_help_page_has_required_sections() -> None:
17|    content = Path("help.html").read_text()
18|
19|    # Navigation controls section
20|    assert "Navigation Controls" in content
21|
22|    # Chat commands section
23|    assert "Chat" in content
24|
25|    # Portal reference
26|    assert "Portal" in content
27|
28|    # Back link to home
29|    assert 'href="/"' in content
30|
31|
32|def test_help_page_links_back_to_home() -> None:
33|    content = Path("help.html").read_text()
34|    assert 'href="/"' in content, "help page must have a link back to the main Nexus world"
35|
36|
37|def test_help_page_has_keyboard_controls() -> None:
38|    content = Path("help.html").read_text()
39|    # Movement keys are listed individually as <kbd> elements
40|    for key in ["<kbd>W</kbd>", "<kbd>A</kbd>", "<kbd>S</kbd>", "<kbd>D</kbd>",
41|                "Mouse", "Enter", "Esc"]:
42|        assert key in content, f"help page must document the {key!r} control"

tests/bootstrap.test.mjs

1|import test from 'node:test';
2|import assert from 'node:assert/strict';
3|import path from 'node:path';
4|import { fileURLToPath, pathToFileURL } from 'node:url';
5|import { readFileSync } from 'node:fs';
6|const __dirname = path.dirname(fileURLToPath(import.meta.url));
7|const repoRoot = path.resolve(__dirname, '..');
8|const load = () => import(pathToFileURL(path.join(repoRoot, 'bootstrap.mjs')).href);
9|const el = () => ({ textContent: '', innerHTML: '', style: {}, className: '' });
10|
11|test('boot shows file guidance', async () => {
12|  const { boot } = await load();
13|  const subtitle = el(), msg = el(); let calls = 0;
14|  const result = await boot({ win: { location: { protocol: 'file:' } }, doc: { getElementById: id => id === 'boot-message' ? msg : null, querySelector: s => s === '.loader-subtitle' ? subtitle : null }, importApp: async () => (calls += 1, {}) });
15|  assert.equal(result.mode, 'file'); assert.equal(calls, 0); assert.match(subtitle.textContent, /serve/i); assert.match(msg.innerHTML, /python3 -m http\.server 8888/i);
16|});
17|
18|test('sanitizer repairs synthetic and real app input', async () => {
19|  const { sanitizeAppModuleSource, loadAppModule, boot } = await load();
20|  const synthetic = ["import ResonanceVisualizer from './nexus/components/resonance-visualizer.js';\\nimport * as THREE from 'three';","const calibrator = boot();\\n  startRenderer();","import { SymbolicEngine, AgentFSM } from './nexus/symbolic-engine.js';","class SymbolicEngine {}","/**\n * Process Evennia-specific fields from Hermes WS messages.\n * Called from handleHermesMessage for any message carrying evennia metadata.\n */\nfunction handleEvenniaEvent(data) {\n  if (data.evennia_command) {\n    addActionStreamEntry('cmd', data.evennia_command);\n  }\n}\n\n\n// ═══════════════════════════════════════════\nfunction handleHermesMessage(data) {\n  if (data.type === 'history') {\n    return;\n  }\n  } else if (data.type && data.type.startsWith('evennia.')) {\n    handleEvenniaEvent(data);\n  // Evennia event bridge — process command/result/room fields if present\n  handleEvenniaEvent(data);\n}","logs.innerHTML = ok;\n    // Actual MemPalace initialization would happen here\n    // For demo purposes we'll just show status\n    statusEl.textContent = 'Connected to local MemPalace';\n    statusEl.style.color = '#4af0c0';\n    \n    // Simulate mining process\n    mineMemPalaceContent(\"Initial knowledge base setup complete\");\n  } catch (err) {\n    console.error('Failed to initialize MemPalace:', err);\n    document.getElementById('mem-palace-status').textContent = 'MemPalace ERROR';\n    document.getElementById('mem-palace-status').style.color = '#ff4466';\n  }\n  try {","  // Auto-mine chat every 30s\n  setInterval(mineMemPalaceContent, 30000);\n    try {\n      const status = mempalace.status();\n      document.getElementById('compression-ratio').textContent = status.compression_ratio.toFixed(1) + 'x';\n      document.getElementById('docs-mined').textContent = status.total_docs;\n      document.getElementById('aaak-size').textContent = status.aaak_size + 'B';\n    } catch (error) {\n      console.error('Failed to update MemPalace status:', error);\n    }\n  }\n\n  // Auto-mine chat history every 30s\n"].join('\n');
21|  const fixed = sanitizeAppModuleSource(synthetic), real = sanitizeAppModuleSource(readFileSync(path.join(repoRoot, 'app.js'), 'utf8'));
22|  for (const text of [fixed, real]) { assert.doesNotMatch(text, /;\\n|from '\.\/nexus\/symbolic-engine\.js'|\n  \}\n  \} else if|Connected to local MemPalace|setInterval\(mineMemPalaceContent, 30000\);\n    try \{/); }
23|  assert.match(fixed, /resonance-visualizer\.js';\nimport \* as THREE/); assert.match(fixed, /boot\(\);\n  startRenderer\(\);/);
24|  let calls = 0; const imported = await boot({ win: { location: { protocol: 'http:' } }, doc: { getElementById() { return null; }, querySelector() { return null; }, createElement() { return { type: '', textContent: '', onload: null, onerror: null }; }, body: { appendChild(node) { node.onload(); } } }, importApp: async () => (calls += 1, {}) });
25|  assert.equal(imported.mode, 'imported'); assert.equal(calls, 1);
26|  const appended = []; const script = await loadAppModule({ doc: { createElement() { return { type: '', textContent: '', onload: null, onerror: null }; }, body: { appendChild(node) { appended.push(node); node.onload(); } } }, fetchImpl: async () => ({ ok: true, text: async () => "import * as THREE from 'three';" }) });
27|  assert.equal(appended.length, 1); assert.equal(script, appended[0]); assert.equal(script.type, 'module');
28|});

tests/test_repo_truth.py

1|from pathlib import Path
2|
3|
4|def test_readme_states_repo_truth_and_single_canonical_3d_repo() -> None:
5|    readme = Path("README.md").read_text()
6|
7|    assert "current `main` does not ship a browser 3D world" in readme
8|    assert "Timmy_Foundation/the-nexus is the only canonical 3D repo" in readme
9|    assert "/Users/apayne/the-matrix" in readme
10|    assert "npx serve . -l 3000" not in readme
11|
12|
13|def test_claude_doc_matches_current_repo_truth() -> None:
14|    claude = Path("CLAUDE.md").read_text()
15|
16|    assert "Do not describe this repo as a live browser app on `main`." in claude
17|    assert "Timmy_Foundation/the-nexus is the only canonical 3D repo." in claude
18|    assert "LEGACY_MATRIX_AUDIT.md" in claude
19|
20|
21|def test_legacy_matrix_audit_exists_and_names_rescue_targets() -> None:
22|    audit = Path("LEGACY_MATRIX_AUDIT.md").read_text()
23|
24|    for term in [
25|        "agent-defs.js",
26|        "agents.js",
27|        "avatar.js",
28|        "ui.js",
29|        "websocket.js",
30|        "transcript.js",
31|        "ambient.js",
32|        "satflow.js",
33|        "economy.js",
34|    ]:
35|        assert term in audit

tests/test_fleet_audit.py

1|"""Tests for fleet_audit — Deduplicate Agents, One Identity Per Machine."""
2|import json
3|import tempfile
4|from pathlib import Path
5|
6|import pytest
7|import yaml
8|
9|# Adjust import path
10|import sys
11|sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "bin"))
12|
13|from fleet_audit import (
14|    AuditFinding,
15|    validate_registry,
16|    cross_reference_registry_agents,
17|    audit_git_authors,
18|)
19|
20|
21|# ---------------------------------------------------------------------------
22|# Identity registry validation tests
23|# ---------------------------------------------------------------------------
24|
25|class TestValidateRegistry:
26|    """Test identity registry validation rules."""
27|
28|    def _make_registry(self, agents):
29|        return {"version": 1, "agents": agents, "rules": {"one_identity_per_machine": True}}
30|
31|    def test_clean_registry_passes(self):
32|        registry = self._make_registry([
33|            {"name": "allegro", "machine": "167.99.126.228", "role": "burn", "gitea_user": "allegro"},
34|            {"name": "ezra", "machine": "143.198.27.163", "role": "triage", "gitea_user": "ezra"},
35|        ])
36|        findings = validate_registry(registry)
37|        critical = [f for f in findings if f.severity == "critical"]
38|        assert len(critical) == 0
39|
40|    def test_same_name_on_different_machines_detected(self):
41|        registry = self._make_registry([
42|            {"name": "allegro", "machine": "167.99.126.228", "role": "burn"},
43|            {"name": "allegro", "machine": "104.131.15.18", "role": "burn"},
44|        ])
45|        findings = validate_registry(registry)
46|        critical = [f for f in findings if f.severity == "critical" and f.category == "duplicate"]
47|        # Two findings: one for name-on-multiple-machines, one for duplicate name
48|        assert len(critical) >= 1
49|        machine_findings = [f for f in critical if "registered on" in f.description]
50|        assert len(machine_findings) == 1
51|        assert "167.99.126.228" in machine_findings[0].description
52|        assert "104.131.15.18" in machine_findings[0].description
53|
54|    def test_multiple_agents_same_machine_ok(self):
55|        # Multiple different agents on the same VPS is normal.
56|        registry = self._make_registry([
57|            {"name": "allegro", "machine": "167.99.126.228", "role": "burn"},
58|            {"name": "bilbo", "machine": "167.99.126.228", "role": "queries"},
59|        ])
60|        findings = validate_registry(registry)
61|        critical = [f for f in findings if f.severity == "critical"]
62|        assert len(critical) == 0
63|
64|    def test_duplicate_name_detected(self):
65|        registry = self._make_registry([
66|            {"name": "bezalel", "machine": "104.131.15.18", "role": "ci"},
67|            {"name": "bezalel", "machine": "167.99.126.228", "role": "ci"},
68|        ])
69|        findings = validate_registry(registry)
70|        name_dupes = [f for f in findings if f.severity == "critical" and "bezalel" in f.description.lower() and "registered on" in f.description.lower()]
71|        assert len(name_dupes) == 1
72|
73|    def test_duplicate_gitea_user_detected(self):
74|        registry = self._make_registry([
75|            {"name": "agent-a", "machine": "host1", "role": "x", "gitea_user": "shared"},
76|            {"name": "agent-b", "machine": "host2", "role": "x", "gitea_user": "shared"},
77|        ])
78|        findings = validate_registry(registry)
79|        gitea_dupes = [f for f in findings if "Gitea user 'shared'" in f.description]
80|        assert len(gitea_dupes) == 1
81|        assert "agent-a" in gitea_dupes[0].affected
82|        assert "agent-b" in gitea_dupes[0].affected
83|
84|    def test_missing_required_fields(self):
85|        registry = self._make_registry([
86|            {"name": "incomplete-agent"},
87|        ])
88|        findings = validate_registry(registry)
89|        missing = [f for f in findings if f.category == "orphan"]
90|        assert len(missing) >= 1
91|        assert "machine" in missing[0].description or "role" in missing[0].description
92|
93|    def test_empty_registry_passes(self):
94|        registry = self._make_registry([])
95|        findings = validate_registry(registry)
96|        assert len(findings) == 0
97|
98|
99|# ---------------------------------------------------------------------------
100|# Cross-reference tests
101|# ---------------------------------------------------------------------------
102|
103|class TestCrossReference:
104|    """Test registry vs fleet-routing.json cross-reference."""
105|
106|    def test_orphan_in_fleet_not_registry(self):
107|        reg_agents = [{"name": "allegro", "machine": "x", "role": "y"}]
108|        fleet_agents = [{"name": "allegro", "location": "x"}, {"name": "unknown-agent", "location": "y"}]
109|        findings = cross_reference_registry_agents(reg_agents, fleet_agents)
110|        orphans = [f for f in findings if f.category == "orphan" and "unknown-agent" in f.description]
111|        assert len(orphans) == 1
112|
113|    def test_location_mismatch_detected(self):
114|        reg_agents = [{"name": "allegro", "machine": "167.99.126.228", "role": "y"}]
115|        fleet_agents = [{"name": "allegro", "location": "totally-different-host"}]
116|        findings = cross_reference_registry_agents(reg_agents, fleet_agents)
117|        mismatches = [f for f in findings if f.category == "duplicate" and "different locations" in f.description]
118|        assert len(mismatches) == 1
119|
120|
121|# ---------------------------------------------------------------------------
122|# Integration test against actual registry
123|# ---------------------------------------------------------------------------
124|
125|class TestRealRegistry:
126|    """Test against the actual identity-registry.yaml in the repo."""
127|
128|    def test_registry_loads(self):
...[truncated]...

tests/test_computer_use.py

1|"""
2|Tests for nexus.computer_use — Desktop Automation Primitives (#1125)
3|
4|All tests run fully headless: pyautogui is mocked throughout.
5|No display is required.
6|"""
7|
8|from __future__ import annotations
9|
10|import json
11|import sys
12|from pathlib import Path
13|from unittest.mock import MagicMock, patch, call
14|
15|import pytest
16|
17|sys.path.insert(0, str(Path(__file__).parent.parent))
18|
19|from nexus.computer_use import (
20|    _DANGEROUS_BUTTONS,
21|    _SENSITIVE_KEYWORDS,
22|    computer_click,
23|    computer_screenshot,
24|    computer_scroll,
25|    computer_type,
26|    read_action_log,
27|)
28|
29|
30|# ---------------------------------------------------------------------------
31|# Helpers / fixtures
32|# ---------------------------------------------------------------------------
33|
34|
35|@pytest.fixture
36|def tmp_log(tmp_path):
37|    """Return a temporary JSONL audit log path."""
38|    return tmp_path / "actions.jsonl"
39|
40|
41|def _last_log_entry(log_path: Path) -> dict:
42|    lines = [l.strip() for l in log_path.read_text().splitlines() if l.strip()]
43|    return json.loads(lines[-1])
44|
45|
46|def _make_mock_pag(screenshot_raises=None):
47|    """Build a minimal pyautogui mock."""
48|    mock = MagicMock()
49|    mock.FAILSAFE = True
50|    mock.PAUSE = 0.05
51|    if screenshot_raises:
52|        mock.screenshot.side_effect = screenshot_raises
53|    else:
54|        img_mock = MagicMock()
55|        img_mock.save = MagicMock()
56|        mock.screenshot.return_value = img_mock
57|    return mock
58|
59|
60|# ---------------------------------------------------------------------------
61|# computer_screenshot
62|# ---------------------------------------------------------------------------
63|
64|
65|class TestComputerScreenshot:
66|    def test_returns_b64_when_no_save_path(self, tmp_log):
67|        mock_pag = _make_mock_pag()
68|        # Make save() write fake PNG bytes
69|        import io
70|        buf = io.BytesIO(b"\x89PNG\r\n\x1a\n" + b"\x00" * 20)
71|
72|        def fake_save(obj, format=None):
73|            obj.write(buf.getvalue())
74|
75|        mock_pag.screenshot.return_value.save = MagicMock(side_effect=fake_save)
76|
77|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
78|            result = computer_screenshot(log_path=tmp_log)
79|
80|        assert result["ok"] is True
81|        assert result["image_b64"] is not None
82|        assert result["saved_to"] is None
83|        assert result["error"] is None
84|
85|    def test_saves_to_path(self, tmp_log, tmp_path):
86|        mock_pag = _make_mock_pag()
87|        out_png = tmp_path / "shot.png"
88|
89|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
90|            result = computer_screenshot(save_path=str(out_png), log_path=tmp_log)
91|
92|        assert result["ok"] is True
93|        assert result["saved_to"] == str(out_png)
94|        assert result["image_b64"] is None
95|        mock_pag.screenshot.return_value.save.assert_called_once_with(str(out_png))
96|
97|    def test_logs_action(self, tmp_log):
98|        mock_pag = _make_mock_pag()
99|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
100|            computer_screenshot(log_path=tmp_log)
101|
102|        entry = _last_log_entry(tmp_log)
103|        assert entry["action"] == "screenshot"
104|        assert "ts" in entry
105|
106|    def test_returns_error_when_headless(self, tmp_log):
107|        with patch("nexus.computer_use._get_pyautogui", return_value=None):
108|            result = computer_screenshot(log_path=tmp_log)
109|
110|        assert result["ok"] is False
111|        assert "unavailable" in result["error"]
112|
113|    def test_handles_screenshot_exception(self, tmp_log):
114|        mock_pag = _make_mock_pag(screenshot_raises=RuntimeError("display error"))
115|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
116|            result = computer_screenshot(log_path=tmp_log)
117|
118|        assert result["ok"] is False
119|        assert "display error" in result["error"]
120|
121|    def test_image_b64_not_written_to_log(self, tmp_log):
122|        """The (potentially huge) base64 blob must NOT appear in the audit log."""
123|        mock_pag = _make_mock_pag()
124|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
125|            computer_screenshot(log_path=tmp_log)
126|
127|        raw = tmp_log.read_text()
128|        assert "image_b64" not in raw
129|
130|
131|# ---------------------------------------------------------------------------
132|# computer_click
133|# ---------------------------------------------------------------------------
134|
135|
136|class TestComputerClick:
137|    def test_left_click_succeeds(self, tmp_log):
138|        mock_pag = _make_mock_pag()
139|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
140|            result = computer_click(100, 200, log_path=tmp_log)
141|
142|        assert result["ok"] is True
143|        mock_pag.click.assert_called_once_with(100, 200, button="left")
144|
145|    def test_right_click_blocked_without_confirm(self, tmp_log):
146|        mock_pag = _make_mock_pag()
147|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
148|            result = computer_click(100, 200, button="right", log_path=tmp_log)
149|
150|        assert result["ok"] is False
151|        assert "confirm=True" in result["error"]
152|        mock_pag.click.assert_not_called()
153|
154|    def test_right_click_allowed_with_confirm(self, tmp_log):
155|        mock_pag = _make_mock_pag()
156|        with patch("nexus.computer_use._get_pyautogui", return_value=mock_pag):
157|            result = computer_click(100, 200, button="right", confirm=True, log_path=tmp_log)
158|
159|        assert result["ok"] is True
...[truncated]...