systems/ARENA_JOIN_PACKAGE.md
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: _source/02_Tier_2_Region_Pages/flores_island.md § Zone Catalog (and the 14
sibling pages' Zone Catalogs) ·
registries/T0_Environment_Grammar_Registry [DRAFT v0.1]·registries/T0_Boss_Attack_Rail [DRAFT v0.1]·
registries/T0_Region_Index [ACTIVE v1.2] ·
_source/01_Tier_1_Foundation/T1_Combat_System_Spec [ACTIVE v1.0] § "Nature-Derived Counter Classes" ·
docs/spine/CH_02.md
READ THAT CANON FIRST. **If this document disagrees with canon, CANON WINS and this document is
the defect.** Nothing here is applied until a director ratifies it.
THIS LANE MINTS NOTHING. No id was allocated, no registry row was written, no region page was
touched, no gate was run. The id-mint brief in §6 routes to the director (the A16 vetoable precedent).
Pipeline row: A13 arena-mutation framework (docs/PIPELINE_LEDGER.md L102 — next step
*"SCHEMA: the arena-to-env-feature join (the gate's named work order). THEN the Naga Padoha exemplar.
Rides A12"*). Work order source: docs/proposals/boss_rail_exemplar/BOSS_RAIL_EXEMPLAR_BE_0002.md
§8 (schema brief 2) and its S3/S5/S6/S7/S8 findings. Deliverable pair: this package and
harness/emit_zone_registry.py → docs/proposals/combat/zone_registry_proposed.csv
(113 proposed zone rows, 15 pages, 0 parse failures, 0 ids minted) — of which **29 rows derive
from three pages that are NOT at HEAD**; read the provenance disclosure below before acting on any
count in this document.
---
**Three of the fifteen Zone Catalogs this package reads are UNTRACKED working-tree files, not
committed canon:** _source/02_Tier_2_Region_Pages/arabia_persia.md, levant.md and
mesopotamia.md. They are another lane's unlanded work. This package cites all three as canon
authority (§0 below, §2.2, §3.1, and finding F2), and that citation is only as good as their
landing.
Observed state, recorded with its timestamp because it can change under this document. Checked
2026-08-07T23:10Z against HEAD 924e0d09: git status --porcelain reports all three as
?? (untracked), and git cat-file -e HEAD:<path> fails for all three — they are ABSENT at HEAD,
not merely modified. The same state is re-verified mechanically on every emit and written to
docs/proposals/combat/zone_registry_proposed.manifest.json, so a later reader never has to trust
this paragraph's date.
What the three pages contribute, measured.
environment_grammar_refs. All 29 carry diag_eg_refs_state = absent: these are thethree 10-column pages that deliberately do not carry the T0.2 extension pack (§2.2). They
therefore add nothing whatever to the join this package exists to build — every one of the
253 EG tokens comes from the twelve pages that ARE at HEAD.
84 zones across 12 pages, and because mint_ordinal_proposed is a single alphabetical
sequence over all pages, 79 of those 84 HEAD zones take a different ordinal depending on
whether the three pages are present. The ordinal is the proposed allocation ORDER (§6), so
minting against the wrong state does not just add or remove 29 ids — it renumbers almost the
whole table.
The consequence for the mint is a named hard prerequisite; see §6. Nothing else in this package
changes: the schema, the arena_zone_ref recommendation and all five S-findings are derived from
the twelve HEAD pages and the registries, and none of them turns on a row from the three.
DERIVED FROM
harness/check_counter_class_coverage.py L565-569 — the finding in the gate's own words:*"A1 cover / trigger_interrupt DECLARED NOT-ARMED: their answer universes exist in
T0_Environment_Grammar_Registry (tactical_affordance_class COVER; attack_verbs / loaded_potential /
enemy_usable) but are answered PER ARENA, and no arena-to-env-feature join exists in the schema, so
both WARN and neither can pass clean. The join is the named work order."* This package is that work
order; L555-564 (A5d, A1 weak_point judged on the row) and L570-585 (the parry universe declared
UNREACHABLE) were read alongside it so the two briefs stay separable.
_source/01_Tier_1_Foundation/T1_Combat_System_Spec [ACTIVE v1.0].md §"Nature-Derived CounterClasses" — L635 *"An arena-wide attack is answered by neither, and the response vocabulary
expands to meet it: take cover behind rock, pillar, or whatever else the arena affords; strike a
weak point; or trigger a reaction that stops the boss or the hazard before it lands."*; L647
Cover's data home (*"T0\_Environment\_Grammar\_Registry tactical\_affordance\_class, whose declared
enum already carries COVER"*); L649 Trigger interrupt's data home (*"attack\_verbs,
loaded\_potential, and enemy\_usable, which is the same affordance set the factory contract already
requires of every arena"*); L637 the move-fits-the-body-and-the-arena clause; L651 the seven data
tokens. The two arena-wide classes name a registry and a per-arena scope, and the scope has no key.
_source/02_Tier_2_Region_Pages/flores_island.md §"Zone Catalog" — L38 the section header,L40-47 the preamble that declares the column ownership split (*"Rows sourced from spine §3b
site_ids copy terrain / functions / ambient_threat / vril_density / climactic_affordance / protected
VERBATIM from the CH_02 and CH_03 site rows; the page-authored columns are the page-side geometry
… plus the T0.2 extension-column pack"*), L49 the 20-column table header, L50-60 the eleven
data rows. L52 kelimutu_crater_lakes environment_grammar_refs EG_0003;EG_0009;EG_0010;EG_0011
and L53 mount_inerie_cone_country EG_0003;EG_0004;EG_0005;EG_0012 — the two BE_0002 phase
arenas. This is the join, and it is a markdown table.
UNTRACKED, not at HEAD — §0.0):
angkor_khmer.md L59/L70 · †arabia_persia.md L80/L100 · bali.md L39/L47 ·
central_africa_congo.md L41/L51 · ethiopia.md L41/L50 · †levant.md L63/L74 ·
†mesopotamia.md L52/L64 · rift_valley.md L52/L68 · south_africa.md L72/L88 ·
south_india.md L46/L54 · sri_lanka.md L42/L55 · sumatra_java.md L50/L61 ·
swahili_coast.md L41/L51 · west_africa.md L40/L50 (section header line / table header line).
Read in full at Zone Catalog grain: flores_island.md, bali.md, arabia_persia.md,
swahili_coast.md — the three live column variants plus the pilot. The input contract generalizes:
every one of the fifteen opens with the same ten base columns in the same order, and the
variants differ only by which extension columns follow (§2).
registries/T0_Environment_Grammar_Registry [DRAFT v0.1]/Sheet1.csv — 23 columns, 225 rows, header read and rows sampled at EG_0001/EG_0002/EG_0003 and across every tactical_affordance_class. The
join target column is env_feature_id; the answer columns the two arena-wide classes need are
tactical_affordance_class (COVER), attack_verbs, loaded_potential, enemy_usable, and the
care columns protected_care_ref / alteration_permanence_default (hard_blocked).
registries/T0_Boss_Attack_Rail [DRAFT v0.1]/Sheet1.csv — the exact 27-column header, 0 rows.docs/proposals/boss_rail_exemplar/BE_0002_rail.csv — the 19 authored rows, recomputed here rather than carried from prose: phase 1 = 11 rows, phase 2 = 8; counter classes any 8 · dodge_only 8 ·
cover 1 (AT_0011, phase 1) · trigger_interrupt 1 (AT_0005, phase 1) · weak_point 1 (AT_0019,
phase 2) · parry_only 0 · neutralize 0. telegraph_shape values: 6 rows inside the proposed
closed key, 13 outside it. tell_shape_class 16 families (14 used once); tell_motion_class
19 families (19 of 19 used exactly once).
docs/spine/CH_02.md — L99 the BE_0002 thin-join row (phases [P1→CH02_B07, P2→CH02_B08]), L141 CH02_B07 (site mount_inerie_cone_country · boss BE_0002.P1), L146 CH02_B08
(site kelimutu_crater_lakes · boss BE_0002.P2), and L85/L87 the two §3b site rows those zone rows
copy. The per-phase arena binding is canon; it is simply not data.
registries/T0_Region_Index [ACTIVE v1.2] — 76 region rows keyed region_id. Every one of the fifteen region-page slugs resolves to a region_id (verified, 15/15), which is what makes
region_id an FK rather than a label.
docs/fk_spec.json § T0_Boss_Attack_Rail — the seven-member counter_class id_format and its _note naming the answer homes (*"cover and trigger_interrupt = T0_Environment_Grammar_Registry
(tactical_affordance_class COVER, and attack_verbs / loaded_potential / enemy_usable)"*), the four
FKs, the composite-uniqueness tooth on (boss_id, attack_id), and the closed struct-mirror
exception on the first nine columns.
docs/pipeline_review/MINT_DESIGN_B_ITEM_ALLY_2026-07-27.md L1122-1150 — the per-column type table (the only specification of telegraph_shape's ten-member closed key at L1128, attack_kind's five
members, tell_shape_class / tell_motion_class as enums, and phase_index as an int); L1152-1156
the consumers.
docs/spine/DECISIONS_PENDING_JOSH.md §10 L994-1001 (the counter class states the RESPONSE neverthe OUTCOME; the scale is ORDINAL and no number is ruled) and L1028-1031 (the ordinal mitigation
column *"mints with that rail's first authored moveset at window-plan rank 16 — a column with no
rows behind it is a promise, not a shape"*) — the S6 trigger, quoted because the recommendation
turns on its own words.
AUTHORSHIP OF THAT PARAGRAPH, verified because §5.3 rests on how much authority it carries.
It is loop/director landing prose, not Josh verbatim. §10 opens at L974 and carries exactly
one Josh-attributed block, L976-981, introduced *"Josh, verbatim:"* and quoting him on
the elemental parry and the mitigation scale — *"There's need to be a scale there. Not all
'parry's' … will negate 100% damage"*. **Josh's quoted words say nothing about a column, a
registry, a mint, or a trigger.** The L1028-1031 paragraph sits far below that quote, inside the
loop's own landing record — it follows L1020 *"Landing sites (all applied the same day):"*
and is headed *"One queued item, deliberately not minted:"*. The next Josh-verbatim marker in
the file is L1035, in the following section (§11). So the "first authored moveset" trigger is the
loop's own scheduling note recording how it chose to sequence a mint — binding as prior loop
discipline, and squarely a director-adjudicable call, not a Josh ruling that would have to go
back to him.
Head of the file read for prior rulings per contract §7: the 2026-08-04
board is RULED, including the standing derive-don't-board law and the care-does-not-ACCRETE law —
no care column is proposed here.
docs/COMBAT_PROGRAM_ADDENDUM.md §6 L98-110 — the arena-mutation framework, and the sentence thiswhole package serves: *"the W-SPACE affordance list per arena doubling as the boss's mutation
vocabulary (the environment the arena offers is the environment the boss can weaponize — one
authored list, two consumers)."* The authored list is environment_grammar_refs; the second
consumer is the rail.
docs/PIPELINE_LEDGER.md L102 (A13, and its next step naming this schema as the work order),L101 (A12), L250 (the wave-2 ordering), and L263-284 (the beyond-floor VETOABLE block, including
VETOABLE 4 — *"minting a registry is a canon-adjacent act and is named here rather than done
silently"* — the precedent this package's §6 follows).
harness/regen_zone_catalog.py L145-174 (find_zone_block, split_block) and harness/extract_zone_catalog.py L46-83 (snake, parse_zone_rows) — imported, never
re-implemented, so all four Zone Catalog tools agree byte for byte on where the block is.
registries/T0_Hard_Lines [ACTIVE v0.2] @ HL_0078 — read in full (550 characters) and citedONLY for what it carries: Pillar-8 blended mode (*"every boss blends combat plus weapons plus
abilities plus puzzles plus character engagement. No boss is a pure DPS check"*), the Ch 17 Entu
Priestess three-phase template and its narrative-tentpole scope, and the monster/honored-adversary
exemption. The words "evolve" and "swap" do not appear in it. The exemplar dossier
(BOSS_RAIL_EXEMPLAR_BE_0002.md S5) attributes evolve-not-swap to this hard line; that
attribution is a mis-citation, corrected here — and it is the same class of defect
docs/FUN_REBUILD_PLAN.md L70 already caught and re-attributed once (*"NOT HL_0078, which is the
Pillar-8 blended-boss hard line — critic re-attribution"*).
docs/COMBAT_ENCOUNTER_SYSTEM.md L364 — the worked example's phase-2 step, *"P1's sweeps are
still live (evolve-not-swap)"* — and L609, which names it inside the RULED build target
(*"The per-chapter build target (2-4 thread-woven sub-bosses → one main boss, primary+secondary
verb, three-axis split, evolve-not-swap phases, ≥2 counters, Hades reward on win and death) is a
RULED work order"*). Corroborated on the spine at docs/spine/CH_02.md L97 — *"phase
escalation that evolves the moveset"* — which is the sentence BE_0002's own chapter states, and
which itself carries the [SRC: T0_Hard_Lines @ HL_0078] tag that propagated the mis-citation.
NOT DERIVED (authored judgment, and why it had no canon home)
1. The emitter's ten LEAD columns and two DIAG columns (mint_ordinal_proposed, zone_key,
region_id, source_page, source_line, spine_sourced, chapter_ids, source_site_ids,
source_site_ref_raw, diag_columns_absent_on_page, diag_eg_refs_unresolved). No zone registry
exists anywhere in the 76-registry corpus, so no schema shape was available to mirror. Eight of the
twelve are derivations of cells the pages already carry (the source cell splits into chapters and
site ids; the page path and line are the citation); four are emitter bookkeeping. Every one is
director-vetoable in isolation and none carries content the pages do not.
2. The ZONE_\d{4} id-space proposal and the sequential allocation scheme (§6). The corpus's
other id spaces are the evidence, not a rule anyone wrote.
3. The five S-finding recommendations (§5). These are schema readings over silences the canon
deliberately left — each is presented with its status quo, its alternatives, and the strongest
objection, for a director to adjudicate. None changes a canon number, name, hard line or §17 cell.
4. The arena_zone_ref column name. The dossier names the shape (§8, *"either arena_zone_ref
on T0_Boss_Attack_Rail … or a phase → zone_id map on the boss row"*); the spelling is this
lane's.
---
Two of the seven counter classes name an answer universe that exists and a scope that does not.
cover is answered by T0_Environment_Grammar_Registry.tactical_affordance_class = COVER (T1_Combat_System_Spec L647).
trigger_interrupt is answered by that registry's attack_verbs / loaded_potential / enemy_usable (L649).
arena. The gate therefore WARNs both and neither can pass clean (check_counter_class_coverage.py
L565-569).
The join is not missing. It is unreachable. It lives in the region pages' Zone Catalogs as the
environment_grammar_refs column — flores_island.md L52-53 is the whole BE_0002 fight's arena
grammar, in a markdown table. The chain from a boss row to that cell today is: boss row → chapter_id
→ spine beat prose → site_id → region-page table row → environment_grammar_refs → EG row. Four
hops, two of them prose.
The ruled direction this lane inherits: EMIT, do not hand-author. The pages are the authority and
stay it; the registry is a projection of them that can be regenerated when they change. That is the
same discipline regen_zone_catalog.py already enforces in the other direction.
---
harness/emit_zone_registry.pyWhat it does. Walks every .md under _source/02_Tier_2_Region_Pages/, parses each page's
## Zone Catalog table, and writes one proposed zone row per catalog row to
docs/proposals/combat/zone_registry_proposed.csv. Stdlib only. Deterministic: two runs are
byte-identical (asserted in the self-test and verified on the live corpus).
The guards, because a proposal tool that can touch canon is not a proposal tool.
registries/. guarded_write() resolves the output path and hard-failsif it lands inside the registry tree. The self-test exercises the refusal by trying to write
registries/T0_Zone_Registry/Sheet1.csv.
zone_id cell is the literal ZONE_XXXX. mint_ordinal_proposed is a proposed allocation ORDER, not an id, and is named as such on the
column and in §6.
zone_id, a missing | zone_id header, a table with no data rows, a header that is not the ten base columns, or an
unparseable source-site cell is reported by page name with its reason and makes the run exit
non-zero. A page with no ## Zone Catalog header (README.md) is reported as *no catalog*, which
is a different thing and is not a failure. A zone_key that collides across two pages is
treated as worse than a malformed page: a global key that is not unique cannot be an FK target at
all, so the run names both pages and emits zero rows rather than keeping the first and dropping
the second.
zone_registry_proposed.manifest.json carrying the git SHA the emit ran against and every source
page that was UNTRACKED or DIRTY at emit time (today: the three of §0.0). A row derived from an
unlanded page is a fact about the table, and the manifest is what makes it visible to a reader of
the CSV instead of something to be rediscovered. The manifest is a pure function of repo state —
no wall clock — so both artifacts stay byte-identical across two runs.
T0_Zone_Registry* directory exists under registries/, the emitter hard-fails with exit 4 unless
(region_id, zone_key) → zone_id preservation is implemented, which it is not; --preserve-ids
is declared so the guard can name it and fails the same way. That is §6's id-stability prerequisite
made mechanical, so the second emit cannot silently renumber the table.
--out that resolves outside the repo is a named REFUSED failure raised BEFORE the write, so a rejected path leaves nothing on disk.
Exit codes. 0 clean · 2 a named parse failure or any hard fail · 3 --strict with an unresolved
EG token · 4 a minted zone registry exists and id preservation is not implemented.
The self-test (--self-test, run over both CRLF and LF fixtures — region pages are CRLF, and the
newline handling is inherited from regen_zone_catalog.py for exactly that reason):
ZONE_XXXX placeholder on every row; the ordinal 1..N in page order; region_id = page slug; a CH_90 alpha_ruin source
cell split into chapter_ids + source_site_ids with spine_sourced TRUE; a region-ambient
source cell yielding spine_sourced FALSE with the raw cell preserved; a comma-bearing cell
surviving verbatim and CSV-quoted in the bytes; environment_grammar_refs copied verbatim and
typed populated; an unresolved EG_9999 diagnosed rather than dropped; source_line a real
1-based page line; byte-identical re-emission; LF terminators regardless of the page's newline.
diag_eg_refs_state values in one run — a page without the extension pack (absent), and a page
with it carrying a populated cell, an empty cell and a literal none cell. It asserts the four
states are typed in order AND that only the populated row contributes an EG token, so empty and
none can never be counted as refs.
wrong cell count, (b) a duplicate zone_id, (c) an unparseable source-site cell, (d) a
table whose header is not the ten base columns, (e) a page with no catalog at all (asserted to
be a *no-catalog* report, not a failure), (g) the same zone_id on two different pages —
each page valid alone, so the emitter's own cross-page branch is what must catch it — and
(f) an attempted write under registries/. In (a)-(d) and (g) the assertion is both that the
failure is NAMED and that zero rows are emitted — a malformed page never contributes a
half-parsed row, and an unkeyable table contributes nothing at all.
EMIT_ZONE_REGISTRY SELF-TEST: PASS (47 assertions — 23 per newline mode, plus the write guard).
| measure | value |
|---|---|
| pages scanned | 16 |
| pages with a Zone Catalog | 15 (README.md is the sixteenth and carries none) |
| zones emitted | 113 |
| parse failures | 0 |
environment_grammar_refs tokens | 253, 0 unresolved against the 225-row registry |
| join state, by row | populated 78 · absent 29 · empty 5 · none 1 (§7, F2) |
| columns emitted | 31 (28 proposed schema + 3 diagnostic) |
| ids minted | 0 |
| git SHA at emit | 924e0d09 |
| source pages untracked or dirty | 3 — arabia_persia, levant, mesopotamia (§0.0) |
Per page: †arabia_persia 11 · flores_island 11 · †levant 9 · †mesopotamia 9 ·
central_africa_congo 8 · rift_valley 8 · south_africa 8 · bali 7 · ethiopia 7 · south_india 7 ·
sri_lanka 7 · west_africa 7 · angkor_khmer 5 · swahili_coast 5 · sumatra_java 4.
† = not at HEAD (§0.0). Every count in this table is a WORKING-TREE count. **Against HEAD state
alone the same emitter yields 84 zones across 12 pages.** The 253 EG tokens, the 0 unresolved, and
the populated 78 / empty 5 / none 1 states are all unchanged — the 29 excluded rows carry no refs at
all, so absent simply falls from 29 to 0 and the join itself loses nothing. What DOES change is
the allocation order: 79 of the 84 surviving zones take a different mint_ordinal_proposed. Both
numbers are real; which one is correct is decided by whether the three pages have landed at apply
time (§6).
The generalization claim is the one that decides whether an emitter is the right tool, so it was
measured rather than asserted.
zone_id, source site_id(s) (spine §3b), terrain, gameplay functions, ambient_threat, vril_density,
climactic_affordances, protected, area_bounds, build_priority. The emitter asserts this and
fails the page if it ever stops being true.
†levant, †mesopotamia — the three untracked pages of §0.0, which deliberately do not carry
the T0.2 extension pack, arabia_persia L85-87 stating the reason: *"only
harness/apply_zone_catalog_columns.py may write them"*);
19 columns (central_africa_congo, ethiopia, rift_valley, south_africa, south_india, sri_lanka,
swahili_coast, west_africa); 20 columns (angkor_khmer, bali, flores_island, sumatra_java — the 19
plus zone_space_class).
zone_idvalues are globally unique** across pages. Both were checked, not assumed; both are the properties
a registry key needs.
none are threedifferent facts**, and the emitter keeps all three distinguishable rather than collapsing them into
a blank: diag_columns_absent_on_page lists the union columns that page's table does not carry at
all, and diag_eg_refs_state types every row absent · empty · none · populated. The three
10-column pages carry zone_bounds_wgs84 … footfall_surface_secondary as absent-by-design;
angkor_khmer carries environment_grammar_refs as present-but-empty, a populate-empty gap for the
env-grammar lane; and flores_island's manggarai_village_edge (L55) carries the literal token
none, which is an AUTHORED assertion that the zone affords nothing — the opposite of an
unauthored blank. Each belongs to a different lane (§7, F2).
CH_NN[/CH_NN…] <site_id> (93 rows) and region-ambient / region-ambient (page-authored) (20 rows). Both are parsed; a
third shape would be a named parse failure, not a silent blank.
---
Twenty-eight columns, of which eighteen are the Zone Catalog's own columns copied verbatim and ten
are the key/provenance layer that makes the table joinable. Three further diag_ columns are emitter
output and are dropped at apply.
| column | value | mirrors / cited to |
|---|---|---|
zone_id | ZONE_XXXX today; ZONE_\d{4} after the mint | the corpus's own EG_\d{4} / AT_\d{4} / BE_\d{4} convention. PK |
mint_ordinal_proposed | 1..113, emitter order | a proposed allocation ORDER, not an id (§6) |
zone_key | the page's own zone_id cell | flores_island.md L49 col 1. The NATURAL key; page authority. Globally unique across all 113 rows (verified) |
region_id | the page slug | FK → T0_Region_Index.region_id (15/15 resolve) |
source_page | repo-relative page path | the authority the row projects |
source_line | 1-based line of the row in that page | the citation anchor. kelimutu = L52, mount_inerie = L53, matching the dossier's own cites |
spine_sourced | TRUE / FALSE | derived from the source cell: TRUE where it names CH_NN sites, FALSE for region-ambient page-authored zones (arabia_persia L92-98 names those six and calls them a FEED to the spine's site pass, never an assertion the spine rowed them) |
chapter_ids | CH_02;CH_03 | derived from the source cell. FK candidate → T0_Chapter_Index.chapter_id |
source_site_ids | liang_bua_cave | derived from the source cell — the spine §3b site_id the row copies |
source_site_ref_raw | the source cell verbatim | nothing is lost to the split |
Ten base columns minus the two the key layer re-expresses (zone_id → zone_key, the source cell →
the three derived columns), leaving eight; plus the ten T0.2 extension columns.
| column | mirrors | note |
|---|---|---|
terrain | Zone Catalog col 3 | SPINE-OWNED — copied VERBATIM from the §3b site row (flores_island.md L40-41; regen_zone_catalog.py L9-11) |
gameplay_functions | col 4 | SPINE-OWNED |
ambient_threat | col 5 | SPINE-OWNED |
vril_density | col 6 | SPINE-OWNED |
climactic_affordances | col 7 | SPINE-OWNED |
protected | col 8 | SPINE-OWNED |
area_bounds | col 9 | PAGE-AUTHORED geometry (flores_island.md L44-46 — *"page authority, not spine canon"*) |
build_priority | col 10 | PAGE-AUTHORED |
zone_bounds_wgs84 | T0.2 pack | PAGE-AUTHORED; written only by apply_zone_catalog_columns.py |
meters_per_vertex | T0.2 pack | as above |
worldcover_biome | T0.2 pack | as above |
footfall_surface | T0.2 pack | as above |
care_tier | T0.2 pack | as above. Mirrored, never widened — the 2026-08-04 care-does-not-ACCRETE law binds |
asset_routing | T0.2 pack | as above |
environment_grammar_refs | T0.2 pack | THE JOIN. A ;-delimited FK list → T0_Environment_Grammar_Registry.env_feature_id. 253 tokens across 78 rows, 0 unresolved |
zone_space_class | T0.2 pack (4 pages) | as above |
ambient_population_band | T0.2 pack | as above |
footfall_surface_secondary | T0.2 pack | as above |
zone_id, pattern ZONE_\d{4}.region_id, zone_key) — the natural key. Today zone_key alone is globally unique;the composite is the safe declaration because zone naming is page-local by construction.
region_id → T0_Region_Index.region_id, min_tokens 1.chapter_ids → T0_Chapter_Index.chapter_id, min_tokens 0 (blank on the 20 region-ambient rows,which is correct rather than missing).
environment_grammar_refs → T0_Environment_Grammar_Registry.env_feature_id, min_tokens 0 —0 today because 35 of 113 rows carry no ref, in three distinct ways: the column ABSENT from
the page (29), PRESENT-BUT-EMPTY (5), and the literal token none (1) (§7, F2). A floor the table
cannot meet is a rusted tooth. Note that only two of the three are gaps: none is authored.
The registry is a PROJECTION. emit_zone_registry.py must gain a --check mode at apply — the same
shape regen_zone_catalog.py --check already has — that re-emits and asserts the registry matches the
pages, so a page edit that is not carried into the registry is a reported finding rather than silent
drift. A hand-edit to the emitted registry is a defect by construction; the fix always lands on the
page. That mode is not written here because there is nothing to check against until the mint.
---
arena_zone_ref**RECOMMENDATION: one new column on T0_Boss_Attack_Rail, arena_zone_ref, landing at position 28
after the three J5 elemental columns.** This is the dossier's own recommendation (§8: *"Recommend the
rail column: it is one column, it puts the answer beside the demand, and it lets A1 judge cover and
trigger_interrupt row by row instead of declaring them not-armed"*), and reading the 19 rows against
the two arenas confirms it rather than merely repeating it.
Which of the 27 columns it joins to.
phase_index is the determinant. BE_0002's two phases are two different zones — CH_02.md L141 site mount_inerie_cone_country · boss BE_0002.P1, L146
site kelimutu_crater_lakes · boss BE_0002.P2 — and the two zones carry *different* affordances
(flores_island.md L53 vs L52). A per-boss binding would be wrong on this boss, which is the whole
evidence for per-row.
counter_class is the consumer. A1 resolves cover against the zone's refs filtered to tactical_affordance_class = COVER, and trigger_interrupt against refs with enemy_usable = TRUE
and a non-none attack_verbs.
boss_id is the corroborator, not the key. The zone is bound per row because the row knows itsphase; the boss row does not need a second copy of it.
source_ref and notesbecause *the rail has no column for it* (dossier §5). Those cells stay as the reasoning; the column
becomes the check.
Type. arena_zone_ref → FK T0_Zone_Registry.zone_id, min_tokens 0. Blank must stay legal —
the rail is header-only across the other 267 movesets, and a boss whose phase sites the spine has not
typed cannot be made to invent one; a floor the table cannot meet is a rusted tooth, which is the same
reasoning fk_spec.json already applies to every other rail FK. On BE_0002 all 19 rows bind,
because the spine types both phase sites (L141, L146). It is deliberately a single ref rather than a
list; see the S5 interaction below, which is the one case that argues otherwise and is answered there
by phase_live_through instead.
The migration for the 19 existing BE_0002 rows. Recomputed from BE_0002_rail.csv, never carried
from prose:
| rows | phase_index | arena_zone_ref | authority |
|---|---|---|---|
| AT_0001 … AT_0011 (11 rows) | 1 | the zone minted for mount_inerie_cone_country | CH_02.md L141 (CH02_B07 · BE_0002.P1) → flores_island.md L53 |
| AT_0012 … AT_0019 (8 rows) | 2 | the zone minted for kelimutu_crater_lakes | CH_02.md L146 (CH02_B08 · BE_0002.P2) → flores_island.md L52 |
That is a mechanical, fully derived migration: the phase index already carries the answer, and the
spine already binds the phase to the site. It cannot be applied until §6's ids exist, and this lane
does not apply it.
What it buys immediately, checkable on the day it lands.
cover → EG_0005 (Cave Pillar and Karst Column, tactical_affordance_class = COVER) isin mount_inerie's refs. The row passes A1 by resolution instead of by warning.
cover would FAIL — and correctly. kelimutu's refs carry no COVER-class feature, which is the canon that demoted AT_0014 from cover to dodge_only at re-derivation (dossier §3). The
column turns that hand-caught defect into a gate-caught one, which is the argument for the column
in a sentence.
trigger_interrupt still resolves to nothing. The snare is a boss-PLACED hazard with no env_feature_id, so even a perfect arena join leaves it unjudgeable. That is a real open item and
the dossier names it (§8): decide whether boss-placed hazards get env rows — which
COMBAT_PROGRAM_ADDENDUM §6 L98-110 implies they must, since it specifies *"spawn-hazard"* as a
first-class arena_mutation sub-kind and *"hazard actors that BOTH sides interact with"* — or
whether the class splits into arena-trigger and hazard-interrupt. **Recommendation: give boss-placed
hazards env rows.** The addendum's own one-authored-list-two-consumers principle says the boss's
mutation vocabulary IS the arena's affordance list; a hazard the boss places that no data describes
is a third list. The split-the-class alternative buys nothing the env row does not, and costs a
vocabulary change to a Josh-ruled seven-member set.
terrain names WHICH terrain"*) is today a critic's reading. With the column, a row whose
arena_zone_ref resolves to a zone containing a protected_care_ref / hard_blocked feature can
be REQUIRED to name its feature explicitly. 58 of the 113 zones contain at least one such feature,
so this is not a corner case. No new care column is proposed — the existing cells become reachable,
which is the opposite of accretion.
Not recommended: a phase → zone_id map on the boss row. It is one cell instead of 19, but it
puts the answer one hop from the demand, cannot express a row that is live in two arenas, and leaves
A1 parsing a packed object to judge a row. The rail column is the cheaper check.
---
Each is a SCHEMA reading over a canon silence. None changes a canon number, name, hard line or §17
cell, so each is the director's to adjudicate and none goes to Josh (contract §7; the 2026-08-04
derive-don't-board law).
attack_kind cannot type a no-strike moveThe finding. The five members (melee · ranged · cast · grab · arena_mutation,
MINT_DESIGN_B L1140) type a strike. Three of BE_0002's 19 rows are pressure, reposition and listening
(AT_0001, AT_0002, AT_0006) and carry an EMPTY attack_kind rather than a falsified melee — against
a spec that marks the column required: yes. Every B4 kit in the corpus carries no-strike abilities,
so this recurs across the remaining 51 slice rows.
| option | pros | cons |
|---|---|---|
| A. Status quo — five members, no-strike rows blank, semantics undeclared | zero change | the column is spec'd required and 16% of the first moveset violates it; a gate cannot tell a deliberate blank from an unauthored one |
| A′. Declare the column NULLABLE, blank = no-strike | zero enum change; matches what the exemplar already did | blank is still ambiguous with unauthored; the meaning lives in an fk_spec note rather than in the data |
B. Add a sixth member no_strike | one token closes it; the engine's dispatch question (does this row need a hitbox?) is answered in the cell | a negation as an enum member; conflates pressure / reposition / listening |
C. Add stance instead | reads as a kind rather than an absence; familiar register | implies a persistent posture state, which a silent approach is not |
D. Add both stance and reposition | precise | enum inflation decided from three examples |
RECOMMENDATION: B. The enum's job is engine dispatch, and one token answers it. The finer taxonomy
is already carried by tell_shape_class, which is the read channel; damage = 0.00 is the
corroborating cell on all three rows. Land it with a precedence note in fk_spec.json: where a
no-strike move also mutates the arena, arena_mutation wins.
Strongest objection. Enums of negations rot — the day a no-strike move spawns a hazard, both
no_strike and arena_mutation apply and the row can carry only one. Answered by the precedence
note above, which is why it is part of the recommendation rather than a footnote to it.
phase_index is a scalar and cannot express evolve-not-swapThe finding. The corpus's boss doctrine is evolve-not-swap: phase-1 rows stay live in phase 2.
Duplicating 11 rows to say so would break the composite uniqueness on (boss_id, attack_id) that
fk_spec.json declares.
Where evolve-not-swap actually lives — a citation corrected. The exemplar dossier's S5 homes this
in HL_0078. It is not there: the hard line's full 550-character rule_text carries Pillar-8
blended mode, the Ch 17 Entu three-phase template and the monster/honored-adversary exemption, and
the words "evolve" and "swap" do not appear in it. The doctrine's real homes are
docs/COMBAT_ENCOUNTER_SYSTEM.md L364 (*"P1's sweeps are still live (evolve-not-swap)"*, the
worked example's phase-2 step) and L609 (the RULED per-chapter build target, which lists
*"evolve-not-swap phases"* among its terms), corroborated on the spine at docs/spine/CH_02.md
L97 (*"phase escalation that evolves the moveset"*) — BE_0002's own chapter, and the line whose
[SRC: T0_Hard_Lines @ HL_0078] tag propagated the mis-attribution. HL_0078 stays cited here for
what it does carry: the phase TEMPLATE. Nothing in the finding changes — the doctrine is real and
ruled either way — but a schema argument that rests on a hard line must rest on the right one.
BE_0002's own phase_breakdown_object is the second, unaffected leg of the evidence.
The interaction this package found, which changes the answer. Under the FIRST-LIVE reading the
exemplar applied, AT_0011 (cover, arena_mutation, phase 1) stays live in phase 2 — in an arena
that carries neither its answer (EG_0005 COVER is in mount_inerie's refs, flores_island.md L53, and
NOT in kelimutu's, L52) nor its ingredient (EG_0004, the scree slope it releases, likewise phase-1
only). The same reasoning that correctly demoted AT_0014 from cover condemns AT_0011's phase-2
liveness. This is invisible without the arena join and becomes a gate failure the day the column
lands, so the two findings must be adjudicated together.
| option | pros | cons |
|---|---|---|
| A. Status quo — scalar, semantics undeclared | zero change | the next author guesses; the exemplar already had to state the reading on AT_0001's notes |
B. Declare phase_index = FIRST-LIVE, monotonic | zero schema change, one fk_spec note; matches the exemplar | cannot express a row that RETIRES — and the AT_0011 case proves one must |
C. B, plus a nullable phase_live_through | expresses retirement; composite uniqueness untouched; AT_0011 takes phase_live_through = 1 and the contradiction disappears | a second column to keep consistent with the first |
D. Make phase_index a token list (1;2) | one column | breaks the int type and the engine's per-phase filter; MINT_DESIGN_B L1138 specifies int |
E. Derive liveness from arena_zone_ref alone (a row is live where its arena is) | no new column at all | silent on the 16 rows with no arena binding, and makes an FK carry phase semantics |
RECOMMENDATION: C. Declare FIRST-LIVE monotonic AND add phase_live_through (int, nullable, blank
= through the final phase), gated so the only sanctioned early retirement is the arena-bound one — a
phase_live_through earlier than the last phase is legal only where the row's arena_zone_ref names a
zone the later phase does not use. That keeps the hard line intact and fixes AT_0011 with a derivation
rather than a judgement call.
Strongest objection. Evolve-not-swap (COMBAT_ENCOUNTER_SYSTEM.md L364/L609) means the moveset
EVOLVES, and a retirement column invites authors to swap movesets by retiring rows — the exact thing
the ruled build target forbids. Answered: the doctrine forbids swapping the moveset, not retiring
a move whose ARENA is gone; the gate condition above makes that the only legal use, so the column
cannot become a swap mechanism. Note the objection is now weighed against a RULED work order rather
than a hard line — a real difference in force, and the honest one: the corrected citation makes this
doctrine, strongly ruled, rather than HL-severity canon, which slightly LOWERS the bar this option
must clear and is stated here rather than quietly enjoyed.
The finding. DECISIONS_PENDING_JOSH L1028-1031 queues the column explicitly: it *"mints with
that rail's first authored moveset at window-plan rank 16 — a column with no rows behind it is a
promise, not a shape."* BE_0002 is that moveset. **That paragraph is loop/director landing prose, not
Josh verbatim** — §10's only Josh-attributed block is L976-981 and says nothing about a column, a
mint or a trigger (authorship verified in §0). So this is a director call in both directions, and
neither option below needs Josh.
The fact both sides argue from, recomputed from the CSV. BE_0002 authors **zero parry_only rows
and zero neutralize rows** — by canon, not by choice (the Ch-2 reduced scope,
T1_Combat_System_Spec L685; defender_element = none, dossier §4). The mitigation scale is the
outcome scale of a *parry or elemental answer* (DECISIONS L990-997). Minting the column against
this moveset therefore produces a column whose 19 cells are all blank. **The two options read that
same fact in opposite directions, and both readings are defensible:**
names — a column with no rows behind it is a promise, not a shape.
A blank cell on a column that is INAPPLICABLE to the row's counter_class is not an unfilled
promise — it is the semantically CORRECT value, exactly as a blank is correct rather than missing
for chapter_ids on the 20 region-ambient rows (§3.3) and for every rail FK spec'd min_tokens 0
(§4). A dodge_only row has no mitigation outcome to record; blank IS its answer. On that reading
the column is not empty-pending-authoring, it is fully authored at 19/19, and the queue's sentence
simply does not describe this case.
The internal contradiction, reconciled explicitly. This package cannot argue "a blank is correct,
not missing" for chapter_ids and min_tokens 0 in §3.3 and §4, and then treat 19 blanks as
self-evidently disqualifying here — that is the same fact scored two ways. The reconciliation is that
the two situations differ in ONE respect and the director should rule on that respect alone: for
chapter_ids, rows that DO populate the column exist in the same table, so the blank is legible by
contrast; for the mitigation column at this mint, no populated cell exists anywhere in the corpus, so
the blank has nothing to be read against. That is a real difference, and it is a difference of
LEGIBILITY, not of correctness — which is why this is a judgement call and not a derivation.
What is derivable today, and therefore not at risk either way. The column's SHAPE does not depend
on the mint: the ruling's own three landmarks (full negation exists; full negation is not guaranteed;
a much-stronger attack always leaves residual damage, DECISIONS L994-1001) yield the token ladder
full_negation · partial · residual regardless of when it lands. **The mint-now case's strongest
point is that this shape is available NOW**, and the 51 remaining slice rows will otherwise be
authored against the column's ABSENCE — after which every one of them is a migration rather than a
fill.
| option | pros | cons |
|---|---|---|
| A. Status quo — the scale stays in prose | zero change | the twenty-ninth sitting's ruling exists only as prose; the queue is unserved |
| B. Mint an ordinal token ladder NOW with this landing | honours the queue's letter; the shape is fully derivable today and lands BEFORE the 51 remaining slice rows author against its absence; on the inapplicable-blank reading the column is authored 19/19, not empty | all 19 cells blank — which, if blank is read as unfilled rather than inapplicable, is the queue's own stated objection, self-inflicted |
B′. Mint the ladder with BE_0003's parry rail, alongside brief 1's defensive_class | the first moveset that can POPULATE it; honours the queue's reason | the letter of "first authored moveset" is not followed, and the 51 intervening rows author against a column that is not there |
| C. Mint it as an int rank 1..N | orders trivially | an int reads as a magnitude and invites tuning; L998-1001 rules the scale ordinal with no number ruled |
D. Put it on T0_Element_Effectiveness | the grid already carries strength | the scale is attack-strength against answer-strength and the attack strength lives on the RAIL; a grid column expresses half of it |
DIRECTOR ADJUDICATES — B AND B′ ARE BOTH LIVE. This lane declines to pre-decide it, because the
two options turn on a reading of one word ("blank") that this package uses in the opposite sense two
sections earlier. The shape is the deliverable and is identical under both: a token ladder over
the ruling's own three landmarks (full negation exists; full negation is not guaranteed; a
much-stronger attack always leaves residual damage), i.e. full_negation · partial · residual,
blank where the counter class admits no defence-with-an-outcome. This lane mints nothing either way.
§3.3 and §4 already apply to chapter_ids and to every min_tokens 0 rail FK. Cost: a column
whose first populated cell arrives with BE_0003. Benefit: the 51 remaining slice rows author WITH
the column instead of migrating onto it later.
can yet see exercised is not yet a shape. Cost: 51 rows author against the column's absence.
Benefit: the column's debut is also its proof.
The lane's lean, offered as a lean and not as a recommendation: B′, on the ground that the queue
sentence is prior loop discipline and the burden should sit on departing from it. **The strongest
objection to that lean, which the director should weigh first:** the queue names *"that rail's first
authored moveset"*, BE_0002 IS that moveset, and deferring reads the trigger's REASON over its LETTER
while the package simultaneously argues elsewhere that an inapplicable blank is correct rather than
missing — so B′ can only be right if blanks mean one thing on chapter_ids and another here. That
inconsistency is real, it is reconciled above only as a difference of legibility rather than of
correctness, and it is the reason this is presented as a fork rather than a recommendation.
telegraph_shape key rejects 13 of 19 rowsThe finding, recomputed. MINT_DESIGN_B L1128 proposes promoting telegraph_shape from a free
string to a CLOSED ten-member key (whip_arc · low_sweep · high_feint · lunge · feint ·
grab · venom_spit · swipe · coil · disc). Against BE_0002's 19 rows: 6 rows inside the key
(lunge ×2, feint, swipe ×2, grab), 13 outside it — silent_close, break_cover,
throw_line, snare_lay, listening_hold, close_press, drop_fall, slope_release,
frenzy_commit, run_through_water, drain_state, unbraced_strike, arrest_stagger. The ten
members are inherited from two hand-coded movesets (a serpent and a caci champion) and cannot express
a human hunter's grammar. The promotion is not in fk_spec.json yet, so nothing breaks today.
| option | pros | cons |
|---|---|---|
| A. Status quo — leave it a free string, promotion unlanded | nothing breaks | the promotion is on the board and will land; the problem is deferred, not answered |
| B. Widen the closed key with the 13 human-register shapes (→ 23 members) | keeps closure, so the gate can check membership | 268 movesets ahead across aquatic, avian, constructed and materialized-argument registers — each will need another widening, and a key widened per-moveset is not closed in any useful sense |
C. Land it OPEN and gate the READ instead — check tell_shape_class, not the animation key | no rejection; the thing gates actually partition (A3) is the class read | free strings drift into synonyms (lunge vs the_lunge) with no membership tooth |
D. Two-level: closed telegraph_shape_family + open telegraph_shape | closure where checkable, freedom where authored | two columns; telegraph_shape is the FHumanityAttackRow struct mirror and MINT_DESIGN_B L1122 warns a rename silently drops the column |
RECOMMENDATION: C, with the S8 tooth attached. What must be closed is the player-facing READ
(tell_shape_class, which is what gate A3 partitions), not the animation content key. Content ids are
open everywhere else in this corpus; a ten-member list derived from two hand-coded bodies is a
rejection machine pointed at 268 movesets. If the director wants closure today, **B is the low-risk
fallback** and the 13 members above are the exact widening it needs.
Strongest objection. An open key is untestable, and the QA program's per-frame state record already
carries telegraph_shape (MINT_DESIGN_B L1152-1156), so QA loses the ability to assert a known shape.
Answered: QA asserts the CLASS read, which is the player-facing contract and the thing the fight
must teach; the animation key is a content id, and a QA assertion against a content id is a brittle
test of the artist's naming rather than of the fight.
tell_shape_class / tell_motion_class have no authored vocabularyThe finding. Both are specified as enums (MINT_DESIGN_B L1142-1143) with no vocabulary authored
anywhere in the corpus. Gate A3 partitions over a set that does not exist.
The measurement that decides it. BE_0002's 19 rows mint 16 shape families and 19 motion families.
14 of the 16 shape tokens are used exactly once, and 19 of 19 motion tokens are used exactly once.
A3's tooth is *"no tell_shape_class carries two counter classes"* — a COLLISION check. On a rail
where every token is a singleton, that check is trivially true and can never fire. The gate is
armed against a vocabulary that does not exist AND, as authored, against a distribution that cannot
violate it.
| option | pros | cons |
|---|---|---|
| A. Status quo — enums with no members | zero change | A3 is armed and unsatisfiable-and-unviolatable at once |
| B. Ratify the exemplar's 16 + 19 as the corpus vocabulary | it exists, it is derived move-by-move from the bestiary body-origin column, testable today | a one-body vocabulary (a human hunter) ratified corpus-wide; 268 movesets will break it immediately |
| C. Author a lane-owned vocabulary first (a design pass over the 57 kits), then re-derive | designed rather than accreted | blocks the 51-row volume pass behind a design pass the ledger has not scheduled |
| D. Declare both columns OPEN and let A3 check collisions over whatever is authored | matches what A3 actually does; no false closure | drifts to 268 singleton tokens and the tooth degrades to trivially true — measurably, already |
RECOMMENDATION: D as the schema, B as the SEED, plus the tooth D is missing. Declare both columns
open; publish the exemplar's 16 + 19 as the seed vocabulary authors must reuse-or-extend rather than
re-invent; and add to A3 a per-boss singleton-RATIO report — the share of a rail's tokens used
exactly once — because a per-token warning is noise at 19 rows and useless at 5,000. BE_0002's ratio
is 14/16 shape and 19/19 motion, which is the honest baseline the next rail is measured against.
Strongest objection. An open vocabulary with a warn-only ratio check will still drift, and a
report nobody fails is not a gate. Answered honestly: that is the real failure mode, and it is
why the recommendation specifies the ratio as the metric rather than leaving the check per-token — a
ratio can carry a threshold once two or three rails exist to calibrate it, and a per-token warn never
can. Setting that threshold from one rail would be inventing a number; it is named as owed at the
third authored moveset.
---
docs/PIPELINE_LEDGER.md L277-279 (VETOABLE 4) states the rule this brief follows: *"minting a
registry is a canon-adjacent act and is named here rather than done silently."* **This lane minted
nothing.**
MINT AGAINST HEAD STATE ONLY — the hard prerequisite, either way. Three of the fifteen source
pages are untracked and absent at HEAD (§0.0), and they account for 29 of the 113 rows. Because
mint_ordinal_proposed is one alphabetical sequence over all pages, minting against the wrong state
does not merely add or drop 29 ids — 79 of the 84 HEAD zones would take a different id. So:
arabia_persia.md, levant.md and mesopotamia.md have landed at HEAD by apply time, mint113.** Re-run the emitter first and confirm the manifest reports zero untracked-or-dirty source
pages; the CSV in this package is then correct as it stands.
allocating only the new keys through the id-preservation pass below. Do NOT mint 113 against an
untracked working tree: the 29 rows would enter canon citing pages that do not exist at HEAD, and
a later --check against HEAD would report the whole table as drifted.
ambiguous about it: zone_registry_proposed.manifest.json carries the git SHA and the
untracked/dirty page list for the run that produced the CSV, so the minting agent verifies state
from the artifact rather than from this paragraph's timestamp.
Nothing else in the mint depends on which branch is taken — the schema, the FKs, the
arena_zone_ref design and all five S-findings are unchanged, and the 84-row table carries all 253
EG tokens and every populated join, because the three pages contribute none.
How many ids. 113 against the working tree today, 84 against HEAD (see above), one per
emitted row. The corpus will grow: T0_Region_Index carries 76 region rows and only 15 have a
Zone Catalog (12 at HEAD), so 61 regions are unwritten. At today's mean of 7.5 zones per page, the
finished corpus lands near 570 zones.
The id space. ZONE_\d{4}. Four digits matches every neighbouring space in this corpus
(EG_\d{4} at 225 rows, AT_\d{4}, BE_\d{4}) and leaves ~17× headroom over the 570 projection.
Three digits would fit the projection and leave none.
The allocation scheme.
| option | pros | cons |
|---|---|---|
A. Sequential in emitter order (page slug alphabetical, then page row order) — mint_ordinal_proposed as emitted | the only scheme the emitter can REGENERATE byte-identically, which is what makes drift detectable; matches every other flat id space in the corpus | the id carries no meaning; flores (CH_02) lands at 38-48 rather than at the front |
| B. Chapter-ordered (CH_02 first) | reads in story order | the 20 region-ambient rows have no chapter, and a page serving two chapters (flores CH_02+CH_03) has no single position |
| C. Region-blocked (100 ids per region) | the id names its region | wastes the space and breaks the day a region exceeds its block; region_id already names the region as data |
RECOMMENDATION: A. An id is a key, not a description, and the emitter's reproducibility is the
property that keeps this table honest.
What the mint costs, so it can be approved in full.
registries/T0_Zone_Registry [DRAFT v0.1]/Sheet1.csv — 28 columns, 113 or 84 rows per the HEADruling above.
docs/fk_spec.json entry carrying §3.3's PK, unique constraint and four FKs.docs/fidelity_baseline.json refresh in the same commit (CLAUDE.md's standing rule).docs/DOC_MAP.md entry for the registry, the emitter and this package. exist, emit_zone_registry.py must read the live registry's (region_id, zone_key) → zone_id
map and PRESERVE it, allocating only for new keys. Today it cannot, because there are no ids to
preserve. Without that pass the second emit renumbers the table and every arena_zone_ref on the
rail silently re-points. Named as a blocking prerequisite of the second emit, not of the first
— and no longer only named: the emitter now hard-fails with exit 4 the moment a
T0_Zone_Registry* directory exists under registries/, whether or not --preserve-ids is
passed, because the flag is declared but not implemented. The prose prerequisite and the machine
guard say the same thing, and the guard is the one that cannot be forgotten. Implementing
preservation is what lifts it.
What it unblocks. arena_zone_ref (§4) and with it A1's two permanently-warning classes; the A13
arena-mutation framework, whose ledger row says outright that it rides this join; the 52-row volume
pass's arena resolution step; and the care-boundary check on the 58 zones that contain a protected or
hard-blocked feature.
---
| id | finding | why it matters |
|---|---|---|
| F1 | The join covers 15 of 76 regions — and only 12 of them at HEAD. 15 region pages carry a Zone Catalog; T0_Region_Index has 76 region rows, so 61 regions have no zone data at all. Of the 15, three (arabia_persia.md, levant.md, mesopotamia.md) are UNTRACKED and absent at HEAD — another lane's unlanded work, contributing 29 of the 113 rows and zero join refs (§0.0, checked 2026-08-07T23:10Z at HEAD 924e0d09) | the emitted registry is complete against what the pages say and incomplete against the world — and, today, complete against a WORKING TREE rather than against canon. Any gate reading it must treat an absent region as unwritten, never as empty; and the mint must resolve the HEAD question first (§6), because 79 of the 84 HEAD zones change ordinal depending on it |
| F2 | 35 of 113 zones carry no environment_grammar_refs, in THREE different ways — not 34 in two. (i) The column is ABSENT from the page for 29 zones: arabia_persia (11), levant (9) and mesopotamia (9) do not carry the extension pack at all, by their own declared design — and are also the three untracked pages of F1. (ii) It is PRESENT-BUT-EMPTY for 5: angkor_khmer carries the full 20-column pack with the refs cell blank. (iii) It is the literal token none for 1: flores_island.md L55 manggarai_village_edge | cover and trigger_interrupt remain unanswerable in those zones after the join lands. The three causes need three different lanes, and only one of them is a defect: (i) is a page-design decision, (ii) is a populate-empty gap for the env-grammar lane, and (iii) is an AUTHORED assertion that the zone affords nothing — a none is answered, not missing, and a lane that "fixes" it would be overwriting canon. The emitter types all three in diag_eg_refs_state (absent/empty/none/populated) so no consumer has to re-derive the distinction from a blank |
| F3 | The reverse orphan set is three rows. 222 of 225 EG features are referenced by at least one zone. The unreferenced three: EG_0001 Lava Vent (HAZARD_VOLUME), EG_0002 Sieidi Stone (protected, NONE), and EG_0225 The Broker's Hauling Yard on the Open Valley Ground (COVER) | EG_0225 is a live COVER answer that no arena can reach — a cover row authored against it would resolve to nothing. Finding for the env-grammar / region-page lane |
| F4 | AT_0011 outlives its arena under the FIRST-LIVE phase reading (§5.2). A cover row whose answer and whose ingredient both exist only in the phase-1 zone stays live in phase 2 | the arena join and the phase semantics must be adjudicated together or the column lands red on the exemplar it was built for |
| F5 | The S6 mint trigger names a moveset that cannot populate the column (§5.3) — 19 blank cells by canon. The queue paragraph is loop/director landing prose, NOT Josh verbatim (verified in §0) | DIRECTOR ADJUDICATES; both options live. Deferring reads the trigger's reason over its letter, but minting now is equally defensible because a blank on an inapplicable row is the same correct-blank this package argues for chapter_ids (§3.3) and every min_tokens 0 FK (§4). The lane declines to pre-decide it and states the contradiction rather than resolving it silently |
| F6 | Arena answerability, measured. Of 113 zones: 17 contain a COVER-class feature; 24 contain a feature with enemy_usable TRUE and non-none attack_verbs; 58 contain a protected_care_ref or hard_blocked feature | a cover row authored in any of the other 96 zones is unanswerable — and, for the first time, checkable. The 58 is the care-boundary surface dossier gate 11 currently checks by reading |
| F7 | Citation drift in the exemplar dossier. §0 cites CH_02.md L142 / L147 for CH02_B07 / CH02_B08; the typed beat rows are at L141 / L146 (L142 / L147 are their objective lines) | minor, recorded so a later reader is not sent one line past the join. No content is wrong |
| F8 | harness/extract_zone_catalog.py already exists and projects ONE page to build/zones/<slug>.csv with a clean snake_case header. The new emitter imports its parser rather than duplicating it, and differs in scope (all pages, one registry-shaped table, keys and provenance, EG resolution, the registry write-guard) | if the director prefers one tool, the merge is small — but the two have different outputs and different consumers today, and neither writes canon |
| F9 | The Zone Catalog preambles say "seven" T0.2 extension columns; the live pack is TEN. flores_island.md L43-44 (*"the T0.2 extension-column pack (the seven per-zone build columns…)"*, in a preamble whose own bracketed note at L46-47 says it was aligned to the live column set on 2026-07-27) and arabia_persia.md L85 (*"The seven declared T0.2 extension columns are deliberately absent"*). The live extension set is zone_bounds_wgs84 · meters_per_vertex · worldcover_biome · footfall_surface · care_tier · asset_routing · environment_grammar_refs · zone_space_class · ambient_population_band · footfall_surface_secondary | a stale count in a living-source preamble is exactly the thing CLAUDE.md's living-source rule exists to prevent — the next agent reads "seven" and builds against the wrong pack. Region-page lane item; NOT edited here (the pages are canon and this lane may not change them) |
| F9b | A second preamble-vs-rows staleness, in the same class and on the join column itself. swahili_coast.md L48-49 declares *"worldcover_biome, asset_routing and environment_grammar_refs are deliberately blank pending their own passes, exactly as on the pilot page"* — while that page's own rows carry environment_grammar_refs FULLY POPULATED (L53 kilwa_kisiwani → EG_0094;EG_0095;EG_0096;EG_0097;EG_0098;EG_0099;EG_0100;EG_0101, and every other row likewise). The refs pass evidently ran and the preamble was never updated; worldcover_biome and asset_routing ARE still blank, so only the third clause of the sentence is stale | worse than F9's stale count, because this one is about the join column this whole package is built on: an agent reading the preamble concludes swahili_coast contributes nothing to the arena join and skips 5 populated zones. Same region-page lane, same living-source rule, same NOT-edited-here boundary. The emitted CSV is unaffected — the emitter reads rows, never preambles, which is itself the argument for projecting the pages rather than summarizing them |
---
No canon was changed: not a region page, not the spine, not a registry row, not the CVD, not a
numerical, naming, hard-line or §17 cell. No id was minted — every emitted zone_id is the literal
ZONE_XXXX, and §6 routes the allocation to the director. No arena_zone_ref cell was written on the
19 BE_0002 rows; the migration is specified and left unapplied. harness/run_gates.py was not run and
docs/DOC_MAP.md was not edited, per the wave override. No question is put to Josh: every one of the
five findings is a schema reading, presented with its status quo, its alternatives and the strongest
objection, for a director to adjudicate — four carry a recommendation, and **S6 (§5.3) deliberately
carries a FORK instead**, because the lane found its own reasoning inconsistent across §3.3/§4 and
declined to resolve that silently in its own favour. No care column, flag or protection was
proposed — the 2026-08-04 care-does-not-ACCRETE law binds, and §4's care benefit is reaching cells
that already exist.
Three source pages were READ but not landed, and this lane did not land them.
arabia_persia.md, levant.md and mesopotamia.md are another lane's untracked work (§0.0); this
lane disclosed their state, measured their contribution, made the mint conditional on it (§6) and
made every future emit stamp it (the manifest) — but did not commit them, edit them, or treat their
absence from HEAD as this lane's to resolve. Two region-page defects were likewise flagged and left
unedited (F9, F9b), and one exemplar-dossier mis-citation was corrected in this document only
(§0, §5.2 — HL_0078 does not carry evolve-not-swap); the dossier and docs/spine/CH_02.md L97 still
carry the mis-attributed [SRC:] tag and are named for their owning lanes rather than edited here.