pipelines/MINT_DESIGN_B_ITEM_ALLY_2026-07-27.md
Schema design only. READ-ONLY run — nothing in C:/dev/humanity-forgotten or C:/dev/Humanity/Humanity
was modified; this file is the sole write. Canon HEAD ab5d32c.
Scope: the concrete data homes for addendum §§7-12 as integrated at T1_Combat_System_Spec §§12-15,
T1_Ability_Tree §5H/§5I/§14A, T1_Integrity_Paths_Worldstates_Master §8.11.5-.9, and
T1_Tier_C_MMO_Spec §8.7-.9 — restricted to FAMILY B: the gear-imprint lane, loadout sets, the ally
homes, boss timing/feedback on the attack rail, and the status grid.
Lens inputs consumed (SURVIVES verdicts + refuter corrections binding; KILLED findings not resurrected):
p1a_bl_loot_engine.md (F1,F2,F4,F5,F6,F7,F8,F9,F10,F11 survive · F3 KILLED — the acquisition-σ
vs roll-quality-σ split is a false premise, §13.2 names acquisition outright; its RESIDUE (quality
windows have no fields) is carried into T0_Rarity_Grade/T0_Imprint_Affix and not as a sigma
disambiguation) · p1a_creature_collection_ally_ai.md (1-9 all survive, 4 corrections applied) ·
p1a_sekiro_timing_defense.md (F1-F8 all survive; F2's i-frame recommendation is a **system-canon
change routed to the rank-18 Josh brief** and is NOT designed here) · p1a_dqpm_turn_readability.md
(F1,F2,F3 survive; F4,F5,F6,F7 killed — the rank-16/20 residue stays out; only F1's status read
columns and F2's read_persistence co-freeze here).
---
0.1 THREE landing vectors, all gate-safe, all already precedented (reconciled M6 — the first
draft had two, and three of its five extension targets were on the wrong one).
registries/, declared in docs/fidelity_baseline.json added_registries (12 registries already landed this way:
T0_Rune_Registry, T0_Flora_Registry, T0_Minigame_Registry, T0_Gather_Node_Registry, …),
registered in docs/fk_spec.json with a PK, an id_formats pattern + min_values floor, and
its FKs. This is the vector docs/registry_extensions.json _deferred.familiar-bond-ladder
already names for the bond ladder ("a bond_abilities child table … to be registered in
docs/fk_spec.json once minted").
_source reference width, declared in docs/registry_extensions.json under a named owning system AND in
docs/fidelity_baseline.json added_columns, content pinned by extension_hashes.
harness/check_registry_extensions.py enforces I1/I2/I3 (no phantom column, exactly one owner,
no ownerless manifest key). **Vector B applies only to a registry that HAS a frozen _source
reference width** — verified targets in this design: T0_RNG_Drop_Table, T0_Chapter_Index,
T0_Creature_Roster.
stub_superseded REGISTRY = extend that registry's fidelity_baseline.json candidate_header and re-pin it; **no added_columns entry and no
extension_hashes key**. Verified this pass:
`stub_superseded = { T0_Boss_Encounter_Registry/Sheet1, T0_Familiar_Registry/Sheet1,
T0_Questline_Index/Sheet1, T0_School_Registry/Sheet1, T0_Weapon_Registry/Sheet1 }`, none of them
in added_columns and none in the 19 extension_hashes keys.
check_registry_extensions.py L59-69 handles them on a separate path: when a manifest key has no
added_columns, it is satisfied only if every manifest column appears in
stub_superseded[key].candidate_header. So the correct landing edit for defensive-properties
(Weapon, §4.2), ally-build (Familiar, §3.4) and counter-coverage (Boss_Encounter, §4.1) is
the candidate_header + its pin — NOT the added_columns story §6.2 first told.
to exceed, so adding columns to it is free and it declares no manifest key. Verified: not one
of the 12 added_registries carries a manifest key today (the extensions manifest holds 18
keys, none an added registry), and I3 fires immediately on a key with neither added_columns nor
a stub_superseded entry: `{"rule": "I3", "key": key, "detail": "manifest key has no
added_columns and no stub_superseded entry ..."}`. The first draft would have turned the gate RED
on two keys — the imprint-loot group on T0_Rune_Registry (§1.5) and the
assetgen + sfx-pipeline groups on the new T0_Equipment_Registry (§1.4). **Both are dropped:
the columns still land, the manifest keys do not.** (Family A gets this right by construction and
says so — it adds zero columns to any existing registry.)
0.2 §5H.4's ruling, applied. "Per-ability tier-bonus rows land on the T0_Ability_Tree_Registry
extension rail rather than as new columns on the base row." Family B reads that as the general
law: new structured canon is ROWS in a child table, not a widened base row. Vector B is used only
where the fact is genuinely one-per-existing-row (a weapon's defensive_class, a creature's
status_immunity, a drop row's sigma_window_length) — never to hold a one-to-many set.
0.3 Id formats. House patterns are <PREFIX>_\d{3,4}, PK column <thing>_id, uppercase prefix
(WPN_\d{3}, CR_\d{4}, FAM_\d{2}, RN_\d{4}, MG_\d{4}, GN_\d{4}, FL_\d{4}, FR_\d{4}).
Every new table below follows it, and every one is registered with a min_values floor so a silent
mass-emptying fails the gate (the F4/F5 hardening rule already in fk_spec.json).
0.4 THE SHARED TUNING FIREWALL — one discipline, stated identically in Family A and Family B.
*(Reconciled M8. This block is verbatim-identical to §0 of mint_design_A_build_space.md. If the
two ever diverge, they are not two firewalls — they are one broken one. The first drafts collided
twice: one column name magnitude_ref with two contracts, and each design's stated firewall
stricter than its own tables.)*
1. A tuning MAGNITUDE is never a literal in a canon row. It is a ref into
T0_Balance_Constant_Registry.constant_id — the _deferred address this mint declares
(balance-constant-table; constant_id PK; owner Phase-5M; declared, not minted). The ref
columns are magnitude_ref, threshold_ref, breadth_floor_ref, expected_pick_band_ref
(Family A) and magnitude_ref (Family B, on T0_Imprint_Affix and T0_Status_Table):
one column name, one contract, both families. Family B's magnitude_ref was "string |
Phase-5M placeholder key" with no target; it now takes Family A's target.
2. Ordinals, ratified counts, and indices are LITERALS, and are not magnitudes:
ordinal_rank, ordinal, grade_index, gate_index, pick_budget, pick_cost,
precedence_rank, set_ordinal, imprint_slot_count, roll_window_ordinal, int_tier,
bond_value, bond_threshold, stat_point_budget, priority, phase_index, charges,
stack_cap, and the level / reroll / event counters. They order and count; they do not tune.
3. Deferred magnitudes land BLANK with a TODO_5M note — never as an interim number. Blank at
landing: every ref column above, plus min_tell_ms, answer_window_ms, telegraph_lead_ms,
parry_window_ms, parry_stamina_cost, sigma_window_length.
4. The struct-mirror exception is ENUMERATED and CLOSED. A column that mirrors a shipped engine
struct field keeps that struct's type and units, because the registry becomes the struct's data
source and REGISTRY_ROW_STRUCT_SPEC.md §1 makes a rename or retype a silent column drop. The
complete list is five columns, all on T0_Boss_Attack_Rail (mirroring FHumanityAttackRow):
windup_seconds, active_seconds, recovery_seconds, damage, punish_damage_mult. Their
values stay ILLUSTRATIVE until Phase-5M. Nothing else in either family is exempt — this is
the honest statement of what §4.1 already does; the first draft's blanket "every magnitude is
ORDINAL or DEFERRED" and that table disagreed in writing.
Shapes and relation classes freeze now; numbers land at Phase 5M / 5H per
T1_Combat_System_Spec §12's "Schema and Numerical Deferral" and §15's sweep charter.
0.5 Player-facing strings — Family A's treatment ADOPTED (reconciled M4). Every column that
renders to a player carries the suffix _display or _text and is authored BLANK. The marker is
string_status — Family A's enum (placeholder DEFAULT → authored → voice_passed), which
doubles as the natural-voice pipeline's worklist query — not the `voice_status =
pending_natural_voice` marker this design first declared and then carried in none of its tables.
Every table below with an FText column carries string_status; the tables whose strings are
voiced lines also carry voice_register_ref → T0_Voice_Registry.voice_id. Per
docs/NATURAL_VOICE_DOCTRINE.md §5 (THE NATURAL-VOICE REWRITE PASS) no system-voice string is
invented in this design. Per docs/REGISTRY_ROW_STRUCT_SPEC.md §2 rule 2 those columns infer to
FText and enter the localization flow; machine tokens (*_id, *_ref, *_class, *_tag) stay
FString and must never carry display text — note the precise mechanism (n1): *_class is in rule
2's EXCLUSION set while *_token is simply never MATCHED and falls through to rule 7 (FString).
0.6 Seeds are FIRST ROWS, never ceilings. Josh's verbatim addendum examples are mapped onto the
first authored rows of each table. Every pool-bearing table carries an examples_not_limits note in
its notes cell and in the DOC_MAP entry, per addendum §12's closing rule and §5I.1's
"a pool offering one real choice with filler beside it is not a pool."
0.7 What Family B does NOT own (stated so the mint does not double-author): tier-bonus pool rows
themselves (§5H.4 rail — Family A); the role-axis and spec-purpose ROSTERS (Family A's
T0_Role_Axis_Registry / T0_Spec_Purpose_Registry — this design CONSUMES them by FK, M3); the
boss-row pip columns pip_fill_event / pip_wipe_event / pip_neutral_event / chain_scope
(sekiro F1 → rank 16); hitzone_parts carve keys (the rank-16 hitzone lane — corrected M5:
this is NOT sekiro F7; F7 is the Ch-2 ParryOnly-scope finding, which this design carries as gate
assertion 1 in §4.1 and correctly calls "the F7 tooth"); tell_shape_class
counter-class-partition tooth (dqpm F3 → rank 16, though the tell_shape_class COLUMN lands
here on the attack rail so rank 16 has something to partition); the ally party-frame UI and bark
class (rank 22); T1_UI_UX_Spec (rank 20).
Two SURVIVING sekiro findings deferred WITH a recorded owner (reconciled M5). The lens-input
line asserts F1-F8 all survive; two were then neither designed nor deferred, and a survivor may be
deferred but may not be silent:
pips; canon says 5 / 7 / 9+", re-classed UPWARD by the refuter (*"F8 — understated. Re-classed
WEAKER → WEAKER + MISSING, LOW → MEDIUM-HIGH: the player HUD pip strip never reads a boss actor's
pips at all"*). Owner: the HUD / pip lane (rank 16), alongside F1's pip_fill_event columns
it is the twin of. Not owned here, and no attack-rail column presumes a cap.
consumers. Owner: the SFX consumer lane.** §4.1's telegraph_sfx_id_ref is F3's INCOMING-class
sting and is explicitly *not* F6's deflect-feedback channel; the two must not be conflated when
the SFX lane lands.
---
T0_Rarity_Grade — the shared grade PROPERTY table (NEW registry · 24 columns)Why it exists. bl-loot F5 SURVIVES-STRENGTHENED: three live registries run three different
rarity_class vocabularies (T0_Rune_Registry {common 8, rare 3, epic 5, legendary 2, none 1},
T0_Flora_Registry {common 18, uncommon 4, rare 2, signature 1}, T0_Mineral_Registry {common 10,
uncommon 6, rare 3}), a FOURTH is spec'd in T0_Schema_Dictionary L958 for T0_Creature_Roster
(common, uncommon, rare, legendary, mythological), and T0_Weapon_Registry has no rarity column at
all — so RN_0018's ratified 1/2/3/4 slot scheme is not computable from data for any item. MYTHIC
is being added on top of that. One grade table with an explicit axis closes it.
RECONCILED M2 — the surrogate keyspace is GONE; this is a PROPERTY table, not a rival enum.
The first draft minted a new RG_\d{3} id space and re-pointed the live rarity_class columns at
it as FKs. That contradicts a binding refuter correction the design itself lists as an input:
Finding 8 — extend the existingrarity_classenum (which already carriesnoneand
legendary), do not mint a parallelitem_rarity; reconcile Flora's and Mineral's divergent
vocabularies in the same pass. — p1a_creature_collection_ally_ai.md L1020-1022
The reconciled shape satisfies bl-loot F5 (a grade table, FK-able, so imprint_slot_count /
obtainable_by_roll / the read channels get a home) and creature F8 (no parallel id space,
no live cell moved) simultaneously:
rarity_class stays the shared TOKEN vocabulary, and it is stated ONCE — here — as the union of the live vocabularies: none · common · uncommon · rare · epic · legendary ·
mythic · signature. mythic is the one token no live cell carries; uncommon
(10 live cells: Flora 4 + Mineral 6) and signature (1, Flora) are already in the data and
any ladder that omits them is broken by construction. *(Re-counted programmatically this pass —
Flora 25 rows {common 18, uncommon 4, rare 2, signature 1}, Mineral 19 {common 10, uncommon 6,
rare 3}, Rune 19 {common 8, epic 5, rare 3, legendary 2, none 1}; the critic's "11 uncommon"
over-counted by one. 11 cells in total had no legal value under Family A's first-draft enum.)* Family A cites this ladder and mints no rarity enum of its own.
(axis, rarity_token), not a surrogate. Live precedent verified in docs/fk_spec.json: T0_Thread_Grid keys "primary_key": ["thread_id","chapter_id"], so the
composite is an exercised house shape, not an extension of the checker.
(axis, rarity_token) pair declared for it — the range_id/capability_ref _note idiom.
Parent / relation. Standalone shared dimension, referenced by token:
T0_Rune_Registry.rarity_class · T0_Flora_Registry.rarity_class ·
T0_Mineral_Registry.rarity_class (all axis=item_grade, all resolving today with zero cell
edits) · T0_Imprint_Affix.rarity_token_min · T0_RNG_Drop_Table.rarity_class ·
T0_Equipment_Registry.base_rarity_class · WS_046.rarity_class · and Family A's
T0_Bonus_Pool_Registry.grade_ceiling / T0_Bonus_Option_Registry.rarity_class at
axis=pool_option.
Key. composite PK (axis, rarity_token); id_formats.rarity_token = the closed 8-token
alternation, min_values floor 6 at landing. No RG_ ids exist anywhere.
| column | type | enum / FK | required | |
|---|---|---|---|---|
axis | enum | item_grade · pool_option · creature_spawn | yes (PK part 1) | |
rarity_token | enum | none · common · uncommon · rare · epic · legendary · mythic · signature — the shared ladder, stated once | yes (PK part 2) | |
ordinal | int | — (rank within axis; the ordering both families read; not a magnitude) | yes | |
display_name_text | FText | — | blank → natural-voice pass | |
string_status | enum | Family A's string_status_enum: placeholder (DEFAULT) · authored · voice_passed (M4) | yes | |
power_source | enum | Family A's power_source_enum (M4) — DEFAULT blank on the grade axis; carried so every effect-bearing §§7-12 row shares one vocabulary and defect D8 source_dominance can compute | no | |
lifecycle | enum | Family A's lifecycle_enum: active (DEFAULT) · deprecated · retired (M4) | yes | |
superseded_by | self-ref | (axis, rarity_token) of the successor grade (M4) | no | |
imprint_slot_count | int | — (RN_0018: 1/2/3/4) | on axis=item_grade | |
ability_count | int | — (GDD §12.1: 0/1/2/3/4) | nullable — see OPEN-1 | |
name_rule_ref | string | naming-ladder key (military-rank → generals → war-heroes) | nullable | |
unique_per_type | bool | — | default FALSE | |
obtainable_by_roll | bool | — | yes (the MYTHIC tooth) | |
gate_kind | enum | none · questline · mode · both | yes | |
gate_ref | FK | T0_Questline_Index.questline_id \ | difficulty-tier token | nullable |
roll_window_ordinal | int | — (F3-residue: the "better windows" band; magnitudes 5M) | on axis=item_grade | |
quality_distribution | enum | uniform · centered · top_weighted | on axis=item_grade | |
shape_token | string | the COLOUR-INDEPENDENT read glyph key | yes | |
colour_ref | string | palette key (never the sole channel) | nullable | |
pickup_sfx_id_ref | FK | T0_SFX_Registry.sfx_id | nullable | |
beam_vfx_ref | string | VFX key | nullable | |
card_layout_ref | string | UI layout key (rank-20 consumer) | nullable | |
source_ref | string | canon citation | yes | |
notes | string | incl. examples_not_limits | — |
Seed rows (the ratified ladder, verbatim-mapped).
PK (axis, rarity_token) | ordinal | imprint_slot_count | obtainable_by_roll | gate_kind | source |
|---|---|---|---|---|---|
(item_grade, common) | 1 | 1 | TRUE | none | RN_0018; §13.1 |
(item_grade, rare) | 2 | 2 | TRUE | none | RN_0018; §13.1 |
(item_grade, epic) | 3 | 3 | TRUE | none | RN_0018; §13.1 |
(item_grade, legendary) | 4 | 4 | TRUE | none | RN_0018; §13.1 |
(item_grade, mythic) | 5 | 4 | FALSE | questline | §12 "never produced by a drop table or an imprint roll"; §5I.4 |
(creature_spawn, legendary) | — | — | FALSE | none | CVD §17.12 firewall (33 species / 22 slots) |
ability_count is authored BLANK on all six pending OPEN-1.
Two further landing rows the reconciliation requires, both free: (item_grade, uncommon) and
(item_grade, signature) — the 11 live Flora/Mineral cells that Family A's first-draft ladder
could not express. Ordinals interleave without touching the ratified 1/2/3/4 slot scheme:
uncommon sits between common and rare and carries imprint_slot_count BLANK pending OPEN-1
(the 4-vs-5 grade question is exactly this row), signature is obtainable_by_roll = FALSE and
unique_per_type = TRUE. Neither invents a value: both tokens are already in the data.
The axis=pool_option rows Family A reads are authored in the same landing, one per token in use.
Consumer. the imprint foundry (slot count per item) · T0_RNG_Drop_Table (which grade a row may
emit) · the §15 build-space sweeps (grade ceiling per pool option) · rank-20 UI (the read channels) ·
the loot-read audio/VFX pipeline.
Gate / tooth additions.
fk_spec.json: new registry entry — registry + tab + composite "primary_key": ["axis","rarity_token"] (the T0_Thread_Grid precedent) +
id_formats.rarity_token (FULLMATCH alternation over the 8 tokens, min_values 6) +
foreign_keys. Entry shape per the verified live schema (n2): every FK object carries column /
references_registry / references_tab / references_column / min_tokens, plus
multi_value_separator on delimited columns and _note where the axis constraint is documented
rather than encoded.
harness/check_rarity_firewall.py (teeth class = check_entity_taxonomy.py): (a) no row in T0_Familiar_Registry or T0_Creature_Roster carries an axis=item_grade value;
(b) mythic never appears in any creature-side column;
(c) no T0_RNG_Drop_Table row references a grade whose obtainable_by_roll=FALSE —
this turns §12's prose invariant into a gate-able fact;
(d) every populated rarity_class cell across Rune/Flora/Mineral resolves to a rarity_token
at axis=item_grade (the creature-F8 reconciliation half, now satisfiable because the ladder
carries uncommon and signature);
(e) the affix-side twin of (c) (m9): no T0_Imprint_Affix.rarity_token_min names a grade
with obtainable_by_roll = FALSE unless the row carries hand_authored_override = TRUE — an
affix reachable only through a roll that can never produce it is a dead row that reads as
authored content. Family A's T-A4 clause (b) is the pool-side twin of the same guard;
(f) every referencing column resolves against the (axis, rarity_token) pair declared for it —
item columns to item_grade, Family A's pool columns to pool_option, creature columns to
creature_spawn. This is what keeps one token vocabulary from becoming three by drift.
Zero-token, permanent. Positive control: the creature-collection lens confirmed
grep -rn "rarity" harness/*.py docs/fk_spec.json = 0 today, so this gate is genuinely unbuilt.
OPEN-1 (surfaced, NOT ruled here — numerical canon, routes to the rank-18 bundled brief).
docs/original_predclaude/extracted/GDD_Humanity_Game_Complete.md §12.1 authors a FIVE-grade
ladder at ability-counts 0/1/2/3/4 with UNCOMMON present and a naming rule (military-rank → generals
→ war-heroes); the ratified scheme (RN_0018, §13.1) is a FOUR-grade ladder at slot-counts 1/2/3/4
with UNCOMMON dropped. The M2 reconciliation does NOT pre-empt this ruling: the uncommon
TOKEN must exist because 10 live Flora/Mineral cells already carry it (firewall class (d) cannot
pass otherwise), and its imprint_slot_count is authored BLANK — whether uncommon confers a
slot rung is precisely question (a), and it stays Josh's. Provenance: docs/original_predclaude/INGEST_GDD.md L141 (RECOVERED-NEW),
work order L185, queued in ledger 2026-07-24q, absent from the ranked 30. Two questions: (a) four
grades or five; (b) is ability_count the same axis as imprint_slot_count or a second axis on one
grade. The schema above is deliberately neutral on both — resolving either is an additive ROW
edit (insert an uncommon row, shift ordinals, populate ability_count), never a schema change.
---
T0_Imprint_Affix — the rollable affix definitions (NEW registry · 26 columns)Why it exists. bl-loot F2 SURVIVES (mechanism refuted, schema gap real): RN_0018 fixes slot
COUNTS and nothing types a SLOT. Four untyped slots drawing one flat pool make "four copies of one
affix" and "four freely-stacking best-in-slot affixes" legal by schema; the §15 sweeps would find it
*after* the freeze. docs/proposals/systems/combat-and-spellcraft.md §C5 L330 shows the affix pool
is an unruled Josh item that fell out of the queue — RN_0018 ratified source, slot counts and the
re-roll model but NOT the pool. bl-loot F9 SURVIVES: per-class enumerated pools are an authoring wall
(72 weapons × 12 tiers alone ≈ 864 pools), and the answer is an eligibility PREDICATE, evaluated by
the shipped ConditionExpr engine (docs/CONDITION_EXPRESSION_GRAMMAR.md [ACTIVE v1.0] +
harness/condition_expr.py + Source/Humanity/Public/Quest/ConditionExpr.h) — an existing evaluator
with a new operand set, not a new architecture.
Parent / relation. Child of T0_Rune_Registry — the enchantment lane, anchor RN_0018
(rune_lane = enchantment). NOT the operator lane (RN_0016/RN_0017), never conflated (§13 opening).
Id format. affix_id = IMP_\d{4}. min_values floor set at the landed row count.
| column | type | enum / FK | required |
|---|---|---|---|
affix_id | id | IMP_\d{4} | yes (PK) |
affix_name_text | FText | — | blank → natural-voice pass |
string_status | enum | Family A's string_status_enum, DEFAULT placeholder (M4) | yes |
rune_anchor_ref | FK | T0_Rune_Registry.rune_id (RN_0018) | yes |
slot_axis | enum | per-family, closed: edge · haft · binding · pommel · barrel · stock · sight · weave · ward · lining · vessel · reagent · fuse | yes |
applies_to_tags | delimited list | predicate operands: weapon_class · weapon_subclass · damage_type · element · equip_slot · item_class · trade · alignment | yes |
excludes_tags | delimited list | same vocabulary | nullable |
eligibility_expr | string | CONDITION_EXPRESSION_GRAMMAR expression (the reuse) | nullable |
conflict_group | string | two affixes in one group never co-roll on one item | nullable |
stack_rule | enum | unique · additive · diminishing | yes |
exclusive_with_refs | delimited list | FK → T0_Imprint_Affix.affix_id | nullable |
rarity_token_min | token FK | T0_Rarity_Grade.rarity_token @ axis=item_grade (M2 — no RG_ surrogate) | yes |
bonus_axis | enum | damage · resistance · crit · resource · timing_window · utility · proc | yes (sekiro F4) |
spec_purpose_ref | FK | T0_Spec_Purpose_Registry.purpose_id (Family A owns the roster — M3). The six seeds resolve directly: SPEC_VRIL_HEAL_PERSONAL · SPEC_VRIL_HEAL_AREA · SPEC_VRIL_DAMAGE_PERSONAL · SPEC_VRIL_DAMAGE_AREA · SPEC_VRIL_REGEN_RATE · SPEC_HEAL_OVER_TIME | nullable |
role_axis_ref | FK | T0_Role_Axis_Registry.role_axis_id (Family A owns the roster — M3): ROLE_GROUP_SUPPORT · ROLE_GROUP_HEAL · ROLE_GROUP_DAMAGE · ROLE_SINGLE_SUPPORT · ROLE_SINGLE_HEAL · ROLE_SINGLE_DAMAGE (+ ROLE_GROUP_TERRAIN, Tier-C) | nullable |
power_source | enum | Family A's power_source_enum, DEFAULT gear_imprint (M4) — the column defect D8 source_dominance needs to see the gear side at all | yes |
lifecycle | enum | Family A's lifecycle_enum, DEFAULT active (M4) | yes |
superseded_by | self-FK | T0_Imprint_Affix.affix_id (M4 — deprecate-never-delete, combinatorial F9) | no |
roll_scale_class | enum | flat · scaling · binary | yes |
roll_window_override | JSON blob | {rarity_token: [min_ord, max_ord]} — exceptions only (M2: keyed on the shared token, not a retired RG_ id) | nullable |
lockable | bool | — (the ~5-re-roll lockable-affix floor) | default TRUE |
hand_authored_override | bool | signature/bespoke affix, exempt from predicate derivation | default FALSE |
magnitude_ref | ref | T0_Balance_Constant_Registry.constant_id — Family A's target, one column name one contract (M8) | blank at landing |
care_firewall_flag | enum | none · review (mirrors T0_Rune_Registry) | default none |
source_ref | string | canon citation | yes |
notes | string | incl. examples_not_limits | — |
Seed rows (Josh's verbatim §C5 seeds + the GDD's authored typed-pool axis).
| affix_id | name (blank, voice-pass) | slot_axis | applies_to_tags | bonus_axis | source | |
|---|---|---|---|---|---|---|
| IMP_0001 | fire-affix grenade | vessel | item_class=grenade | damage | combat-and-spellcraft §C5 (Josh seed) | |
| IMP_0002 | homing weapon-enchant | edge | weapon_class=* | utility | §C5 (Josh seed) | |
| IMP_0003 | lifesteal poison | reagent | item_class=poison | resource | §C5 (Josh seed) | |
| IMP_0004 | chain-lightning bomb | fuse | item_class=bomb | proc | §C5 (Josh seed) | |
| IMP_0005 | catch-window width | binding | `defensive_class=parry\ | catch` | timing_window | Combat Spec §12 Lane-A named exemplar |
IMP_0005 is deliberately the fifth seed: §12's own named bonus kind ("catch-window width on the parry
weapons") is unauthorable until §1.7's weapon defensive_class exists — the two land together.
Reuse anchor recorded (bl-loot F9 refuter): Josh's GDD §12.1 already authors a typed pool axis —
"four good and four evil weapon abilities available for that weapon type" — the first
authored applies_to_tags[] instance, a (weapon_type × alignment) partition. The seed axis set is
harvested from there and from weapon_subclass, not invented.
Consumer. the imprint foundry (roll resolution) · T0_RNG_Drop_Table (payload) ·
T0_Equipment_Registry / T0_Weapon_Registry (slot_axis_set match) · **the §15 build-space
sweeps** — a pool is a QUERY over this table (pool_ref derived, never stored), which is the only
shape under which the "never-picked pool option = balance defect" tooth is cheap; the sweeps key
affix_id as a subject_ref on the per-option scorecard rows and populate keep_rate there
— combinatorial F7's rolled-layer analogue of pick share, which neither design carried before
reconciliation (m3; Family A §7.5 is the home) · rank-20 UI.
Gate / tooth additions.
fk_spec.json: PK affix_id, id_formats.affix_id = IMP_\d{4}, FK rune_anchor_ref → T0_Rune_Registry.rune_id, FK rarity_token_min → T0_Rarity_Grade.rarity_token (with the
axis=item_grade constraint in the _note), FK role_axis_ref →
T0_Role_Axis_Registry.role_axis_id and FK spec_purpose_ref →
T0_Spec_Purpose_Registry.purpose_id (Family A's registries, M3), FK
exclusive_with_refs (multi-value, self-referential, min_tokens at the landed count).
check_rarity_firewall.py (same script, second class): every eligibility_expr parses under harness/condition_expr.py (reuse the shipped reference evaluator —
a malformed predicate must fail loudly, not silently match nothing: the
a-search-that-cannot-match-reports-zero class).
conflict_group / exclusive_with_refs symmetry check (A excludes B ⟹ B excludes A).---
T0_RNG_Drop_Table — population schema + the imprint-loot extension group (10 added columns)Why. bl-loot F4 SURVIVES: the ACTIVE T1_RNG_Sigma_Constraint_System §5 declares four consumer
fields "on every RNG-bound row"; the live 18-column header renames two (sigma_base_rate →
base_rate_mean, sigma_reset_on_drop → reset_condition) and is missing two
(sigma_window_length, sigma_constraint_enabled). Source/Humanity/Public/Data/RngDropTableRow.h
mirrors the CSV verbatim (18 columns, 0 rows at generation), so neither is first-class today (they
*could* ride the extensions JSON rail — the finder's "provably cannot read" was over-claimed — but
a runtime gating boolean read by three pipeline contracts belongs in a typed column). F10 SURVIVES
NARROWED: σ bounds each table, nothing bounds the aggregate; the budget must be scoped to ROLLED
events or it collides with the easter-egg hand-siting doctrine. F6 SURVIVES: mode_gate must be an
explicit enum, never a per-difficulty-tier roll multiplier (the ruled ladder's dial "is ONLY vril
density"). F7 SURVIVES-CORRECTED: the boss join already exists in reverse (linked_entity_id +
linked_entity_type) and rng_drop_anchor is the established anchor-column NAME on four registries —
do not mint drop_table_ref.
Vector B (columns, not rows) — justified: every one of these is exactly one value per drop row.
| column | type | enum / FK | default (reproduces current canon) | ||
|---|---|---|---|---|---|
sigma_constraint_enabled | bool | — | TRUE (today's unconditional constraint) | ||
sigma_window_length | int | ordinal band; magnitude 5M | blank | ||
drop_table_group | string | the σ-counter SCOPE key (§8's per-drop-table rule made addressable — expressing the ruling, not re-ruling it) | required on landing rows | ||
acquisition_source | enum | **reuse the T0_Rune_Registry column NAME *and its live vocabulary* (m7): operator-library · crafted · crafted-and-bounded-drop · boss-drop · part-break-carve · easter-egg-hand-placed · milestone · quest-guaranteed · found-exploring · purchased. Reconciled: the first draft omitted operator-library — the DOMINANT live value (17 of 19 populated cells; crafted-and-bounded-drop ×1, blank ×1) — and mixed hyphen-case with snake_case inside one closed set. Reusing a column name means reusing its vocabulary, and no live cell moves, so hyphen-case wins for this column only**; every other enum in this design stays snake_case | required | ||
cadence_class | enum | ambient · encounter · milestone · boss · hand_placed | required | ||
mode_gate | enum | none · forgotten_one | none | ||
rarity_class | token FK | T0_Rarity_Grade.rarity_token @ axis=item_grade (M2 — the shared ladder, no RG_ id) | required | ||
payload_ref | FK | T0_Imprint_Affix.affix_id \ | T0_Equipment_Registry.equip_id \ | T0_Weapon_Registry.weapon_id | nullable |
payload_kind | enum | affix · equipment · weapon · material · none | none | ||
part_ref | string | the hitzone_parts part name this drop rides (§13.4 carve loop) | nullable |
linked_entity_id / linked_entity_type are hereby DOCUMENTED as the drop SOURCE binding (the
schema-dictionary entry is part of this landing) — payload_ref/payload_kind are the dropped THING.
The two were conflatable in an 18-column, 0-row table; they are not after this.
Invariants stated as data, not prose: `acquisition_source ∈ {easter-egg-hand-placed,
quest-guaranteed} ⟹ sigma_constraint_enabled = FALSE` (§8's exemptions + §13.4's hand-siting);
cadence_class = hand_placed ⟹ counted for visibility, never gated by the cadence budget.
Seed rows — the first imprint-roll tables (per rarity, per §13.5 "imprint-roll tables per rarity").
| drop_id | drop_table_group | acquisition_source | cadence_class | rarity_class | payload_kind | sigma_constraint_enabled |
|---|---|---|---|---|---|---|
| DROP_IMPRINT_COMMON | imprint_roll | boss-drop | boss | common | affix | TRUE |
| DROP_IMPRINT_RARE | imprint_roll | boss-drop | boss | rare | affix | TRUE |
| DROP_IMPRINT_EPIC | imprint_roll | boss-drop | boss | epic | affix | TRUE |
| DROP_IMPRINT_LEGENDARY | imprint_roll | boss-drop | boss | legendary | affix | TRUE |
| DROP_IMPRINT_CARVE | imprint_carve | part-break-carve | boss | (per part) | affix | TRUE |
| DROP_IMPRINT_EASTER | imprint_hand | easter-egg-hand-placed | hand_placed | (per site) | affix | FALSE |
No mythic row exists in this table by construction — (item_grade, mythic) carries
obtainable_by_roll = FALSE and firewall class (c) fails any drop row that names it.
Each group is its own σ counter, so "a rarity tier's dry streak is bounded independently of any other
table" (§13.2) becomes computable. Note for the mint: the 9 populated
T0_Creature_Roster.rng_drop_anchor values (DROP_BIGGAME_HIDE_MEAT ×7, DROP_FISH_FLESH_ROE ×2)
are DANGLING against the 0-row table — the seed drop_id style above matches theirs so the
rank-26 population pass closes both dangles with one vocabulary.
Cadence budget (F10, scoped). rolled_reward_events_target as an **added column on
T0_Chapter_Index** (owner system imprint-loot), checked as a sum over drop rows whose
cadence_class is a rolled class. Authored placements (hand_placed) are counted for visibility and
never gated. The check is a sweep output, not a runtime — reported beside the §15
viability-breadth scorecard.
Consumer. the drop resolver (runtime) · the σ constraint system (per-drop_table_group counter) ·
the Phase-6 factory (per-boss/per-chapter rows, §13.5) · the sweeps (cadence sum).
Gate / tooth additions.
registry_extensions.json: new system key "imprint-loot" under T0_RNG_Drop_Table/Sheet1 listing all ten columns + under T0_Chapter_Index/T0_Chapter_Index listing rolled_reward_events_target.
fidelity_baseline.json added_columns for both, re-emitted via python harness/registry_fidelity.py --emit-baseline in the same commit (the standing rule).
Tools/gen_row_struct.py regeneration of RngDropTableRow.h (the diff is the review, per REGISTRY_ROW_STRUCT_SPEC.md §5). sigma_constraint_enabled infers bool, sigma_window_length
int32, payload_ref/acquisition_source FString — no override needed.
check_rarity_firewall.py class (c) reads this table.---
T0_Equipment_Registry — the four unhoused imprint targets (NEW registry · 26 columns)Why. bl-loot F8 SURVIVES-REFRAMED: §13 names five imprint targets — "weapons, armour, off-hand
items, grenades and bombs, potions and poisons" — and only weapons have a data home; off-hand rides
the 72 via §6's Sorcery Off-Hand Canonical Lock. No armour/consumable registry exists (verified
programmatically across all 40 registry dirs). REFRAME: the slot layout is **DESIGNED-UNBUILT with a
never-executed routing**, not missing — docs/original_predclaude/INGEST_GDD.md L144 recovers
"16 equipment slots (7 enhanceable element slots + 9 stat slots: glasses / earrings / armlets /
bracers / rings / gloves)" as RECOVERED-NEW with work order L188. "Armour" is the finder's word, not
canon's; the canonical framing is the recovered slot layout.
Id format. equip_id = EQ_\d{4}.
| column | type | enum / FK | required |
|---|---|---|---|
equip_id | id | EQ_\d{4} | yes (PK) |
equip_name_text | FText | — | blank → voice pass |
string_status | enum | Family A's string_status_enum, DEFAULT placeholder (M4/§0.5) | yes |
equip_slot | enum | the recovered 16-slot layout — 7 vril-seat element slots (seat_1..seat_7) + the 6 NAMED stat slots (glasses · earrings · armlets · bracers · rings · gloves) + unassigned_stat_slot ×3 placeholder — see OPEN-2 | yes |
item_class | enum | wearable · offhand · grenade · bomb · potion · poison | yes |
slot_axis_set | delimited list | the T0_Imprint_Affix.slot_axis values this base accepts | yes |
base_rarity_class | token FK | T0_Rarity_Grade.rarity_token @ axis=item_grade (M2) | yes |
craft_trade_ref | FK | T0_Trade_Registry.trade_id (Engineering / Alchemy) | nullable |
recipe_ref | string | recipe key (recipe rows are a later lane) | nullable |
charges | int | consumables only; ordinal | nullable |
consumed_on_use | bool | — | default FALSE |
element_enhanceable | bool | the "7 enhanceable element slots" property | default FALSE |
region_native | FK | T0_Region_Index.region_id | nullable |
canonical_chapter_acquisition | FK | T0_Chapter_Index.chapter_id | nullable |
care_tier | enum | mirrors the house care_tier vocabulary | nullable |
mesh_id_ref … regeneration_trigger (8) | — | the 8-column assetgen rail copied verbatim from the Creature / Character / Familiar / Boss form (which carries mesh_id_ref INSIDE the group), so the P2 AssetGen socket picks equipment up for free. Corrected m8: the T0_Weapon_Registry group is 7 columns — [mesh_pipeline_type, substrate_material_ref_path, mesh_tags, generation_status, generation_prompt_hash, last_generated_timestamp, regeneration_trigger] — because mesh_id_ref sits in Weapon's base header, not its group. A new registry has no base header to inherit it from, so the 8-column form is the right copy and the source is now named | per assetgen |
equipment_sfx_id_ref_array | delimited FK list | T0_SFX_Registry.sfx_id (the sfx-pipeline group idiom) | nullable |
source_ref | string | — | yes |
notes | string | incl. examples_not_limits | — |
Column arithmetic, corrected (m8). The first draft listed 16 domain columns and then reconciled
"15 + 8 + 1 = 24", which closes at neither end. The emitted header is 17 domain columns
(the 16 named above plus string_status from M4/§0.5) + 8 assetgen + 1 sfx = 26. A header
width is a freeze artifact, so it is stated once and it adds up.
Consumer. the imprint foundry (which slot_axis set a base accepts) · loadout sets (a set
references equipment instances) · the crafting lane (trade gates) · P2 AssetGen · the sweeps.
Gate / tooth. fk_spec.json entry (PK equip_id, pattern, FKs to Trade/Region/Chapter and the
token FK to T0_Rarity_Grade). **NO registry_extensions.json key is declared for this registry
(M6b).** The first draft added assetgen + sfx-pipeline manifest groups here; T0_Equipment_Registry
is a wholly-added registry with no frozen width, so those keys would carry neither added_columns
nor a stub_superseded entry and each would be an immediate I3 FAIL — verified: not one of the
12 added_registries carries a manifest key today. The assetgen and sfx columns still land as part
of the registry's own header; only the manifest keys are dropped.
OPEN-2 (surfaced, not ruled). The recovered layout names 9 stat slots and enumerates 6.
Three are unauthored detail. The enum above ships six named + three unassigned_stat_slot
placeholders so the count (16) is expressible without inventing three slot names. Routes with OPEN-1
to the rank-18 bundled brief; resolution is a row/enum-value edit, never a schema change.
---
T0_Rune_Registry — 2 added columns, NO manifest key (reconciled M6b)The vector, corrected. T0_Rune_Registry is a wholly-added registry (it is in
added_registries), so it has no frozen reference width to exceed: adding columns to it is
free, it takes no added_columns entry, and it declares no registry_extensions.json key. The
first draft put an imprint-loot group on it, which would have tripped I3 and turned the gate RED
on the landing commit ("manifest key has no added_columns and no stub_superseded entry"). The
columns land exactly as designed; the manifest key does not exist.
Why. bl-loot F11 SURVIVES on the DENOMINATION half (the curve half is KILLED — §13.1 defers "the
exact re-roll cost curve" to Phase 5M, which is DESIGNED-DEFERRED, not a gap). No doc anywhere states
what a re-roll costs in; T0_Region_Index has no medium_of_exchange column, so the value
abstraction a numeric cost would need does not exist even in prospect. A scalar reroll_cost column
would bake the universal coin the economy ruling rejected, by omission. The reroll_model column
already exists on RN_0018 (cheap-rising-lockable-floor) — the denomination belongs beside it.
| column | type | enum / FK | default | |
|---|---|---|---|---|
reroll_cost_material_refs | delimited FK list | T0_Flora_Registry.flora_id \ | T0_Mineral_Registry.mineral_id | blank |
imprint_affix_pool_ref | string | the derived-pool query key over T0_Imprint_Affix (§10 pools apply to imprints too) | blank |
Denominating in materials is region-agnostic by construction under the no-universal-coin ruling,
routes the sink into the Engineering/Alchemy/Inscription trades RN_0018 already names as the lane's
source, and reuses the shipped spend primitive UHumanityGatherLedger::TryConsume(YieldsRef, Count) —
a second caller, not a new economy.
Consumer. the re-roll transaction · the crafting lane · the sweeps (sink pressure).
---
WS_036 inner shape + WS_046 (NEW worldstate row)KEYSPACE NOTE (M1). This row was drafted as WS_043, which Family A also took. The
WS_043+number space is now allocated ONCE, inmint_design_A_build_space.md§6.0 and
reproduced verbatim at §2.2 below: WS_043 bonus_selection_state (A) · WS_044
bonus_respec_ledger(A) · WS_045loadout_set_state(B) · WS_046item_instance_ledger
(B). The draft ids appear in no landed artifact.
Why, and why NOT a registry. bl-loot F1 SURVIVES with two evidence corrections. The doctrine is
per-object mutable state (rolled bonus, re-roll counter, locked-slot mask, owner) and every T0 registry
is a TYPE registry — but the finder's "no home exists anywhere" was wrong: T0_Worldstate_Variables
already carries WS_036 worn_rune_loadout (type object, default {}) and WS_035
spellcraft_authored_spells, already wired downstream (ws.worn_rune_loadout appears in
T0_Boss_Encounter_Registry BE_0015's ws_writes). The correct verdict is **the home exists and is
shapeless.** The registry's own convention is to spell the inner shape out inline in the domain
column (WS_008, WS_012, WS_019, WS_026, WS_027, WS_041 all do). **Do NOT mint a free-floating
T0_Item_Instance registry** — declare the rail as worldstate domains, per the refuter's correction.
Also corrected: the "only inventory-shaped runtime is the GatherLedger" claim is false —
Source/Humanity/Public/Quest/ConditionExpr.h:42 ships TSet<FString> Inventory (a key-FLAG
inventory, no object identity), read at ConditionExpr.cpp:569. The finding survives; the control
was wrong.
WS_036 worn_rune_loadout — declared inner shape (6 sub-fields).
`{ owner_kind (player|companion|familiar), owner_ref, instance_ref (→ WS_046 instance_id),
slot_index, affix_id (→ T0_Imprint_Affix.affix_id), locked (bool) }`
**WS_046 item_instance_ledger — NEW row (per the §6.0 allocation; the registry's max is WS_042).
15 sub-fields.**
| sub-field | type | enum / FK | ||
|---|---|---|---|---|
instance_id | id | II_\d{6} (runtime-minted) | ||
base_type_ref | FK | T0_Weapon_Registry.weapon_id \ | T0_Equipment_Registry.equip_id | |
base_type_kind | enum | weapon · equipment · consumable | ||
rarity_class | token FK | T0_Rarity_Grade.rarity_token @ axis=item_grade (M2) | ||
owner_kind | enum | player · companion · familiar · stash | ||
owner_ref | FK | T0_Character_Index.character_id \ | T0_Familiar_Registry.familiar_id \ | null |
acquired_source | enum | mirrors T0_RNG_Drop_Table.acquisition_source | ||
acquired_chapter | FK | T0_Chapter_Index.chapter_id | ||
reroll_count | int | the ~5-re-roll lockable floor counter | ||
sigma_seq | int | the σ-counter sequence position of the roll that produced this instance, joined on T0_RNG_Drop_Table.drop_table_group (reconciled m3 — combinatorial F7's NET enumerates it, and without it the σ bound cannot be audited per instance; blank on non-rolled acquisitions) | ||
locked_slot_mask | int | bitmask over slot indices | ||
slot_rolls | array of objects | {slot_index, slot_axis, affix_id, rolled_ordinal, roll_percentile, locked} | ||
rolled_ability_refs | delimited FK list | T0_Ability_Tree_Registry.ability_id — the GDD §12.1 requirement ("one/two/three/all four abilities assigned at random from the regional ability pool"; "two players using the same weapon type may have meaningfully different ability combinations based on what dropped for them") | ||
unique_per_type | bool | GDD §12.1 "only one legendary weapon per weapon type in the entire game" | ||
departure_disposition | enum | returns_to_player · leaves_with_ally · player_level_unlock |
departure_disposition is not free design space — docs/spine/DECISIONS_PENDING_JOSH.md L38-40
records the applied interim three-way split verbatim: "intrinsic growth leaves with the ally, gear and
imprint items return per the departure contract, purchased 2nd/3rd loadout-set slots are player-level
unlocks that never depart (Integrity_Paths §8.11.6-§8.11.7)". The rail must express all three, not
just "returns".
Consumer. the inventory/Item-Rift runtime (the diegetic UI frame, T1_Ability_Tree L926, unlocked
Ch 15) · loadout sets (a set references *this rolled sword*, not "sword") · the departure-return
transaction (§8.11.6) · the re-roll transaction · save persistence.
Reuse-first. UHumanityGatherLedger stays the MATERIAL tally (stackable, no identity); this rail
covers only rolled/equippable objects. Two ledgers, two shapes, no overlap.
Recorded, not designed here: the Item Rift unlocks Ch 15, so the **Ch 2-14 slice — the AAAAA
slice — has no authored carry model at all.** That is a design gap for the factory/W10 lane, surfaced
by bl-loot F1 and unrefuted; it is not a schema question and is not resolved by this mint.
Gate / tooth. harness/check_ws_value_form.py — WS_036 and WS_046 domains must be extended in the
SAME commit, or every ws.worn_rune_loadout write becomes an unresolved-head finding. WS_046 must
also be added to the +-prefix legal set — an instance ledger is append/accumulate by nature. That
is a ruled-list edit, recorded as a required companion change rather than assumed.
The legal set's real size, verified this pass (m4 — corrected in both directions). The draft
said CG-25 names six append-legal object variables (WS_008, WS_012, WS_018, WS_019, WS_026,
WS_027); the critic read seven (adding WS_010) off a truncated first line of the constant. The
live constant holds TWELVE:
# harness/check_ws_value_form.py:116-118
APPEND_LEGAL_IDS = frozenset({"WS_008", "WS_010", "WS_012", "WS_018", "WS_019",
"WS_020", "WS_021", "WS_026", "WS_027", "WS_028",
"WS_031", "WS_034"})
with the reason at L112-115: *"EXTENDED 6->12 by Josh's ruling D-CG25-APPEND-SET (2026-07-24)"*,
the contract's CG-25 enumeration reconciled to the data in the same commit. **The landing edit is
therefore 12 → 14** (WS_044 from Family A and WS_046 here; WS_043/WS_045 are keyed-replace
objects and need no +), and the stale "six" in the L20 docstring is fixed in the same pass.
Standing lesson applied: read the constant, not the label — the label was wrong twice.
---
T0_Loadout_Set_Definition — the purchasable slot unlocks (NEW registry · 14 columns)Why a table at all. The SETS' contents are runtime state (Part 2.2); what is authored canon is the
slot unlock: which set ordinals exist, what gates each, where quick-change is legal, and — per
§8.11.6's ratified reading — that the slots are **player-level unlocks that never depart with a
companion**. Three rows, and they carry the rules three runtimes read.
SOLE OWNER of loadout-set canon (reconciled M1/m1). Family A's draft restated the same fact as
WS_044 loadout_set_state {unlocked_count (int, DEFAULT 1, ceiling 3), active_set_index, …} —
a double owner, and the weaker home: this table carries the unlock class, the gates, the venue
class, and the departs_with_companion = FALSE investment-protection invariant. **The ceiling is
the ROW COUNT of this table**, never a number restated elsewhere; A's unlocked_count /
active_set_index collapse into set_id + is_active on WS_045, and Family A keeps
loadout_set_id only as a key INTO that row, which is all its reachability classing needs.
Id format. set_id = LS_\d{2}.
| column | type | enum / FK | required | |
|---|---|---|---|---|
set_id | id | LS_\d{2} | yes (PK) | |
set_ordinal | int | 1 · 2 · 3 (canonical ceiling per §14; derived from the ruling's naming of a second and a third set — revisable at this schema pass, per the spec's own words) | yes | |
unlock_class | enum | default · purchased | yes | |
unlock_cost_material_refs | delimited FK list | T0_Flora_Registry.flora_id \ | T0_Mineral_Registry.mineral_id (no universal coin — same denomination rule as §1.5) | nullable |
unlock_gate_chapter_min | FK | T0_Chapter_Index.chapter_id | nullable | |
unlock_gate_ref | FK | T0_Questline_Index.questline_id | nullable | |
owner_scope | enum | player_level (fixed — the §8.11.6 ratified reading) | yes | |
applies_to_allies | bool | TRUE — "apply across whoever is on the bench" | yes | |
departs_with_companion | bool | FALSE (fixed — the investment-protection invariant, stated as data) | yes | |
switch_venue_class | enum | vril_recharge_zone · waterfall | yes | |
switch_venue_ref | FK | T0_Vril_Site_Registry.site_id (nullable = any site of the class) | nullable | |
switch_in_encounter_allowed | bool | FALSE — derived from the siting rule, and the spec flags the derivation as revisable at this pass | yes | |
composition_kinds | delimited list | gear · abilities · bonus_picks (§14: "its own gear, its own ability selection, and its own bonus picks") | yes | |
source_ref / notes | string | incl. examples_not_limits | yes |
Seed rows.
| set_id | set_ordinal | unlock_class | switch_venue_class | departs_with_companion |
|---|---|---|---|---|
| LS_01 | 1 | default | vril_recharge_zone | FALSE |
| LS_02 | 2 | purchased | vril_recharge_zone | FALSE |
| LS_03 | 3 | purchased | vril_recharge_zone | FALSE |
Consumer. the build/loadout UI (W10 book screens — addendum §9's named owner) · the vril-site
interaction (HumanityVrilRechargeZone already ships as the actor) · the departure-return transaction
· the sweeps (a set is a build container the sweep enumerates over).
Gate / tooth. fk_spec.json entry (PK, pattern LS_\d{2} min_values 3, FK
switch_venue_ref → T0_Vril_Site_Registry.site_id, FK unlock_gate_chapter_min →
T0_Chapter_Index.chapter_id). One invariant assertion: departs_with_companion is FALSE on every
row — a TRUE here would silently invert investment protection.
WS_045 loadout_set_state — NEW worldstate row (6 stored sub-fields + 1 derived)THE WORLDSTATE KEYSPACE ALLOCATION — one table, allocated once (M1). *(Verbatim-identical to
mint_design_A_build_space.md §6.0. Both designs read the registry's max correctly as WS_042 —
verified, 42 rows, added_rows 17 — and both then took 043.)*
| id | variable_name | owner | append-legal (CG-25) | shape |
|---|---|---|---|---|
WS_043 | bonus_selection_state | Family A | no (keyed replace) | key (capability_kind, capability_ref, tier_key, lane, loadout_set_id) → {option_id, selected_at_beat_ref, pick_cost_charged, exclusion_groups_held[]} |
WS_044 | bonus_respec_ledger | Family A | yes | {event_id, respec_kind, pool_id, from_option_id, to_option_id, cost_paid_ref, site_ref, refundable} |
WS_045 | loadout_set_state | Family B | no (keyed replace) | below |
WS_046 | item_instance_ledger | Family B | yes | §1.6's 15 sub-fields |
Stored: `{ owner_kind (player|companion|familiar), owner_ref, set_id (→
T0_Loadout_Set_Definition.set_id), gear_instance_refs[] (→ WS_046.instance_id),
ability_selection_refs[] (→ T0_Ability_Tree_Registry.ability_id), is_active (bool) }`
Derived, NOT stored (M1): bonus_pick_refs[] = the read of WS_043 filtered on
loadout_set_id = set_id. The draft stored them here while Family A stored the same selection
state under its own key — the selection-state shape was double-owned, which is worse than an id
clash. §14's "its own gear, its own ability selection, and its own bonus picks" is preserved by
T0_Loadout_Set_Definition.composition_kinds; what is removed is the second copy. One writer, one
home: Family A writes bonus selection, Family B writes gear + ability selection + is_active.
Companion parity is the same shape with owner_kind=companion — §8.11.6's "a companion has gear
slots, an ability loadout, the selectable minor and major bonus picks … and the purchasable second and
third loadout sets." No mirror table; one shape, an owner discriminator. That is what makes parity a
schema property rather than an authoring promise.
WS_028 extension — the return transaction (2 sub-fields)WS_028 companion_loyalty_transition_events today carries `{character_id, event (DRAWN|WAVERING|
DEPARTED|REBONDED), chapter, integrity_at_event, rerouted_paths[]}`. Add:
returned_instance_refs[] (→ WS_046.instance_id) and retained_by_ally_refs[]. The DEPARTED event is
where the transaction lands (§8.11.6: "The return transaction rides the DEPARTED event at WS_028
beside rerouted_paths"). REBONDED returns the ally "as who they were" — spread/specialties/style
persist; re-equipping is the player's choice again, i.e. returned_instance_refs[] is not
auto-reversed.
Gate / tooth. check_no_dead_end.py already asserts every DEPARTED event carries a non-empty
rerouted_paths[]. Add the sibling assertion: every DEPARTED event whose ally held instances with
departure_disposition = returns_to_player carries them in returned_instance_refs[]. Same teeth
class, same script.
---
Two ACTIVE tier docs specify two incompatible six-stat vectors for the same entity:
T1_IPW §8.11.7 rules the canonical six attributes (Strength, Dexterity, Constitution, Agility,
Accuracy, Intelligence) with "No new attribute is minted for allies"; T3_Familiars_Named
§1.7 specifies the Garden six (Strength; Flight/Swim/Speed; Perception; Endurance; Bond; Specialty) —
no Intelligence — and that is the vector the Familiar Kindergarten, the per-stat training
minigames, and the sigma-bounded generational husbandry gains all key to. T0_Familiar_Registry's 40
columns carry zero stat fields, so neither vector has data today: this mint is the irreversible
moment.
The refuter's CORRECTION binds: the Garden scale has a second, non-ally consumer — §1.7's
husbandry loop runs on the 400-plus ordinary tameable roster owned by T3_Creatures_Tameable, not
on the 22 familiars. Collapsing the Garden six into "derived channels over the six attributes" would
drag the protagonist attribute roster onto 400+ ordinary creatures. **The designed shape is two
namespaced scales plus an explicit bridge on the 22 entities that sit in both:**
ally_stat_block {str, dex, con, agi, acc, int} # allies + familiars, per IPW §8.11.7
husbandry_stat_block {the Garden six} # creature-side, per T3 §1.7, sigma-bounded
garden_channel_map channel_id -> ally_attr_expr # the bridge the Kindergarten writes through
specialty_ref role/spec tag, NOT a 7th stat
ally_stat_block lands on WS_012/WS_027 (§3.1, §3.2); husbandry_stat_block stays on WS_037
husbandry_menagerie_state; garden_channel_map is a small mapping declared in the WS_012 domain.
Suggested bridge seeds (the mapping is nearly free): locomotion → Agility; Endurance → Constitution;
Perception → a derived read over (Intelligence + Accuracy) preserving its vril-sight contribution;
Strength → Strength; Specialty → specialty_ref; Bond → its own non-attribute track (§3.2's
bond_value).
Required companion edit, flagged not applied: T1_IPW L725 records the defect as a *missing
reciprocal cite*, not as a rival vector, and supplies no mapping — so T3_Familiars_Named §1.7 and
T1_IPW §8.11.7 must stop specifying rival vectors in the living source at the same commit as
this mint (the source-docs-are-living rule). That is a DIRECTOR reconciliation, not an authoring task.
WS_027 companion_roster_state — the ally-build extension (10 added sub-fields)§8.11.6 states it outright: "Persistence rides the per-companion roster object at WS_027 … which will
carry loadout state alongside the loyalty fields once the schema pass authors the columns; until
that pass lands, no consumer may read a loadout field on WS_027, because the field does not yet
exist." This is that pass.
| sub-field | type | enum / FK | default |
|---|---|---|---|
ally_stat_block | object | {str,dex,con,agi,acc,int} — the canonical six, ally-side | authored spread at join |
ally_level | int | — | 1 |
stat_point_budget | int | the total spread points an ally may hold at ally_level — ordinal; the per-level grant CURVE is a Phase-5M magnitude and lands as a T0_Balance_Constant_Registry ref, blank until then (reconciled M9) | blank |
unspent_level_choice_points | int | the player's level-up choice budget | 0 |
level_choice_log | array | [{level, attribute, chapter}] — "the player makes the stat choices at each ally level-up" | [] |
derived_specialty_refs | delimited list | DERIVED from the spread, never assigned (§8.11.7 property 2) | derived |
combat_style_ref | enum | frontline · precision · sustain · control · skirmish (seed set, examples-not-limits) | derived |
int_tier | int | ordinal band on the twelve-tier numerical scale — the discrete band the behaviour tree selects on | derived from int |
equipment_influence_snapshot | object | the §8.11.7 property-1 continuity: gear + imprints shift the profile without a respec | {} |
active_loadout_set_id | FK | T0_Loadout_Set_Definition.set_id | LS_01 |
Discipline stated as schema: intelligence is a BUILD property, never an integrity property
(§8.11.8) — int_tier must never appear as an input to the loyalty arithmetic. The
loyalty_belief_axis / loyalty_departure_threshold fields are untouched by this extension, and the
gate below asserts it.
**THE §12 TRADEOFF IS SCHEMA-EXPRESSED, not just prose (reconciled M9 — the one §§7-12 ruling that
had no home in either family).** Addendum §12 rules it verbatim and mandatorily:
THE BALANCE TRADEOFF IS MANDATORY: "can't all be in intelligence or they won't be very
strong" — INT competes with the combat stats, so the player has to balance and customize per role.
As first drafted, nothing bound the six stats to a shared budget and no tooth asserted one: an ally
could be maxed in INT and in every combat stat, which makes the ruled competition unenforceable
and silently deletes the axis §10's build-space sweeps are supposed to vary
(unspent_level_choice_points is a spend counter, not a cap). stat_point_budget is that cap, and
the tooth is the ally-side twin of Family A's T-A12:
T-B1 (ALLY BUDGET COHERENCE). sum(ally_stat_block values) > stat_point_budget FAILs;
spent + unspent_level_choice_pointsmust reconcile against the budget atally_level; the
budget is blank-tolerant until Phase-5M sets the curve, and the tooth is armed the moment it
populates. Host: harness/check_ally_build.py (§3.5).
The ordinal is a ratified count, not a magnitude, so it sits inside the shared firewall's clause 2;
only the per-level grant curve defers.
WS_012 familiar_bond_state — the ally + slot-history extension (12 added sub-fields)Today: `{familiar_id, bond_tier (approach|accepted|mastery), last_interaction_chapter, bond_quality
(realized|footprints|trace_only)}`, 99 writes across 38 nodes in the executed spine.
Creature-lens F2 SURVIVES-STRONGER: the deferred familiar-bond-ladder extension keys on a
5-rung bond_level (bond-formation / Bond-25 / Bond-50 / Bond-75 / Bond-100-Mastery = 110 rows)
while §8.11.5's ruled gating sentence names 3 values (approach, accepted, mastery). Two ACTIVE
canon artifacts specify different arities for the same gate. Creature-lens F3 SURVIVES: the Ch-58
recognition reads a slot-substitution history nothing records — slot_code (`W1..W7, G1..G7, F1..F7,
X22`) is STATIC identity, not runtime occupancy; there is no active-slot state anywhere.
| sub-field | type | enum / FK | default (back-compatible) |
|---|---|---|---|
bond_value | int | the twelve-tier numerical scale, 0..200 (canon-grounded: §1.6 maps formation→Neophyte 1-9 … Bond 100→Philosophus 99 + Adeptus Minor 100, extending to Adeptus Major 101-150, Adeptus Exemptus 151-199, Magister Templi 200) | derived from current bond_tier |
bond_tier | DERIVED view | approach · accepted · mastery over bond_value thresholds | all 99 existing spine writes keep resolving unchanged |
active_slot | enum | ground · flying · water · path_variable · resting | resting |
slot_occupancy_role | enum | primary · sustained_base | primary |
first_bonded_in_slot | bool | the "never replaced" recognition input (§1.2) | TRUE at first bond |
substitution_count | int | rotation-history depth | 0 |
abandonment_events | int | §1.8's permanent consequence, currently unrecorded | 0 |
ally_stat_block | object | {str,dex,con,agi,acc,int} — §8.11.7 "and familiars" | authored at bond |
stat_point_budget | int | the same §12 cap as WS_027, familiar-side — INT competes with the combat stats here too, or the ruled tradeoff has a hole exactly one entity class wide (M9); tooth T-B1 | blank |
int_tier | int | ordinal band (same scale as WS_027) | derived |
element_affinity_developed | enum | the canonical eight-element roster (T1_Ability_Tree L25: five vril-seat + three trade-unlocked) — typed as the affinity a familiar develops toward, per §1.2's "Familiars do not possess vril capabilities at initial bond; vril-integration develops as the player teaches and imbues" | blank |
vril_integration_state | enum | latent · developing · integrated | latent |
The Shell W1 case (from the kills-audit nuance, not assumed away). §6.1 closes: "Shell W1 is the
exception: as sustained-bond, Shell is always present when the Water slot is active. The other six
Water familiars rotate around Shell's always-present base." That reads as Shell occupying the
Water slot *alongside* a rotational water familiar — so active_slot cannot be a single-valued enum
per familiar without the companion flag. slot_occupancy_role is that flag. Whether the Water slot
can hold two occupants is a one-line confirmation the mint must make against §6.1 before landing;
the schema supports both readings, the data must state one.
Companion edit required at the same commit (creature-lens F2's added line). The deferred table's
5-value bond_level enum and §8.11.5's 3-value gating sentence must be reconciled **in the living
source**, or the mint hard-codes the contradiction into fk_spec.json. The design below carries BOTH
(bond_level enum for authoring + bond_threshold int for gating) so the reconciliation is a
living-source wording edit, not a schema migration.
T0_Familiar_Bond_Ability — the deferred bond_abilities table, minted (NEW registry · 15 columns)Why now. docs/registry_extensions.json _deferred.familiar-bond-ladder has specified this table
and its COPY-ONLY discipline since it was registered; every one of the 22 T0_Familiar_Registry rows
carries a blank abilities_granted and a notes pointer to it. Addendum §11 names it outright
("the deferred bond_abilities table is this system's data home"); §8.11.5 and T1_Ability_Tree
§14A.3 both cite it as the home for the bond-gated capability grant.
Id format. bond_ability_id = BA_\d{4}. Shape: 22 familiars × 5 rungs = 110 rows.
| column | type | enum / FK | required |
|---|---|---|---|
bond_ability_id | id | BA_\d{4} | yes (PK) |
familiar_id | FK | T0_Familiar_Registry.familiar_id | yes |
bond_level | enum | bond_formation · bond_25 · bond_50 · bond_75 · bond_100_mastery | yes |
bond_threshold | int | the numeric gate on the twelve-tier scale (0..200), the computable left-hand side §8.11.5 lacks | yes |
ability_name_text | FText | — | blank → voice pass |
string_status | enum | Family A's string_status_enum, DEFAULT placeholder (M4/§0.5) | yes |
ability_effect_summary | string | COPY-ONLY from T3_Familiars_Named §2-§5 per-slot Abilities blocks | yes |
grants_class | enum | passive · active · paired_operation · summon_ultimate | yes |
eam_pair_ref | FK | T0_Ability_Tree_Registry.ability_id (the bond-gated EAM pairing availability, §8.11.5) | nullable |
element_affinity_required | enum | the eight-element roster | nullable |
command_permission | enum | none · directed_with_trust_threshold · full — §1.4's integrity-gated command model, verbatim: L1/L2 "the player cannot override this behavior", L3 "can be directed to attack but with a trust threshold", L4/L5 "the player can attack with their familiar from the beginning of battle" | yes |
integrity_band_gate | enum | L1 · L2 · L3 · L4 · L5 (the §1.4 gate) | nullable |
path_variable_resolution | string | the 22nd-slot path branch (§5.7) | nullable |
source_ref | string | COPY-ONLY citation — no invention | yes |
notes | string | — | — |
Seed rows. COPY-ONLY from T3_Familiars_Named §1.6 (the rung→tier mapping) plus the per-slot
Abilities blocks (§2 W1-W7, §3 G1-G7, §4 F1-F7) and §5.7 (the path-variable 22nd slot). The extension
manifest's own words: "COPY-ONLY when minted — no invention." First authored rows are the W1 Shell
ladder, because §6.1's sustained-bond exception makes it the case that exercises both bond_level and
slot_occupancy_role.
Consumer. the bond-gate resolver · the EAM pairing availability check · the Ch-58 reunion
dispatcher · T1_Ability_Tree §14A.3 (familiar choice as an ability-build input) · the sweeps ("a
familiar whose bond ladder supports no viable build is a balance defect in the same sense that a
never-picked pool option is").
Gate / tooth. fk_spec.json: PK bond_ability_id, id_formats.bond_ability_id = BA_\d{4}
min_values 110, FK familiar_id → T0_Familiar_Registry.familiar_id, FK eam_pair_ref →
T0_Ability_Tree_Registry.ability_id. Coverage tooth (host: harness/check_ally_build.py,
§3.5 — the first draft left this tooth without a named script): **every one of the 22 familiar_ids
has all 5 rungs** (22×5=110 exactly — a coverage assertion, not a floor), and every
T0_Familiar_Registry.abilities_granted blank is now backed by a real join. Also: remove the
_deferred.familiar-bond-ladder block from registry_extensions.json in the same commit, or the
manifest points at a home that exists (a stale pointer is the defect class this project keeps catching).
T0_Familiar_Registry — the ally-build extension (4 added columns · Vector C)Vector, corrected (M6a). T0_Familiar_Registry/Sheet1 is in fidelity_baseline.stub_superseded,
not in added_columns, and carries none of the 19 extension_hashes keys. So these four columns
land by extending its candidate_header and re-pinning it — check_registry_extensions.py
L59-69 satisfies a manifest key with no added_columns only when every manifest column appears in
stub_superseded[key].candidate_header. The ally-build system key still names the four columns
(I2: exactly one owner); what changes is where the width is declared.
| column | type | enum / FK | default |
|---|---|---|---|
element_affinity | enum | the eight-element roster — the affinity this familiar develops toward (see OPEN-3) | blank |
role_tag | delimited FK list | T0_Role_Axis_Registry.role_axis_id (Family A's roster — M3): ROLE_GROUP_SUPPORT · ROLE_GROUP_HEAL · ROLE_GROUP_DAMAGE · ROLE_SINGLE_SUPPORT · ROLE_SINGLE_HEAL · ROLE_SINGLE_DAMAGE (+ ROLE_GROUP_TERRAIN) | blank |
bond_ability_ref_floor | FK | T0_Familiar_Bond_Ability.bond_ability_id (the formation rung) | per row |
is_sustained_base | bool | Shell W1's always-present property (§6.1) | FALSE (TRUE on W1) |
role_tag matters independently of the element question: rank 14 mints role tags for pools and specs;
nothing in the plan gives them to familiars, yet §14A.3 and §15 explicitly enter familiar choice into
the build-space sweeps. A sweep cannot count a role a familiar cannot be tagged with.
OPEN-3 (surfaced, not ruled — the second, smaller DIRECTOR call). §1.2 promises "Each familiar
type carries elemental associations within its category" and never assigns one across all 22 slot
entries; T0_Familiar_Registry has no element or affinity column (both zeros positive-controlled).
Two branches: (a) author 22 values grounded in each species' cultural substrate (Shell/Kasa
water-ancient, Khan earth-apex, Koi water→dragon cross-domain, Raven air-trickster) — a one-pass
factory job with citations; or (b) retire §1.2's promise in the living source. The lens recommends
(a); the column above supports either (nullable if (b) is ruled). Note the EAM leg is WEAKER than the
lens first framed it — §8.11.5 could gate *the player's* pairings without the familiar carrying an
element of its own — but the primary leg (an undischarged canon promise + §15's "familiar choice
shapes the ability build" having no data expression) stands.
T0_Ally_Behavior_Policy — the INT-tier AI schema (NEW registry · 21 columns)Why, and the CORRECTION that must be applied. Creature-lens F5 SURVIVES **WITH A MATERIAL
CORRECTION**: the finder proposed adding int_tier to the ratified fallback key
(companion_id, relationship_state_bucket) — do not. `RUNTIME_STRATEGY_TOKEN_SCHEMA
[RATIFIED v1.0] §3.4 and RUNTIME_GENERATIVE_LAYER` §A.4 rule combat zero-model ("in a fight the
companion/familiar is a GOAP/utility agent at 60 Hz … Deterministic executor owns: the entire combat
loop"), so that key belongs to the LLM layer's authored default — disposition bias and banter. Putting
a build property there would route INT across a ruled firewall.
The policy table is keyed by the DETERMINISTIC executor: (ally_ref, role_tag, int_tier).
combat_disposition remains the LLM's orthogonal *bias* over whatever the deterministic policy
selects. The LLM sets style; the deterministic layer executes actions; INT buys deterministic
behaviour depth. Both rulings preserved.
Id format. policy_id = AP_\d{4}.
| column | type | enum / FK | required | ||
|---|---|---|---|---|---|
policy_id | id | AP_\d{4} | yes (PK) | ||
ally_ref | FK | T0_Character_Index.character_id \ | T0_Familiar_Registry.familiar_id \ | * (role-generic) | yes |
ally_kind | enum | companion · familiar · any | yes | ||
role_tag | FK | T0_Role_Axis_Registry.role_axis_id (Family A's roster — M3; the key is (ally_ref, role_tag, int_tier), so this column is the join the sweeps use and a bare enum would not resolve against a pool row) | yes | ||
int_tier | int | ordinal band on the twelve-tier numerical scale (the KEY field — the spread is continuous, the tiering is discrete) | yes | ||
domain | enum | pattern_detection · proactivity · awareness — §8.11.8's three, verbatim | yes | ||
condition_expr | string | the CONDITION_EXPRESSION_GRAMMAR [ACTIVE v1.0] grammar — has() flag() at() read() ws.key ==/>=/<=/in, &/` | `. NOT a second language. | yes | |
action_ref | FK | T0_Ability_Tree_Registry.ability_id \ | the interpose/LOS-break/stagger verb keys | yes | |
priority | int | ordinal ordering within a tier | yes | ||
autonomy_band | enum | prompted · situational · autonomous — the fraction of the policy set an ally runs unprompted | yes | ||
policy_source | enum | canon_seed · factory_authored — a CLOSED enum with no player-authored member | yes | ||
unlock_int_min | int | the ordinal band at which this row becomes live | yes | ||
disposition_legal_set | delimited list | subset of {protective, aggressive, cautious} — a FUNCTION of integrity band, not the free triad | yes | ||
display_intent_text | FText | the readable "why" (rank-22 bark class renders it) | blank → voice pass | ||
string_status | enum | Family A's string_status_enum, DEFAULT placeholder (M4) | yes | ||
voice_register_ref | FK | T0_Voice_Registry.voice_id — this table's string is a spoken line, so it takes the full Family-A treatment (§0.5) | nullable | ||
power_source | enum | Family A's power_source_enum, DEFAULT ally_spread (M4) — without it defect D8 source_dominance cannot see the ally side at all | yes | ||
lifecycle | enum | Family A's lifecycle_enum, DEFAULT active (M4) | yes | ||
superseded_by | self-FK | T0_Ally_Behavior_Policy.policy_id (M4) | no | ||
source_ref | string | COPY-ONLY where seeded | yes | ||
notes | string | incl. examples_not_limits | — |
THE GAMBITS-BUSYWORK GUARD, expressed structurally. FF12's gambit system degraded two ways:
*busywork* (drip-fed slots, each new party member's whole list re-authored and re-ordered, late-game
editing as spreadsheet maintenance) and *agency loss* ("the game plays itself"). Our ruling already
diverges correctly on the busywork half — **the player invests a stat and picks a role, and never
authors rules.** That divergence is enforced here by construction, not by convention:
1. policy_source is a closed enum with no player member — a player-authored policy row is
unspellable in data.
2. There is no slot_count column and no per-ally row ordering surface — slots-as-a-progression-
resource (FF12's actual busywork engine) has no home, deliberately.
3. The player's only two inputs to this table are int_tier (which rows unlock) and role_tag
(which rows apply). INT unlocks TIERS; the player never scripts a rule.
Seed rows — COPY-ONLY from canon prose that is already condition→action. T3_Familiars_Named
§1.4 is a set of policy rows in prose: "L1/L2: the familiar steps in voluntarily at 50% or less player
health … removes itself once health recovers above 50%." Those are the first authored rows, exactly
like the bond ladder. §8.11.8's three domains supply the seed spread (pattern detection: "recognizing
the counter-class a given attack demands"; proactivity: "pre-positioning before a telegraphed strike
lands, interrupting a cast worth interrupting, peeling a threat off the protagonist or off a
lower-durability ally"; awareness: "tracking adds, environmental hazards, arena changes, and the
states of the rest of the party"). §8.11.8 names them "**the seed set the factory expands per ally and
per chapter**" — recorded per pool.
Consumer. the deterministic ally executor (GOAP/utility @60 Hz — PRE_5090_BUILD_PLAN C1.17) ·
the rank-22 ally combat-line class (renders display_intent_text) · **the Phase-5M/5H build-space
sweeps** — authoring in the one shipped grammar is what makes §10's "ally spreads enter the sweeps"
*measurable* rather than asserted, because the sweeps can READ the rows · Tier-C §8.9 (the same
architecture at raid scale).
Gate / tooth additions — the NEW gate harness/check_ally_build.py (reconciled M9: three
ally-side assertions in this design had no named host script, and the §12 budget tooth needed one).
It is the third and last new gate this family adds, in the teeth class of check_entity_taxonomy.py
— zero-token and permanent. Joint gate roster: 25 today · 26 build_space (Family A) ·
27 rarity_firewall · 28 counter_class_coverage · 29 ally_build.
fk_spec.json: PK policy_id, pattern AP_\d{4}, FK ally_ref (dual-target, soft), FK role_tag → T0_Role_Axis_Registry.role_axis_id (Family A), FK
action_ref → T0_Ability_Tree_Registry.ability_id.
sum(ally_stat_block) > stat_point_budget FAILs onWS_027 and WS_012 alike; blank-tolerant until Phase-5M sets the curve.
condition_expr lints under harness/condition_expr.py — the shipped reference evaluator. Amalformed predicate fails the gate; it must never silently match nothing.
disposition_legal_set validator: reject an out-of-band disposition the same way V1-V5 rejectan out-of-enum goal. Grounding: §1.4 bars an L1/L2 familiar from attacking at all above 50% player
health, so an LLM-emitted aggressive bias in that window is mechanically illegal, and the ratified
triad carries no integrity constraint today. This validator is correct and cheap **regardless of how
OPEN-4 is ruled**.
int_tier value is readable by the loyalty arithmetic (§8.11.8's seconddiscipline — "behavior quality is a build property, never an integrity property"); and no policy row
gates progression (§8.11.8's first discipline + check_no_dead_end.py's standing contract).
OPEN-4 (RE-ROUTED, not designed here). Creature-lens F6 SURVIVES RE-ROUTED: a player→ally
order channel (focus_target | hold | regroup) collides with RUNTIME_GENERATIVE_LAYER §A.4 L116's
ruled framing — "You don't configure your teammate in a menu — you earn how they show up for you."
The refuter's read: "configure in a menu" governs authoring/configuration while an in-combat order verb
is a live tactical input — a different surface — but the distinction is fine enough that **only Josh /
the director draws it. Routed to the rank-18 bundled brief. No order-verb column is minted here.**
The asymmetry that makes it worth a ruling is real and unrecorded: familiars have a canonical,
integrity-gated command-permission model (§1.4, carried as command_permission in §3.3 above); human
companions have no equivalent rule anywhere.
T0_Boss_Attack_Rail / enemy-state coordination (creature-lens F7)§8.11.8 lists "peeling a threat off the protagonist or off a lower-durability ally" as a canonical
Proactivity behaviour. Register #1's applied engagement recommendation is aggro_radius + detection
channel + patrol + leash-and-reset, "No threat table." The refuter found the code is harder than
absence: HumanityEnemyCharacter.h L163-164's perception step is hard-wired to the player ("is
the player inside this enemy's sight cone right now?"), fields at L211-216 — so an ally **cannot
be a target at all**, and a peel has nothing to move.
Keep "no threat table" (a numeric threat meter is a WoW-register scoreboard the practitioner
framing rejects). Define a peel as a retarget CAUSE over the perception model that already ships:
enemy_state += current_target_ref : actor handle (engine-side, W-SPACE spec)
enemy_state += retarget_cause : enum {perception_lost | interposed | stagger_reacquire | leash_reset}
T0_Ally_Behavior_Policy.action_ref -> the interpose / LOS-break / stagger verbs (no new abilities minted)
All three causes are readable off existing fields; none requires a meter. **This is engine-side state,
not a registry — recorded here as the co-freeze dependency** so the §3.5 policy rows do not author
action_ref values pointing at nothing. The W-SPACE engagement spec and these policy rows are two
halves of one mechanic and must be authored against each other.
Recorded so the mint does not double-author: Tier-C §8.7 states the raid-role taxonomy "is owned by
T1_Combat_System_Spec and T1_Ability_Tree and is read here at group scale … a **non-authoritative
echo carried for legibility." The same role axes serve both arenas — one taxonomy, one
REGISTRY, two arena sizes**, per §8.7's third property ("group scale changes the role's expression,
not its identity").
Reconciled M3. The first draft asserted "one taxonomy, one enum" while carrying a bare
six-value enum (group_support · group_heals · … · st_damage) against Family A's FK'd
T0_Role_Axis_Registry (ROLE_GROUP_SUPPORT … ROLE_SINGLE_DAMAGE) — not one token matched,
so the sweeps could not join an imprint affix or an ally policy row to a pool row without a
translation table neither design minted. Every role_tag / role_axis_ref column in this design
(§1.2, §3.4, §3.5) is now an FK into Family A's registry, and spec_purpose_ref an FK into
T0_Spec_Purpose_Registry — which also repairs a worse defect than the id mismatch: the draft's
flat spec-purpose enum conflated function with scope (personal/aoe are scopes,
vril_healing is a function) and therefore could not express T1_Ability_Tree §5I.2's ruled
derivation — *"A vril-healing personal-scope specification is a single-target heal role; the same
healing purpose at area scope is a group heal role"* — which is a two-axis product that Family A's
function_class × scope_class → role_axis_ref models and enforces with tooth T-A6. Zero authoring
cost: all six purposes and all seven axes already exist as rows in Family A §9.4/§9.5. The four operational teams (Environmental
Manipulation 10-15 · Combat Core 25-30 · Spawnling Management 60 · Enhancement and Protection) are
role CLUSTERS over the same six axes, not a new taxonomy; they need no column of their own until the
Tier-C build phase. The only Family-B obligation is that role_tag stay a single shared enum and that
T0_Ally_Behavior_Policy be keyed generically enough to accept a raid-scale ally_ref later —
satisfied by ally_kind = any + a * role-generic ally_ref.
---
T0_Boss_Attack_Rail — the per-attack rail as DATA (NEW registry · 24 columns)Why it must be minted, not extended. DESIGN_GAP_REGISTER #11 named a recovery_window field per
boss-attack row; W9 realized it only as a C++ struct (FHumanityAttackRow in
Source/Humanity/Public/Combat/HumanityAttackLibrary.h), never as a registry table — every moveset in
the game is hand-coded in .cpp (11 TelegraphShape assignments across
HumanityBossCharacter.cpp L632-706 and HumanityEnemyCharacter.cpp L170-198). Sekiro F3's scoping
condition, F2's schema note, and F5's per-class floor **all presuppose a rail that does not exist as
data.** This is that rail. It is a CHILD TABLE of T0_Boss_Encounter_Registry keyed
(boss_id, attack_id) — one attack is one row, and a boss has many, so §5H.4's rows-not-columns law
applies exactly.
Coordinate, do not duplicate — and this is the shared firewall's ONE enumerated exception (M8).
windup_seconds, active_seconds, recovery_seconds, damage and punish_damage_mult are raw
floats mirroring a shipped engine struct. That is legitimate reuse and the right call, but it is
also the complete and closed list of magnitude columns exempt from §0.4 clause 1 in either
family — stated in the firewall itself so the design and its own stated discipline no longer
disagree in writing. Values stay ILLUSTRATIVE until Phase-5M.
The first nine columns mirror the SHIPPED struct verbatim in
snake_case, per REGISTRY_ROW_STRUCT_SPEC.md §1 ("Member names mirror the CSV header VERBATIM …
any rename silently drops the column"). recovery_seconds IS register #11's landed
RecoverySeconds — the registry becomes its data source, not a second rail beside it. Same for
punish_damage_mult (PunishDamageMult 1.75, IsInRecoveryWindow()) and enabled (bEnabled —
the W9-2 part-break toggle).
| column | type | enum / FK | required |
|---|---|---|---|
attack_row_id | id | AT_\d{4} | yes (PK) |
boss_id | FK | T0_Boss_Encounter_Registry.boss_id | yes |
attack_id | string | the per-creature moveset key (mirrors FName AttackId) | yes |
telegraph_shape | enum | promoted from free string to a CLOSED key: whip_arc · low_sweep · high_feint · lunge · feint · grab · venom_spit · swipe · coil · disc (the nine authored + the struct default) | yes |
windup_seconds | float | ILLUSTRATIVE / 5M | yes |
active_seconds | float | ILLUSTRATIVE / 5M | yes |
recovery_seconds | float | the #11 punish window — the existing rail, not a new one | yes |
counter_class | enum | any · dodge_only · parry_only · neutralize (mirrors EHumanityCounterClass) | yes |
damage | float | ILLUSTRATIVE / 5M | yes |
punish_damage_mult | float | ILLUSTRATIVE / 5M (struct default 1.75) | yes |
enabled | bool | the part-break toggle (SetAttackEnabled) | TRUE |
min_tell_ms | int | PER COUNTER CLASS — a row has one counter_class, so per-row IS per-class. Replaces the actor-wide MinTellSeconds = 0.15 scalar. | blank / 5M |
answer_window_ms | int | how long the correct answer stays available | blank / 5M |
telegraph_lead_ms | int | how far ahead of the strike the CLASS read resolves — distinct from windup_seconds (Sekiro shows the class at the START of a long windup, not at its end) | blank / 5M |
tell_shape_class | enum | the ground/space glyph FAMILY (rank-16 partitions it by counter_class) | yes |
tell_motion_class | enum | the windup animation family — the body read that survives colour-blindness AND audio-off | yes |
telegraph_sfx_id_ref | FK | T0_SFX_Registry.sfx_id — the class sting | nullable |
telegraph_vfx_ref | string | VFX key | nullable |
part_ref | string | the hitzone_parts part this attack rides (breaking it disables the row) | nullable |
phase_index | int | which boss phase the row belongs to | yes |
attack_kind | enum | melee · ranged · cast · grab · arena_mutation | yes |
cast_pattern_ref | FK | T0_Ability_Tree_Registry.cast_pattern (the neutralize class's READ — the one class R2.2 already gives a differentiator) | nullable |
source_ref | string | — | yes |
notes | string | — | — |
Consumer. UHumanityAttackComponent::Moveset (loaded from the DataTable instead of hand-coded in
.cpp — the reuse that retires 11 hard-coded assignments) · the telegraph render path
(ShowTelegraphShape() currently ignores the row it already has) · the QA program's per-frame state
record (QA_WATCHING_PROGRAM.md L46 already carries counter_class and telegraph_shape) ·
rank-16 (the pip fill/wipe columns key off counter_class answers) · the sweeps.
Gate / tooth additions.
fk_spec.json: PK attack_row_id, pattern AT_\d{4}, **composite uniqueness on (boss_id, attack_id)**, FK boss_id → T0_Boss_Encounter_Registry.boss_id, FK
telegraph_sfx_id_ref → T0_SFX_Registry.sfx_id.
harness/check_counter_class_coverage.py — three assertions, one script:1. The F7 tooth (the cheapest tooth in the sekiro lens, with 268 boss rows still to author):
per chapter, every counter_class present in that chapter's authored movesets has its answering
capability granted at or before that chapter. Grounding: the Ch-2 Kelimutu Guardian pair
(BE_0002_S1, chapter_id = CH_02) authors guardian_tail_swipe as ParryOnly while
bParryCapable defaults false with exactly one production grant (if (bIsCaci), the Ch-3 caci)
and WPN_033 Shield is canonical_chapter_acquisition = CH_03 — against a triple-sourced
ratified 2-of-3 scope (memory/puzzle-boss-chain-meter-design.md §7 · FUN_REBUILD_PLAN §4
ruling (5) + R2.2 · PRE_5090_BUILD_PLAN_VOL2 L1614/L1456).
2. The answerability invariant (F5): min_tell_ms[parry_only] >= reaction_budget_ms and
min_tell_ms + answer_window_ms >= reaction_budget_ms for every authored row at every dial
position. reaction_budget_ms is a declared symbolic constant in the combat spec; the gate is
authored against symbols NOW and armed when the magnitudes land at 5M.
3. **Telegraph disjointness (dqpm F3's surviving schema rule — rank-16 owns the tooth, this rail
owns the columns):** within one creature's moveset, no two distinct counter_class values share
a tell_shape_class, and every counter class in use has a distinct key. Arithmetic over a record
that already exists in the QA per-frame stream.
counter-coverage): counter_classes_present (multiset, derived from this rail) and player_capability_gate (which
weapon/chapter grants the answer). Both default EMPTY, which reproduces current canon exactly.
Corrected M6a: T0_Boss_Encounter_Registry/Sheet1 is stub_superseded, so these two land by
extending its candidate_header and re-pinning — not via added_columns/extension_hashes.
Recorded, NOT designed here (routes to rank-18). Sekiro F2 SURVIVES-UNDERSTATED: Josh's
verbatim ruling is symmetric — "attacks that CANNOT be parried — dodge only; attacks that CANNOT
be dodged — parry only" — and the build implemented one half (dodge-only is genuinely unparryable)
while reinterpreting the other as chain-level-only (IsCurrentDodgeable() has zero call sites;
the dodge branch is a bare IsDodgeInvulnerable() with no class test). The lens's recommendation —
keep i-frames universal for DAMAGE and put the matrix's teeth in the CHAIN — is a **system-canon
change** and must route as a briefed Josh item, never be applied by the mint. The rail above is
neutral: it carries counter_class as data either way.
T0_Weapon_Registry — the defensive-properties extension (5 added columns · Vector C)Vector, corrected (M6a). T0_Weapon_Registry/Sheet1 is stub_superseded — not in
added_columns, not among the 19 extension_hashes keys — so these five land by extending its
candidate_header and re-pinning it, which is the path check_registry_extensions.py L59-69
takes for a manifest key with no added_columns.
Why. Sekiro F4 SURVIVES — "the strongest schema-freeze hit in the set." Canon names the
parry-capable set in prose (T1_Combat_System_Spec §Parry L444-454: Rapier Parry · Long Sword
Halbschwerter/Meisterhau · Cutlass Parry Riposte · Madu Horn Catch "the only Trickery dedicated
parry weapon" · Sword 1H Riposte; §Block L435-442 for shields). The live registry is **72 rows × 18
columns with no defensive column of any name**, and the runtime capability is a single bool
bParryCapable (default false) granted from exactly one place. The freeze-collision is real:
§12 Lane-A's own named bonus kind — "catch-window width on the parry weapons" — has nothing to
attach to.
| column | type | enum / FK | default (reproduces current canon) |
|---|---|---|---|
defensive_class | enum | none · block · parry · catch · riposte | none (= today's bParryCapable=false on every weapon) |
parry_window_ms | int | ordinal / 5M | blank |
parry_stamina_cost | int | ordinal / 5M | blank |
parry_generate | enum | none · stamina · vril — §Parry's "generates Stamina or Vril per weapon class": the resource is per-class, so it must be a column, not a constant | none |
riposte_followup_ref | FK | T0_Ability_Tree_Registry.ability_id | blank |
Seed rows (canon prose → data, the five named parry weapons + the two shields). defensive_class
populated from §Parry/§Block; every other of the 72 rows takes the none default. The Madu row carries
defensive_class = catch and is the natural first parry_generate = stamina row ("every successful
catch generates Stamina at Mastery 8").
Reuse anchor. weapon_sfx_id_ref_array already reserves "hit, block, parry, ranged release…" slots
per weapon (T0_Schema_Dictionary L548) — the parry AUDIO contract already assumed a per-weapon
defensive property the registry never modelled. These columns sit beside damage_type /
vril_couple_eligibility (both 0/72, already queued as rank 26) so **one authoring pass fills all of
them**. Dedupe stated: rank 26 fills EXISTING empty columns; this ADDS absent ones — distinct work,
same pass.
bonus_axis cross-reference — scoped to this family (reconciled m2). T0_Imprint_Affix.bonus_axis
(§1.2) carries timing_window as a first-class value precisely so the sweeps can separate a
window-widening pick from a damage pick. A window-widener is the archetypal must-pick and
META-COLLAPSE is the named banned failure mode, so this tag is a direct requirement of the §10
doctrine, not an embellishment.
The draft then added "the same axis applies to Family A's tier-bonus rows" — an assertion about
a column Family A does not mint. Family A expresses the axis at a different granularity
(bonus_kind, the effect's identity, which carries the catch_window value; bonus_axis is the
effect's class), and one family may not declare a column on another's table. **The cross-family
claim is withdrawn**; bonus_axis is a Family-B column on rolled affixes. If the sweeps later want
one axis across both surfaces it is derivable from A's bonus_kind / effect_attribute pair —
a sweep-side mapping, not a schema claim, and not asserted here.
---
T0_Status_Table — the canonical status grid + the read channels (NEW registry · 26 columns)Why. dqpm F1 SURVIVES (owner story corrected). Ten distinct status kind tags ship
(HumanityCombatTags.cpp L16-25: Status.DoT.Burn/.Poison/.Septic/.Scald/.Windburn, Status.HoT.Life,
Status.Impair.Slow/.Freeze/.Paralyze, Status.Stagger) and all ten fire the same cue
(HumanityStatusSubsystem.cpp L156, one tag, no kind, no FGameplayCueParameters); expiry and cure
are silent (CureStatusKinds L197-221, CureWithMaterial L223-239 fire no cue). No T0_Status_*
registry exists; T0_Creature_Roster has no immunity column, matching the subsystem's own admission
(HumanityStatusSubsystem.h L97-98). Window-plan rank 25 already names three deliverables (canon
grid · cure/antidote chains · status_immunity); the six read columns below are strictly ADDITIVE to
it, never a re-report.
Id format. status_id = ST_\d{3}.
| column | type | enum / FK | required | |
|---|---|---|---|---|
status_id | id | ST_\d{3} | yes (PK) | |
status_kind | enum | the ten shipped tags (burn · poison · septic · scald · windburn · life_hot · slow · freeze · paralyze · stagger) — closed, extended only on attested demand | yes | |
status_family | enum | dot · hot · impair · stagger | yes | |
cue_tag | string | the per-kind GameplayCue.Combat.Status.<Kind> tag — mirrors the SHIPPED Composure pattern (eight GameplayCue.Composure.Node.<Mode>.Broken tags routed through a table at HumanityDamageExecCalc.cpp L80-87) | yes | |
read_channel | delimited enum set | vfx_body · vfx_ground · audio · motion · hud_text · screen_edge — minimum 2, and never colour alone | yes | |
read_persistence | enum | event · persistent · both (dqpm F2's shared column) | yes | |
persistent_cue_tag | string | the WhileActive cue key — required when read_persistence ≠ event | conditional | |
persistent_channel | enum | body_vfx · silhouette · posture · audio_loop · screen_edge | conditional | |
icon_class | enum | the FAMILY glyph: drain · restore · bind · stagger — distinct from status_kind, because CLASS reads at a glance and IDENTITY reads on inspection | yes | |
stack_display | enum | none · count · pips · intensity | yes | |
stack_cap | int | ordinal (the §4.4 cap; magnitude 5M) | blank | |
break_event_ref | string | the cap-triggered break (COMBAT_ENCOUNTER_SYSTEM §4.4's chill→shatter) | nullable | |
expiry_tell | string | cue key for the currently-silent expiry transition | yes | |
cure_tell | string | cue key for the currently-silent cure transition | yes | |
status_sfx_id_ref | FK | T0_SFX_Registry.sfx_id | nullable | |
cure_material_refs | delimited FK list | T0_Flora_Registry.flora_id \ | T0_Mineral_Registry.mineral_id (the FL-10 tamarind/Komodo-septic proof case) | nullable |
immunity_tag | string | the token T0_Creature_Roster.status_immunity matches | yes | |
applies_to_ally | bool | ally-side statuses exist and must be readable | TRUE | |
duration_class | enum | instant · short · sustained · until_cured (ordinal, not seconds) | yes | |
magnitude_ref | ref | T0_Balance_Constant_Registry.constant_id — Family A's target, one column name one contract (M8) | blank | |
power_source | enum | Family A's power_source_enum (M4) — which layer applied the status (proficiency · gear_imprint · ally_spread · familiar_bond · consumable · merge); vacuous on day one, and the column defect D8 needs to compute over status effects too | yes | |
lifecycle | enum | Family A's lifecycle_enum, DEFAULT active (M4) | yes | |
superseded_by | self-FK | T0_Status_Table.status_id (M4) | no | |
string_status | enum | Family A's string_status_enum, DEFAULT placeholder (M4) — carried even though this table has no FText column today, so the natural-voice worklist query is uniform across the family and a later *_text column inherits the discipline rather than inventing it | yes | |
source_ref | string | — | yes | |
notes | string | incl. examples_not_limits | — |
Seed rows. Ten, one per shipped kind — the runtime tags ARE the verbatim seed. burn/poison/
septic/scald/windburn → status_family=dot, icon_class=drain; life_hot → hot/restore;
slow/freeze/paralyze → impair/bind; stagger → stagger/stagger. Every seed row carries
read_persistence = persistent (a status with a duration must render for its duration) except
stagger (both).
Consumer. UHumanityStatusSubsystem (the per-kind cue split) · the HUD status row (rides
MakeText/MakePip, HumanityPlayerHUDWidget.cpp L87-99 — a status row is a horizontal box of
widgets already constructed for the pip strip) · the herbalism/alchemy cure economy · the QA rubric
bands RB-9/QT-6 · rank-20 UI.
Reuse-first (both halves already exist in-repo). (1) The per-kind cue pattern is the shipped
Composure table one file over — zero new architecture. (2) The audio data home is the proven
sfx-pipeline extension group (ability_sfx_id_ref_array, boss_sfx_id_ref_array,
familiar_bond_sfx_id_ref_array); status_sfx_id_ref joins it.
Gate / tooth additions.
fk_spec.json: PK status_id, pattern ST_\d{3} min_values 10, FK status_sfx_id_ref → T0_SFX_Registry.sfx_id, FK cure_material_refs (multi-value, dual-target).
check_counter_class_coverage.py or its own tiny script): every row's read_channel set has ≥2 members and never resolves to colour alone — the ratified
colour-independent floor (docs/proposals/DIFFICULTY_SYSTEM.md §Accessibility — Attunements
L82-91; note the corrected citation — T99_Translation_Combat §2.6 is a stale self-pointer that
the sekiro lens's refuter traced and corrected). Every row with read_persistence ≠ event has a
non-empty persistent_cue_tag and persistent_channel.
registry_extensions.json: status-framework group on T0_Creature_Roster/Sheet1 owning status_immunity (1 added column, default EMPTY = no immunity, which matches today's state
exactly). Same group name owns nothing else, satisfying I2.
Applied constraint (not re-litigated). no-mode-taxonomy-on-player-surfaces is respected
throughout: none of the columns above expose internal routing on a player surface. icon_class,
read_channel and persistent_channel are *perceptual* keys — a body that looks poisoned, a sound
that says bound — never a label, never a legend, never the debug HUD's VERB KIT.
Owner correction carried forward (dqpm F1's refuter). PRE_5090_BUILD_PLAN_VOL2 U11.1
(BUILD-NOW) enumerated "cue-driven status" as a deliverable and U11.4 the per-mode Composure
node-break read; the 2026-07-26d W10 landing record lists neither in its shipped list **nor in its
"Honest tail (TODO_CANON)"**. That is a BUILD-NOW deliverable that shipped without one of its own
enumerated elements and without the drop being recorded — a W10-tail bookkeeping item that must be
reinstated or honestly deferred. Recorded, not resolved by this mint.
---
6.1 No existing row is edited. Every landing is (a) a NEW registry directory declared in
added_registries, or (b) NEW columns appended beyond the frozen reference width and declared in
added_columns + registry_extensions.json (Vector B — only T0_RNG_Drop_Table,
T0_Chapter_Index, T0_Creature_Roster), or (b′) NEW columns on a stub_superseded registry
declared in its candidate_header + re-pinned (Vector C — T0_Weapon_Registry,
T0_Familiar_Registry, T0_Boss_Encounter_Registry), or (b″) columns on a wholly-added registry,
which need no manifest key at all (T0_Rune_Registry, T0_Equipment_Registry), or (c) a
domain-cell declaration on an existing T0_Worldstate_Variables row (WS_012, WS_027, WS_028,
WS_036) plus two NEW WS rows (WS_045, WS_046 per the §2.2 allocation; the registry's current max
is WS_042). No frozen _source xlsx is touched. No expected_cell_diffs entry is added or changed.
JOINT arithmetic, one statement for both families (n5). Each design was internally right and
jointly wrong. Landed together: gates 25 → 29 (26 build_space A · 27 rarity_firewall ·
28 counter_class_coverage · 29 ally_build); added_registries 12 → 25 (A +5, B +8);
added_rows["T0_Worldstate_Variables/Sheet1"] 17 → 21 (A: WS_043-044, B: WS_045-046).
6.2 Every new-column default reproduces the current game exactly — by landing vector.
| column | vector | default | what it reproduces today |
|---|---|---|---|
T0_Weapon_Registry.defensive_class | C (candidate_header) | none | bParryCapable default false on all 72 |
T0_Weapon_Registry.parry_* | C | blank | flat player constants ParryWindowSeconds 0.35 / ParryCooldownSeconds 0.25, read by no weapon |
T0_RNG_Drop_Table.sigma_constraint_enabled | B (added_columns) | TRUE | today's unconditional σ constraint |
T0_RNG_Drop_Table.mode_gate | B | none | no mode-gated loot exists |
T0_RNG_Drop_Table.* (all) | B | vacuous | the table is 0 rows — no cell can change |
T0_Creature_Roster.status_immunity | B | EMPTY | "no canon roster immunity column exists yet" (subsystem's own comment) |
T0_Boss_Encounter_Registry.counter_classes_present | C | EMPTY | derived; empty until the attack rail populates |
T0_Boss_Encounter_Registry.player_capability_gate | C | EMPTY | no gate is asserted today |
T0_Familiar_Registry.element_affinity | C | blank | §1.2's promise is currently undischarged |
T0_Familiar_Registry.role_tag | C | blank | familiars carry no role tag today |
T0_Rune_Registry.reroll_cost_material_refs | none (added registry) | blank | no doc states a denomination today |
T0_Chapter_Index.rolled_reward_events_target | B | blank | no cadence budget exists today |
6.3 The one behavioural-continuity guarantee that matters most. WS_012.bond_tier stays a
DERIVED view {approach|accepted|mastery} over the new numeric bond_value, so **all 99 existing
spine writes across 38 nodes keep resolving unchanged**. The numeric axis is added beneath, not
substituted for. Same discipline on WS_027: the loyalty fields (loyalty_score,
loyalty_belief_axis, loyalty_departure_threshold, the 90-cap arithmetic) are untouched, and the
gate asserts int_tier is not readable by them.
6.4 The commit-shape checklist (the gate-safe landing sequence).
1. Create the new registry directories with headers + seed rows.
2. Add every new column to its registry CSV (append at the end; column order is header order and the
generated struct diff is the review).
3. docs/registry_extensions.json, by vector (M6): add the Vector-B system keys
(imprint-loot on T0_RNG_Drop_Table + T0_Chapter_Index; status-framework on
T0_Creature_Roster) and the Vector-C system keys (defensive-properties on Weapon,
ally-build on Familiar, counter-coverage on Boss_Encounter) — the Vector-C three are
satisfied through stub_superseded[key].candidate_header, NOT added_columns.
Declare NO manifest key for T0_Rune_Registry or T0_Equipment_Registry (added registries;
each key would be an immediate I3 FAIL). Remove _deferred.familiar-bond-ladder (its table
now exists).
4. docs/fk_spec.json: register all 8 new registries (registry + tab + primary_key —
composite ["axis","rarity_token"] for T0_Rarity_Grade — + id_formats with min_values, +
foreign_keys carrying references_tab and min_tokens), including the FKs into Family A's
T0_Role_Axis_Registry and T0_Spec_Purpose_Registry (M3), which therefore land first or in
the same commit.
5. python harness/registry_fidelity.py --emit-baseline — refresh added_registries,
added_columns, extension_hashes, and the Vector-C candidate_header pins in the same
commit (the standing rule; skip it and the gates go red).
6. harness/check_ws_value_form.py domain extensions for WS_012/027/028/036 + WS_045/WS_046,
plus the CG-25 +-prefix legal-set edit 12 → 14 for WS_044 (Family A) and WS_046
(a ruled-list edit — flagged, not assumed; the live constant holds twelve ids, not six).
7. Regenerate the affected row structs (Tools/gen_row_struct.py) — RngDropTableRow.h,
WeaponRegistryRow.h, plus the 8 new structs.
8. python harness/run_gates.py UNPIPED — the joint roster is 29 (25 + build_space +
rarity_firewall + counter_class_coverage + ally_build).
9. docs/DOC_MAP.md rows for all 8 new tables (a table is not done until it is listed there).
6.5 What this design does NOT do. It rules nothing that is Josh's. Four items are surfaced with
full framing and routed to the rank-18 bundled brief rather than decided: OPEN-1 the 4-vs-5 rarity
ladder + ability_count axis; OPEN-2 the three unnamed stat slots in the recovered 16-slot layout;
OPEN-3 familiar element affinity (author 22 vs retire the §1.2 promise); OPEN-4 the player→ally
order channel vs the ruled "you don't configure your teammate in a menu". Plus two system-canon
routings recorded and not applied: sekiro F2's universal-i-frame reinterpretation, and the
D-MYTHIC-GATE brief amendment (bl-loot F6 — the *third* edit point at
docs/proposals/DIFFICULTY_SYSTEM.md, which is headed RULED and already sites the rarity apex at The
Forgotten One, plus the unstated Sundered hard-mode prerequisite that CVD §12.1 gates the
post-Ch-77 prestige questline on Ch-77 completion with no difficulty condition of any kind). The
schema is neutral on every one of them: each resolution is a row or enum-value edit, never a shape
change.
6.6 The lens wave's standing correction, honoured. docs/original_predclaude/ is part of the
current-state corpus for any design-canon absence claim — it produced four of this design's binding
inputs (the five-grade rarity ladder + naming rule, the acquisition-source-by-rarity table, the 16
equipment slots, and per-instance rolled ability sets). Every "no home exists" statement above is
scoped to a search that included it.
---
*(Counts and vectors below are POST-reconciliation. Where a number moved, the reason is the
finding id in the last column.)*
| # | home | vector | columns / sub-fields | reconciled |
|---|---|---|---|---|
| 1 | T0_Rarity_Grade (composite PK (axis, rarity_token)) | new registry | 24 | M2 (−rarity_id), M4 (+4) |
| 2 | T0_Imprint_Affix | new registry | 26 | M4 (+4) |
| 3 | T0_Equipment_Registry | new registry | 26 | m8 arithmetic, M4 (+1) |
| 4 | T0_Loadout_Set_Definition | new registry | 14 | sole owner of set canon (m1) |
| 5 | T0_Familiar_Bond_Ability | new registry (retires _deferred) | 15 | M4 (+1) |
| 6 | T0_Ally_Behavior_Policy | new registry | 21 | M4 (+5), M3 (role FK) |
| 7 | T0_Boss_Attack_Rail | new registry (child of Boss_Encounter) | 24 | M8 (the one struct-mirror exception) |
| 8 | T0_Status_Table | new registry | 26 | M4 (+4) |
| 9 | T0_RNG_Drop_Table + imprint-loot | Vector B added columns | 10 | m7 (vocabulary), M2 (token) |
| 10 | T0_Weapon_Registry + defensive-properties | Vector C candidate_header | 5 | M6a |
| 11 | T0_Familiar_Registry + ally-build | Vector C candidate_header | 4 | M6a, M3 |
| 12 | T0_Rune_Registry | no vector — added registry, NO manifest key | 2 | M6b |
| 13 | T0_Boss_Encounter_Registry + counter-coverage | Vector C candidate_header | 2 | M6a |
| 14 | T0_Creature_Roster + status-framework | Vector B added column | 1 | — |
| 15 | T0_Chapter_Index + imprint-loot | Vector B added column | 1 | — |
| 16 | WS_036 worn_rune_loadout | domain declaration | 6 | M1 (points at WS_046) |
| 17 | WS_046 item_instance_ledger | new WS row | 15 | M1 (was WS_043), m3 (+sigma_seq) |
| 18 | WS_045 loadout_set_state | new WS row | 6 stored + 1 derived | M1 (was WS_044; bonus_pick_refs[] derived) |
| 19 | WS_012 familiar_bond_state | domain extension | 12 | M9 (+stat_point_budget) |
| 20 | WS_027 companion_roster_state | domain extension | 10 | M9 (+stat_point_budget) |
| 21 | WS_028 companion_loyalty_transition_events | domain extension | 2 | M1 (refs WS_046) |
New gates: 3 — harness/check_rarity_firewall.py (6 assertion classes incl. the affix-side
mythic twin, plus the affix-predicate lint), harness/check_counter_class_coverage.py (3 assertion
classes, one armed at 5M), and harness/check_ally_build.py (the §12 stat-budget tooth T-B1, the
110-row bond-ladder coverage assertion, the INT/loyalty firewall assertion, and the
disposition_legal_set validator — three of which had no named host before reconciliation). All
zero-token and permanent, in the teeth class of check_entity_taxonomy.py and
check_no_dead_end.py. Joint gate roster with Family A: 29.
---
Applied against mint_design_critic.md (verdict: NO-GO as a pair · GO-WITH-FIXES individually).
Read-only on C:/dev/humanity-forgotten @ ab5d32c; the two design files are the only writes.
Where the critic prescribed a fix it is applied verbatim; where it offered options, the choice
minimising cross-family coupling was taken and is recorded below with its reason.
WS_043+ space is allocated ONCE (§2.2 here, verbatim-identical to Family A §6.0): WS_043 bonus_selection_state (A) ·
WS_044 bonus_respec_ledger (A) · WS_045 loadout_set_state (B) · WS_046
item_instance_ledger (B). *Choice recorded:* contiguous per-family blocks over interleaving, so
each family's rows stay independently reviewable; the item ledger keeps the critic's prescribed
WS_046. One owner for loadout state = Family B (this table carries the unlock gates, the
venue class and the departs_with_companion = FALSE invariant), and **one owner for selection
state = Family A**: bonus_pick_refs[] is now a DERIVED read over WS_043, not a stored copy,
which closes the double-owned shape the critic called worse than the id clash. Every downstream
reference re-pointed (WS_036.instance_ref, WS_028.returned_instance_refs[], §2.2, §6.1, §6.4).
T0_Rarity_Grade is re-shaped from a new RG_\d{3} keyspace into a PROPERTY table keyed (axis, rarity_token); the
shared 8-token ladder is stated ONCE here and cited by Family A; every FK becomes a token FK
(rarity_token_min, rarity_class, base_rarity_class) and no live cell moves. Both halves
of the survivor are carried: bl-loot F5 gets its FK-able grade table, creature F8 gets no parallel
enum and the Flora/Mineral reconciliation (uncommon, signature are in the ladder, so
firewall class (d) can pass). Column count 21 → 24. Verified: composite PKs are live
(T0_Thread_Grid), and the live distributions were re-counted programmatically — 10 live
uncommon + 1 signature = 11 cells, correcting the critic's figure.
role_tag / role_axis_ref / spec_purpose_ref in §1.2, §3.4 and §3.5 is now an FK into Family A's
T0_Role_Axis_Registry / T0_Spec_Purpose_Registry; §3.7's "one taxonomy, one enum" becomes
"one taxonomy, one REGISTRY". This also removes the function/scope conflation that made §5I.2's
ruled derivation inexpressible. Zero authoring cost — A's §9.4/§9.5 rows are the roster.
power_source, lifecycle (+ superseded_by) and string_status land on T0_Imprint_Affix, T0_Ally_Behavior_Policy,
T0_Rarity_Grade, T0_Status_Table; string_status additionally on T0_Equipment_Registry and
T0_Familiar_Bond_Ability (every FText-bearing table), and voice_register_ref on
T0_Ally_Behavior_Policy (the one spoken line). §0.5's voice_status marker is retired for A's
enum. All defaulted, all vacuous day one — and D8 source_dominance can now compute.
the HUD / pip lane, rank 16) and F6 (owner: the SFX consumer lane, explicitly distinguished
from §4.1's telegraph_sfx_id_ref, which is F3's channel), and the F7 mis-attribution is
corrected: the hitzone_parts carve keys belong to the rank-16 hitzone lane; F7 is the Ch-2
ParryOnly-scope finding this design implements as §4.1 gate assertion 1.
stub_superseded registry → extend candidate_header + re-pin) for Weapon / Familiar /
Boss_Encounter, and a fourth "no vector at all" case for wholly-added registries. The two
I3-tripping manifest keys are dropped (imprint-loot on T0_Rune_Registry, assetgen +
sfx-pipeline on the new T0_Equipment_Registry) — the columns still land, the keys do not.
§6.1, §6.2 and §6.4 step 3 are re-columned by vector.
UHumanityAttributeSet extension as step 0 and T-A5 scopedto WARN on a declared seven-name list; both, because either alone leaves a hole).
Family A §0. magnitude_ref takes A's target (T0_Balance_Constant_Registry.constant_id) on both
T0_Imprint_Affix and T0_Status_Table. The five raw floats on T0_Boss_Attack_Rail are named
in the firewall itself as the complete, closed struct-mirror exception, so the design and its own
stated discipline no longer disagree in writing.
stat_point_budget added to WS_027 and WS_012 (familiars carry ally_stat_block too, so a companion-only cap would leave a hole one
entity class wide), with tooth T-B1 in Family A's T-A12 class. *Choice recorded:* the tooth
needed a host script, and three further ally-side assertions (bond-ladder coverage, the INT/loyalty
firewall, the disposition_legal_set validator) had none either — so they are consolidated into
ONE new gate, harness/check_ally_build.py, rather than scattered into subject-mismatched
scripts or into Family A's gate (which would have added cross-family coupling). Joint roster 29.
T0_Loadout_Set_Definition is stated as the SOLE owner of set canon; the ceiling is its row count, and A's unlocked_count is gone.
bonus_axisis a Family-B column on rolled affixes; the assertion about Family A's tier-bonus rows is
withdrawn, with the derivation noted as a sweep-side option, not a schema claim.
sigma_seq added to WS_046 (the join back to the per-drop_table_group σcounter, without which the σ bound cannot be audited per instance); keep-rate homed in Family
A §7.5 via a subject_ref/subject_kind re-key, with this design's affix rows as the subject.
twelve ids, not the design's six and not the critic's seven — verified at
check_ws_value_form.py L116-118 with the D-CG25-APPEND-SET ruling comment at L112-115. The
landing edit is 12 → 14 (WS_044, WS_046); the stale "six" in the L20 docstring is fixed in
the same pass. Family A also now names the gate in its landing order.
acquisition_source includes operator-library (the dominant live value, 17 of19 populated cells) and the whole enum is hyphen-case to match the live column it reuses; the
seed rows and the σ-exemption invariant are restated in that case.
T0_Equipment_Registry is 26 columns (17 domain + 8 assetgen + 1 sfx) andthe assetgen rail is explicitly the 8-column Creature/Character/Familiar/Boss form, because
T0_Weapon_Registry's group is 7 (its mesh_id_ref sits in the base header).
check_rarity_firewall.py gains class (e), the affix-side twin of the mythic guard (no rarity_token_min on a non-roll-obtainable grade without
hand_authored_override), plus class (f), the per-column axis check that keeps one token
vocabulary from becoming three by drift.
tab, references_tab, min_tokens).added_registries 12 → 25, added_rows[WS] 17 → 21).
None. Every finding was reconcilable without contradicting a ruled canon point or a lens
SURVIVES verdict. **OPEN-1 through OPEN-4 and the two system-canon routings (sekiro F2's
universal-i-frame reinterpretation, the D-MYTHIC-GATE brief amendment) remain Josh's and are
untouched by this pass — in particular the M2 reconciliation does not** pre-empt OPEN-1: the
uncommon token exists because 10 live Flora/Mineral cells already carry it, and its
imprint_slot_count is authored blank precisely because the 4-vs-5 grade question is his.
Status: the pair is landable together. The four cross-design collisions are closed with one
owner each, both self-defeating teeth are fixed, and every MODERATE and MINOR is applied in place.