Dibs-v0.7.1
What's new
Changelog
[v0.7.1] -- random-enchant gear gets its EP
Changed -- release notes for v0.7.1
Release prep, 2026-10-12. A patch bump: everything since Dibs-v0.7.0 is a fix. GUI/Intro.lua's UPDATES now lists
v0.7.1 only (whatsnew_v071_suffix under Fixed, and whatsnew_v071_known, v0.7.0's known-issues text unchanged);
the whatsnew_v070_* keys are deleted from Locale/enUS.lua. The CurseForge page gains the v0.7.1 block and drops
v0.5.0 (five versions kept), and docs/24-test-run-checklist.md names the v0.7.1 key as its example. The player
bullet says healer scoring of the spell damage and healing bonuses is not yet checked in game, as the Peer Review
entry below requires. README and the page's feature list needed no change: neither claimed random-enchant items were
unscored. Self-audit thread d10e58a2: the bullet's first draft named "a ring of the Owl" unchecked; it now names the
"of the Owl" and "of the Bear" families, which Peer Review confirmed in ItemDB's suffix table.
Fixed -- random-enchant gear ("of Striking", "of the Owl") shows its EP
Cpthammer88, #dibs 2026-10-12: "dibs does not know what to do with some blues with varied stats ... Abyssal Leather
Leggings ... dibs doesn't assign them an EP. Pawn is able to." Loadout.Scorable refused every link with a random
suffix, because the suffix's stats are not on the base item id and scoring the id alone answers about a different
item. So the tooltip EP line, the upgrade arrow, every "vs. yours" column, the upgrade popup and the per-spec block
all said nothing for these drops. That refusal was the narrow fix; this is the complete one that
Modules/Declaration.lua's comment already described.
Data/ItemSources.lua gains Items:SuffixStats(link): the client's GetItemStats on the full link minus the same
call on that link with field 7 (the suffix) zeroed, so both sides come from one API in one vocabulary. The difference
is folded by Items.FoldSchoolStats (an "of Fiery Wrath" school's spell damage counts only for a spec that casts that
school, the same gate GetItemScore applies) and scored by the library's ScoreStats. Items:GetEP(id, hand, link)
and Items:ScoreForClassSpec(id, classID, spec, { link = }) add it to the base item's score, each on its own weight
scale; the base item's set, use and proc value is unchanged. Loadout.Scorable now accepts a suffixed link whose
suffix can be read and still refuses one it cannot (an uncached item), failing closed when there is no reader.
Loadout:DropDelta and Items:OffHandEPFrom take the link through, and SpecUpgrades.ForItem passes it.
Items:SuffixStats memoises a link that answered (never a not-loaded nil), since one hover asks it once per spec.
The WORN side too (self-audit, same day): Loadout:EquippedSlotEP and SpecUpgrades.SlotEPForSpec scored the worn
item by id, so a suffixed drop compared against a suffixed piece already worn counted the drop's suffix and not the
worn one's, and read as too large an upgrade. Both now pass GetInventoryItemLink, falling back to the base item's
score when the worn suffix cannot be read. Specs: Tests/scorable_spec.lua (+2) and Tests/specupgrades_spec.lua
(+2), both mutation-checked.
Peer Review on that self-audit (thread 23224f93, reply c9ffb968):
- Healer credit for "of Sorcery". If the client reports "Damage and Healing Spells" as the single key
ITEM_MOD_SPELL_POWER, ItemDB's alias reads it as spell damage only (ItemDB/Scoring/Vanilla.lua:78-80, where the key means aura 13 in ItemDB's own effect data), so a healer got nothing for it.Items.FoldSchoolStatsnow counts that live key as damage AND healing, which is what the Classic stat does. Which key the client reports is still NOT VERIFIED; if it reports the two halves separately this branch never runs. Release notes must say that healer scoring of random-enchant items is unverified until someone readsGetItemStatson a suffixed link in a Classic Era client. - The three field-7 readers are tied together.
ML.ItemKey,Declaration.SuffixOfand the zeroing rewrite inItems:SuffixStatseach read the suffix field; newTests/fieldseven_spec.luaruns seven link shapes (plain, positive and negative suffix, blank fields, an enchanted link, a short item string, a bare id) through all three and asserts they agree and that the rewrite changes field 7 only. Mutation-checked by zeroing field 8 instead and by refusing blank fields. The Data file reading a GUI module (ns.MasterlooterUI) is a LOW finding left for a later move ofItemKeyinto a shared module. - The Discord answer overstated that the EP fills in once the item loads; nothing repaints on an item load, so it shows on the next hover. A one-line correction was posted to the player.
NOT CHANGED: the auto Main/Alt tier (Declaration.TierForItem) still withholds its stat axes on a suffixed drop and
asks the player. NOT VERIFIED in a client: which stat keys Classic Era's GetItemStats reports for a random enchant's
healing and spell damage lines. ItemDB's alias table folds both ITEM_MOD_SPELL_HEALING_DONE spellings, and the
per-school damage keys are folded here. Specs: Tests/suffixscore_spec.lua (new, 13, mutation-checked on the
GetEP suffix term, the ScoreForClassSpec link term and the zeroed base link), Tests/scorable_spec.lua (+4), and
the loot table and roll window column specs retitled to the unreadable-suffix case they still pin.
[v0.7.0] -- resistance sorting, and two group-loot fixes
Changed -- release notes for v0.7.0
Release prep, 2026-10-12. GUI/Intro.lua's UPDATES now lists v0.7.0 only (whatsnew_v070_resist,
whatsnew_v070_untick, whatsnew_v070_rollhang, and whatsnew_v070_known rewritten from v0.6.0's with "new" dropped
from live rolling); the whatsnew_v060_* keys are deleted from Locale/enUS.lua. KNOWN COST: v0.6.0 shipped only
days ago, but Tests/locale_spec.lua pins UPDATES to one New / Changed / Fixed run matching the CurseForge page's
newest block, so its bullets cannot be carried; the page still has them. That spec's PAGE_ONLY is now empty, since
v0.6.0's LibDBIcon bullet is no longer in the newest block. The CurseForge page gains the v0.7.0 block and drops
v0.4.7 (five versions kept), and its feature list and the README now mention the Resistances sort and that unticking
"Let Dibs handle loot" opts out of the raid-wide switch. docs/24-test-run-checklist.md names a v0.7.0 key as its
example. ItemDB MINOR 43 is ItemDB v1.4.1, which is prepared but NOT yet tagged (its newest tag is ItemDB-v1.4.0,
read 2026-10-12), so the player notes say "ItemDB v1.4.1 or newer" and ItemDB should ship first.
Peer Review on the prep's self-audit (thread 2616e94e): the untick bullet first said "works again", which claimed a
regression that never happened (the raid switch never honoured the untick), and named only officers, although
TurnOnForRaid is gated on Comms:CanPush; reworded. The README and CurseForge opt-out sentence now carry the
master-looter exception (Settings.MasterLootRaider). GUI/Intro.lua's CARRY-FORWARD comment is rewritten to say
the popup shows only the newest version, which is what Tests/locale_spec.lua enforces.
Fixed -- no second window for a roll already answered, after a loading screen
Peer Review, thread 2616e94e, finding 5. Whether GetActiveLootRollIDs still lists a roll the player answered is
undocumented, and Blizzard's own PLAYER_ENTERING_WORLD loop re-opens every id it returns with no answered check
(wow-ui-source-classic_era Blizzard_UIParent/Vanilla/UIParent.lua:569). So GUI/LootRoll.lua now remembers each
game roll this client answers (rememberAnswered, called beside the one RollOnLoot) in ns.db.answeredRolls for
300 s, and LootRollUI:Resume skips a remembered id. A fresh START_LOOT_ROLL on a reused id forgets it (Show
without redraw). Correction to the entry above and to ResetState's comment: the risk was never a second
AUTOMATIC answer, because auto-roll lives only in ShowMLRoll (the master-loot prompt), not in Show. It was a
window put back up for a roll already answered. Blizzard's own frame for such a roll is left to the client.
Specs: Tests/lootrollui_spec.lua (+2), mutation-checked. Player notes: whatsnew_v070_rollhang and the matching
CurseForge bullet gain "Dibs does not reopen a roll you had already answered" (Dibs' window only; the game's own
frame is the client's).
New -- the Planner's Sort menu has a Resistances group
Asked for 2026-10-09 (resist sets) and recorded unfinished until now. The Build tab's Sort menu gains a
Resistances submenu with Fire, Nature, Frost, Shadow and Arcane; picking one orders the gear list by that school,
highest first. ns.Items.RESIST_KEYS (Data/ItemSources.lua) names the five keys and StatCategory files them under
a new resist bucket instead of Defense. No weight scale prices a magic resist (ItemDB Scoring/Vanilla.lua lists
armor only), so GUI/Planner.lua's new sortableStats() appends them to GetWeightStats(), and the menu and the
label cache now read that ONE list so a sortable key always has a caption. Captions come from the client's
RESISTANCEn_NAME globals with an L["filter_resist_<key>"] fallback. Specs: Tests/source_spec.lua (fire resist
now files under resist), Tests/plannerdraw_spec.lua (+1, Sort -> Resistances -> Frost re-orders the list;
mutation-checked by removing the group). The sort needs ItemDB MINOR 43 or later, which fills the magic-resist values
the Classic clients' GetItemStats never reported (Vanilla 1,371 items, TBC 667; Glacial Cloak 22658 now carries 24
frost). On an older ItemDB the menu still works but finds few items. Contract f93b32b3. No version gate in Dibs: the
API did not change, only the data.
Fixed -- unticking "Let Dibs handle loot" did nothing under a raid-wide switch
crimsonmane, #dibs 2026-10-11: "once enabled, disabling it does nothing. still has the dibs windows on grouploot".
Settings.LootEnabled ORs in the raid-wide switch (ns.db.raidLootOn), and nothing on the raider's client could end
it, so the box wrote the player's setting while every loot window stayed on until the group broke up. Unticking now
calls the new Settings.DeclineRaidLootOn (GUI/Settings.lua's General tab callback): the switch is dropped here and,
when somebody else pressed it, remembered as ns.db.raidLootDeclined so AdoptRaidLootOn refuses the next re-push.
The decline is per group, not per officer (Peer Review, thread fff716e1, finding 3): keyed to one officer, a
second officer pressing the button switched the player straight back on. That holds for the officer who pressed
the button and then unticked their own box, too. It lapses when the player is no longer grouped
(OnRosterSettled), and ticking the box again withdraws it (Settings.WithdrawRaidLootDecline, so the saved field
has one owning module). Master-loot raiders are unchanged: under a
master looter their loot side stays on regardless, as agreed 2026-10-08. KNOWN COST: the lapse is seen by the 5 s
roster debounce, so leaving and joining another group inside that window carries the decline across, the same
window the raid switch itself has. Specs: Tests/raidlooton_spec.lua (+7, including a different officer's push, and the presser's own decline),
Tests/dkpsettingsui_spec.lua (+1, untick declines, re-tick clears the decline).
Fixed -- a pending roll hung the group after a loading screen
crimsonmane, #dibs 2026-10-11: "caused rolling to hang for the group. after disabling the addon and /reload then
the timer showed for the item that was hung up". Blizzard re-opens every pending roll on PLAYER_ENTERING_WORLD
(wow-ui-source-classic_era Blizzard_UIParent/Vanilla/UIParent.lua:573-577, GetActiveLootRollIDs ->
GroupLootFrame_OpenNewFrame). In replace mode our OnShow hook hid that frame because replaced[rollID] was still
set, then LootRollUI:ResetState dropped our own frame: a live roll with no window, so nobody could answer it and
the group waited out the server timer. ResetState's comment claimed no roll survives a loading screen; that was
wrong and is corrected. The new LootRollUI:Resume runs after it and draws Dibs' window for every roll with time
left. When Dibs no longer takes a roll (loot handling off, augment mode, an unreadable item) the game's own frame is
shown again. That includes WoW Forever's gamepad roll list, which refills from the same call and could be left hidden
if its handler ran first (Peer Review, fff716e1, finding 6). Hiding and re-showing the game's frames is now one
helper, setBlizzardRollShown; hideBlizzardRoll wraps it (finding 7). Resume calls Show(id, left, true), and a
redraw skips UpgradeAlert:OnDrop, because UpgradeAlert wipes its dedupe memory on the same PLAYER_ENTERING_WORLD
and would otherwise re-pop the upgrade alert for every pending roll (Peer Review, thread 90fe4ab6, item 3). KNOWN
COST: a drop first seen during the login itself gets no upgrade popup. NOT VERIFIED: whether GetActiveLootRollIDs
still lists a roll the player already answered; if it does, auto-roll could answer it twice. That needs an in-game
check. Specs: Tests/lootrollui_spec.lua (+5).
Mutation checks: the session that started this work reported five production lines broken one at a time, each
turning its named test red (not re-run since). Later checks, each run and observed: the gamepad re-show, the redraw's
UpgradeAlert skip, and the presser's own decline.
[v0.6.0] -- the Buffs tab rebuilt, live rolling, and raid status
Opened as v0.5.3; renumbered to v0.6.0 at release prep (2026-10-09) because live rolling, the per-item roll tabs and raid status are new player-facing features, which makes this a minor release rather than a patch.
Changed -- release notes lead with the Buffs tab
The operator, 2026-10-10: "i like where we are with the buffs revamp, that should be the highlight for this
release." The title names it, and whatsnew_v060_buffs moved from Changed to the top of New in both
GUI/Intro.lua's UPDATES and the CurseForge Recent Updates. Its text now also says the list is searchable by stat
and that, on Classic Era, ItemDB v1.4.0 adds many more foods (DIBSREQ-IDB-010, MINOR 42: Vanilla foods 11 -> 67; TBC
already had its foods, so no TBC claim). Stones stay in their own Fixed bullet only, with its wrong-weapon caveat.
Peer Review (thread 7b2948d7) found both of those (a Classic-only claim stated for every flavour, and stones
announced twice) and a Planner comment that still gave the buff cap as 16; it is 32 (Buffs:Cap). It also found
nothing read the page: Tests/locale_spec.lua now asserts UPDATES and the page's newest Recent Updates block have
the same bullet count per group, with the page-only LibDBIcon bullet declared by name (+1 test, mutation-checked:
moving the Buffs bullet back to Changed in the popup alone turns it red).
Fixed -- release-prep reviews (Peer Review, d25eeeb0 and 9937c49d)
What's New lists v0.6.0 only (+whatsnew_v060_resize, with its LibAceGUIWidgets caveat, also on the CF page); every
retired whatsnew_* key deleted. Live MS under a Main-only gate says roll_no_raid_status_main (+2 specs,
mutation-checked). Two strings name the "Loot & Rolls" tab; the CF page names no files. The stone bullet says it
needs ItemDB v1.4.0; a stone that does not fit says so in its tooltip (buffs_tip_nofit, +1 spec, mutation-checked).
Boards retired (D6): docs/AUDIT.md and Tests/HARNESS_CONTRACT.md imported into the writ inbox and deleted, with
Tests/auditdoc_spec.lua, which only guarded the board; CLAUDE.md points at the inbox. Test checklist step 9 made
version-neutral.
Fixed -- the whole suite could not finish: nocaller_spec scanned the corpus once per export
Found by running the suite in chunks: Tests/nocaller_spec.lua alone ran past five minutes, and with a filter that
matched no test it finished, so the cost was in the sweeps. references and specReferences ran a gmatch over the
whole corpus for every export. wordCounts now counts every word in one pass, reproducing the gmatch's skip of an
occurrence that shares a separator with the previous one, so the counts are meant to be identical; every
count-based assertion and ceiling passes unchanged (old and new were not run side by side). Whole suite at
release prep (2026-10-10): 6016 passed, 0 failed. No player-visible change.
Changed -- under a master looter, every raider's loot side is on
crimsonmane, accepted by the operator 2026-10-08: "Let Dibs handle loot" governs group loot only. Settings.LootEnabled
ORs in Settings.MasterLootRaider() (master loot, group state ready, not the ML). The reminder popup is dropped.
Tests/raidlooton_spec.lua +5, mutation-checked. Peer Review (03a3c612): "not the ML" asked AmMasterLooter
(window-only), so between corpses an opted-out ML authored loot-log/SR+ entries; now RaidMasterLooterIsMe (+3).
Changed -- the Buffs tab is laid out like the Build tab
The operator: "i'm not really happy with the layout of this page at all". PlannerUI:DrawBuffs now draws every
PLANNED buff as a lit toggle chip at the top (pooled per buff key; a click removes it), a slot column with one row per
category (raid, world, totems, then each consumable slot) and its picks, and the library's W:CreateSearchBox over a
scrolling list of the selected category's options, best EP first, up to 150. The "+ Add" dropdowns and their plain
menu search box are gone. Tests/buffsdraw_spec.lua rewritten (7, mutation-checked). Slow open/close fixed: EP and
base stats are cached per repaint, and slot/list rows are pooled -- each repaint used to detach a container of
rows that stayed anchored to the scroll content, so closing re-laid out all of them (+3 tests). LibItemDB MINOR 42 adopted: stones score via
Items:WeaponDamageEP at the PLANNED weapon's base MELEE_WEAPON_SPEED (live speed only as fallback), and
Buffs:EntryFits (GetImbueWeapons) parks a stone that does not fit its weapon, e.g. a weightstone on a sword
(DIBSREQ-IDB-011, +7 tests, mutation-checked); the food strip is old-library only (weapondamage_spec).
Changed -- every on/off switch in Dibs' windows is a toggle chip
The operator, 2026-10-08, with Grouper's type chips as the reference: "they added toggle tokens, and they look
REALLY nice. we should use them where it makes sense", then "can we also clean up dibs settings to use the toggle
tokens? i think it will allow us to make the settings much more clean", then "update the windows to use toggle
tokens if it makes sense". New C.Toggle (Constants.lua) builds LibAceGUIWidgets' LAGW-Chip (MINOR 40) on a
plain frame, or the old checkbox-with-label on a library without chips, and returns one frame with GetChecked /
SetChecked / Click / SetLabel / SetOnChange / SetEnabled either way.
- View filters: the Planner's Upgrades only and Hide 2H, the Raids tab's Group alts, Show ex-members, Show test runs and the Accounts pane's In raid only, the DKP tab's Show ex-members and Group alts, the Loot Log's Show test runs. The Raids tab's list insets moved from -26 to -28 for the 22-tall chip.
- Settings:
B:Check(GUI/Settings.lua) builds a chip, and CONSECUTIVE ones share a line, wrapping at the tab's width; any other row (a number, a dropdown, a header, a button) ends the run, so the next switch starts a new line. The minimap switch goes throughB:Checktoo, with its own get/set pair. Each chip's_dibsIDis the setting key it writes. - The other windows: the loot table's "Decide main/alt for me", My Main / Alt's "this is my main" (moved up to
-239 to clear the dropdown below;
Tests/declbuttonoverlap_spec.luanow counts it as a button and names a chip in its failure message), the master looter's priority picker (a pooled chip per name), and the Soft Reserve settings tab's two switches. Only the Buffs picker stays a checkbox: its row is an item (icon, EP, tooltip, shift-click to link), and a chip would take the row's mouse. - Peer Review (thread e384723d): Settings lines are packed for the window's minimum width
(
MainWindow.SETTINGS_MIN_W), since a tab is drawn once and narrowing it used to push chips past the edge; a spec pins that a chip has a width when made andSetLabelresizes it; and one pins that the enchanters' disposal roll never lands on the item's roll tab. All three mutation-checked. - Specs find toggles by the
_dibsTogglemarker or their_dibsID, and set one by clicking only when it is not already in the wanted state (a chip's click flips it, so "SetChecked then Click" flipped it back).Tests/constants_spec.luadrives both paths ofC.Toggle;Tests/settingsui_spec.luapins the line sharing, mutation-checked (one toggle per line turns it red). Not seen in a game client.
New -- live rolling: the Gargul-style loot window, as an officer option
crimsonmane (#dibs 2026-10-01); the operator: "i'll leave that as is as an option, then build this new". Officer
setting lootTableStyle ("Loot window", Loot & Rolls): table (default, unchanged) or live. The master looter
stamps it into the table (t.live, TablePayload.live); both faces open it locked and executing (openLive), so
there is no picking phase. The rows lose Main / Off / Pass; a bar above the roll tabs reads Rolling on [item],
then Winner! name, with MS / OS / Pass that throw a real /roll (Pass is /roll 1) once per row
(UI:LiveRoll), greyed with the reason when nothing is called, already rolled, not listed, or barred by raid
status. OnRollNow rolls nothing for anyone on a live table. The roll tab is sorted MS, OS, Pass, highest first,
with everyone eligible who has not rolled as Yet (UI.LiveList). The master looter counts a 1-1 roll as a pass
(ML.LivePass), never as a roll. A row's item tooltip names its competitors (UI.RowTooltipText, both styles;
BindFrame's extra now follows the BiS block). Late rows are closed at once. EPGP and auction tables keep their own flow.
The master looter awards (crimsonmane, 2026-10-08: "masterlooter should pick the name and press Award, because
someone might roll on something they shouldnt"): a live row is called with manualAward, so LootSession.Resolve
turns the rolls into a ranked award list (tier, then roll; no tie-break) and the phase holds at "award". The
list lands on the live bar (state.liveAward, top roll picked); a click on a roll-tab line picks someone else
(UI:PickLiveAward); Award goes through ML:AwardTo -> ResolveRoll, as EPGP's does. The two-part table still
awards on the die. Raiders learn the rolls closed from loot_table_award (ML-gated, also in the reload catch-up):
UI:OnAward greys the bar and UI:LiveRoll refuses until the result (+4 tests). /dibs abandon then a re-call of
the same row left raiders latched (myRolled), the old rolls on the tab, and the award open: RollPayload now
carries a call token (sender:time:counter) and a new one resets all three, both table styles (+3 tests).
A handover clears the old call and award (AdoptMasterLooter). loottablegate_spec sweeps every groupOnly type. Raid assist is not built; Take all is the Distribute Later setting (docs/20). Eight tests in loottableui_spec and four in livepass_spec (live and two-part), all mutation-checked.
Peer Review (thread ea5329b9): MS / OS / Pass only asked the call's only list, so on a soft-reserved or
Competition row under Standard a non-competitor could throw a /roll the master looter then refused. Both the
painter and UI:LiveRoll now ask liveListed, which adds LootTable.Eligible. Also pinned: a setting change
mid-table leaves the open table's style alone. loottableui_spec now loads ItemInteract, as the TOC does, so
the row hover is driven for real. All mutation-checked.
Not seen in a client.
Changed -- LibAceGUIWidgets MINOR 41 adopted: windows re-lay-out during a drag on their own
ClearFrame now calls DoLayout from its own onResize (DIBSREQ-LAGW-003, which Dibs raised), skipping a second
layout when AceGUI's content hook already ran for that size. Dibs' staged copy, C.LiveRelayout, is deleted with
its two calls in GUI/MainWindow.lua and its five tests: on MINOR 41 it would have replaced the library's
onResize and lost that guard. A player still on MINOR 40 gets contents that settle at drag-end, as before the
helper existed. Not dragged in a client.
New -- the loot table shows every roll, one tab per item
The operator, 2026-09-30, answering Crimsonmane's "people did not like not knowing (not seeing) what everyone else rolled": "add a tab to the current rolls window that shows rolls per item ... it would auto generate 3 tabs, 1 for each piece of gear. and as the rolls come in, populate them into the correct tab", with "< and > arrows at either side of the tabs", and "this should be in the widgets library".
- The strip is LibAceGUIWidgets'
LAGW-TabStrip(MINOR 40, delivered on contracts thread cb965300), detected byAceGUI:GetWidgetVersion("LAGW-TabStrip"). With an older library the window is unchanged. - The panel (
GUI/LootTable.lua,paintRolls) takes the EPGP board's slot under the row list -- the two never meet, since an EPGP table throws no dice -- and appears once the master looter calls a row. One tab per row in table order; the shown tab follows the row being rolled until the player clicks another, and the next row the master looter calls takes it back. Each line is the roller, the number, and Main / Off spec / Tie-break or the range thrown (UI.RollLine); the winner's line turns green once the row ends. Not on auction or EPGP tables. - The wire: a new
loot_table_rollsbroadcast (LootTableSync.RollsPayload, master-looter gated like the other table broadcasts). The chat handler inGUI/Masterlooter.luanow handsonRollSeenthe roll the tally COUNTED (nil on a refusal), so only counted rolls appear -- a refused roll is already reported to the master looter and did not count. Live rolls go one per message; a reloading raider's catch-up (UI:OnTableAsk) sends each rolled row's whole list withfull, which replaces rather than appends. The master looter ignores its own echo. All of it is cleared with the table (OnTable,StartTable,ResetState), so a new corpse starts with no old tabs. - Fixture defect found on the way, fixed:
Tests/loottableui_spec.lua's "the window never appears on its own" block gave its second namespace this file's ownns.Comms, so its copy of LootTableSync re-registered every message type over the file's real ones; any broadcast asserted in a later block was provided from a window with no table and pushed nothing. ItsRegisterSyncTypeis now a no-op. - Eleven tests in
Tests/loottableui_spec.lua("the rolls per item"), mutation-checked: removing the broadcast, thefullreplace or the master looter's echo guard each turns one red. Not seen in a game client.
Fixed -- the Planner's Reset button left every gear-list filter in place
The operator, 2026-10-05, from WoW Forever: "bug: the reset button on forever isn't clearing out the
filters". The loadout bar's Reset (GUI/Planner.lua, resetBtn) only called Loadout:ResetToEquipped, so
Upgrades only, the search box, the source dropdown, the type dropdown and the sort stayed as they were. The
filters live in locals of the Build-tab draw and Reset never touched them. Nothing here is flavour-gated, so
Classic Era and TBC had the same bug. New PlannerUI.ResetFilters clears all six, Hide 2H included,
through the same setters the dropdowns and ApplyView use, so the saved copy in DibsDB.filters.planner is
cleared too and a /reload does not bring them back. Reset now calls it after the loadout reset;
loadout_reset_tip says so. Pinned in Tests/plannerdraw_spec.lua.
Fixed -- CurseForge was told LibDBIcon is embedded, so the app never installed it for Era or TBC
The vendored Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua arrived with --@curseforge-project-slug: libdbicon-1-0@
on line 1. The BigWigs packager reads that marker from every packaged .lua and records the slug as an
embedded library, overwriting the required-dependencies entry .pkgmeta set (release.sh:1078, then
:1880-1890). Dibs.toc and Dibs_TBC.toc hard-depend on LibDBIcon and do not load the copy (only
Dibs_Camelot.toc does), so a CurseForge-app install on Era or TBC got no LibDBIcon. Silent: the packager
exits 0. Marker deleted; Tests/pkgmetaclaims_spec.lua now sweeps every vendored file the Forever TOC loads
for it, with a floor, mutation-checked (red with the marker restored). Reported on inbox threads ec639087
(TOGBankClassic) and c7938f3e (Peer Review).
New -- raid status: which guild ranks may roll in a raid
The operator, 2026-10-05: "it should decide whethere they can roll in a specific raid. like if you set you 'progression' raid as AQ40 for example, you have to have AQ40 status to roll on an item, or to roll main on an item. so we need to be able to set it and have it enforced", and earlier the same day "this needs to be an officer setting and synced, so the auto roller accounts for it."
- Model: new
Modules/RaidStatus.lua. A raid may carry one rule: the guild rank indices (0 = GM, stored by index so a renamed rank cannot split clients) that hold its status, and a mode --"roll"(no status, no roll at all) or"main"(no status, no Main Spec roll;"ms"and soft reserve's"main"are both the top tier, a tie-break"tb"is not). A rule with no ranks ticked is not enforced, the same callLootSession.IsEligiblemakes for Manual with nobody flagged. A player the roster does not know (a pug, or the roster not loaded yet) holds no status. Loaded afterModules/RaidSystems.luain all three TOCs. - Sync: a
raidStatusfield inSettings.SerializePush/ApplyPush, under the same three rules as the other per-raid officer maps (keyed by raid, absent-means-silent, replace-not-merge); every edit callsSettings.Touch, so the newest stamp wins. - Enforced by the master looter:
LootSession.Newtakes an injectedstatusGate;AddRollandAddChoicerefuse with the new reasonno_raid_status(refused, never quietly re-filed as off-spec).LootTable.Newtakes the same gate andLootTable.Selectrefuses a barred pick; a pass is never barred.GUI/Masterlooter.luabuilds the gate fromRaidStatus.Gate(raidKey)in bothstartRollandRunTableRow, and theCHAT_MSG_SYSTEMhandler always printsml_roll_no_status(debug on or off), because the likeliest roller is a raider without Dibs. A refused choice reaches the raider aslootack_reason_no_raid_status. - Enforced on the raider's own windows:
GUI/LootRoll.lua's ML prompt andGUI/LootTable.lua's rows grey the barred buttons with the reason (greyed, never hidden; Pass untouched). The auto-roller moves a decided Main to Off-spec when only Main is barred, rolls nothing when rolling is barred, andUI:OnRollNowwill not throw a pick made before a rule arrived. Both table builders (the master looter's and the raider's) carry the gate, so a raider who takes over as master looter keeps enforcing it. - Officer UI: a Raid status block on the Loot settings tab, one dropdown per raid: the three modes, then a
tick per guild rank (
GuildControlGetRankName, which is 1-based and which the Classic Era calendar's mass-invite menu reads for any member; LibGuildRoster's rank names are the fallback). Greyed for anyone who is not an officer, and a click from a non-officer is ignored. New general row builderB:Menufor a dropdown whose menu the caller builds. NewComms:GuildRankOf(name). - Peer Review (thread 331a2c79), all three taken: (R1) a rank index is a position, so a rank the GM
inserts above a ticked one silently moves the status; each ticked rank's name is now recorded beside it,
RaidStatus.MovedRanksreports any that no longer match, the settings row says "(a rank changed -- check)" and the master looter gets one chat line per raid per session (ml_raidstatus_moved). It is a warning only: enforcement stays on the index so two clients still agree. (R2) the single-item Roll's gate is now pinned throughML.RollSlotinlootmethods_spec. (R3) the master looter's raid key now rides the roll (roll.raid) and the table (TablePayload.raid,t.raid), and every raider window gates on that key, falling back to its ownCurrentRaidonly for a master looter on an older build -- so the two cannot disagree about which raid it is. - Tests: new
Tests/raidstatus_spec.lua(27), plus raid-status blocks inlootrollui_spec(6),loottableui_spec(6),settingsui_spec(6),strayroll_spec(2) andlootmethods_spec(2). Every production line they name was mutation-checked: broken, seen red, restored. Nothing here has run in a client. constants_spec'sC.HoverWhileDisabledsite count forGUI/Settings.luagoes 5 -> 6 forB:Menu.- DKP / GDKP bids, when the officers turn it on (2026-10-08). The operator: "only if the officers turn it
on. these need to be settings so each guild can chose". New officer setting
raidStatusBids("Status also needed to bid", Raid status block), off by default and in the push. On,DKPAuction.Bidrefuses a bidder without status withno_raid_status(checked after pug eligibility, before the bid's own rules), and the bid popup greys Bid and says why (DKPBidUI.PaintStatusGate); Pass stays live. A bid is judged as a Main Spec claim, so mode"main"bars it as"roll"does. EPGP needed nothing: its award list is the table's picks, which the gate already checks. Five tests inraidstatus_spec, three mutations each seen red.
Changed -- .pkgmeta carries no comments
The operator ruled out comments in .pkgmeta entirely on 2026-10-03, and ClassicThreatLogger's review found
Dibs' still had two comment blocks. Both are removed. What they explained (no trailing # on a list item,
bare folder names, single-star quoted globs, every hard dependency listed explicitly) now lives in
CLAUDE.md's Testing section. Tests/pkgmetaclaims_spec.lua gains "carries no comment line at all" so
a comment cannot come back unnoticed. No player-visible change.
Changed -- the library-contracts board is retired
docs/LIBRARY_CONTRACTS.md was imported into writ's inbox and deleted, and docs/README.md and the
C.LiveRelayout comment in Constants.lua no longer point at it. The one request still open, ClearFrame
re-laying out its contents during a resize drag, went to LibAceGUIWidgets as a contract; MINOR 41 shipped it
(above).
[v0.5.2] -- raid sync fixes from the first Zul'Gurub night
Opened 2026-09-29 for crimsonmane's raid report, on the operator's instruction: "a few things for dibs v0.5.2".
Fixed -- P2P sync starved in a real raid: wishlists, the live raid and every ledger
Reported from a Classic Era Zul'Gurub raid: "wishlist are not syncing, none of us can see anyone's
wishlists", "all players show No Raid In Progress under Raids>Live except me", and "pretty much looks like
nothing is syncing at all, everyone is independent except the loot window". The loot window rides the direct
DibsPush channel; everything else rides DeltaSync's P2P hash-offer exchange, and that is where the fault was.
DeltaSync's P2P contract (P2PSession.lua:48-55) requires the host to call p2p:ReleaseSendSlot(requester)
after serving a request and p2p:OnItemCompleted(itemKey, sender) after making one. I never called either,
anywhere in Dibs. A provider has three outbound slots in total and a requester three sessions, so each was
pinned for its full safety timeout (90s and 180s). Two clients testing each other fit inside that. A raid
forming does not: twenty raiders ask the master looter for the raid record in one roster-settle window, three
are served, the rest are answered sync-busy, retry five times at 20s and give up.
Modules/Comms.luaonDataRequestnow releases the send slot on every exit, through a new localreleaseSendSlot. A served answer releases it when the reply has DRAINED, throughSendData'sonComplete(DeltaSync MINOR 18+); exits that send nothing (nothing to send, sender refused, unknown type) release at once. Peer Review finding A (thread 76b9da8b): my first cut released onSendData's return, which under AceCommQueue is up to ~70s before the last chunk leaves, and so turned the three-slot cap into no cap. A DeltaSync older than 18 has no callback and keeps release-on-return.- The
onSyncAcceptedcallback records each sent QUERY inawaiting, andcompleteArrivedcompletes it when that peer's answer of the owning type arrives. Peer Review finding B: my first cut completed it as soon as the query left, which gave up DeltaSync's delivery timeout -- the thing that fails a session whose provider logged off mid-reply and schedules a catch-up. The offline-peer and no-handler exits fail the session at once, so DeltaSync's catch-up retries. KNOWN COST: a provider with nothing to send holds that session until the delivery timeout, the same wait as before. - Completion is per ITEM and per SESSION (Peer Review's second pass on the same thread). It had matched by type,
and seven types own more than one key (LedgerSync, Characters, WishlistSync and SoftResSync's four), so the
first answer completed every awaited key of its type. Each now carries an
Answers(itemKey, data)handler naming the key an answer belongs to. And each awaited entry records DeltaSync's session id at query time: if that session has since been failed and replaced, a late answer drops the entry instead of completing its successor.Tests/syncanswers_spec.lua, mutation-checked. - No wire change: a v0.5.1 peer is unaffected, and a mixed raid benefits on whichever side is updated.
Tests/commscatchup_spec.luapins every exit and both findings, mutation-checked.
Not verified in a client. This is the one cause I found and fixed by reading the code; whether it is the whole of Crim's report needs a raid on v0.5.2.
New -- officers can turn Dibs on for the whole raid
Crim's ZG follow-up: a raider with a wishlist missed a roll because "Let Dibs handle loot" was off. The
operator's call: officers turn Dibs on for everyone and sync the raid settings, without touching anyone's
SR or wishlist. Settings.TurnOnForRaid (Settings, General tab) stores ns.db.raidLootOn and pushes
settings carrying raidLootOn; LootEnabled ORs it in and never writes lootEnabled. It lapses when
ungrouped, is taken only from a sender InSameGroup (the push also goes to GUILD), and is adopted before
the recency guard so a late joiner holding the same stamp still gets it. The client that pressed it
re-pushes group-only (Comms:Push(key, groupOnly)) when the roster grows. Tests/raidlooton_spec.lua,
mutation-checked.
It also forces autoRollTier and autoRoll on while it holds (Settings.RAID_FORCED, read in Settings.Get,
never written). The operator: the switch fixes a missed roll "only if pushing autoroll is part of this", and
"if you have auto rolling on, it'll just fix it" for a raider who never noticed the roll start. Known cost:
those two boxes read ticked during the raid and unticking them does nothing.
Peer Review (thread 7b592d3d): only the client that pressed the button carries raidLootOn on a push (R1 --
an adopted copy let any assistant re-spread it, ageless, past the recency guard), and a received switch lapses
when its author is no longer InSameGroup (R2 -- a raid converted to a party kept auto-roll forced). Both
mutation-checked. Its second pass on forced auto-roll found the mechanics sound (it fires only when the Main/Alt
setup singled out one button) but asked that raiders be told Dibs now rolls in their name: raidloot_on_chat,
settings_raidloot_active, the What's New, CurseForge and README text all say so, and that the roll is FINAL:
LootSession.AddPass refuses a player who already answered (already_answered) and the prompt hides Pass
once a roll is sent (GUI/LootRoll.lua, the sent branch). Not reviewed, not
verified: the loot table's fold on the master looter's face; nothing in this release has run in a client.
New -- the loot table folds down, and closing it says how to get it back
Crim: "the inability to see where I'm going, but also I forgot the /command to bring back the ML window"; the
operator: "it's too big, can we compact it somehow?" A collapse button beside Close folds the window to its
title, clock and status line (UI:SetCollapsed, saved in lootTableCollapsed). UI:Refresh now wraps the old
body (UI:RefreshContent) and applies the fold after it, so the repaint's own show/hide rules cannot undo it.
Closing a live table was always only a hide; it now prints once per table that /dibs ml (a new alias of
/dibs loot) brings it back. Tests/loottablecollapse_spec.lua, mutation-checked; the master looter's face
folded and a real client's resize floor under a 58px height are not verified.
Fixed -- Zul'Gurub coins opened the loot table on every trash pack
Same report: "zulian coins triggered loot window, was obnoxious". ZG runs master loot at green and the coins
are green, so scanLootItems (GUI/Masterlooter.lua) gave each one a row. New pure ML.IsTokenDrop(classID, quality): a quest-class item (Enum.ItemClass.Questitem = 12, ItemConstantsDocumentation.lua) below rare.
An unclaimed token is scanned at quality 0, so the officer's matsAction decides it the way it decides cloth.
The line is quality because ItemDB's own data draws it there (Data/Vanilla/_core/Quest.lua): the nine coins
and the AQ20 Stone Scarab are green, the Hakkari bijous blue, the Primal Hakkari idols and the Head of Onyxia
epic. A class-only rule would have taken rows from the last three. A reserved token (or a wishlisted one under
Wishlist Loot) keeps its row. Four tests in Tests/lootmethods_spec.lua.
Fixed -- WoW Forever: Dibs' tooltip lines repeated a dozen times
Reported on Forever 2026-09-29 (Adestar, screenshot): the EP, upgrade and per-spec lines stacked down the
tooltip, interleaved with two other addons' lines. On the data-driven tooltip (TooltipDataHandler.lua,
Forever tree) InternalProcessInfo runs every AddTooltipPostCall for each info it processes -- the primary one
and every info another addon APPENDED (info.append, which skips ClearLines) -- and RebuildFromTooltipInfo
replays the whole list on each refresh. My post-call never asked which pass it was on. New
UpgradeTooltip.ShouldAugment reads GetProcessingTooltipInfo() and skips an appended pass. Same fix reaches the
TBC build, which takes the same path; Classic Era uses OnTooltipSetItem and is unchanged. Two tests in
Tests/upgradetooltip_spec.lua, mutation-checked. Not verified on Forever: which addon appends was not
identified (the "Not suitable" line is in no addon installed here), and the other addons' repeated lines are
theirs to fix.
Changed -- the call to roll is a raid warning
Same report: "roll notification not done as a raid warning. would help those not using dibs and/or those not
auto-rolling". New C.RollCallChannel() (Constants.lua) answers RAID_WARNING in a raid when the player is
leader or assistant (UnitIsGroupLeader / UnitIsGroupAssistant, the pair Blizzard's own raid UI uses), and
otherwise falls back to C.AnnounceChannel(), since a raid warning from anyone else is dropped by the game. Only
the two roll-call lines use it (the single-item roll and the loot-table row roll in GUI/Masterlooter.lua);
awards, ties and whispers stay in raid chat so the warning keeps meaning "roll now". Four tests in
Tests/constants_spec.lua pin the rule; the two call sites are not driven under a raid leader by any spec.
Also: settings_lootenabled_tip tells raiders they never need to switch Dibs off for trash, and no longer says
"On by default" -- the default has been off since 2026-08-15.
Also: a README / CurseForge feature-parity pass (2026-09-30) brought each document up to the other's features,
and settled eight places they contradicted each other against the code: attendance does not need the loot
switch, low-quality loot has four options (Settings.CHOICES.matsAction), the roll window Shift-clicks to link
and Alt-clicks to compare (GUI/LootRoll.lua), alts stay per-character with an opt-in Group alts, pug
authority includes assistants, and neither document now claims TBC is verified live. The CurseForge Known
issues name EPGP awards too.
Also: the release doc-pass (2026-09-30) put the raid-wide switch and the loot table's collapse in the What's New
list (whatsnew_v052_raidon, whatsnew_v052_collapse), the CurseForge page's v0.5.2 New group, the README and
docs/20-loot-table-window.md; docs/05-settings.md carries the switch. The What's New popup carries the v0.5.2 changes, and v0.4.7 and v0.4.6's remaining headings moved to
CHANGELOG_ARCHIVE.md to bring this file back under the 120,000-character ceiling.
[v0.5.1] -- WoW Forever fixes, small upgrades show their arrow, and alts plan in their own gear
Opened 2026-09-27, straight after v0.5.0 was tagged (34ec9e6), on the operator's instruction: "we can continue under v0.5.1 after the release". v0.5.0 below is closed.
Fixed -- WoW Forever API sweep (and one shift-click bug on every client)
Operator, 2026-09-28: "we can do the wow forever work now". A read-only sweep of every WoW API Dibs calls
against the Forever client source (F:\Blizzard API Docs\wow-ui-source-forever, pulled 2026-09-26) found no
load-time error, master loot, group rolls, tooltips and the calendar all present in the shape Dibs reads, and
these gaps, each now read both ways with Classic Era and TBC unchanged:
- Chat lockdown secrets. Forever flags the raid/party/say/yell/whisper and system chat events
SecretInChatMessagingLockdown(ChatInfoDocumentation.lua).RaidRoster.NoteGUIDcompared and pattern-matched the sender GUID and the master looter's/rolltally parsed the system line, both from tainted code. Each now skips a valueissecretvaluereports as secret (feature-detected, absent on Era/TBC). - Pop-up edit boxes. Forever's StaticPopup is
GameDialogMixin: the edit box is.EditBoxviaGetEditBox()and the buttons sit inButtonContainerviaGetButton1(). Soft Reserve's save-template, save-setup and import did nothing there and export opened empty; Enter did nothing in every Dibs pop-up. NewC.PopupEditBoxreads both shapes andC.PopupEnterClicksAcceptreads both button shapes; Soft Reserve and the Planner's talent-save pop-up use it. - Shift-clicking a link into a Dibs box -- broken on Classic Era too, not only Forever.
ItemInteract.AcceptLinkshooked theChatEdit_InsertLinkglobal, but both the Era and Forever client source route every modified click throughChatFrameUtil.InsertLink, and the global is only a deprecation-fallback alias (Blizzard_DeprecatedChatInfo/Deprecated_ChatFrame.lua). Hooking the alias never fires. It now hooksChatFrameUtil.InsertLinkwherever it exists. The 2026-09-25 protected-mats fix was never confirmed in a client, and by this reading it could not have worked on a current Era client either. - Profession skill lines. Forever has only
C_SkillInfo, whoseGetSkillLineInforeturns a table. NewProfessions.SkillLineAPIadapts it to Classic's tuple; the profession scan andDisenchant.LiveSkill(the enchanter's rank for disenchant offers, which read nil on Forever) both use it.ExpandSkillHeaderis feature-detected the same way. - Every Forever player was scored as "default" spec. Forever has no
GetNumTalentTabs, soItems:PlayerClassSpectook the Mists path -- and Forever'sGetSpecializationanswers one id per class (1482-1491,Camelot/Blizzard_ClassTalentUtil.lua) thatSPEC_BY_IDcannot map. Forever keeps the three classic trees as GROUPS of one trait tree; newspecFromTraitGroupsreads each group's spent points the way Forever's own talent frame does (C_ClassTalents.GetActiveConfigID,C_Traits.GetConfigInfo,GetGroupDisplayInfoByTreeID,GetGroupCurrencyInfo), picks the tree with the most, and maps it throughSPEC_MAPlike Era.TRAIT_CONFIG_UPDATEDnow clears the cached spec. The group order was MEASURED on a Forever paladin (operator's/dump, 2026-09-28): Holy, Protection, Retribution atorderIndex0, 1, 2, which isSPEC_MAP's order; the other classes are assumed to match. Specs: 4 inTests/playerspec_spec.lua, mutation-checked. - Smaller:
Items:ItemSchoolsprefersC_Item.GetItemStats; the Wishlist event-tag menu anchors throughGetMouseFociwhereGetMouseFocusis absent. - The talent planner starts from your real Forever talents. On Forever the planner already drew
LibItemDB's tree, but
overlayLiveRanksneeds talent tabs and bailed, so it opened empty. NewoverlayTraitRanks(Modules/Talents.lua) walks the trait tree's nodes, takes each committedactiveRank, follows the active entry to its definition'sspellID, and matches it to the talent carrying that spell in LibItemDB's tree -- which is also what tells the three Weaponmaster entries apart. A node whose spell the tree does not list is skipped rather than guessed. NOT VERIFIED: that a trait definition's spell is the same rank-1 spell LibItemDB lists. - Gamepad roll list. Forever's gamepad-mode
GamepadGroupLootRollFrameanswers every roll in one list and was not hidden by the replace-the-roll-window option. It is now hidden when every roll it lists is one Dibs replaced, and left up while it holds a roll Dibs is not showing.
Specs: Tests/forevercompat_spec.lua (17 cases) and two new cases in Tests/lootrollui_spec.lua, all
mutation-checked; the Masterlooter /roll guard is not specced (its frame is file-local). Only the spec-order
reading has been checked in a Forever client (the paladin /dump); nothing else here has run there.
Peer Review (thread 857fcfc2), all five taken. overlayTraitRanks matched on overriddenSpellID and fell
back to a choice node's first entry, either of which could put a rank on the WRONG talent; it now uses spellID
only and the first entry only on a single-entry node. AcceptLinks also hooks the ChatEdit_InsertLink alias,
which still holds the unhooked original. The /roll skip is now ML.ReadRollLine (specced in
Tests/mlrollline_spec.lua) and tells the master looter once per roll, through a sayStatus helper shared with the
resume line so it lands on whichever window is showing. Not verified: whether a later status repaint during the
same roll paints over it. Characters registers
TRAIT_CONFIG_UPDATED. Each fix mutation-checked.
Fixed -- the Planner hid white gear while you were still wearing white gear
Operator report, 2026-09-27: a low-level paladin with the Copper Mace recipe searched the Main Hand list for it
and got "No gear matches your filters". Copper Mace is a white item, and itemsForSlot (GUI/Planner.lua) listed
white items only when the "Show white items in the planner" setting (showWhites, off by default) was on. The
empty-list message never mentioned that setting, so there was no way to find it. While levelling most gear on offer
is white, so the list read empty in exactly the slots that most needed an answer.
New PlannerUI.MinQuality sets the quality floor: whites are listed when the setting is on, and also whenever the
planned character wears a white or grey item in that slot or nothing at all, since a white can then be a real
upgrade. It keys on what is worn rather than on character level, so there is no level threshold to pick; once a
green is in the slot, whites drop out again. The floor is part of the slot-list cache key, so a gear swap never
serves the other floor's cached list. The "no matches" message now names the setting. Specs:
Tests/altgear_spec.lua "PlannerUI.MinQuality", mutation-checked (removing the worn-quality rule turned the
reported case red); Tests/plannerrowshape_spec.lua's source guard follows the new itemsForSlot signature.
Found at the same time, and not a Dibs defect: on WoW Forever Copper Mace also read "Dibs EP: 0" with no per-spec lines, because ItemDB's Forever weapon rows carry no damage-per-second (the same item scores protection 6.84 and retribution 20.86 on the Vanilla data, measured through Dibs' own scorer). Filed with ItemDB as DIBSREQ-IDB-009. Delivered the same day in ItemDB's working tree for LibItemDB v1.1.3 (Forever data rebuilt from an in-client item scan); through Dibs' own scorer on that data Copper Mace now scores protection 6.81 against the Anvilmar Hammer's 3.79, and retribution 20.77 against 11.57. The same rebuild moves Forever caster scores (hit and crit now land on both melee and spell keys, and many items gained spell power). Not verified in a client.
Dev tooling -- the replication watcher no longer deletes a live file from the replica installs
Peer Review from TOGProfessionMaster (thread e2a9ef3a): Sync-File in wow-version-replication.ps1 honoured a
Deleted event without checking that the source file was gone. An editor that saves by replacing a file raises
Deleted and Changed for one save, in either order, and when Deleted ran last the file vanished from every replica
while the source still had it; TOGPM lost Compat.lua from three installs that way. Dibs' copy had the same
branch. It now treats a Deleted event for a file that still exists as a change. Checked afterwards: _classic_,
_anniversary_ and _classic_beta_ hold the same 104 addon files as the source, so nothing had been lost here.
The running watcher keeps the old code until it is restarted. Not packaged (the script is dev-only).
Fixed -- WoW Forever: the bag arrow never showed in the stock bags
Operator report, 2026-09-27: the tooltip arrow worked on Forever and the bag-icon arrow did not.
BagArrowUI.AttachBlizzard hooked ContainerFrame_Update, and when that global was missing it returned "no
ContainerFrame_Update" and installed nothing. Forever has no such function. Its bags are the retail-family mixin
frames (camelot loads the mainline container files), which redraw through each frame's own UpdateItems, list
their buttons through EnumerateValidItems, and put several bags in one Combined Backpack frame, so the bag has to
be read from each button's GetBagID. The bags-greenarrow UpgradeIcon texture is on those buttons and nothing
in that client drives it, as on Era.
AttachBlizzard now falls back, only where ContainerFrame_Update is absent, to the new BagArrowUI.AttachFrames.
That post-hooks UpdateItems on every frame in ContainerFrameContainer.ContainerFrames plus
ContainerFrameCombinedBags, per frame rather than on the mixin, because Mixin has already copied the method into
each frame. PaintFrame gained the per-button branch, sharing a new paintButton with the Era path. Era and TBC
still take ContainerFrame_Update, unchanged. Three new tests in Tests/bagarrowui_spec.lua, mutation-checked
(each branch removed turned its test red). Verified by the operator in the Forever client after a reload: the
arrow shows in the Combined Backpack. The bank window was not checked.
Fixed -- small upgrades rounded to zero, so the green arrows never fired at low level
Operator report, 2026-09-27: a paladin in 5-armor Squire's Boots hovering 16-armor Ragged Leather Boots saw
"Dibs EP: 0", three "+0 EP" spec lines and no arrow. The scoring was right; the rounding threw it away. ItemDB
weighs armor at 0.02 EP a point for a Protection paladin and 0.005 for Holy and Retribution, so the swap is worth
about +0.22 or +0.06 EP. I rounded every EP figure to a whole number (Loadout:DropDelta, Loadout.ItemEPFor,
SpecUpgrades.TextFor), and the tooltip arrow and bag arrow both test delta > 0, so a sub-0.5 upgrade could
never show one. At low level nearly every upgrade is under 1 EP.
New C.RoundEP (Constants.lua) is the one rounding rule: whole numbers from 10 up, one decimal from 1, and two
significant figures below 1, so a real gain never rounds to 0. The operator's rule, the same day: "for the very
early game, we can't round to 0, or the green arrows don't work for anything". My first cut used a fixed two
decimals, which still zeroed anything under 0.005 (one point of armor for a Holy paladin). Only float noise under
1e-6 reads 0, so two identical items get no arrow, and a value never prints as "-0". math.log10 is present on
Classic Era (Blizzard's own Blizzard_SharedXMLBase/Compat.lua:27 reads it). All three sites use it. The four tooltip strings (tooltip_upgrade, tooltip_ep,
tooltip_spec_upgrade, tooltip_spec_upgrade_pct) take the EP as %s instead of %d, because %d truncates
0.22 to 0. The "vs. yours" columns, the loot roll window and the upgrade popup print through
Loadout.DeltaText, which concatenates the number, so they show the decimals with no change of their own.
Specs in Tests/scorable_spec.lua: the reported boots as a fixture, the tooltip string, and C.RoundEP's own
cases; mutation-checked (whole-number rounding put back turned five red, one of them printing "Upgrade: +0 EP").
Nothing had pinned the old rounding: the whole suite stayed green when I changed it, before the new specs
existed. Not verified in a client.
Fixed -- WoW Forever: hovering an action button raised a "secret number" error
Operator report from the Forever client, 16 repeats: Dibs/Data/ItemSources.lua:1859: attempt to compare local 'mainSpeed' (a secret number value, while execution tainted by 'Dibs'), reached from UpgradeTooltip:Augment
through Loadout into equippedContext, which feeds UnitAttackSpeed("player") to Items.SwingsPerSec. Forever
returns that speed as a secret value, and tainted addon code may not compare one. I wrote mainSpeed > 0 with no
thought for a client that could do this.
SwingsPerSec now routes each hand through usableSpeed, which treats a value issecretvalue reports as secret
the same as no weapon, so the item library falls back to its per-class swing default. issecretvalue is the
documented addon-side test (warcraft.wiki.gg, Secret Values), read as a global at call time and feature-detected,
so Classic Era and TBC, which do not have it, run the old path unchanged. Added to both .luacheckrc and
.luarc.json, which Tests/ asserts stay one list.
Spec: Tests/attack_spec.lua "with a secret speed (WoW Forever)", mutation-checked (removing the check turned both
cases red). Not verified in the Forever client. Other Forever reads that could come back secret have not been
swept; this fixes the one reported.
Fixed -- planning an alt showed the logged-in character's gear
The known issue "switching character on the Build tab can leave the previous character's equipped gear on the paperdoll, and the gear lists may then show 'no matches'". Three places read the LIVE character where the PLANNED one was meant, all mine:
- Every "else what's worn" fallback in
GUI/Planner.lua(the slot icon, its tooltip, its chat link, the enchant row's piece and the upgrades-only baseline) calledGetInventoryItemID("player"). They all go through one newPlannerUI.WornID(key)now, which reads the planned character's record for an alt.PlannerUI.PlanningLive()gates the two things only the live character has:SetInventoryItem("player")on the hover andGetInventoryItemLinkfor the chat link. Loadout:EnsureCharDefaultgave an alt an empty loadout, so every slot fell through to the live read. It now seeds from the alt's record.Loadout:ResetToEquippedalways rebuilt the logged-in character's default, so Reset on an alt jumped the Planner back to you. It takes the planned character's key now (the Reset button passesPlannerUI._planChar).
The record is new: Characters snapshots worn (, through Loadout:SeedFromEquipped) at
login and on PLAYER_EQUIPMENT_CHANGED, which the module now registers. Its comment used to say it deliberately
registered no equipment event, and that was right while no stored field moved on an equip; membership_spec
pinned it and now pins the new set (six events, still not UNIT_INVENTORY_CHANGED). Characters:Worn(key)
reads it back. An alt last seen before this version has no record and opens empty until it logs in once.
Specs: new Tests/altgear_spec.lua (5), plus two worn capture tests in membership_spec. Mutation-checked:
making WornID ignore the planned character, and making Reset always live, each turn their test red. Not
verified in a client. Not changed: the Planner's live stat panel (hit, crit, attack power) still reads the
logged-in character's own stats from the game, which an alt cannot supply.
Also found stale while checking, and removed from the CurseForge page's known issues: "the 'no matches for this
slot' message runs off the edge of the window" was already fixed (the hint is anchored on both sides and wraps,
GUI/Planner.lua), and "the Wishlist doesn't say which boss drops an item" was too (the Wishlist has a Boss
column filled from the item's drop source, GUI/Wishlist.lua sourcesFor). That empties the page's Planner and
Wishlist known-issues list, so its heading went with it.
DKP: the Absences dialog is the library's form dialog too
ensureAbsenceDialog (GUI/DKPTab.lua) was the last hand-built dialog in the file, because its paragraph (the
night, who signed up, who was absent, the penalty) is rebuilt on every open and the form dialog had nowhere to
put changing text. LibAceGUIWidgets added opts.body / f:SetBody in the same MINOR 37 on request
(DIBSREQ-LAGW-015), so the dialog is now W:CreateFormDialog with no rows: Refresh sets the paragraph with
f:SetBody(text) or clears it with f:SetBody(nil) beside the refusal, and the dialog's height follows the
text. Apply is the dialog's own OK button (f.applyBtn is kept as an alias to f.ok, tooltip unchanged) and
calls f._apply, which is the old OnClick body unchanged. Same frame name DibsDKPAbsence, same confirm, same
refusal sentences. The existing Absences specs in Tests/dkptab_spec.lua pass unchanged; mutation-checked by
setting a fixed body, which turned both body tests red. No new release precondition: it needs the same MINOR 37
as the paragraph above. Not verified in a client, including whether the height is right on the first open,
which the library flagged as unverified for a SetBody before the dialog has been drawn.
DKP: the Adjust and Season dialogs are the library's form dialog
The last of the LibAceGUIWidgets 33-37 adoption. ensureAdjustDialog and ensureSeasonDialog (GUI/DKPTab.lua)
no longer hand-build frames; both are W:CreateFormDialog (MINOR 37, DIBSREQ-LAGW-013), same names
(DibsDKPAdjust, DibsDKPSeason), same field aliases (charBox, decayBtn, clearBox and so on), same
Record logic and the same parented confirm. The Amount row's "negative deducts" line is that row's note; the
master looter's "this raid only" line is the Character row's note, set per open by SetScopeShown; the Season
dialog's leaver tick is a kind = "check" row. The title now takes the library's accent instead of
C.Colorize. DKPTabUI.CaptionClash walks every shown descendant rather than direct children, because the
form dialog puts each row's button in a holder frame; hidden children are skipped, so a closed confirm's OK is
not mistaken for a clash. Specs: Tests/dkptab_spec.lua reads the scope line from notes[1] (text included)
and gains a test that the guard sees a row's button, mutation-checked by cutting the descent. (The Absences
dialog followed; see the entry above.) Not verified in a client, including how the wrapped check label looks.
Tests: the Source-cell click finds its row through the library
Tests/questbookdraw_spec.lua's sourceOverlay now asks RowList:CellFrameFor (LibAceGUIWidgets MINOR 37,
DIBSREQ-LAGW-014) instead of reading rl.rows and rl:_getSortedData(), as Peer Review asked (thread
f0d06737). It errors rather than skips on a library without the method. Mutation-checked: asking for a column
that does not exist turns all four Source-click tests red. Test-only.
All of v0.5.0, v0.4.7, v0.4.6, v0.4.5, v0.4.4, v0.4.3, v0.4.2 and v0.4.1, and every version from v0.4.0 back, have moved to
CHANGELOG_ARCHIVE.md -- this file has to stay under GitHub's 125,000-character
release-body limit, which Tests/pkgmetaclaims_spec.lua asserts at 120,000 rather than leaving to whoever
happens to remember it. Released versions go across a few sub-sections at a time, oldest first; the archive's
own header records each move.
This mod has no additional files