- 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
756 lines
31 KiB
Markdown
756 lines
31 KiB
Markdown
# 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`](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 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 `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
|
||
|
||
```text
|
||
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 Beat’s 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 |
|
||
|
||
7. Preserve observation-before-confirmation rules: a behavior return value alone is not semantic
|
||
success.
|
||
8. 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 character’s 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 |
|
||
|
||
3. 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.
|
||
4. Use thread coverage debt to favor stories that have developed off-camera.
|
||
5. 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.
|
||
6. 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 plan’s 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.
|