worldbox-observer-mod/docs/event-reason.md
2026-07-16 16:20:33 -05:00

89 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Event reasons (camera + orange dossier)
## A/B layers
| Layer | What | Gate |
|-------|------|------|
| **B ticker** | Activity / Life / chronicle prose for live ids | Always may write when the game fires |
| **A story camera** | Interest candidates, orange reason, camera cuts | `EventCatalog.IsCameraWorthy` only |
Happiness: `Presentation.Signal` = A; `Ambient` / `Aggregate` = B-only.
Discrete / status / WorldLog: `CreatesInterest` = A gate.
`InterestDirector` only ranks A candidates. It does not author inventory.
Character fill runs only when the pending **EventLed** queue is empty, and uses an empty Label (task chip only).
Orange dossier reason = the owning A events `Label` while the scene is active (`InterestDirector.TryGetOwnedReasonCandidate`).
Quiet grace / inactive dwell clears the reason even if focus has not switched yet.
## Activity History peek freshness
Layer B ring stays append-only (Lore Activity = full journal).
Dossier History peek uses `ActivityRelevance` / `ActivityLog.LatestRelevantForSubject`:
- Soft window (~8s): newest beats always show.
- After that: same verb/family as live task, or family live predicates (combat ↔ attack target / in_combat, haul ↔ carrying, status ↔ still has/lost status).
- Milestones always peek-worthy.
- Peek refreshes when live AI fingerprint changes (task, attack target, carrying), not only when the ring grows.
Drops (ambient, createsInterest=false, identity filter, below margin, …) go to `InterestDropLog`.
## EventReason
[`IdleSpectator/Events/EventReason.cs`](../IdleSpectator/Events/EventReason.cs) is the sole Label factory for camera candidates.
It lives in the Events module with catalogs (not in the director).
Feeds call typed helpers (`Fight`, `BuildingEat`, `SeekingLover`, …) or `Apply(template, a, b, id)`.
## Events module layout
| Path | Role |
|------|------|
| `Events/EventCatalog.cs` (+ `Catalogs/EventCatalog.*.cs`) | **One inventory** - nested domains + `IsCameraWorthy` |
| `Events/Feeds/` | Build + register **A** candidates from catalog dials |
| `Events/Patches/` | Harmony hooks → feeds / Activity |
| `Events/EventReason.cs` | Orange reason sentences |
| `Events/EventFeedUtil.cs` | Shared Register path |
| `Events/InterestDropLog.cs` | Why a beat stayed B or missed A |
| `Events/DiscreteEventEntry.cs` | Shared catalog row shape |
Rules:
- Sentence form with names: `{a} is fighting {b}`, `{a} is eating a beehive`, `{a} decides to change the kingdom's culture`.
Decision orange reasons use the `action` field (infinitive after "decides to").
Other domains use `label` or happiness `variantsWithRelated` / `variantsWithoutRelated`.
All of that - plus per-id `strength` - lives in [`event-catalog.json`](../IdleSpectator/event-catalog.json).
`EventReason.Decision` only formats `{name} decides to {phrase}`.
Species/character score overlays live in the same JSON; global formula stays in `scoring-model.json`.
- Set `RelatedUnit` when the sentence names a second party.
- Never invent stranger subjects for unit-led events.
- Never use engine jargon that misstates the action.
## Removed crumbs
Do not author these as orange reasons:
- `In action`
- bare `Duel` / `Fight (N)` without a named sentence
- identity titles (`King of X`, `Leader of X`, `Lone beetle`)
- `Consumes a building` / `Damages a building: {a}` crumb form
## Strength dial (Layer A only)
`CreatesInterest` / Signal gates whether an id registers for the camera; `EventStrength` ranks among A.
Instant cut-in when `next.TotalScore >= current + cutInMargin` (sticky scenes use `stickyCutInMargin`).
See `scoring-model.json`.
## Dossier display
`UnitDossier.EventLabelBeat` keeps EventReason / `Name verb …` sentences.
It rejects identity/weak crumbs and scrubs living stranger names (`ReasonNamesStranger` / `LeadsWithForeignName`).
Active EventLed A scenes should not silently blank a valid Label.
## See also
- [`event-e2e.md`](event-e2e.md) - catalog inject + live outcome suite
- [`event-observation.md`](event-observation.md) - observe → confirm → emit (Beh Continue is not success)
- [`event-audit.md`](event-audit.md) - sources, MaxWatch, A demotion set
- [`event-prose-audit.md`](event-prose-audit.md) - prose vs game call sites
- [`scoring-model.md`](scoring-model.md) - score weights