Spec: agent-scale architecture — many agents, one main #
Type: spec
Status: IMPLEMENTED
Status note: slices A–K are held as of 2026-07-10: advisory activity and shared
worktree/run state; package-aware fast/land verification; generated ledgers;
worktree bootstrap/prune; corpus engine; hidden-window Bevy evidence;
heartbeats; spec-owned work-order metadata plus generated ROADMAP status;
human/JSON project status; dispatch-consistency fixtures; deterministic
agent scenarios; safe task lifecycle wrapper with serialized final landing;
and read-only environment doctor.
Stage: Process
Work order: project-operations
Work priority: 5
Work class: process
Blocked by: none
Exclusive keys:
- tools/
- wiki/process/
- crates/misaligned-bevy/
Design:
- wiki/vision/simulation-laws.md#justification-and-legibility
- wiki/process/living-spec.md#the-corpus-rule
- wiki/process/meta.md#change-rule
Depends on:
- wiki/engineering/crate-workspace.md#spec-crate-workspace-core-terminal-bevy-assets
- wiki/engineering/env.md#spec-the-environment-variable-registry-every-switch-documented
- wiki/process/workflows.md#workflows
- wiki/process/ROADMAP.md#roadmap-the-dispatch-board
Dependency notes #
The structured references above identify the contracts to re-verify. Relationship context:
engineering/crate-workspace.md (target package shape; implement as its own work order), engineering/env.md (new tool env vars register there), process/workflows.md (knowledge — keep in sync when gates change), process/ROADMAP.md (knowledge — dispatch board and conflict flags).
The problem this solves #
Misaligned is built by many agents in parallel. The corpus-first rule and
worktrees are right; the coordination architecture was thin, so agents
collided on the same files, ran maximum gates at once, union-merged the same
ledgers, and filled the disk with private Bevy target/ trees. Speed of
check.sh matters; who may work on what and what "done" means
matter more.
This spec is the standing process law for that scale. Implement criteria as independent landings unless a criterion names another as a hard prereq.
Decided shape (summary) #
| Layer | Decision |
|---|---|
| Task visibility | Work orders advertise likely paths; overlap warns but never blocks isolated worktrees |
| CPU mutex | At most one full Rust gate on the machine (lock — already in check.sh) |
| Landing mutex | One short task-finish lock covers final rebase, land gate, merge, and push |
| Verification | Mid-loop narrow tier; once land gate; CI fast corpus plus change-aware Rust |
| Ledgers | Append-only uniquely named files; generated indexes, not hand-edited tips |
| Disk | Private package outputs per worktree; shared dependency cache only |
| Packages | Workspace shape in crate-workspace.md (deferred) |
| Docs gates | Fast, testable Python corpus/wiki engine with fixture contract |
| Bevy evidence | Headless/offscreen land proof, not only interactive windows |
| Observability | Heartbeat files so "is it stuck?" is answerable |
| Dispatch truth | Specs own structured work metadata; ROADMAP receives a generated live index |
| Project status | One human/JSON command combines work orders, activity, runs, worktrees, issues, and freshness |
| Scenario evidence | Deterministic agent scripts emit transcripts and machine-readable evidence |
| Task lifecycle | One safe wrapper starts, checks, lands, or abandons a metadata-backed work order |
| Environment | One doctor command reports missing required and optional local capabilities |
1. Dispatch activity (advisory overlap) #
Behavior #
Before an agent starts repository work that can edit code or binding wiki pages, it records work-order activity:
- id — worktree/task name (e.g.
dark-frame) - class —
docs|frontend|sim|save|process - exclusive_keys — legacy metadata name for likely edit surfaces (for
example
crates/misaligned-bevy/src/main.rs,crates/misaligned-core/src/save.rs, or a binding wiki page). These paths are collision information, not ownership. - status —
claimed→checking→landing→done, withblockedandabandonedavailable when the task is not progressing.
A second activity record whose paths intersect an active task warns and
proceeds. Worktrees isolate edits; Git exposes textual collisions; the
corpus, tests, and landing agent resolve semantic collisions against current
main. Hard path exclusion was retired after it created a wait cycle on
2026-07-10: action-contract held main.rs while waiting for camera-exposure
work that needed the same file. A blocked task must never stop its own
prerequisite.
Only scarce phases are serialized: the machine-wide Rust gate and the short
task.sh finish landing phase. The ROADMAP rule of at most one sim+save-heavy
work order remains an explicit dispatch constraint rather than a path lock.
Storage keeps the historical machine-local gitignored path
.agents/claims/<id>.claim (override with MISALIGNED_CLAIMS_DIR).
The claim.sh filename and claim verb are compatibility interfaces for
older worktrees; activity is the current verb. Discover with
tools/claim.sh list.
Holder pid is the calling agent/shell
(PPID, or MISALIGNED_CLAIM_PID). Dead holders are reaped on the next
claim/list. No Tangled auth required.
tools/claim.sh activity dark-frame --class frontend --key crates/misaligned-bevy/src/main.rs \
--key wiki/interface/material-dark-frame.md
tools/claim.sh list
tools/claim.sh set-status dark-frame blocked # visible, never a path lock
tools/claim.sh set-status dark-frame checking # or landing
tools/claim.sh release dark-frame # done (or --status abandoned)
# bundled with worktree create/remove:
tools/worktree-new.sh dark-frame --class frontend --key crates/misaligned-bevy/src/main.rs
tools/worktree-done.sh dark-frame
ROADMAP conflict flags (🟥 sim+save / 🟧 sim / 🟩 isolated) remain the dispatch index. Path activity predicts reconciliation cost; it does not prohibit work.
Acceptance criteria (slice A) — HELD 2026-07-09 #
- A documented activity procedure exists (tool or script + AGENT.md / prompts pointer) that records class + likely paths + status.
- Attempting a second overlapping activity succeeds with a warning naming the other task and paths; it never creates a wait dependency.
blockedis representable, and completing or abandoning work clears its activity record.- ROADMAP or workflows.md states that agents inspect overlaps, work in isolated worktrees, and reconcile at landing.
2. Verification phases (fast vs land) #
Behavior #
| Phase | When | What |
|---|---|---|
| Fast / mid-loop | After an edit | Narrowest ./tools/check.sh mode that can catch the edit (--docs / --lib / --frontend or auto) |
| Land | Once before merge to main | One green auto or --full under the rust lock when Rust is involved |
| CI: corpus | Every push / pull request | Python fixtures, corpus/wiki gates, generated-index freshness |
| CI: Rust | Rust-impacting pushes; every pull request / manual run | fmt, tests, terminal + Bevy clippy/build |
Already held (2026-07-09): path auto-classification,
--docs|--lib|--frontend|--full|--land, rust gate lock, parallel docs gates,
collapsed agent smoke. Package-aware classification waits on
crate-workspace.md.
Tangled runs two contracts. .tangled/workflows/corpus.yml is the fast
always-on corpus job and explicitly installs python3; it runs classifier and
corpus fixtures before the real corpus/wiki/index checks. check.yml uses a
two-commit clone and tools/ci-rust-changed.sh to stop after classification
when a push cannot affect Rust. Pull-request and manual triggers deliberately
run the complete Rust/Bevy safety gate. Tangled has no scheduled workflow
trigger or native path filter, so the manual trigger is the supported full
safety run instead of pretending a scheduler exists.
Acceptance criteria (slice B) #
- AGENT.md and prompts/README state mid-loop narrow vs one land gate — HELD.
./tools/check.sh --landis an alias for the land policy (auto) documented in workflows.md — HELD.- After crate-workspace.md lands,
classification keys off packages, not only path prefixes — HELD
(
check.shclassifies core/terminal vs Bevy/assets package paths). - Docs failures run in a small Python-enabled workflow before expensive Rust work; non-Rust pushes skip the Rust wall, while pull-request/manual runs are full — HELD.
3. Append-only ledgers and generated indexes #
Behavior #
Shared tip-of-file ledgers (wiki/log/DEVLOG.md hand edits, hand-maintained
rows in wiki/process/specs.md when avoidable) cause every parallel land to
rebase-union the same lines.
Target:
- Session / devlog bodies remain uniquely named files
(
wiki/log/YYYY-MM-DD-topic.md) — already the habit. - DEVLOG ledger becomes a generated index (or a strict append-only
convention with one tool that inserts a block without rewriting others'
entries). Preferred: generate
DEVLOG.mdfrom session log headers / frontmatter so agents never edit the index by hand. - specs.md status table is generated from each spec page's
Status:frontmatter (and title), or updated only by a tool that rewrites the whole table from the tree. Agents do not hand-merge table rows. - Decision volumes stay dated append-only files (already).
Union-merge rules in AGENT.md remain the safety net until generation lands; after generation, the generator is the source of truth for the index file.
Behavior (landed) #
# after adding wiki/log/YYYY-MM-DD-topic.md and/or amending a Type: spec:
tools/ledger_index.sh # rewrite DEVLOG.md + specs.md
tools/ledger_index.sh --check # fail if indexes stale (wired into check.sh)
Session bodies stay uniquely named. Indexes are fully generated; last writer
wins on the index files only. ./tools/check.sh docs path runs --check.
Both projections declare Generated: tools/ledger_index.sh in their metadata,
making their replaceable status machine-readable instead of relying on prose.
Acceptance criteria (slice C) — HELD 2026-07-10 #
- A tool can regenerate the DEVLOG index and specs board from the tree
without manual row editing — HELD (
tools/ledger_index.sh). - AGENT.md / prompts say: add a uniquely named log file; run the generator; do not hand-edit generated indexes — HELD.
- Parallel landings no longer require hand union of DEVLOG rows — HELD (unique session files + regenerate).
4. Worktree bootstrap, dependency cache, prune #
Behavior (landed helpers) #
tools/worktree-new.sh <task> --class frontend --key crates/misaligned-bevy/src/main.rs
# -> .Codex/worktrees/<task> on worktree-<task>, seed-cargo-target, activity
tools/worktree-done.sh <task> # clear activity, rm target/, remove worktree
.Codex/worktrees is the one repository default for every agent surface.
Operators that need another location set MISALIGNED_WORKTREE_ROOT for both
helpers; tool brands do not define separate roots.
Shared dependency compilation cache (sccache or cargo cache) remains
recommended; local package artifacts stay per-worktree. Never share one
CARGO_TARGET_DIR across worktrees for local packages.
Acceptance criteria (slice D) — HELD 2026-07-09 (helpers; sccache optional) #
- A single documented command creates a seeded worktree — HELD
(
tools/worktree-new.sh). - workflows.md documents private-target rule; sccache is optional install guidance — HELD (private target); sccache install remains operator choice.
- Removing a worktree prunes
target/by default viatools/worktree-done.sh— HELD.
5. Corpus / wiki engine #
Behavior (landed) #
tools/corpus_engine.py is a one-pass Python engine (indexes pages and
headings once). Entry points stay stable:
tools/wiki_gate.sh # python3 tools/corpus_engine.py --wiki
tools/corpus_gate.sh # python3 tools/corpus_engine.py --corpus
tools/test_corpus_engine.sh # fixture suite (also in check.sh docs path)
Requires python3 (same as ledger_index.sh). Any page with a valid
Generated: metadata owner is skipped for link checks so a derived, truncated
blurb cannot false-fail; the corpus gate verifies that the generator target
exists.
Acceptance criteria (slice E) — HELD 2026-07-10 #
- Docs gates complete in well under a few seconds on a quiet machine — HELD (~0.3s each for wiki + corpus on ~200 pages).
- Fixture tests cover reachability, links, Design and dependency anchors,
exact roles/status, generated owners, doorway bounds/structure, and retired
authority wording — HELD (
tools/test_corpus_engine.sh). - Existing hooks and the fast CI workflow still call
tools/wiki_gate.shandtools/corpus_gate.sh— HELD.
6. Headless Bevy land evidence #
Behavior #
Frontend land confidence must not require a human watching a window. Extend the shot harness (already env-driven) so land-tier frontend work can assert:
- process exit 0
- fog-audit (or successor) pass
- optional frame metric / hash stability where deterministic
Document the required MISALIGNED_SHOT=… (or successor) for "seen running"
on Bevy-impacting landings.
Acceptance criteria (slice F) — HELD 2026-07-10 #
- A documented headless/offscreen path produces pass/fail without manual window interaction.
- workflows.md "definition of done" for Bevy cites that path.
--frontendgate optionally invokes a cheap harness smoke when Bevy sources change (time-bounded; not a full art review).
7. Agent heartbeats #
Behavior (landed) #
tools/heartbeat.sh start <id> --worktree path --phase boot
tools/heartbeat.sh phase <id> 'check-land'
tools/heartbeat.sh end <id> --status ok|fail
Writes gitignored .agents/runs/<id>/status.json + events.ndjson
(override MISALIGNED_RUNS_DIR). Humans and other agents inspect status
files instead of empty stdout pipes. Dispatch prompts should call start/
phase/end around long runs (implement-gap contract).
Acceptance criteria (slice G) — HELD 2026-07-09 (tool + docs) #
- Documented convention + helper to start/update a run record — HELD.
- implement-gap / prompts path documents heartbeat use — HELD (prompts README + implement-gap pointer; agents still must call the tool).
- Paths are gitignored; convention is written in process docs — HELD.
8. Structured work orders and project status #
Behavior #
Every non-IMPLEMENTED spec is a dispatchable or intentionally staged work
order and declares the structured fields owned by
meta.md: task slug, priority, work class, hard
blockers, and likely edit surfaces (the legacy Exclusive keys field).
Status: remains the lifecycle source of truth;
the work-order fields must not duplicate it.
tools/work_orders.py validates that metadata and generates the live
work-order region of ROADMAP. Human rationale and historical dispatch prose
remain hand-authored outside the generated region. The generated projection is
replaceable and may never become a second owner of status or dependency law.
tools/project-status.py is the one answer to "what is happening now?" It
combines, without mutating the repository:
- ready, active, blocked, and staged work orders in priority order;
- advisory activity and heartbeat/run state;
- every worktree's dirty, ahead/behind, branch, and last-commit age;
- Tangled decision issues when the local CLI is available/authenticated;
- generated-index freshness and a short recommended-next-lane read.
Human output is concise. --json emits the same facts for agents and other
tools. Network/auth failure degrades the issue field to unavailable; it does
not make local project status unusable.
Acceptance criteria (slice H) — HELD 2026-07-10 #
- Every non-IMPLEMENTED spec carries valid work-order metadata, and malformed class, priority, blocker reference, task slug, or advertised path fails the docs gate.
- One generator writes/checks a clearly marked live region in ROADMAP from those fields; changing a spec status or blocker without regeneration fails.
tools/project-status.pyreports work orders, activity, runs, worktrees, optional decision issues, and generated freshness in both human and JSON forms without changing repository state.- At least one fixture proves a blocked spec cannot be recommended as the next lane and a later-stage READY spec remains staged rather than becoming an accidental B1 dispatch.
9. Dispatch consistency gate #
Behavior #
The docs path runs a fast project-operations consistency check alongside the corpus and wiki checks. It enforces facts that are mechanical enough to prove:
- work-order metadata and anchored blocker targets are valid;
- generated dispatch output is fresh;
- explicit status labels in ROADMAP agree with the owning spec;
- an
[OPEN]section does not retain entries explicitly marked resolved; - an acceptance criterion cannot remain locally marked blocked on an issue that the same current page declares closed;
- advertised path keys exist, so retired workspace paths cannot silently remain dispatch locks.
Semantic design disagreements still belong to ticks; the gate must not guess at meaning from ordinary prose. Network-only issue truth is shown by project status and is never required by CI.
Acceptance criteria (slice I) — HELD 2026-07-10 #
- The local docs gate and fast corpus CI call the same consistency command.
- Fixtures pin every rule above, including useful file/line diagnostics.
- Existing current-corpus violations are repaired before the gate is enabled; dated logs remain untouched even when they preserve superseded language.
10. Deterministic scenario evidence #
Behavior #
Checked-in scenarios/*.agent files are agent-protocol command scripts with
declarative EXPECT / REJECT assertions. tools/scenario.py builds or uses
the terminal binary, runs a scenario twice with the same seed, rejects output
drift or ANSI, checks its assertions, and writes an optional evidence bundle:
the input, transcript, SHA-256 digest, and JSON result.
The initial suite covers the opening read, machine delegation/routing, and the general project smoke path. Scenarios prove semantic state and narration; they do not use pixel-perfect screenshots as game-law assertions. A scenario may name a Bevy shot kind as additional visual evidence, routed through the headless harness in slice F.
Acceptance criteria (slice J) — HELD 2026-07-10 #
- Scenario definitions are validated by the docs gate without compiling
Rust; malformed directives or missing
quitfail fast. - A normal run is deterministic for the same seed, checks every declared assertion, and exits nonzero with a focused diff/error on failure.
--output DIRwrites a transcript, copied command source, digest, and JSON summary; a no-output run leaves the tree clean.- At least three checked-in scenarios cover project smoke, opening/senses, and machine-work delegation.
11. Task lifecycle and environment doctor #
Behavior #
tools/task.sh composes the existing helpers; it does not replace their
contracts:
tools/task.sh start wiki/process/agent-scale.md
tools/task.sh status
tools/task.sh check project-operations
tools/task.sh finish project-operations
tools/task.sh abandon project-operations
start resolves task/class/likely paths from the spec and creates the seeded
worktree with an activity record. check moves activity/heartbeat to checking
and runs the land gate. finish requires a clean committed branch, rebases onto
current origin/main, reruns the land gate, fast-forwards a clean current
primary checkout, pushes main, ends the heartbeat, and removes the task
worktree. One machine-local landing lock serializes that final
rebase/check/merge/push window. A failed rebase, check, relationship check, or
push stops before
cleanup. It never commits, stashes, resets, force-pushes, or guesses through a
conflict. abandon uses the existing clean-worktree refusal unless explicitly
handled by the operator through the lower-level helper.
tools/doctor.sh performs read-only checks for required commands, Cargo
workspace health, Tangled context/auth availability, worktree/activity state,
private-target safety, and optional accelerators such as sccache and the
headless display path. Required failures exit nonzero; optional absences are
plain warnings.
Acceptance criteria (slice K) — HELD 2026-07-10 #
- The wrapper starts a task using only its spec path and metadata and exposes the same project status command.
- Check/finish update activity and heartbeat phases, and finish refuses dirty, uncommitted, divergent-primary, failed-gate, or rejected-push states before deleting anything.
- Abandon preserves dirty work by default.
- The doctor distinguishes required failures from optional warnings and has a no-network mode suitable for CI or offline work.
- Shell syntax/fixture tests exercise argument handling and the destructive refusal paths without creating or pushing a real task branch.
- The final landing lock refuses a live holder, reaps a dead holder, and cannot be confused with advisory path activity.
Relationship to the crate workspace #
crate-workspace.md is the package
work order. This page is the coordination and tooling work order. They
reinforce each other but land separately. The crate workspace has landed;
project-operations metadata and checks therefore use current crates/
package paths and reject retired monorepo activity paths.
Implementation order (suggested, not binding) #
- Slice A (activity) — makes likely collisions visible without blocking work
- Slice C (ledgers) — stops landing union theater
- Slice D (worktree bootstrap/prune) — disk
- Slice B leftovers + G (land alias, heartbeats) — agent behavior
- Slice E (corpus engine) — docs gate latency
- Slice H + I (structured work/status and consistency) — dispatch truth
- Slice J (scenarios) + F (headless Bevy) — reproducible evidence
- Slice K (task lifecycle + doctor) — one safe operator doorway
Rejected alternatives #
- Only faster check.sh — necessary but insufficient without visible activity, landing serialization, and ledger shape.
- Shared CARGO_TARGET_DIR across worktrees — false-green risk.
- CI as the only gate — local loop must stay honest.
- One mega-agent that owns main — rejects the multi-agent premise of the repo.