Agent Legion
A local-first 2D colony monitor inspired by RimWorld — and a worked example of making parallel AI coding agents, context rot, and worktree collisions visible at a glance.
When four or five CLI coding agents run in parallel across local terminals, coordination failures stay invisible until two agents overwrite the same git worktree or a 260,000-token session quietly forgets its instructions. Agent Legion turns live session transcripts, OS processes, git worktrees, and tool connectors into an inspectable 2D workshop map — and pairs it with a bounded choice classifier for high-volume background screening.
TL;DR
- Why I shut down my cloud observability dashboard and replaced it with a local-first 2D workshop map
- How four invisible multi-agent failure modes become physical mechanics you can spot in half a second: Worktree Contention (Two Chefs, One Wok), Context Rot (scroll stacks and walking speed), Blocked Sessions, and Orphaned Processes
- How a four-seam architecture — privacy-stripping sensors, a pure deterministic world reducer, decoupled room coordinates, and a zero-build-step 2D Canvas renderer — keeps the simulation fast and testable
- How a bounded choice classifier handles repetitive background tagging with crash-safe job markers and mandatory human review gates
Why I built this (and why it replaced my cloud dashboard)
Earlier in the year, I built a hosted observability dashboard on Railway and Postgres to monitor my cloud functions, weekly maintenance jobs, and API spend. In October, I shut it down. The cloud services were quiet and predictable; checking a web dashboard for green uptime bars wasn’t solving an actual failure mode.
The real operational failures were happening locally on my workstation whenever I ran four or five coding agents (Claude Code, Codex, Gemini, Mistral, DeepSeek) in parallel:
- Silent worktree collisions: Two agents in separate terminal tabs would start editing the same repository at the same time, tangling unstaged hunks before either ran tests.
- Unnoticed context rot: A long debugging session would drift past 250,000 tokens of context, losing track of earlier constraints while still sounding confident.
- Forgotten approval prompts: A subagent in a background tab would pause on a clarifying question and sit idle for forty minutes.
- Orphaned background processes: Finished sessions would leave behind detached dev servers, browser automation bridges, or hung child processes.
A table of process IDs and log files forces you to read rows to spot a collision. Video games solved parallel autonomous status decades ago: in RimWorld, you can tell in half a second whether two colonists are fighting over the same workbench, who is carrying too much weight to walk fast, and which generator is sparking.
I built Agent Legion as a local-first 2D workshop colony where every project repository is a workshop room, every running CLI agent is a colonist pawn, and every coordination failure has an unmistakable physical signature.
Four invisible problems made visible
1. Worktree contention (“Two Chefs, One Wok”)
Each project room contains one Anvil (the room’s single write station — the “wok” in the kitchen analogy), representing exclusive write ownership of that git worktree. Reading files and delegating research happen at the Research Bench; running shell commands and tests happens at the Terminal.
When an agent invokes a file-editing tool, the state reducer claims a 10-minute write reservation on that room’s write station for the root session — automatically rolling child subagent edits up to the parent session that dispatched them. If a second root session edits the same worktree while that reservation is active, both colonists crowd the same station, red sparks fly between them, and a top-priority WorktreeContested alert fires on the HUD.
2. Context Rot (scroll stacks, walk speed, and the handoff relay)
One epistemic rule carried directly over from my retired cloud dashboard is that unknown is a value, not a zero. A large transcript file on disk does not prove how full the live context window is (for example, after context compaction), and a harness that does not report token telemetry should never be rendered as having 0 tokens used — instead surfacing an explicit ContextUnknown state.
Agent Legion extracts verified per-turn context fill from each vendor’s native telemetry and evaluates it against a 250,000-token clean-handoff goal:
| Stage | Token Band | Physical & Visual Effect on Colonist |
|---|---|---|
| Nimble | 0 – 125k | Green bar, 1–2 scrolls on back, 100% walking speed |
| Loaded | 125k – 200k | Warm amber bar, 3–4 scrolls on back, 80% walking speed |
| Handoff Zone | 200k – 250k | Orange bar, 6-scroll tower + sweat droplets, 60% walking speed, handoff warning |
| Closeout Grace | 250k – 285k | Active while committing or writing a session handoff: 5 scrolls and a 110% closeout sprint |
| Context Rot | > 250k (> 285k w/ grace) | Red bar, 8-scroll boulder shedding loose papers, 30% walking speed, high-priority rot alert |
| Unknown / Estimated | Unverified | Striated neutral bar — never faked as green |
When a heavily loaded agent writes a clean session handoff (HANDOFF.md) before crossing the 250,000-token threshold, the colonist walks to the room’s Bookshelf, shelves a gold-trimmed volume, and increments the colony’s Clean Handoff Streak Monument. When the next session starts in that repository and reads the handoff file within its first 10 minutes, a cyan relay arc connects the bookshelf to the incoming colonist.
3. Blocked sessions and specialist summons
A tail state machine inspects the last 15 transcript records of each session to distinguish an agent that is actively thinking from one waiting on human input. Blocked colonists walk to the room’s Lectern, raise a pulsing ? speech bubble, and trigger an AgentAwaitingInput alert so a paused tab never goes unnoticed.
When a parent session dispatches a specialist subagent, a smaller Specialist Pawn spawns inside the parent’s workshop room with a tether line to its summoner and a visual archetype matching its role:
- Inquisitor (Adversarial Bias Auditor): carries a magnifying lens and audits plans at the Easel.
- Engineer (Architecture Reviewer): carries a cyan blueprint roll at the Research Bench.
- Owl Scout (Discovery & Literature Search): carries a lantern and inspects sources at the Bookshelf.
- Classifier Mechanoid: metallic worker pawn rendered with a live progress bar during batch classification runs.
4. Orphaned daemons and degraded connectors (“Lights Left On”)
Below the project workshops sits the Comms & Power Bay. Every configured MCP server renders as a Comms Console and every long-running local background process renders as a Generator.
Process sensors walk OS process ancestry. If a child process outlives its parent terminal session, it is flagged as a suspected orphan. Rather than offering a blind “kill” button, the Inspector enforces a two-step reviewed cleanup flow:
- Dry-run inspection verifies the process is still orphaned, checks listening sockets and child processes, and issues a single-use review token valid for 60 seconds.
- Confirmed termination re-verifies both the process ID and its exact OS creation timestamp (preventing PID-reuse accidents) and refuses to terminate any process that belongs to an active session or holds a listening server socket.
Architecture: four primitives, zero framework bloat
Agent Legion runs as a lightweight Python server paired with a vanilla ES module HTML5 Canvas client — zero frontend build step, zero bundler, and zero UI framework dependencies. It separates concerns across four strict seams:
Sensors strip sensitive content at the boundary; the pure reducer computes world state deterministically; layout coordinates stay decoupled from state; and the 2D Canvas caches static terrain in 16×16 tile sections.
Three non-obvious design decisions
- Strict privacy boundary at the sensor edge: Because Agent Legion inspects live transcripts across every project on the machine, the sensor layer strips all user prompts, assistant prose, code diffs, shell command arguments, and environment variables before creating observation records. Only structural metadata — tool category, target filename, and token counts — ever enters world state.
- Colony weather reflects machine strain, never backlog guilt: The renderer supports five atmospheric states (
Clear,Gentle Snowwhen the colony is dormant,Rain,Thunderstorm, andFogwhen telemetry is stale). Early on, I considered tying weather to overdue issue-tracker tickets. I rejected that immediately: an operational monitor should tell the truth about machine state right now. If you have a hundred backlog tickets but zero contested worktrees, healthy MCP connectors, and nimble context windows, colony weather isClear.Thunderstormis reserved for active local emergencies (contested worktrees, severe context rot, or a downed connector). - Section-cached procedural map generation: The frontend builds the colony terrain through a seeded 9-step procedural pipeline (elevation noise, terrain, room plots, prefabs, corridors, doors, roofs, props, and lighting) over a flat typed-array grid, then caches static layers into
16×16offscreen canvas sections with dirty-bit invalidation. Per-frame rendering only blits visible sections and draws moving colonists, sparks, and weather particles on top.
Bounded choice classification: when not to use a chat model
Packaged alongside the colony monitor is my shared classification worker, built on TypeSafe’s constrained choice primitive (system_one). Unlike a general LLM chat completion, a choice primitive returns only discrete category probabilities — zero thinking tokens and zero free-text explanations — at roughly $0.04 per million input tokens.
Fast classifiers fail silently unless you pair them with calibration gates. Frontier reasoning tokens are too slow and expensive to spend on repetitive tagging, but a small non-thinking classifier gives you no chain-of-thought to audit when a rubric is ambiguous.
To keep high-volume classification trustworthy, the worker enforces three guardrails:
- Versioned rubric contracts: Every classification task declares explicit output categories, hard inclusion/exclusion criteria, and calibration examples.
- Crash-safe
in_flightjob markers: Before each paid API call (with automatic retries disabled), the job store writes an atomicin_flight(outcome_unknown) durability marker to disk so an interrupted batch never silently double-bills or duplicates work. - Mandatory human review until validated: New rubrics default to
review_policy: all, routing every prediction into a human inspection state until the rubric has been validated against a gold-labeled benchmark.
The stack, annotated
| Layer | Technology | Why it is here |
|---|---|---|
| Sensors & Local Server | Python 3.11+, psutil | Reads multi-vendor session transcripts, OS process trees, git worktrees, and connector health with strict content redaction |
| World Simulation | Pure Python state reducer | Deterministic (prev_state, observations, now) -> WorldState engine with 10-minute write-station leases and capacity state machines |
| 2D Colony Client | Vanilla ES Modules + HTML5 Canvas 2D | Seeded 9-step procedural terrain pipeline, 16×16 offscreen section cache, 8-connected A* pathfinding, and Perimeter HUD |
| Choice Classifier | TypeSafe SDK (system_one primitive) | High-throughput discrete classification with crash-safe job manifests and review_policy: all calibration gates |
| Test Suite | pytest (254 Python tests) + Node test runner (78 JS tests) | Covers sensor privacy redaction, reducer edge transitions, cold-start notification seeding, and deterministic map generation |