promotional bannermobile promotional banner

gobignintterrupt

lightweight, fully self-contained Mythic+ utility addon designed for (Patch 12.0.5). It helps you and your group stay coordinated by tracking critical party cooldowns and interrupts, so you never miss a beat in high-key pushes.

File Details

v1.0.0

  • R
  • Apr 29, 2026
  • 298.96 KB
  • 8
  • 12.0.5
  • Retail

File Name

GOBIGnINTERRUPT-v1.0.0.zip

Supported Versions

  • 12.0.5

GOBIGnINTERRUPT

v1.0.0 (2026-04-29)

Full Changelog Previous Releases

  • v1.0.0 — non-addon peer coverage + presence badge
    Peer presence detection
    • CDComm tracks peerSeen[name] on inbound addon messages, lastQueryAt on
      Q broadcast. M.PeerHasAddon(unit) returns true/false/nil after a 5s grace.
    • M.DumpPeerPresence + /gbi peers slash command for diagnostic visibility.
    • Brain.Reset wipes peerSeen so dungeon entry re-queries fresh.
      "No addon" badge
    • Bar.lua: per-row red "?" font string anchored left of the unit name,
      refreshed on the existing 0.5s tick.
    • UnitOverlay.lua: per-host frame badge with dark plate, anchored INSIDE
      the host TOPLEFT offset by ICON_SIZE/2 so it clears the leader-crown
      area without colliding with role icons or the cooldown strip.
      Aura-duration back-calculation
    • Evidence computes castedAt from aura.expirationTime - aura.duration,
      laundered via tostring->tonumber + double-pcalled comparison so
      12.0.5 secret-tagged numeric fields fail closed instead of throwing.
    • Brain.OnCast accepts castedAt 5th param; back-dates startedAt when
      within effDur of now (non-peer paths only). Non-addon peer CDs now
      show accurate remaining time instead of "full duration from when
      polling first saw the buff".
      CastTracker taint cleanup
    • Early IsSecret(castGUID) check skips the string.match recovery for
      tagged casts, eliminating the "match failed" debug-log spam.
    • Single concise "skip: tagged cast on partyN (Evidence will catch
      via aura)" line replaces three verbose lines per cast.
      Data_Cooldowns audit (~30 new entries)
    • Warrior: Ravager, Bladestorm (Arms+Fury), Champion's Spear,
      Demoralizing Shout
    • DK: Empower Rune Weapon, Dancing Rune Weapon, Lichborne, AMZ
    • DH: Soul Carver, Immolation Aura
    • Druid: Tranquility, Innervate, Incarnation (all 4 specs)
    • Evoker: Rewind, Tip the Scales
    • Hunter: A Murder of Crows, Double Tap
    • Mage: Cold Snap, Evocation
    • Monk: Revival, Celestial Brew, Dampen Harm
    • Paladin: Holy Avenger, Aura Mastery
    • Priest: Vampiric Embrace, Apotheosis, Symbol of Hope, Rapture
    • Rogue: Cold Blood, Killing Spree
    • Shaman: Earth/Fire/Storm Elemental, Healing Tide, Spirit Link
    • Warlock: Dark Soul (Misery + Instability), Summon Infernal
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Remove CombatLog.lua — CLEU is protected for unsigned addons in 12.0.5
    User got ADDON_ACTION_FORBIDDEN at addon load:
    AddOn 'GOBIGnINTERRUPT' tried to call the protected function
    'GOBIGnINTERRUPT_CLEUFrame:RegisterEvent()'.
    The earlier research suggesting CLEU was usable was wrong on this
    specific path: Blizzard now blocks UNSIGNED addons from registering
    COMBAT_LOG_EVENT_UNFILTERED at all. (Details and other large addons
    in the prior comparison may be on a different policy tier or older
    installations didn't hit this.)
    Reverts the v0.5.0-beta CLEU module entirely. Brain's peer-priority
    guard (state.fromPeer + 5s lockout for local detections) is kept —
    still useful for the remaining detection paths (UNIT_SPELLCAST,
    UNIT_AURA, polling, CDComm).
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • CLEU-based party CD detection + peer-priority dedup
    New CombatLog.lua: registers COMBAT_LOG_EVENT_UNFILTERED and routes
    SPELL_CAST_SUCCESS from friendly party members into Brain.OnCast.
    Catches gaps the existing paths miss:
    * Pure interrupts (Pummel, Mind Freeze) - no buff to poll
    * Instant CDs without auras (Touch of Death, Wake of Ashes)
    * Spells whose buff name differs from cast name
    * Locale-independent (spellID, not name)
    GUID -> unit cache flushed on roster updates. Pet sources skipped.
    Class filter applied. Default DB.combatLog.enabled = true.
    Brain peer-priority guard: once a peer broadcasts a U message
    (arriving with their actual talented duration), local detections
    (CLEU / CastTracker / UNIT_AURA) ignore that (unit, spellID) for
    the next 5s. Prevents the "guess" duration from overwriting the
    authoritative peer-reported one. state.fromPeer flag tracks origin.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Follow-up audit fixes: Inspect nil-guard, cooldownsMode sync, StackTracker reset
    • CastTracker.lua:108 — defensive nil-guard on GBI.Inspect for the
      GetSpecByGUID call (matches the pattern used in Data_Cooldowns).
    • MinimapIcon.lua + Slash.lua: legacy unitOverlay.enabled toggle
      replaced with show.cooldownsMode toggle. Avoids drift between the
      two fields after the v5 schema migration.
    • StackTracker.Reset() added; Brain.Reset now calls it so stack
      counters drop on zone change / explicit reset.
    • TalentSync intentionally NOT reset on zone change — its data is
      player-keyed talent IDs that persist across zones; spec-change
      broadcasts handle the only legitimate invalidation case.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Audit fixes: drop dead Purify nil, BW alias, glow leaks, schema migration
    • Data_Cooldowns: removed [527] = nil line that was deleting the
      Priest Purify entry above. Purify is back in the DB.
    • Data_Cooldowns: removed bogus auraAliases = { "Aspect of the Wild" }
      from Bestial Wrath; the buff name is just "Bestial Wrath".
    • Bar.lua PopulatePlaceholders prune + TestInterruptFill cleanup
      now both call hideGlow() before table.remove so glow textures
      don't leak when icons are pruned.
    • App schema migrated v4 -> v5: legacy DB.show.cooldownBar +
      DB.unitOverlay.enabled fields are read once, written into
      DB.show.cooldownsMode, then cleared. No more drift.
    • CastTracker: defensive nil-guards on GBI.Taint.SafeSpellID +
      SafeGUID for cases where Taint module fails to load.
      Tag-only release; CurseForge push held until verified.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Bar.Reset clears icon list in-place (was orphaning bar.iconList)
    The interrupt bar's progBar.iconList is set ONCE at row creation
    to point at self.icons[unit]. Bar.Reset was reassigning that slot
    to a fresh {} on every zone change / Brain.Reset, so the bar's
    iconList kept pointing at the dropped old table. Subsequent casts
    landed in the new table but the bar's OnUpdate kept iterating the
    orphan -> bar appeared dead.
    Fix: clear the existing list in-place (list[i] = nil from
    length down to 1) so the table identity is preserved across
    resets. Also clean any active overlay glow on the way out.
    Reproducer: enter a M+ instance, get a CD, exit / re-enter, cast
    something. Pre-fix the bar wouldn't update; post-fix the new cast
    animates correctly.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Read notInterruptible at correct position (8th return)
  • Two crash fixes
    • Brain.OnCast bails for stackingResource spells (calls FlashCast
      instead). Was setting endsAt + scheduling a CD timer that later
      crashed on math.abs(nil - endsAt) once SetStacks nil'd endsAt.
      Reproducer: any peer broadcasts U for Void Meta -> receiver throws.
    • StackTracker.scanUnit checks IsSecret on aura.spellId before
      indexing auraIndex, and pcalls the index for defense in depth.
      Reproducer: enemy aura with secret-tagged spellId scanned by the
      player's HELPFUL loop -> 1940 throws/run.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Layout pass + interrupt bar restore + fixes
    • Options panel left-margin alignment fixed: Glow checkbox no
      longer indents off cdModeDD; Overlay slider stack drops the
      24px inset; Test/5s preview buttons moved BELOW the slider
      stack (no right-edge clipping); Bar appearance header now
      anchors to testBtn at the same left margin.
    • Overlay section gets a "Overlay" header above its sub-controls
      for visual separation.
    • Interrupt bar restored to the original layout per user request:
      bar fills the right portion of the row at icon-tall height,
      icons ride the moving fill edge as the CD ticks down. Reverts
      the v0.4.0-beta grid+thin-strip experiment.
    • TestInterruptFill now schedules a cleanup after the test duration
      ends, so test-cast icons don't linger as ready-glowing forever.
    • Profiles: auto-create a "Default" profile on PLAYER_LOGIN
      capturing current settings; M.List always includes "Default"
      defensively so the dropdown is never empty.
    • Zenith (1249625): re-classified to MONK Windwalker (spec=2),
      60s, 2 charges. Was incorrectly added as Ret Paladin.
    • Rogue: Deathmark (360194, Assa, 120s) + Shroud of Concealment
      (114018, all specs, 360s, utility) — added in earlier work,
      now part of this release.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Split Options into Settings subcategories
    The main GOBIGnINTERRUPT panel was getting unwieldy. Spell Database
    and Profiles are now their own canvas-layout subcategories under
    GOBIGnINTERRUPT in the AddOns settings tree.
    • New makeSubPanel(name, subtitle) helper: builds a scrollable
      Settings subpanel and registers it with the parent category.
    • Spell Database section moved to its own subcategory.
    • Profiles section moved to its own subcategory.
    • Main panel keeps General + Bars + Overlay + Sounds (still scrolls
      but much shorter and aligned).
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Profile manager: save / switch / import / export
    New Profiles.lua module:
    • GBI.Profiles.Save(name) snapshot current settings into named slot
    • GBI.Profiles.Load(name) swap current settings with named slot;
      the UI prompts to /reload to apply
    • GBI.Profiles.Delete(name)
    • GBI.Profiles.Export(name) serialize to "GBINT:" text
    • GBI.Profiles.Import(name, str) parse text into a new profile slot
      DB schema:
      DB.profiles[name] = snapshot table (deep copy minus profiles +
      activeProfile + transient fields like context)
      DB.activeProfile = string
      Options panel adds a Profiles section at the bottom with a
      dropdown of saved profiles, name input + Save/Delete buttons, and
      an import/export edit box. Loading a profile pops a "reload now?"
      dialog since most settings need a refresh to apply.
      Serialization is plain Lua-table-to-string with sandboxed
      loadstring on import (setfenv to , so no globals reachable).
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Engine active only in 5-man parties (size 2-5, not raid)
  • Interrupt bar: grid layout like cooldowns + thin progress strip below
  • Unify show/hide: single Cooldowns dropdown + drop showAlways
    • New DB.show.cooldownsMode = "off" | "bar" | "overlay" replaces
      the old DB.show.cooldownBar checkbox + DB.unitOverlay.enabled
      pair, which interacted in 3 confusing ways.
    • Options panel now has a single "Cooldowns display" dropdown
      (Bar window / Party-frame overlay / Off). The legacy "Show CDs
      on party frames" checkbox is gone. Mirroring keeps back-compat
      for any code still reading the old fields.
    • Engine no longer context-gates ("show outside dungeons"
      removed). Tracking happens whenever the addon's master enable
      is on; the show toggles do all visibility work. Removes the
      perceived conflict where a context flip overrode user toggles.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Late-join CD sync (Q query) + class stuns/CC additions
  • Placeholders render at full color/alpha (per user preference)
  • Stack-resource spells: glow at threshold, flash on cast, no fake CD swipe
    Void Metamorphosis (and any future stackingResource spell) was
    showing a 2h cooldown swipe because SetStacks set endsAt to
    GetTime()+9999. Fix:
    • Brain.SetStacks no longer sets endsAt; the field stays nil so
      CooldownFrameTemplate has nothing to swipe.
    • Bar.OnCDStart + UnitOverlay.OnCDStart detect stackCount and
      switch to stack-mode rendering: SetCooldown(0,0) + glow when
      count reaches stackThreshold.
    • CastTracker bypasses Brain.OnCast for stackingResource spells
      (no CD timer to track) and instead calls Brain.FlashCast(unit,
      spellID) which sets flashUntil = now+2s. Bar/Overlay treat
      flashUntil > now as a forced glow ("just cast" indicator).
    • expireIcons clears the flash glow when its 2s window ends.
      Stack reset is automatic - StackTracker observes the buff drop
      on UNIT_AURA / poll and pushes count=0 into Brain.SetStacks.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Bar.Show: cooldownBar=false now hides overlay too (showAlways respect)
  • Fix CD sync bug + correct several base CD values
    • Brain.OnCast now accepts an overrideDuration; the peer-comm
      receive path passes the sender's broadcast duration verbatim
      instead of recomputing via local TalentSync. Avoids double
      talent-CDR application when both sender and receiver had
      partial/conflicting talent data, which is why CDs weren't
      syncing correctly.
    • Player Broadcast now sends the actually-applied effDur (post
      TalentSync) so receivers see the player's real CD.
      Data fixes per in-game observation:
    • Takedown (1250646): 90 -> 60
    • Fortifying Brew (115203): 120 -> 240
      Survival of the Fittest (264735) is a 2-charge spell — the
      charges already track live via C_Spell.GetSpellCharges on the
      caster, broadcast in U;sid;dur;ch;chMax. Display shows X/2.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Per-window UI toggles (interrupts bar / cooldowns bar / overlay)
    Two new checkboxes in /gbi config General section:
    * Show Interrupts bar
    * Show Cooldowns bar / overlay
    Both default on. Off = the bar/overlay is hidden but tracking,
    peer broadcast, and receiving still work. Useful for "headless"
    mode (broadcast-only) or hiding one window while keeping the other.
    Bar.M.Show is now the single source of truth for visibility
    decisions; RefreshLayout just delegates. Show toggles + the
    existing unitOverlay.enabled flag interact in 4 combinations:
    cooldownBar=on, overlay=off → cooldown bar window
    cooldownBar=on, overlay=on → unit-frame overlay (bar hidden)
    cooldownBar=off → both hidden regardless of overlay
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Drop Voidfall from DB (not needed)
  • Devourer DH: wire Void Metamorphosis aura (1225789) + add Voidfall (1256301)
  • Stack-resource tracking infrastructure (Void Meta etc.)
    Some 12.0.5 abilities don't have a traditional cooldown - they
    unlock when the player accumulates N stacks of a charging aura.
    Devourer DH's Void Metamorphosis (1217605, threshold 50) is the
    canonical case ("Cooldown: n/a" on Wowhead).
    • New StackTracker.lua: builds an aura-id -> spell-id index from
      cdEntry.stackingResource = { auraID, threshold }. Listens to
      UNIT_AURA on player + party + polls C_UnitAuras every 0.5s.
    • New CDComm message K;<sid>;<count> for peer broadcast of stack
      counts. Throttled - only sent when the value actually changes.
    • Brain.SetStacks(unit, sid, n, threshold) synthesizes a state
      entry with stackCount/stackThreshold so the bar/overlay can
      render.
    • Bar + UnitOverlay show "n/total" overlay when stack data is
      present; falls back to "ch" charge display otherwise.
      Devourer Meta's stackingResource auraID is currently 0 (placeholder)
    • the aura ID needs to be supplied once observed in-game via the
      /run scan we asked the user to perform.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Add Void Metamorphosis (1217605) - Devourer DH dynamic CD
  • Dynamic CD support: detect runtime CDR + peer D delta messages
    For spells whose remaining cooldown shrinks during the cooldown
    window (Devourer DH Metamorphosis reduced by ability usage,
    charge-per-cast reductions, etc.), peer broadcast keeps everyone
    in sync.
    • New CDComm message: D;<spellID>;<remaining>
    • Brain.UpdateRemaining(unit, sid, sec) - applies an inbound delta
      by shortening the existing entry's endsAt and re-rendering the
      bar/overlay. Ignores noise (<0.5s diff).
    • SPELL_UPDATE_COOLDOWN listener on the local player polls every
      active tracked spell; if the engine reports a shorter remaining
      than we expect, locally update + broadcast D (throttled to 1/sec
      per spell).
    • Works for any spell with dynamic CDR - no per-spell DB flag
      needed. The poll is generic.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Peer-comm enhancements: spec / interrupt / charges / talent CDR
    • CDComm protocol extended (back-compat: receivers tolerate missing
      trailing fields):
      • U;sid;dur[;charges;chargesMax] cast announcement
      • S;specID spec announcement
      • I self-interrupt landed
      • T;node,node,... active talent node IDs
        All outgoing messages gated by GBI.Taint.IsSecret to never leak
        tagged values to the addon channel.
    • Inspect.SetSpecForUnit lets CDComm S messages preempt the slow
      NotifyInspect cycle for units running the addon.
    • KickCounter.AttributePeer credits the sender directly when their
      CDComm I message arrives; suppresses temporal-attrib race for
      ~0.5s afterwards. Self-attribution now broadcasts I so peers
      credit us instead of running their own heuristic.
    • Bar + UnitOverlay show a charge-count overlay (NumberFontNormalSmall,
      bottom-right of icon) when state.charges/chargesMax > 1.
      Brain reads C_Spell.GetSpellCharges on local-player casts and
      includes the count in the U broadcast.
    • New TalentSync.lua: each instance reads its own active talent
      nodes via C_Traits and broadcasts them as T messages. AdjustCD
      applies registered CDR rules per-unit so a talented Hunter sees
      Aspect of the Turtle at 150s while an untalented one sees 180s.
      Seed list of 5 well-known reductions; extensible at runtime.
      All four features fall back to existing behavior when the peer
      isn't running the addon - no impact on non-addon party members.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Aura aliases + interrupt-pull only fires for interruptible casts
    • AuraMap: entries can declare auraAliases = { "name1", ... } for
      spells whose buff name differs from cast name. build() registers
      each alias under the same class. Lookup function unchanged -
      unknown names still miss cleanly.
    • Interrupt.lua: onStart_inner now probes UnitCastingInfo's
      notInterruptible flag (tag-safe boolean check via laundered_bool)
      and skips the alert + sound when the cast can't be interrupted.
      Boss casts and uninterruptible channels no longer trigger spam.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Tidy remaining audit items (#3, #4, #9, #10)
    • Constants.lua adds K.IsCooldownBarCategory(cat); Bar + Overlay
      use it instead of the duplicated chain of category != X checks (#3).
    • Drop empty M.OnAllReady stub from Bar.lua + the call site in
      Brain.lua. Sound was already fired by Brain.fireAllReady directly;
      the stub never did anything (#4).
    • Options.rebuildList early-returns if neither GBI.IterCooldowns
      nor GBI.Cooldowns is loaded yet (defensive against load-order
      races) (#9).
    • Bar instance Hide() now clears any active overlay-glow state on
      its icons so glows don't leak when the engine flips off mid-glow
      and back on later (#10).
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Cooldown sort modes + interrupts always-tracked + audit cleanup
    • New sort modes for cooldown bar + unit overlay:
      • "By remaining CD" (default) — current behavior
      • "Offensive first" — BIGCD/OFFENSIVE > DEFENSIVE > UTILITY
      • "Defensive first" — DEFENSIVE > BIGCD/OFFENSIVE > UTILITY
        Within each bucket: live by endsAt, then placeholders.
        Setting in /gbi config under Bar appearance, applies to both
        Cooldowns bar and party-frame overlay (DB.cdSort).
    • Interrupts are now ALWAYS tracked internally even if the user
      unticks them in the Spell DB. GBI.GetCooldown ignores the
      disabled flag for K.CAT_INTERRUPT spells, so the Interrupts
      bar + KickCounter keep working regardless.
    • Audit cleanup #5: dropped vestigial DB.interrupt.mode/ratio
      fields from ensureCfg + slash + comments. /gbi int now uses
      "delay <seconds>" instead of "ratio/fixed".
    • Audit cleanup #6: rewrote Evidence.lua header to match its
      current implementation (UNIT_AURA + polling + CDComm
      redundancy) instead of "Phase 1 stub" boilerplate.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Audit fixes: orphan placeholders + dispatch nil-guard
    • PopulatePlaceholders (Bar + UnitOverlay) now ALWAYS prunes
      stale icons - whether placeholder or live - if the spell ID
      is no longer in the expected set (user disabled it, spec
      changed). Previously a disabled spell that was actively on
      cooldown left the icon orphaned forever.
    • Pruning also runs when the placeholders feature is toggled
      off mid-session, so existing dim icons disappear cleanly.
      Add step is gated by the feature flag; prune step always runs.
    • Bar.dispatch checks GBI.UnitOverlay isn't nil before indexing
      ov[fnName] - falls back to barCD if the module isn't loaded.
      Per audit findings #1, #2, #8 (high/medium severity).
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Cooldown bar + overlay: same as interrupts (no glow on ready, no re-dim)
  • Interrupt bar: don't re-dim after first detection (only placeholders dim)
  • Interrupt bar: dim icons when ready (not glow); position placeholders
    • When an interrupt CD ends, the icon stays visible but dims to
      alpha 0.4 + desaturated (matching the placeholder look). Live
      cast un-dims it. Cooldown bar still glows on ready (unchanged).
    • Bar OnUpdate now positions placeholder/ready icons at the
      "frac=0 / ready end" of the bar so they're visible without
      needing endsAt.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • CDComm: default ON when DB.comm.enabled is unset (existing savedvars)
  • CDComm: verbose tracing on broadcast + receive paths
  • Spec-aware placeholders: use GetSpecialization for player; prune wrong-spec icons
    Fixes overlay/bar showing OTHER specs' icons greyed out:
    • SpellsForUnit now uses GetSpecialization() directly when unit
      is "player" - the local API is always correct, no need to wait
      for GBI.Inspect to populate the player's GUID->spec cache.
    • PopulatePlaceholders (both Bar and UnitOverlay) now PRUNES
      existing placeholder entries that aren't in the current
      expected-spell set. Previously, when inspect was permissive
      initially (unknown spec -> show all class spells) and later
      resolved the actual spec, the wrong-spec icons stayed.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • IterCooldowns honors disabled list; UI bypasses with true flag
    Disabled spells were still showing as overlay placeholders because
    GBI.IterCooldowns merged built-in + custom but never consulted
    DB.spellDb.disabled. SpellsForUnit (used by placeholder code) and
    all other runtime callers now get only the active subset.
    The Spell DB UI in /gbi config calls IterCooldowns(true) so it
    can display + manage disabled entries (otherwise unticking a
    spell would make it disappear from the list).
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Spell DB category filter + DH Devourer + burst offensive-only
    • Spell DB tab now has a third dropdown: All / Offensive /
      Defensive / Interrupt / Utility/Dispel. Filters the list with
      class+spec for fine-grained DB management.
    • DEMONHUNTER spec list extended with "Devourer" (spec 1480) -
      was missing from the spec dropdown.
    • Burst-ready trigger now only considers OFFENSIVE big CDs
      (CAT_BIGCD, CAT_OFFENSIVE). Defensives in seenBig or
      allReadyList are filtered out so the cd_ready sound fires
      strictly on offensive readiness.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Spell DB: add spec sub-filter dropdown next to class
  • Spec-aware placeholder pre-population (default on)
    Bars and unit overlays now pre-populate with dim/desaturated icons
    for every CD a party member's class+spec could use - regardless of
    whether it's been observed yet. When the spell is actually cast,
    the placeholder converts to a live icon (un-dim, alpha 1.0,
    cooldown swipe runs). When ready: glow as before.
    • GBI.SpellsForUnit(unit) - iterates the merged DB filtered by
      class+spec, permissive on unknown spec.
    • Bar.PopulatePlaceholders + UnitOverlay.PopulatePlaceholders
      create dim icons for matching spells (interrupt-only on the
      Interrupts bar; defensives + big DPS elsewhere).
    • OnCDStart finds existing entries by spellID first; placeholder
      -> live conversion on first observed cast.
    • relayout sort: live entries first (by endsAt), placeholders
      trailing (by spellID).
    • Triggered on Show() + INSPECT_READY + PLAYER_SPECIALIZATION_CHANGED
      • GROUP_ROSTER_UPDATE.
    • Default DB.placeholders.enabled = true.
      Inspired by InterruptTrack's IT.GetSpellsForUnit (IT_Core.lua:778).
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Peer-to-peer CD sharing (default on)
    New CDComm.lua module: each addon instance broadcasts its own
    observed casts to the party/raid via C_ChatInfo.SendAddonMessage
    on prefix "GBINT". Receivers parse and call Brain.OnCast for the
    sender's party unit, painting their CD as if locally observed.
    Solves the 12.0.5 remote-PC redaction problem cleanly: where
    local UNIT_SPELLCAST and UNIT_AURA fail for foreign party
    members, the data arrives instead via the addon channel from
    their own (unredacted) instance. Both ends must run GBINT.
    Wire format: U;<spellID>;<duration>
    Channel: RAID if in raid, else PARTY.
    Inspired by InterruptTrack's prefix-based protocol (see
    InterruptTrack.lua:185, 4050, 3683-3715). Default enabled; toggle
    via DB.comm.enabled.
    Hooked into Brain.OnCast: broadcasts every player cast that
    already passed CastTracker's filters. Not broadcasted for unit
    ~= "player" (so we don't echo received messages back).
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Spell DB tab: per-class spell list + custom add
  • Add 20 missing tracked CDs from InterruptTrack DB
    Cross-referenced our Data_Cooldowns vs InterruptTrack's IT_Core
    spell data; added 16 entries we were missing plus 4 commonly-
    tracked extras.
    Defensives: Fiery Brand, Metamorphosis (Veng), Last Resort,
    Berserk (Guardian), Ironbark, Time Dilation, Life Cocoon,
    Divine Hymn, Guardian Spirit, Ascendance (Resto), Die by the
    Sword, Enraged Regeneration, Alter Time, Ice Cold,
    Blessing of Spellwarding.
    Big DPS: Takedown (12.0.5 SV Hunter), Invoke Niuzao,
    Shadow Dance, Avenging Crusader.
    Interrupt: Axe Toss (Felguard pet, Demo Lock).
    Skipped racials (Stoneform, Shadowmeld, Feign Death) and one
    duplicate (19647 Spell Lock — already covered by 119910).
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Hide interrupt-bar rows for classes/specs without an interrupt
  • Visual-settings audit: interrupt grow-direction live + exposed
    • applyScale for progressBar bars now re-applies SetReverseFill
      according to effGrowDir() so changing the dropdown reflects on
      the existing bars without /reload.
    • Re-add Grow direction dropdown to the Interrupts window in
      /gbi config (next to the Bar width slider). Was hidden after
      the bar-width refactor.
      Other visual settings audited - all wire correctly through their
      refresh path:
      • overlay side/offsets/iconSize/iconGap -> UnitOverlay.Refresh
      • cooldowns iconSize/iconsPerRow/growDir -> ApplyAllBars
      • glow toggle -> expireIcons reads glowEnabled() each tick
      • lock anchor -> applyLocked + RefreshLocked
        Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Interrupt bar: grow direction now flips fill side + icon tracking
    The grow-direction control on the Interrupts window was previously
    ignored - bars always tick down right-to-left. Now:
    • RIGHT (default): fill anchored at left edge of bar; as time
      passes the right edge of fill moves leftward. Icons track that
      moving edge (sliding left).
    • LEFT: SetReverseFill(true) so fill is anchored at the right
      edge; as time passes the LEFT edge of fill moves rightward.
      Icons anchored CENTER -> bar.RIGHT and slide right toward the
      bar's right end.
      Note: re-add the grow-direction dropdown to the Interrupts window
      in Options if you want it controllable from /gbi config (it's
      currently hidden because makeBarControls' isProgressBar branch
      shows bar-width instead).
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Container width = icon row width (not n*(size+gap))
    Off-by-one: previous formula counted a trailing gap after the
    last icon, so increasing the spacing slider grew the test-mode
    green background past the actual icon row. Use the precise span
    n*size + (n-1)*gap.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Centralized secret-value detection via issecretvalue
    WoW exposes issecretvalue as a global in 12.0.5 (used by OmniReborn,
    InterruptTrack, Kicker as the canonical detection helper). Adopting
    the same pattern:
    • GBI.Taint.IsSecret(v) - true if v is secret-tagged. Falls back
      to a pcall probe if the global isn't available.
    • GBI.Taint.SafeString2(v) - returns v only if non-tagged string.
    • SafeSpellID now uses IsSecret for the up-front gate.
    • AuraMap.LookupByName uses SafeString2 to reject tagged names
      cleanly before any table access.
      Eliminates the throw class entirely instead of catching them
      one by one with reactive pcalls.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Pcall AuraMap.LookupByName lookup; drop GUID :sub in log
  • Kicker-style cross-attribution for interrupt counter
    Inspired by /Kicker/Modules/Interrupt.lua and verified compatible
    with InterruptTrack's pattern. Sidesteps 12.0.5 spellID redaction
    on remote-PC parties:
    • Track timestamp of every UNIT_SPELLCAST_SUCCEEDED per friendly
      unit (player/partyN/partypetN). Spell ID isn't needed - only
      the unit token, which is never tagged.
    • Listen to UNIT_SPELLCAST_INTERRUPTED on enemy units (target,
      focus, bossN, nameplateN).
    • When an enemy cast is interrupted, attribute the kick to the
      party member whose UNIT_SPELLCAST_SUCCEEDED fired in the last
      0.5s. Most recent wins.
    • Existing per-spell counter (player's own kicks via spellID
      match) still works in parallel; cross-attrib runs alongside.
      This finally lets the counter increment for party members on the
      remote-PC scenario where event spell IDs are unusable.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Persist tracked-CD icons for the run; glow when ready
    Once a CD is detected (via cast event or aura poll), the icon now
    stays on the bar and on the unit-frame overlay for the rest of
    the run. expireIcons no longer removes them; instead it adds an
    ActionButton overlay glow when the CD is ready and clears the
    glow when a new cast resets endsAt. Brain.Reset (zone change /
    new dungeon) still flushes everything via Bar.Reset for a clean
    slate.
    Resolves the "icons fade away" complaint from in-game testing -
    the addon now functions as a persistent run-long readiness board.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Route poller through fire() for dedup against UNIT_AURA path
  • Aura polling fallback for redaction-walled party CDs
    On the user's remote-PC dual-box configuration, both
    UNIT_SPELLCAST_* and UNIT_AURA events are silent for partyN.
    Bypass the event wall by directly calling C_UnitAuras.GetAuraDataByIndex
    every 0.75s on each party member, comparing aura names against the
    AuraMap byNameClass index, and firing Brain.OnCast for newly-seen
    aura names. Re-detection allowed after a 5s gap so refreshed CDs
    re-fire.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Aura name fallback for remote-PC party CD detection
    UNIT_SPELLCAST_SUCCEEDED is silent for remote-PC party members in
    12.0.5 and the existing UNIT_AURA path's aura.spellId can be
    unlaunderable. OmniReborn and MiniCC both solve this with a
    spell-NAME fallback - aura.name is plain text and never tagged.
    • AuraMap now also indexes Data_Cooldowns by name+class
      (byNameClass[name][CLASS_TOKEN] -> spellID), with a
      LookupByName(name, classToken) helper.
    • Evidence.lua tries spellId first; on miss, pcalls aura.name and
      consults AuraMap.LookupByName. Same downstream fire path as the
      spellId case, with Brain dedup preventing double-fires.
    • Verbose Debug logging at every step so we can see exactly what
      UNIT_AURA delivers per cast.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Launder castGUID via string.format before regex; log match failures
  • Recovery now always sets spellID, even for non-DB spells
    Previously the castGUID recovery only set spellID if the recovered
    ID was in our CD database — so any party-member spell whose ID
    wasn't tracked would silently fall through to "cannot resolve",
    even when we'd successfully extracted the correct ID. The user
    saw e.g. party2's Aimed Shot (19434) parsed correctly but logged
    as unresolvable.
    Now recovery sets spellID unconditionally on a successful parse;
    the downstream "not in CD database" branch handles the actual
    miss with proper logging.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Sturdier laundering: dual-path tostring + string.format("%d")
    Logs from a remote-PC dual-box show party member rawSpellIDs are
    losing the launder despite tostring printing the digits ("19434"
    shows in the log but tonumber returns nil). Try a second pass with
    string.format("%d", x) - C-level integer conversion, output is
    plain Lua, tonumber strips any residual taint. Also pcall the

    0 comparison in SafeSpellID since arithmetic comparisons on
    tagged numbers can throw.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

  • Log skip when spellID can't be resolved (silent returns)
  • Harden GBI.GetCooldown against secret-tagged spellIDs
    The remote-PC scenario shows that even after laundering through
    tostring -> tonumber, the resulting number can still be secret-
    tagged (engine appears to track propagation through arithmetic on
    some classes of values). type(spellID) returns "number" on tagged
    numbers so the existing gate doesn't catch it.
    Single-chokepoint fix: GBI.GetCooldown now pcalls the table index.
    A tainted key just misses cleanly instead of throwing. KickCounter
    .isInterruptSpell now uses GBI.GetCooldown instead of direct
    GBI.Cooldowns indexing so it inherits the protection.
    Resolves the recurrence at KickCounter.lua:33 reported on remote
    PC after the prior launder-only fix.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Workflow: grant contents:write so packager can create GitHub releases
  • Simplify .pkgmeta: drop license-output + manual-changelog + empty externals
  • Remote-PC party CD tracking fixes (12.0.5 secret-value hardening)
    • KickCounter.lua: launder UNIT_SPELLCAST_SUCCEEDED's arg3 spellID
      through GBI.Taint.SafeSpellID before any table-key indexing.
      Mirrors the pattern already used in CastTracker.lua. Without this,
      remote-PC party members trigger "secret value" Lua errors at
      GBI.Cooldowns[spellID] lookup.
    • CastTracker.lua: wrap the castGUID:match recovery call in pcall.
      castGUID strings can also be secret-tagged on remote-PC party
      members; the method-call index would throw before. Now falls back
      silently if the string is tagged.
    • Bar.lua: ensure the unit-frame overlay is re-shown whenever a CD
      is dispatched to it. The overlay's visible flag could be flipped
      off by App.UpdateContext (e.g. context=world) hiding the engine
      even though the user explicitly opted into the overlay. Calling
      ov.Show() before painting is idempotent and keeps the display
      honoring DB.unitOverlay.enabled regardless of engine state.
      Diagnosed by debug-handoff session on a remote-PC dual-box setup
      where party members are genuinely "remote" and trigger taint
      enforcement that the original-PC dual-box did not.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Burst-ready quality gates (A + B + C)
    Refuses to fire the cd_ready sound on trivial bursts. The latch
    still re-arms on all-ready, but the actual sound only fires when
    ALL three gates pass:
    A) effective set has >= 3 spells (no fire from a one-spell
    observation in auto mode)
    B) peak simultaneous-on-CD count during the burst was >= 2
    (no fire when only one spell ever overlapped)
    C) burst window has been open for >= 30s (no instant on/off
    re-fires from a brief recast)
    Tracks burstStartAt + burstPeakInFlight, recomputed at every cast
    of a tracked spell. Both reset on M.Reset and after each fire/skip
    decision. Gate-failures log at Debug level for tuning.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Drop dead bar.text writes (no font template -> SetText errors)
  • Overlay icon spacing slider (0..20 px)
    ICON_GAP is now read from DB.unitOverlay.iconGap (default 2)
    instead of being a hardcoded local. New slider in /gbi config
    under the overlay icon-size slider; live-applies via UnitOverlay
    .Refresh which re-relayouts every container.
    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Interrupt row: drop duplicate seconds text + fix ready-stick
    • OnCDReady on the Interrupts window no longer hides the icon -
      it just sets endsAt to "now" so the next expireIcons tick
      flips the entry into the ready/glowing state. Previously the
      Brain's CD-ready timer fired Hide() unconditionally, undoing
      the persistence we'd added.
    • Removed the bar's own seconds-remaining fontstring (the icon's
      CooldownFrameTemplate already shows the countdown). Avoids a
      duplicate number rendering on the far right of the bar.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Interrupt row polish: name strip, ready-glow, hide empty rows
    • Name strip is now a separate area on the LEFT of the bar, right-
      aligned against the bar's left edge. Bar starts at NAME_WIDTH px
      in from the row's left.
    • When a spell goes off CD on the Interrupts window, the icon now
      PERSISTS at the bar's leftmost (frac=0) edge with an Action-
      Button glow effect, indicating the player is ready to interrupt.
      On next cast the glow clears and the icon rides the fill edge
      right-to-left again.
    • OnCDStart now reuses the existing entry by spellID (so the same
      ready-glowing icon transitions back into a ticking CD without
      creating a duplicate).
    • refreshNames hides empty party slots entirely and repacks the
      visible rows + shrinks the panel height to fit.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Interrupt name overlays bar + always visible
    Name fontstring is now anchored to row.progBar's LEFT (overlaying
    the bar) instead of sitting in its own strip above. Row height
    collapses back to icon-tall. applyLocked no longer hides the name
    • it stays visible regardless of lock state since locking should
      just dim chrome, not strip identification.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Interrupt row: name above bar, icons ride the fill edge
    • Row layout reshaped: name strip (14 px, BLACK OUTLINE for class
      color legibility) sits above the bar; bar fills bottom portion
      at icon-tall height.
    • Bar fill color switched to neutral teal-blue so it never collides
      with class-color names.
    • Each visible icon now rides its own fill edge: position recomputed
      every frame as (remaining/total) * barWidth from the bar's left
      edge. As the CD ticks down, icons slide leftward with the fill.
    • Default per-icon layout (LEFT/RIGHT grow + per-row wrap) is now
      bypassed for the Interrupts window since the bar OnUpdate owns
      icon positioning.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Interrupt bar fixes: tick-down fill + fixed panel width
    • Row-wide progress bar now ticks DOWN: full at cast, empties to
      zero at ready. Previously grew from 0 -> max which looked like
      "no bar visible" right after cast. SetValue now feeds remaining
      seconds against (0, total).
    • Interrupts window panel width is fixed (DB.bars.interrupts.barWidth,
      default 220 px) instead of being multiplied by icons-per-row.
      Icons just stack inside the bar area; bar always spans the same
      visual length regardless of how many CDs are active.
    • Options panel now shows a "Bar width" slider for the Interrupts
      window, and the icons-per-row + grow direction controls only for
      the Cooldowns window. Avoids the confusion of icons-per-row
      affecting an interrupts bar where the bar IS the row.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Visual interrupt test (button + slash)
    GBI.Bar.TestInterruptFill schedules a fake 15s Pummel CD on every
    party slot so row layout, progress bars, scaling, and grow direction
    can be tuned without a live M+ pull. Wired in two places:
    • /gbi test interrupt - now fills the bar in addition to firing
      the interrupt-alert sound (previous behaviour kept).
    • "Test interrupts (15s)" button in /gbi config beside the
      Bar appearance header.
      Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
  • Initial public release setup