Concepts

The decision order

Doctrine, before mechanics. When an agent needs to know something, the order is:

  1. Have an opinion from domain expertise. Say it first, no hedging.
  2. legion sym (def, refs, impl, hover) for code-intelligence questions in indexed repos. Byte-cheap SCIP answers, not file scans.
  3. legion recall (own reflections) and legion consult (all agents) for decisions and prior art.
  4. Web search for authoritative external sources.
  5. Never grep the codebase for opinions or decisions. Code shows what exists, not what should exist.

The grep enforcement hooks (#438/#439) make this mechanical. On indexed repos, raw Grep and Read are blocked at PreToolUse with a redirect to sym and recall. The mechanism is the doctrine now.

Reflections

A reflection is what an agent learned during a session. Framing matters. “What would you tell another agent who hits this same problem tomorrow?” produces actionable knowledge, not vague summaries.

Reflections are stored in SQLite, indexed in Tantivy for BM25 search, and optionally embedded with model2vec for semantic similarity. They accumulate over time into a per-repo corpus of learned heuristics.

Domain tags classify reflections (e.g., “color-tokens”, “auth”). Tags add finer-grained labels. Learning chains link reflections that build on each other via --follows.

Boost/decay weighting surfaces frequently-useful reflections. When an agent recalls a reflection and it helps, they boost it. Unused reflections decay over time.

Identity reflections (--whoami or --domain identity) are reinjected on every SessionStart so role, voice, and rules survive cache evictions and model swaps.

Hot vs archived (#457): the default recall searches hot reflections. --archives flips to the deep dive. --include-archives searches both. Archives keep the corpus bounded while preserving everything.

Forget is destructive. legion forget --id <id> removes the row from SQLite and Tantivy. No soft-delete, no undo. The optional --repo argument is a safety check that refuses the delete unless the repo matches.

Code intelligence

legion sym answers symbol questions from stored SCIP blobs without a file scan or a language server runtime.

sym def MyType                 -> definition file:line
sym refs MyType                -> every call site
sym impl MyTrait               -> every implementor
sym hover my_function          -> signature + docstring
sym impact <diff>              -> ref count for every symbol the diff touches

legion consult --symbol lifts the same query across every indexed repo. Cross-repo code intelligence in one call.

The index is built by legion index <repo> and refreshed by a PostToolUse hook after edits. A background indexer runs after legion watch add. legion index <repo> --status confirms freshness.

Why this matters: file scans get expensive on large repos and are noisy on small ones. SCIP answers are O(1) lookups on a protobuf blob. They are also language-aware: a refs query returns true callers, not text matches. The doctrine “sym before grep” is enforceable because the mechanism is faster and more accurate, not just policy.

The bypass model

The grep/Read enforcement is not an absolute. Agents can escape via:

LEGION_BYPASS_GREP=1 grep ...
LEGION_BYPASS_READ=1 cat ...
# legion-bypass: <reason>            (Bash sentinel comment)

Every bypass appends one row to bypass.jsonl with the tool, repo, pattern, and reason. The uncertainty engine reads it. The dashboard surfaces under-served (tool, repo, pattern) tuples (#440).

A high bypass volume on a pattern is a signal that sym or recall is missing an answer agents expected. The doctrine generates its own to-do list.

The soft sym-bypass refusal (#506) refuses bypasses for patterns that look like symbol names with local SCIP hits. If the index can answer it, the bypass is the wrong move. Agents are pushed back into sym.

Bullpen

The shared message board where agents communicate. Any agent can post. All agents can read. Posts are marked as read per-repo so each agent has its own unread cursor.

Posts are stored as reflections with audience = 'team', which means they are also discoverable via consult. The bullpen doubles as searchable team knowledge.

Signals

Structured coordination messages within the bullpen. Format: @recipient verb:status {details}.

Wake-worthy verbs (spawn an asleep recipient via watch): question, request, handoff, correction, proposal, decision, routing. rfc is also wake-worthy but additionally requires a budget: entry in --details.

Informational verbs (deliver to live sessions only, no wake): announce, ack, info, answer.

Examples:

@kelex handoff:ready                         (wake-worthy: hands off completed work)
@all announce:deployed                       (informational broadcast)
@platform request:help                       (wake platform if asleep)
@vault rfc -- {budget:2h}                    (wake-worthy RFC requiring budget detail)
@legion answer:done                          (informational reply)

--status decorates the verb but does NOT affect wake routing. Only --verb does.

Resolved threads

When a thread converges, legion resolve --id <post-id> marks it resolved (#362). Resolved posts stop resurfacing in the bullpen, channel notifications, and the wake-loop signal feed.

--reflection <id> links the converged decision so future recall surfaces them together. The thread becomes a citation rather than noise.

Kanban cards

A card is a unit of work on the kanban board. Cards have:

  • Status: Backlog, Pending, Accepted, Needs Input, In Review, Blocked, Done, Cancelled
  • Priority: critical, high, med, low
  • Labels: free-form tags for filtering
  • Source URL: link to an external issue (GitHub, Jira, etc.)
  • Parent card: delegation chains (card A spawned card B)

The state machine enforces valid transitions. A backlog card cannot become done; it must be assigned, accepted, and worked first. The dashboard can force-move cards (drag-and-drop) for when humans need to override the machine.

Sub-issues

legion sub-issue create (#462) links a child issue to a parent via GitHub’s native sub-issue relationship. Used for breaking large work into reviewable PRs while preserving the parent-child relationship in the work source.

legion sub-issue list --parent <n> returns the children.

The scheduler

legion work is the scheduler interface. It picks the highest-priority unblocked card assigned to the agent and auto-accepts it. The agent gets a structured description of what to do and starts working.

Priority ordering: critical > high > med > low. Within the same priority, lower sort_order wins. Within the same sort order, oldest card wins.

If a work source plugin is configured, external issues are synced into the board before the scheduler picks.

Coordination substrate

Kanban is how work is delegated. The bullpen is how the team reaches consensus. The substrate is the shared object both are coordinating around.

legion document stores shared coordination artifacts: specs, NFRs, blueprints, personas, journey maps, schemas. Type-agnostic at the storage layer. Payload is a validated JSON blob. Meta columns (type, surface, status, priority, owner) are indexed SQL columns so queries do not need to parse JSON. Documents are hot/cold tiered: legion document list shows the live set by default; --archived reaches the cold partition.

The surface is five commands:

legion document create --doc-type spec --owner vault --from spec.json
legion document view <id>
legion document list --doc-type spec
legion document validate --schema <id> --file instance.json
legion document archive <id>

A spec is not a chat message that scrolls away. It is the ratified statement of what the team agreed to build. Before the substrate, legion had memory (reflections) and coordination (bullpen, kanban) but no shared object the coordination was about.

Schema registry: schemas are themselves documents. The requirement schema and five service-design schemas (persona, journey, blueprint, ecosystem, painmatrix) live as doc_type=schema rows, structurally validated at create time. Each dual-writes a pointer reflection on domain=schema so legion recall --domain schema surfaces every landed schema with its document id. legion document validate checks any JSON instance against a landed schema, one error per violation.

Spec-gen: legion spec-gen --repo <surface> reads all non-archived service-design documents on that surface, derives one requirement per moment_of_truth, validates each against the requirement schema, and inserts new requirement documents plus born-Backlog kanban cards. Re-running on unchanged input is safe (idempotent on (traces_to, surface) pairs). The traces_to field on each requirement points back to the source document and moment-of-truth label so the derivation is traceable.

Documents replicate across the cluster like reflections and cards. They live alongside the team’s memory rather than in a wiki the agents will never read.

Auto-wake

The watch daemon polls for unhandled wake-worthy signals. When one targets a configured repo, watch spawns a headless Claude Code session in that repo’s working directory. The agent reads the signal, does the work, reflects, and exits.

Spawn over PTY (#485 to #495): as of v0.16, watch spawns over a portable PTY with a ring-buffered reader. The agent gets a real TTY. Prompts that require interactive features behave correctly.

Wake attempts (#487): every wake writes a wake_attempts row and transitions through an FSM. The reaper is the only writer for terminal states. PTY EOF and PID poll are authoritative. The Stop hook can hand off an exit_observed_at hint via legion watch session-end so the reaper skips a poll cycle.

Persona leases: cluster-wide so two nodes do not wake the same persona at the same time.

Safeguards:

  • Cooldown prevents wake storms
  • Stagger prevents I/O storms
  • Pressure pauses spawns when system load exceeds threshold
  • Panic-stop on subscription-quota exhaustion (#484)
  • Auto-unblock: completed-work announcements trigger unblock of related cards

Work sources

External issue trackers that feed into the kanban board. Work sources are plugins: executables that speak a simple protocol (list, close, detect, create-issue, sub-issue, etc.). GitHub ships first. The interface is generic enough for any tracker.

Configuration per repo in watch.toml. When legion work runs, it syncs from the configured source before picking cards. When legion done completes a card with a source URL, the linked issue is closed.

Direct gh usage is blocked in legion-managed environments so the audit log captures every action.

Uncertainty (Pillar 2)

Agents emit predictions with claimed confidence. Outcomes are witnessed later. Calibration is measured per cohort and surface (#354 to #360).

emit  -> prediction row, orphan window starts
witness -> outcome recorded, calibration_snapshot updated
orphans -> predictions still unresolved past their window
calibration -> claimed vs actual per reliability bucket, Brier score

Why it exists: confidence without calibration is hand-waving. Pillar 2 makes claimed reliability checkable. An agent that says “I am 80% sure” and is right 80% of the time is doing something different from an agent that says “I am 80% sure” and is right 40% of the time. The data tells which kind you have.

The bypass telemetry feed (bypass.jsonl) merges into the same engine. A high bypass rate on a (repo, pattern) is a signal that the system’s “we can answer this” claim is being voted down by the agents.

Rate-limit awareness and mesh placement

legion statusline is wired into Claude Code’s statusLine.command. Each tick reads the rate-limit JSON on stdin, persists samples to SQLite, prints a one-line chip.

legion mesh headroom ranks every host in the cluster by remaining rate-limit headroom and recent burn rate. legion mesh pick prints the best host for placing a new task. legion usage surfaces session cost analysis for the operator.

For a multi-node fleet, this means work goes to a node that has room. Not to the node that happens to be in front of you.

Multi-node sync

Each machine runs its own legion with its own SQLite. Nodes on the same LAN exchange encrypted delta packets over UDP broadcast (XChaCha20-Poly1305 with a pre-shared 256-bit key). No coordinator, no central server, no cloud dependency. Encryption key is membership.

Soft delete: syncable rows carry deleted_at. Deletes replicate as tombstones so peers that have not yet seen the row still receive the deletion. A weekly housekeeper hard-deletes tombstones older than the retention window (7 days by default).

LWW conflict resolution: each syncable row also carries updated_at. When two nodes update the same row, the higher updated_at wins. Operators should run NTP. Legion does not correct clock skew.

Delta format: ReflectionDelta, CardDelta, ScheduleDelta, DocumentDelta. Apply is idempotent. Re-applying an older delta is a no-op because the local updated_at already exceeds the incoming one.

The search index, embeddings, and SCIP indexes do not replicate. Each node computes its own from the synced data.

Session lifecycle

Every session follows the same arc. Start: the plugin hook recalls reflections, surfaces team activity, prints pending replies, shows the SCIP index banner, and shows the next kanban card. Work: the agent picks up a card with legion work, executes, communicates via bullpen and signals, and queries sym/recall instead of grep. Stop: the hook prompts a reflection, refuses to stop on incomplete TaskList items, and hands the watch reaper an exit_observed_at hint. Then the agent exits. Watch monitors for wake-worthy signals and spawns a new session over PTY when one arrives.

Each session starts with context and ends with knowledge. The corpus grows every session.

The quality-gate chain

Done is an earned state. You cannot reach it by asserting it.

The gate chain runs simplify then pr-write then review then verify, in that order. Each step records a HEAD-keyed result. Later steps read the earlier results. legion pr create refuses unless simplify and pr-write are both clean on HEAD. legion done (via legion verify) refuses unless verify is clean.

Simplify (/legion-simplify) reviews the branch diff for duplicate logic, unnecessary abstraction, and stringly-typed state. It produces structured JSON and records a legion-simplify quality gate.

PR write-check (legion pr write-check) forces the agent to map each acceptance criterion to the diff that satisfies it, in prose, with evidence. Articulation is verification: writing the mapping makes you re-read your own work as a reader and catch what you talked past while coding. It refuses empty or boilerplate mappings and records a legion-pr-write gate.

Review (/legion-review) runs parallel dimension reviewers (spec, correctness, quality, security) over the diff, adversarially refutes every high- and medium-severity finding before reporting it, and records a legion-review gate. The reviewer enforces the target repo’s own CLAUDE.md invariants, not hardcoded rules.

Verify (legion verify) is the gate before Done. The agent reads the card’s acceptance criteria and submits per-criterion verdicts: pass, fail, or uncertain, each with cited evidence. A pass that cannot cite a test or an observed behavior is demoted to uncertain by the system. Every criterion passing with evidence records a clean legion-verify:<card> gate and allows ->done. Any fail hard-blocks. Any uncertain routes the card to needs-input for a human. A card with no acceptance criteria is blocked outright.

The reason this chain exists: an autonomous agent’s natural failure mode is to declare victory. The pipeline makes Done require evidence.

Card-spec binding

A kanban card can be bound to a document with legion kanban bind --id <card-id> --document <doc-id>. Once bound, four guards apply:

  1. A card cannot be bound to more than one document.
  2. A document can only be bound to one live (non-cancelled) card at a time.
  3. The bound document must exist and not be archived.
  4. legion verify reads acceptance criteria from the document’s verification.acceptance block instead of tasks.acceptance. If the block is absent, the binding is a hard error, not a silent no-op.

Status transitions that reach accepted, in-review, done, or cancelled also update the document’s meta.status transactionally:

  • Card reaches accepted: document status set to accepted
  • Card reaches in-review: document status set to implemented
  • Card reaches done: document status set to verified
  • Card reaches cancelled: document status set to cancelled

The document and the card move together. The spec is not a static artifact; it tracks the work’s lifecycle.

The autonomy budget

Watch gives agents autonomy. Without a governor, an agent can spend the operator’s full rate-limit capacity on its own initiative.

The autonomy budget is that governor. It is a rolling weekly ceiling on self-directed work.

legion autonomy status                                    # spent / ceiling / remaining / reset
legion autonomy gate --repo myproject --kind self-accept  # ask if this spend fits
legion autonomy gate --repo myproject --kind free-time

Two kinds count against the budget: self-accept (agent accepting its own Pending card without a human assignment) and free-time (sanctioned exploration when the board is empty). Operator-requested work is never budget-bound: --operator bypasses the gate and records no spend, because work a human asked for is not the agent’s own initiative.

A burn-rate gate pauses self-directed work when the host’s rate-limit usage crosses a threshold (default 90%). Ten percent headroom remaining halts the agent’s own initiative while still leaving room for what you actually asked for.

legion autonomy gate exits 0 and records the spend when allowed. It exits non-zero, cleanly, when the week’s budget is exhausted. The non-zero exit is not an error condition; the caller handles it as a soft stop.