CONCEPT_ART_PRODUCTION_PROGRAM.md

assets/CONCEPT_ART_PRODUCTION_PROGRAM.md

THE CONCEPT ART PRODUCTION PROGRAM

CANON SUBORDINATION — this document is PROPOSAL-TIER: it serves canon and never outranks it.
The canon: the CVD · the T1 foundation docs · the T0 registries (registries/) · the spine
(docs/spine/CH_*.md) · the region pages (_source/02_Tier_2_Region_Pages/). Authority order:
docs/DOC_MAP.md § 0.
Canon served: docs/spine/CH_*.md § ## Asset anchors + § ## Mechanical anchors (79 nodes) ·
registries/T0_Chapter_Index [ACTIVE v1.2] · registries/T0_Region_Index [ACTIVE v1.2] ·
registries/T0_Creature_Roster · registries/T0_Character_Index [ACTIVE v1.1] ·
registries/T0_Boss_Encounter_Registry · registries/T0_Weapon_Registry ·
registries/T0_Ability_Tree_Registry [ACTIVE v0.1] · registries/T0_Familiar_Registry ·
_source/02_Tier_2_Region_Pages/*.md § Zone Catalog.
READ THAT CANON FIRST — open it and derive from it before you build anything from this document.
If this document disagrees with canon, CANON WINS and this document is the defect — fix the
document, never the canon. Nothing here is applied until it is ratified into canon.
Status: PROPOSAL-TIER. v1.0, 2026-08-02.
It answers Josh verbatim: *"build the concept art (hundreds for the region and thousands for all
the assets architecture fauna npcs etc in the region, and thousands more for everything the player
will walk through and experience, and thousands more for abilities, and thousands more for weapons,
and thousands more for characters and companions and npcs, and thousands more for equipment), not
all is needed at once and you develop those pages and I approve them or make comments on how to
adjust or modify."*

0. WHAT THIS IS, AND WHAT IT DOES NOT RE-RULE

docs/proposals/CONCEPT_ART_PROGRAM.md (1,840 lines) answers WHAT art exists and how much — the

43-class density model, the three-panel board shape, the licence routing. It is the TAXONOMY and it

stands; nothing here re-derives a single one of its counts.

This document is the other half: **HOW ~17,900 plates are produced, stored, reviewed, and kept

honest, by one person with one GPU.** Six questions, six sections, every number traced.

It does not re-rule the board shape (CONCEPT_ART_PROGRAM.md §2.1), the board medium

(docs/art_boards/README.md "The board medium is RULED"), the six-value rights vocabulary

(docs/art_boards/README.md "Conventions the gate enforces"), the licence tiering

(docs/pipeline_review/tech_research/PIPE_ART_2026-07-29.md §1), or the five-state generation

lifecycle (docs/generation_lifecycle.json enum). It **uses all five and invents no parallel of

any of them.** Where it proposes something new the text says PROPOSED.

The failure this program is shaped around. On 2026-08-01 a session built BRD_0001 from the two

program docs and never opened docs/spine/CH_02.md, whose ## Asset anchors block at line 226 had

already typed and id'd the board's entire contents. It passed the structural gate and four

adversarial critics. harness/check_canon_derivation.py (GATE 39) closed the derivation hole after

the fact. **Every mechanism below is designed so that failure is not merely caught but

uneconomic** — the cheapest path through this program is the one that starts at the canon node.

---

1. THE COUNTED VOLUME

Counted from the live registries on 2026-08-02, not from any document's summary.

SurfaceLive countSource
Narrative nodes79 (+ 80 spine files incl. CH_PROLOGUE / CH_EPILOGUE)registries/T0_Chapter_Index [ACTIVE v1.2]
Region rows76registries/T0_Region_Index [ACTIVE v1.2]
Region PAGES authored12 (+ README)_source/02_Tier_2_Region_Pages/*.md
Zone-catalog rows84 across those 12 pages_source/02_Tier_2_Region_Pages/*.md § Zone Catalog
Bosses281registries/T0_Boss_Encounter_Registry
Abilities185registries/T0_Ability_Tree_Registry [ACTIVE v0.1]
Characters164registries/T0_Character_Index [ACTIVE v1.1]
Creatures150registries/T0_Creature_Roster
Antagonist operatives64registries/T0_Antagonist_Network_Registry [ACTIVE v0.1]
Weapons72registries/T0_Weapon_Registry
Vril sites42 · Interior spaces 44 · Familiars 22 · Flora 25 · Gather nodes 24 · Minerals 19 · Runes 19 · Festivals 16 · Schools 13 · Trades 10the named registries
Equipment1 rowregistries/T0_Equipment_Registry — the emptiest art-bearing registry in the repo

**Multiplied to plates by docs/concept_art/density_model.csv (43 classes, emitted by

harness/concept_art/emit_density_model.py, never typed):**

FLOORTARGET
Slice (Prologue→Ch 13; 14 nodes / 15 legs)2,5253,471
Full game (79 nodes / 76 region rows)12,79917,876

Two of those counts are ceilings, not rows, and the program must not pretend otherwise. The

density model sizes creatures at 420 and named entities at 1,000+ against live registries holding

150 and 164 — that is ~2,900 plates budgeted against rows that do not exist yet

(CONCEPT_ART_PROGRAM.md §6.2 item 1 states this openly). The production consequence is stated in

§5: you cannot generate art for a row that has not been authored, so registry population is a

hard predecessor, not a parallel track.

1.1 The number that actually sizes the work: REVIEW UNITS

Plates are not the review unit — a SHEET_4 boss is four plates and one decision. Dividing each

class's plate count by its art_unit arity gives the number of things a human must actually look at:

platesreview unitsof which parametric ("cheap")of which authored ("expensive")
Slice target3,4712,368664 units / 855 plates1,704 units / 2,616 plates
Full game target17,87612,8402,851 units / 3,745 plates9,989 units / 14,131 plates

cheap_or_expensive is a live column of density_model.csv; the split is the model's own, not a

judgement made here. 20.9 % of all plates are parametric — they are produced by applying a rule

that one board sets (the rarity ladder, the per-element VFX law, the weapon grade ladder). Those do

not get looked at one by one, and §6 says exactly what they get instead.

---

2. THE UNIT OF REVIEW — THE LEAF, capped at 12 rows

Not one giant board. **A LEAF: one canon anchor line-set, at most 12 entity rows, one page, one

scroll.** A leaf is the atomic reviewable, and its cap of 12 is measured, not chosen.

2.1 Why 12 — the measured half

Across all 80 spine files, every bullet under ## Asset anchors and ## Mechanical anchors that

carries a canon id was counted:

ids per anchor LINEids per anchor SECTION
lines / sections measured547135
min · median · mean1 · 1 · 2.4— · 9 · 9.2
p75 · p90 · p953 · 6 · 8
max1235
share ≤ 12100 %

No anchor line anywhere in the spine names more than 12 ids. So a cap of 12 can always split a

node's anchor section on a LINE boundary and never cut a line in half — the leaf boundary and the

canon boundary are the same boundary, at every node, by measurement. A cap of 10 would split

CH_02.md's creatures_and_fauna line (7 ids) away from nothing, but a cap of 8 would eventually

cut one; a cap of 16 buys nothing the canon grain uses. **12 is the smallest cap that never cuts a

canon line.**

And because the median anchor SECTION carries 9 ids, **most nodes' entire ## Asset anchors block is

one leaf.** The reviewable unit and the canon unit coincide without either being bent.

2.2 Why 12 — the review-time half (MODELED, not measured — the parameters are named)

Assumptions, stated so they can be argued with or replaced by a measurement after the first ten

leaves: an approve is ~12 s (look, recognise, tap), a comment is ~45 s (look, decide what is

wrong, type it), and ~20 % of rows draw a comment. Mean 18.6 s/row.

captime per leafleaves, slice (expensive)leaves, full game (expensive)
103.1 min171999
123.7 min142833
165.0 min107625

Josh's phrase is *"scannable on a phone in a few minutes."* 12 lands at 3.7 minutes. The row count is

the lever, not the plate count: a leaf of 12 boss rows is 48 plates and still 12 decisions.

The total human cost, which is the number that decides whether this program is real:

142 leaves for the entire Prologue→Ch 13 slice ≈ 8.8 hours of Josh, and ~833 leaves ≈ 52 hours

for the full game — plus ~20 law leaves (§2.3) that cover the other 2,851 parametric units.

Spread across the build that is roughly one leaf a day. The GPU is not the bottleneck; this is

which is why §5 orders the work by what unblocks the build rather than by what is cheap to render.

2.3 The three leaf kinds

kindbound tohow the residue is computedcount
ANCHOR LEAFone spine node's anchor section, or a ≤12 slice of itexactly GATE 39 D2 over that section — the ids canon names in scope~150 for all 79 nodes (135 sections, median 9 ids)
CLASS LEAFone density class × one region leg, for classes homed in a registry rather than an anchor line (ambient crowd buckets, foliage sets, minerals)D2 over the registry rows filtered to the nodethe balance of the 833
LAW LEAFa rule, not a place — the existing LANGUAGE_BOARD unit (CONCEPT_ART_PROGRAM.md §1.1)n/a — one pick sets a law applied hundreds of times~20, and they come FIRST (§5)

The ANCHOR LEAF is the load-bearing one and it is the direct structural answer to the 2026-08-01

failure: **a leaf cannot be authored without opening the anchor section it is bound to, because the

anchor section is what enumerates its rows.** There is no leaf-shaped thing you can build out of

CONCEPT_ART_PROGRAM.md.

2.4 What a leaf page shows, per row

One row = one entity = one decision. Recommended plate at full page-width; the two alternates as a

collapsed strip; the entity's canon id and its [SRC:] anchor visible as text; one approve control

and one comment field. Below the rows, unchanged from GATE 39 D5: **the residue block — every id

this leaf declares pending, with its reason and its owning registry — rendered on the page's own

face**, so a leaf that realized nothing is visibly a leaf that realized nothing.

---

3. THE ID AND STORAGE SCHEME

3.1 Ids — extend, never mint a second key

plate_id (PLT_0001_A1_1) and board_id (BRD_0001) are landed keys in

docs/art_boards/PLATES.csv / MANIFEST.csv. They stay. Nothing here mints a parallel id space.

What is added is a path, because the landed asset_path column has no ruled form and 17,876

free-form paths is not a scheme.

3.2 The path form and the canon-id law

AMENDED IN PLACE 2026-08-03 (thirty-first sitting — the pipeline-hardwiring mandate, applied

through the four-lane compatibility pass). The path form below is unchanged and is exactly what

harness/check_art_pointers.py enforces. What this amendment adds is the LAW that fills the second

segment, the token grammar that carries the derivation onto the row instead of losing it at the CSV

boundary, the column set the row needs, the stage discipline, and the render-tier axis. Every clause

here was forced by a measured defect: the 2026-08-02 lane generated 76 plates whose second segment

held a prompt-pack kebab slug, so GATE 39 moved by nothing and no dispatcher could find a plate for

any canon row. Nothing below re-rules the path FORM — the form was right; the VALUES were not.

$HUMANITY_ART_ROOT / <class_id> / <canon_id> / <plate_role>__<hash12>.<ext>
                     D1-15        CR_0001      silhouette__9f2a1c4b7e03.png

budgets in, so "how much of D1-15 exists" is ls | wc -l and never a query.

plate that cannot name a canon id has nowhere to be written.

harness/generation_prompt_hash.py already computes over a CLOSED payload **including the model's

licence class**.

That last choice is the whole design. Because the hash covers the prompt, the seed, the sampler

config, the tier and the licence class, *any* regeneration — a comment-driven re-prompt, a seed

change, a model demoted from SHIPPABLE to PREVIZ_ONLY — produces a different filename. So:

becomes a property of the naming scheme;

filesystem, not just the row.

3.2a THE CANON-ID LAW — the class selects the registry, the canon id is a live key in it

AMENDED IN PLACE 2026-08-03 (director ruling R-G, the art final build). The sentence below used

to read "Nothing else may occupy segment 2 — not a prompt slug, not a beat id, not a site name, not a

title." That wording contradicted this section's own armed tooth: harness/check_art_pointers.py

P9 has always resolved zone: (against build/zones/<page_slug>.csv), beat: and scene: (against

docs/spine/CH_NN.md) as PRIMARY tokens, and site: through the class's key registry — so a law that

banned a beat id from segment 2 was banning something the gate lawfully admits, and the first row that

needed one (the landed fire-night row, D1-03/CH02_B02) had to be declared a deviation instead of a

key. The FORM is unchanged. What is corrected is which token families may be the PRIMARY.

SUPERSEDED: the old prohibition on beat ids in segment 2. STANDING: the prohibition on a

prompt slug and on a title, which is the defect the section was written for.

<class_id> SELECTS a registry. <canon_id> is a LIVE PRIMARY KEY — a key that a named repo source

resolves, which is exactly the set check_art_pointers.py resolve_token() implements:

default and the majority case;

because a zone_id is unique only within its page. Segment 2 is the bare zone_id;

§10 beat lines and §13 scene-anchor table. A staging or key-frame plate whose subject IS a beat

keys on the beat; a beat with no §13 scene anchor may never have one minted for it to key on,

because the spine is what declares scene anchors and an invented SC_ id fails P9 by construction.

A prompt slug and a title remain banned, and so does any token no source resolves. The refusal at

§3.2b is untouched: a row with no live primary key is NOT GENERATED.

key_column on all 43 class rows, so the class-to-registry rule is derivable rather than

remembered. key_registry is the docs/generation_lifecycle.json enrolment key

(T0_Creature_Roster/Sheet1) and key_column is that registry's own primary-key column

(creature_id) — the same pair generation_lifecycle.json already declares under id_columns

for the twelve enrolled registries. It is a re-point of live data, not a new id space.

D1-16 folklore beings moved off T0_Chapter_Index.folklore_entities_in_chapter — a

semicolon-joined token list on a NODE registry — onto T0_Creature_Roster/Sheet1 creature_id,

because CR_0151 (the Polo) and CR_0010 (Ebu Gogo) are the live rows those tokens name. D1-03 and

D1-04 layouts moved off "spine 3b site rows" onto the ZONE key: build/zones/<page_slug>.csv

zone_id, which is the spine §3b site_id carried verbatim by harness/regen_zone_catalog.py.

(§2.3) — a class whose subject is a rule, not a place, keys its plates on the art-bible artifact

id and never on a fabricated canon row; the two law leaves are the LANGUAGE_BOARD classes, D1-28

Hollow surfaces and D1-34 UI iconography and rarity language, and art_unit == LANGUAGE_BOARD is

the machine discriminator so the set cannot drift by opinion. build/zones/<page_slug>.csv is the

ZONE key, page-qualified because a zone_id is unique only within its region page. (none) means

no live primary key exists ANYWHERE, so the class cannot dispatch at all — D1-12 non-boss enemy

classes is the only one, and its home is DESIGN_GAP_REGISTER gap 1.

order in both columns (D1-20 minerals and gather nodes; D1-37 scripts and inscription spine). The

plate row's own canon_registry then says which of the two that plate keyed on, so the ambiguity

is resolved per row rather than left in the class.

path while the untouched token stays on the row in canon_ref. A filesystem must never be the

reason a canon key changes shape, and a canon key must never be the reason a path breaks.

amendment landed — 0 unresolved, with a positive control run on a deliberately wrong registry and

a deliberately wrong column so the zero is a real zero and not a dead search.

3.2b THE MULTI-ID RULING — three dispositions, and the refusal

"One row = one entity = one decision" (§2.4) is not advice; it is what makes the path form

expressible. A row that names more than one canon id takes exactly one of three dispositions.

naming CR_0003, CR_0004, CR_0005 and CR_0006 is four rows at D1-15/CR_0003, /CR_0004,

/CR_0005, /CR_0006 — which is what D1-15's own formula (species x 3) already asks for.

THE VIEW, and every other id rides canon_id_refs. An antagonist operative who is also a named

character and a boss is D1-11/NW_0001 with canon_id_refs CHAR_0008|BE_0002 — and he

separately OWES a D1-09/CHAR_0008 portrait and a D1-13/BE_0002 boss sheet. Three classes, one

man, three plate sets; never one plate wearing three hats.

style register, the Hollow surface language, the rarity language) keys on the art-bible artifact

id under (law). It names no canon row because it realizes none.

GENERATED. It goes to its board's coverage block as pending with its reason and its owning

registry, and the composer refuses the work order. This is the tooth that would have stopped the

four unmintable rows of 2026-08-02 (a realm object routed to a registry holding one row, a carved

mark with no home anywhere, a beat id, and a hard-line id that is a rule and not a thing).

3.2c THE canon_ref TOKEN GRAMMAR — the derivation, carried on the row

canon_ref is a pipe-joined list of prefix-typed tokens. The FIRST token is the PRIMARY, and the

path's second segment is its path-safe form. The polymorphic-reference precedent the repo already

carries is T0_Dialogue_Line.speaker_ref and T0_RNG_Drop_Table.linked_entity_id; nothing new is

invented here except the prefix set.

row is the only key that resolves both the level and the sequence slot.

one phase, because two phases can be two different zones with two different tells.

on a row whose generation_status is complete. A pending token obliges a matching pending row

in the owning board's coverage block, in the same commit.

AMENDED IN PLACE 2026-08-03 (director ruling R-B). pending is deliberately NOT a member of

harness/read_boards.py CANON_REF_PREFIXES, and the two do not contradict once the SCOPE is

stated, which it was not: CANON_REF_PREFIXES is the LANE-G/P prefix set. T-CITE-REQUIRED

inspects canon_ref only on lane G and lane P (read_boards.py, the if lane in ("G","P")

guard), which is exactly §3.2b's REFUSAL stated as arithmetic — a dispatchable row may never key a

pending: token, and a lane-R / generation_barred row may, because it dispatches nothing. The

guard §3.2c owed was not generation_status but LANE, and it was already armed. Nothing in the

code changes; the sentence now says what the tooth does. The live occasion: six Ch-2 rows were

authored at lane G/P on pending: keys, and R-B re-keyed all six to live primaries rather than

widening the prefix set to admit them.

3.2d THE PLATE ROW COLUMN SET — appended, never reordered

docs/art_boards/PLATES.csv keeps its existing seventeen columns in their existing order (renaming

or reordering them re-baselines every consumer and every fixture) and gains thirteen, appended:

columnwhat it is
density_class_idFK to density_model.csv class_id; must equal asset_path segment 1
canon_idthe PATH-SAFE primary key; must equal asset_path segment 2
canon_registrythe key_registry the id belongs to — what makes the id unambiguous across id spaces
canon_refthe §3.2c token list; first token is the primary
canon_id_refspipe-joined secondaries, the PRIMARY+REFS disposition
subject_slugthe human-readable name. Keep it — it is good copy for the site. It is simply not an address
care_tiercopied from the subject's own row, never hand-typed
protectedcopied from the subject's own row; with care_tier it is what makes the Lane-G refusal arithmetic instead of habit
stagedraft · review · hero (§3.2e)
render_tierITERATION · REVIEW · HERO_FAST · HERO (§3.2f)
directional_labelMANDATORY and non-empty on any previz_only row (docs/art_boards/README.md, the thirty-first-sitting previz amendment)
regeneration_triggerthe lifecycle column DR-2 §6.3 fires through; without it a hash change has nothing to fire on this table
last_generated_timestampthe lifecycle column its siblings already have on every enrolled registry

3.2e THE STAGE DISCIPLINE — four candidates are one plate's DRAFT, not four plates

The four-seeds-per-subject sweep is the DRAFT stage of ONE subject. Recording those four as

generation_status=complete makes every downstream count wrong by about 4x and inflates D1 progress

against floors that count FINISHED plates.

STAGE" — which is the only reading under which a candidate sweep and a delivered plate are not

byte-identical from the outside.

floors, so "how far is D1-01 against 180" becomes a query. That ledger does not exist yet; it is

named as NOT BUILT here and its rider (FR-084) carries the honest-NONE tooth until it does.

them is not optimistic, it is wrong.

3.2f render_tier IS ITS OWN AXIS — RATIFIED

RATIFIED 2026-08-03 (thirty-first sitting, the pipeline-hardwiring mandate). render_tier joins the

CLOSED generation_prompt_hash payload key set as its own key, with the closed vocabulary

ITERATION · REVIEW · HERO_FAST · HERO — the render presets §4 measures. Adding a key to a

closed set is a RATIFICATION and never an edit, which is why it is recorded here with its sitting.

background · region_standard · region_hero · realm · apex. It is not a render preset and

it never was.

name inside the budget-class enum. That is the exact five-senses-of-tier collision

docs/ASSET_DROPIN_CONTRACT.md §1 bans a bare tier column to prevent, and it happened one level

down where no tooth was looking. Two axes, two keys, two closed vocabularies is the fix.

from its REVIEW predecessor by construction — the same self-enforcement the licence class already

gets, applied to resolution.

3.2g The factory riders this section arms

docs/FACTORY_CONTRACT.md carries the per-chapter obligations that ride this section, so the art

program is measured by the same machinery that measures everything else: FR-083 (key every plate

on a live canon id), FR-084 (deliver against the per-class floors, counted not asserted),

FR-085 (the Lane-G care refusal, as arithmetic), FR-086 (a resolvable licence record per

generated plate), FR-087 (previz honesty and the marketing-clear publish gate). All five are

SEED: they surface in every chapter checklist and gate nothing yet, which is the honest state.

3.3 What git carries, and the arithmetic that forbids the alternative

Git carries rows and hashes only — the landed rule (docs/art_boards/README.md: "Plate BINARIES

never enter this repo"). The arithmetic behind it, now that the corpus is sized:

Plate corpus, full game at target286.8 GiB (docs/concept_art/art_throughput_plan.json)
Entire packed repo history43.71 MiB (git count-objects -vH)
Ratio~6,700 ×
E: free (the ruled art root's drive)7.3 TB → the corpus uses 3.8 %

Storage does not constrain this program. Git would have — by four orders of magnitude. The

pointer discipline is not fastidiousness; it is the only thing that keeps the repo clonable.

3.4 How the gate verifies a pointer resolves — LANDED: harness/check_art_pointers.py

Specifying this was the brief; a landed gate beats a spec, so it is landed and mutation-proven.

The hole it closes. read_boards.py T-PLATE-FILE checks that a declared asset_path exists and

is non-empty (read_boards.py:33). sha256 is a live PLATES.csv column carried into the critics'

worklist and no tooth in the 42-gate roster reads it. Today, before this commit: a plate file

swapped on disk passes; a complete row with an empty sha256 passes; a regeneration that

overwrites its predecessor passes; and the entire table passes on any machine where

HUMANITY_ART_ROOT is not mounted, because a path that is never resolved never fails.

toothwhat it makes unrepresentable
P0-VACUITYa missing/unreadable/empty PLATES.csv, or one missing a required column, reporting the same green as a table that was checked
P1-COMPLETE-CLAIMgeneration_status=complete without a path and a 64-hex sha and a prompt hash
P2-PATH-FORMan absolute path, a drive letter, a .., a backslash, or the wrong depth — a row that resolves on exactly one machine
P3-CONTENT-ADDRESSEDa filename that is not __<hash12>.<ext>, or whose hash12 disagrees with generation_prompt_hash — i.e. a regeneration that *could* overwrite its predecessor
P4-PATH-COLLISIONtwo rows claiming one file with different sha256
P5-DUPLICATE-CONTENT (WARN)byte-identical images under two plate ids — at volume, the generator returning one image for four seeds
P6-INTEGRITYbytes on disk that are not the bytes the row approves. Byte-exact, no tolerance
P7-BLIND-MODEa gate that cannot see the drive reading exactly like a gate that looked. With the root absent it runs MANIFEST_ONLY, says so on the scorecard, and FAILs any row claiming complete

P7 is the tooth this repo's own history demands: GATE 36 and GATE 39 both exist because a check

that cannot match reads as a check that passed. An unmounted volume is that same defect wearing a

drive letter.

Proof, not assertion. 8 teeth · 20 must-fire fixtures + 2 must-not-fire baselines, run on every

real invocation (exit 3 if a ruler rotted). All 8 were additionally mutation-proven **live against

docs/art_boards/PLATES.csv**, each mutation firing its named tooth and no other, with the file

restored byte-exact (sha ed9dd9ec… before and after). One mutation — complete with an empty sha,

on a machine with no art root — legitimately trips two teeth (P1 and P7); re-run with a root

present it fires P1 alone, which is the isolation proof.

It runs green today: MODE=MANIFEST_ONLY declared=0 complete=0 verified=0 PASS. Every plate in the

repo is still pending, so there is nothing yet to verify — and the gate says which of those two

things it means, which is the point.

---

4. THROUGHPUT — MEASURED ON THIS BOX, 2026-08-02

Not a published benchmark. A real timing sample against the live ART ComfyUI instance

(127.0.0.1:8189, the only instance that exists on this box —

docs/5090_SETUP_RUNBOOK.md:1276), day window, GPU otherwise idle, 16 generations.

tierresolutionstepss/plateplates/hrMB/platepeak VRAM
ITERATION1024²82.781,2951.5320,653 MiB
ITERATION (batch 4)1024²82.911,2391.6420,623 MiB
REVIEW1344²85.356732.7520,623 MiB
HERO-fast2048²815.042396.6420,623 MiB
HERO2048²2034.381056.6720,344 MiB

Generator: Z-Image-Turbo bf16 (Tongyi-MAI/Z-Image-Turbo, Apache-2.0, SHIPPABLE per

PIPE_ART_2026-07-29.md §1) + qwen_3_4b text encoder, res_multistep/simple, cfg 1.0, shift 3.0.

Record: docs/concept_art/art_throughput_measurements.json. Re-measure with

python harness/art_throughput.py --measure.

Finding worth more than the headline: BATCHING IS NOT A LEVER. Batch 4 is *slower* per plate

(2.91 s) than batch 1 (2.78 s). The queue is compute-bound, not overhead-bound, so no scheduling

cleverness buys throughput — only resolution and step count do. Recorded because the obvious

optimisation is the wrong one and someone will otherwise spend a day on it.

4.1 The four-stage pipeline and the wall clock

A one-stage model is what makes art schedules lie. The real pipeline: DRAFT k=4 candidate seeds

per plate at ITERATION tier (you do not ship the first sample) → REVIEW the one selected

candidate at REVIEW tier for the leaf page → HERO only APPROVED plates at HERO tier → REGEN

10 % of plates repeat DRAFT+REVIEW after a comment. k and the 10 % are PARAMETERS, printed with the

plan so nobody mistakes them for measurements.

scopeplatesDRAFT hREVIEW hHERO hREGEN hTOTAL GPU-hnightsGiB
P0 law set (slice)4651.40.74.40.26.80.87.5
Slice FLOOR2,5257.83.824.11.236.84.640.5
Slice TARGET3,47110.75.233.11.650.66.355.7
Full game FLOOR12,79939.519.0122.25.9186.623.3205.3
Full game TARGET17,87655.226.6170.78.2260.732.6286.8

Emitted by python harness/art_throughput.pydocs/concept_art/art_throughput_plan.json. Not one

of these numbers is typed into this document by hand.

**The headline: the entire Prologue→Ch 13 art slice is ~6 nights of unattended GPU. The entire game

is ~33 nights.** Against ~8.8 h and ~52 h of Josh respectively. The GPU is not the constraint and

never was — the review surface is, which is what §2 is for.

4.2 Night window versus day window — the VRAM law, now measured

docs/5090_SETUP_RUNBOOK.md:1572 (D-11b) rules: *"keep 04:00 as the serialized nightly reservation;

never overlap asset-gen on VRAM."* That ruling is now also a measurement: the ART lane peaks at

20,653 MiB of 32,607, leaving 11,954 MiB — not enough to hold a UE 5.8 editor session with a

loaded landscape beside it with any margin.

the 04:00 soak and does not resume until the soak reports.

demand while Josh is actually commenting. A comment answered in the same sitting is worth more

than a night of throughput, and it fits in the headroom.

always wait for the night.

4.3 The honest caveat: this is not the ruled primary

PIPE_ART_2026-07-29.md §1 rules HiDream-O1-Image-Dev (MIT, 28 steps) the T2I PRIMARY and

Qwen-Image-2512 the co-primary. Neither is installed

models/diffusion_models/ holds z_image_turbo_bf16 only. Every figure above is therefore a

ceiling on speed and a floor on cost.

Isolating the fixed term from the two 2048² points — (34.38 − 15.04)/12 = 1.612 s/step, fixed

= 2.147 s — and scaling the sampling term to 28 steps: **the full-game target becomes ~396 GPU-hours

(≈49.5 nights)** if HiDream replaces Z-Image at equal resolution. The schedule is stated at both ends

so nothing rests on the fast model staying the pick. (The ITERATION multiplier ×1.57 uses the

2048²-derived fixed term, which understates it at 1024²; treat it as a lower bound.)

---

5. THE APPROVAL LOOP

Josh's ask: *"you develop those pages and I approve them or make comments on how to adjust or

modify."* Four mechanisms, all four already exist — this section wires them, it mints nothing.

5.1 The page's response surface

The leaf is published as a default-private artifact page (the ruled medium —

docs/art_boards/README.md "The board medium is RULED"), re-published to the SAME url recorded in

MANIFEST.csv.published_url. Under the rows it renders a PICK BLOCK: pre-formed lines, already

in the ruled intake grammar, behind one copy control.

- [ ] BRD_0142 — A2 · B1 · C-SQ-3
- [ ] PLT_0142_CR_0001_sil — regen: too many dorsal spines, the real Komodo reads smoother
- [ ] PLT_0142_CR_0010_beh — hold

That grammar is not invented here. It is docs/JOSH_REVIEW_INBOX.md's ruled line format

- [ ] <TARGET> — <comment>, and python harness/queue.py intake already turns each line into a

work-queue item with its gates, its critic and its derivation attached, ticking the line to

- [x] WQ_00NN in place so intake is idempotent. **Josh copies one block on his phone and pastes it;

nothing is transcribed by hand and nothing is dropped** — an unrecognised target becomes a BLOCKED

item with reason unresolved_target, never a silent drop.

Dependency, stated plainly: docs/JOSH_REVIEW_INBOX.md, harness/queue.py and
docs/AUTONOMOUS_RUN_LOOP.md are the run-loop lane's artifacts, landed at 04aa2a7e
("GATE 42 queue"). Its BRD_ target row already routes a board id to a concept-art-region
regeneration item with gates boards + canon_derivation and the four image critics attached —
which is exactly the receiver §5.2 needs, built independently and arrived at from the other side.
What that row does not yet resolve is a PLATE id. A leaf comments per row, not per board, so
PLT_… must join queue.py's target resolver or every plate-level comment lands as a BLOCKED
unresolved_target item. That is one resolver case, it is named here rather than assumed, and
until it lands the honest fallback is a board-level comment naming the plate in its text.

5.2 How a comment becomes a regeneration — using the landed states, not new ones

1. intake creates the work item. The comment text is preserved verbatim.

2. The comment joins the prompt payload. harness/generation_prompt_hash.py recomputes

generation_prompt_hash over the closed key set. The hash necessarily changes.

3. A changed hash is the landed regeneration trigger (docs/generation_lifecycle.json

trigger_column: regeneration_trigger): the row moves to regeneration_required — one of the

five ratified states, not a sixth — and regeneration_count increments.

4. Because the hash changed, <hash12> changed, so the new plate writes to a new path. The

predecessor survives as the evidence a rejected output is required to be. GATE 43 P3 is what

makes that true rather than intended.

5. first_pass_rate = boards_with_zero_regenerations / boards_reviewed stays a query over

MANIFEST.csv — which is why regeneration_count is a column and not a memory

(docs/art_boards/README.md "The review cadence is RULED"). It drives the ruled cadence: one leaf

at a time until ~90 % first-pass over a trailing ten, then all remaining at once.

Nothing in that chain required a new state, a new column, or a new hash function. The licence

self-enforcement rides along free: a model demoted to previz_only changes the same hash and fires

the same regeneration, with no one re-reading a licence.

5.3 What an approval becomes

An approved plate is not canon and does not become canon. It becomes a **locked

canon-adjacent reference** by being written into the art bible — docs/art_bibles/<subject>.md, which

REALM_DESIGN_PROGRAM.md §3.6 makes the only artifact the asset factory reads, precisely so

boards never become a second source of truth. The bible field carries plate_id + sha256 +

generation_prompt_hash.

Three properties follow, all mechanical:

bible; the bible names the plate; GATE 43 P6 proves the plate's bytes are the approved bytes. The

chain from a 3D asset back to the image Josh approved is hash-verified end to end.

means locked to a sha, not to a filename.

AWAITING PICK; only the director writes RATIFIED, "a subagent writing a user ruling into canon

reads as fabricated authority." This program does not touch that line.

---

6. PRIORITY ORDER FOR THE PROLOGUE→CH 13 SLICE

Ranked by what unblocks the most downstream work per plate, not by cheapness. The tier column of

density_model.csv supplies P0/P1/P2; the ordering *within* and the two hard predecessors are this

document's, and are named as such.

RANK 0 — THE HARD PREDECESSOR THAT IS NOT ART

Populate the rows the art is for. T0_Equipment_Registry holds 1 row;

T0_Environment_Grammar_Registry 2; T0_Quest_Seed_Registry and T0_RNG_Drop_Table 0; and

the density model budgets creatures against 420 where 150 exist. You cannot generate a plate

for a row that has not been authored, and a leaf cannot enumerate rows that are not there. This is

registry work (docs/task_router.json), it is zero GPU, and it gates everything below it.

Naming it Rank 0 is this document's call, and it is the single most important line in this section.

RANK 1 — THE LAWS (P0, ~465 plates, 6.8 GPU-hours — under one night)

One pick sets a rule that applies hundreds of times. Every one of these built *after* dependent art

means regenerating that art.

#classslice plateswhy it is first
1D1-34 UI iconography and rarity language67LANGUAGE_BOARD. The rarity ladder colours, shapes and beam VFX are inherited by every weapon grade, every drop, every card. Building 432 grade variants before this is 432 regenerations
2D1-28 Rifts and Hollow surfaces15LANGUAGE_BOARD. The Hollow's surface grammar recurs across the whole arc
3D1-27 Magic and ability VFX — mastery-tier variants69the per-element visual law. 185 abilities × tiers inherit it; full-game 1,116 plates
4D1-23 Weapons — grade variants165the grade ladder, parametric off D1-34's pick
5D1-26 Equipment and carried objects15includes the Ch-2 realm object, whose art brief is already written in CH_02.md's anchor line and which is carried unbroken to the Ch-13 gift — it is on screen for the entire slice
6D1-05 Architecture kits per culture134every settlement, interior, dwelling variant and civic monument derives from the kit. Kit before dwelling, always

RANK 2 — THE REGION LOOK, in build order

D1-01 region key art (180 slice plates) per leg, in the ruled build order Ch 2 → Ch 13. Key art

sets palette and light; §7's palette-conformance tooth measures every later plate in that region

against it, so the key art must be picked before the region's volume runs.

RANK 3 — THE PER-LEG CONTENT, riding each chapter leg

Anchor leaves in node order, each derived from that node's ## Asset anchors: creatures and fauna →

folklore beings → NPC personas → bosses → flora/minerals/gather → festivals → interiors. Ch 2 first,

because CH_02.md's anchor block is already complete, already id'd, and is the node the 2026-08-01

failure was supposed to have realized.

RANK 4 — SLICE-END

D1-32 realms and the fairy-realm material for the Prologue and Ch 1. This is not a scheduling

preference; it is REALM_DESIGN_PROGRAM.md's ruling that the fairy realm and Prologue/Ch 1 are built

LAST, on the matured concept-art pipeline — restated in CLAUDE.md § Model seats.

Deferred out of the slice

D1-17 legendary species, D1-24 regional weapon skins (no formula possible — the multiplier is

unruled), D1-25 legendary bespoke weapons, D1-36 cinematic key frames, D1-38 schools, D1-39 runes,

D1-41 dungeon interiors. density_model.csv sizes each at 0 for the slice; that is the model's

own arithmetic, not an omission here.

---

7. THE ANTI-SLOP BAR AT VOLUME

Four adversarial critics per board is the landed bar (docs/art_boards/README.md "The four critics

run BEFORE Josh sees anything"). It does not survive 17,876 plates — and pretending otherwise is

how a quality bar becomes ceremonial. The bar is therefore split three ways by what each mechanism is

actually good at.

7.1 DETERMINISTIC — 100 % of plates, zero model cost

Already landed and running on every plate: read_boards.py (18 teeth),

check_canon_derivation.py (10 teeth, D2 residue at zero), check_art_pointers.py (8 teeth).

PROPOSED additions — image-space measurements, numpy/PIL only, ~ms per plate, no model. These are

not taste; each is an arithmetic property whose violation is always a defect:

measurecatcheswhy deterministic is right
luminance std-dev floorblank, near-blank, blown, and crashed outputsa plate with no tonal range is broken, never a style
perceptual hash (dhash) collision within a classthe generator returning one image for four seedsat volume this is THE failure mode. It is exact, and P5-DUPLICATE-CONTENT already catches the byte-identical case — this catches the *visually* identical one
resolution / aspect conformancea plate rendered at the wrong tierarithmetic
palette ΔE against the region's locked key-art palettethe generic teal-and-orange drift, per regionthis is the real mechanical anti-slop tooth. The key art is picked FIRST (§6 Rank 2), its palette is locked, and every later plate in that region is measured against it. "Culturally registered, accurate-not-generic" (CONCEPT_ART_PROGRAM.md §2.4) becomes a number

7.2 SAMPLED — the four critics, as a tripwire for SYSTEMATIC defects

Generation defects at volume are systematic, not independent: a bad prompt template, a wrong

LoRA, a mis-set palette. Systematic defects are exactly what a small sample catches.

To catch a defect running at rate *d* with 95 % confidence, sample *n = ln(0.05)/ln(1−d)*:

defect ratesample size
30 %9
20 %14
10 %29
5 %59

The rule: 14 plates sampled per class × leg. Any systematic defect running at 20 % or worse is

caught with 95 % confidence. And the response is not per-plate: **one sampled failure rejects the

whole batch** back to regeneration. The sample is a tripwire, not a verdict on the plates it drew.

That is a ~140× reduction in critic invocations against per-plate review, with a stated and

defensible confidence — instead of an unstated one.

7.3 EXHAUSTIVE — never sampled, no matter the volume

Every care-gated subject takes all four critics, every plate. The scale is known: **47 of 88

slice site rows are care-gated (53 %), and independently 46 of 84 zone rows are protected true**

with 51 of 84 at care_tier elevated (CONCEPT_ART_PROGRAM.md §0.1, whose zone figure this

document re-counted from the region pages and confirms at 84).

The principle: sample the taste questions, exhaust the care questions. A cultural-authenticity

defect on a protected living tradition is not a statistical matter and a 95 %-confidence tripwire is

not an acceptable answer to it. This also honours the CVD §17.1 calibration in BOTH directions —

docs/art_boards/README.md is explicit that care-tier inflation is itself a defect and "a critic that

only ever pushes toward restraint produces reverent empty places."

7.4 HUMAN — what Josh still sees

failed a floor; that is the landed rule and it does not change.

(§1.1) are covered by a law he picked, but a law can be picked correctly and applied wrongly. Each

batch surfaces 3 randomly drawn parametric plates on the leaf that set their law. It costs seconds

and it is the only thing standing between "the ladder was approved" and "the ladder was rendered."

---

8. WHAT THIS COMMIT LANDED, AND WHAT IT DID NOT

Landed and running:

must-not-fire baselines, all 8 mutation-proven live against PLATES.csv with byte-exact restore.

art_throughput_plan.json — every figure in §4 emitted, re-measurable with --measure, 7 self-test

fixture groups including a monotonicity check on the committed record and an integer-parse check on

every density row (a silent 0 would shrink the schedule and look green).

NOT landed, and named so it is not mistaken for done:

commit; nothing in §7.1 is running today.

route today; plate-level ones do not.

12-row leaf is a new template.

live under D:\ComfyUI\output\timing_*, and are not canon-adjacent, not registered, and not

routed to the art root.

---

9. PROVENANCE — how every number here is re-derived

§claimre-derive with
1every registry row countcsv.DictReader over registries/<name>/*.csv
184 zone rows / 12 pagescount `\-rows under ## Zone Catalog in _source/02_Tier_2_Region_Pages/*.md`
117,876 / 12,799 / 3,471 / 2,525 platesharness/concept_art/emit_density_model.py; sum the four columns of docs/concept_art/density_model.csv
1.112,840 / 2,368 review units; the cheap/expensive splitplates ÷ art_unit arity, grouped by cheap_or_expensive
2.1547 lines · max 12 ids · 135 sections · median 9scan ## Asset anchors + ## Mechanical anchors bullets across docs/spine/CH_*.md for canon-id tokens
2.2142 / 833 leavesexpensive units ÷ 12
3.3286.8 GiB vs 43.71 MiBart_throughput_plan.json · git count-objects -vH
4every s/plate and MB/platepython harness/art_throughput.py --measure
4.1every GPU-hour and nightpython harness/art_throughput.py
7.2the sample tablen = ceil(ln 0.05 / ln(1−d))

The two numbers in this document that are NOT measured are §2.2's 12 s / 45 s review times and its

20 % comment rate. They are labelled MODELED where they appear, the sensitivity is tabulated at caps

10/12/16, and they should be replaced by a measurement after the first ten leaves — at which

point §2.2's table is re-run and the cap is re-derived rather than defended.

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