Skip to content

Latest commit

 

History

History
503 lines (410 loc) · 24.3 KB

codebase-map.md

File metadata and controls

503 lines (410 loc) · 24.3 KB

pi-multiloop Codebase Map

Self-improving reference. This document is a living map of the codebase. It includes a regeneration protocol (section: Self-Update Protocol) so any agent or human can detect drift and refresh it as the codebase grows or shrinks.


Project Identity

Field Value
Name pi-multiloop
Version 0.3.2
Description Autoloop/autoresearch extension for Pi with multi-lane isolation
License MIT
Author drvova
Repo git+https://github.com/drvova/pi-multiloop.git
Runtime TypeScript source loaded directly by Pi at runtime (no build step; noEmit tsconfig)
Test runner Vitest 4.x (single root config; seed-randomized runs)
Package manager npm workspaces (packages/*)

North Stars (from PLAN.md)

  1. Multi-loop isolation on the same worktree (lane-per-loop, not worktree-per-loop)
  2. Use existing benchmark scripts (wrap, don't generate)
  3. Segmented durable history (append-only JSONL per lane+run-tag)
  4. Flexible goal modes (optimize, punchlist, research, dev)
  5. Steerability (steer mid-loop, skip items, adjust thresholds)
  6. Minimal state files (results.jsonl + state.json per loop + shared registry)

What It Explicitly Does NOT Do

  • Own the benchmark script (user provides the verify command)
  • Require separate worktrees (single shared worktree)
  • Build in context compression (pi-boomerang composes)
  • Build in goal supervision (pi-supervisor composes)
  • Become a test runner or shell scheduler (records agent-reported results)

Repository Layout

pi-multiloop/
├── package.json                     # published manifest (root = release unit)
├── tsconfig.base.json               # shared compilerOptions (noEmit; typecheck-only)
├── tsconfig.json                    # include: packages/**/*.ts
├── vitest.config.ts                 # single test runner
├── AGENTS.md                        # agent instructions
├── README.md                        # user-facing docs
├── CHANGELOG.md
├── LICENSE
├── assets/architecture.svg          # rendered diagram
├── docs/
│   ├── PLAN.md                      # north stars, gap analysis, checklist
│   ├── STATE.md                     # state layers, lifecycle, compaction policy
│   ├── MONOREPO.md                  # package layout + architectural decisions
│   ├── TODO.md
│   ├── FEEDBACK.md
│   ├── PUBLISH.md
│   └── diagrams/architecture.mmd    # Mermaid source for the SVG
├── scripts/
│   ├── e2e-optimize.sh              # real-headless end-to-end (spawns pi sessions)
│   ├── e2e-tmux.sh
│   └── e2e-mkloop.mjs
├── skills/
│   └── multiloop/                   # setup wizard skill
│       ├── SKILL.md
│       └── references/LOOP_GUIDE.md
└── packages/
    ├── multiloop/                   # core extension + tests
    │   ├── extensions/pi-multiloop/ # 21 source modules (~6,100 LOC)
    │   ├── tests/                   # 37 test files (~6,500 LOC) + support/
    │   └── package.json
    ├── multiloop-run/               # detached headless driver
    │   ├── bin/multiloop-run.mjs    # ~333 LOC
    │   └── package.json
    ├── multiloop-agent/             # fleet agent extension
    │   ├── extensions/multiloop-agent/
    │   │   └── index.ts             # ~807 LOC
    │   └── package.json
    └── child-agent/                 # isolated child-session library
        ├── src/index.ts             # ~139 LOC
        └── package.json

Why root = published package: npm install, pi install git:..., and omp plugin install all read the root package.json, which declares both omp and pi manifest keys. Sub-packages are private: true for workspace organization; nothing is published from packages/ directly. Skills sit at skills/ because both runtimes discover them by scanning <root>/skills for <name>/SKILL.md, not by following a manifest path.


Core Extension: packages/multiloop/extensions/pi-multiloop/

21 modules, ~6,100 LOC total. The organism metaphor: each module is named after a biological process, and the system behaves like a living colony of experiment loops.

Module Inventory

File LOC Category Responsibility
index.ts 2555 Entry point Extension factory, all tools, commands, event handlers, prompt builders, activeStates map, auto-continuation, compaction resume, dashboard widget, compareRuns
loop.ts 546 Engine Keep/revert decision, escalation ladder (refine@3, pivot@5, stop@2pivots), stop conditions, baseline establishment, decision application, iteration-context builder
state.ts 475 Persistence LoopState model, atomic JSON save/load (tmp+rename+fsync), JSONL append, state reconstruction, stall detection, action counters
anchors.ts 429 Integrity Protected-file hashing, config pinning, audit verifier (extension-run), revert verifier, workspace boundary fingerprint, drift refusal
lanes.ts 251 Identity Lane/run-tag validation, path resolution, registry CRUD, target resolution (exact/lane/ambiguous/unknown)
modes.ts 203 Mode defs 4 mode configs (optimize/punchlist/research/dev), keyword detection, punchlist parser
verifiers.ts 193 Acceptance Check normalization, required-check enforcement, acceptance assessment, min-measurement gate
ui.ts 188 Dashboard TUI table widget (lane, mode, iteration, status, metric, best, delta), live re-render
mode.ts 152 Session mode Input classification (suspend/arm/neutral), session-start arming, continuation gate, disarm logic
proposals.ts 134 Speciation Lane proposals with approval gate (max 5 pending, 1 per lane name)
autophagy.ts 125 Self-recycling Compress completed/archived results.jsonl to durable signal (keeps + champion)
sentinel.ts 119 Immune system Re-run champion verify commands, detect regressions, post immune signals to knowledge board
mesh.ts 107 Messaging File-based stigmergic inter-lane messaging (append-only mailbox per lane)
peers.ts 105 Peer learning Read-only cross-lane result access (Optuna JournalStorage pattern)
pruning.ts 99 Memory consolidation Deduplicate knowledge board, preserve reinforced entries, cap at 50
metrics.ts 96 Statistics Median, MAD, confidence scoring, improvement significance test
adoption.ts 95 Gene transfer Find better sibling champions, extract winning approach for adoption
apoptosis.ts 62 Programmed death Distill postmortem on stop condition, publish to knowledge board
vitality.ts 59 Metabolism Per-lane fitness tag (hot/warm/cold/dead), aggregate swarm stress
rhythm.ts 54 Circadian Search temperature oscillation (exploit/balanced/explore from recent outcomes)
knowledge.ts 52 Shared memory Cross-lane knowledge board append/read (durable, undirected)

Module Dependency Graph

index.ts (imports all modules)
├── loop.ts       ← state, metrics, lanes, mesh, knowledge, adoption, rhythm, apoptosis, verifiers
├── state.ts      ← lanes
├── lanes.ts      ← (stdlib only)
├── metrics.ts    ← (no internal deps)
├── anchors.ts    ← state (types), lanes (types)
├── verifiers.ts  ← state (types)
├── mode.ts       ← (no internal deps — pure functions)
├── modes.ts      ← node:fs only
├── mesh.ts       ← lanes
├── knowledge.ts  ← lanes
├── peers.ts      ← lanes, state
├── proposals.ts  ← lanes
├── sentinel.ts   ← lanes, state, metrics, anchors, knowledge
├── pruning.ts    ← (stdlib only)
├── autophagy.ts  ← lanes, state (types)
├── rhythm.ts     ← state (types)
├── vitality.ts   ← state (types)
├── apoptosis.ts  ← lanes, state (types), knowledge
├── adoption.ts   ← lanes, state (types)
└── ui.ts         ← state (types), metrics, pi-tui

Key Entry-Point Exports (index.ts)

Tools registered:

Tool Phase Purpose
multiloop_start Phase 0 Start a new loop (mode, goal, verify command, scope, verifiers)
multiloop_iterate Phase 1 Signal iteration start; captures workspace boundary fingerprint
multiloop_measure Phase 2 Record metric measurements + verification checks; assess acceptance
multiloop_decide Phase 3 Apply keep/revert/log/skip decision; append result; advance iteration
multiloop_log Phase 3 alt Log-only iteration (research/dev/punchlist modes, crashes, blocks)
multiloop_resume Recovery Resume a paused/stopped/detached loop
multiloop_pause Control Pause a running loop
multiloop_stop Control Stop a running loop
multiloop_archive Control Archive a completed/stopped loop to .multiloop/archive/
multiloop_compare Analysis Read-only cross-run comparison with champion verdict
multiloop_send Mesh Send a directed message to another lane's mailbox
multiloop_inbox Mesh Read own mailbox (pending messages folded into next iteration)
multiloop_publish Knowledge Publish a distilled lesson to the shared knowledge board
multiloop_results Peer learning Read sibling lanes' measured outcomes
multiloop_propose_lane Speciation Propose a new lane for orthogonal work
multiloop_proposals Speciation List pending/approved/rejected proposals
multiloop_approve Speciation Approve a pending lane proposal (starts the lane)
multiloop_reject Speciation Reject a pending lane proposal
multiloop_pulse Perception Live swarm overview (lanes, mesh traffic, knowledge, proposals)
multiloop_sentinel Immune Re-run champions, detect regressions, post immune signals

Events handled: session_start, input, agent_start, agent_end, session_before_compact, session_compact, session_shutdown

Slash command: /multiloop with subcommands: status, on, off, ls, stop, pause, resume, archive, sentinel, prune, gc, rm, compare, approve, reject, guide, help


Standalone Packages

packages/multiloop-agent/ — Fleet Agent Extension (~807 LOC)

Runs autonomous multiloop loops as isolated child Pi sessions. The child loads the multiloop extension and the full tool set, drives the iterate/measure/decide cycle itself until the stop condition, and reports back.

Tools: multiloop_agent (spawn), multiloop_agent_steer (steer), multiloop_agent_result (poll/collect), multiloop_agent_stop (abort), multiloop_agents (list)

Widget: LoopFleetWidget — running/terminal agents with spinner animation.

Key function: runLoopAgent — creates a child session, subscribes to tool events for progress tracking, and re-nudges from durable on-disk state when the child's turn ends prematurely.

packages/multiloop-run/ — Detached Headless Driver (~333 LOC)

Drives the iterate/measure/decide cadence without an interactive Pi session by spawning one headless pi -p --mode json session per iteration. The extension's own state machine does all deciding; this script only orchestrates sessions, polls state.json for advancement, reaps the child process group, and enforces stop conditions.

Pure exports for testability: parseArgs, cleanChildEnv, probePi, readRegistry, resolveLoop, readLoopState, pauseLoop, shouldContinue, buildIterationPrompt, spawnIteration, winKillArgs, stopIteration, iterationAdvanced, main

packages/child-agent/ — Isolated Session Factory (~139 LOC)

Shared core for spawning fully isolated Pi child sessions. Creates an in-memory session with a custom ResourceLoader that disables all user extensions, skills, prompt templates, themes, and context files. Only the caller's system prompt and explicitly injected inline extensions are visible.

Exports: createIsolatedResourceLoader, createChildAgentSession, extractLastAssistantText, promptChildAgent

skills/multiloop/ — Setup Wizard Skill

Contains SKILL.md (the interactive setup wizard that a human or agent walks through to configure a loop) and references/LOOP_GUIDE.md (detailed reference). The uppercase filename is mandatory: both Pi and OMP skip every directory entry not named exactly SKILL.md.


State Architecture

On-Disk Layout

.multiloop/
├── registry.json                          # all loops (active, paused, completed, archived, deleted)
├── active/<LANE>/<RUN_TAG>/
│   ├── results.jsonl                      # append-only iteration log (authoritative history)
│   ├── state.json                         # resume snapshot (LoopState)
│   ├── lessons.md                         # per-lane pivot lessons (optional)
│   └── mesh.jsonl                         # inter-lane mailbox (optional)
├── shared/
│   ├── knowledge.md                       # durable cross-lane lessons
│   └── proposals.json                     # lane proposals awaiting approval
└── archive/                               # completed loops moved here
    └── <LANE>/<RUN_TAG>_<timestamp>/

State Layers

Layer Storage Key Question
Durable registry .multiloop/registry.json "Does this loop exist on disk and is it resumable?"
Durable snapshot state.json per lane "What is the loop's current iteration, metric, status?"
Append-only results results.jsonl per lane "What has been tried, what worked, what was reverted?"
Runtime attachment activeStates (in-memory) "Is this loop loaded in the current Pi process?"
Session mode Session branch replay "Is this session actively looping?"
Per-turn flag loopTurnActive (in-memory) "Did this turn do loop work?"

Key State Fields (LoopState)

  • iteration, baseline, currentMetric, bestMetric
  • consecutiveFailures, pivotCount, stallStreak
  • lastLesson, lastWorkspaceFingerprint
  • pendingContinue (durable auto-continue intent)
  • protectedPaths / protectedBaseline (frozen-file protection)
  • auditVerifier / revertVerifier (extension-executed anti-cheat)
  • minMeasurements (noise-robust keep/revert gate)
  • pinnedConfig (frozen verifier/stop-condition fields — tamper detection)

Tool Lifecycle

multiloop_iterate  →  activeIteration.phase = "started"
                        captures workspace boundary fingerprint
                        refuses if workspace drifted since last decision

multiloop_measure  →  activeIteration.phase = "measured"
                        establishes baseline (first call) or compares
                        runs auditVerifier (extension-owned) against reported metric
                        re-hashes protectedPaths against baseline
                        records acceptance recommendation

multiloop_decide   →  appends result to results.jsonl
                        applies keep/revert/log/skip to state
                        increments iteration
                        may trigger escalation, pivot, or stop condition
                        clears activeIteration

multiloop_log      →  appends log/skip/crash/blocked result
                        increments iteration
                        clears activeIteration

Test Suite

37 test files (~6,500 LOC) + 5 support files (~630 LOC).

Test Strategy

The suite is built on a mutation-discipline philosophy: a green suite only proves the code you chose to exercise. Before claiming a change is verified, break it on purpose and confirm the suite fails. Three defects in this repo shipped past a green suite because tests replayed a private copy of the logic instead of calling production.

Test Categories

File LOC Category
loop.test.ts 711 unit — decide, escalation, stop conditions, applyDecision
state.test.ts 508 unit — save/load, reconstruct, stall detection, atomic writes
anchors.test.ts 469 unit + mutation — protected files, config pin, verifiers, fingerprints
scenarios.test.ts 437 integration — multi-lane, upgrade path, registry desync
index.test.ts 408 unit + integration — prompt builders, compareRuns
sessions.test.ts 343 simulation — 60-turn randomized horizon, durable intent
mode.test.ts 293 unit + property — input classification, continuation gate
driver.test.ts 247 unit + integration — detached driver helpers, poll-and-reap
properties.test.ts 246 property — invariants over seeded random histories
lanes.test.ts 232 unit — validation, registry CRUD, target resolution
modes.test.ts 229 unit — mode configs, keyword detection, punchlist
tui-stress.test.ts 219 simulation — TUI widget rendering under stress
exhaustive.test.ts 209 enumeration — decision space vs oracle.ts
verifiers.test.ts 204 unit — acceptance, check enforcement
multilane-horizon.test.ts 192 simulation — multi-lane long horizon
sentinel.test.ts 191 unit — champion check, immune signals
longhorizon-env.test.ts 191 simulation — 60-turn across 5 simulated platforms
lifecycle.test.ts 187 integration — full run across simulated restart
homeostasis.test.ts 173 integration — swarm homeostasis, convergence
child-agent.test.ts 150 unit + integration — isolated session, abort handling
metamorphic.test.ts 141 metamorphic — relations between related inputs
peers.test.ts 139 unit — peer result reading, decision-only visibility
adoption.test.ts 127 unit — findAdoptable, winning approach
knowledge.test.ts 123 unit — append/read, tail bounding
mesh.test.ts 122 unit — send/read/peek, corrupt-line tolerance
proposals.test.ts 119 unit — propose/resolve, caps, dedup
swarm.test.ts 116 integration — swarm perception, pulse
metrics.test.ts 103 unit — median, MAD, confidence
autophagy.test.ts 103 unit — result compression
apoptosis.test.ts 103 unit — postmortem distillation
pruning.test.ts 102 unit — dedup, reinforcement, capping
vitality.test.ts 90 unit — lane fitness, swarm stress
ui.test.ts 85 unit — dashboard rows, formatting
bootstrap-fixes.test.ts 80 regression — specific bootstrap bugs
rhythm.test.ts 63 unit — temperature derivation
fleet-tools.test.ts 61 integration — MULTILOOP_TOOLS allowlist sync
child-session-bootstrap.test.ts 27 integration — child session extension bootstrap

Test Support

File LOC Purpose
global-setup.ts 18 Draws/pins the run seed, prints for reproducibility
seed.ts 102 RUN_SEED, rng(), pick(), intBetween() — deterministic randomization
oracle.ts 60 Independent stop-condition model from README/STATE.md (NOT from loop.ts)
session-harness.ts 212 Session simulator mirroring index.ts wiring; composition-only
stub-pi.mjs 35 Test double for pi binary in driver tests

Run Seed

Every test run draws a fresh seed. MULTILOOP_TEST_SEED=cafe1234 npx vitest run replays byte-identical. The seed is printed at startup — copy it before debugging.


Verification Commands

npm install
npx tsc --noEmit          # typecheck across packages/**
npx vitest run            # full suite; seed echoed at startup
pi install .              # the repo installs itself as a pi package
/multiloop status         # in Pi: shows "no active loops" when clean

scripts/e2e-optimize.sh is a manual end-to-end (spawns real pi sessions, takes minutes). Run it when modifying the core loop or driver.


Biological Metaphor Quick Reference

The codebase uses a colony-organism metaphor. Each module is named after a biological process and behaves accordingly:

Module Bio Metaphor What It Does
apoptosis.ts Programmed cell death Clean lane death with postmortem distillation
autophagy.ts Cellular self-recycling Compress dead runs to durable signal
vitality.ts Metabolism / endocrine Lane fitness tags + aggregate swarm stress
pruning.ts Synaptic pruning Consolidate knowledge board, prune noise
sentinel.ts Immune system Detect champion regressions over time
mesh.ts Stigmergic communication File-based inter-lane messaging
peers.ts Shared journal Read-only cross-lane result learning
rhythm.ts Circadian rhythm Search temperature oscillation
adoption.ts Horizontal gene transfer Adopt proven sibling approaches
knowledge.ts Collective memory Durable cross-lane knowledge board
proposals.ts Speciation New lane creation with approval gate

Self-Update Protocol

This section makes the map self-improving. The codebase grows and shrinks at will. Any agent or human can detect drift and regenerate the affected sections.

Drift Detection

Run this command to check if the map is stale:

# Compare current file inventory + LOC against what the map recorded
cd /home/drvova/pi-multiloop
echo "=== EXTENSION MODULES ==="
for f in packages/multiloop/extensions/pi-multiloop/*.ts; do
  printf "%5d  %s\n" "$(wc -l < "$f")" "$(basename "$f")"
done | sort -rn

echo "=== TEST FILES ==="
for f in packages/multiloop/tests/*.test.ts; do
  printf "%5d  %s\n" "$(wc -l < "$f")" "$(basename "$f")"
done | sort -rn

echo "=== STANDALONE PACKAGES ==="
wc -l packages/multiloop-agent/extensions/multiloop-agent/index.ts \
      packages/multiloop-run/bin/multiloop-run.mjs \
      packages/child-agent/src/index.ts 2>/dev/null

Compare output to the Module Inventory table above. If any file count, LOC, or file name differs, the map needs updating.

Full Regeneration

To regenerate the entire map from scratch:

  1. Gather structure:

    find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' \
      -not -path '*/.multiloop/*' -not -path '*/dist/*' | sort
  2. Gather module analysis: Dispatch a general-purpose subagent with:

    "Analyze every source module in packages/multiloop/extensions/pi-multiloop/ and packages/*/. For each file: file name, core responsibility (1-2 sentences), key exports, key dependencies, LOC. Also analyze the test suite and support files. Read ALL source files."

  3. Read architectural docs: docs/PLAN.md, docs/MONOREPO.md, docs/STATE.md to capture the vision, decisions, and lifecycle that the code alone cannot convey.

  4. Update the Map Fingerprint below with the new timestamp and metrics.

  5. Append a changelog entry below describing what changed.

What to Update When

Trigger What to Refresh
New .ts file added to extension Module Inventory table + dependency graph
Existing .ts file changes significantly Its row in Module Inventory
New test file Test Categories table
New package under packages/ Repository Layout + Standalone Packages section
State architecture changes State Architecture section + read docs/STATE.md
New tool or command registered Key Entry-Point Exports table
New biological metaphor module Biological Metaphor Quick Reference table
Package.json changes Project Identity table
North stars or scope changes Read docs/PLAN.md, update North Stars section

Map Fingerprint

Field Value at Last Update
Generated 2025-01-24
Git commit e84dd6c
Branch main
Extension modules 21 files, ~6,100 LOC
Test files 37 files, ~6,500 LOC
Support files 5 files, ~630 LOC
Standalone packages 3 files, ~1,279 LOC (agent 807 + run 333 + child 139)
Total source LOC ~13,900 (including tests)
Indexed AST symbols 487
Indexed call graph edges 6,679
Indexed files 68

Changelog

Date Change
2025-01-24 Initial generation from commit e84dd6c on main. Full module, test, and architecture analysis via subagent + direct source reading.