- Keep LifeSagaSession across Clear; wipe on map load/clear with bind key - Soft-bias Prefer/cross-MC/cast and prefer roster MCs in aftermath/recover/park - Resolve war/plot principals plus discrete/WorldLog labels from live libraries - Amortize roster world scans, gate Relayout, and add hitch probe/save-slot helpers
13 KiB
Story Planner
IdleSpectator commits to short multi-beat stories instead of channel-surfing ranked tips.
Feeds, sticky ensembles, presentability, and InterestDirector stay in place.
StoryPlanner sits above the director and owns which story the next 30–90s belongs to.
Short-arc board (not the dossier rail)
Hard storylines (notable duel / mass / war / plot / love / grief) still form a small watching + parked board (cap 4) for short-chapter resume. Cutting away parks a hard arc; soft anonymous scraps still end. There is no rail Commit / hard pin anymore.
- Lore browse uses
browsePause- freezes idle withoutStoryPlanner.Clear(). StoryPlanner.Cleardoes not wipe the life-saga roster.- Combat spine Kind comes from sticky/live ensemble scale +
StoryArcKind, not tip Label prefixes.
Dossier life-saga rail is owned by LifeSagaRoster / LifeSagaRail (up to 10 MCs).
Click toggles Prefer (soft favorite).
Dossier/Saga tabs and hover preview are owned by LifeSagaViewController + LifeSagaPanel.
Durable observed facts live in LifeSagaMemory (independent of ChronicleEnabled).
Hover temporarily pauses camera switching without disabling Idle Spectator.
See life-saga.md.
Crisis camera hold: WarFront always blocks Mass/Battle cut-ins (war_theater_hold).
Disaster / outbreak signals use the same class rule while their chapter is live
(crisis_theater_hold), classified via live AssetManager.disasters + catalog categories
(not tip-string IndexOf).
InterestDirector is a partial class: session orchestrator + StickyMaintain + SwitchPolicy.
Harness: story_lore_pause_keeps, story_board_park_resume (short-arc park/resume via
story_resume_parked), story_crisis_war, story_crisis_disaster, war_front_sticky,
plus saga_* scenarios.
See also: life-saga.md, scoring-model.md, event-reason.md.
Pipeline
Feeds → InterestRegistry → StoryPlanner (boost / inject)
→ InterestVariety.Pick → InterestDirector → CameraDirector
Director still owns camera, sticky maintain, and switch margins.
Planner never calls MoveCamera.
Arc model
| Kind | Climax | Hard hold |
|---|---|---|
| CombatDuel | sticky pair | only notable pairs (2+ notables) |
| CombatMass | Skirmish+ | yes |
| WarFront / Plot / Love / Grief | existing sticky / life | yes |
Phases: Climax → Aftermath → Epilogue → Done.
- Aftermath injects on climax cold (quiet grace /
follow_lost), or claims naturaldeath_*grief. - Only grief / love-outcome pending tips block synthesis - cast hatch/food noise does not.
- Injected story beats bypass FixedDwell beat hard-skip and PreferOver cast life tips while the arc is live.
- Soft duel Mass↔Duel key flips still cold-hook once per theater anchor.
- Epilogue once after aftermath if a living lover/friend resolves.
- Soft anonymous duels still get aftermath tips; they do not use raised
arcHoldMargin. - Aftermath follow, cast recovery, and epilogue pick Prefer/MC among living theater/cast candidates via
LifeSagaRoster.PreferRosterUnit(never MCs outside the cast). - Parked-board trim drops anonymous scraps before Prefer/MC/cast arcs when over cap.
Dossier spine
While the current watch tip is the active arc's climax or story aftermath/epilogue beat,
the dossier shows a muted line under the orange reason: Kind · Phase
(e.g. Duel · Aftermath, War front · Climax).
Hidden when idle is off, the dossier is hidden, a status banner owns the reason row, or the camera has left the story for an unrelated tip (cast membership alone is not enough).
Love climax is outcome-only (set_lover / fallen-in-love). find_lover seeking stays camera-worthy
but does not open a Love arc. Leaving a climax without aftermath ends the arc (Done).
Sticky combat reframes (Battle ↔ Duel) sync climax Kind in place via SyncActiveClimax
so the spine cannot say Mass · Climax on a Duel tip.
arcHoldMargin applies only while the watch tip is the aftermath/epilogue beat.
Cast life tips (parenthood, sleep) no longer keep the raised cut-in wall.
Family life chapters (just_had_child, pregnancy, …) share arc:life:family, use a longer
beat cool (including world-wide happiness cool), and bleed ledger heat so the village cast
cannot monopolize the next picks.
Intimacy chapters (just_kissed / shares intimacy) use arc:life:intimacy with the same
soft-life cool class (pair + world) so one couple cannot keep cutting between combat tips.
Settlement finds (just_found_house / finds a home) use arc:life:home with the same soft cool
(world + subject) so house crumbs cannot keep cutting between combat beats.
Kill crumbs (milestone_kill / Killed X (species) / just_killed) use arc:life:kill with
the same soft cool (subject + world) so hunter kill tips cannot keep cutting between mass beats.
Courtship crumbs (fallen_in_love / smitten / find_lover seeking) use arc:life:love soft cool
(subject + world + pair). Bond outcomes (set_lover) stay hotter.
Life-intent decisions (sexual_reproduction_*, decision find_lover, …) share
arc:decision:intent soft cool (subject + world). Outcomes (pregnancy, child, seeking) stay hotter.
Ordinary civ construction finishes (building:complete / tents / houses) share arc:construction
soft cool. Spectacle wonders stay hotter.
Soft personal status FX (taking_roots, singing, moods, …) share arc:status:fx soft cool.
Danger / combat statuses stay hotter. Hatch crumbs use arc:life:hatch.
Sleep / wake / dream cycle (sleeping, just_slept, had_*dream, had_nightmare, …) share
arc:life:rest soft cool so rest crumbs cannot monopolize between richer beats.
WorldLog politics meta (king_dead, city_destroyed, favorite fell, …) share arc:worldlog:meta
soft cool so sticky death crumbs cannot re-monopolize.
Interrupted resume: already-cooled moment beats are not parked / not resumed after a cut-in (hatch/reproduce cannot bounce back after combat).
Story spine: aftermath tips must match the active arc CorrelationKey (no stale duel aftermath
under a Mass spine). Combat Kind word follows tip Label (Battle / Skirmish / Mass / Duel).
Wiped opposing combat camps collapse to a living-side pack label at live scale
(never Mass - A (n) vs B (0)).
Combat aftermath names only a dead theater partner (stands over X), else
stands over the fallen. Living lover/friend fallbacks are never the fallen.
Spine Kind follows the live tip Label for combat (Duel vs Mass/Skirmish/Battle), not sticky
scale-hold alone. Cold climax tips do not show · Aftermath - only the aftermath tip does.
Aftermath Follow loss promotes another living cast member before follow_lost.
Harness: story_spine / story_hold_margin / story_family_variety / story_intimacy_cool
/ story_settlement_cool / story_kill_cool / story_courtship_cool
/ story_decision_intent_cool / story_decision_intent_live / story_construction_cool
/ story_status_fx_cool / story_rest_cool / story_resume_cooled / story_worldlog_meta_cool
/ combat_wiped_side / reason_life_principal
(story_arc_combat, story_spine_scoped, story_aftermath_combat, story_family_variety).
Crisis chapters (Phase 4)
Parallel overlay beside short StoryArc - war / disaster / outbreak thresholds open a
multi-minute chapter without thrashing when unrelated tips cut in.
| Signal | Enter when |
|---|---|
| WarFront | live participants ≥ crisisWarParticipantMin (default 8), both sides present (peak lock alone does not enter) |
| EarthquakeActive / Disaster category | live or EventStrength ≥ crisisDisasterStrengthMin (95) |
| StatusOutbreak | EventStrength ≥ disaster floor |
While live:
crisisOwnershipBoost+crisisHoldMarginon matching tips- Character-fill ambient is suppressed (
SuppressAmbientFill) - Epic peers (≥ disaster floor) may still cut
On exit (signal linger / max duration / harness interest_crisis_end): inject
epilogue_crisis ({a} surveys the aftermath of the crisis), then cooldown.
Disaster / outbreak chapters use a shared family anchor (disaster / outbreak), a longer
linger after the last WorldLog pulse, and crisisDisasterCooldownSeconds (default 120)
armed when the closer injects so stacked tornado tips cannot double-close.
War chapters stamp a kingdom theater pair. Local Mass/Battle on that pair keeps the chapter
warm and cannot steal the War tip (war_theater_hold). Crisis closers clear short-arc spine
and suppress combat task detail crumbs under the orange reason.
Short-arc related epilogue is skipped when a crisis closer owns the exit.
When a short-arc war linger injects while a war crisis is live, the crisis quiet-closes
(no second epilogue_crisis). Harness batch end and interest_story_purge_leftovers
drop leftover crisis closers so free AFK does not inherit a fake ending.
After hard story/crisis ends, softFillQuietSeconds suppresses soft-life crumbs,
short interrupt FX (stunned / invincible), and character-fill ambient briefly.
FamilyPack does not break that quiet window (see life-saga.md).
Selection discipline
InterestVariety.Picktakes#1when score gap ≥story.nearTieEpsilon(default 8).- Prefer / MC / MC-cast tips may still win when the gap is below
story.sagaNearTieEpsilon(default 28). - Near-ties among peers still Prefer > MC > cast, else roll among top-3.
- Causal heat + life-saga Prefer/MC/cast soft bias + cross-MC bonus add into
RecalcTotal. - See life-saga.md for camera / rival / dual-rail contracts.
Knobs
scoring-model.json → story block (loaded by InterestScoringConfig.Story).
Catalog policy ids live under event-catalog.json → story
(aftermath_survivor, aftermath_mourner, aftermath_war_linger, epilogue_crisis, …).
Harness
| Scenario | Proves |
|---|---|
story_crisis_war |
WarFront → crisis active → epilogue_crisis |
story_crisis_disaster |
EarthquakeActive → crisis → epilogue_crisis; cool blocks reopen |
story_aftermath_combat |
duel cold → aftermath tip |
story_aftermath_partner_truth |
aftermath never names living lover as fallen |
story_aftermath_cast_noise |
hatch noise pending still gets aftermath |
reason_duel_principal |
bystander Follow cannot own duel reason |
reason_life_principal |
Follow hijack cannot keep intimacy Label |
story_intimacy_cool |
just_kissed cools; peer wins next pick |
story_settlement_cool |
finds-a-home cools; peer wins next pick |
story_kill_cool |
kill crumbs cool; peer wins next pick |
story_courtship_cool |
smitten/seeking cool; peer wins next pick |
story_decision_intent_cool |
reproduce/find-lover decisions cool as a class |
story_decision_intent_live |
live EmitDecision path cools intent class |
story_construction_cool |
tent/house finishes cool; peer wins |
story_status_fx_cool |
taking_roots FX cool; peer wins |
story_rest_cool |
sleep/wake rest cycle cools; peer wins |
story_resume_cooled |
cooled soft tip does not resume after cut-in |
story_worldlog_meta_cool |
king_dead cools; peer wins next pick |
combat_wiped_side |
wiped camp never prints vs (0) |
story_aftermath_war |
war sticky cold → aftermath; no dual crisis closer |
story_crisis_hygiene |
auto-opened war crisis + short-arc linger; purge leaves no epilogue leak |
story_near_tie |
large gap always picks #1 |
story_arc_combat |
climax → aftermath ownership |
story_arc_preempt_resume |
epic cut then fresh scrap aftermath |
story_ledger_revisit |
featured unit wins near-tie |
./scripts/harness-run.sh --repeat 3 story_crisis_war
./scripts/harness-run.sh --repeat 3 story_crisis_disaster
./scripts/harness-run.sh --repeat 3 story_aftermath_war
./scripts/harness-run.sh --repeat 3 war_front_sticky
./scripts/harness-run.sh --repeat 3 story_aftermath_combat
./scripts/harness-run.sh --repeat 3 story_aftermath_partner_truth
./scripts/harness-run.sh --repeat 3 story_aftermath_cast_noise
./scripts/harness-run.sh --repeat 3 reason_duel_principal
./scripts/harness-run.sh --repeat 3 story_near_tie
./scripts/harness-run.sh --repeat 3 story_arc_combat
./scripts/harness-run.sh --repeat 3 story_ledger_revisit
./scripts/harness-run.sh --repeat 3 story_kill_cool
./scripts/harness-run.sh --repeat 3 story_courtship_cool
./scripts/harness-run.sh --repeat 3 story_decision_intent_cool
./scripts/harness-run.sh --repeat 3 story_decision_intent_live
./scripts/harness-run.sh --repeat 3 story_construction_cool
./scripts/harness-run.sh --repeat 3 story_status_fx_cool
./scripts/harness-run.sh --repeat 3 story_rest_cool
./scripts/harness-run.sh --repeat 3 story_resume_cooled
./scripts/harness-run.sh --repeat 3 story_worldlog_meta_cool
./scripts/harness-run.sh --repeat 3 combat_wiped_side
Files
IdleSpectator/Story/StoryPlanner.cs- short-arc ownership, cold-hook, inject, spine, crisisIdleSpectator/Story/LifeSagaRoster.cs- durable MC cast + Prefer + chapter stampsIdleSpectator/Story/LifeSagaOverview.cs/LifeSagaRail.cs- saga hover + railIdleSpectator/Story/CrisisChapter.cs- parallel crisis chapter stateIdleSpectator/Story/CausalHeat.cs- decaying cast heatIdleSpectator/Story/StoryReason.cs- presentable aftermath LabelsIdleSpectator/WatchCaption.cs- dossierKind · Phasespine row