V3_CONVERSION_RUNBOOK.md

pipelines/V3_CONVERSION_RUNBOOK.md

Spine v3 Conversion Runbook — the per-node brief for the 79-chapter v3.1 restructure

Status: ACTIVE (2026-07-14, Fable-5 v3 rewrite). The operational brief for converting one live spine entry

(docs/spine/CH_NN.md) from the v2.5 twenty-block form to the ratified SPINE_SCHEMA_v3.md v3.1

beat→fork→state event graph, in place. Proven on the CH_02 pilot (commit c6840cd, tag spine/v3-ch-02,

gates 14/14, fresh-context adversarial critic GO-WITH-FIXES → all findings applied). Read this WITH:

docs/SPINE_SCHEMA_v3.md (the ratified spec), the class-matching gold exemplar

(docs/spine/v3_exemplars/CH_12_v3.md early/assimilation · CH_39_v3.md modern · CH_76_v3.md

finale/path_dispatch), and docs/spine/CH_02.md (the live-file layout precedent — the exemplars are

delta docs; CH_02 shows the in-place form).

The one rule

JOIN AND TYPE — never author new canon. Every typed row must trace to content already in the v2.5 entry

(or its cited registry/personas sources). Placement judgment calls (which beat carries a chapter-level

fact) are allowed; new canon claims are not. Anything the v2.5 entry asserts that the conversion re-types

differently (a classification, a zone, a vocabulary label) gets a Decisions-block entry recording the

resolution (the CH_02 bargain-refusal precedent). Load-bearing v2.5 content must survive SOMEWHERE

(a beat objective, a thin-index note, an env line, a kept block) — dropping canon is a MAJOR.

What changes, what stays

Continuity anchor, Body prose, Threads advancing, Civilizational marks, Everything-woven, Key ally

NPCs, Protected-chapter combat framing, Anti-fringe, Three-layer, Asset anchors, Decisions surfaced.

travel line; per-site env sub-line preserves the environmental_params + hosted-beat list).

beats; the narrative lives in the beat graph)"). Keep the Pillar-8 intro line. One row per boss:

BE_xxxx "name" · primary VERB · secondary VERB · axis n/n/n · phases [Pn→CHNN_Bnn, …] · win {ws_writes} · death {…}.

Sub-bosses one compact line each → their beat. Ambient threats (no registry row) noted as such.

A reveal-binding line where the chapter has one.

table + the intent-trio deferral note.

the v3 vocabulary (point to the fork graph + §18; CH_02 shows the wording) and reconcile any

zone/site line the beat graph resolves.

re-key its path_states line to the v3 vocabulary.

keys; the prose bridge stays in the transitions file)" — both seams (prev→this, this→next).

Row grammars (fixed field order; every field present-or-none; the checker parses these exactly)

Beat (id convention CHNN_Bnn; prologue CHPRO_Bnn; epilogue CHEPI_Bnn):

Fork (### FORK_CHNN_A — name (anchor CHNN_Bnn), then):

path_dispatch). Bands NEVER appear as option labels — actions + direction only; bands are derived (§18).

Site row: - {site_id}: terrain {enum} · functions [{modes}] · ambient_threat {…} · vril_density {…} · climactic_affordance [{…}]|none · protected {true|false} · [SRC: …] + an indented - env: … line.

SQ row: - SQn · questline {slug} · stage {SEED|ADVANCE|GATE|COMPLETE} · gate {expr|none} · beats [CHNN_Bnn, …] · reward {…}.

Scene row (table): | scene_anchor_id | beat_ref | scene_type | path_bucket | anchor_type | participant_refs | hard_line_relevance | — SC_CHNN_slug ids; scene_type from the §5.5 eleven VERBATIM.

Seam row: - assert ws.key: value · … — plain dashes, both seams as separate H3-less lists under one H2

(intro sentence per seam, as in CH_02).

Closed vocabularies (get these exactly right)

dialogue, stealth, vril_management, discovery, trade. ("exploration" is NOT a mode — map to

discovery/traversal.)

exploration_interaction, path_dispatch. direction: generative, neutral, corrosive.

TRULY_GOOD/GOOD_ENOUGH/EVIL (per-variant rows), ALL (post-lock path-invariant rows).

boss_encounter_phase_transition, boss_encounter_resolution, convergence_node, per_chapter_cutscene,

architect_appearance, ambient_cinematic, canonical_set_piece, prologue_cinematic, epilogue_cinematic,

branch_decision_point.

wilderness, cave_system, water_body. climactic_affordance: vista, threshold, sacred,

climactic_geometry, weather_dramatic (only where the v2.5 text supports it — no decoration).

in their registries); format-only: object: decode: inscription: familiar: journal: gate: verb:

agility_course: thread:. Do NOT use the reveal: prefix in systems (the checker's beat parser claims

any "reveal:" token as the reveal-state field and errors) — use inscription:/object: instead.

world_state_current, path_bucket, path_lock_in_state, appearance_score, age_stage,

relationship_history, journal_status, journal_entries_unlocked, journal_voice_texture,

familiar_bond_state, cassius_velheim_state, maatherion_negotiation_outcome, atlantis_passage_decision,

vimana_5th_mode_access, father_encounter_variant, six_option_cumulative_record,

community_state_per_region, antagonist_operational_state_per_region, faction_standing_per_faction,

convergence_node_state, vril_polarity_signature, atmospheric_vril_density_modifier,

identity_hiding_status, reputation_biography_propagation, companion_roster_state,

companion_loyalty_transition_events, trade_sovereignty_valence, hearthway_network_state,

dwelling_home_ledger, reputation_tier_per_faction, prestige_tier, nemesis_memory_per_boss,

spellcraft_authored_spells, worn_rune_loadout, husbandry_menagerie_state, husbandry_extinction_flag,

hollow_containment_fork_last. Anything chapter-scoped is local.* (produce-before-consume enforced).

SQ3 archaeology_inscription_occult · SQ4 mystery_detective · SQ5 astrology_psychology ·

SQ6 pirate_naval · SQ7 sorcery_music_theory · SQ8 trickery_gambling · SQ9 cryptids ·

SQ10 anomalous_phenomena · SQ11 lost_civilizations · SQ12 secret_societies.

Grounding protocol (run these; do not work from memory)

1. python harness/enrich.py chapter CH_NN --json — the grounded pack (registry rows, hard lines,

threads, personas pointer). Boss ids: grep the pack or

registries/T0_Boss_Encounter_Registry [DRAFT v0.1]/Sheet1.csv for chapter_id CH_NN.

2. The live docs/spine/CH_NN.md — the SOURCE OF TRUTH being typed. Also read the prev/next chapters'

End-state + header blocks (the seam material) and the relevant docs/spine/transitions/ section.

3. grep -n "CH_NN\|Ch NN\b\|Ch-NN\b" docs/proposals/WHOLE_ARC_CONNECTIVE_WEB_INVENTORY.md — the

connective-web links touching this chapter; any link the v2.5 entry carries MUST become an explicit

fork option / mark_write / ws write / seam item.

4. python harness/rag/query.py "<question>" --top-k 8 for any governing-rule doubt (rulings,

precedents); the deterministic pack wins on FK facts.

5. BOSS_PERSONAS: grep ## boss Ch NN in docs/proposals/BOSS_PERSONAS.md directly (enrich misses the

Ch 59-65 counter-campaign cast — known gap).

The checker loop (iterate to zero errors BEFORE returning)

python harness/check_spine_graph.py --registries registries --out <YOUR-SCRATCH-DIR>/sg_CH_NN docs/spine/CH_NN.md

ALWAYS pass --out to your own scratch path (the default writes harness/spine_graph_scorecard — parallel

agents must not race it). Zero errors required; the only acceptable WARNs are the cross-file classes

(mark forward_track / FOUNDATIONAL catalogue / seam equality). Also run

python harness/lint_canon_format.py docs/spine/CH_NN.md (no --out) — zero FAILs required.

Hard-won gotchas (each cost a real finding — pilot + exemplar verification)

1. mark_write is keyed by COMMUNITY id, not object id: {manggarai: valence via <object>, persistence,

forward_track [CH_NN…]}. forward_track lists ONLY chapters that actually READ the mark (the gate

upgrades this to ERROR at full corpus) — influence-into-a-later-read is carried by the

relationship/opportunity columns, not forward_track.

2. Site functions must cover the modes of every beat hosted at that site; derive site_ids

(lowercase_snake) from the v2.5 key-site names; enum-forced attribute changes keep their lost texture

in the env line ("low-rising" → low + env note).

3. Threads: join a thread to a beat ONLY where that beat's content actually advances it. An

event-claiming thread (the Thread_Grid cell names an in-chapter event) needs a carrier beat;

chapter-ambient threads stay in the preserved Threads block with an explicit thread-join note under

the mode-variety fold. NEVER guess thread numbers — verify each THREAD_NN against the chapter's own

Threads block + T0_Thread_Grid (wrong numbers are the recurring gate-invisible MAJOR).

4. Registry-drift protocol: the FK join WILL surface spine-vs-registry contradictions (verbs, axis

splits, names). Adjudicate against BOSS_PERSONAS + both spine versions. Spine+personas agree →

QUEUE the correction: record it in the Decisions block ("queued for the orchestrator's serialized

landing") and REPORT it in your return payload with cell = "<row_id>.<column>" + old/new/grounding.

Do NOT write any registry CSV yourself — parallel whole-file read-modify-writes clobber each other

(a proven batch-3 race); the ORCHESTRATOR applies all corrections serially at landing and refreshes

docs/fidelity_baseline.json in the same commit. Genuinely ambiguous → in-file note, report, do not

propagate either value into new text.

5. Prose hygiene in NEW text: [SRC:] anchors use the doc BASE name only — never a path, never a .md

suffix, section via " # " (e.g. [SRC: WHOLE_ARC_CONNECTIVE_WEB_INVENTORY # ECV-11]; a path form

fails the anchors gate as "doc base 'docs' not found"); never write the literal "[SRC:]" outside a real anchor;

no asterisk in any heading (ws state keys, not ws.*, in headers); no bare B-number tokens (B12) in

prose — always full CHNN_Bnn; no middots inside reveal(...) parentheticals; no commas inside {}

values in in:/out: maps; nested bullets ONLY the five sub-fields (objective/actors/in/layers/env).

6. Reveal ladder on NEW text (objectives/reveal lines are new prose — the who-frame discipline applies):

pre-Ch-38 no House-of-Velheim name/family/pattern and NO agent-noun who-frame constructions (not even

invoked-and-negated); Cassius named at Ch 76 B01 only (the beat's reveal must literally carry

"Cassius NAMED"); thesis/Grand-Sage only Ch 77; Corr unattributed before Ch 63 (grep \bCorr\b = 0

pre-63); no tier-6 substrate reads before Ch 15 (HL_0028 — Neophyte vril-trace register); D-14

ceiling: Ch 28-37 may carry "an organized power exists (unnamed)" but the five pattern-components

stay dark until Ch 38.

7. Care: care_band per the chapter's tier (standard → open); protected communities never combat-routed —

carry the apparatus-only routing inside the combat beats' reveal(care: …) and the fork care_note;

protected sites flagged true. Never sanitize: monsters/oppressor-apparatus stay fought.

8. Ability/chapter-level fact placement: attach to the most-fitting beat (fire ability → the fire

puzzle beat); placement must not ASSERT new canon. Win/loss reward specifics may live in the registry

row the thin index FK-binds — verify the row carries them before dropping from the file.

9. Endgame (Ch 66+, esp. 74-77): the SPINE is the authoritative endgame topology (Ch 74 Yggdrasil ·

Ch 76 Two Courts · Ch 77 Grand Sage/Waterfall). T0_Quest_Definition_Registry, T0_Scene_Spec_Registry,

and the RCM endgame structural block still carry the OLD topology — do NOT propagate them.

10. Vocabulary re-key (schema §1): wherever the v2.5 entry uses truly_good/good_enough/neutral/evil as

path/ending/option labels, re-key — options action-keyed + direction; bands only in §18 derived

axes; "Neutral" only as the WS3 world-state name; "Truly Good path" as an ending-path name is fine.

integrity deltas write ws.integrity_score (not ws.integrity_level — the level is derived).

11. §18 must contain the literal tokens integrity_level, path_bucket, reveal_state (the checker warns

otherwise) plus identity_hiding_status and age_stage where the chapter tracks them.

12. ENTRY SEAM FIRST (a batch-2 MAJOR): before writing any beat, pin your entry state = the previous

chapter's exit seam VERBATIM — especially age_stage at a D-06 boundary (the stage crosses AT the

chapter boundary, never inside a beat; a mid-chapter ws.age_stage write is wrong unless the v2.5

entry itself stages a crossing there). age_stage values are Title-case (Childhood/Adolescence).

13. Fork-option delta symmetry (a batch-2 MAJOR): EVERY option row carries its class-bounded

ws.integrity_score delta — corrosive rows mirror the generative side; a missing delta on one arm

silently unbalances the whole fork.

14. Beat thread STAGES come verbatim from the chapter's own Threads block / Thread_Grid row (a

batch-2 MAJOR): never escalate a stage (ANCHOR → ESCALATION/CONFRONTATION) by feel — the number

AND the stage are both canon.

15. Multi-phase-over-fewer-beats bosses: a beat's boss ref cites the FIRST phase it hosts; the §9

thin-index phase→beat map is the authoritative assignment (BE_xxxx.P1/P3 on the beats, all four

phases in the index).

16. care_band mapping: standard tier → open; elevated faith/culture tier → elevated; protected-people

chapter (its own protected-combat-framing block) → protected-elevated; maximum-care → maximum;

the D-08 chapter class → antisemitism-guardrail.

17. Enum/key hygiene: vril_density ∈ {baseline, low, moderate, high, anchor} (never mid/null); seam

integrity asserts use ws.integrity_score (integrity_level is DERIVED — never a seam key);

ws.identity_hiding_status values are the bare WS_025 enum (pressure rides a note, never a

compound token).

ws.journal_status values are the bare WS_009 enum, written only at the five WRITE-anchors of §8.4

(Ch 13 ACTIVE_HEALERS_CHILD, Ch 38 TRANSFERRED_TO_PROTAGONIST, Ch 56 DECISION_TO_PUBLISH, Ch 57 the

public-release register, Ch 58 SHARED_POST_CH58); the Ch-57 register's own value-set is under

adjudication (O-5) and is NOT settled — do not type it from this line. §8.4's remaining two bullets

(Ch 59-65 continental revisits, Ch 77 Epilogue) are state-machine READ nodes, matching WS_009's own

Ch59_65_continental_revisit_dispatcher and Ch77_epilogue_renderer consumers — a read node still carries

journal content (e.g. CH_62:108), which rides ws.journal_entries_unlocked (WS_010) or a beat note, never

a +token on the enum. There is no journal_status WRITE before Ch 13; CH_03:161's precursor token is

pre-journal texture, not a NOT_BEGUN write.

18. SQ gates use the §5.3 expression grammar with keys a prior beat's out actually produces —

has(local.k) / at(site_id with the site row's exact id) / flag() / ws.key ops — never prose

inside braces.

19. ws.six_option_cumulative_record (WS_018) is RESERVED for dialogue_six_option forks (the

canonical six enum selections). Never write it from combat_resolution / exploration /

assimilation forks — carry corrosive-pattern texture via ws.integrity_score +

ws.reputation_biography_propagation instead (this defect recurred three times: CH_05, CH_32).

Return payload (each conversion agent returns exactly this JSON)

{"chapter": "CH_NN", "checker": "PASS|FAIL", "lint": "PASS|FAIL", "beats": n, "forks": n,

"registry_corrections": [{"registry": "...", "cell": "...", "old": "...", "new": "...", "grounding": "..."}],

"decisions_recorded": ["one line each"], "notes": ["anything the verify pass must know"],

"connective_links_carried": ["IR/LINK ids typed into forks/marks/seams"]}

The batch landing loop (orchestrator, serial — agents never do these)

1. gates_config.json: append the converted files to spine_graph targets.

2. If any agent reported registry corrections: refresh docs/fidelity_baseline.json

(registry_fidelity.py … --emit-baseline docs/fidelity_baseline.json) in the SAME commit.

3. python harness/rag/index.py --scope canon (LAST content step), then python harness/run_gates.py

UNPIPED — 14/14 exit 0.

4. Fresh-context adversarial verify workflow over the batch (fidelity/reveal/care/machine-semantics

lenses per chapter + a cross-chapter seam sweep), fix-forward, re-gate.

5. Commit + tag spine/v3-bN; update the ledger docs at milestones.

20. ws-key ENUM-DOMAIN discipline (a batch-6 MAJOR — the number-coincidence trap): a ws.* key is

bound to its registry-defined DOMAIN, not its name's surface reading. ws.vimana_5th_mode_access

is the ENDGAME fifth-operational-MODE enum (UNAVAILABLE/FULL_ACCESS_TGP/PARTIAL_GE/

FRAGMENT_RESIDUE_EVIL, consumed by the Ch66-77 resolver) — NOT the modern-arc coordinate hunt

(Coordinate 5 of 6 is chapter-scoped local./prose tracking). Before writing any ws key, read its

WS_NNN row's domain/consumers; if the value you want is not in the enum's world, the key is wrong.

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