# Event E2E suite (maintainable) Goal: every catalog event is covered by automation that stays cheap when new ids are added. Live accuracy roadmap: [`event-live-verification-plan.md`](event-live-verification-plan.md). Observe → confirm → emit: [`event-observation.md`](event-observation.md). ## Tracks | Track | Scenario | What it proves | How new events join | |-------|----------|----------------|---------------------| | **Inventory audits** | nested in `event_coverage` | Live library vs authored catalog | Add JSON row / live seed; audit fails until matched | | **A inject coverage** | `event_inject_coverage` / `ec30` | Every **camera-worthy** id can register an interest key + label | Auto via `EventCatalog.*.AuthoredIds` | | **Live apply loop** | `event_live_apply_loop` | Happiness Signal + status gains via real apply | Auto loop; ledger `id` / `id_drop` / `fail` | | **Live relationship** | `event_live_relationship` | Vanilla setters/Beh + join negative; leave/`family_removed`/baby live fires | Shared runner + ledger claims | | **Live pipelines** | `event_live_pipelines` | Traits/decisions/WorldLog/wars/eras/books/plots + libraries (worldLaw/item/subspecies_trait/power/gene/phenotype/meta traits); spell/biome best-effort unsupported; boat/combat best-effort | Auto loops + honest gaps | | **Live outcome smoke** | `event_live_outcome_smoke` | Short family + sample apply | Curated | | **Live coverage ledger** | `event_live_coverage` | Honest `none|feed|pipeline|id_drop|id|…` TSV | Seeded from catalogs; claimed by live runners | | **Semantic presentation** | `narrative_presentation_coverage` | Registry intake is typed; render uses no compatibility lookup or redundant phrase | Automatic through `EventFeedUtil` | | **Canonical narrative ingestion** | `narrative_canonical_ingestion` | Duplicate hooks create one event and one development for each durable family | Extend the recorder matrix | ## Suite split (weight-aware) | Gate | Includes | When | |------|----------|------| | `event_suite` | audits + inject + apply loop + relationship + outcome smoke | Day-to-day event gate | | `regression` | `event_suite` + `event_live_pipelines` (+ other product smokes) | Full gate | | `critical_smoke` ×3 | Spectator/settings/tip/exit | PR gate | `event_live_pipelines` stays **regression-only**. Flake budget is proven (`--repeat 3` PASS); it remains heavier than day-to-day. ## Idle quality (camera presentability) See [`camera-presentability-plan.md`](camera-presentability-plan.md). | Check | Scenario / assert | |-------|-------------------| | Unpresentable Label rejected at Register | `camera_presentability` / `presentability_probe` | | No `Watching: something interesting` | `no_placeholder_tip` | | Focus survives max_cap | `camera_presentability` / `has_focus` | | Ambient building falls are B-only | live patch + pipelines `id_drop` | | Status-overlap onset without live status | `status_overlap_camera` / `status_overlap_absent` | | Combat tip subject matches focus | `combat_focus` / `tip_matches_focus` | | Combat dossier nametag matches focus | `combat_focus` / `dossier_matches_focus` | | Combat 1v1 escalates to sided ensemble | `combat_focus` / `combat_ensemble_escalate` + `tip_matches_any` (`Battle -` / ` vs `) | | Cold combat clears fight tip | `combat_focus` / `tip_not_contains` fighting | Live idle soak helper (Player.log window): ```bash ./scripts/soak-audit-player-log.sh 60 ./scripts/soak-audit-player-log.sh --since-bytes "$(wc -c < ~/.config/unity3d/mkarpenko/WorldBox/Player.log)" ``` ## Coverage levels Recorded in `.harness/event-live-coverage.tsv` (gitignored; regenerated each live run). | Level | Meaning | Verified? | |-------|---------|-----------| | `none` | Inject only; no live fire this run | No | | `feed` | Production feed entry with synthetic payload | Partial | | `pipeline` | Vanilla fire proved the shared hook family; this id may not have fired | Partial | | `id_drop` | Real fire; authored drop matched | Yes (accurate drop) | | `id` | Real fire + expected note/interest | Yes | | `unsupported` | No deterministic fire API; reason in `notes` | Honest gap | | `fail` | Unexpected miss/drop | Broken | Inject / `domain_feed` never writes a live level other than leaving `none`. ## Busy policy Most feeds early-out on `AgentHarness.Busy`. Live runners set `ForceLiveEventFeeds` (relationship also sets `ForceRelationshipFeeds`). `LiveFeedsAllowed` = `!Busy || ForceLiveEventFeeds || ForceRelationshipFeeds`. Ledger `notes` should record `busy_bypass=ForceLiveEventFeeds` (or the specific flag) when used. ## Commands ```bash ./scripts/harness-run.sh event_inject_coverage ./scripts/harness-run.sh event_live_apply_loop ./scripts/harness-run.sh event_live_relationship ./scripts/harness-run.sh event_live_pipelines ./scripts/harness-run.sh event_suite ./scripts/harness-run.sh narrative_presentation_coverage ./scripts/harness-run.sh narrative_canonical_ingestion ./scripts/harness-run.sh regression ``` Artifacts (under `IdleSpectator/.harness/`, gitignored): - `event-inject-coverage.tsv` - inject wiring - `event-live-coverage.tsv` - **source of truth for live proof** - `event-trigger-audit.tsv` - design / call-site audit (not live proof) ## Design rules 1. Loop `AuthoredIds` - no per-id harness steps for inject/apply/traits. 2. Inject ≠ live. `domain_feed` is never live. 3. `ForceLiveEventFeeds` / `LiveFeedsAllowed` bypass Busy during live runners only. 4. Coverage levels must not over-claim (see plan). 5. Registry max-96: remove/clear during mass live loops so trim does not false-fail. 6. Assert fails on `fail > 0`; do not fail on camera `none` until a budget is explicitly tightened. ## Code - `EventInjectCoverageHarness`, `EventLiveApplyLoopHarness`, `EventLiveRelationshipHarness`, `EventLivePipelinesHarness` - `EventLiveCoverageLedger` - Asserts: `event_inject_coverage`, `event_live_apply_loop`, `event_live_relationship`, `event_live_pipelines`, `event_live_coverage`