Vigil
← All news

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_fight refactored. Previously iterated fight_model rows 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 React TimelineDiff component needs no changes.
  • ability_game_id in 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).