worldbox-observer-mod/docs/narrative-coherence-and-persistence-plan.md
DazedAnon ff4b40d00e feat(narrative): enhance narrative probes and presentation coverage.
- 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
2026-07-23 14:12:23 -05:00

31 KiB
Raw Blame History

Narrative coherence and durable Legacy plan

Status

Implemented — phases 08 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:

  1. Who is this about?
  2. What changed?
  3. Who or what was involved?
  4. Why is this development part of the current story?
  5. What lasting consequence belongs in this lifes 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.

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 Actor references, 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

  1. Capture the current full regression result and a natural story-first soak as the comparison baseline.
  2. Add a machine-readable inventory of candidate families and current prose provenance: structured narrative, EventReason, feed-authored label, ensemble renderer, or compatibility fallback.
  3. 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.
  4. Add a deterministic reproduction of: Gains a child, Omyahel · their child.
  5. Add a harness snapshot that explains one selected cut from observation through rendered caption.

New harness targets

  • narrative_prose_redundancy
  • narrative_cut_explain
  • narrative_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

  1. Add NarrativePresentationModel, explicit viewpoint, and a renderer that owns the complete Beat.
  2. Stamp an exact event/development reference onto InterestCandidate; stop using NarrativeStoryStore.LatestFor(..., 8f) when an exact id exists.
  3. 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.
  4. Encode relationship roles as data. Do not derive them from already-rendered text.
  5. Add semantic novelty checks:
    • a relation descriptor is omitted when the action already states it;
    • ordinal detail such as first child is allowed because it adds information;
    • names appear once within a Beat;
    • Context cannot repeat the Beats event, relationship, role, or outcome tags.
  6. Structured Beats bypass CaptionComposer.EnrichBeat.
  7. 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

  1. Introduce NarrativeEventRecorder and NarrativeEventReceipt.
  2. 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.unscaledTime as persistent identity.
  3. 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.
  4. Convert LifeSagaMemory into a read/index/projection facade. Its public Record... helpers either delegate once to the recorder during migration or are removed.
  5. Remove direct NarrativeDevelopmentRouter... calls from patches that already record the same observation.
  6. 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
  1. Preserve observation-before-confirmation rules: a behavior return value alone is not semantic success.
  2. Add bounded duplicate-observation telemetry by event family and provenance.

New harness targets

  • narrative_ingestion_idempotent
  • narrative_ingestion_perspectives
  • narrative_hook_single_writer
  • narrative_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:

  1. Make EventReason a compatibility facade over typed rendering where it remains useful; prevent feeds from constructing selected prose ad hoc.
  2. Replace string-based verb/relation classification with presentation enums/tags.
  3. Remove generic SagaProse.BeatClause concatenation from caption composition. Relation evidence remains useful as semantic input to the renderer.
  4. Add a safe unknown-event presentation that never appends inferred relationship prose.
  5. Audit capitalization, punctuation, tense, names, raw ids, placeholder tokens, and missing actor behavior across the entire catalog.
  6. 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

  1. Introduce LegacyChapterBuilder as the only Legacy author.
  2. Build chapters from semantic events, consequences, and resolved/meaningfully changed threads.
  3. Replace presentation-time competition between:
    • narrative chapters;
    • raw LifeSagaFact lines;
    • roster-stamped prose chapters; and
    • special combat prose.
  4. 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.
  5. Rank chapters by consequence importance, narrative completeness, recency, and diversity—not raw event volume.
  6. Preserve more semantic chapters than the two-line panel can display; presentation chooses the best two without destroying history.
  7. Ensure a deceased or currently unbound characters Legacy renders entirely from snapshots.

New harness targets

  • narrative_legacy_chapters
  • narrative_legacy_lineage
  • narrative_legacy_role_arc
  • narrative_legacy_war_outcome
  • narrative_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:

  1. Identify the most reliable WorldBox/NML save-start, save-complete, and load-complete hooks.
  2. Determine a stable world/save identity that survives process restart. The current runtime bind key contains object identity and is unsuitable for durable storage.
  3. Confirm the supported mod-data location and atomic replace behavior on the target platforms.
  4. 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

  1. Add a versioned NarrativeSaveState DTO and explicit mapper.
  2. Serialize only durable fields and world chronology.
  3. Write atomically through temporary file plus replace; keep one last-known-good backup.
  4. Tag each snapshot with its confirmed save generation/fingerprint and durable world tick.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. Add schema migration support beginning with version 1. Unknown newer schemas load empty without overwriting the unreadable data.
  10. Persist Saga Prefer by stable character/world identity. Do not persist transient rail order.

New harness and live targets

  • narrative_persistence_roundtrip
  • narrative_persistence_corrupt
  • narrative_persistence_unknown_version
  • narrative_persistence_missing_actor
  • narrative_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

  1. Assign shot roles from semantic development/function and current episode state.
  2. 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
  1. 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.
  2. Use thread coverage debt to favor stories that have developed off-camera.
  3. 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.
  4. Log why each selected shot satisfied the episode and which role remains uncovered.

New harness targets

  • narrative_episode_grammar
  • narrative_episode_no_forced_footage
  • narrative_episode_reintroduction
  • narrative_episode_interrupt_resume
  • narrative_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

  1. Add bounded screen-time debt for roster MCs and active threads.
  2. Count meaningful authored exposure, not seconds spent on unavoidable camera holds.
  3. Prefer an under-covered MC only when they have a presentable new development.
  4. Prevent prolific event generators—combatants, rulers, or frequently reproducing characters—from monopolizing the narrative through volume alone.
  5. Reintroduce dormant MCs only after a confirmed change.
  6. Preserve priority order: critical world event > productive episode > meaningful under-covered thread > bounded world fill.
  7. 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_balance
  • narrative_prolific_mc_guard
  • narrative_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.md
  • docs/life-saga.md
  • docs/event-reason.md
  • docs/event-e2e.md
  • docs/story-planner.md
  • this plans status and completed checkpoints

Historical plans remain history; their ownership tables must not override the final architecture.

Final verification sequence

  1. Clean NML compile.
  2. New narrative scenario group.
  3. Existing relationship, caption, Saga, Legacy, scheduler, and persistence scenarios.
  4. Full ./scripts/harness-run.sh regression.
  5. Three organic developed-world soaks:
    • relationship/lineage-heavy;
    • conflict/war-heavy;
    • mixed long-running world.
  6. 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.cs
  • IdleSpectator/Narrative/NarrativeEventRecorder.cs
  • IdleSpectator/Narrative/NarrativePresentation.cs
  • IdleSpectator/Narrative/NarrativeRenderer.cs
  • IdleSpectator/Narrative/LegacyChapterBuilder.cs
  • IdleSpectator/Narrative/NarrativePersistence.cs
  • IdleSpectator/Narrative/NarrativePersistenceModels.cs
  • focused partial harness files if AgentHarness.cs is split during implementation

Major edits

  • InterestCandidate.cs
  • Events/EventFeedUtil.cs
  • relationship, combat, role, war, plot, status, and world-event feeds/patches
  • NarrativeModels.cs
  • NarrativeDevelopmentRouter.cs
  • NarrativeStoryStore.cs
  • NarrativeThreadRules.cs
  • NarrativeProse.cs
  • StoryScheduler.cs
  • EpisodeShotSelector.cs
  • CaptionComposer.cs
  • SagaProse.cs
  • LifeSagaMemory.cs
  • LifeSagaPresentation.cs
  • LifeSagaSession.cs
  • AgentHarness.cs

File names may be consolidated to match existing repository conventions. The ownership boundaries and exit gates are the contract.

Implementation discipline

  1. Land phases in order; do not combine persistence and scheduler behavior in one cutover.
  2. Keep compatibility adapters internal and temporary.
  3. Every migrated event family receives deterministic tests before its old path is removed.
  4. Each phase ends with a clean build and targeted regression.
  5. Do not tune editorial weights to hide ingestion or prose defects.
  6. 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.
  7. Do not persist presentation strings when they can be regenerated from semantic state.
  8. Update this plan with checkpoint evidence as each phase completes.

Definition of done

The plan is finished when a character can:

  1. enter the ensemble through a confirmed development;
  2. appear in a caption whose Beat states that development once;
  3. continue through a deliberate multi-beat episode;
  4. accumulate a concise, non-redundant Legacy;
  5. survive save, quit, restart, and reload with that history intact; and
  6. return to the camera only when their story has genuinely changed.