systems/SPINE_SCHEMA_v3.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 §7·CVD §9·CVD §15·T1_Build_Pipeline_Contracts·T1_Integrity_Paths_Worldstates_Master·T1_World_Geography_Master·docs/spine/CH_39.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.
Status: PROPOSAL for Josh ratification. v3.1 folds in the Fable-5 adversarial validation (docs/spine/v3_exemplars/FABLE_V3_VALIDATION.md, verdict GO-WITH-CHANGES → all 8 blocking changes B1-B8 applied). Grounded by the v3-derivation deep-dive (consumer-needs vs T1_Build_Pipeline_Contracts + the integrity/choice canon + the v2.5→v3 gap catalogue). This is the target Fable-5 rewrites all 79 nodes to. Companion: docs/spine/SPINE_V3_PLAN.md (the why + the Opus↔Fable split). Additive over docs/SPINE_SCHEMA_v2.md — v2.5's 20 blocks stay; v3 re-shapes Blocks 8/9/10/11/13/18 into an addressable graph.
The information the pipelines need is ALREADY in every entry — scattered across Boss-structure, Key-sites, Main-quest-beats, integrity_divergence, and the two carry-forward blocks as PROSE at chapter granularity. v3 does not author new content; it JOINS and TYPES what exists into a beat → fork → state graph — preserving the header schema, the [SRC:] provenance, the reveal discipline, and the carry-forward contract that already work. Block 4 (body prose) stays a short emotional lens; it was never the event spec. (Fable verified the join-and-type claim under audit: every typed row in the CH_39 exemplar traces to content already in CH_39.md.)
Quest Writing (BPC §6) expands the spine into the ~3M-word branching main questline (6-9M with paths). Its quest_substrate (§6.5) assumes: an ordered quest_beat_structure with stable beat IDs; branching_logic as a decision tree with named split-point beats + path_bucket per variant; per-beat spatial_requirements_array, participant_entity_refs, boss phases as ordered beats; a line_variant condition surface; machine SQ gates; per-branch consequence deltas. 3D (§1.2 PCG + §1.8 Save State), Cinematic (§5.5/§5.6 scene rows), Region-Page (§7.3) all hang off the same graph. Today the spine emits 4-5 prose bullets with the fork named once at the resolution beat and state only at chapter boundaries — none can bind. v3 supplies the graph.
---
FIVE integrity LEVELS (the Ch 2-65 texture axis) → THREE ending PATHS (committed at the Ch-65 lock). The spine's current four labels (truly_good/good_enough/neutral/evil) are a vocabulary bug to correct (drops L5, mis-types "neutral"). Cite: T1_Integrity_Paths §3.1-3.5, §4.1, §10.6; CVD §7.2/§9.2/§17.2; registry WS_001-005.
| Level | Band name (texture) | Score | World State (WS_003) | Ending path (Ch 65) |
|---|---|---|---|---|
| L1 | Truly Good | +60…+100 | WS1 Luminous | TRULY_GOOD |
| L2 | Good Enough | +20…+59 | WS2 Restored | GOOD_ENOUGH |
| L3 | Drifting | −19…+19 | WS3 Neutral | collapse to NEAREST (§3.5) — no "Drifting/Neutral ending" (HL §10.6) |
| L4 | Evil | −59…−20 | WS4 Damaged | EVIL |
| L5 | Pure Evil (terminal) | −100…−60 | WS5 Corrupted | EVIL (terminal texture, CVD §15) |
integrity_level ∈ {L1…L5} (texture, five, DERIVED) and path_bucket (ending, DERIVED, committed at Ch 65). "Neutral" is ONLY the WS3 world-state name; the middle band is "Drifting" and yields no ending.path_bucket enum (B3, reconciled to WS_004 + a path-invariant value): {TRULY_GOOD, GOOD_ENOUGH, EVIL, UNLOCKED, ALL}. UNLOCKED = pre-Ch-65 (the ending is not yet committed). ALL = a row that plays identically on every committed path (post-lock invariant content — the Ch 77 reveal, the epilogue). Ratification action: reconcile the contract's Decision-2B enum {truly_good,good_enough,evil,convergence_neutral,pre_branch} to this in the SAME commit (convergence_neutral→ALL, pre_branch→UNLOCKED), and grep-fix the contract's stale vocabulary (§6.2 "Neutral/Bad ending"; §7.3 S19 "L5 Truly Bad").neutral/evil used as a path/ending label → re-key to the two-axis vocabulary; restore L5; bands live ONLY in the derived axes (never as a fork-option label — see §3c/B2).appearance_register (3 visual: luminous/hardened/ambiguous, WS_006) is a DECOUPLED derived axis (CVD §9.4).---
KEEP verbatim: Blocks 1 (preamble), 2 (header), 3 (continuity), 4 (body prose — the short emotional lens), 5 (threads), 6 (civ-marks), 7 (everything-woven), 12 (ally NPCs), 14 (protected-framing), 15 (anti-fringe), 16 (three-layer), 17 (asset anchors), 19 (end-state carry-forward), 20 (decisions). The [SRC:] discipline is inherited by every new typed row.
RE-SHAPED / NEW (the machine layer, §3 below): Block 8 → typed SITE ROWS · Block 9 → boss phases as a THIN JOIN INDEX (phase→beat) · Block 10 → the BEAT GRAPH · Block 10b → the FORK GRAPH · Block 11 → SQ beats + machine gate · Block 13 → typed SCENE ROWS bound to beat_id · Block 18 → per-beat/per-fork state deltas roll up (Block 19 becomes the fold over them). Transitions → seam state-diffs.
---
none)Rules that bind every node (gate-enforced, §6): (i) one row form per element (below); (ii) fixed field order; (iii) every field written out, none where empty (never omitted); (iv) tables ONLY for fork-option matrices + scene rows; (v) beat_id PERMANENCE — once committed a beat_id is NEVER renumbered; insertions take the next free number, seq carries order (QW hangs 3-9M words off these IDs); (vi) §9 is a thin index — all narrative lives in the beat.
beat row (Block 10)
- {beat_id} · seq {n} · role {hook|core|climax|exit} · mode {gameplay_function} · site {site_id|none} · boss {BE_xxxx.Pn|none} · fork {fork_id|none}
- objective: {one sentence, player-facing goal}
- actors: [{entity_id}, …] · systems: [{prefix:id}, …] · threads: [{THREAD_NN:STAGE}, …]
- in: {ws.key: val, local.key: val} → out: {ws.key: +val, local.key: +val}
- layers: {L1|L2|L3 combo} · reveal: {HELD(reason)|LIFTED(reason)|none} · [SRC: …]
role (B/K, new) = the RP §7.3 S13 dramatic slot (hook/core/climax/exit). mode = gameplay_function (closed enum §4). actors = participant_refs (B5, first-class, every beat — never smuggled into systems). systems = FK-checked prefixes (§5). threads = thread_refs (B5, canonical THREAD_NN encoding, gate-checked vs the 22-roster — never T3 in systems). in/out = state deltas over the two-namespace key grammar (§5). reveal = the per-beat reveal/awareness gate (mandatory; pushes §9.8 discipline to the line/shot).mode roll-up (fold over beats → the chapter mode_variety_check) retires the R4 waiver structurally.site row (Block 8 — B1, the previously-dangling join target)
- {site_id}: terrain {§7.3 enum} · functions [{gameplay_function}, …] · ambient_threat {none|low|moderate|high} · vril_density {baseline|low|moderate|high|anchor} · climactic_affordance [{vista|threshold|sacred|climactic_geometry|weather_dramatic}, …|none] · protected {true|false} · [SRC: T1_World_Geography_Master # …]
Beats reference sites by site_id. terrain feeds §1.2 PCG + §6.5 required_zone_type; climactic_affordance binds the boss beat. (Traversal distances/zone polygons stay downstream — PCG, not the spine.)
fork_node + ACTION-keyed options (Block 10b — B2/B3)
- {fork_id} · anchor {beat_id} · magnitude {FOUNDATIONAL|SUBSTANTIAL|ACCUMULATIVE|SIGNATURE} · kind {dialogue_six_option|assimilation_participate_subvert|combat_resolution|environmental_boss|exploration_interaction|path_dispatch} · care_band {open|elevated|maximum|protected-elevated|antisemitism-guardrail|none} · convergence_node {node_id|null} · progression_guarantee: {≥1 through-line open; equivalent_class_alternative for any closed opportunity}
- coverage: {directions reachable: generative|neutral|corrosive} # required annotation (B2)
| option | direction | delta (class-bounded) | axis_value | mark_write | relationship/faction | opportunity_effects | ws_writes |
direction {generative|neutral|corrosive} + a class-bounded delta + (for assimilation) an axis_value {participate|subvert_and_remain|honorable_open}. The coverage annotation asserts which directions are reachable. Bands never appear as option labels — they are derived-axis outputs (§1). This kills the re-conflation Fable flagged and forces honorable_open to be authored where it exists.kind: dialogue_six_option → author the canonical six {TRULY_GOOD, TRULY_BAD, SYMPATHETIC, DETACHED, CURIOUS, DEFIANT} (HL_0039/HL_0040) + a collapse map to durable outcomes.kind: path_dispatch (B3, NEW) → the post-Ch-65 case: options keyed by path_bucket (or worldstate), NO player choice, NO integrity delta — the LOCKED path selects the variant. Types the 13+ post-lock nodes (66-77, epilogue, the Father reunion, the endings) that were previously unrepresentable.ws.*) MUST be a fork_node; ephemeral-only branches stay accumulative notes.| option | direction | delta (class-bounded) | axis_value | mark_write | relationship/faction | opportunity_effects | ws_writes |. (2) DISPATCH-7 (kind: path_dispatch): | path_bucket (LOCKED) | variant | direction | mark_write | relationship/faction | opportunity_effects | ws_writes | — the delta/axis columns are structurally ABSENT (post-lock dispatch carries no integrity delta; the missing columns encode the rule). (3) SIX-OPTION-7 (kind: dialogue_six_option): | option | direction | delta (class-bounded) | axis_value | six_option_slot | opportunity_effects | ws_writes | — the slot column is load-bearing (each row's ws_writes appends its slot enum to WS_018). Kind⇔shape cross-rule enforced by the gate.mark_write (§5.5.4) = the community-specific durable record ({community_id, valence, persistence, forward_track:[CH_NN]}) — the "memory of events" backbone; the gate checks every forward_track target chapter actually READS the mark.BE_xxxx: primary {verb} · secondary {verb} · axis {n/n/n} · phases [{Pn→beat_id}, …] · win {ws_writes} · death {ws_writes}. No narrative (it lives in the phase beats).{sq_id} · questline {qid} · stage {SEED|ADVANCE|GATE|COMPLETE} · gate {condition-expr} · beats [{beat_id}, …] · reward {text} (twelve_level_gate where it binds).{scene_anchor_id | beat_ref | scene_type (§5.5's 11 values VERBATIM) | path_bucket | anchor_type | participant_refs | hard_line_relevance} — the executed SEVEN-column shape (verified uniform across all 79 live nodes, 2026-07-15 critic pass; an earlier revision of this line swapped the 7th column — the corpus, and each node's own §13 preamble, carry hard_line_relevance in-table and defer the INTENT TRIO thematic_anchor | audio_priority_layer | scene_audio_intent to the Scene_Spec population pass downstream). anchor_type is the trigger-condition class, enum as executed: {scene_entry, beat_transition, dialogue_initiation, branch_decision_point, reveal} — the Contract-§6 trigger vocabulary plus reveal, which marks exactly the four arc-reveal moments (CH_66 letter · CH_76 the name · CH_77 the face · Epilogue understood-at-the-water); it is never a level/map anchor (maps resolve via beat→§3a site→region). The path_bucket column takes the §1 enum plus LOCKED as executed in post-lock nodes (CH_76's five rows, CH_77's gloss row): one row whose variant resolves through the node's own path_dispatch fork, never per-bucket row duplication. The per-phase pre/transition/post slot ARRAY is declared once as a pattern and expanded by Cinematic.from {CH}.exit_state → to {CH}.entry_state, an assertion set over ws.* keys the gate checks for equality. The prose bridge stays the 3D asset brief; the Through-line ledgers become a derived fold.---
gameplay_function (closed) = {combat, puzzle, learning, character, transformation, traversal, investigation, dialogue, stealth, vril_management, discovery, trade} + the PROJECTION onto the Pillar-1.12 FIVE (for the roll-up + encounter_mode_tag): traversal/stealth→combat-adjacent context but count as their own; the roll-up maps {combat→combat · puzzle,investigation,vril_management→puzzle · learning,discovery,trade→learning · character,dialogue→character · transformation→transformation · traversal,stealth→(context, no letter unless paired)}. (Ratify the projection table; the five Pillar-1.12 letters are what mode_variety_check needs.)scene_type = §5.5's 11 VERBATIM (boss_encounter_intro/…_phase_transition/…_resolution, convergence_node, per_chapter_cutscene, architect_appearance, ambient_cinematic, canonical_set_piece, …). Adopt as-is; do not re-invent.sq_stage = {SEED, ADVANCE, GATE, COMPLETE} (no OPEN). fork_kind = §3c list. magnitude = §3c. direction = {generative, neutral, corrosive}. path_bucket = §1. care_band = §3c.1. State keys — two namespaces. ws.{name} = DURABLE; MUST resolve to a WS_NNN registry variable (gate-checked). local.{name} = chapter-scoped; MUST be consumed within the chapter (produce-before-consume) or explicitly promoted. The seam validator + the Block-19 fold run over ws.* only.
2. systems_touched prefixes — FK-checked. ability:A_F_012 (real Ability_Tree IDs, not third_eye_probability_sight); boss:BE_xxxx, trade:TRADE_NN, familiar:{id}, inscription:{id}, vril_site:VS_xxxx, object:{id}, antagonist:NW_xxxx, decode:{surface}, sub_boss:BE_xxxx_Sn. Each prefix's id resolves against its registry (gate-checked). Entities go in actors, threads in threads — NOT here.
3. Gate/condition expressions — produce-before-consume. One mini-grammar: has(local.key), at(site_id), flag(local.key), ws.key {==|>=|<=|in} val, read(surface), boolean &/|. Every consumed flag/local.key MUST be produced by a prior beat's out: (gate-checked — no unresolvable tokens like the old flag(deep_time_echo_present)).
harness/check_spine_graph.py (B7, BUILT + wired + teeth-verified)Status: BUILT and green. Wired into run_gates.py as the spine_graph gate (10/10 suite PASS, exit 0); teeth-verified against 7 injected faults (bad enum, unresolvable ws.*/ability FK, dangling fork ref, consume-before-produce, Cassius-before-naming leak, off-roster thread) — each caught with exit 1.
Checks (ERROR = fail): beat_id uniqueness + reference closure (forks/scenes/SQ/boss-phase anchor real beats); closed enums (role/mode/magnitude/fork_kind/direction/sq_stage/scene_type/path_bucket/care_band); FK-resolvable systems prefixes (ability:/boss:/sub_boss:/trade:/antagonist:/vril_site: resolve to their registry; object/decode/inscription/familiar/journal/gate format-only); thread_refs THREAD_NN roster membership; ws.* keys resolve to a Worldstate variable_name; local.* produce-before-consume; per-beat objective/actors/systems/threads/in/out/layers/reveal present-or-none; fork option direction/path_bucket enums + ≥1 option row; and (reveal) no thesis-locked phrase or Grand Sage on any player-facing objective, Cassius no earlier than the beat whose reveal NAMES him. UPGRADED TO ERROR (2026-07-15, the corpus cross-file pass — fires only on full-79-node invocations; per-file runs keep WARN-era behaviour): mark_write.forward_track cross-file consumer continuity (machine-visible in:-line reads); magnitude:FOUNDATIONAL §5.4-catalogue anchor (a T1_Integrity_Paths cite in the fork block); §18-vs-fork path_bucket pre/post-lock consistency; seam cross-file equality (leading enum tokens on both faces must agree; prose-valued keys and one-sided carries stay WARN-class). Teeth-verified 4/4 with injected faults.
docs/review/build_reel.py + reveal re-verify after each. The normalize-doc critic gets a v3 section.--emit-baseline in the same commit.neutral/evil-as-ending; add ALL; reconcile the contract Decision-2B enum) — canon-grounded, applied in the rebuild.[POPULATE→] slots, does not invent.path_dispatch node kind + ALL path_bucket + Decision-2B reconciliation §1/§3c), B4 (closed enums §4 + three grammars §5), B5 (first-class thread_refs/actors §3a), B6 (one row grammar, fixed order, mandatory-with-none, beat_id permanence, §9 thin-index §3), B7 (gate spec §6), B8 (exemplar regeneration §7). Added role (dramatic slot), the L3 tie-break flag, the micro-fork promotion rule, the mark forward-track continuity check.