Skip to content

fix: avoid AceEvent shared-frame taint for combat log registration (#22)#26

Merged
Xerrion merged 3 commits into
masterfrom
fix/22-aceevent-taint
May 12, 2026
Merged

fix: avoid AceEvent shared-frame taint for combat log registration (#22)#26
Xerrion merged 3 commits into
masterfrom
fix/22-aceevent-taint

Conversation

@Xerrion
Copy link
Copy Markdown
Owner

@Xerrion Xerrion commented May 11, 2026

Summary

Fixes the ADDON_ACTION_FORBIDDEN taint error fired against DragonShout when registering for COMBAT_LOG_EVENT_UNFILTERED via AceEvent-3.0.

Closes #22

The bug

Users hit:

AddOn 'DragonShout' tried to call the protected function 'AceEvent30Frame:RegisterEvent()'.

…even though DragonShout itself does nothing protected. The AceEvent30Frame is shared across every Ace3 addon in the session, and CallbackHandler-1.0's lazy OnUsed path registers the underlying WoW event on that shared frame the first time any consumer subscribes. If an upstream addon taints that path before DragonShout's first registration, the next call - ours - blows up.

This is a known, widespread issue affecting ~55 Ace3-based addons since January 2026 (CallbackHandler / shared AceEvent30Frame interaction).

Root cause

  • AceEvent30Frame is one process-wide frame shared by all Ace3 addons.
  • CallbackHandler-1.0's OnUsed callback calls frame:RegisterEvent(event) lazily on first subscription.
  • If that path is reached during a tainted execution context (common when other addons' secure handlers run before us), the shared frame inherits taint.
  • Subsequent RegisterEvent calls from clean addons - including DragonShout's CLEU registration - are then blocked by Blizzard's secure-execution guard.

The fix

Replace addon:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED", handler) in DragonShout/Listeners/CombatLogListener.lua with a private, unnamed CreateFrame("Frame") plus an OnEvent script. The frame is owned exclusively by DragonShout's CLEU listener - no shared CallbackHandler path, no shared frame, no cross-addon taint vector.

What is intentionally NOT changed

Core/Lifecycle.lua still uses AceEvent for the one-shot PLAYER_LOGIN registration. It runs early in the addon lifecycle, is rarely the first registrant on the shared frame, and degrades gracefully if it ever did fail. Keeping it on AceEvent preserves the standard Ace3 lifecycle conventions for everything that isn't the high-volume CLEU path.

See .deliverables/tech-lead/ADR-0001-dragonshout-aceevent-taint.md for the full design record (local working artifact, not committed).

Verification

  • luacheck . - 0 warnings, 0 errors

Notes

  • Squash merge per repo convention.
  • Please do not merge - waiting on CodeRabbit AI review; repo owner merges manually.

Summary by CodeRabbit

  • Refactor

    • Combat event registration moved to a private frame to reduce cross-addon taint; observable spell interrupt/aura/dispel behavior unchanged.
  • New Features

    • Added runtime detection for restricted retail environments and a fallback listener that uses unit events when combat-log events are unavailable.
  • Behavior

    • Interrupt and aura handlers now accept unified payloads from either combat-log or unit-event sources for consistent reporting.
  • Documentation

    • Updated guidance and known-gotchas about combat-log registration and taint mitigation.

Review Change Stack

Replace addon:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED", ...) with a
private, unnamed CreateFrame("Frame") + OnEvent script. The shared
AceEvent30Frame can be tainted by upstream addons via CallbackHandler's
OnUsed lazy-registration path, which then blocks any first-time event
registration from clean addons with ADDON_ACTION_FORBIDDEN. Using a
private unnamed frame for CLEU isolates DragonShout from this class of
cross-addon taint.

The one-shot PLAYER_LOGIN registration in Core/Lifecycle.lua remains on
AceEvent - it's rarely the first registrant and degrades gracefully.

See .deliverables/tech-lead/ADR-0001-dragonshout-aceevent-taint.md
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 272bdaa9-977e-4c53-98c7-93a216cd4531

📥 Commits

Reviewing files that changed from the base of the PR and between 49f0cd1 and 3199c8c.

📒 Files selected for processing (8)
  • .luacheckrc
  • DragonShout/Core/Capabilities.lua
  • DragonShout/Core/Init.lua
  • DragonShout/DragonShout.toc
  • DragonShout/Listeners/AuraListener.lua
  • DragonShout/Listeners/CombatLogListener.lua
  • DragonShout/Listeners/InterruptListener.lua
  • DragonShout/Listeners/UnitEventListener.lua

📝 Walkthrough

Walkthrough

Add runtime CLEU capability detection, gate CLEU behavior on ns.capabilities.cleuRestricted, move CLEU registration to private frames, add UnitEventListener to translate UNIT_* events into CLEU-shaped calls for restricted clients, and update listeners/docs/lint to accept translated extras.

Changes

CLEU gating and translation

Layer / File(s) Summary
Capability detection and flags
DragonShout/Core/Capabilities.lua
Detects retail CLEU restriction and publishes ns.capabilities.cleuRestricted and dispelAttribution.
Manifest & init wiring
DragonShout/DragonShout.toc, DragonShout/Core/Init.lua
Adds Core\Capabilities.lua and Listeners\UnitEventListener.lua to the TOC and includes UnitEventListener in LISTENER_MODULES.
CombatLogListener private-frame binding
DragonShout/Listeners/CombatLogListener.lua
Cache CreateFrame, create a private unnamed Frame, bind CLEU OnEvent to it, and gate Initialize/Shutdown on ns.capabilities.cleuRestricted.
UnitEventListener translation
DragonShout/Listeners/UnitEventListener.lua
New module: translates UNIT_SPELLCAST_INTERRUPTED and UNIT_AURA for player into CLEU-shaped calls and is enabled only when cleuRestricted is true.
Listener signature and CC exposure
DragonShout/Listeners/AuraListener.lua, DragonShout/Listeners/InterruptListener.lua
Expose ns.AuraListener.CC_TYPE; extend OnAuraApplied and OnInterrupt to accept an extras argument used by translated calls.
Docs and lint
AGENTS.md, .luacheckrc
Update AGENTS.md to recommend private-frame CLEU registration due to shared AceEvent frame taint; add UnitName to .luacheckrc read_globals for the addon.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

core

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title follows Conventional Commit format with 'fix' type and a clear, descriptive scope-description that accurately reflects the main change: avoiding AceEvent shared-frame taint for combat log registration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@DragonShout/Listeners/CombatLogListener.lua`:
- Around line 68-70: The CombatLogListener currently calls
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") unconditionally in
ns.CombatLogListener.Initialize which causes ADDON_ACTION_FORBIDDEN on Retail
12.0+; modify Initialize to check a capability flag (ns.capabilities.combatLog)
before registering and ensure that ns.capabilities is defined/initialized
(detect API availability in Init.lua during OnEnable where LISTENER_MODULES are
iterated), and if ns.capabilities.combatLog is false/absent, do not register the
combat log event and instead wire up a fallback path (create or reuse a
UnitEventListener fallback and call its Initialize from the same module); ensure
the check references ns.CombatLogListener.Initialize and the LISTENER_MODULES
loop so the gating covers the current initialization flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 8fe0550c-628c-4a3e-b278-2f6fa482400b

📥 Commits

Reviewing files that changed from the base of the PR and between 7d85f86 and ade0b98.

📒 Files selected for processing (1)
  • DragonShout/Listeners/CombatLogListener.lua

Comment on lines +68 to 70
function ns.CombatLogListener.Initialize(_addon)
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
ns.DebugPrint("CombatLogListener initialized")
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate capability routing and listener initialization =="
rg -n --type=lua -C3 'ns\.capabilities\.combatLog|CombatLogListener\.Initialize|UnitEventListener\.Initialize'

echo
echo "== Locate all CLEU registrations =="
rg -n --type=lua -C2 'RegisterEvent\("COMBAT_LOG_EVENT_UNFILTERED"\)'

echo
echo "Expected:"
echo "1) A selection branch on ns.capabilities.combatLog."
echo "2) combatLog=false path initializes UnitEventListener (not CombatLogListener)."
echo "3) No unconditional call path that reaches CombatLogListener.Initialize when combatLog=false."

Repository: Xerrion/DragonShout

Length of output: 1360


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find Initialize calls in main addon files =="
rg -n --type=lua 'CombatLogListener\.Initialize|UnitEventListener\.Initialize' --glob='!Listeners'

echo
echo "== Check for capability gating near Initialize calls =="
rg -n --type=lua -B5 -A5 '(CombatLogListener|UnitEventListener)\.Initialize'

echo
echo "== List all Listener files =="
fd -t f '\.lua$' DragonShout/Listeners/

Repository: Xerrion/DragonShout

Length of output: 111


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== List top-level addon files =="
ls -la DragonShout/*.lua 2>/dev/null || echo "No top-level lua files"

echo
echo "== Find all Lua files and structure =="
fd -t f '\.lua$' | head -20

echo
echo "== Search for ADDON_LOADED or main initialization =="
rg -n --type=lua 'ADDON_LOADED|DragonShout|RegisterAddon' | head -20

echo
echo "== Check if UnitEventListener exists =="
fd -t f 'UnitEventListener'

Repository: Xerrion/DragonShout

Length of output: 2540


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Check Core/Init.lua =="
cat -n DragonShout/Core/Init.lua

echo
echo "== Check Core/Lifecycle.lua =="
cat -n DragonShout/Core/Lifecycle.lua

echo
echo "== Search for ns.capabilities =="
rg -n --type=lua 'ns\.capabilities'

Repository: Xerrion/DragonShout

Length of output: 7746


Capability gating is missing — COMBAT_LOG_EVENT_UNFILTERED unconditionally registers without version/capability checks.

CombatLogListener is initialized unconditionally via the LISTENER_MODULES loop in Init.lua:OnEnable() (lines 117-122), with no gating on ns.capabilities.combatLog. On Retail 12.0+, this triggers ADDON_ACTION_FORBIDDEN. The addon needs either:

  1. A capability flag in ns.capabilities.combatLog that gates initialization, or
  2. A UnitEventListener fallback when the capability is unavailable.

Currently neither exists — ns.capabilities is not defined, and UnitEventListener does not exist in the codebase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DragonShout/Listeners/CombatLogListener.lua` around lines 68 - 70, The
CombatLogListener currently calls
frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") unconditionally in
ns.CombatLogListener.Initialize which causes ADDON_ACTION_FORBIDDEN on Retail
12.0+; modify Initialize to check a capability flag (ns.capabilities.combatLog)
before registering and ensure that ns.capabilities is defined/initialized
(detect API availability in Init.lua during OnEnable where LISTENER_MODULES are
iterated), and if ns.capabilities.combatLog is false/absent, do not register the
combat log event and instead wire up a fallback path (create or reuse a
UnitEventListener fallback and call its Initialize from the same module); ensure
the check references ns.CombatLogListener.Initialize and the LISTENER_MODULES
loop so the gating covers the current initialization flow.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Thanks for the catch — the ns.capabilities.combatLog and UnitEventListener references you flagged are stale documentation, not a missing capability gate. They came from abandoned PR #24 (fix/22-cleu-midnight-migration), which misdiagnosed issue #22 as a Retail 12.0 HasRestrictions = true registration block. That PR was closed unmerged.

The actual root cause of #22 is unrelated to Midnight: AceEvent-3.0 shares a single AceEvent30Frame (one CallbackHandler dispatch frame across every addon that embeds AceEvent), so cross-addon taint accumulates on it and triggers ADDON_ACTION_FORBIDDEN when CLEU is registered through AceEvent:RegisterEvent. The fix in this PR — registering COMBAT_LOG_EVENT_UNFILTERED on a private unnamed CreateFrame("Frame") — sidesteps the shared frame entirely. No capability gate or unit-event fallback is needed; verified against current FrameXML annotations and the CLEU wiki history (no 11.x/12.x restriction entries; HasRestrictions = true is the longstanding 'no varargs, use CombatLogGetCurrentEventInfo()' flag, not a registration block).

I've just pushed a follow-up commit cleaning up the stale AGENTS.md prose so it matches the actual fix.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Xerrion, thanks for the detailed correction — you're right on all counts.

The cross-addon taint cascade via AceEvent30Frame is a well-documented real-world issue where ADDON_ACTION_FORBIDDEN is thrown against AceEvent30Frame:RegisterEvent() through the OnUsed path in CallbackHandler-1.0 — exactly as you described. The error can appear attributed to a completely innocent addon because the shared AceEvent30Frame was already tainted by a different addon's execution context. The private unnamed CreateFrame("Frame") pattern is also consistent with Wowpedia's own recommended example code for registering COMBAT_LOG_EVENT_UNFILTERED.

My concern about ns.capabilities.combatLog and UnitEventListener was based on artifacts from PR #24 that no longer exist in the codebase. The fix here is correct and sufficient for the stated root cause. I'm withdrawing that comment.

One minor heads-up worth tracking separately (not a blocker for this PR): the combat log system was fundamentally changed in 12.0.0 (Midnight) — COMBAT_LOG_EVENT_UNFILTERED is no longer available to addons, and CombatLogGetCurrentEventInfo() is unavailable to tainted code. The private frame won't help there since the event won't fire at all on Midnight. That's a separate migration concern for whenever you need to support 12.0+, not for this PR.


✏️ Learnings added
Learnt from: Xerrion
Repo: Xerrion/DragonShout PR: 26
File: DragonShout/Listeners/CombatLogListener.lua:68-70
Timestamp: 2026-05-12T07:50:03.252Z
Learning: In DragonShout (WoW addon, Lua), the ADDON_ACTION_FORBIDDEN taint error on COMBAT_LOG_EVENT_UNFILTERED registration is caused by AceEvent-3.0's shared AceEvent30Frame getting tainted by cross-addon execution context (via CallbackHandler-1.0's OnUsed lazy registration path), not by a Retail 12.0 HasRestrictions flag. The correct fix is to register CLEU on a private unnamed CreateFrame("Frame") owned by the addon instead of using addon:RegisterEvent() through AceEvent.

Learnt from: Xerrion
Repo: Xerrion/DragonShout PR: 26
File: DragonShout/Listeners/CombatLogListener.lua:68-70
Timestamp: 2026-05-12T07:50:03.252Z
Learning: In DragonShout/Listeners/CombatLogListener.lua, HasRestrictions=true on COMBAT_LOG_EVENT_UNFILTERED is the longstanding "no varargs, use CombatLogGetCurrentEventInfo()" flag — it is NOT a registration block in pre-12.0 clients. COMBAT_LOG_EVENT_UNFILTERED is genuinely removed for addon code only in WoW 12.0.0 (Midnight). Do not flag private unnamed frame registration of CLEU as needing a capability gate for current (pre-Midnight) retail/MoP Classic/TBC.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Cleanup leftover design notes from abandoned PR #24 that documented
a ns.capabilities.combatLog gate and UnitEventListener module which
were never merged. PR #26 fixes #22 via a private CreateFrame frame
to escape shared AceEvent30Frame taint; the prior Midnight/HasRestrictions
diagnosis was incorrect.
@Xerrion
Copy link
Copy Markdown
Owner Author

Xerrion commented May 12, 2026

@coderabbitai — flagging this for the learnings table.

Update: PR scope vs. user-visible symptom on retail Midnight

After more digging into why issue #22 happens on retail 12.0.5 specifically, there's a second platform-level factor in play that this PR does not address. The PR is still correct and still mergeable as-is, but it's worth being explicit about what it does and doesn't fix.

What this PR fixes (still valid)

The Ace3 shared-bus taint problem is real: AceEvent-3.0 uses a single named global frame AceEvent30Frame, and CallbackHandler-1.0 calls OnUsed -> RegisterEvent without a securecallfunction wrap, so caller taint propagates into the shared frame and surfaces as ADDON_ACTION_FORBIDDEN on subsequent clean addons. Moving CLEU (and other high-traffic events) to a private unnamed CreateFrame("Frame") bypasses the shared bus entirely. That fix:

  • Eliminates the loud ADDON_ACTION_FORBIDDEN error on every flavor.
  • Fully restores functionality on Classic flavors (MoP Classic, TBC Anniversary, etc.) where CLEU still works normally for addons.

What this PR does NOT fix (new finding)

Retail Midnight (interface 120000+, pre-patch 2026-01-28, release 2026-03-02) removed COMBAT_LOG_EVENT_UNFILTERED for addon code during boss encounters and Mythic+ as part of the broader "addon disarmament" effort. CombatLogGetCurrentEventInfo() returns obfuscated "secret values" addons cannot parse. The engine-only COMBAT_LOG_EVENT_INTERNAL_UNFILTERED is not an addon replacement.

Sources:

Real-world impact: Details! ships a parser_nocleu1.lua codepath, WeakAuras has discontinued Midnight support, Decursive 2.8.0-RC5 release notes cite "CLEU being gone and APIs returning secret values."

Net effect for the reporter in #22: after this PR lands, the error stops spamming on login, but interrupt/CC/dispel announcements still won't function inside raids or Mythic+ on retail because CLEU won't fire there at all. Classic flavors are unaffected and benefit fully from this PR.

Note re: CodeRabbit's withdrawn comment

CodeRabbit's withdrawn comment on this PR had a buried claim that CLEU wouldn't fire on Midnight. The stated rationale (HasRestrictions) wasn't the actual mechanism, but the directional conclusion was correct for a different reason — the platform-wide CLEU removal above. Worth recording in the learnings table: "CLEU not firing on retail Midnight" is a real constraint as of interface 120000+, just sourced from the addon-disarmament change rather than from HasRestrictions.

Follow-up

Tracked in #28 — capability-gated migration to UNIT_SPELLCAST_INTERRUPT / UNIT_SPELLCAST_SUCCEEDED / UNIT_AURA + C_UnitAuras / C_Spell namespaced APIs, with Classic flavors continuing to use CLEU.

Recommend merging this PR as-is — it's a strict improvement on every flavor — and treating #28 as the separate retail-Midnight functional fix.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Tip

For best results, initiate chat on the files or code changes.

@Xerrion: Noted — recording all of this in the learnings table. Thanks for the thorough writeup and source citations.


✏️ Learnings added
Learnt from: Xerrion
URL: https://github.com/Xerrion/DragonShout/pull/26

Timestamp: 2026-05-12T08:12:55.723Z
Learning: In DragonShout (WoW addon, Lua), AceEvent-3.0 uses a single named global frame `AceEvent30Frame` shared across all addons. CallbackHandler-1.0 calls `OnUsed` -> `RegisterEvent` without a `securecallfunction` wrap, so caller taint propagates into the shared frame and surfaces as `ADDON_ACTION_FORBIDDEN` on subsequent clean addons. The fix is to register high-traffic events like `COMBAT_LOG_EVENT_UNFILTERED` on a private, unnamed `CreateFrame("Frame")` owned exclusively by the addon, bypassing the shared CallbackHandler path entirely.

Learnt from: Xerrion
URL: https://github.com/Xerrion/DragonShout/pull/26

Timestamp: 2026-05-12T08:12:55.723Z
Learning: In DragonShout (WoW addon, Lua), `COMBAT_LOG_EVENT_UNFILTERED` was removed for addon code on retail WoW Midnight (interface 120000+, pre-patch 2026-01-28, release 2026-03-02) during boss encounters and Mythic+ as part of Blizzard's "addon disarmament" effort. `CombatLogGetCurrentEventInfo()` returns obfuscated "secret values" that addons cannot parse. `COMBAT_LOG_EVENT_INTERNAL_UNFILTERED` is engine-only and is not an addon replacement. Classic flavors (MoP Classic, TBC Anniversary, etc.) are unaffected and CLEU still works normally there.

Learnt from: Xerrion
URL: https://github.com/Xerrion/DragonShout/pull/26

Timestamp: 2026-05-12T08:12:55.723Z
Learning: In DragonShout (WoW addon, Lua), the correct reason CLEU does not fire for addons on retail Midnight (interface 120000+) is the platform-wide addon-disarmament change (Blizzard, 2025-10-23), NOT `HasRestrictions`. Any future review comments citing `HasRestrictions` as the mechanism for CLEU suppression on Midnight are directionally correct but technically wrong on the mechanism.

Learnt from: Xerrion
URL: https://github.com/Xerrion/DragonShout/pull/26

Timestamp: 2026-05-12T08:12:55.723Z
Learning: In DragonShout (WoW addon, Lua), the retail-Midnight functional fix for missing interrupt/CC/dispel announcements (CLEU unavailable in raids/M+) is tracked in issue `#28`: a capability-gated migration to `UNIT_SPELLCAST_INTERRUPT`, `UNIT_SPELLCAST_SUCCEEDED`, `UNIT_AURA` with `C_UnitAuras`/`C_Spell` namespaced APIs for retail, while Classic flavors continue using CLEU.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Xerrion added a commit that referenced this pull request May 12, 2026
Retail patch 12.0 (Midnight, Interface 120000+) suppresses
COMBAT_LOG_EVENT_UNFILTERED for addons - the event registers but never
fires. Route interrupt and CC-on-player detection through UNIT_* events
on affected clients while leaving Classic (MoP, TBC Anniversary) on the
identical CLEU code path.

Per ADR-0002:

- New Core/Capabilities.lua exposes ns.capabilities.cleuRestricted, set
  at file scope from (C_RestrictedActions ~= nil) AND mainline build
  >= 120000.
- New Listeners/UnitEventListener.lua: private unnamed CreateFrame
  (mirrors PR #26 anti-taint pattern), registers UNIT_SPELLCAST_INTERRUPTED
  and UNIT_AURA. Translates payloads into the CLEU-shaped positional tuple
  the existing handlers consume, plus a trailing 'extras' table for spell
  info (CombatLogGetCurrentEventInfo is not the source of truth on
  Midnight).
- CombatLogListener.Initialize/Shutdown self-gate on cleuRestricted -
  no CLEU registration on retail Midnight.
- InterruptListener.OnInterrupt and AuraListener.OnAuraApplied gain an
  optional trailing 'extras' parameter. When nil (Classic forwarding
  via CombatLogListener), behavior is unchanged.
- Both dispatchers are always in LISTENER_MODULES; each self-gates on
  ns.capabilities inside its own Initialize.

Dropped on retail Midnight (no clean attribution event):
- dispels: DispelListener stays on disk but is never invoked.
- ccApplied (CC the player lands on others): sourceUnit on AuraData is
  unreliable.

Files added: Core/Capabilities.lua, Listeners/UnitEventListener.lua.
Files modified: Core/Init.lua, DragonShout.toc, .luacheckrc,
Listeners/{CombatLogListener,InterruptListener,AuraListener}.lua.

Refs: #28, ADR-0002
… (#29)

Retail patch 12.0 (Midnight, Interface 120000+) suppresses
COMBAT_LOG_EVENT_UNFILTERED for addons - the event registers but never
fires. Route interrupt and CC-on-player detection through UNIT_* events
on affected clients while leaving Classic (MoP, TBC Anniversary) on the
identical CLEU code path.

Per ADR-0002:

- New Core/Capabilities.lua exposes ns.capabilities.cleuRestricted, set
  at file scope from (C_RestrictedActions ~= nil) AND mainline build
  >= 120000.
- New Listeners/UnitEventListener.lua: private unnamed CreateFrame
  (mirrors PR #26 anti-taint pattern), registers UNIT_SPELLCAST_INTERRUPTED
  and UNIT_AURA. Translates payloads into the CLEU-shaped positional tuple
  the existing handlers consume, plus a trailing 'extras' table for spell
  info (CombatLogGetCurrentEventInfo is not the source of truth on
  Midnight).
- CombatLogListener.Initialize/Shutdown self-gate on cleuRestricted -
  no CLEU registration on retail Midnight.
- InterruptListener.OnInterrupt and AuraListener.OnAuraApplied gain an
  optional trailing 'extras' parameter. When nil (Classic forwarding
  via CombatLogListener), behavior is unchanged.
- Both dispatchers are always in LISTENER_MODULES; each self-gates on
  ns.capabilities inside its own Initialize.

Dropped on retail Midnight (no clean attribution event):
- dispels: DispelListener stays on disk but is never invoked.
- ccApplied (CC the player lands on others): sourceUnit on AuraData is
  unreliable.

Files added: Core/Capabilities.lua, Listeners/UnitEventListener.lua.
Files modified: Core/Init.lua, DragonShout.toc, .luacheckrc,
Listeners/{CombatLogListener,InterruptListener,AuraListener}.lua.

Refs: #28, ADR-0002
@Xerrion Xerrion merged commit f85fe8b into master May 12, 2026
2 of 3 checks passed
@coderabbitai coderabbitai Bot added core and removed listeners labels May 12, 2026
@Xerrion Xerrion deleted the fix/22-aceevent-taint branch May 12, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: RegisterEvent error

1 participant