worldbox-observer-mod/docs/event-audit.md

64 lines
3.2 KiB
Markdown

# Event audit (A/B layers)
**Layer B (ticker):** Activity / Life coverage for live library ids - honest prose, completeness.
**Layer A (story camera):** only `EventCatalog.IsCameraWorthy` beats register interest and cut the camera.
**Single inventory:** [`EventCatalog`](../IdleSpectator/Events/EventCatalog.cs) (partials under `Events/Catalogs/`).
Feeds/patches register A from those dials; `InterestDirector` only ranks candidates.
## Dials
| Dial | Layer | Role |
|------|-------|------|
| Happiness `Presentation` | A if Signal | Ambient/Aggregate = B-only |
| `CreatesInterest` | A gate | Discrete / status / WorldLog / libraries |
| `EventStrength` | A rank | Base `TotalScore` (interrupt via margin) |
| `Completion` | A hold | Hold-until-complete kind |
| `MaxWatch` | A hold | Hard cap (`scoring-model.json`) |
| `EventReason` / Label | A display | Orange dossier sentence |
## MaxWatch class defaults
| Class | Seconds | JSON field |
|-------|--------:|------------|
| Moment / FixedDwell | 15 | `maxWatchMoment` |
| Status | 30 | `maxWatchStatus` |
| Grief | 45 | `maxWatchGrief` |
| Combat | 60 | `maxWatchCombat` |
| Epic world | 50 | `maxWatchEpic` |
Peer rotate / soft cut on EventLed floors at `minCameraDwell` (15s).
Score-margin interrupts bypass that floor; sticky A scenes use `stickyCutInMargin`.
Ambient / Character fill is exempt - A events may take the camera immediately.
## Sources (wired)
| Source | Primary owner | Reason factory | Notes |
|--------|---------------|----------------|-------|
| WorldLog | `InterestFeeds.OnWorldLogMessage` | catalog / EventReason | A if CreatesInterest |
| Happiness | `IngestHappiness` | catalog + EventReason | Signal = A; see [event-prose-audit.md](event-prose-audit.md) |
| Status | `OnStatusChange` | EventReason.Status | Gain A if CreatesInterest; egg **loss** → hatch A |
| Scanner combat | WorldActivityScanner | EventReason.Fight / Battle | `EventCatalog.Combat` |
| War / Plot / Relationship / Trait / Building / Boat / Book / Era | Feeds + patches | EventReason / catalog | A via CreatesInterest |
| Libraries | Deferred feeds | EventReason.Library | Mostly B; spectacle ids A |
| Character fill | InterestDirector.TryCharacterFill | empty Label | Only when no pending EventLed |
## Layer A demotion (this pass)
Happiness Signal → Ambient (B ticker only): gifts, cry, inspired, enchanted, divine, magnetised, strange_urge, tantrum, forced_power, lost_fight, got_robbed, house find/lose, made friend, injured.
Status CreatesInterest → false (Activity remains): surprised, confused, motivated, inspired, magnetized, starving, ash_fever, powerup, voices_in_my_head, angry.
## Interrupt / hold / grace
1. Instant cut when `next.TotalScore >= current + cutInMargin` (sticky: `stickyCutInMargin`).
2. Ambient fill: any A EventLed may take the camera immediately.
3. Else hold while `InterestCompletion.IsActive` and under MaxWatch.
4. Soft peer rotates on EventLed only after `minCameraDwell` (15s), never during protected sticky hold without margin.
5. Quiet grace 6s: still-worth-watching filter, then Character fill with empty reason.
6. Drops recorded in `InterestDropLog`.
## Mutation gap triage
`mutation_discovery` refreshes `.harness/mutation_gaps.tsv`.
Open rows are unexplained Wire candidates only.