AUTONOMOUS_RUN_LOOP.md

pipelines/AUTONOMOUS_RUN_LOOP.md

THE AUTONOMOUS RUN LOOP — the procedure a session runs unattended

What this is. The exact loop a Claude Code session executes with no human present, so that work

advances correctly while Josh is asleep, driving, or playing. It is a procedure with gates, not an

aspiration. Every step below is either a command that exists today or a tooth that fails the tree.

The target it serves. Josh, verbatim, 2026-08-01: *"We built the entire fucking spine and region

pages and all the thousands of hours of architecture so you can build 79 chapters autonomously. I've

already made all the decisions I should be needing to make. You have what you need so stop fucking

going in blind."* The loop's whole job is to make that true mechanically — the session reviews

nothing with him except creative art and quest dialogue.

The failure it ends. A cold-start trace walked the repo's own prescribed orientation chain on

2026-08-02: six forwarding layers, nine named destination docs, 19,774 words to reach the

chain's designated terminus — and the answer there was *wrong* ("the next node is Ch 13"; the spine

has been 79/79 and v3-rewritten since 2026-07-15). Full obedience to every pointer the chain issued

came to ~118,761 words. No document in the repo stated the next action at any word count. The

two artifacts that actually were current-truth were a row 20,981 words into docs/DOC_MAP.md and a

cell in a CSV nothing pointed at.

So the loop's first law is that "what next" is one file and one command, and the answer is

identical for any two sessions on the same tree:

python harness/queue.py next

---

0. The loop, in one screen

SESSION START
  0.1  git status ; git log --oneline -5
  0.2  python harness/run_gates.py            # UNPIPED. Exit 0 or the loop HALTS (§6.1)
  0.3  python harness/queue.py recover        # session-death recovery (§4.3)
  0.4  python harness/queue.py intake         # Josh's review comments -> queue items (§5)

PER ITEM  (repeat until `next` exits 4)
  1    python harness/queue.py next           # THE pick. Deterministic. exit 4 = empty (§6.4)
  2    python harness/queue.py claim <ID> --session <tok>
  3    python harness/route.py <kind> --chapter CH_NN --region <r>   # READ THE READ-SET, IN ORDER
  4    produce                                 # canon is WHAT; the proposal is only HOW (§2.2)
  5    python harness/run_gates.py            # the item's named gates must be green
  6    the adversarial critic                  # mandatory for authored prose/images (§2.4)
  6b   the comparator rubric                   # if the item touched a fun-bearing domain (§2.4b)
  7    git add <your paths> ; git commit       # never `git add -A`; never push
  8    python harness/queue.py land <ID> --commit <sha> --critic GO
  9    git add docs/work_queue.json docs/archive/WORK_QUEUE_HISTORY.md docs/START_HERE.md ; git commit
       # claim/land REWRITE the map's Ranked-next in place (§4.4) — commit map + queue together

SESSION END
  the queue file IS the handoff. There is nothing else to write. (§4)

Everything below is that loop, specified.

---

1. How a session picks its next work

1.1 The queue is one file

docs/work_queue.json. It replaces reading docs/START_HERE.md ▶ Ranked next, the

▶ PROGRESS LEDGER in docs/PRE_5090_BUILD_PLAN.md:219, the head of

docs/spine/DECISIONS_PENDING_JOSH.md, STATUS.md, and the cascade runbook — and guessing which of

the five is current. Those five stay as the record; none of them is a work order any more.

It is machine-readable because a human-readable queue is a queue that drifts. queue.py next reads

it; a session never picks by eye.

1.2 What an item is

Every field is an FK into something that already exists, so the queue mints no vocabulary of its own:

FieldMeaningBound to
work_kindwhich of the 21 kinds of work this isdocs/task_router.json — so every item resolves to a read-set via harness/route.py
target{chapter, region, artifact_id, path}the node the work is scoped to
derivationthe canon this item realizesdocs/spine/ · _source/01_… · _source/02_… · registries/ — a docs/proposals/ path here is a FAIL
gatesthe gates that prove itkeys in harness/gates_config.json (42 gates)
criticthe adversarial reviewer that must clear itnormalize-doc, the four image critics
authorityloop or josh§3
stateready · in_flight · blocked · awaiting_joshclosed enum; there is no done (§4.2)
priority · attempts · blocked_bythe ordering and the dependency graphother item ids

1.3 What makes an item ready

An item is claimable iff all four hold, and queue.py next applies exactly this test:

1. state == "ready", and

2. authority == "loop" — an item that is Josh's is never handed to the loop, and

3. every id in blocked_by is blocked or gone (landed items leave the file), and

4. attempts < 3.

Order is (priority asc, attempts asc, id asc) — total, deterministic, no coin flips. attempts

sits in the sort key on purpose: an item that just failed sorts behind its siblings instead of being

retried at 3am until the cap parks it. Two sessions on the same tree get the same item.

1.4 Seeded from real state, not from a plan

The queue as landed carries seven items derived from docs/START_HERE.md § Ranked next, the

awaiting_pick row in docs/art_boards/MANIFEST.csv, the single-entry artifacts list in

docs/canon_derivation.json, the ratchet in docs/ledger_baseline.json, and the 64 unbuilt

T0_Region_Index rows. One of them (WQ_0002) is a defect the seeding itself found: the map's

third-ranked open item is a decision Josh ruled on 2026-07-19

(docs/spine/DECISIONS_PENDING_JOSH.md:2919) whose ⚠ NOT ruled banner still sits nineteen lines

above its own ✅ RULED line. That is precisely the class of rot a single machine-checked queue

exists to stop.

---

2. The execution contract, per work item

2.1 Read canon, through the router — never a proposal

python harness/route.py <work_kind> --chapter CH_NN --region <region_id>

Read what it prints, in the order it prints it. The read-order law is

docs/TASK_ROUTER.md § *The read-order law*: the spine node first, then the region page at section

grain, then the T0 registries the [POPULATE→ …] tags route to, and **the program doc LAST and

subordinate**.

This is the step whose absence cost a whole session. docs/spine/CH_02.md:226 ## Asset anchors

already listed the Flores board's entire contents — every CR_ id, the architecture set dressing,

the material palette, the realm object's art brief — each tagged with its destination registry. The

lane built the board from docs/proposals/CONCEPT_ART_PROGRAM.md instead, invented a "pick one of

three readings of Flores" frame for content the spine had settled, and shipped zero images. **It

passed a structural gate and four adversarial critics.**

The queue now refuses that item before a word is written: gate 42's T-DERIVATION fails any

node-scoped item whose derivation list is empty, unresolvable, or points into docs/proposals/.

2.2 Produce

Canon is WHAT; a proposal is only HOW (order of operations, lane routing, licence posture,

board shape). If a proposal asks you to re-decide something the node already settled, the proposal is

the defect — docs/DOC_MAP.md § 0 rank 6.

Fan-out rules, when the item warrants lanes: paste docs/SUBAGENT_CONTRACT.md §1 verbatim into every

lane prompt, and set model explicitly on every lane — nothing inherits the session model

(standing global rule; one 8-agent fan-out once burned ~20% of a weekly limit).

2.3 Gate

python harness/run_gates.py, read unpiped — a pipe masks the exit code. The item names its own

gates; the suite must be green as a whole. The per-kind assignment below is not invented here — it is

docs/task_router.json's gate column, reduced to the roster keys each entry names. An item may

name *more* gates than its kind's floor (WQ_0005 adds ws_value_form and slice_coherence to a

region page); it may never name fewer.

Work kindIts gates
spine-chapterlint · anchors + the entry enrolled in BOTH target lists · normalize-doc critic
region-pageregion_pages (+ check_region_page.py --self-test)
concept-art-region · concept-art-classboards · canon_derivation
art-bibleboards (the bible header is AWAITING PICK or RATIFIED, nothing between)
questseed_housing · no_dead_end · spine_graph · promise_ledger
music · voicegrand_sage_silence · generation_lifecycle
sfxsfx_coverage
terrainregion_pages · build_space
assetgen-3dgeneration_lifecycle (no binary enters this repo)
cinematicreveal_discipline
bossboss_axes · counter_class_coverage · no_dead_end · rarity_firewall
registryfidelity (--emit-baseline in the SAME commit) · registry_extensions · stress · scan
range-reviewthe RR file enrolled in lint + anchors; tag spine/rr-NN
decisionledger — the ledger stays current-truth: a ruling COLLAPSES its options
realmrealm_ruleset
marketing-webboards (T-MARKETING · T-PANELA-PREVIZ · T-MARKETING-COMPUTED)
program-docregistered in docs/DOC_MAP.md in the SAME commit
gatemust-fire and must-not-fire fixtures; registered in gates_config.json
ue-wiringthe game repo's own build/PIE verification; canon gates unaffected

2.4 Adversarial critic

Mandatory for every kind that produces authored prose or images —

spine-chapter, region-page, concept-art-region, concept-art-class, art-bible, quest,

cinematic, boss, realm, marketing-web, program-doc. It is the critic_required set in the

queue contract, and queue.py land refuses an item of one of those kinds without a recorded

verdict of GO or GO_WITH_FIXES.

The critic loads canon cold (normalize-doc; the four image critics for boards) and it replaces

per-item first-line review, never Josh's authority — docs/CASCADE_RUNBOOK.md §10, ratified

2026-07-04.

Mechanical kinds (registry, gate, ue-wiring, terrain, assetgen-3d, sfx, voice,

decision) take their deterministic gate instead. That is not a lower bar: a registry item is

edited with the csv module only and re-baselined in the same commit, and a gate item ships a

failing fixture per tooth or does not ship.

2.4b The comparator rubric — mandatory when the work touched a fun-bearing domain

Josh, 2026-08-05: *"Remember those critic lenses we created — all those gates based on the

comparable games I mentioned. Are you running everything against those?"* The honest answer was no.

The feel bands were comparator-anchored and fifteen teacher lenses ran on 2026-08-04 as one-shot

sweeps, but nothing made a later piece of work read them again, so a wave could build a fun-bearing

system, close, and never be scored against the teacher Josh named for it. This step is that gap

closed, and it is the same shape as §2.4: the work does not certify itself.

The rule. If the item touched any of the twelve fun-bearing domains — movement ·

combat-melee · combat-ranged-cast · magic-fantasy · loot · discovery · collection ·

raising · building · vehicles · minigames-secrets · systems-social — open that domain's

rubric at harness/qa/comparator_rubrics/<domain>.json, run its checks against what the item

actually built (each check names the instrument it reads — a feel band, the FG-11 chain, a canon

gate — or declares it has none), answer the exceeding_bar question in prose, and record the

verdict on the landing:

COMPARATOR VERDICT - <domain_id> vs <teacher(s)>: EXCEEDS|MEETS|BELOW|UNMEASURED - <evidence>
COMPARATOR VERDICT - <domain_id>: NOT_APPLICABLE - <reason>          # the considered escape

MEETS and BELOW and UNMEASURED are legal answers and are the point. UNMEASURED says the

instrument for that mechanism does not exist yet and carries what it would take — which is a

finding, not a failure. A rubric that can only return EXCEEDS is flattery, and a verdict written

without opening its instrument is the self-certifying pattern §2.4 exists to stop.

The tooth. Gate 51 comparator_rubric reads the wave-close entries in

docs/FACTORY_CONTINUATION.md and FINDS any that triggered a fun-bearing domain and carries no

verdict line for it. It never asks whether a verdict is *right* — that is the rubric's prose

question and a human or a critic answers it. Entries dated on or before 2026-08-05 are DECLARED

NOT-ARMED and can never be findings: no retroactive reds.

2.5 Land

Commit your own paths explicitly — never git add -A (lanes share this tree). **Commit, do not

push; the director pushes. A new doc is not done until it has a docs/DOC_MAP.md row in the same

commit**.

Explicit is not the same as YOURS (added 2026-08-04, from the R7 swallow). An explicit pathspec

is only half the rule: the paths you name must be paths *your own item wrote*. Commit 2eb4b8b1

staged by name a file another lane had authored and left in the shared index

(T1_Personal_Dimension_Spec [ACTIVE v1.1].md) plus a whole unrelated gate apparatus, so a commit

titled for the healing-model ruling became the sole carrier of two other lanes — and a revert of the

ruling would now take a tier doc and a gate with it. **git add <another lane's path> is the same

defect as git add -A.** Before committing, read git status --short and challenge every path

outside your declared write-set; scope the commit itself (git commit -m ... -- <your paths>), never

a bare git commit. If you do carry another lane's work anyway, say so in the commit body and

land an empty provenance commit naming the carrier — the record is the repair, because rewriting

pushed history is worse than the misattribution.

2.6 Update the queue

python harness/queue.py land <ID> --commit <sha> --critic GO

The item leaves docs/work_queue.json and its line lands in

docs/archive/WORK_QUEUE_HISTORY.md. The queue update belongs in the same commit as the work: a

landed artifact with a stale queue is the drift this whole system exists to prevent.

---

3. What stops the loop — the test for "is this mine or Josh's"

3.1 The genuinely-Josh set is four classes, and it is CLOSED

ClassIt is Josh's becauseGrounded at
cardinal_canonchapter/Component assignments, numerical canon, naming, hard lines, §17, the pillars, the visionCLAUDE.md § Cardinal rule (the LOCKED list) · docs/CASCADE_RUNBOOK.md §10
spendmoney leaves an accountCLAUDE.md § Book acquisition (the ~158 MUST-ACQUIRE buys)
credentialsany account, key, or tokendocs/spine/DECISIONS_PENDING_JOSH.md item 3, 2026-07-31 — the standing work-accounts boundary
creative_approvalthe pick on art; the sign-off on character lines that shipdocs/proposals/CONCEPT_ART_PROGRAM.md:1809 ("Josh touches the pick line and nothing else") · CASCADE_RUNBOOK.md §10 batch sign-off

Nothing else is his. authority: josh with any fifth class fails T-AUTHORITY.

3.2 The test, applied in order

Run E1→E5 and stop at the first hit.

numerical-canon count (77+2 nodes, 22 threads, 72 weapons, 10 trades, 12 tiers, 33 species, 22/33

familiar slots, 185 abilities), a name, a §17 hard line, a pillar, the vision.

JOSH, cardinal_canon.

*Filling in* locked structure is not changing it. Only contradiction escalates.

spend or credentials.

JOSH at the approval step only, creative_approval. Producing the candidates, running the

four critics, and publishing the board are all the loop's. He sees one pick line.

docs/BATCH1_RULINGS.md, docs/CANON_AUDIT.md § Josh's rulings,

docs/proposals/systems/RULINGS.md. → LOOP: apply the ruling. A ruling is a work order, not

an open question, and his rulings are the FLOOR — generalize the principle and sweep the whole

class, never just the named instance.

alternatives → deep dive → adversarial review → recommendation + strongest objection), **apply the

recommendation, and show the path.** Do not ask.

3.3 The anti-pattern list — these are E5 and are NEVER surfaced

Data-hygiene renames · docs/DOC_MAP.md rows · file moves and archive retirements · gate teeth and

fixtures · scorecard and baseline re-emissions · fixing a proposal that disagrees with canon ·

collapsing a superseded marker beside its own ruling · choosing which unbuilt region page is next ·

schema and column additions inside a declared extension contract · which model tier a lane runs at.

Surfacing one of these is the failure the loop was built to end, in the opposite direction from going

in blind: it costs Josh a decision he already delegated.

3.4 An escalation is never a bare question

authority: josh requires a brief, and gate 42 checks its shape where the shape lives:

*alternatives*, a *recommendation*, and the *strongest objection* (T-BRIEF-SHAPE). This is

CLAUDE.md's standing protocol, mechanized.

one marked recommendation per panel, armed by gate boards' T-ONE-REC. So the tooth requires

the item to route through boards. One rule, two surfaces, never a second vocabulary.

An escalation never stops the loop. An awaiting_josh item is invisible to queue.py next by

construction — the loop takes the next claimable item and keeps going. Josh being asleep is not a

blocker; it is the normal operating condition.

---

4. The accumulation surface — how a session hands to the next one

4.1 The handoff is the queue file, and there is nothing else

No handoff doc, no session-close narrative, no "next session" paragraph. Those are what produced a

six-layer chain in the first place. A session hands over by having landed its work and updated

docs/work_queue.json — which the next session reads with one command.

4.2 Current-truth, enforced

There is no done state. A landed item is deleted from the queue and its line appended to

docs/archive/WORK_QUEUE_HISTORY.md; T-NO-DONE fails the tree if one lingers. This is the same

split docs/START_HERE.md and docs/archive/START_HERE_HISTORY.md got on 2026-08-02, applied to the

worklist before it can rot the same way.

4.3 Sudden session death costs nothing

Sessions die without warning — an account-auth event killed two live sessions at once on 2026-07-17.

So:

is the memory, not the chat.

every in-flight item to ready, counts the attempt, and records which session died holding it.

The third death parks the item as blocked with the reason *"it is mis-specified, split it"*,

because three deaths on one item is a fact about the item.

4.4 START_HERE is a view (LANDED — WQ_0007)

python harness/queue.py view emits the entire ## Ranked next section, and **every successful

queue mutation rewrites that section of docs/START_HERE.md in place** (sync_map, called after

the save survives the teeth) — so the loop cannot leave the map behind by omission. Commit

docs/START_HERE.md alongside docs/work_queue.json; queue.py prints the reminder. The backstop

against HAND edits is check_orientation.py tooth T-QUEUE-VIEW: whole-section equality against a

fresh queue.py view, duplicate ## Ranked next headings fire, an unavailable view fires, and the

generated section's lines are exempt from T-NO-DATED/T-PATHS (gate queue owns that content). The

view is bounded at 8 numbered items with a generated overflow line, so the map's 120-line cap and

this tooth can never become jointly unsatisfiable.

---

5. How Josh's review re-enters

He reviews two things: creative art and quest dialogue, as he plays. Both re-enter

mechanically.

5.1 Art — through the board state machine

python harness/queue.py intake reads docs/art_boards/MANIFEST.csv and applies the state machine

that docs/proposals/CONCEPT_ART_PROGRAM.md:1780 already defines:

Row stateItem createdRegeneration state
approval_state=awaiting_pickloop item, ready: the DIRECTOR's pick, gates boards — RULED 2026-08-04 (the no-veto law, composed with the thirty-third sitting's picks-delegated ruling). It used to mint awaiting_josh + creative_approval, which is precisely the veto item Josh told the loop twice to stop bringing him. Josh comments by exception on the rendered board instead
approval_state=picked, writeback_status=pendingloop item: run the deterministic applier onto the owning registry's art hooks
pick_amendment_a/b non-emptyloop item: regenerate against the amendment, gates boards + canon_derivation, four image criticsapproval_stateregenerating, regeneration_count + 1

The amendment rides as a note on the realization. The derivation does not move: an amendment is

never a re-decision of the canon node, and treating it as one is how the Flores board went wrong.

The picked → applier item is not bookkeeping — harness/read_boards.py fails a picked board

whose writeback_status stays pending past one commit, so this item is what keeps that gate green.

5.2 Dialogue and everything else — through the inbox

docs/JOSH_REVIEW_INBOX.md, one comment per line:

- [ ] CH_05 — Ana wouldn't say "obviously", she'd say nothing

intake resolves the target (BRD_dddd → board · CH_NN → chapter, derived from

docs/spine/CH_NN.md · a repo path → that file), attaches the gates and the critic, mints the item,

and ticks the line to - [x] WQ_00NN in place — so intake is idempotent and every line carries the

id that owns it.

**A target the resolver does not recognise becomes a BLOCKED item with reason unresolved_target.

It is never dropped.** A silently dropped Josh comment is the worst failure this file can have, so

the failure mode is loud and parked.

---

6. Failure modes

6.1 A gate goes red at 3am

the history and makes the next session's baseline a lie.

anything, the red belongs to someone else's landing. HALT. Do not start an item. Write the

finding as a queue item (work_kind: gate, priority: 0) and stop the session. A loop that builds

on top of an inherited red gate multiplies the debugging surface for whoever fixes it.

That counts the attempt, parks the item, and next hands you the following one. The tree stays

green because nothing was committed.

that.

6.2 A model is unavailable

Return the item to ready with the attempt counted (block then unblock, or let recover do it

if the session died outright). Commit nothing partial — a half-authored spine node that lands is

worse than no node, because the next session cannot tell it is half. Then take the next item: most

kinds do not need the tier that was unavailable. Deterministic work — gates, scripts, git, index

rebuilds, baseline re-emissions — never waits on model budget at all, and the queue is deliberately

stocked with some of it so a token-starved session still advances the tree.

6.3 A critic says NO-GO

A NO-GO is a finding, not a block.

Fix and re-run. queue.py land refuses the item until the verdict is GO or GO_WITH_FIXES, so a

NO-GO cannot be argued past.

and is split. Escalate to Josh only if the NO-GO is about canon — a §17 risk, a cultural-care

flag at a high-care chapter, or a canonical-substrate gap — which is the immediate-escalation set

docs/CASCADE_RUNBOOK.md §10 already names.

6.4 The queue empties

queue.py next exits 4 and says so loudly. An empty queue is a state, never a finish — the

one thing it must never do is look like success. The refill procedure, in order:

1. queue.py intake — the board state machine and the inbox may have items waiting.

2. Un-park what can be un-parked: queue.py list --state blocked, resolve the reasons.

3. Scan the four standing backlog surfaces, each of which is a measured number, not an opinion:

4. Propose the next N items as a program-doc queue item, run through the four-step protocol,

applied not asked. The loop refilling its own queue is E5 work.

A queue that empties with awaiting_josh items outstanding is reported as exactly that: the loop is

done, Josh is the constraint, and the session says so instead of inventing work.

6.5 The halt conditions, complete

The loop stops — fully, and says why — in exactly four cases: an inherited red gate (§6.1); every

remaining item is awaiting_josh or blocked (§6.4); a cardinal_canon escalation whose brief

cannot be written without an answer only Josh has; and gate 42 itself firing, which means the queue

is untrustworthy and must be repaired before it is obeyed.

---

7. The gate that keeps this honest — GATE 42 queue

harness/queue.py --check, enrolled in harness/gates_config.json as queue. Numbering: 37

boards, 38 ledger, 39 canon_derivation are taken; router and orientation landed unnumbered

and hold 40/41 by landing order, so the queue gate takes 42 — which is also the roster count

after this lands.

ToothWhat becomes unrepresentable
T-VACUITYa missing or unparseable queue reading exactly like a clean one
T-CONTRACTan item legalizing itself by widening the vocabulary in the file it lives in
T-IDduplicate or malformed item ids
T-STATE · T-NO-DONEa landed item lingering — the queue rotting into an append-only log
T-KINDan item no router entry can produce a read-set for (going in blind, in queue form)
T-GATEan item no gate proves
T-NODEa node-reading work kind that never says which node
T-DERIVATIONa node-scoped item with no canon, unresolvable canon, or a docs/proposals/ path standing in for canon
T-DERIVE-MATCHan item citing a spine node that is not its own target
T-AUTHORITYa loop item dressed as Josh's, or a Josh item with a fifth class or no brief
T-BRIEF-SHAPEa bare question — no alternatives / recommendation / objection, or a creative approval routed off boards
T-JOSH-STATEauthority and state disagreeing
T-BLOCK-REASONa blocked item with no reason; an in-flight item with no claimant to recover
T-CRITICauthored prose or images with no critic named
T-ATTEMPTSthe loop head-banging past the cap instead of parking the item
T-DEPS · T-CYCLEan item that can never run, or a queue that deadlocks silently

Eighteen teeth, 50 self-test fixtures (the land-flow, generated-view, and dependency-clear fixtures

joined 2026-08-02), run on every real invocation — a rotted ruler exits 3, never 0. Each

fixture asserts the named tooth fired and that no other tooth fired alongside it; a fixture that

fails for two reasons proves nothing about the tooth it was built to arm. --check also refuses to

mutate a queue that is already red, and re-validates after every mutation, so no command can leave

the queue in a state the gate rejects.

---

8. What this does not yet cover — stated, not hidden

and the first several refills will be the loop's own coverage work.

bibles, and emitted consumption manifests). Gate 42 checks that an item *names* its canon; gate 39

checks that the produced artifact *realizes* it. Every other kind still relies on

docs/SUBAGENT_CONTRACT.md §1 clause 5, which is author-side and uncheckable.

concurrent sessions on this tree would both be handed the same item. That matches the standing

one-lane-per-project session model and is a real constraint, not an oversight.

Generated by harness/site/structure_site.py — the URL path is the repo path. review root