v1.5.0 — cactbot Stage 2.2 (slot-driven matching: multi-ID variants handled correctly)
- **`timeline_diff_for_fight` refactored.** Previously iterated `fight_model` rows and looked up matching cactbot entries per row. Now iterates **cactbot timeline slots** and look…
Changed — timeline diff is now slot-driven, not row-driven
timeline_diff_for_fightrefactored. Previously iteratedfight_modelrows and looked up matching cactbot entries per row. Now iterates cactbot timeline slots and looks up matching cast events in the pull. A single cactbot slot can declare multiple ability IDs (Ability { id: ["9CD3", "9CD5"] }— random-variant mechanics like Sinsmoke/Sinsmite); the new code finds whichever variant fired in the pull and attributes it to the slot.- Effect: the FRU Sinsmoke +42.8s drift outlier from v1.4.0/v1.4.1 is fixed. In that pull, 9CD5 (Sinsmite) actually fired at 17.5s, but the row for 9CD3 (Sinsmoke) was hunting the late-position cast at 59.4s. Slot-driven correctly sees the 17.5s cast as filling the slot.
- Type filter dropped. Cactbot only lists curated mechanics in the timeline body (sub-cast VFX live in comment blocks, which we already skip for the diff). Trusting cactbot's curation means cosmetic-typed slots in our fight_model still appear in the diff. This is necessary because the headline cast (e.g. "Sinbound Blizzard III" 9D42) is what cactbot tracks, even though our auto-classifier marks it cosmetic (the damage comes from a follow-up sub-cast).
- Result shape unchanged — same fields per entry (
cactbot_label,ability_game_id,type_label,expected_t_ms,actual_t_ms,drift_ms,fired). The ReactTimelineDiffcomponent needs no changes. ability_game_idin each entry is now the variant that actually fired (where the slot has multiple IDs), not always the consensus's primary ID. Useful for click-through.- New API param
version(existing) still drives which fight_model snapshot supplies type labels. - Test injection point:
timeline_diff_for_fight(..., _timeline=ParsedTimeline)— lets tests construct synthetic timelines without depending on vendored cactbot files.
Live AC — FRU kill fight 1500 (same pull as v1.4.0 baseline)
v1.4.1 → v1.5.0
P1 median drift +1.2s → +0.9s (Sinsmoke outlier gone)
P2 median drift +10.9s → +10.9s (cascade signal preserved)
Adds NO ENTRIES → -13.0s ← Adds Phase now visible (14/16 fired)
P3 median drift -14.1s → -14.0s
P4 median drift +7.8s → +7.8s
P5 median drift +11.0s → +11.1s
Slot count per phase also up substantially (P1 9→22, P2 16→28, Adds 0→16, P3 14→24, P4 18→34, P5 12→29) — more curated cactbot slots now reflected, since we're not filtering by our own type classifier.
Tests
- 3 new multi-ID regression tests (
test_multi_id_slot_variant_a_fires,test_multi_id_slot_variant_b_fires,test_multi_id_slot_two_slots_two_variants). - 1 test updated from "cosmetic filtered" to "cosmetic slot still included" to match the new behavior.
- Existing 8 tests rewritten to use synthetic
_timeline=...injection so they don't need vendored cactbot files for arbitrary encounter IDs. - 342 tests passing (340 → 342).