Eight live fixes: profile switches, the dispel ring, a fatal GetChildren, Max Debuffs, retargets, the removed item globals, the RES icon, and a stranded visibility latch - #261
Merged
Conversation
… mid-apply Field report: "Factory.lua:890 script ran too long", through SyncFrame -> UpdateAuras -> IterateRaidFrames -> UpdateAllAuras -> FullProfileRefresh -> ApplyRuntimeProfile, i.e. an auto-profile switch. Run synchronously this walks every party, raid and pinned frame and rebuilds the whole Aura Designer signature set for each -- placedCoSig and placedStructSig per indicator per member group, all string-built. On a full raid with a busy Aura Designer profile that is enough to trip Blizzard's watchdog, and the watchdog does not warn, it ABORTS the execution. Everything in FullProfileRefresh after this call never ran, so a profile switch could half-apply and leave frames stale with nothing to re-drive them. The watchdog measures a single execution, so splitting the walk across frames removes the failure rather than making it less likely. Same shape as AuraContainer._kickLiveParse: snapshot the work list up front because the iterators walk registries that churn, re-validate each frame at execution time, and use a generation token so a newer call supersedes a pending one instead of two walks interleaving. The first chunk runs inline, so a 5-man is one chunk and behaves exactly as before. Not a regression from the current aura work: every function in the reported stack was last touched between 10 July and 12 August, and none of today's Factory changes are in that chain. (cherry picked from commit 8bc2288b656d2f6d53466dc93403ca6eee7d794c) (cherry picked from commit 10f36a085339b64da406fa7c302490384ca9542d)
…atal unguarded GetChildren
Dispel colour ring (undee): the by-type flag was read on its own, so a
user with Show Border OFF still got a coloured ring on every dispellable
debuff, with no reachable setting to turn it off -- the by-type checkbox
lives inside the Border section and is out of reach while that section is
off. Their only escape was to re-enable the border, untick by-type, and
disable the border again. The ring is border art, so it now obeys
debuffShowBorder. The colourblind SYMBOL stays ungated on purpose: it is
text, not border art, and shipped decoupled deliberately.
PropagateMouseOnChildren (mist, 126x in one session): frame:GetChildren()
was the one call in that function not pcall'd, while the comment directly
under it promised "everything here is pcall'd". The guard above tests
that the METHOD EXISTS, which says nothing about whether calling it is
permitted -- GetChildren refuses to hand forbidden children to tainted
code and throws doing it. The IsForbidden check cannot prevent this: a
child passes it and can still have forbidden children of its own, so the
throw lands one level down on the recursion.
Per that function's own note, a throw there does not skip one child, it
abandons the PLAYER_ENTERING_WORLD settle callback and takes
ApplyGlobalBindings, RunBindingRepair("zone-in") and
ResolveColdStartProfile with it -- so click-casting recovery was failing
on every zone-in that touched those frames.
Both parse clean, ENV-SAME, CRLF intact.
(cherry picked from commit 562b85c1792865bfadc3ebb481acc493ae977860)
…king it Reported as "set it to a maximum of 3, it shows 4 or more" on Show All with stock settings. Confirmed: Blizzard caps at maxFrameCount PER AURA GROUP and the engine has no container-level total, so a row split into N records renders up to N x max. The Important Debuffs highlight splits Show All into three groups and is ON BY DEFAULT, so the stock config already has a 3x ceiling; category mode reaches five or six. It cannot be budgeted away. Sharing one allowance would need to know how many auras each group will match, and the Show All records are separated only by candidate BOOLEANS which GetUnitAuraInstanceIDs cannot evaluate -- all three carry the same filter string, so a count returns the same number for each. Dividing blindly under-shows the common case, and collapsing the split to make the number true silently deletes the highlight. Krathe's call: never lose debuffs or functionality, so state the real ceiling. DF:GetDebuffRowGroupCount reports the worst case from settings alone (claims only ever remove records), and the Max Debuffs slider now carries a note giving the category count and the resulting maximum. It stays silent at one group, where the number means exactly what it says. hideOn for visibility and refreshContent for the text, because RefreshChildStates only calls refreshContent on a shown widget -- hiding from inside it would have frozen the note hidden. Refresh goes through tools2.refreshStates, not self:RefreshStates, since this group is also built into a popout. (cherry picked from commit c93a47b31bba4507a3848635d57316f94c0e1676)
…inding back
Report, still present on v5.4.0-alpha.3: "happens every time I join raid,
to fix have to reload after everyone has joined" -- Earth Shield drawn on
a player who never had it, and a Riptide indicator not lighting for the
player who did. Only while the raid is forming; a reload clears it.
Two places wrote the new unit BEFORE calling SetUnit and ignored the
result. SetSlotOwnerUnit guards on `owner.unit == unit` at the top, so an
optimistic write turns any refusal into a PERMANENT desync: the owner
reads as retargeted, every later call short-circuits, and the container
stays bound to the previous occupant until a reload. The regen drain had
it worse -- it clears pendingUnit and drops the owner from the registry
first, so a refusal lost the retarget outright.
Both now commit only on success. On failure the old token is left in
place, which is exactly what the combat branch already documented
("owner.unit is left on the OLD token deliberately, so GetUnit stays
truthful ... and a repeat call simply re-queues"); the OOC path was the
one place not honouring its own contract. The drain re-queues after the
loop, never inside it -- adding a key during a pairs() traversal is not
legal, only nil-ing an existing one is.
Also reads the engine back. AuraContainerSharedMixin:GetUnit returns a
plain string, not a secret, so "did the retarget land" is directly
answerable and was simply never asked -- on the slot path and in
confirmRetarget, which until now only checked that A parse happened and
not which unit it parsed. A mismatch is reported, not repaired: the unit
is already committed and Blizzard's SetUnit no-ops when its own token
matches, so the cure would be a rebuild.
⚠ Candidate fix plus a detector, NOT a confirmed diagnosis. SetUnit is
plain mixin state and rarely refuses, so these may be latent rather than
the reported cause. The read-back is the part that settles it: if this
recurs, the log names the wrong unit instead of leaving it looking like a
filter fault.
(cherry picked from commit 5a780c35d71e17631587bb41842810a981f92505)
…obals GetItemSpell and GetItemCount live in Blizzard_DeprecatedItemScript, which 12.1.5 removes outright. Both were exact 1:1 forwards to C_Item, and both were already dead for anyone running without loadDeprecationFallbacks, so the item pickers in CC:GetSlotItemInfo and CC:GetItemInfoById could error on a nil global today. CC:GetItemCount fell back to the alias of the very function it had just called, so the fallback is dropped rather than renamed. (cherry picked from commit 2d40a8a215a6431582d35192788e6f45218d21c8)
…payload names
Reported on live 5.3.1: the RES text intermittently does not appear in M+ or
raid, while the corpse still shows Blizzard's own res glow and the target can
accept. One reporter saw it for every battle rez they cast.
What is proven from the code:
* INCOMING_RESURRECT_CHANGED is the ONLY show driver for this icon. The
"pending accept" (yellow) state is derived from having previously observed
the cast -- resCache[unit] == 1 -- so a frame that never saw the casting
edge can reach neither state and falls through to Hide().
* ResTimerCleanup only ever hides. It never discovers.
* The only recovery is UpdateAllStatusIcons, reachable from a full-frame
refresh or a combat transition. Inside an M+ pull or a boss fight neither
happens, so a single missed update lasts the whole fight -- which is the
envelope every report describes.
So this icon is the one where dropping one update is permanent, and it was the
only status icon routing that update through a single unitFrameMap[arg1]
lookup. The payload is typed UnitTokenVariant (retail dump,
UnitDocumentation.lua:3497): the token delivered is not guaranteed to be the
token the map is keyed by.
Blizzard's own CompactUnitFrame registers this event with a plain RegisterEvent
and calls CompactUnitFrame_UpdateCenterStatusIcon(self) without reading the
payload at all -- every frame refreshes itself. Mirror that: walk all frames,
plus a separate pinned walk since IterateAllFrames has no pinned arm. The event
fires when a resurrection starts or stops on a group member, not on a timer, so
the sweep is affordable.
⚠ This removes the class of failure, not a confirmed trigger. I have not
reproduced the miss in game and this does not claim to name it. Two other
threads stay open: whether an instant battle rez presents an observable casting
edge at all, and that GetStatusIconFadeAlpha multiplies this icon by
fadeDeadIcons -- an icon which by definition only ever exists on a dead unit.
Field report, Krathe 2026-09-07: joined a raid from player housing; every
Aura Designer icon vanished -- including on his OWN frame -- and did not come
back until a reload. The park was clean (not one "believed dark but not
locked" warning in 5,043 entries). It was this latch. Three faults, three
fixes.
1. THE SELF-EXEMPTION WAS A STATE TEST, AND IT RACED
Frames/Update.lua gated on UnitIsUnit(unit, "player"). One second after a
reload, with the roster still building, that answers FALSE for your own
raidN token while UnitExists is already true and UnitIsVisible already
false -- so the branch ran on the player and latched him. The trail:
19:19:57 --- UI Reload ---
19:19:58 visibility latch ON unit=raid9 <- his own token
Now it requires the identity to have RESOLVED: two GUIDs that both read
back as plain strings. Anything else -- nil during roster build, a secret
under identity restriction, a pcall failure -- leaves the unit shown,
which is this latch's standing fail-safe rule. UnitGUID is localised like
its neighbours; the edge is on a per-tick path.
2. THE VERDICT WAS STORED IN THE CONTAINER, AND CONTAINERS ARE RETARGETED
This is the one that made it permanent. The registry is keyed by UNIT;
the actuation was keyed by whichever container pointed at that unit AT
THAT INSTANT. SetUnitVisibilityLatched(unit, nil) therefore could not
reach a container that had since moved -- and Handle:SetUnit re-seeded it
from its NEW unit, which during a raid join is very likely also latched.
The verdict migrated with the container instead of staying with the unit.
19:20:12 debuff: retarget raid8 -> raid9
19:20:16 defensive: retarget raid9 -> raid8
19:20:18 defensive: retarget raid8 -> raid9
19:20:32 raid2->raid1, raid3->raid2, raid4->raid3, raid5->raid4 ...
Nothing could recover it: ReconcileLatches walks the registry and calls
the same unit-scoped function, and checkDarkMismatch only ever warned in
the LEAK direction. A reload worked because it discards every handle.
Nothing stores the verdict now. Every actuation asks the registry for the
unit it is bound to RIGHT NOW (unitVisLatched / handleVisDark /
slotVisDark); _visLatched survives only as a memo of the last application,
to gate redundant work. Both _setVisLatch setters ignore their argument
and recompute, so every existing caller became correct without a call-site
change and none can poison a handle by reasoning about the wrong unit.
ReconcileLatches now re-asks every handle and slot -- idempotent, since a
container that agrees returns on the memo check -- and WARNS when it finds
one held dark for a unit that is not latched. That direction had no
detector at all, which is why the log showed nothing.
This is the converse of the August death-latch fix: that established that
an actuation keyed by UNIT needs an edge keyed by UNIT. The half never
checked was that an edge keyed by UNIT needs an ACTUATION keyed by UNIT.
3. IT DARKENED POOLS THE LEAK CANNOT REACH
The guard exists for the PLAYER filter token failing open, which can only
affect pools whose filter names PLAYER. Krathe's session: 2 of 10
container filters were source-relative; all 10 were darkened. The two
halves both come out of his log -- the PLAYER-token partition broke 5
times and every one carried vis=0, while the same probe ran 32 times at
vis=1 without a break; and 17 raid units latched in a single tick while
their containers still reported live helpful counts (raid18 held 8
throughout). So the trigger is real and the scope was not.
handleVisDark/slotVisDark gate on _idGateSourceRelative -- a flag this
file already computed in five places and read in none outside the debug
dump.
☠ This overrules a documented 2026-08-18 decision, at Krathe's explicit
direction. That note said every pool on a unit outside your world is
stale, not just source-relative ones, and narrowing once left a
cross-instance unit showing a stale debuff row. It is not retracted --
the comment above SetUnitVisibilityLatched now carries both sides and
names the switch to reverse it. What changed is the price: UnitIsVisible
goes false for out-of-RENDER-range group members, not only cross-instance
ones, so the blanket response fires constantly in a raid.
The death latch is untouched and keeps its per-unit blanket scope: death
really does freeze every pool, with no aura event to follow.
⚠ Not verified in game. Reproduce by joining a raid from an instance.
Krathe82
force-pushed
the
krathe/live-5.3.1-fixes
branch
from
September 8, 2026 02:27
f5aafdb to
f7a20c6
Compare
DanderBot
pushed a commit
that referenced
this pull request
Sep 9, 2026
…r GUID is read once, and the RES walk uses the handler's own pinned iterator Follow-ups from the PR #261 review. handleVisDark now returns false for a parentDrivenVisibility handle, matching every other latch path; the reconcile warning names the retune case as well as the retarget one; Update.lua caches the player's own GUID after one guarded read instead of two pcalls per unit per update, and its comment records the alternative reading of the 19:19:58 trail; Headers.lua's INCOMING_RESURRECT_CHANGED branch walks pinned frames through the same local upvalue its neighbouring branches use. (cherry picked from commit 579da52)
DanderBot
pushed a commit
that referenced
this pull request
Sep 11, 2026
Four live 5.3.1 fixes: aborted profile switches, the dispel ring, a fatal GetChildren, and Max Debuffs
DanderBot
pushed a commit
that referenced
this pull request
Sep 11, 2026
…r GUID is read once, and the RES walk uses the handler's own pinned iterator Follow-ups from the PR #261 review. handleVisDark now returns false for a parentDrivenVisibility handle, matching every other latch path; the reconcile warning names the retune case as well as the retarget one; Update.lua caches the player's own GUID after one guarded read instead of two pcalls per unit per update, and its comment records the alternative reading of the 19:19:58 trail; Headers.lua's INCOMING_RESURRECT_CHANGED branch walks pinned frames through the same local upvalue its neighbouring branches use.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Eight fixes on top of the ported aura-container work. Seven are from live 5.3.1 reports; the eighth (§8) is a regression in this branch's own lineage, found in the field tonight. Independent of each other, so they can be taken separately if any one is contentious.
⚠ Rebased onto current
mainon 2026-09-07 — the branch had drifted 39 commits behind.1 · A profile switch could be aborted part-way through
"script ran too long", through
SyncFrame→UpdateAuras→IterateRaidFrames→UpdateAllAuras→FullProfileRefresh→ApplyRuntimeProfile— i.e. an auto-profile switch.Run synchronously,
UpdateAllAuraswalks every party, raid and pinned frame and rebuilds the whole Aura Designer signature set for each —placedCoSig/placedStructSigper indicator per member group, all string-built. On a full raid with a busy Aura Designer profile that is enough to trip the watchdog, and the watchdog aborts rather than warns: everything inFullProfileRefreshafter that call never ran, so a profile switch could half-apply and leave frames stale with nothing to re-drive them.The watchdog measures a single execution, so splitting the walk across frames removes the failure rather than making it less likely. Snapshot the work list up front (the iterators walk registries that churn), re-validate each frame at execution time, generation token so a newer call supersedes a pending one — the same shape as
_kickLiveParse. The first chunk runs inline, so a 5-man is one chunk and behaves exactly as before.⚠ Not a regression from the ported work: every function in the reported stack was last touched between 10 July and 12 August.
2 · The dispel colour ring ignored Show Border
All borders disabled, yet every dispellable debuff still had a coloured ring — green on poison, brown on bleed.
debuffBorderColorByTypewas read on its own with no reference todebuffShowBorder. Worse, the by-type checkbox lives inside the Border section, so with borders off the user could not reach it to untick — their only escape was to re-enable the border, untick by-type, and disable the border again.The ring is border art, so it now answers to
debuffShowBorder.⚠ The colourblind symbol stays ungated on purpose: it is text, not border art, and shipped decoupled deliberately — someone running without borders should still get it.
3 · An unguarded
GetChildrenkilled click-casting recoveryReported 126× in one session:
frame:GetChildren()was the one call in that function not guarded, while the comment directly beneath it promised everything there was. The guard above it tests that the method exists, which says nothing about whether calling it is permitted —GetChildrenrefuses to hand forbidden children to tainted code and throws doing it.The existing
IsForbiddencheck cannot prevent this: a child passes it and can still have forbidden children of its own, so the throw lands one level down, on the recursion.Per that function's own note, a throw there does not skip one child — it abandons the
PLAYER_ENTERING_WORLDsettle callback and takesApplyGlobalBindings,RunBindingRepair("zone-in")andResolveColdStartProfilewith it. So click-casting recovery was failing on every zone-in that touched those frames.4 · Max Debuffs did not mean what it says
"Despite having set it to a maximum of 3 it shows 4 or more", on Show All with stock settings.
Confirmed, and it is an engine limit rather than a bug in the row:
maxFrameCountis per aura group and there is no container-level total (Blizzard_AuraContainerGroups—maxFrameCountis group state, nothing sums across groups). A row split into N records can therefore render up to N × max. The Important Debuffs highlight splits Show All into three groups and is on by default, so the stock configuration already carries a 3× ceiling; category mode reaches five or six.It cannot be budgeted away. Sharing one allowance would need to know how many auras each group will match, and the Show All records are separated only by candidate booleans (
isBossOrRoleAura/isPriorityAura) whichC_UnitAuras.GetUnitAuraInstanceIDscannot evaluate — all three carry the same filter string, so a count returns the same number for each. Dividing blindly under-shows the common case (a unit with only ordinary debuffs would get max/3), and collapsing the split to make the number true silently deletes the highlight.Neither trade is acceptable — losing debuffs or losing a feature to make a label honest is worse than the label. So it says so instead.
DF:GetDebuffRowGroupCountreports the worst case from settings alone (Aura Designer claims only ever remove records, so ignoring them is the right direction for a ceiling), and the slider carries a note:It stays silent at one group, where the number means exactly what it says.
Implementation notes:
hideOnfor visibility andrefreshContentfor the text, becauseRefreshChildStatesonly callsrefreshContenton a shown widget — hiding from inside it would freeze the note hidden. Refresh goes throughtools2.refreshStatesrather than the page's, since that group is also built into a popout.5 · A refused retarget desynced the container permanently
"happens every time I join raid, to fix have to reload after everyone has joined" — an indicator drawn on a player who never had that aura, and not lighting for the player who did. Only while the raid is forming; a reload clears it.
Two places wrote the new unit before calling
SetUnitand ignored the result.SetSlotOwnerUnitguards onowner.unit == unitat the top, so an optimistic write turns any refusal into a permanent desync: the owner reads as retargeted, every later call short-circuits, and the container stays bound to the previous occupant until a reload. The regen drain had it worse — it clearspendingUnitand drops the owner from the registry first, so a refusal lost the retarget outright.Both now commit only on success. On failure the old token is left in place, which is what the combat branch already documented (
owner.unitis left on the OLD token deliberately, soGetUnitstays truthful and a repeat call simply re-queues); the out-of-combat path was the one place not honouring its own contract. The drain re-queues after the loop, never inside it — adding a key during apairs()traversal is not legal, only nil-ing an existing one is.It also reads the engine back.
AuraContainerSharedMixin:GetUnitreturns a plain string, not a secret, so "did the retarget land" is directly answerable and was simply never asked. A mismatch is reported, not repaired: the unit is already committed andSetUnitno-ops when its own token matches, so the cure would be a rebuild.⚠ Candidate fix plus a detector, not a confirmed diagnosis.
SetUnitis plain mixin state and rarely refuses, so these may be latent rather than the reported cause. The read-back is the part that settles it: if this recurs, the log names the wrong unit instead of leaving it looking like a filter fault.6 · Two item globals that 12.1.5 removes
GetItemSpellandGetItemCountlive inBlizzard_DeprecatedItemScript, which 12.1.5 removes outright. Both are exact 1:1 forwards toC_Item, and both are already dead for anyone running withoutloadDeprecationFallbacks— so the item pickers inCC:GetSlotItemInfoandCC:GetItemInfoByIdcould error on a nil global today, not only after the patch.CC:GetItemCountfell back to the alias of the very function it had just called, so that fallback is dropped rather than renamed.7 · The resurrection icon could miss its only update
"It has happened twice this week… I could see the res light on the corpse and they could take the res, but the RES text didn't show", in M+ and in raid.
This is the one status icon where a single missed update is permanent:
INCOMING_RESURRECT_CHANGEDis its only show driver. The "pending accept" (yellow) state is derived from having previously observed the cast (resCache[unit] == 1), so a frame that never saw the casting edge can reach neither state and falls through toHide().ResTimerCleanuponly ever hides. It never discovers.UpdateAllStatusIcons, reachable from a full-frame refresh or a combat transition. Inside an M+ pull or a boss fight neither happens — so a missed update lasts the whole fight, which is the envelope every report describes.And it was the only status icon routing that update through a single
unitFrameMap[arg1]lookup. The payload is typedUnitTokenVariant, so the token delivered is not guaranteed to be the token the map is keyed by.The client's own
CompactUnitFrameregisters this event with a plainRegisterEventand refreshes each frame without reading the payload at all. This mirrors that: walk all frames, plus a separate pinned walk sinceIterateAllFrameshas no pinned arm. The event fires when a resurrection starts or stops on a group member, not on a timer, so the sweep is affordable.⚠ This removes the class of failure, not a confirmed trigger — the miss has not been reproduced in game and the change does not claim to name it. Two threads stay open and are not addressed here: whether an instant battle rez presents an observable casting edge at all, and that
GetStatusIconFadeAlphamultiplies this icon byfadeDeadIcons— an icon which by definition only ever exists on a dead unit.8 · A stranded visibility latch blanked every Aura Designer icon until a reload
Field, 2026-09-07: joined a raid from player housing; every Aura Designer icon vanished — including on the reporter's own frame — and did not return until
/reload. Three faults, all in the visibility latch.The self-exemption was a state test, and it raced. The edge gated on
UnitIsUnit(unit, "player"). One second after a reload, with the roster still building, that answers false for your ownraidNtoken whileUnitExistsis already true andUnitIsVisiblealready false — so the branch ran on the player and latched him:It now requires the identity to have resolved — two GUIDs that both read back as plain strings. Anything else (nil during roster build, a secret under identity restriction, a pcall failure) leaves the unit shown, which is this latch's standing fail-safe rule.
The verdict was stored in the container, and containers are retargeted. This is what made it permanent. The registry is keyed by UNIT; the actuation was keyed by whichever container pointed at that unit at that instant. So
SetUnitVisibilityLatched(unit, nil)could not reach a container that had since moved — andHandle:SetUnitthen re-seeded it from its new unit, which during a raid join is very likely also latched.Nothing could recover it:
ReconcileLatcheswalks the registry and calls the same unit-scoped function, and the drift check only ever warned in the leak direction. A reload worked because it discards every handle.Nothing stores the verdict now — every actuation asks the registry for the unit it is bound to right now, and
_visLatchedsurvives only as a memo of the last application. Both_setVisLatchsetters ignore their argument and recompute, so every existing caller became correct without a call-site change and none can poison a handle by reasoning about the wrong unit.ReconcileLatchesadditionally re-asks every handle and slot (idempotent — a container that agrees returns on the memo check) and warns when it finds one held dark for a unit that is not latched. That direction had no detector at all, which is why the log showed nothing.⚠ This is the converse of the August death-latch fix, which established that an actuation keyed by UNIT needs an edge keyed by UNIT. The half never checked was that an edge keyed by UNIT needs an actuation keyed by UNIT.
It darkened pools the leak cannot reach. The guard exists for the
PLAYERfilter token failing open, which can only affect pools whose filter namesPLAYER. In the reported session, 2 of 10 container filters were source-relative and all 10 were darkened. Both halves come out of the same log: thePLAYER-token partition broke 5 times and every one carriedvis=0, while the same probe ran 32 times atvis=1without a break; and 17 raid units latched in a single tick while their containers still reported live helpful counts (one held 8 buffs throughout). The trigger is real; the scope was not. The actuation now gates on_idGateSourceRelative— a flag this file already computed in five places and read in none outside the debug dump.☠ This overrules a documented 2026-08-18 decision, deliberately and at the maintainer's direction. That note said every pool on a unit outside your world is stale, not just source-relative ones, and that narrowing once left a cross-instance unit showing a stale debuff row. It is not retracted — the comment above
SetUnitVisibilityLatchednow carries both sides and names the single switch to reverse it. What changed is the price:UnitIsVisiblegoes false for out-of-render-range group members, not only cross-instance ones, so the blanket response fires constantly in a raid.The death latch is untouched and keeps its per-unit blanket scope: death really does freeze every pool, with no aura event to follow.
Confirmed in the field after the fix. The same event class — a 27-unit mass latch on a zone transition in a 25-man raid — cleared 14 units within one second and the rest within two minutes, with no reload. Session ledger went from 65 ON / 7 OFF to 45 ON / 41 OFF, the new blanking detector never fired in 3,963 entries, and the player's own token never latched once.
Verification
All changed files parse; no new globals; no line endings changed against
main; the toc is untouched.Fixes 2, 3, 6 and 7 are reproduced from the reports and from source rather than measured in game — 2 is a missing condition, 3 an unguarded call, 6 a removed global, 7 a missing recovery path, all readable from the source. Fix 1 reproduces a reported stack. Fix 4's engine limit is checked against the client's own
AuraContainerGroups. Fix 8 is diagnosed from the reporter's own debug trail and confirmed in the field after the change — it is the only one here measured on both sides. Fixes 5 and 7 are explicitly candidate fixes and say so in their commit messages; both add a detector or remove a failure class rather than claiming a proven trigger.