ENTITY_PERSONA_SYSTEM.md

music/ENTITY_PERSONA_SYSTEM.md

Entity Persona System — the per-entity creative-bible layer and the relationship / integrity / opportunity engine

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):
T1_Integrity_Paths_Worldstates_Master · T0_Antagonist_Network_Registry · T0_Bonus_Pool_Registry · T0_Chapter_Index · T0_Character_Index · (+17 more t0)
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.

Proposal, cascade v2.4, DRAFT. Architecture only — this invents no canon and edits no canon doc. It proposes the schema, the state model, the generation strategy, the registry wiring, and the phased build plan for the layer Josh named on 2026-07-08: every entity — person, faction, animal, place, and thing — carries a detailed persona (personality, background, history, its own life), and every faction/NPC/ally/enemy carries a relationship status that the player's integrity and decisions evolve, opening and closing opportunities but never blocking or gating progression. Josh authors chapter, numerical, naming, hard-line, and Component canon; this proposes the grounded structured framework for his confirmation. Every mechanism traces to an existing system, registry, or CVD anchor, cited inline.

Amended 2026-07-30 — the DAILY-RHYTHM AXIS. Section 5.2 gained subsection 5.2.1: the

machine-readable projection of own_life (schedule states, home and work sites, presence windows,

occupation and trade), the two child tables that hold a rhythm one row cannot (T0_Persona_Schedule,

T0_Persona_Bucket), and the ambient-population band on the Zone Catalog and Region_Index rails.

The amendment closes the SCHEMA half of DESIGN_GAP_REGISTER GAP-139 (CRITICAL, npc-simulation)

before rank 11 mints, so a persona is authored once against the complete shape rather than authored

and then retrofitted. It authors no persona content and mints no registry.

0. Why this exists, and what it is not

Josh set the depth mandate directly: the game's content goes far beyond the 30M-word chapter spine, to roughly 90-120M words once the dictionaries, lore, creative bibles, per-entity descriptions, dialogue choices, and story paths are counted (every-entity-detailed-persona-nonblocking-paths). Every entity the player meets, fights, tames, walks through, or picks up has its own life. And the living, faction, and ally/enemy entities carry a relationship that the player's integrity and choices reshape — opening and closing opportunities — under one hard rule: it never blocks or gates progression, because people need to be able to play through in different ways.

This document is the architecture for that layer. It is a downstream tier: the Story Spine and the T0 registries anchor every entity with a canonical row, and this layer is the creative-bible expansion of those anchors plus the relationship engine that makes them react. It is deliberately the counterpart, on the entity side, to what SIGNIFICANCE_LENSES.md is on the region-weave side and what RELATIONSHIP_COMBAT_SYSTEMS_NOTE.md flagged on the systems side — the three compose into one lens on the same world.

What this is not: it is not a rewrite of any existing system. The integrity, path, world-state, mark, faction-standing, and six-option-dialogue architecture is already canonical and fully specified at T1_Integrity_Paths_Worldstates_Master; the runtime strategy-token layer over every entity is already designed at docs/RUNTIME_GENERATIVE_LAYER.md; the significance lenses and their relevance filter are already proposed. This doc composes those into the persona-and-relationship layer, adds only the connective structure they leave open (the persona schema, the opportunity model, the non-gating invariant, the persona registry and doc set), and holds the do-not-invent line throughout.

1. The two things this layer adds

The layer has exactly two deliverables, and everything below serves one of them.

2. The persona schema

2.1 The premise — the persona extends the anchor row, it does not replace it

Every entity already has, or will have, exactly one canonical anchor row in a T0 registry: persons at T0_Character_Index (118 rows) and antagonist operatives at T0_Antagonist_Network_Registry (62 rows), creatures at T0_Creature_Roster (132 rows), places at T0_Region_Index (67 rows), and things distributed across T0_Weapon_Registry (72 rows), T0_Vril_Site_Registry, T0_Inscription_Spine, and the Thread-20 sacred-object content. The anchor row is the canonical skeleton — the FK-clean identity, the chapter assignment, the thread involvement, the hard-line and care-band flags, the voice reference. It is the do-not-invent surface: Josh authors it, the harness verifies it, and the fidelity gate protects it.

The persona is the creative-bible expansion of that row. It is a structured document, one per entity, that grows the row's terse fields into the full life the mandate calls for. The relationship between them is exactly the relationship between a Character_Index row's voice_signature_notes cell and the actual authored voice of that character across a chapter — the cell anchors, the persona realizes. The anchor row stays the single source of canonical fact; the persona is downstream of it and can never contradict it (the same containment the runtime context pack enforces, where the LLM voices a character but owns zero of their facts, RUNTIME_GENERATIVE_LAYER §A.2).

2.2 The common persona spine — the fields every entity carries

Every persona, regardless of class, carries a common spine of thirteen fields. The class-specific extensions in §2.4 add to this spine; they never remove from it.

2.3 The relevance gate — not every entity earns a deep persona

The mandate is depth, not sprawl. The same five-gate relevance filter that bounds the region weave (SIGNIFICANCE_LENSES.md §4) bounds persona depth, so effort follows significance. Persona depth is tiered:

The gate assigns a tier to every entity: an entity that clears the real-tie, lens, arc-service, layer-fit, and care gates at high value earns a flagship or supporting bible; one that clears only the ambient bar gets a bucket. Nothing that fails the care-and-Frame-A gate is kept-and-sanitized — it is cut, per the Gate-5 discipline.

2.4 The five authoring classes and their extensions

Josh named five entity classes — person, faction, animal, place, thing. Each maps onto the runtime taxonomy's six classes (RUNTIME_GENERATIVE_LAYER §A, which splits person into ambient / named / partner and treats environment as its own class) and onto a home registry. Each adds a class-specific extension to the common spine.

2.4.1 Person

Home registry: T0_Character_Index, with antagonist operatives cross-referenced to T0_Antagonist_Network_Registry. Runtime classes: 1 (ambient), 2 (named), 4 (partner/companion/familiar-handler). Extension fields:

The living-faithful protection binds this class hardest: a faithful community member is never a combat target and never the antagonist, and no sacred proper name sits on a mortal (the Kxao rule) and no revered real person is recast as a villain (the Liqä rule) — §17.1.

2.4.2 Faction

Home registry: T0_Antagonist_Network_Registry for the Velheim apparatus; protected communities and other groups are carried as faction rows referenced from T8 region pages Section 8 and from Character_Index faction_affiliations (there is no standalone faction registry today — see §5.2). Runtime class: 5 (societies/factions), the strategic heart of the generative layer. Extension fields:

2.4.3 Animal / creature

Home registry: T0_Creature_Roster (132 rows, already carrying folklore_behavior_substrate, folklore_attribution_source, integrity_visible_aesthetic_shift, and vril_substrate_response columns). Runtime class: 3 (fauna). Extension fields:

2.4.4 Place

Home registry: T0_Region_Index (67 rows). Runtime class: 6 (environments/world-state). A place is a persona: it has a character, a history, a life, and — per the Nemesis-class persistent-memory vision — a memory of the player. Extension fields:

2.4.5 Object / thing

Home registries: T0_Weapon_Registry (72 rows), T0_Vril_Site_Registry, T0_Inscription_Spine, and the Thread-20 sacred-object content; objects are the one class whose anchors are distributed, and the persona layer unifies them through the T0_Persona_Index in §5.2. Runtime binding: objects are mostly deterministic props; a legendary or charged object carries a thin generated read akin to the environment class. Extension fields:

3. The relationship / integrity / opportunity engine, and the non-gating invariant

3.1 What already exists — the state model this engine drives

The relationship engine is not built from scratch; it drives state variables that are already canonical and already rowed in T0_Worldstate_Variables:

The engine adds one new state class to this set — the opportunity flag — and the invariant that governs it.

3.2 Relationship tiers — the disposition ladder

An entity that holds a relationship with the player (persons, factions, companions, bonded creatures) carries a disposition on a ladder. The ladder is descriptive here; the numerical bands are Josh's to set and are not invented in this doc, consistent with the do-not-invent rule and with the way faction standing already runs on an authored -100 to +100 range.

Because integrity is read as a real property, an entity notices who the protagonist has become even without having observed her deeds — the disposition is generated fresh each encounter from the current state (RUNTIME_GENERATIVE_LAYER §A.2). A skilled player cultivates dispositions; they can never configure an entity in a menu, and they can never jailbreak canon out of a persona.

3.3 Opportunities — what opens and closes

An opportunity is a bounded, non-progression-critical affordance that a relationship, an integrity band, or a mark opens or closes. The engine adds opportunity_flags as a new worldstate-variable class (a per-opportunity object: opportunity_id, open/closed state, and the relationship/integrity/mark conditions that set it). Opportunities are, by construction, the texture and the kit around the spine, never the spine itself. They include:

Opportunities close symmetrically: participating in a raid from inside a community's trust closes the found-family opportunity there and burns standing with the victims; behaving corrosively closes the recharge-site and familiar-approach opportunities as the world reads the negative signature. Closure is real and consequential — it reshapes relationships, the mark, and the available texture. What closure never does is stop the game.

3.4 The hard rule — the non-gating invariant, designed concretely

Josh's rule is absolute: opportunities open and close, but they never block or gate progression; the player must be able to play through in different ways, with no dead-ends. This is not a hope pinned to careful authoring — it is an invariant enforced by four concrete mechanisms and one automated gate.

The one reconciliation this requires is with §8.7, which says faction standing gates certain questline entries. Read within the invariant, that gating is on optional side-questline branches and their specific rewards — which variant runs, which ally appears, which reward tier drops — never on main-spine progression, and every gated branch has a parallel ungated route to the node's completion under Mechanism 3. The proposal is to formalize this reading: opportunity flags and faction gates apply to the optional and the textural, and the harness enforces that they never touch the progression_critical set.

3.5 The no-dead-end gate — automating the invariant

The invariant is only trustworthy if it is checked, not asserted (the repo's hard-won verify-with-fan-out lesson). The proposal adds a no-dead-end reachability gate to the harness, run alongside the fidelity gate:

This mirrors the runtime validator's role — the sim always runs a valid token because an invalid one is caught and replaced with an authored fallback (RUNTIME_GENERATIVE_LAYER §2.3-2.4). Here the build always ships a playable-through world because a dead-end is caught and flagged before it can reach the player.

4. Generation and scale — how 90-120M words get produced

4.1 The three production modes, and what is authored versus generated

The corpus is produced in three modes, and the distinction between them is the load-bearing scale decision.

4.2 The scale decomposition — why 90-120M is plausible

The figure is Josh's target; the decomposition below is an illustrative accounting for his confirmation, not locked canon. It shows the target is reachable without padding, and where the bulk sits.

The relevance gate (§2.3) is what keeps this from becoming 200M words of sprawl: depth follows significance, ambient entities share buckets rather than getting bibles, and the distance-decay budget bounds how far each region's web reaches.

4.3 The guardrail — every generated persona is canon-graph and Care-Doctrine bound

Build-generation and runtime-generation run the same four-stage guardrail sandwich, at two different times (RUNTIME_GENERATIVE_LAYER §2, §D):

At build time the critic is the fresh-context normalize-doc adversarial pass (the standing normalization gate) plus the lens-coverage-and-relevance check; at runtime the validator is deterministic and per-call. The Care Doctrine's Gate 5 is a pass-or-cut condition at both times, never a softening. And the do-not-invent rule binds through all of it: generation aligns and expands grounded structure; it never decides chapter, numerical, naming, hard-line, or §17 canon, which stays Josh's, flagged when a persona would need it.

5. How the layer anchors to the spine and the registries

5.1 The spine is the persona manifest

The Story Spine already enumerates, per chapter, exactly the roster this layer expands. The CH_12 entry, for instance, carries a Key ally NPCs section (the babalawo, the python-temple priestess, the Agojie captain, the Hogon elder, and more, each with a role-and-voice sketch), a Boss structure section (the sovereign and the sub-bosses, each an antagonist persona), a companions_and_recurring_npcs mechanical anchor listing every canonical figure by character_id, an antagonist_operative row, a fauna and sites set, and the objects (the Ifa implements, the goldweights, the inscription fragment). That roster is the manifest of which personas Ch 12 needs. The persona layer is the downstream tier the spine anchors: for each entity the spine names, the persona layer holds the bible; for each relationship beat the spine surfaces (the Dahomey Decision as the chapter's assimilation-integrity test), the engine holds the state.

This makes the persona layer live and cascade-coupled in exactly the way the spine is a living backbone (assimilation-integrity-choice-mechanic): as each chapter builds one at a time, its roster's personas are expanded and its relationship and opportunity beats are authored against §5.5 and the non-gating invariant, and the spine entry is enhanced to point at them. The persona layer grows one node at a time, held in alignment by the spine, exactly as the systems in RELATIONSHIP_COMBAT_SYSTEMS_NOTE.md are meant to.

5.2 The new structure — the persona index and the persona doc set

Two new structures hold the layer, both FK-clean and harness-checkable:

The relationship engine extends the existing T0_Worldstate_Variables rather than adding a registry: it adds the opportunity_flags variable class and formalizes the mark as a first-class variable (today the mark is described in §5.5.4 and carried implicitly in the accumulation variables). No standalone faction registry exists today; the proposal is to add a lightweight faction anchor (either a T0_Faction_Index or a formalization of the faction rows referenced from T8 region pages Section 8) so faction personas have a clean anchor row like every other class, rather than living only in Character_Index faction_affiliations strings.

5.2.1 The daily-rhythm axis — the columns that make own_life readable by a runtime component

Amendment of 2026-07-30, closing the schema half of DESIGN_GAP_REGISTER GAP-139 (CRITICAL,

npc-simulation) before rank 11 mints. The gap in its own terms: the 5.2 column list enumerated

persona_id, anchor_ref, entity class, persona tier, care band, significance-lens set, persona-doc

path and runtime class, and own_life was not among them — so the field 2.2 calls the one that makes

the world feel populated rather than staged lived only as prose in a persona doc, unreadable by a

runtime component. The queued consumer states its own blocker: PRE_5090_BUILD_PLAN_VOL2 LW-8

(ambient-NPC presence and schedule) reads "no schedule field in Character_Index", and

DESIGN_GAP_REGISTER entry 2 recommends one shared schedule component with a three-to-four state enum

covering home, work, social and sleep. Entry 5 asks the matching question for crowds, and no number

exists anywhere for how many people a village, market, port or trade compound holds. Re-measured on

2026-07-30 against the live tree: a header scan across all sixty-five live registry tabs returns ONE

column matching schedule, routine, occupation, population or crowd —

T0_Chapter_Index.ambient_crowd_voice_signature_array, a voice-casting array and a false friend in

exactly the way footfall_surface is a walk-surface foley material rather than an occupancy figure.

The same scan returns twenty-seven trade, home and density columns across eighteen registries, so

the near-zero is a real absence and not a search artifact.

own_life's DEFINITION is unchanged by this amendment: its 2.2 bullet gains one pointer sentence and

nothing else. It remains the persona doc's prose account of a life and the authority on what that

life means. The columns below are its projection: the small, closed,

queryable part a schedule component, a spawner ring and an asset budget can read. This subsection

authors no persona content. Population belongs to rank 11 for named personas and buckets, and to

rank 5 for the crowd budgets that read the population band.

The daily-rhythm columns on T0_Persona_Index, added to the 5.2 list:

the closed four-value vocabulary home, work, social and sleep. One shared vocabulary for every

class and every culture, per register entry 2; the cultural specificity lives in the sites, the

hours and the prose, never in a bespoke per-NPC state machine.

the form state:HH-HH on a twenty-four-hour local clock, wrapping permitted so a sleep window may

read sleep:21-05. This is the common case and it needs no child rows: a weaver at her loom by day

and at home by night is two pairs.

prefix-typed site reference: interior:INT_NNNN resolving to T0_Interior_Space_Registry.interior_id,

or zone:<zone_id> resolving to a Zone Catalog row on the persona's own region page. The

prefix-typed form follows the polymorphic-reference precedent the repo already carries at

T0_Dialogue_Line.speaker_ref, T0_Bonus_Pool_Registry.capability_ref and

T0_RNG_Drop_Table.linked_entity_id; the interior half is a declarable foreign key today and the

zone half is an id-format until the carried zone-catalog registry work order gives zone rows a

table, at which point it becomes a second declared key.

T0_Character_Index role slug (the aksumite coffee-ceremony mistress, the manggarai cotton-ikat

weaver) where nothing can group or count it. Cited to the region page's own role vocabulary, in the

culture's own term per 17.1 self-naming.

canonical trades, and blank where it is not. This is the join that lets a trade home base with an

upgrade ceiling be staffed by named practitioners instead of reading as a prop.

which is the edge that makes 2.3's pooled model countable rather than aspirational.

alternate_reach_declared or non_progression, described under the invariant below.

T0_Persona_Schedule — the child table for a rhythm one row cannot hold. A persona whose day varies

by calendar day, by world state, or across more windows than presence_window can carry gets rows

here, and those rows are authoritative for the hours they cover while presence_window remains the

fallback for the hours they do not. Its columns: schedule_id as the primary key; persona_ref as the

foreign key to T0_Persona_Index.persona_id; window_ordinal for the ordering within a persona, because

a CSV carries no implicit order; schedule_state from the same closed four-value vocabulary;

window_start_hour and window_end_hour on the twenty-four-hour local clock; site_ref in the same

prefix-typed form as home_site_ref; day_class for the calendar selector, defaulting to every_day and

otherwise carrying the region's own calendar term (the Ch-4 Pawukon day names being the canonical

hard case); condition_expression, validated by docs/CONDITION_EXPRESSION_GRAMMAR.md and

harness/condition_expr.py rather than by this spec, for a window that only exists in some world

states; and the house tail source_ref, notes, lifecycle, superseded_by and extensions.

T0_Persona_Bucket — the ambient tier's roster, which 2.3 has always implied and never rowed. One row

per pooled persona bucket, so a crowd of forty sharing six buckets is six authored rows rather than

forty absent ones. Its columns: bucket_id as the primary key; region_ref as the foreign key to

T0_Region_Index.region_id; zone_ref as an optional narrowing to one Zone Catalog row;

community_or_culture and role_label carrying the region page's own Section-4 and Section-12 role

terms; occupation_ref and trade_ref exactly as on the index; schedule_state_set and presence_window,

because a crowd has a day too and a market that is busy at dawn and empty at dusk is the whole point;

voice_register_ref as the foreign key to T0_Voice_Registry.voice_id, targeting the cultural-register

rows so a bucket speaks in a registered voice rather than a default one; appearance_register for the

dress-and-bearing note; care_band resolved from T0_Hard_Lines exactly as the persona spine resolves

it, since a pooled crowd inside an elevated-care community carries that community's bar; and the

house tail source_ref, notes, lifecycle, superseded_by and extensions.

The ambient-population band, on two rails. ambient_population_band joins the region page Zone Catalog

as a page-authored extension column, at the per-zone grain the crowd mesh and material budgets

actually need, and ambient_population_band_default joins T0_Region_Index as the region-level default a

zone may override — the naming following the env_density_band_default column the same rail already

carries. Both take one closed ordered vocabulary: uninhabited, sparse, occupied, busy, crowded. The

vocabulary is deliberately a MAGNITUDE band and not a head count: the count each band authorises is an

asset-budget decision that belongs with rank 5's crowd mesh and material sizing, and a number invented

here would be false precision of exactly the kind the empty worldcover_biome cells were left blank to

avoid. Village, market and port scale is the target; metropolis density is out of scope, per register

entry 5.

The non-gating invariant, extended to time. The persona layer's absolute rule is that opportunities

open and close but never block progression (3.4), and a schedule is the one persona field that can

break that rule by accident rather than by design: a required teacher asleep behind a locked door at

the hour the player arrives is a dead end that no authored decision chose. So the invariant reads in

time as well as in state. Every persona whose schedule bounds a progression-critical interaction reads

presence_nongating_class as always_present, meaning the schedule never removes them from reach, or as

alternate_reach_declared, meaning a declared alternate route to the same objective is reachable in

every window — a second practitioner, a message left, a waking condition, a different site. Personas

outside the progression-critical immune set read non_progression, and their windows are free. A closed

window is consequence and texture, per Mechanism 4: it changes the flavour, the difficulty and the

route, and it never changes whether the node can be completed. Asserting this over the authored rows

is the no-dead-end reachability gate's job (3.5, harness/check_no_dead_end.py) and is named here as

the tooth that arms with the mint, not as a claim this amendment has already checked.

What lands with the mint. This subsection is the column contract and nothing more; the three tables

are minted by rank 11. In that same commit the foreign keys are enrolled in docs/fk_spec.json —

persona_ref, persona_bucket_ref, trade_ref, region_ref, zone_ref and voice_register_ref as declared

keys, and schedule_state_set, presence_window, the prefix-typed site references,

presence_nongating_class, day_class and the two population bands as id-formats over their closed

vocabularies — the extension columns are declared in docs/registry_extensions.json under the

persona-rhythm owner, and the fidelity baseline is refreshed by re-emission in the same commit, per

the standing declare-in-the-same-commit rule. The population band's two columns and their ownership

declaration land with this amendment, because their rails exist today; the persona and bucket columns

cannot, because their tables do not.

5.3 The FK and graph wiring

The layer is a graph, wired to the existing canon graph so the harness verifies it the way it verifies every registry:

5.4 The runtime binding closes the loop

Each persona's runtime_binding field is what the on-device layer consumes: the persona doc plus the anchor row plus the resolved hard-line vector are baked into the per-entity context pack at load (RUNTIME_GENERATIVE_LAYER §2.1, §D.1), frozen, and KV-prefix-cached once. At play time the strategy-token loop reads that frozen pack and emits the small validated token that the deterministic sim executes — the disposition, the faction intent, the behavior flavor, the ambient mood. The persona is the write-time-structured truth; the runtime token is the read-time-cheap animation of it. The authored and build-generated corpus is the invariant; the runtime layer varies the texture around it, which is the replayability bet made literal.

6. Honest current state

The verified read, matching the pattern the CANON_AUDIT already established for the wider foundation (systems and vision strong; per-chapter structured anchors largely unpopulated):

7. Build plan

Five phases, ordered cheapest-and-unblocking-first, coupled to the living cascade, and validated throughout. Each phase names its exit condition.

8. Guardrails held

9. Return payload

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