SPINE_SCHEMA_v3.md

systems/SPINE_SCHEMA_v3.md

Spine Entry Schema v3.1 — the beat→fork→state event graph (pipeline-consumable, Fable-reconciled)

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 one-line thesis

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.)

Why (the load-bearing consumer)

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.

---

1 · Canonical integrity / ending model (RESOLVED against canon — Fable-verified)

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.

LevelBand name (texture)ScoreWorld State (WS_003)Ending path (Ch 65)
L1Truly Good+60…+100WS1 LuminousTRULY_GOOD
L2Good Enough+20…+59WS2 RestoredGOOD_ENOUGH
L3Drifting−19…+19WS3 Neutralcollapse to NEAREST (§3.5) — no "Drifting/Neutral ending" (HL §10.6)
L4Evil−59…−20WS4 DamagedEVIL
L5Pure Evil (terminal)−100…−60WS5 CorruptedEVIL (terminal texture, CVD §15)

---

2 · The v3 entry — block structure (delta from v2.5's 20 blocks)

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.

---

3 · THE AUTHORING TEMPLATE (B6 — ONE grammar, fixed field order, every field present-or-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.

3a · 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: …]

3b · 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.)

3c · 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 |

3d · Boss THIN INDEX (Block 9 — B6.vi) · SQ beats (Block 11) · Scene rows (Block 13) · Seam diffs

---

4 · Closed ENUMS (B4)

5 · Three GRAMMARS (B4 — the single highest-leverage fix)

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)).

6 · The gate — 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.

7 · Migration + sequencing

8 · Open items (Josh)

Changelog

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