- Introduce new narrative probes for episode grammar, ensemble balance, and prose redundancy - Implement narrative presentation coverage tracking for better event visibility - Update harness scenarios to include new narrative ingestion and persistence tests - Refactor event emission to include narrative development IDs and presentations - Expand InterestCandidate to support narrative presentation models and roles
31 KiB
Narrative coherence and durable Legacy plan
Status
Implemented — phases 0–8 complete (2026-07-23).
Release evidence: clean NML restart; 20/20 full regression scenarios; semantic presentation
coverage at intake=1/0, compat=0, redundant=0; 10/10 canonical event-family idempotency
matrix; persistence round-trip and corrupt/newer-schema recovery; Legacy lineage/combat
consolidation; episode grammar and ensemble balance; relationship live suite 3/3; seeded and
20-second natural story soaks. The persistence format and real save-complete/load-complete hooks
are integrated; automated verification intentionally uses isolated DTO round-trips rather than
overwriting a player's WorldBox save.
This is the successor to
mc-story-director-rewrite-plan.md. The story-first
director and current Saga UI are the foundation; this plan does not reopen that cutover or redesign
the HUD.
The work is complete only when:
- every selected orange reason is rendered once from semantic evidence;
- each observed game event enters narrative state through one idempotent writer;
- captions, threads, Cast, and Legacy refer to the same event identity;
- durable MC stories survive a real save/reload cycle;
- episodes communicate a deliberate development rather than only selecting the highest related score; and
- the old label-concatenation, duplicate-routing, and raw-fact Legacy fallbacks are removed.
Product outcome
IdleSpectator should feel like an automatically edited chronicle of a small ensemble.
For every authored cut, the player should be able to understand:
- Who is this about?
- What changed?
- Who or what was involved?
- Why is this development part of the current story?
- What lasting consequence belongs in this life’s Legacy?
The same confirmed observation must answer all five questions. Camera captions and Legacy may use different tense and detail, but they must not invent separate versions of the event.
Problems this plan resolves
Multiple prose owners
An InterestCandidate.Label may already say Gains a child, Omyahel, after which
CaptionComposer.EnrichBeat asks SagaProse.BeatClause to add their child. Both pieces are
individually reasonable, but together they repeat the same relationship.
The structured NarrativeProse path avoids this for a small set of event ids. Most event families
still depend on authored labels, feed-local string construction, or generic relationship suffixes.
Multiple fact writers
Several Harmony patches call both LifeSagaMemory.Record... and
NarrativeDevelopmentRouter..., while the memory helper also calls the router. Store idempotency
limits visible damage, but ownership is ambiguous and the system attempts to emit the same semantic
change more than once.
Session-only stories
LifeSagaSession.ResetForWorldChange currently clears Saga memory and the narrative store after a
map load. This is safe, but it makes “Legacy” a session summary rather than a durable world history.
Related-shot selection is not yet episode composition
StoryScheduler owns story-first selection and tracks episode purpose, but
EpisodeShotSelector still chooses mainly through one editorial score. It does not yet require the
episode to cover distinct editorial roles such as establishment, change, pressure, and consequence.
Legacy has overlapping sources
Legacy currently combines narrative chapters, selected LifeSagaFact records, special combat
summaries, and presentation-layer suppression rules. It works, but its correctness depends on
several layers agreeing about duplicates.
Non-goals
- No new tabs, dossier layout, or rail redesign.
- No return to event-first camera ownership.
- No invented emotions, motives, causal claims, victories, heirs, or rivalries.
- No forced establishing shots when presentable footage does not exist.
- No persistence of live
Actorreferences, camera position, current combat phase, or active episode timing. - No permanent compatibility setting for the old prose or old narrative writer.
- No attempt to turn every ambient activity into a durable life event.
Target architecture
Confirmed game observation
|
v
NarrativeEventRecorder -- stable id + idempotency + provenance
|
+--> immutable NarrativeEventRecord (one world fact)
|
+--> character-perspective NarrativeDevelopment projections
| +--> threads / consequences / story potential
| +--> durable per-life indexes
|
+--> NarrativeEventReceipt
+--> InterestCandidate semantic presentation + exact development id
InterestCandidate --> StoryScheduler --> camera cut
--> NarrativeRenderer --> orange Beat / Context
NarrativeEventRecord + threads + consequences
--> LegacyChapterBuilder --> Legacy
Durable records / identities / threads / preferences
<--> versioned world narrative save
One observation, multiple perspectives
A world event is recorded once. It may produce more than one character-perspective development.
Example: a birth creates one NarrativeEventRecord, then:
- the parent projection can render
Welcomes first child Omyahel; - the child projection can render
Is born to Joon; and - a partner projection may be created only when a confirmed parent/partner relationship supports it.
These projections share the same event id. They are not mirrored copies pretending to be separate world events.
Core contracts
1. One fact, one writer
Every confirmed narrative observation enters through NarrativeEventRecorder.Record. Callers do
not separately write Saga memory, narrative developments, consequences, or thread state.
2. One selected event, one presentation model
Every selected candidate carries an exact semantic presentation reference. Caption composition must not search for “the latest similar event” and must not infer the related actor from prose.
3. One semantic fact, one phrase
The orange Beat is rendered as a complete sentence by one renderer. Relationship information is included only when it contributes a fact not already expressed by the action.
4. Context adds information
Context may name confirmed pressure, an unresolved question, or a consequence. It must not paraphrase the Beat, repeat Identity, or expose scheduler terminology.
5. Runtime and durable time are different
- World date/tick orders persistent facts and supplies display dates.
- Session monotonic time controls cooldowns, holds, momentum decay, and active episode timing.
- Session timestamps are never serialized as durable chronology.
6. Persistence is fail-safe
Missing, corrupt, or newer-version narrative data must not prevent a world from loading. The mod logs the problem, preserves the unreadable file when possible, starts an empty narrative state, and continues operating.
7. Episode grammar never fabricates footage
The scheduler may wait, hand off, or close an episode when a needed role has no presentable candidate. It may not manufacture a development just to complete a planned sequence.
Proposed data model
NarrativeEventRecord
Immutable world observation:
- schema version;
- stable event id;
- semantic event kind;
- subject, other, and participant identity snapshots;
- relationship/evidence roles rather than player-facing relation text;
- correlation, family, city, kingdom, war, and plot keys;
- previous state, new state, and confirmed outcome;
- world tick/date;
- evidence source and confidence;
- magnitude and presentability;
- ingestion provenance and dedupe key.
NarrativeDevelopment
Character-perspective projection:
- event id;
- perspective character id;
- development kind and narrative function;
- other character/participants in that perspective;
- thread id when assigned;
- confirmed change/outcome;
- session-observed time for runtime scheduling;
- world tick/date for durable ordering.
Existing NarrativeDevelopment fields should be migrated rather than duplicated where possible.
SubjectId becomes the perspective owner explicitly instead of relying on an implied viewpoint.
NarrativePresentationModel
Player-facing semantic input, containing no final English:
- event/development id;
- presentation kind and tense;
- perspective id;
- primary and secondary identity snapshots;
- evidence-backed relationship role;
- ordinal/count when confirmed;
- change and outcome tokens;
- ensemble side snapshots when applicable;
- confidence and provenance.
The model is attached to or referenced by InterestCandidate. Dynamic ensemble cards may retain a
typed ensemble presentation rather than becoming durable character developments.
NarrativeEventReceipt
Returned by the recorder:
- whether this was a new event or a duplicate observation;
- stable event id;
- development ids by perspective;
- semantic presentation model for the emitting candidate;
- any rejection or confidence reason.
Persistence DTOs
Use separate plain DTOs rather than serializing runtime dictionaries or Unity/game objects.
Persist:
- durable event records within retention limits;
- identity snapshots;
- character story indexes;
- open/resolved threads and consequences;
- authored Legacy chapter state if it cannot be deterministically rebuilt;
- Saga Prefer choices;
- schema version, stable world key, and last durable world tick.
Rebuild after load:
- dictionaries and reverse indexes;
- effective story potential and runtime decay anchors;
- live actor bindings;
- current Saga ranking;
- captions, active episode, cooldowns, and camera state.
Implementation phases
| Phase | Primary outcome | Risk |
|---|---|---|
| 0 | Measurable baseline and explainable cuts | Low |
| 1 | Relationship/lineage captions rendered once | Medium |
| 2 | One canonical, idempotent narrative writer | High |
| 3 | Complete typed orange-reason coverage | High |
| 4 | One semantic Legacy chapter pipeline | Medium |
| 5 | Save/reload durability and recovery | Very high |
| 6 | Deliberate episode grammar | High |
| 7 | Long-horizon ensemble balance | Medium |
| 8 | Old-path deletion and release proof | Medium |
The order is intentional. The visible caption defect is fixed first, canonical ingestion then removes its root ownership ambiguity, and persistence begins only after the in-memory facts and chapters have one stable shape. Scheduler behavior changes after persistence so camera tuning cannot hide state migration defects.
Phase 0 — Baseline, inventory, and observability
Work
- Capture the current full regression result and a natural story-first soak as the comparison baseline.
- Add a machine-readable inventory of candidate families and current prose provenance:
structured narrative,
EventReason, feed-authored label, ensemble renderer, or compatibility fallback. - Add selection telemetry for:
- candidate key and asset id;
- event/development id, when present;
- prose path;
- perspective and related ids;
- thread and episode purpose;
- whether Beat, Context, or relation semantics overlap.
- Add a deterministic reproduction of:
Gains a child, Omyahel · their child. - Add a harness snapshot that explains one selected cut from observation through rendered caption.
New harness targets
narrative_prose_redundancynarrative_cut_explainnarrative_prose_coverage
Exit gate
- Existing regression remains green.
- The child redundancy test fails for the expected semantic reason before the fix.
- Every naturally selected authored cut reports its prose provenance.
Phase 1 — Semantic presentation foundation and relationship cutover
This phase fixes the reported orange-reason problem first while establishing the model used by the remaining migrations.
Work
- Add
NarrativePresentationModel, explicit viewpoint, and a renderer that owns the complete Beat. - Stamp an exact event/development reference onto
InterestCandidate; stop usingNarrativeStoryStore.LatestFor(..., 8f)when an exact id exists. - Implement semantic rendering for:
- lover formed/broken;
- friend formed;
- parent/child and birth;
- death and grief with confirmed relationship;
- gifts or relationship interactions that name both actors.
- Encode relationship roles as data. Do not derive them from already-rendered text.
- Add semantic novelty checks:
- a relation descriptor is omitted when the action already states it;
- ordinal detail such as
first childis allowed because it adds information; - names appear once within a Beat;
- Context cannot repeat the Beat’s event, relationship, role, or outcome tags.
- Structured Beats bypass
CaptionComposer.EnrichBeat. - Keep the legacy label path only for unmigrated event families during this phase.
Required prose matrix
Test each applicable event with:
- named and unnamed related actor;
- living and dead related actor;
- MC as subject and MC as related actor;
- parent and child viewpoints;
- first, second, and 3+ child;
- current and former lover;
- best friend and ordinary friend;
- missing/stale actor binding with a valid identity snapshot;
- status-banner ownership;
- re-entry caption;
- Beat plus thread Context.
Exit gate
- No relationship or lineage Beat repeats its action, relationship, or related name.
- All relationship candidates bind to the exact development they display.
- No viewpoint reversal in the deterministic matrix.
- Existing caption, relationship, Legacy, and presentability scenarios pass.
Phase 2 — Canonical event ingestion and single-writer cutover
Work
- Introduce
NarrativeEventRecorderandNarrativeEventReceipt. - Define stable dedupe keys from semantic identity and durable world chronology. Use a
source-instance/correlation token when the game exposes one; otherwise use a bounded
family-specific dedupe window so two legitimate same-kind events are not collapsed. Do not use
final prose or session-only
Time.unscaledTimeas persistent identity. - Make the recorder transaction:
- validate evidence;
- record or identify the event;
- create perspective developments;
- apply thread rules;
- create consequences;
- update per-life indexes;
- return candidate presentation metadata.
- Convert
LifeSagaMemoryinto a read/index/projection facade. Its publicRecord...helpers either delegate once to the recorder during migration or are removed. - Remove direct
NarrativeDevelopmentRouter...calls from patches that already record the same observation. - Migrate ingestion by family:
| Order | Family | Representative sources |
|---|---|---|
| 1 | Relationship and lineage | ActorChroniclePatches, RelationshipEventPatches, relationship feed |
| 2 | Death, kill, rivalry, combat outcome | actor patches, combat/story hooks |
| 3 | Role, founding, home gain/loss | roster deltas, city/kingdom hooks |
| 4 | War and plot | war/plot feeds and lifecycle patches |
| 5 | Transformation and recovery | status/happiness routers |
| 6 | World-context events | disasters, eras, discoveries, civic changes |
- Preserve observation-before-confirmation rules: a behavior return value alone is not semantic success.
- Add bounded duplicate-observation telemetry by event family and provenance.
New harness targets
narrative_ingestion_idempotentnarrative_ingestion_perspectivesnarrative_hook_single_writernarrative_event_candidate_alignment
Exit gate
- One game observation produces one event record regardless of how many patched surfaces observe it.
- Expected character projections are created once.
- No production patch calls both memory recording and the development router.
- Candidate, caption, consequence, and thread all report the same event id.
- Story potential and Legacy counts do not inflate under duplicate hook invocation.
Phase 3 — Complete semantic orange-reason migration
Work
Migrate all remaining selected candidate families to typed presentation:
| Family | Rendering rule |
|---|---|
| Role and succession | Describe the confirmed role transition; do not repeat Identity |
| Founding and home | Name the confirmed home/settlement when known |
| Combat and rivalry | Separate live action, decisive outcome, and earned rivalry |
| War and plot | Use confirmed sides/phase/outcome; avoid invented victory or betrayal |
| Disaster and loss | State the observed impact, not an assumed emotion or cause |
| Status and transformation | Distinguish application, ongoing condition, recovery, and completion |
| Family/war/plot/combat ensembles | Keep typed side/count presentation owned by the ensemble renderer |
| Milestones and discoveries | State the concrete first/change |
| Ambient texture | Use concise authored semantic templates; do not create durable consequences |
| Meta/world context | Preserve useful world-scale subjects without pretending they are MC developments |
Additional work:
- Make
EventReasona compatibility facade over typed rendering where it remains useful; prevent feeds from constructing selected prose ad hoc. - Replace string-based verb/relation classification with presentation enums/tags.
- Remove generic
SagaProse.BeatClauseconcatenation from caption composition. Relation evidence remains useful as semantic input to the renderer. - Add a safe unknown-event presentation that never appends inferred relationship prose.
- Audit capitalization, punctuation, tense, names, raw ids, placeholder tokens, and missing actor behavior across the entire catalog.
- Track structured coverage at registry intake and at actual selection.
Exit gate
- 100% of selected authored candidates use a typed renderer or a typed ensemble renderer.
- No selected candidate uses a feed-local final sentence.
- No raw asset id, enum name, placeholder, or duplicated semantic tag reaches the HUD.
- The generic label-plus-relation-suffix path is deleted.
- Catalog prose audit and full presentability suite pass.
Phase 4 — Unified Legacy chapter authoring
Work
- Introduce
LegacyChapterBuilderas the only Legacy author. - Build chapters from semantic events, consequences, and resolved/meaningfully changed threads.
- Replace presentation-time competition between:
- narrative chapters;
- raw
LifeSagaFactlines; - roster-stamped prose chapters; and
- special combat prose.
- Keep thread-specific merge policy:
- partnership formation and loss remain distinct identity changes;
- 3+ births consolidate into a lineage chapter;
- repeated kills consolidate into one combat chapter;
- an earned rivalry remains distinct from a kill count;
- role gain/loss may form one reign chapter when both are known;
- war/plot chapters prefer confirmed outcome over entry notification;
- living Cast relationships do not echo in Legacy unless a historical transition matters.
- Rank chapters by consequence importance, narrative completeness, recency, and diversity—not raw event volume.
- Preserve more semantic chapters than the two-line panel can display; presentation chooses the best two without destroying history.
- Ensure a deceased or currently unbound character’s Legacy renders entirely from snapshots.
New harness targets
narrative_legacy_chaptersnarrative_legacy_lineagenarrative_legacy_role_arcnarrative_legacy_war_outcomenarrative_legacy_snapshot_only
Exit gate
- Each displayed Legacy line traces to one chapter id and its source event/thread ids.
- Cast, current Context, and Legacy do not repeat the same unchanged fact.
- Existing combat consolidation remains correct.
- Raw-fact prose fallback is removed from production Legacy rendering.
Phase 5 — Durable narrative persistence
Phase 5A: integration spike
Before writing the format:
- Identify the most reliable WorldBox/NML save-start, save-complete, and load-complete hooks.
- Determine a stable world/save identity that survives process restart. The current runtime bind key contains object identity and is unsuitable for durable storage.
- Confirm the supported mod-data location and atomic replace behavior on the target platforms.
- Verify actor ids across a real save/reload and document cases where ids are recycled or absent.
Default implementation: a versioned sidecar in the supported mod-data location, keyed by stable save/world identity. If the game exposes a reliable per-save mod-data container, prefer that after the spike.
Phase 5B: format and lifecycle
- Add a versioned
NarrativeSaveStateDTO and explicit mapper. - Serialize only durable fields and world chronology.
- Write atomically through temporary file plus replace; keep one last-known-good backup.
- Tag each snapshot with its confirmed save generation/fingerprint and durable world tick.
- Debounce dirty writes, but promote a snapshot to durable state only at a confirmed world-save boundary. A transition or clean-shutdown flush may preserve a recovery snapshot, but it must not silently become the history of an older game save.
- On load, select the snapshot matching the loaded save generation. Never apply narrative events newer than the loaded world tick; preserve unmatched newer data as a separate recovery generation.
- On load:
- clear runtime state;
- read and validate the matching world state;
- rehydrate indexes;
- rebind living actors;
- retain missing/dead actors as snapshot-only history;
- rebase runtime freshness from durable world time;
- resolve or abandon threads whose evidence no longer exists;
- rebuild roster ranking and clear the active episode.
- Add retention limits by character/event importance and test maximum expected file size. Events referenced by retained threads, consequences, or Legacy chapters cannot be pruned; compact them into a durable semantic chapter record before their detailed event payload is removed.
- Add schema migration support beginning with version 1. Unknown newer schemas load empty without overwriting the unreadable data.
- Persist Saga Prefer by stable character/world identity. Do not persist transient rail order.
New harness and live targets
narrative_persistence_roundtripnarrative_persistence_corruptnarrative_persistence_unknown_versionnarrative_persistence_missing_actornarrative_persistence_no_replay- a real save, process restart, and reload check
Exit gate
- Relationships, births, deaths, threads, consequences, Legacy, and Prefer survive a real restart.
- Reload does not duplicate events or replay old changes as new camera developments.
- A corrupt or mismatched sidecar cannot block game load.
- Snapshot-only Legacy remains readable when an actor cannot be rebound.
- Persistence adds no recurring full-world scan and no visible save hitch.
Phase 6 — Editorial episode grammar
Data additions
Add:
EpisodeShotRole: Establish, Development, Pressure, TurningPoint, Payoff, Consequence, Reintroduction, WorldContext;- covered/pending role ledger;
- recent semantic event/action keys;
- non-progress and repeated-subject counters;
- explicit episode completion reason;
- per-thread coverage debt and last-shown world time.
Work
- Assign shot roles from semantic development/function and current episode state.
- Define a minimal grammar by
EpisodePurpose:
| Purpose | Desired communication |
|---|---|
| Establish | protagonist/cast plus the confirmed opening change |
| Develop | new pressure or state change |
| Payoff | turning point or confirmed outcome |
| Consequence | what the outcome changed for the protagonist |
| Reintroduce | identity/context plus a genuinely new development |
- Apply eligibility constraints before scalar score:
- the shot must advance an uncovered role or be a permitted interrupt;
- identical semantic action/subject combinations cool down;
- texture cannot satisfy episode progress;
- routine combat remains within its existing budget;
- critical world events can interrupt and the episode may later resume.
- Use thread coverage debt to favor stories that have developed off-camera.
- End or hand off when:
- the information goal is met and a settle beat has elapsed;
- no presentable development arrives before expiry;
- the thread resolves or becomes invalid;
- a stronger coherent episode has materially greater coverage debt.
- Log why each selected shot satisfied the episode and which role remains uncovered.
New harness targets
narrative_episode_grammarnarrative_episode_no_forced_footagenarrative_episode_reintroductionnarrative_episode_interrupt_resumenarrative_episode_coverage_debt
Exit gate
- Deterministic episodes cover distinct roles instead of repeating the same development class.
- Ordinary unrelated material cannot break productive continuity.
- Critical interrupts and return behavior remain correct.
- An unavailable payoff closes safely rather than starving world coverage.
- Existing combat share and maximum-run gates remain green.
Phase 7 — Ensemble balance and long-horizon direction
Work
- Add bounded screen-time debt for roster MCs and active threads.
- Count meaningful authored exposure, not seconds spent on unavoidable camera holds.
- Prefer an under-covered MC only when they have a presentable new development.
- Prevent prolific event generators—combatants, rulers, or frequently reproducing characters—from monopolizing the narrative through volume alone.
- Reintroduce dormant MCs only after a confirmed change.
- Preserve priority order: critical world event > productive episode > meaningful under-covered thread > bounded world fill.
- Add telemetry for:
- authored cuts per MC/thread;
- development-family diversity;
- repeated semantic action runs;
- episode completion/expiry;
- dormant reintroduction;
- screen-time debt before and after selection.
New harness targets
narrative_ensemble_balancenarrative_prolific_mc_guardnarrative_dormant_reintroduction
Exit gate
- A prolific MC cannot win repeatedly without distinct developments.
- Under-covered MCs receive attention when meaningful footage exists.
- No balancing rule forces a weak or unpresentable cut.
- Prefer and world-critical behavior remain unchanged.
Phase 8 — Cleanup, documentation, and release hardening
Delete or retire
- generic label-plus-relationship Beat enrichment;
- latest-similar-development caption lookup when exact ids exist;
- direct patch-level dual writes;
- router side effects hidden inside memory helpers;
- raw-fact Legacy prose fallback;
- session-time-based durable event ids;
- temporary shadow adapters, migration counters, and harness-only cutover flags.
Documentation updates
README.mddocs/life-saga.mddocs/event-reason.mddocs/event-e2e.mddocs/story-planner.md- this plan’s status and completed checkpoints
Historical plans remain history; their ownership tables must not override the final architecture.
Final verification sequence
- Clean NML compile.
- New narrative scenario group.
- Existing relationship, caption, Saga, Legacy, scheduler, and persistence scenarios.
- Full
./scripts/harness-run.sh regression. - Three organic developed-world soaks:
- relationship/lineage-heavy;
- conflict/war-heavy;
- mixed long-running world.
- One save → quit → restart → reload continuation soak.
Release gates
- Zero camera-health, focus/reason alignment, placeholder, or object-focus failures.
- Zero detected semantic duplication in selected Beat/Context/Legacy.
- 100% typed presentation coverage for selected authored cuts.
- Zero double-ingestion failures.
- Zero old-event replay after reload.
- Combat remains at or below the existing 35% long-sample gate.
- Routine uninterrupted combat run remains at or below three.
- Recurring MCs show multiple distinct development families, not only repeated activity.
- Episode telemetry demonstrates establishment/development/payoff or an explicit evidence-based expiry reason.
- Persistence corruption and unknown-version tests fail safe.
Planned source ownership
| Concern | Final owner |
|---|---|
| Confirmed observation and idempotency | NarrativeEventRecorder |
| Immutable world facts | narrative event store |
| Character perspective and consequences | narrative projector/store |
| Thread state | NarrativeThreadRules + NarrativeStoryStore |
| Candidate semantic presentation | event receipt + registry intake |
| Orange Beat/Context English | NarrativeRenderer / consolidated NarrativeProse |
| Relationship evidence | SagaProse relation resolver, returning semantic roles |
| Episode purpose and coverage | StoryScheduler |
| Shot eligibility and role choice | EpisodeShotSelector |
| Legacy chapter construction | LegacyChapterBuilder |
| Durable save lifecycle | narrative persistence service |
| Cast and Legacy layout | existing Saga presentation/UI |
| Camera switching and safety | existing director/camera path |
Likely file changes
Add
IdleSpectator/Narrative/NarrativeEventModels.csIdleSpectator/Narrative/NarrativeEventRecorder.csIdleSpectator/Narrative/NarrativePresentation.csIdleSpectator/Narrative/NarrativeRenderer.csIdleSpectator/Narrative/LegacyChapterBuilder.csIdleSpectator/Narrative/NarrativePersistence.csIdleSpectator/Narrative/NarrativePersistenceModels.cs- focused partial harness files if
AgentHarness.csis split during implementation
Major edits
InterestCandidate.csEvents/EventFeedUtil.cs- relationship, combat, role, war, plot, status, and world-event feeds/patches
NarrativeModels.csNarrativeDevelopmentRouter.csNarrativeStoryStore.csNarrativeThreadRules.csNarrativeProse.csStoryScheduler.csEpisodeShotSelector.csCaptionComposer.csSagaProse.csLifeSagaMemory.csLifeSagaPresentation.csLifeSagaSession.csAgentHarness.cs
File names may be consolidated to match existing repository conventions. The ownership boundaries and exit gates are the contract.
Implementation discipline
- Land phases in order; do not combine persistence and scheduler behavior in one cutover.
- Keep compatibility adapters internal and temporary.
- Every migrated event family receives deterministic tests before its old path is removed.
- Each phase ends with a clean build and targeted regression.
- Do not tune editorial weights to hide ingestion or prose defects.
- Do not solve semantic duplication with substring-only suppression. String checks may be a final safety net, but enums, roles, event ids, and evidence tags own correctness.
- Do not persist presentation strings when they can be regenerated from semantic state.
- Update this plan with checkpoint evidence as each phase completes.
Definition of done
The plan is finished when a character can:
- enter the ensemble through a confirmed development;
- appear in a caption whose Beat states that development once;
- continue through a deliberate multi-beat episode;
- accumulate a concise, non-redundant Legacy;
- survive save, quit, restart, and reload with that history intact; and
- return to the camera only when their story has genuinely changed.