music/LEITMOTIF_ARCHITECTURE.md
CANON SUBORDINATION — this document is PROPOSAL-TIER: it serves canon and never outranks it.
The canon: the CVD · the T1 foundation docs · the T0 registries (registries/) · the spine
(docs/spine/CH_*.md) · the region pages (_source/02_Tier_2_Region_Pages/). Authority order:docs/DOC_MAP.md§ 0.
Canon served (scanned from this document's own citations — widen it by hand where it is thin):
CVD §17·T0_SFX_Registry·T0_Theme_Registry·T0_Thread_Grid·T0_Thread_Registry·docs/spine/CH_PROLOGUE.md
READ THAT CANON FIRST — open it and derive from it before you build anything from this document.
If this document disagrees with canon, CANON WINS and this document is the defect — fix the
document, never the canon. Nothing here is applied until it is ratified into canon.
Tier: PROPOSAL. The machine form of everything below is
docs/proposals/music/theme_architecture_rows.json, applied by
harness/apply_theme_architecture.py. The reasoning is here; the cells are there; the registry is
the authority once it lands.
T0_Theme_Registry carries fourteen rows. Twelve are the cardinal roster Josh pinned at the
twenty-second sitting; two are the Cassius cue variants. That roster is good and it is not an
architecture, because five things it needs were reserved and never minted, and each absence has a
consequence you can name:
motif_class. The doctrine routes each class to its own acceptance test, and states outright that a row with motif_class unset cannot be scored and cannot be rendered. Two of the
twelve are deliberately not tunes — the wonder identity is a harmonic-transformation identity and
the voice colour is a timbre — so a single hummability checklist run over the roster would have
failed them for not being what they were never meant to be.
reveal_gate column, and no pre-gate row at all. The reveal discipline lived in prose inside a hard_line_relevance cell. Music is a named surface of HL_0043 and HL_0044, and the one
thing the antagonist material must do — recur, unnamed, for thirty-six chapters — had no row to
live on.
deny_register_list. Section 1.9 of the doctrine says it in one line: a motif with anempty deny list has not been reviewed. Twelve rows, twelve empty deny lists.
triad-outlining *or* purely stepwise, deliberately, never both — that nobody had made for any
motif.
familiar bond across twenty-two species slots, and eleven realms, all with nowhere to go. The
doctrine's own allocation says twenty-two independent themes exceeds any listener's recognition
budget and produces mush; the fix is families, and there were none.
This pass mints all five, adds twenty-four rows, and moves the cardinal count by zero.
The cardinal roster is untouched: twelve rows, row_class = cardinal_theme, and the applier
refuses to write if that count moves in either direction. Everything below is Tier B or Tier C.
| class | count | what it is |
|---|---|---|
cardinal_theme | 12 | unchanged; now carrying all thirteen new columns |
family_head | 8 | seven thread families over the 22 threads, plus the realm family |
companion_theme | 1 | the healer's child |
cue_variant | 4 | the two landed Cassius stages, plus the childhood-constancy cue and the pre-gate pattern |
timbral_signature | 1 | the voice colour, judged on the atmosphere bar |
region_cell | 12 | the slice's Tier-C region cells, one per region page |
A thread earns its own Tier-B motif only if it recurs across five or more nodes, has an on-screen
agent, and has an arc that changes state. Every one of the twenty-two clears the first test — the
smallest, Thread 15, still carries seven anchors — so the first test discriminates nothing here and
the allocation is decided by the other two plus the recognition budget. Seven families, each a
shared head cell and a shared harmonic signature; every inheriting thread takes that cell with
exactly one interval substituted and a distinct carrier voice.
Thread 22 Sacred Stones and Thread 9 The Great Work inherit.
of Life inherit; all three are one idea at three scales — a built thing, a raised thing, an axis.
7, 12 and 20 inherit.
family alone is reveal-gated at Ch 38, and its head is stated unnamed before then.
shared idea and contour inversion is the shared operation.
Thread 21 heads it because this is the one thread where music is the mechanic rather than the
scoring — Flores canonizes the gong-waning as the region's vril-active instrument and the arc's
first audible vril resonance, and Bali reaches confrontation on it with sorcery music theory as
the chapter's lead questline.
Which number the anchor counts above are, and the divergence they sit on top of. Every count in
this section is T0_Thread_Registry.anchor_count (T01 24 · T02 37 · T03 36 · T15 7 · T17 39 — each
verified against the registry cell). That column diverges from T0_Thread_Grid's live population by
two to three times: counting Thread_Grid rows whose progression_stage is anything other than
NOT_PRESENT gives T01 65 · T02 63 · T03 69 · T15 8 · T17 44, and counting raw rows regardless of
stage gives 70 · 78 · 78 · 8 · 44. The allocation above is unaffected either way — the ≥5-node
test discriminates nothing under any of the three readings, which is exactly what this section
already says, and the family heads were chosen on the other two tests. But the two columns should be
reconciled before any Tier-B promotion decision is made to rest on a count, and the reconciliation
is a registry question rather than a music one: the divergence is pre-existing and was not minted by
this pass. Recorded here rather than boarded because this is the document that consumes the number,
and the consumer is where a stale number does its damage.
The two threads that came closest to earning independence and still inherit are Thread 17, which has
the anchor count for it but whose on-screen agent is the same agent as Thread 20's for most of the
arc, and Thread 13, whose arc changes state mostly through places rather than through itself. Both
are recorded here rather than argued, so a later pass can promote either on evidence.
The head is four notes and purely stepwise, chosen deliberately against the alternative: this
arc leans on fragile solo statements far more than on statements that have to cut through a loud
orchestra, and the head-cell law forces the choice rather than allowing both. One deviant only, a
borrowed degree on the third note.
The shape is the single-theme arc: the Prologue statement and the Ch-77 statement are the same tune
and the player must feel the distance. Eight transformations budgeted out of the allowed six to ten
— augmentation at the losses, diminution in pursuit, fragmentation as the combat-layer form,
reharmonisation at each vril escalation, one mode shift per integrity-band crossing, carrier
migration per region, and the closure change reserved for Ch 77. The three ending rows are not three
new tunes; they are that withheld cadence spent three ways, and the deny list forbids them any
shared arrival gesture, because CVD §17.5 requires three-ending differentiation with no convergence
cue.
The row's own notes already record the risk: its thematic kinship to the Grand Sage reveal makes a
pre-reveal leak high-risk. That is now a deny-list clause with a named check rather than a comment.
The healer's child, CHAR_0002, has walked the route as the unsigned watcher since Ch 3 and steps
into companion at CH13_B01, where she signs her first note *Ntala* and opens the evidence journal
— the one canonical Ch-13 join-and-journal beat, bound by HL_0070. The motif is built for that
shape: the head cell alone for every unsigned-watcher appearance from Ch 3 to Ch 12, and the
full phrase first stated when she signs her name.
Fragmentation is normally the foreshadow operation, and using it for ten chapters would ordinarily
be a reveal problem. It is not one here, and the reason is worth stating: there is nothing held. She
is really there, the player really can see her, and the fragment is doing presence rather than
prophecy. What the fragments buy is that CH13_B01 lands as recognition instead of introduction.
And the thing it may not do. The spine records a designer-only resonance: the healer's child's
gender inherits from the player's Ch-1 best-friend selection, so the companion who joins at Ch 13
carries the same gender as the fairy-realm friend left behind — *invisible on first play and never
surfaced*, carrying zero forward reveal-valence. A shared motif between the two would surface it,
which is the one thing it may not do. So COMPANION_HEALERS_CHILD is declared independent of the
childhood-constancy cue, on the same grounds and by the same precedent as HOME_AND_LOSS's declared
independence from the protagonist head cell.
*The strongest objection:* that forfeits the best recombination payoff available anywhere in the
slice — two childhood motifs fusing at the Ch-13 departure would be the most affecting thirty
seconds in the first fourteen hours. *The answer:* the payoff that is actually earned there is the
object's, not the friend's. MOT_0001, the mother's ward, already recurs Prologue → Ch 1 → Ch 2 →
Ch 13 and resolves at Ch 13 in the live motif registry, and HOME_AND_LOSS already names those
exact beats. The departure has its cadence; it does not need a second one built out of material a
hard line says must stay silent.
Head of three notes, triad-outlining because it has to read under twenty-two different species
carriers, with one unusual leap — an ascending sixth into the third note — that every per-species
variant keeps. Per-species carrier migration is the whole mechanism: the tune does not change, the
voice does. Twenty-two slots in the base game, thirty-three at the post-game Epilogue.
The deny list carries the trap. The player believes the Child Familiar dead from Ch 2; the truth
gates to the Truly Good path at Ch 58. No grief colour may touch that material before Ch 58 — a
mourning register applied early tells the player the score knows something they do not. The one
combination the motif is allowed, with the protagonist carrier, is reserved for the Ch-58 reunion
and states the relation with no dialogue.
PATTERN_UNNAMED_PREGATE, row_class = cue_variant, parent HOUSE_OF_VELHEIM_THEME,
reveal_gate = CH_38. It is modelled exactly on the ruled Cassius collapse: one motif with logged
transformations, not a second identity, so no foreign key moves.
Thread 3 is live from Ch 2 at its pre-family scales — an individual bargain at Ch 2, then the
institutional forms at Ch 11, Ch 12 and Ch 13, each a real local antagonist and none of them named
as anything larger. The motif is the parent's three-note head, fragmented, and fragmentation is
the *only* operation permitted before Ch 38: augmentation, combination or a mode shift would each
announce that the score has an opinion. A different local carrier every time, so the listener
remembers the shape and never the sound. One statement per node, never the phrase, never a swell,
never on a face.
An unnamed recurring motif is the leitmotif's entire power. The player is meant to have heard this
many times before anyone tells them it means anything, so that Ch 38 is a recognition. **The count,
read off T0_Thread_Grid rather than estimated: Thread 3 is present in thirty-five chapters before
Ch 38, so one statement per node is thirty-five pre-gate fragments, of which eleven fall inside
the Prologue→Ch 13 slice** (Ch 2–9, Ch 11, Ch 12, Ch 13; Ch 10 carries no Thread-3 presence).
Eleven is the slice commission; thirty-five is the transformation budget.
The firewall is mechanical, not a promise. The row is named *The Recurrence* — a name that says
what the cue does and never what it means. harness/emit_music_cues.py refuses to emit any cue
whose reveal_gate is later than its own chapter if the cue **name, its site or its trigger
context** carries a held token — those are the three fields that exist at construction time and all
three are scanned there. The cue_id is assigned inside the writer, so its value cannot be scanned
at construction; its shape is MC_dddd, which carries no token by construction, and verify()'s V4
scans the assigned value along with the name and the trigger. Must-fire fixtures prove both
refusals fire. **The gate source is the theme row's own reveal_gate, taken as the stricter of that
column and a frozen crosscheck dict, and it fails closed**: an unparseable gate raises rather than
defaulting to ungated, a theme the dict has never heard of is still held if its row declares a gate,
and a column-versus-dict contradiction raises rather than being averaged. The earlier shape read the
dict alone with a default of zero, which is a fail-open whitelist coupling. The recurrence anchors are
deliberately left unpopulated: each statement has to be placed against its own node's staging by
a lane that has read that node, and a hand-typed anchor list here would be exactly the invented-canon
failure this repo gates against. The firewall arms itself the moment the first anchor lands.
The Ch-1 best friend is *the one who stayed while others turned*. That is the chapter's warmth and
the thing the argument puts at risk. It lands as a cue_variant of HOME_AND_LOSS rather than a new
row, because giving the best friend an independent identity would make the childhood readable as
setup, and HL_0116 forbids exactly that: the rift-fall has no author, no aim, no omen and no curse,
and the score is a player-facing surface of that hard line.
The transformation plan has a deliberate hole in it. Nothing happens at the argument. The
correct cue for CH01_B12 is the abundance register continuing, unbothered — which is what actually
happens to a childhood afternoon after a fight, and is also the only setting that refuses to
editorialise.
Twelve region cells for the slice, one per region page, each parented to JOURNEY_WORLD, each
one-to-two bars, none of them required to be hummable — only coherent with the parent.
Every cell's instrumentation is copied from that page's own Section 10 rather than composed
here, so a composer reading the row is reading the page. That is what stops the generic
world-music wash the doctrine names as a care failure rather than a taste note. It also means the
cells are honestly uneven, and the unevenness is the point: Bali's cell has a full ensemble
architecture to build on and Swahili Coast's has three attested sentences, because the page says so
first and the page is right to. Where a page states its lane is thin, the cell is built from what is
attested and the gap is left.
The Tier-C rule that matters: where a region also carries a Tier-A payoff, its cell **must quote the
parent head at least once**, or the payoff has no soil to grow in — and that quotation is a
composer-authored stem laid over generated material, or fed as lego conditioning, never a prompt,
because the generator has no melodic conditioning that could honour one.
**The one licence constraint that binds the moment generated material enters, stated here because
this is where generated material and lego conditioning are specified.** The Sonniss bundle licence
prohibits use as training data, so **no Sonniss file is ever a fine-tune corpus, a LoRA corpus, or an
audio conditioning input for any generator** — direct use only. The landed Sonniss beds ride in the
bed worklist's landed_beds_excluded list as the material a generated bed must not be derived from,
and the constraint travels on the row rather than in a note: every cue row carries licence_class
alongside bed_source, the per-page §10 projection block carries both columns and restates the rule
in its header, and every produced bed writes its generating model and licence into
T0_SFX_Registry.generation_licence_ref — a dispatch field the worklist hands the operator, not an
assertion nobody was instructed to satisfy. The generator itself (ACE-Step-1.5-XL-SFT) is MIT with
no output-use clause; that read is recorded as DL_0008/DL_0009 in the tooling download manifest
with the licence body pinned by hash, and it is the *other* side of the constraint, not a
substitute for it.
Every new row carries a real deny list. One clause is universal, because CVD §17.1's collective
protection expressed in music has exactly one failure mode and it is easy to commit by accident:
No region's own register is used as the threat signal, systematically darkened, inverted or
menace-coded. The darkening operations belong to the antagonist material and travel with it,
never with the place.
The corollary is equally binding and points the other way: an individual antagonist may be scored
fully inside his own culture's register, with the same craft as that region's protagonist cues. A
blanket register ban is the named failure, not the safe option. Care-tier inflation is a defect;
care-tier evasion is also one.
JOURNEY_WORLD carries the clause hardest, because it is the motif that visits every region, and it
may never become the recurring cue for threat in any region it enters.
SUPERSEDED IN PART, 2026-08-05. The first bullet below was true when this pass ran and is no
longer true of the roster. Josh's ruling that day — *"Humans aren't composing the music… what the
fuck. The entire music pipeline is yours"* — retired composer-in-loop, and the melody A/B then
measured that authored composition delivers the brief's contour where text conditioning does not
(dossier §8). The melodies now exist: thirteen served head cells plus one held parent, in
notes, atbuild/audio/authored/**, composed byharness/music_gen/theme_compositions.pyand
checked by author_head_cell.py's battery. Two of this document's own named transformations are
composed as well — the companion's head-cell-alone form (§3.2) and a family's minor-mode
recurrence, built on the realm family because §4's care clause forbids darkening a real region's
register and that is the one family whose register is invented. **Everything else in this section
still stands**, in particular the unpopulated recurrence anchors of §3.4: each statement is still
placed against its own node by a lane that has read that node, and none of them were placed here.
flag stays True on every row.~~ What is specified here is the *shape a melody must satisfy* — head
length, contour class, the single deviant, the transformation budget, the payoff node — which is
what a brief is for, and it is exactly what made the composed cells checkable rather than merely
present: every constraint in this document became a control the battery runs against the notes.
cultural_substrate on the Tier-A rows that leave it blank. Section 1.9 makes an allow listmeaningless without a deny list and its region-as-threat check; those rows now have both, and the
substrate column fills when a register card is signed off per node.
lanes held the shared baselines open — re-emitting the fidelity baseline in that tree would have
swallowed their work, which is the reason recorded at the head of
harness/apply_theme_architecture.py. The director ran it in a clean tree:
T0_Theme_Registry went 14 rows x 30 columns to 38 x 45, the cardinal roster asserted at twelve on
both sides, the fifteen columns declared under owner leitmotif-architecture in
docs/registry_extensions.json, the parent_theme_id self-edge declared in docs/fk_spec.json
(16 tokens, 0 dangling) alongside floors on theme_id, row_class, tier, motif_class and
reveal_gate, and the fidelity baseline refreshed through the SCOPED vector on the theme keys
alone. The edge and the four enums are mutation-proved rather than asserted: a dangling parent, a
cardinal row spelled under a new row_class token, an off-axis tier and an off-grammar
reveal_gate each fail the gate, and the untouched registry passes.
The consequence named the same day is therefore DISCHARGED. Eleven of the fifteen composed
artifacts had no live registry row, so their records were not permitted to claim one; each licence
and generation record DERIVES its registry pointer from whether the row actually exists and
carries registry_row_present beside it. Twenty-four records were re-derived by
author_head_cell.py --refresh-docs in the landing commit and now name the live registry — the
derivation is what made that a one-command repair instead of a hand sweep. A record asserting a
canon home that does not exist is the same defect class as a proof number that no longer
re-derives, and so is a record still denying a home it now has.
stale.** Found by the apply lane, 2026-08-05. harness/apply_theme_architecture.py held a
hardcoded thirteen-name NEW_COLUMNS list beside a docstring stating the count came from the data
and "never written down here". When plant_node and diegetic_surface were minted into
theme_architecture_rows.json the constant did not move, so an apply would have landed thirteen
columns while the bullet below claimed the two new ones were minted "which makes the gate
representable" — they would have existed in a proposal file and nowhere a gate can read, which is
the half-unrepresentable defect restated one level up. The constant is deleted, the set is read
from the data, and self-test fixture 5 reads the WRITTEN header rather than the constant. It is
mutation-proved: restore the thirteen-name list and the fixture names plant_node and
diegetic_surface as never minted.
diegetic surface, payoff node and closure policy all assigned before first render." Two of the four
had columns and two had none, anywhere — so the gate could not be failed on them, and a first render
landed and is publicly served on fourteen pages with half its gate unrepresented. plant_node and
diegetic_surface are now minted as columns in theme_architecture_rows.json and, since the
2026-08-05 apply, as real columns on the live T0_Theme_Registry — which is what actually makes the
gate representable, since a column that exists only in a proposal is a column no gate can read.
They are deliberately unpopulated, on the same reasoning as the recurrence anchors
above: a diegetic surface exists in a NODE — an instrument a character plays, a performance the
player can walk up to, a surface the player physically inputs — and it is assigned by a lane that has
read that node. Filling it from here would invent it. **Outstanding, and owed before the next render
of any Tier-A row:** a plant node and a diegetic surface for each Tier-A theme, assigned from the
spine entry it plants in; PROTAGONIST_THEME is first, because brief §1 makes its diegetic plant a
named requirement inside the first three nodes it appears in, and docs/spine/CH_PROLOGUE.md
# music_mood currently describes a register with no diegetic performance surface in it at all.
python harness/apply_theme_architecture.py --self-test # 6 fixtures, isolated copies
python harness/apply_theme_architecture.py # the dry-run diff
python harness/emit_music_cues.py --self-test # the fixture suite; exit 2 on any failure
python harness/emit_music_cues.py --check # the cue table re-derives identical
python harness/emit_music_cues.py --verify-s10-flip # the blocker proof, re-run
The emitter's fixture count is deliberately not written down here or printed by the script. It was
written down as eleven, then as thirty-five, and both numbers were stale the moment the next fixture
landed — a rotted proof number reads as evidence, which is worse than no number. The suite runs on
every invocation of the emitter rather than only under --self-test, so the exit code is the
claim.
The self-test mutation-proves the refusals against temporary copies rather than asserting them: a
thirteenth cardinal row is refused, a live roster that has already drifted off the pin is refused, a
column fill naming a theme that does not exist is refused, and a second apply is recognised as
already applied instead of duplicating twenty-four rows. The live registry is never touched by any
of it.