File Details
1.9-release
- R
- May 22, 2026
- 610.79 KB
- 81
- 12.0.5
- Retail
File Name
CombatAnalytics-1.9-release.zip
Supported Versions
- 12.0.5
tag 55eda37c235ba4499fac3b2b941284576af2975a 1.9-release
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sat May 23 01:54:38 2026 +0300
Major fixes
commit f00be2635d72840d74aabcba40a9dcaae6154fb9
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sat May 23 01:36:56 2026 +0300
fix(stats): sum both versatility sources to match character sheet
GetVersatilityBonus returns only non-rating % (buffs, PvP scaling,
War Mode). GetCombatRatingBonus returns only the rating-derived %.
The character sheet sums both. Old elseif fallback picked one and
dropped the other, showing ~4% in Build Overview while the sheet
showed ~24% during arena/BG PvP scaling.
commit 01224165f189affbe268c001815fba473cff7701
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sat May 23 00:56:10 2026 +0300
fix(summary): stop a nil session sub-table from blanking the dashboard
SummaryView:Refresh dereferences session.metrics and session.totals
directly in ~45 places. CombatHistoryView, DummyBenchmarkView and
SuggestionsView all guard these with `or {}`; SummaryView did not. A
session that reaches the dashboard before metrics/totals are populated
(interrupted finalize, pre-metrics schema) makes the first nil deref
throw, which aborts the entire render and leaves the tab a wall of
blank, unlabeled bars.
- CombatStore migration now normalizes session.totals to a table, next
to the existing session.metrics guard. session.totals was the one
unprotected sub-table.
- SummaryView:Refresh normalizes session.metrics/session.totals once
before rendering, covering sessions finalized in-session that never
pass through migration.
commit 01224165f189affbe268c001815fba473cff7701
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sat May 23 00:56:10 2026 +0300
fix(summary): stop a nil session sub-table from blanking the dashboard
SummaryView:Refresh dereferences session.metrics and session.totals
directly in ~45 places. CombatHistoryView, DummyBenchmarkView and
SuggestionsView all guard these with `or {}`; SummaryView did not. A
session that reaches the dashboard before metrics/totals are populated
(interrupted finalize, pre-metrics schema) makes the first nil deref
throw, which aborts the entire render and leaves the tab a wall of
blank, unlabeled bars.
- CombatStore migration now normalizes session.totals to a table, next
to the existing session.metrics guard. session.totals was the one
unprotected sub-table.
- SummaryView:Refresh normalizes session.metrics/session.totals once
before rendering, covering sessions finalized in-session that never
pass through migration.
commit f9d83d3c4a3da5d1360471d0cdb529ba1de6b8ab
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sat May 23 00:52:38 2026 +0300
feat(counters): surface Solo Shuffle win rate vs enemy specs
The Counters screen showed "No data" for enemy specs the player had
only met in Solo Shuffle rounds. The `specs` aggregate records one
opponent (primaryOpponent.specId) per match, but a Solo Shuffle match
is a single session of six rounds against rotating opponents, so five
of six round outcomes and two of three specs were dropped.
- ArenaRoundTracker: complete the previously-dead `opponentSpecs` field.
EndRound now records each round's enemy specs and flags the round
`specsIncomplete` when a slot's spec cannot be resolved;
CopyStateIntoSession persists the flag onto the session.
- CombatStore: add GetSoloShuffleWinRateVsSpec — an on-demand scan of
db.combats computing round-level win rate vs a spec. Excludes
irregular, nil-result and specsIncomplete rounds; counts a spec at
most once per round; reports `matches` (distinct sessions) so the
sample size is honest. Kept fully separate from the `specs` bucket.
- CounterGuideView: add a memoized "Solo Shuffle: NN% (R rounds ·
M matches) est." line below the match-level win-rate gauge. Round
units and match units stay visually distinct, never merged. Also
carries pending Counters-view refinements from prior work.
Forward-only: Solo Shuffle matches recorded before the producer fix
carry no per-round spec data and cannot be backfilled.
test_SoloShuffleSpecWinRate.lua adds 15 cases for the round-scan helper.
commit 48c1ce08a6ff02ebb173005cbbf86e8d0d615c24
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sat May 23 00:44:57 2026 +0300
fix(damage-meter): report the real reason a damage import failed
The PvP Performance Dashboard showed "Damage import failed: enable
Blizzard's Damage Meter" even when the meter was already enabled. The
import-status pipeline collapsed every failure into one code and the UI
mapped that code to a single misleading hint.
- DamageMeterService:ImportSession now writes a precise importStatus on
every return-false path. Zero-candidate results are disambiguated via
#allSessions: no DM sessions at all -> FAILED_DAMAGE_METER_UNAVAILABLE;
sessions exist but none matched the fight -> FAILED_NO_CANDIDATE.
- CombatTracker no longer hardcodes FAILED_NO_CANDIDATE on every import
failure (SetImportAuthority unconditionally overwrites importStatus);
it now passes the precise status the service resolved.
- SummaryView replaces the partial 3-branch hint logic with a complete
per-status hint table. failed_no_candidate no longer tells the user to
enable a meter that is already on.
Adds regression coverage for the unavailable, zero-session, and
no-matching-candidate paths.
commit 32fe13a1f26e960c1ea4acd2c8a0e056e0864735
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Wed May 20 23:51:11 2026 +0300
docs(design): Solo Shuffle per-round fusion design + spike harness
Design artifacts for the deferred "Solo Shuffle per-round damage" item.
Not wired into the addon — design skeleton + a throwaway in-game probe.
The item is blocked on one unverified question: how C_DamageMeter
segments Solo Shuffle rounds. The skeleton covers BOTH possible answers
as co-equal capture strategies, so whichever the spike confirms needs
minimal rewiring:
- Strategy A: snapshot the newest Expired C_DamageMeter session each
PostRound (works if the meter spawns one session per round).
- Strategy B: delta-track the Current session — round N damage =
cumulative(N) - cumulative(N-1) (works if the meter keeps one
continuous match session). Negative delta clamps to 0; a dropped
capture uses a carry-forward baseline; a mid-match session-ID change
disqualifies B.
Dual capture records both signals every round; selectCaptureStrategy
picks A or B post-match; a single shared _applyScaledFusion scales the
chosen per-round shape to the authoritative scoreboard total. Fused
per-round values carry estimated (never high) confidence.
soloshuffle-spike-harness.lua is a standalone /cadmspike diagnostic —
one Solo Shuffle run determines which strategy (or neither) is viable.
Both files pass luac -p. No addon code touched — implementation stays
gated on the in-game spike result.
commit aea9ca3d3828e936a4ad08c39594a0bcf2846f51
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Wed May 20 23:36:13 2026 +0300
feat(metrics): per-metric provenance + confidence-gated coaching
Coaching scores were presented with uniform authority — a pressureScore
from an authoritative C_DamageMeter import looked identical to one from a
cast-count estimate. Under WoW 12.0.5's restrictive combat API, where
damage data is frequently degraded, that uniformity actively misleads.
Per-metric provenance (schema v10):
- Constants: new METRIC_CONFIDENCE enum (high/estimated/low/unknown).
- Metrics.ComputeDerivedMetrics now writes session.metrics.provenance —
one {confidence, basis} record per derived score. Damage metrics map
from importedTotals.totalAuthority; timeline metrics from
coverage.visibleCasts; CC metrics from coverage.ccReceived;
survivabilityScore from authority + absorb-data presence. Purely
additive — no metric value changes (asserted by test).
- CoverageService:Finalize now runs before metric derivation so
timeline/CC provenance is accurate rather than UNKNOWN.
- SuggestionEngine: damage-derived suggestions gate on the provenance of
the metric they read — LOW suppresses, ESTIMATED downgrades severity
and tags the suggestion summary_derived.
- SuggestionsView: per-metric confidence tags replace reliance on the
single session-wide trust badge; pre-v10 sessions fall back to it.
- CombatStore: v10 migration gate (no backfill — provenance source is
unrecoverable for stored sessions).
Also folds in the scoreboard-anchor stickiness fix (Codex adversarial
review finding): a late non-authoritative C_DamageMeter re-import could
overwrite an already-anchored arena total. RestoreScoreboardAnchorIfRegressed
reinstates the anchor in the deferred and post-finalize retry paths
unless the re-import is genuinely authoritative.
Tests: test_MetricProvenance.lua (11), test_ScoreboardAnchor.lua (6).
luac -p clean; full suite failure count unchanged at the 56 baseline.
commit dcbc3f0afb4aa201028f61a3bbaba4a8721775d2
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Wed May 20 11:11:44 2026 +0300
feat(metrics): anchor arena damage totals from the post-match scoreboard
When C_DamageMeter import fails for an arena session, pressure/burst/
survivability scores collapsed to zero because session.totals.damageDone
stayed 0. WoW Midnight 12.0.5 restricts CLEU in PvP, so C_DamageMeter is
the only live damage source and it fails often.
Add a terminal fallback: when import yields no usable total for a
non-Solo-Shuffle arena session, substitute the player's damage row from
the post-match PvP scoreboard (C_PvP.GetScoreInfo, already captured into
session.postMatchScores by HarvestPostMatchData). The scoreboard total is
authoritative for the match damage TOTAL, so sustained-pressure metrics
become trustworthy; it carries no per-spell timing, so burst/window
metrics stay approximate — hence the new ANCHORED_FROM_SCOREBOARD import
status maps to the "estimated" authority tier, not "authoritative".
- Constants: new IMPORT_STATUS.ANCHORED_FROM_SCOREBOARD in estimated tier.
- DamageMeterService: public GetScoreboardPlayerDamage accessor.
- CombatTracker: ApplyScoreboardAnchorIfNeeded — shared, idempotent,
self-healing method called from all three persist paths (FinalizeSession,
the deferred re-import, the post-finalize one-shot retry) so a failed
re-import cannot downgrade an already-anchored session's authority.
Scope: arena only. Solo Shuffle excluded — its scoreboard spans all 6
rounds while a session is one round (per-round fusion deferred pending an
in-game spike). Battlegrounds excluded by context. Dummy/duel unaffected.
Robust to secret values: if post-match damageDone is secret, SanitizeNumber
zeroes it and the anchor silently no-ops.
Tests: 5 new GetScoreboardPlayerDamage cases. luac -p clean, no regressions.
commit 4f0a94d88987d9f71b2977da0bc784a192037240
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Wed May 20 02:40:18 2026 +0300
fix(classifier): record sessions for 12.0.5 training dummies
Silvermoon's new Training Dummy (NPC 243214) is classified as "normal"
rather than "trivial"/"minus", so SessionClassifier's allow-list
classification gate rejected it after name matching. With the CLEU
fallback removed in 831f753 (T031), no session was ever created and
every analytics page was empty.
- Catalog: add CID 243214 to TRAINING_DUMMY_CREATURE_IDS so creature-ID
detection scores 100 and bypasses the classification gate entirely.
- Classifier: invert the UnitClassification gate to a deny-list
(elite / rareelite / rare / worldboss) so name-matched dummies with
any non-combatant classification still promote.
- Zone fallback: new IsInPracticeZone() + PRACTICE_ZONE_MAP_IDS catches
unknown-CID NPCs whose name contains "dummy" inside capital cities,
gated by the same classification deny-list to avoid false positives
on vendors and guards.
- Fix latent Addon:Warn signature so trace logs capture actual error
detail from pcall-guarded sites (CombatStore UpdateAggregatesForSession
/ MigrateSchema failures were dropping the formatted message).
Tests: 5 new SessionClassifier cases (normal-class name match, elite
rejection, zone fallback positive/negative, non-practice-zone negative).
luac -p clean. Pre-existing test failures unchanged.

