Vigil
← All news

v1.14.0 — body-check fault by assigned role (strat-aware aoe_party)

aoe_party deaths were always classified as root — but for body-check / soak / spread mechanics, that misattributes fault. A DPS dying to a 4-tank-tower mechanic shouldn't be blame…

Why

aoe_party deaths were always classified as root — but for body-check / soak / spread mechanics, that misattributes fault. A DPS dying to a 4-tank-tower mechanic shouldn't be blamed; the assigned tank's absence (or fail to soak) is the actual issue. v1.14.0 reads strat_config's role_map for the killing mechanic occurrence and reclassifies root → cascade when the dying player's job role isn't in the expected target set.

Added — _expected_job_roles_from_role_map()

analysis/fault_attribution.py. Translates strat's {slot_name: MT|OT|H1|H2|D1..D4} map into the set of FFXIV job roles ({tank, healer, dps}) expected to be targeted. Handles "any" (wildcard → all 3), null (skipped), and the empty-map case (returns None → caller falls back to existing classification).

Added — _aoe_party_casts()

Mirrors the raidwide-cast lookup from mit_audit but scoped to aoe_party. Returns (cast_ts, ability_id, occurrence) per cast, sorted by time — used to match each aoe_party death to its specific occurrence so strat_config (which is per-occurrence) can be consulted.

Changed — compute_fault_scores_for_fight body-check refinement pass

After the first-pass classification, walk death_records:

  1. Skip non-root deaths or non-aoe_party kinds.
  2. Locate the cast that killed the player (_aoe_cast_for_death, same 15s lookback as raidwide).
  3. Look up strat_config.assignments for {ability_id}_{occurrence} mechanic_ref.
  4. Derive expected job roles via _expected_job_roles_from_role_map.
  5. If the dying player's job role isn't in the expected set → flip kind to cascade, mark body_check_reclassified=True on the death record.

Cleanly opt-in: only fires when the user has actually configured assignments for that mechanic. No strat → existing root behavior preserved. Returns a body_check_reclassified counter in the summary so downstream consumers can surface "N body-check faults rebalanced this fight."

Tests

  • 10 new in tests/test_body_check_fault_v1_14.py: pure tests for _expected_job_roles_from_role_map (empty / tank-only / mixed / wildcard / null-role-skipped / full party); end-to-end DPS-dies-to-tank-only-aoe_party reclassifies to cascade; tank dying to same mechanic stays root (was assigned); no-strat keeps existing root behavior.
  • 456 tests passing (446 → 456, +10).

Note on the deeper "missing-slot attribution"

The fuller version of body-check fault attribution — identify which assigned-role player wasn't in position — needs damage-target counting against expected slot count. Tractable but not in this ship. v1.14.0 ships the simpler-but-already-valuable version: redistribute blame away from players who shouldn't have been hit. The "find who failed to soak" follow-up can be added later as a strat-aware extension once usage validates the approach.

No schema change. No new env vars.