TOGProfessionMaster-v1.6.0
What's new
TOG Profession Master Changelog
[v1.6.0] (2026-10-17) - Allied guild tags; Reagent Tracker scale, opacity and background; cross-realm supply mail
New Features
- Allied guild members carry a short guild tag after their name, like
Ahmbrai <Other>, so it is clear at a glance who is in which guild (operator, 2026-10-17: "The Old Gods (TOG) and The Other Gods (TOG) show you the problem"). The tag is worked out automatically from the first word that tells the guild names apart once filler words ("the", "of") are dropped, cut to five letters, and never shared by two guilds -- so The Old Gods and The Other Gods read<Old>and<Other>. Settings -> Cross-Guild has an "Allied guild tags" box (Guild Name = Tag, one per line) to pick your own, and lists the tags in use. Home-guild members carry no tag. The tags are LibGuildRoster's (MINOR 35, GuildRoster 1.4.0), at the operator's direction ("it should feed that to every addon that consumes a sister guild"; inbox 49703e36): the library derives them and stores your own tags account-wide under the home guild, so every TOG addon shows one guild under one tag. TOGPM's readers (GetGuildAbbrev,GetCharGuildAbbrev, the Settings box) are thin wrappers overGetGuildTag/GetCharGuildTag/SetGuildTag, the name columns repaint on the library'sOnGuildTagsChanged, andMigrateGuildTagsToLibrary(fromOnRosterReady) moves any tags a working copy kept inprofile.guildAbbrevsinto the library once and deletes them. Under an older GuildRoster there are no tags and the Settings box is greyed out. Typing a tag another allied guild already shows keeps it (your choice) but says so in chat, naming that guild, fromSetGuildTag's second return (Peer Review G1, inbox 2899de7d) -- once, when that tag is changed, not on every later save, and in the form the library stored (trimmed, at most 12 characters). TOGPM's own derivation is gone; its spec fixtures now run against the library's. Specs inTests/sisterguild_spec.lua. Location:TOGProfessionMaster.lua,GUI/Settings.lua,Locale/enUS.lua. - A supply mail the game refuses now says why in chat. After the Cooldowns
tab fills in a mail, the game's own mail error (and a failed send) is
repeated in chat with the recipient's name and the error's
ERR_MAIL_name until the mail goes or the mailbox closes. Only mail errors are repeated -- the first cut also blamed unrelated ones such as "Out of range." on the mail (Peer Review F1, inbox 60941923). Added for the operator's report of a mail to an allied-guild member that loops on the "might be someone you don't know" confirm (cause not yet known; GuildRoster asked to check its mailbox autocomplete, inbox 9aa8c30f). Spec inTests/cooldownmail_spec.lua. Location:GUI/CooldownsTab.lua. - Scale and opacity sliders for the Reagent Tracker (the shopping-list
window opened by right-clicking the minimap button). Settings -> Display now
has "Reagent Tracker scale" (50-150%) and "Reagent Tracker opacity"
(0-100%) right below the main window's pair, and both apply live. Changing
the scale keeps the window's top-left corner on the same spot on screen.
Saved per profile as
trackerScale/trackerOpacity. Specs inTests/reagenttracker_spec.lua. Scale checked in game on Classic Era. - The Reagent Tracker has a dark background, and the opacity slider sets
only that. It had none, so the list read as see-through over bright ground.
A black panel (50% at 100% opacity) now sits behind the rows; the slider
fades just the panel, never the text, icons or buttons, and 0% removes it,
matching the old look. The first cut faded the whole window, text included.
Not yet checked in game.
Location:
GUI/ReagentTracker.lua,GUI/Settings.lua,Locale/enUS.lua.
Bug Fixes
The Professions tab kept showing the recipe list from login until a /reload -- a guildmate learning a recipe, or someone leaving the guild, never reached it. CallbackHandler keeps one handler per (event,
self) and a second registration under the sameselfreplaces the first (Ace3CallbackHandler-1.0.lua:126-129). BrowserTab registered itsGUILD_DATA_UPDATEDre-warm asaddon:RegisterCallback(...)at load, and MainWindow registered its refresh the same way at OnEnable, so MainWindow's replaced BrowserTab's and the cache built 8 s after login was served all session (Peer Review H1, inbox 95fc14a4). Every addon-event subscription now registers as its own module (BrowserTab,MainWindow,CooldownsTab,addon.GUI,SyncLog, the Sync Log window's listener), and TOGPM's seven LibGuildRoster subscriptions register as the addon rather than as the library, a slot another addon making the same mistake would have shared (Peer Review M1). Specs inTests/callbackkeys_spec.lua. Its first seven cases (the GUILD_DATA_UPDATED one and the six AH / SYNC key checks) were red against the old code (run 20261017-173040.786-2899038007, 0 of 7 passing); the seven LibGuildRoster key cases were added after the fix and have only been run green. All 14 pass (run 20261017-174130.037-3435744109). Location:GUI/BrowserTab.lua,GUI/MainWindow.lua,GUI/CooldownsTab.lua,GUI/SharedWidgets.lua,GUI/Settings.lua,Modules/SyncLog.lua,TOGProfessionMaster.lua.The crafter-online alert named the two characters the wrong way round. When an alt of a shopping-list crafter logged in, chat read "Crafter is online (alt of Alt)" instead of "Alt is online (alt of Crafter)" -- the two names were passed to the message in swapped order. Specs (the first for this alert) in
Tests/crafteralert_spec.lua. Location:TOGProfessionMaster.lua.Right-clicking a crafter on the Professions tab whispered the display text, not the character -- the crafter list carried no character key, so the whisper fell back to the shown name, which can read "Alt (Main)". Each entry now carries its key -- the online alt's when an alt stands in for an offline crafter, so the whisper reaches someone who is on. Typing in the search box also no longer matches the allied-guild tag's colour code ("cff", "9d"). Specs in
Tests/browserlist_spec.lua. Location:GUI/BrowserTab.lua.Cooldown supply mail to a crafter on another realm went to the wrong person (operator, 2026-10-16). The mail button cut the realm off the stored
Name-Realmkey before filling the To: box, and a bare name is delivered to that name on the sender's own realm. The recipient now keeps the realm when it is not the sender's, and stays a bare name on the same realm; the greeting still uses the bare name. Spec inTests/cooldownmail_spec.lua. Not yet checked in game. Location:GUI/CooldownsTab.lua.The Reagent Tracker showed a scroll bar and one row too few (operator, 2026-10-15: "why did you add a scroll bar to the tracker?"). The list sizes its host to exactly the rows it holds, then counts the rows that fit by flooring the host's height; a height returned a hair short (likeliest at a tracker scale other than 100%, not verified in game) floored to one row fewer. The host now carries one unit of slack, and the window is sized from the list's own height so the slack never shows. LibAceGUIWidgets is told (inbox 62eb5c52) and RowList tolerates it itself from MINOR 44, so the slack is applied only under an older LibAceGUIWidgets. Spec in
Tests/reagenttracker_spec.lua. Location:GUI/ReagentTracker.lua.Clicking a recipe on the Professions tab did nothing while TradeSkillMaster's Crafting window was open -- the recipe went into TSM's search box and our detail panel stayed on "Select a recipe" (operator, 2026-10-15, with a screenshot of TSM's search reading the clicked recipe). TSM replaces
HandleModifiedItemClickwithorig(link) or hookFunc(link)(LibTSMWoWSource/API/Item.lua:158-166), so its hook runs on every click and answers "consumed" whenever its search box takes the link, modifier or not.ItemLink.Clickhanded every click to that router, so a plain left click looked consumed andClickRowreturned before drawing. It now asks the router only when a modifier Blizzard's own router acts on is held (CHATLINK and DRESSUP everywhere, plus EXPANDITEM, which on Retail opens the profession window for profession gear), so nothing changes without TSM. Every item link click in the addon goes through this one function, so all of them are fixed. Specs inTests/itemlink_spec.lua. Not yet checked in game. Location:GUI/SharedWidgets.lua.The shopping list on the Professions tab could still draw as an empty box with a scrollbar (Discord, 2026-10-13, one recipe on the list). v1.3.1 gave the section 2 px of slack against the client's whole-pixel snapping. That figure was a guess and was never confirmed in game, and at a large UI scale several snapped edges can take more than 2 px off. The list then floored one row to zero. The slack is now half a scale-1.0 row (
SL_SLACK = 7), which is well past what pixel snapping should cost and still less than a row at scale 0.5 or above. That snapping is the whole cause is read from the code, not measured in the client. A new spec inTests/browserdetail_spec.luatakes 4 px off the content and requires the row to draw. It was run with 2 px and fails, and only that spec fails. Verified in game on WoW Forever with one item (operator, 2026-10-13: "it looks good in forever with 1 item"). Not seen on the other flavours or with the scale change, and the poster's addon version is not known. Location:GUI/BrowserTab.lua.The shopping list did not re-fit when the shared text scale changed (Peer Review on the above). The section was sized once, at draw time. Another addon's Text Size slider (TOG Bank's) raising the LibAceGUIWidgets scale grew each row but not the section, so one item could floor to zero rows again until the tab was redrawn. The tab now registers a scale listener and refits the section while it is showing. The listener takes the tab as its owner argument, so the weak-keyed listener table does not keep a reloaded tab alive. The section's row height now comes from the scale itself rather than the list, so it does not depend on which listener runs first. The unused first
SetHeightinDraw, a second formula for the same height, is removed. Spec inTests/mainwindow_spec.lua(scale 1.0 to 2.0). Location:GUI/BrowserTab.lua.TOGPM handed three kinds of damaged widget back to the shared AceGUI pool (operator, 2026-10-13: "i think we may have some tabs not recycling widgets properly, and they are getting corrupted widgets from teh pool"). A read of every GUI file found three.
- Every tab draw set the main TabGroup's
LayoutFinishedto nil. TabGroup copies that method onto each instance, so this deleted it from the pooled widget. Once the window closed, the next addon to get that TabGroup could not auto-size it. It is nowSetAutoAdjustHeight(false)at creation, and AceGUI's Release clears that. - The shopping-list section's reference outlived the widget. The TOG Bank watcher's timer could refill a section already back in the pool, parking our list inside another addon's box. That box's release would later hide our list while it was showing in our window. The reference is now cleared by a release hook, which also covers the scale refit above.
- Column headers turned word wrap off on a pooled label and never turned it back on. AceGUI's Label does not reset that, so the next owner's text was cut to one line. That included our own Missing Recipes empty-state message. A release hook turns it back on.
- Also: the Professions tab's three next-frame redraws and the shopping-list
refresh only checked that the tab had ever been drawn. They now check that
the window is open on that tab (
BrowserTab:IsShowing), so a close in the same frame cannot draw the tab into a TabGroup already back in the pool.
Three specs in
Tests/mainwindow_spec.lua. Each would fail against the old code (read, not run).CLAUDE.mdsaid the TabGroup is not pool-recycled. That was wrong, and the rule now says how to undo raw changes. Whether these caused the Discord empty box is not shown. Location:GUI/MainWindow.lua,GUI/BrowserTab.lua,GUI/SharedWidgets.lua.- Every tab draw set the main TabGroup's
Improvements
- Removed: the Scan AH buttons on the Professions, Cooldowns, Missing
Recipes and Crafting tabs (operator on Discord, 2026-10-17: "remove the
scan AH button from TOGPM."; inbox 546bcca7). ItemDB's own button on the
Auction House scans the whole house in one pass, and every [AH] button and
price in TOGPM already reads that scan. The per-tab button looked items up by
name in the game's item cache and silently skipped any it had not seen, which
is why Missing Recipes missed patterns that were listed (Discord, GUNNEY,
inbox efaad501).
addon.GUI.MakeScanAHButtonandCraftingTab:ScanAHItemsare gone. What the button's callbacks also did -- repaint the visible tab's [AH] buttons when the AH opens, closes or a scan lands -- is nowaddon.GUI.SetAHRefresh(tab, fn), registered once per tab and run only for the active tab of an open window. The Crafting list is a row taller without the button, socraftscroll_specscrolls to 35 rather than the list's end. Specs inTests/sharedwidgets_spec.lua. The 12 specs that tested only the button are deleted (operator approval, 2026-10-17: "yes, delete the old tests and leftover test strings"), and so are the button's 11 locale keys (BrowserScanAH*,CooldownsScanAHDesc,MissingScanAH*,CraftScanAH*) from all 15 locale files. The sixaddon.AHscan functions only the button called (StartScan,StartFullScan,CancelScan,IsScanning,IsFullScanning,GetScanProgress) are deleted with their five specs inTests/pricefacade_spec.lua, on Peer Review's finding (inbox 0b5694c8) that they had no production caller left. Location:GUI/SharedWidgets.lua,GUI/BrowserTab.lua,GUI/CooldownsTab.lua,GUI/MissingRecipesTab.lua,GUI/CraftingTab.lua,Modules/AHScanner.lua,Locale/*.lua. - Less empty space above the shopping list — the shopping-list box is an
AceGUI InlineGroup with no title, but an InlineGroup draws its border 17 px
below its top for a title regardless. The Professions tab now lifts the
border to the top of the group and puts AceGUI's own anchor back when the
pooled group is released (a keyed
OnWidgetReleasehook). The section's height sum drops the strip with it: the chrome is nowSL_CHROME(23 px), one exported constant the specs read instead of a hard-coded 40. A spec inTests/mainwindow_spec.luachecks the border is lifted while showing and back at -17 after the window closes. Location:GUI/BrowserTab.lua. - A click spec through the row's real mouse handler — the recipe-list
click spec called
onRowClickdirectly, skipping the library's dispatch. A new spec drives the row frame's ownOnMouseDownand checks the detail panel shows the recipe, not the placeholder. Location:Tests/browservirtual_spec.lua. - The settings file is fully covered by the offline suite (Peer Review
thread bf2313d2, item F4). New specs drive the purge buttons and their
confirm dialogs, the alert options, the cross-guild options and the sync-log
window, which no spec reached before.
GUI/Settings.luais at 881/881 executable lines, and the whole suite passes (1907, 0 failed). Location:Tests/settings_spec.lua. - A report line for the toolbar "anchor family connection" error (seen in
game when opening the main window from the minimap; cause not yet found,
Peer Review thread 329b6368). Before a toolbar control's slot is laid out,
the toolbar now checks whether that slot is already reachable through the
toolbar's own anchors. The first time it is, it prints the chain of widgets
once per session, so the next report names the stale widget. A loop is only
one candidate: warcraft.wiki.gg names anchoring to the screen after a frame
is gone instead. So if a control comes out of AceGUI's layout anchored to
nothing, the toolbar also prints (once) every point of that control, of the
toolbar's first control and of the toolbar, with each target and parent.
AceGUI already runs its layout under its own error catcher, which hands the
error to BugSack and carries on, so this changes nothing about what is drawn
or logged; it only adds the chat line. Specs in
Tests/toolbar_spec.luadrive the real AceGUI layout with a SetPoint that raises and one that does nothing. The test harness is pinned atbaca63c, whose frames refuse an anchor loop with the client's message, so the loop spec now checks that AceGUI's layout hit that error and that both report lines print once. A control AceGUI leaves unplaced on purpose is skipped: any but the first child of a Fill layout, and one added after a full-height child in a Flow layout. Every other toolbar case now fails if it prints either report, so an ordinary draw can never show a player a false "please report this line". Location:GUI/SharedWidgets.lua. - Spec files no longer leave Settings open or stubbed timers behind.
Tests/settingsbleed_spec.luaclosed Settings by calling the toggle, which opened it for every example that had not; it now closes through the dialog and moves the clock.Tests/browserlink_spec.lua,Tests/craftsuppress_spec.luaandTests/pettraining_spec.luanow reinstall the base globals inteardown, so the next file does not load against theirC_Timer/GetTimestubs.
[v1.5.0] (2026-10-13) - Guild tab: search and filter by crafting skill; the Reagent Tracker stays open and minimises
New Features
- Guild tab filter strip, built from LibAceGUIWidgets. The library's
search box and two of its number steppers (Min skill / Max skill) sit
above the list in toolbar slots.
- The search matches every label on the page: a profession whose name
matches keeps everyone in it, a specialization whose name matches keeps
all its members, and otherwise only characters whose name matches are
kept (case-insensitive, realm ignored; "You" finds your own characters;
an account-mate's name finds its main, whose row reads "Alt (Main)").
It is applied in
GuildTab:BuildTree, which recounts from what it keeps. Branches open when a search starts and close when it is cleared, never on a refresh in between. - The skill steppers filter on CRAFTING skill in the profession being
counted, and run from 1 to
addon.SKILL_CAP(300 Classic Era, 375 TBC, 450 Wrath, 525 Cata, 600 MoP; on Retail the highest recorded skillMax, else 100). The full range is no limit, and a bound filters only once moved off its end. A character with no recorded skill in a profession is hidden from it while the range is narrowed. The range is applied per profession inBuildCounts, so headcounts read "how many matching". Empty rows are left out while filtering. - The list re-lays its rows when its scroll box changes size. Before, the
library re-laid only on its outer frame's resize; in game every row was
40px wide and the profession names drew as ".." while the scrollbar sat
at full width, so the last layout read the scroll box before it had its
width (inferred from the screenshot, not instrumented). LibAceGUIWidgets
MINOR 42 fixes it in the library (contract 0f350cea), so the hook is
installed only on an older copy (
GuildTab:NeedsRelayoutHook). - The character counts form one 80px column, centred under the
"Characters" header, which is itself centred over that column; the space
right of it is left free for later columns. The list's
valueColumnoption is LibAceGUIWidgets MINOR 42 (contract a79b0269); an older copy ignores it and keeps the counts at the row's right edge. The library scales that column by its shared scale (another addon can set it), so the headers are sized throughGuildTab:ColumnWidthswithW:ScaledSize, and a scale change redraws the window while the Guild tab shows. - The steppers are parked across draws by the new
addon.GUI.ToolbarStepper(GUI/SharedWidgets.lua), like the other toolbar controls, so a guild sync redrawing the tab mid-typing keeps the box and its number. - A debounced search that fires after a tab switch is dropped (per-draw
token cleared on release), so it cannot write the Guild count into
another tab's status bar. The tab's help tooltip describes the strip.
Location:
GUI/GuildTab.lua,GUI/SharedWidgets.lua, specs inTests/guildtab_spec.lua.
- The search matches every label on the page: a profession whose name
matches keeps everyone in it, a specialization whose name matches keeps
all its members, and otherwise only characters whose name matches are
kept (case-insensitive, realm ignored; "You" finds your own characters;
an account-mate's name finds its main, whose row reads "Alt (Main)").
It is applied in
- The Reagent Tracker stays up like Questie's tracker, and minimises.
It is no longer in
UISpecialFrames, so Escape does not close it. Whether it is open and whether it is minimised are saved per character beside its position (RT:State(),db.char.frames.reagentTracker), andPLAYER_ENTERING_WORLDreopens it (RT:Restore) after a logout or /reload. A new -/+ button left of the close button folds it to its title bar. Dragging now writes the position fields into that record instead of replacing it, so the flags survive a move. The window is always anchored by its top-left corner (RT:PinTop, on build and after every drag): anchored by its centre, as the default spot and the game's own drag anchor could be, minimising pulled the title bar down toward that centre. A position saved as a centre anchor by an older build moves its top once, by half the difference between the old height and 200, on the first open.SetDontSavePosition(true)keeps the client's own layout cache from competing with that saved anchor, and the window is clamped to the screen. Location:GUI/ReagentTracker.lua, specs inTests/reagenttracker_spec.lua.
Bug Fixes
Every icon in the Reagent Tracker was greyed out. The icon column returned
select(10, GetItemInfo(id)), which is the icon AND every value after it; the RowList reads a second return as "desaturate", and the sell price came second.RT:IconFornow returns one value. The other icon columns were checked: each cuts to one value withand ... oror a plain field. Location:GUI/ReagentTracker.lua.The Guild tab's column headers were drawn over the first rows of the list. The list's group was added to the tab's Flow layout while the header group above it was still empty, so it was placed at the very top with the full height; filling the headers afterwards never moved it.
Drawnow lays the tab out again once the top group has its real height. Location:GUI/GuildTab.lua.
[v1.4.0] (2026-10-11) - Item tooltips: an on/off setting for every line, no duplicate sell price, no doubled crafter names
New Features
- Every line TOGPM adds to an item tooltip has its own on/off setting.
Settings > Item tooltip gains five toggles: recipe difficulty, recipe
sources, your characters who haven't learned the recipe, vendor buy price
and vendor sell price. Crafters and IDs already had one each. The vendor
rows had no setting at all; they were driven by one hidden key,
tooltipVendorPrices, whichaddon:MigrateVendorPriceTogglescarries onto both new keys once (off stays off) and then deletes -- at login and again on every profile switch or copy, since a login only reaches the active profile. A row switched off is not priced at all. The recipe-details mode still decides whether the recipe block may appear at all, so its three part toggles are greyed out under "Never". A part switched off is dropped before the block's "is there anything to show" test, so a block left with nothing still writes no heading. Location:GUI/Settings.lua,GUI/SharedWidgets.lua,TOGProfessionMaster.lua,Locale/enUS.lua.
Bug Fixes
The crafters line and the IDs checkbox read a missing setting the opposite way from each other. Tooltip.lua treated a missing
tooltipShowCraftersas off although crafters has shipped on since v1.0.7 and its checkbox read it as on; the IDs checkbox read a missingtooltipShowIdsas on although IDs ship off. Every reader now takes the shipped default. A spec that asserted the old silent default was rewritten to the shipped one. Location:Tooltip.lua,GUI/Settings.lua.The Item tooltip header shared order 19.5 with the cooldown reminder field, a tie between the two. The header is now 19.505. Location:
GUI/Settings.lua.The vendor block repeated the game's own sell price. Classic's client prints "Sell Price:" on item tooltips while a vendor window is open (its
OnTooltipAddMoneyscript, classic_eraGameTooltip.lua:297), and Retail's tooltip data has aSellPriceline type for the same thing (TooltipInfoSharedDocumentation.lua:45; when Retail emits it was not checked). Our "Vendor Sell" row then showed the same number a second time. The hooks now notice the game's line and our sell row stands down; the buy row, which the game never shows, stays. Whether the ClassicOnTooltipAddMoneyscript fires before or after our item hook on every client has not been checked in game: where it fires after, the duplicate remains as before. Location:Tooltip.lua,GUI/SharedWidgets.lua.A crafter who makes an item through two professions was listed twice ("Bob, Bob"). The crafters line deduplicated per character AND profession, and seven Vanilla items come from two (Gold Bar is Mining's Smelt Gold and Alchemy's Transmute Iron to Gold). Deduplicated per character now. Location:
Tooltip.lua.The crafters line and the IDs line scanned every shipped recipe on every item hover (~1,565 on Vanilla, twice with the IDs line on). They read the cached item-to-recipe index the recipe block already used, filtered to crafted items so a teaching scroll is never credited to the recipe's crafters. Each item's recipes are sorted, so "the first recipe" the recipe block and the IDs line describe no longer depends on table layout; the sort falls back to comparing as text, so a non-numeric key cannot make it raise. Location:
Tooltip.lua.The legacy
OnTooltipSetItemhook skipped any item while the previous item's flag was still set. It tested the flag for truthiness instead of comparing it to the hovered item, the same defect the Show fallback had already fixed for itself. The vendor block's per-hover guard had the same shape and now compares to the item too. Location:Tooltip.lua,GUI/SharedWidgets.lua.A tooltip could carry two TOGPM labels. The crafters and IDs lines open with "[TOGPM]" and the recipe and vendor blocks with their own "TOGPM" heading; the vendor block checked only whether the recipe block had written one. A crafted reagent with a guild crafter and a vendor price showed both. One flag now records that a label was written, and every later block continues under it. Location:
Tooltip.lua,GUI/SharedWidgets.lua.Per-hover flags could outlive their item where
OnTooltipCleareddoes not run between two hovers, which the Show fallback's own comment says can happen. The recipe block and the brand heading then stayed suppressed for the rest of the session, and the new game-sell flag could hide the sell row on every later item. When a different item arrives while the last one's flag is still set, those flags are now reset as Cleared would have; the game-sell flag holds the item it was set for, and the modern post-call assigns it per item rather than only ever setting it. Raised by Peer Review on the self-audit thread (245309dd). Location:Tooltip.lua,GUI/SharedWidgets.lua.The IDs line's crafter count could disagree with the crafters line. It counted the first recipe's raw crafters, including ones the visibility gate hides, so it could read "crafters=1" over an empty crafters line. It now counts the same list the crafters line draws. The two IDs-line builders are one function. Location:
Tooltip.lua.Tooltip.luadisabled every tooltip hook if AceHook-3.0 was missing, a library the file never calls (its hooks are the frames' ownHookScriptandhooksecurefunc). The guard is gone. The crafters header's fallback colour was also a stale purple rather than the brand orange. Location:Tooltip.lua.
Improvements
- Tests: specs for each fix above, plus the crafters sort (online first,
then name) and the WoW Forever
TooltipUtilpath, neither of which any spec reached before.Tooltip.luaandGUI/SharedWidgets.luaare at 100% line coverage. Location:Tests/tooltip_spec.lua,Tests/tooltiphooks_spec.lua,Tests/vendorprices_spec.lua,Tests/sharedwidgets_spec.lua.
[v1.3.1] (2026-10-07) - The CurseForge app installs LibDBIcon again; Missing Recipes shows recipe names
Bug Fixes
- The CurseForge app stopped installing LibDBIcon on every client except WoW
Forever. The copy of LibDBIcon bundled for the Forever TOC still had the
packager's
@curseforge-project-slug: libdbicon-1-0@marker on line 1. The BigWigs packager scans every packaged.luafor that marker and lists the slug as an embedded library for the whole upload, which overrides.pkgmeta'srequired-dependencies(release.sh 1880-1890). So the upload told CurseForge LibDBIcon was embedded, and the app did not install the standalone addon that the Era, TBC, Wrath, Cata, Mists and Retail TOCs need before they can load. The marker is removed, andTests/loadorder_spec.lua(PKG-EMBED-001) now fails if either Forever-embedded file carries one. Found by TOGBankClassic's and Peer Review's audits (inbox 5503f91c, 5d7eaf86). Whether the v1.3.0 file on CurseForge shows LibDBIcon as embedded was not checked on the site. Location:Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua. - Missing Recipes cut recipe names to about ten characters and the window
could not be widened (Discord, 2026-10-07: "I cannot read what the recipe is
without scrolling over it and there is no way to move columns to the right to
read it"). The tab was locked at 720x500 to match Cooldowns, and Sources took
a fixed 180 px for a word like "Drop". Recipe is the list's auto-width column,
so it got only what was left over. The tab is now resizable and shares the
saved size of Professions, Crafting and Profit Planner, and Sources sizes to
its widest text (
autoFit, minimum 60), so the space goes to Recipe. Three specs inTests/missingrecipes_spec.lua. Verified in game (operator, 2026-10-07: "the rows are indeed much wider"). Location:GUI/MissingRecipesTab.lua,GUI/MainWindow.lua(comments). - The shopping list on the Professions tab could draw as an empty box with a
scrollbar (operator, 2026-10-07: "i can't see the stuff on my shopping list
anymore", one recipe on the list). Since the move to the LibAceGUIWidgets
RowList in v1.2.0, the section was sized to exactly its rows plus the
InlineGroup's 40 px of chrome. The client snaps anchors to whole pixels, so the
row area could come out a fraction short. The RowList floors height / row
height, so one row became zero, and a longer list lost its last row. The
section now gets 2 px of slack (
SL_SLACK). A new spec inTests/browserdetail_spec.luatakes half a pixel off the content and requires the one row to still draw; it fails without the slack. The diagnosis is read from the code and fits the screenshot, but not yet confirmed in game. Location:GUI/BrowserTab.lua.
[v1.3.0] (2026-10-04) - Retail support; WoW Forever's borrowed ProfessionDB values are shown as unconfirmed
New Features
- Retail (operator, 2026-10-04: "lets add retail support, i belive PDB was
updated enough for that"). A new
TOGProfessionMaster_Mainline.toc(Interface 120005 / 120007 / 120100) loads the same files with the same dependencies as every other TOC; each required library already lists those interfaces, and LibProfessionDB MINOR 13 ships its own Retail data tree. Retail runs the C_TradeSkillUI scan and craft path built for WoW Forever.addon.isRetail(Interface 100000+, the bound LibProfessionDB uses) makes Jewelcrafting, Inscription and Archaeology available and hides First Aid. Retail has no single expansion skill cap, soaddon.SKILL_CAPis nil there and the Guild tab shows the scanned max. Retail recipes carry a per-expansionskillLine, and their skill numbers are on that line's scale: the newaddon:GetRecipeSkillRankreads the local character's rank for that line live (C_TradeSkillUI.GetProfessionInfoBySkillLineID) for "Can learn now" and the scroll tooltip's Requires line, and answers "unknown" (no gate) for another character, whose per-line ranks are not synced yet. The profession registry refresh no longer requiresGetNumSkillLines, which Retail lacks;GetProfessionsserves.wow-version-replication.ps1now also replicates to_retail_. Loads and scans in game (operator, 2026-10-04: "retail opens now without errors, i was able to populate my retail data in"). Location:TOGProfessionMaster_Mainline.toc,Compat.lua,TOGProfessionMaster.lua,Scanner.lua,GUI/GuildTab.lua,GUI/MissingRecipesTab.lua,GUI/SharedWidgets.lua,Modules/CommTest.lua,Tests/compat_spec.lua,Tests/loadorder_spec.lua. - Retail: every character's per-expansion skill syncs, so alts are gated too.
A profession record may carry
lines([skillLine] = { skillRank, skillMax }), recorded by the owner's window scan fromC_TradeSkillUI.GetChildProfessionInfosand sent as an optionallfield on the owner's existingprofessions:leaf, re-minted by the owner only (peers adopt the hash as before). Only a Retail scan writeslines, so a Classic client's payload and minted hash are unchanged, and the v1.2.0 reader ignoresl.GetRecipeSkillRankanswers another character's synced line rank. Four writers that rebuilt a profession record from scratch now keep itslines. On Retail the profession-level rank has one writer, the profession registry (GetProfessionInfo); the window scan no longer overwrites it with the open line's rank, which would have flipped the record and re-minted the leaf whenever the two differed. Location:Scanner.lua,Modules/HashManager.lua,Compat.lua,Tests/retaillines_spec.lua.
Bug Fixes
Retail: recipe names had no quality colour, and "Can learn now" could hide every recipe (operator, in game 2026-10-04). Newer Retail recipes ship from ProfessionDB with only their plans'
itemIdand nocraftedItemId(1229652 Blood Knight's Mercy), so the row had no crafted item to colour by;GetRecipeCraftedItemIdnow falls back to the client'sC_TradeSkillUI.GetRecipeSchematic(id, false).outputItemID, cached per recipe (ProfessionDB asked for the field).ItemLink.QualityHexalso reads Retail's|cnIQ<n>:link colour and a quality string with or without its|c. And with no profession window open, Retail'sGetProfessionInfoBySkillLineIDanswers skill 0 for a line the character has (/runin game printed 0), which "Can learn now" read as a rank;GetRecipeSkillRanknow treats 0 as unknown and falls back to the rank recorded at the last scan. Location:TOGProfessionMaster.lua,GUI/SharedWidgets.lua,Compat.lua,Tests/compat_spec.lua.Retail: a scan of one expansion could strip the others' recipes. The merge removed the character from EVERY recipe of the profession before re-adding what the scan listed, and whether Retail's recipe list covers every expansion line or only the open one is not verified. A Retail scan now removes the character only from recipes on the lines it actually covered (the recipe DB's
skillLine, elseGetTradeSkillLineForRecipe); an unlearned recipe on a scanned line is still removed. A re-scan listing a recipe whose line cannot be placed no longer reports a change and redraws every time. Classic and Forever are unchanged. Location:Scanner.lua,Tests/retaillines_spec.lua.WoW Forever: a skill value ProfessionDB borrowed from Vanilla anchored the orange skill-up tier (LibProfessionDB MINOR 13 notice, thread 9ac3ba2a). The tiers are unanchored when
difficulty[1] == 1andrequiredSkillis absent OR borrowed (IsBorrowed(id, "requiredSkill")), feature-detected so older ProfessionDB builds behave as before. The recipe id now reaches everyFormatSkillTierscall. The Missing Recipes skill sort used the raw first tier, so placeholder tiers sorted as skill 1; it now uses the same learn-skill rule as the "Can learn now" gate. Location:TOGProfessionMaster.lua,Modules/Crafting/CraftingEngine.lua,GUI/CraftingTab.lua,GUI/MissingRecipesTab.lua,GUI/SharedWidgets.lua,Tests/borrowed_spec.lua.Missing Recipes said "Unknown" for recipes ItemDB knows where to get (operator, 2026-10-04: "itemdb has a lot of the drop info, and you're showing it as unkown"). The Sources cell read only ProfessionDB's sources, and Retail ships none, so a row whose [Where] listed a quest and a vendor said "Unknown". When ProfessionDB has no sources for a recipe, the cell now names the kinds of place LibItemDB knows for its scroll -- the same rows [Where] opens, with the same faction filter -- mapped to the usual labels (Drop and Boss as Drop, Vendor, Quest, Crafted, Object as Container), and the Sources sort uses the same text. Worked out once per row. Location:
GUI/MissingRecipesTab.lua,Tests/missingrecipes_spec.lua.
Improvements
- Borrowed values are shown and marked unconfirmed (operator's direction via
ProfessionDB). The Requires line reads "(85, unconfirmed)", and the recipe
Sources heading and Missing Recipes source text say "unconfirmed" when
ProfessionDB borrowed them. A recipe hidden only by a borrowed "never
implemented" flag is no longer hidden. Location:
GUI/SharedWidgets.lua,GUI/MissingRecipesTab.lua,Modules/RecipeGate.lua,Locale/enUS.lua,Tests/recipegate_spec.lua. - A recipe shown only on a borrowed "never implemented" flag says so. Its
Professions and Missing Recipes rows and the detail panel header read
"Name (unconfirmed)", and its tooltip adds "Unconfirmed: this recipe may not
exist in this version of the game." -- outside the recipe-detail block, so the
"never" setting cannot hide it.
RecipeGate:IsUnconfirmedand the gate read the flag through one helper, so a recipe the gate let through on a borrowed flag is always the one marked. Location:Modules/RecipeGate.lua,GUI/BrowserTab.lua,GUI/MissingRecipesTab.lua,GUI/SharedWidgets.lua,Locale/enUS.lua,Tests/borrowed_spec.lua,Tests/browserdetail_spec.lua. - The Professions and Guild tabs build their lists with less work. Three
openperf_specbudgets failed in the 2026-10-04 run (the All-professions list build 349 ms, the Browser cold draw up to 526 ms, both against 250 ms). Profiled, not guessed: the guild-tag hash ran 541 times per build and is now cached per guild key (registration still runs every call); each crafter's display name and online state is resolved once per build instead of once per recipe; the crafter sorts no longer build a comparator per recipe; the search text is onetable.concat; and the Guild tab's counts ask the guild-scope check once per character instead of once per recipe-crafter pair (~37k calls for ~700 characters). Same output, specs unchanged. Location:TOGProfessionMaster.lua,GUI/BrowserTab.lua,GUI/GuildTab.lua. - The perf budgets measure TOGPM, not the machine. On Windows
os.clockis wall time (Microsoft's CRTclock()"doesn't strictly conform to ISO C"), so one sample includes whatever else the box is doing: across 2026-10-04's runs the same Profit Planner draw read 27 to 1089 ms.Tests/wowapimoves from515873bto99111aa(WoWAPITesting953768d:bit5-12x faster, andwow.bestTime, inbox 0822420a). Every repeatable budget now gates on the fastest of five runs;RebuildOnFirstLoad, which works only on its first call, is timed five times on a fresh copy of the tables it writes. The other login stages mutate the database and still run once. The wire budget times every crafters leaf once and re-times only the worst five times: five passes over every leaf ran the file past the runner's 60 s limit under load. Even best of five cannot beat a machine pinned for the whole run (the Professions draw read 402 ms at 100% CPU, 72 ms on a quieter run), so the budgets moved out of the default suite:Tests/openperf_spec.luais nowTests/perf/openperf.lua, outside the runner's*_spec.luapattern, and.writ-suites.jsondeclares it as its own suite to run on purpose (WoWAPITesting's answer, inbox d53bb661). The same file suppresses.busted, which must never be run directly. Location:Tests/openperf_spec.lua,Tests/wowapi.
[v1.2.0] (2026-10-01) - Every window and list moves to LibAceGUIWidgets; WoW Forever: the Crafting tab works, and Forever characters' recipes sync
New Features
- [Where] on Missing Recipes rows: where to buy or farm the pattern
(Discord request 2026-09-29, "add integration with questbook to lead you to
where patterns are sold/dropped"). A [Where] button on each row whose
recipe scroll LibItemDB knows a source for opens ItemDB's "Where to get it"
window on that scroll: every vendor, drop and zone, with coordinates. With
Questbook installed, clicking a place there guides the player to it -- ItemDB
hands the waypoint to Questbook (
LibItemDB:WhereTrack), so TOGPM never talks to Questbook and works the same without it. No button for a trainer-taught recipe (no scroll), for a scroll with no known source (WoW Forever's ItemDB ships no places data), or with a LibItemDB older than the window. Whether a row has sources is asked once per row, not on every paint. The Professions tab has no [Where] yet: its rows are recipes someone already knows. Not yet tried in game. Location:GUI/MissingRecipesTab.lua,Locale/enUS.lua,Tests/missingrecipes_spec.lua. - [Guide] on the Missing Recipes tab and a stop icon on the window: start
and cancel Questbook's route from TOGPM (operator, 2026-10-01: "give us the guide
route/cancel route inside TOGPM too"). With Questbook installed, a [Guide]
button on each row hands the best place for the recipe scroll straight to
Questbook -- one in the player's current zone first, then a vendor, then the
highest drop chance -- and says in chat where it is guiding to; [Where] still
opens the full list to pick another. The window's bottom row carries
Questbook's own stop control beside the gear, on every tab (operator: "use
the same button/function from questbook"): its red X
(
ReadyCheck-NotReady), dimmed to 0.4 while Questbook guides to nothing (Questbook's publicIsTracking, re-checked four times a second while the window is open, since guiding can start from Questbook itself), with its tooltip, and Questbook's universal stop. A first cut put a "Stop Guide" button on the Missing Recipes toolbar, where it wrapped onto a row of its own and existed on that one tab (in game, 2026-10-01); removed. The route and the stop go through LibItemDB (WhereTrack/WhereStopTracking); TOGPM only checks that Questbook is installed, with the same test LibItemDB makes, and readsIsTrackingfor the dim. Without Questbook neither control is drawn: the route is Questbook's, and ItemDB's data alone gives the places and coordinates but no arrow. Questbook is added to## OptionalDepsin all six TOCs. The route was confirmed working in game by the operator (2026-10-01); the stop icon is not yet tried there. Location:GUI/MissingRecipesTab.lua,GUI/MainWindow.lua,Locale/enUS.lua, all six TOCs,.luacheckrc,.luarc.json,Tests/missingrecipes_spec.lua,Tests/toolbar_spec.lua.
Bug Fixes
- The Crafting tab's Enchant button stayed greyed out and could not be
clicked, even with the materials in the bags (Discord, 2026-09-29, Classic
Hardcore, v1.1.2). Since v1.1.2 the secure Craft/Enchant button lives on
its own holder outside the window, placed over the detail panel at the
panel's frame level + 10. The window is Toplevel
(
AceGUIContainer-Frame.lua:194), so any click on it Raises it within FULLSCREEN_DIALOG -- above the button, which is not its descendant. The watcher that keeps the button over the panel compared only position and scale, so it never noticed: the button sat under the panel's backdrop (dimmed) and the panel took its clicks. The watcher's key now includes the panel's frame level, and the button is stacked 100 levels above the panel (the Cooldowns popup's margin) so it also clears the panel's own children. It affected the Craft button for every profession, not only Enchanting. Spec: the button stays above the panel after it is raised. Not yet tried in game. Location:GUI/CraftingTab.lua,Tests/craftingtab_draw_spec.lua. - The Cooldowns tab's [Bank] button raised "attempt to index local
'anchorBelow' (a number value)" (in game, 2026-09-30). Its click passed
select(2, addon.Item.GetInfo(id))as the LAST argument toaddon.Bank.ShowRequestDialog, so every return after the link spilled into the dialog's fourth parameter,anchorBelow, and the dialog indexed a number. The group popup's [Bank] carried the same call. Both now wrap theselectin parentheses, which keeps it to one value, and the dialog anchors only to a frame, so a stray value can no longer stop it opening. Spec: the popup's [Bank] hands the dialog exactly three arguments. Location:GUI/CooldownsTab.lua,Compat.lua,Tests/cooldowndraw_spec.lua. - WoW Forever: clicking the Crafting tab raised ADDON_ACTION_BLOCKED for
CastSpellByName()(in-game report, 2026-09-28), and the tab could not read or craft anything there.Engine:OpenProfessionopened a profession by casting it; the report's stack is the tab click, a hardware event, so Forever blocks that cast from addon code even on a click, where Classic Era allows it. And every read and craft went through the classic trade-skill globals (GetTradeSkillLine/GetNumTradeSkills/GetTradeSkillInfo/DoTradeSkill), which Forever does not have at all -- not in its source tree, not even as deprecation fallbacks. The engine now has a second path onC_TradeSkillUI, the API Forever's own Professions UI uses, taken only where the classic globals are absent (addon:HasModernTradeSkillAPI()):- Open:
C_TradeSkillUI.OpenTradeSkill(skillLineID)on the profession's skill line, from the tab click, the dropdown and a Profit Planner jump. warcraft.wiki.gg marks it restricted with the hwevent tag only, so a click may call it. No Open button on Forever (operator, 2026-09-29: "you should not have added the button"). UNRESOLVED: a tab click still raised ADDON_ACTION_BLOCKED forOpenTradeSkill()on some clicks only (in game, 2026-09-29). A secure button and a "use your profession book" prompt were both tried and removed the same day. Every attempt is now recorded (Engine:_LogOpenAttempt: trigger, skill line, the open base and child profession, first call of the session, calls in the same frame, combat, mouse button, whether Blizzard_Professions is loaded, the result, a stack) and paired with anyADDON_ACTION_BLOCKEDorADDON_ACTION_FORBIDDEN, which is announced in chat./togpm opendebugprints the record. Peer Review's leading hypothesis (thread 4d8158c1): Blizzard's own callers skip OpenTradeSkill when that profession is already open, and the tab does not. - Switching professions from the dropdown drew the old one (in game,
2026-09-29: the K window "pops up briefly, a flicker, then closes, and it
doesn't change to the tradeskill i select"). A switch while a profession is
open lands on
TRADE_SKILL_LIST_UPDATEonce the new data source is built, not onTRADE_SKILL_SHOW-- which is where Blizzard's own window switches, yielding whileIsDataSourceChanging()(Blizzard_ProfessionsFrame.lua:138-161). The engine treated that update as a light refresh, so the tab kept the old profession. It now redraws the whole tab when the update shows a profession other than the one last drawn (Engine._drawnProf), and also listens forTRADE_SKILL_DATA_SOURCE_CHANGED. Every event is logged for/togpm opendebug. - The K key could not open Blizzard's window after the tab had opened a
profession (in game, 2026-09-29). The tab's claim on the session hid that
window on every show. On Forever a show of
ProfessionsFramethat does not follow the tab's own open withinOWN_OPEN_WINDOW(2 seconds: a margin I chose, not measured; the event log records the real gap) is now the player's choice: the window stays and the claim is dropped, as the WoW UI button does. Classic's frames keep the old rule.Tests/pettraining_spec.luanow loads the realCompat.lua, which the engine asks on every event. - The dropdown always landed on the last profession tab (in game,
2026-09-29; the debug log showed every pick ending on Fishing, "Bait and
Tackle"). When
ProfessionsFramegoes from hidden to shown, each of its profession tabs that is not the open profession casts its own profession spell (Blizzard_ProfessionsTemplates.lua:963-971), each cast opening that profession, so the last tab wins. The takeover HID the frame, so every open from the tab re-showed it and set this off. On Forever the frame is now CLOAKED instead -- alpha 0 with mouse input off on it and every frame inside it, restored on reveal -- and stays shown, so a switch does not re-fire it. Its size is never touched: a first build also scaled it to 1%, and the revealed window came up far narrower than Blizzard's normal one with its Create bar off the edge (in game, compared against an untouched window), and a relayout call to correct that made it worse. The first open, which has to show it, asks for the profession a second time once the cascade is over. Operator's conditions (2026-09-29): K must show Blizzard's window -- theTOGGLEPROFESSIONBOOKkeys are override-bound to reveal it while cloaked, sinceToggleProfessionsBookwould toggle the shown frame closed -- and both windows may be up at once: revealing it (K, or the WoW UI button) leaves TOGPM open. Closing TOGPM while cloaked closes Blizzard's window through its ownHideUIPanel, ending the session. Not verified in game: all of it, including whether the second ask goes through inside the same click, and what Escape does with a cloaked frame. - Read: the open profession from
GetChildProfessionInfo, falling back toGetBaseProfessionInfowhen the child id is 0, asBlizzard_Professions.lua:1665does; learned recipes fromGetAllRecipeIDs(falling back toGetFilteredRecipeIDs) plusGetRecipeInfo, grouped underGetCategoryInfonames; difficulty fromrelativeDifficulty; reagents fromGetRecipeSchematic's basic reagent slots with bag counts. - Craft:
C_TradeSkillUI.CraftRecipe(recipeID, count), asBlizzard_ProfessionsTransaction.lua:352does. The row index on this path is the recipe id. - Other windows: Forever's
ProfessionsFramejoinsTradeSkillFrame/CraftFramein the foreign-window suppression and the TOGPM toggle button. Classic Era, TBC, Wrath, Cata and MoP have the classic globals and never take this path. Not verified in game: the cause of the intermittent block, whether Forever hasGetAllRecipeIDs, and whether hidingProfessionsFramekeeps the session open. Location:Modules/Crafting/CraftingEngine.lua,Compat.lua,GUI/CraftingTab.lua.
- Open:
- WoW Forever: a character's own recipes were never recorded or synced to the
guild (known gap since v1.1.2).
Scanner:OnTradeSkillEventreturned early without the classic API. NewScanner:ScanModernTradeSkillIntorecords the learned recipe ids (spell ids, the keyaddon.recipeDBuses) through the sameMergeRecipesIntoGdb, skill rank and cap included. It skips a linked or NPC-crafting session, and it never merges an empty list, which would strip every recipe from the character's crafter set while the data is still loading. Location:Scanner.lua,Compat.lua. - WoW Forever: switching professions stored one profession's recipes under
the others. In game (2026-09-29, debug log) every profession switched to
scanned "16 recipes", the Blacksmithing count, and the result was broadcast:
the list the client handed back did not follow the open profession.
Scanner:ScanModernTradeSkillIntonow skips whileC_TradeSkillUI.IsDataSourceChanging()(asBlizzard_ProfessionsFrame.lua:142 does) and keeps only recipes thatGetTradeSkillLineForRecipeplaces in the open base profession (ProfessionsUtil.lua:79-82); a recipe it cannot place is kept, as before. A character's wrongly stored recipes are replaced the next time each profession is opened and scanned, since a scan rewrites that character's whole set for the profession. Specs inTests/scanner_scan_spec.lua. Location:Scanner.lua. - New specs in
Tests/craftsuppress_spec.luafor the Forever path: never opens throughOpenTradeSkilland never casts, drops the claim when nothing opens, records each attempt (including one that raises), the list, the reagents, the craft call, and the classic path winning wherever both exist. The spec now loads the realCompat.luarather than a copy of its check.C_TradeSkillUIis added to.luacheckrcand.luarc.json. The newCraftUnsupportedClientnote (English only) is now shown only on a client with neither trade-skill API. The Scanner's Forever scan has no spec yet. Location:Tests/craftsuppress_spec.lua,Locale/enUS.lua. - Re-ticking every skill tier on the Professions tab never went back to
"all tiers". The toggle ended
self._selectedTiers = all and nil or sel; in Luatrue and nilis nil, so the expression always yieldssel, and a player who unticked a tier and ticked it again was left on an explicit set that no longer grew with the client's tiers and persisted that way. Now anif. The same idiom inModules/CommTest.luastored the string "nil" as the send error on every successful probe (read only on failure, so never shown); fixed the same way. Found by the newTests/toolbar_spec.lua. Location:GUI/BrowserTab.lua,Modules/CommTest.lua. - The Professions tab's crafter column re-joined the whole name list for
every name it tried.
fitCrafterTextnow extends the string one name at a time. The cold draw on the reporter-scale database had gone from ~140 ms to 676 ms against a 250 ms budget; it is 197 ms again. Location:GUI/BrowserTab.lua.
Improvements
- The spell helpers now use the namespaced API first on every client.
addon.Spell.GetInfo/GetTexture/GetLinktried the bare global first, for a test-only reason: the offline harness had noC_Spell. WoWAPITesting delivered it (a7675ce, with a WoW Forever build), andC_Spell.GetSpellInfo/GetSpellTexture/GetSpellLink/GetSpellCooldownare documented on Classic Era and Classic (Cata/MoP) as well as Forever, so every client now takes the same branch; the bare name is the fallback.GetInfo's second return (rank) is nil on this path; no caller reads it.addon.Spell.IsKnownnow asksC_SpellBook.IsSpellInSpellBookfirst too, and its comment was wrong: the bareIsSpellKnownis a deprecation fallback on Classic Era as well, not only on Forever (Deprecated_SpellBook.lua:16in the classic_era tree). The specs feed spells through the harness'swow.spells/wow.knownSpellsinstead of stubbing bare globals, andTests/compat_spec.luadrops its hand-builtC_Spell/C_SpellBookfor the harness's, with a Forever case.Tests/wowapimoved to 0e90e7d. Location:Compat.lua,Tests/compat_spec.lua,Tests/scanner_scan_spec.lua,Tests/scanner_cooldowns_spec.lua,Tests/scanner_names_spec.lua,Tests/recipedetails_spec.lua. - The Professions tab's recipe list and shopping list are LibAceGUIWidgets
RowLists (LAGW adoption step 5b). The last two hand-built row pools in the
addon. The recipe list was 35 raw frames over an AceGUI ScrollFrame, with its
own header bar, index arithmetic and a container
LayoutFinishedhook that anchored the scroll to that header bar and the detail panel -- the hook behind v1.0.6's "the list is drawn over the game world after opening Settings". It is now a RowList on a host the tab owns for the session (addon.GUI.ParkList), anchored inside its own group only, with the header, hover highlight, selection tint (matched by recipe id, so it survives a guild-data rewarm), scroll memory (ListScrollkeybrowser) and a [Bank] button column. The crafter column is fitted with the list's own cell width and measure (fitCrafterText, now pure), so theWINDOW_RESIZEDrepaint nudge is gone. The shopping list is a second RowList: a +/- expander per recipe with reagents, reagent rows indented under it, [Bank] / [AH] / "!" / [-] qty [+] / <input disabled="disabled" type="checkbox" checked="checked" /> as button columns, capped at its share of the tab in whole rows and scrolling inside. The tab lays out with Flow so the list and detail panel take the rest of its height. Removed:BuildPool,DestroyPool,UpdateVirtualRows,EnsureHeaderBar,EnsureShoppingListScroll,DetachShoppingListPooland the shopping list'sOnReleasecallback on the pooled InlineGroup. The row tooltip is unchanged, nowBrowserTab:ShowRowTooltip. Not yet tried in game. Location:GUI/BrowserTab.lua,Tests/browservirtual_spec.lua,Tests/browserdetail_spec.lua,Tests/mainwindow_spec.lua,Tests/tooltipwrapflag_spec.lua. - The [Bank] request dialog is a LibAceGUIWidgets form dialog (LAGW adoption
step 6, first part).
addon.Bank.ShowRequestDialogbuilt its own frame, backdrop, close button, UIDropDownMenu banker picker and fixed heights (165, or 205 with the shop line). It is nowW:CreateFormDialog(MINOR 36, TOGPM contract 6cf3b4e4): an item row, the library's dropdown box for the banker, a digits-only quantity; the stock line is the hint, "/ max N" a note under the quantity, TOGBank's shop line the body, and the dialog sizes itself to what shows. It opens where it did (below a given anchor, else beside the main window, else centred) through the dialog's ownSetAnchor. The allowance, view-only, shop-order and Send logic is unchanged, and so are the field names the rest of the file reads. Not yet tried in game. Location:Compat.lua,Tests/compat_spec.lua. - Every tab's toolbar uses LibAceGUIWidgets controls, and the window's
"don't redraw under the player's hands" check is the library's (LAGW
adoption step 6). The Professions, Cooldowns, Missing Recipes, Profit
Planner and Crafting toolbars had 14 AceGUI Dropdowns, 4 AceGUI EditBoxes
styled as search fields and 6 AceGUI CheckBoxes. They are now the library's
dropdown box (single choice, or a tick-box menu that stays open with Select
All / Clear All rows that act and close it),
LAGW-SearchBoxandCreateCheckbox, through three shared helpers inGUI/SharedWidgets.lua(ToolbarDropdown,ToolbarSearch,ToolbarCheckbox, plusMenuItems). The raw dropdown and check-box frames are built once per tab and parked in each draw's slot, so the redraw every guild sync causes creates none. A labelled control and its unlabelled neighbours share one height, so a row of them lines up.MainWindow:Refreshnow asksW:IsMenuOpenFor(and redraws once, throughW:OnMenuClosed, when the menu closes) andW:IsInputFocusedIninstead of walking AceGUI's globalAceGUI30Pullout<N>frames -- which every AceGUI addon shares, and which once held this window's redraws forever on another addon's leaked pullout. Removed:addon.GUI.IsAnyDropdownPulloutOpen,OffsetInputLabel,StyleSearchBox,IsAnySearchFocused. Not yet tried in game. Location:GUI/SharedWidgets.lua,GUI/MainWindow.lua,GUI/BrowserTab.lua,GUI/CooldownsTab.lua,GUI/MissingRecipesTab.lua,GUI/AHProfitTab.lua,GUI/CraftingTab.lua. - The hand-rolled list and pool helpers are gone (LAGW adoption step 7).
With every list a library RowList and every toolbar control the library's,
nothing called them any more. Removed from
GUI/SharedWidgets.lua:addon.GUI.DetachPool,PersistentScroll(Acquire/Restore/Resetand its LayoutFinished repair),RowStripe/ApplyRowStripe, the wholeaddon.GUI.Sorttable (SetIndicator,Indicator,Next,NextOrNone,ConfigureHeaderIcon,ConfigureCenteredHeaderIcon),LiftAboveSizers,MakeHeaderHoverGlow, andMakeColumnHeader'sjustifyH/onClick/hoverGlowoptions (its only caller, the Guild tab, passes none of them). The newTests/sharedwidgets_spec.lua(16 cases) covers the paths nothing else drove, bringingGUI/SharedWidgets.luato 100% line coverage (661/661). The Cooldowns group popup, the lastDetachPoolcaller, is parented to UIParent for its whole life, so its release now just hides it and its click-outside overlay.addon.GUI.ListScrollkeeps its store in the samedb.char.frames.scrollTabstable. The specs for the removed helpers (gui_pool_spec,gui_scroll_spec,bottomrow_spec) are rewritten or dropped;gui_scroll_specnow specsListScroll. Location:GUI/SharedWidgets.lua,GUI/CooldownsTab.lua. - The last hand-built row lists are library RowLists (finishing the
LibAceGUIWidgets adoption). Three panels still drew their rows from
their own frame pools, and each is now a RowList, like every other list in
the addon:
- Professions tab, recipe details: the panel's own ScrollFrame, slider, reagent-row pool and crafter-row pool are gone. Reagents and Known By are one list under the header and shopping controls, with the two headings as the library's group-heading rows. The list scrolls itself. Reagent counts read the shopping-list quantity when drawn, so +/- restates them with a refresh. The crafter right-click whisper goes through the list's click handler.
- Crafting tab, reagents: the fixed pool of 12 reagent rows and the
hand-placed "Reagents" / "Cost" labels are replaced by a list whose own
header carries both headings and their tooltips. The list sizes itself to
its rows (
fitContent), and the panel's auto-height reads that. The header bar moves the first reagent row down 2 px. - Cooldowns, the group popup: its pooled rows (name, reagent, [AH],
[Bank], mail) are a list whose columns switch with
SetColumnsbetween a transmute group and a plain one. The reagent's white/grey is now a colour code in the cell rather than a font colour. The popup moves from TOOLTIP strata to the main window's FULLSCREEN_DIALOG, with a frame level 100 above the window's set on every open, so tooltips draw above it without the old frame-level bump. (A first cut set the strata alone and the popup opened behind the window, in game 2026-10-01.) The Crafting tab's "Missing Materials" label sits inside the reagent list's header bar, right-aligned and centred on it (in game it floated above the bar and ran past its end). None of it has been tried in game. New specs: the Crafting reagent list (7 cases incraftingtab_draw_spec), the popup's colours and mail icon, and the Known By whisper. Fourbrowserdetail_specand threecooldowndraw_speccases that read the old frames now read the list's rows and cells instead (test changes approved by the operator 2026-09-30). Location:GUI/BrowserTab.lua,GUI/CraftingTab.lua,GUI/CooldownsTab.lua,Tests/browserdetail_spec.lua,Tests/craftingtab_draw_spec.lua,Tests/cooldowndraw_spec.lua.
- The [Bank] dialog's item keeps hold-to-compare and the chat-link click.
Moving the dialog onto the library's form dialog lost both: the library's
item row drew its own tooltip and click. LibAceGUIWidgets MINOR 39 (TOGPM
contract 1d7a76ba) lets the row take the consumer's
onEnter/onLeave/onClick, and the dialog now passes TOGPM's ownItemLink.SetItem/EndHover/Click. Against an older library the fields are ignored and the built-in hover and click stay. Not yet tried in game. Location:Compat.lua,Tests/compat_spec.lua. - A spec corrupted AceGUI's shared widget pool for every file after it.
settingsbleed_spec's "never re-anchors" case released the Professions list section while it was still listed in its TabGroup's children, so the window's later close released that pooled widget a second time. A TabGroup went back to the pool with a nil child, and every later spec file's Professions draw died inReleaseChildren-- the seventoolbar_specfailures that appeared only in a full run. The case now takes the section out of the list, as AceGUI's ownReleaseChildrendoes. No production code releases a child on its own (onlyMainWindowreleases, and only the root window).toolbar_specnow names the error when the Professions draw fails, instead of failing on a missing toolbar. Location:Tests/settingsbleed_spec.lua,Tests/toolbar_spec.lua. - The offline suite no longer keeps every reload of every tab alive
(WoWAPITesting thread 83f92459). Its heap climbed to 377-720 MB and ~85%
of a 321 s median run was garbage collection. The new
Tests/reloadleak_spec.luareloads each tab and asserts the old copy is collected, printing the reference chain when it is not; five of six were held. Two holders, both in the test env: containersenv.drawTabdrew were never released, so AceGUI's_G-named frames kept the tabs' callbacks; and each module reload re-registered on the addon's, AceEvent's and LibGuildRoster's callback registries under its own (new) owner key. The env now releases drawn containers at each install (collecting every failure and raising them together at the end), re-shows a pooled container before drawing into it, and drops a replaced module's registrations. Full suite 1676/0 in 219 s; the heap still ended near 334 MB. A second pass extended the spec to every module and found the third holder: Scanner, CraftingEngine, MainWindow and MinimapButtonhooksecurefuncthe addon'sOnEnable(Scanner alsoOnPlayerEnteringWorld) at load -- once per session in game -- and each spec file's reload wrapped the previous wrapper, keeping every older copy alive down the chain. Fixed first with a layer in the env, then moved into the harness on TOGPM's request (pin 515873b: a hook re-made from the same source line after a reset replaces the earlier one) and the env layer deleted; the leak spec now resets between its two loads, as between two spec files. 16 module cases added, all collected. Two more found by the full run: AHScanner's LibItemDB callback (the env's reload cleanup now covers LibItemDB's registry, and the leak spec loads the real LibItemDB), and a reloadedGUI/MinimapButton.luagot nil fromLDB:NewDataObjectfor its own, already-held name, so the nextOnEnablehanded LibDBIcon nothing; the file now reuses the existing object (in game it loads once, so nothing a player sees changes). Full suite 1700/0. A spec use of a container after the env released it is NOT guarded: AceGUI's pool is private to the library, so a released widget cannot be marked without breaking its next owner.tooltipwrapflag_spec's floor comments now list each file's remaining tooltip calls. Harness pin 0e90e7d -> 515873b.docs/AUDIT.mdandTests/HARNESS_CONTRACT.mdare deleted; findings and requests travel through writ's inbox, and git history keeps both boards. Location:Tests/env_togpm.lua,Tests/reloadleak_spec.lua,Tests/tooltipwrapflag_spec.lua,CLAUDE.md. - The offline suite runs one full garbage collection per spec file, not per
example (WoWAPITesting thread 48754b3d). Its telemetry put the suite at a
251 s median, the slowest in the fleet, with 89% of example time inside the
reset's full collections. The harness reset already ran exactly one per
example, after the env's own release, so its opt-in deferral alone would
save nothing; the env now turns it on (
wow.deferCollection(true)) and callswow.collect()the first time each spec file installs. Known cost, the harness's: inside one spec file, a frame an earlier example discarded is still alive and still hears events until the next file.reloadleak_speccollects for itself and is unaffected. Full suite 1712/0; the whole background run, agent overhead included, took 64 s against 206-331 s for the day's earlier runs. Location:Tests/env_togpm.lua.
[v1.1.2] (2026-09-27) - WoW Forever support, Craft All stops tripping a blocked action, the Craft button no longer protects the whole window, and LibDBIcon becomes a dependency
Bug Fixes
The shopping-list "Ready to craft" alert fired only for cooldown crafts, and it rewrote the player's crafter-online choices.
CheckAlertsread the reagent fromaddon:GetCooldownData(), so a normal recipe on the list never alerted, although every entry the Professions tab adds carries the recipe's full reagent list (the one the Reagent Tracker sums). It also latched intodb.char.shoppingAlerts, which is the per-recipe opt-in for the crafter-online alert (the "!" on a shopping-list row, read byOnCrafterCameOnline): a ready alert, or logging in with the reagents already in the bags, switched that recipe's crafter alert on, and running out switched a real opt-in off. The alert now checks every reagent on the entry (falling back to the cooldown catalogue for an entry with none), and its latch is a module-local table re-armed at login. A multi-reagent recipe prints the newAlertReadyAllFormat(English only; other clients fall back to it). New specs inTests/reagentwatch_spec.lua; the opt-in spec was not run against the old code. Location:Modules/ReagentWatch.lua,Locale/enUS.lua.WoW Forever: opening a profession would have scanned into a nil call. Forever reaches profession data through
C_TradeSkillUI, and whether it keepsGetTradeSkillLine/GetNumTradeSkills/GetTradeSkillInfois not verified. Newaddon:HasClassicTradeSkillAPI()checks for all three at call time; the Scanner's and the Crafting engine's trade-skill handlers return early without them, so on Forever the Crafting tab shows its "open a profession" prompt instead of erroring. Known gap: no Forever recipe scan is built, so Forever characters contribute no recipes yet (the cooldown scan is separate and runs). Location:Compat.lua,Scanner.lua,Modules/Crafting/CraftingEngine.lua.The Crafting quantity box overwrote what the player was typing on every bag update.
RefreshDetailset the stepper's value on every refresh; it now sets it only when the quantity actually changed and otherwise uses the library's non-forcingRefresh. Location:GUI/CraftingTab.lua.A right-click on a Crafting queue row's <input disabled="disabled" type="checkbox" checked="checked" /> removed the entry. The RowList button column fires on any button; the remove handler now acts only on a left click, as the hand-built queue did. Location:
GUI/CraftingTab.lua.The addon's name in the game's AddOns list used a broken grey colour code in every TOC. All six titles now read
|cffFF8000TOG Profession Master|r |cff999999<version>|r(brand orange, grey version tag), and the Cataclysm TOC's tag is spelled out (DeltaSync's fleet request). Not checked in the AddOns list. Location: all six TOCs.WoW Forever: an error at login, and the scanner never finished starting (in-game report).
Scanner:InitregisteredTRADE_SKILL_UPDATE, which Forever does not have (itsTradeSkillUIDocumentation.luadeclaresTRADE_SKILL_LIST_UPDATEinstead).RegisterEventraises on an unknown name, so everything after it in Init -- the login scans, the broadcast timers, the trainer and learned-recipe events -- was never set up. The events a client may lack (TRADE_SKILL_UPDATE,TRADE_SKILL_LIST_UPDATE,CRAFT_SHOW,CRAFT_UPDATE,NEW_RECIPE_LEARNED) now go through onetryRegisterthat skips an unknown name, andTRADE_SKILL_LIST_UPDATEis registered where it exists. Every other event Init registers is declared in Forever's API docs.CraftingEngine:Inithad the same fault on its own event frame (TRADE_SKILL_UPDATE, and theCRAFT_*trio, none in Forever's docs) and now registers them the same way;TRADE_SKILL_LIST_UPDATEis handled as an update there too. Every other event the addon registers by name is declared in Forever's API docs. Not verified in a Forever client; whether the classic-style profession scan itself works on Forever is also not verified. Location:Scanner.lua,Modules/Crafting/CraftingEngine.lua.WoW Forever: "bad argument #2 to HookScript" at every login (in-game report). Forever's tooltips have no
OnTooltipSetItemscript (the modern engine replaced it withTooltipDataProcessor, which the same function already registers), andHookScriptraises on a script a frame lacks. The tooltip hooks now go on only whereHasScriptsays the frame has them.Tests/tooltiphooks_spec.lua's fake tooltip now raises exactly as a real frame does and a new spec covers a tooltip with noOnTooltipSetItem. Location:Tooltip.lua.WoW Forever: hovering a quest reward raised "attempt to call a nil value" from the tooltip (in-game report). Forever's
GetItemcomes fromGameTooltipDataMixin, which not every tooltip handed to theTooltipDataProcessorpost-call carries. All four link reads now go throughTooltipLink, which usesGetItemwhere it exists andTooltipUtil.GetDisplayedItem(that mixin's own implementation) otherwise.tooltip_specandtooltiphooks_specpass (33/0); not verified in a Forever client. Location:Tooltip.lua.WoW Forever: the cooldown scan failed at every login with "attempt to call a nil value" (in-game report). Forever has no bare
GetSpellCooldown(11_0_0_SpellBookAPITransitionGuide.lua:53maps it toC_Spell.GetSpellCooldown, which answers one table), and itsIsSpellKnownis only an off-by-default deprecation fallback. Newaddon.Spell.GetCooldownanswers the classicstart, duration, enabled, modRatefrom either API, andaddon.Spell.IsKnownfalls back toC_SpellBook.IsSpellInSpellBookexactly as Forever's own fallback does. All seven call sites use them (the cooldown scan, specialization detection,/togpm transmutedebug). Specs inTests/compat_spec.lua. Location:Compat.lua,Scanner.lua,TOGProfessionMaster.lua.WoW Forever: the Guild tab failed to open with "attempt to call a nil value" (in-game report). Forever has no bare
GetSpellInfoeither, and its transition guide also drops the bareGetSpellTextureandGetSpellLink. Newaddon.Spell.GetInfo/GetTexture/GetLinkuse the bare function where it exists (the real one on the classic clients) and otherwiseC_Spell's, withGetInfounpacking itsSpellInfotable into the classicname, rank, icon, castTime, minRange, maxRange, spellIDlist. Every call in the addon now goes through them -- the Guild, Cooldowns, Missing Recipes, Crafting, Profit Planner, Professions and shopping-list code, the scanner, the recipe gate and reagent alerts -- so no other tab hits the same error. Specs inTests/compat_spec.lua. After these five Forever fixes the operator reported no further errors in a Forever client (2026-09-27); nothing beyond logging in and opening the tabs has been exercised there. Known gap:GetSpellBookItemInfo/GetNumSpellTabsare also gone on Forever; the spellbook name cache is guarded and simply stays empty there.A recipe learned from a pattern stayed on the Missing Recipes tab until the profession window was opened (player report). Only the trade-skill and craft scans write the character's own crafter set, and they run only while that window is open. A pattern, plan or formula read from the bag learned the recipe with the window closed, so nothing recorded it. Two new listeners now do:
NEW_RECIPE_LEARNED(recipeID), which Classic Era's API docs declare but which is not confirmed to fire for profession recipes, and theERR_LEARN_RECIPE_Ssystem message ("You have learned how to create a new item: %s."). The message is matched against the client's own localised string and the name is looked up only in the character's own professions. Both go through the newScanner:RecordLearnedRecipe, which adds the one recipe, stamps the scan time, invalidates the profession hash, refreshes the tabs and schedules the broadcast. A spell-known check was ruled out becauseIsSpellKnownanswers false for trade-skill spells on Classic Era (DATA-004). Fixed for recipes whose learn message names them; Enchanting formulas (the Craft window) are not verified, and neither is whether the message's name is the recipe's or the crafted item's. Peer Review (thread dd630106) found Blizzard's own Era UI registersNEW_RECIPE_LEARNEDonly for Engraving, so the chat message is the real path and the event is a bonus. Not yet seen in game. Location:Scanner.lua,Tests/scanner_learn_spec.lua.Craft All raised ADDON_ACTION_BLOCKED "DoTradeSkill()" as soon as the first recipe finished.
_OnCraftSuccesschainedCraftNextthrough aC_TimerfromUNIT_SPELLCAST_SUCCEEDED, butDoTradeSkillonly runs from a hardware event, so the client blocked the second recipe. Craft All now crafts the top eligible entry's full batch and ends the run. When the batch finishes and more is craftable, it printsCraftAllNextNeedsClicktelling the player to click again.CraftAllalso clears_craftAllwhen no batch started (nothing eligible, or the Enchanting hand-off). Before, a later unrelated batch would finish "inside" the run and print the prompt. The tooltip (CraftCraftAllDesc) says a click is needed per recipe. Location:Modules/Crafting/CraftQueue.lua,Locale/enUS.lua.Closing the window in combat raised ADDON_ACTION_BLOCKED "Frame:Hide()" (blamed on Ace3). The secure
TOGPMCraftButtonwas a child of the Crafting detail panel. A frame with a secure child is protected, and so is every ancestor, so the whole AceGUI window became protected. The pooled frame could also carry that protection to the next addon given it. The button now lives onTOGPMCraftButtonHolder, which is parented toUIParentand not anchored to the panel either. Peer Review (thread 008cd6d5) quoted the wiki: protection also carries to frames a secure frame is anchored to.RegisterStateDriver(holder, "visibility", "[combat] hide; show")hides it for combat. NewCraftingTab:SyncCraftButtonplaces it at the panel's screen position, strata and scale, out of combat only, and re-runs on panel show/hide, onPLAYER_REGEN_ENABLED, and from anOnUpdatewatcher that runs only while the button is wanted and follows window drags and scale changes. The stepper row (-, MAX, +) is anchored to the panel instead of the button. The button stays secure because Enchanting'sDoCraftis protected. Location:GUI/CraftingTab.lua.Sister-guild data pulls were refused for every legitimate member once a sister roster was held. The anti-spoof check called
IsInGuildScoped(name, guildKey), but the library's signature is(guildKey, name), so identity always came back false. The gate moved intoScanner:SisterPullGate(sender, baseline), which returns(consentOk, identityOk)and passes the arguments in the right order. Location:Scanner.lua.Placeholder skill tiers were shown and filtered on as real thresholds. ProfessionDB's rule: when
requiredSkillis absent anddifficulty[1] == 1, the tiers are unanchored placeholders. Newaddon.IsUnanchoredDifficultyandaddon.RecipeLearnSkillapply that rule.FormatSkillTiersshows "-" for those recipes. The Browser tier filter and the Missing Recipes "Can learn now" gate treat them as unknown and keep the row. Location:TOGProfessionMaster.lua,Modules/Crafting/CraftingEngine.lua,GUI/BrowserTab.lua,GUI/MissingRecipesTab.lua.The Missing Reagents [Bank] button sent requests with no item name. It passed
entry.name/entry.link, butBuildReagentListentries carryitemNameand no link, so the dialog always gotnilfor both. The name and link are now resolved withaddon.Item.GetInfo(itemId)at click time. Location:GUI/ShoppingListTab.lua. NOTE, found later the same day: nothing in the shipped addon drawsShoppingListTab--MainWindow:DrawTabhas no branch for it andgetTabDefslists no such tab -- so this fix, and the Reagent Watch panel in the same file, cannot be reached in game. The player notes no longer list it.The Professions tab leaked three frames on every redraw.
Drawcreated a new column-header bar and two tooltip hit frames each time and dropped them on release. WoW never frees a frame, andDrawre-runs on everyGUILD_DATA_UPDATED. NewBrowserTab:EnsureHeaderBarcreates them once, andDrawre-parents and re-anchors them, like_detailOuter. Location:GUI/BrowserTab.lua.The Cooldowns tab leaked frames on every refresh and every popup open.
DrawRowcreated up to 11 raw frames per row, plus their textures and font strings, and dropped them on release. The transmute popup built a new shell, click-outside overlay and ~6 frames per row on every open. WoW frees neither frames nor regions. Each row now takes a kit (NewRowKit: every frame and region a row can use, built once) from a free list and returns it on release. Each element keeps its role, and whatever a row sets conditionally (the spec icon and its hover, the group row's click) is reset.ReleaseRowKitdrops the reagent label's entry fromreagentTints, so a kit sitting in the free list is no longer recoloured on everyBAG_UPDATE. The popup keeps one shell (_popupShell) and one row slot per line (NewPopupRow), with unused slots hidden. A per-kit / per-open generation stops a late item-load callback writing into a kit or row that has moved on. Location:GUI/CooldownsTab.lua. Later in this release the rows themselves moved to a RowList (see Improvements), which retired the kits; the popup's shell and row-slot reuse stands.The Scan AH button's tooltip never appeared while it was disabled, which is whenever the Auction House is closed. A disabled Button fires no OnEnter unless
SetMotionScriptsWhileDisabled(true)is set; Blizzard's ownUIButtonMixin:SetDisabledTooltipdoes the same.MakeScanAHButtonnow sets it, and resets it in itsOnReleasebecause the AceGUI frame is pooled. Location:GUI/SharedWidgets.lua.
Improvements
The never-shown Shopping List tab and its Reagent Watch list are deleted as dead code.
MainWindowhas no tab forGUI/ShoppingListTab.luaand has not drawn it since the shopping list moved into the Professions tab (v0.0.15 era), so its Reagent Watch panel -- the only way to add an item todb.char.reagentWatch-- could not be reached in game. Removed: the file (from all six TOCs),ReagentWatch'sWatch/Unwatch/IsWatching/GetWatchedItems, thereagentWatchdefault, andTests/shoppinglist_spec.lua;PLAYER_LOGINdrops any savedreagentWatchtable once. Kept: the bag/bank/mail counts, theREAGENT_WATCH_UPDATEDrefresh the Cooldowns and Crafting tabs listen for, and the shopping-list alert.Tests/shoppingbank_spec.luakeeps only itsaddon.Bank.ShowRequestDialogcases; the specs that loaded or listed the file no longer do. The watch panel's locale strings are left in the 15 locale files, unused. Suite 1615/0. Also stripped four leftoverwrit-cannot:gate markers from spec comments. Location:Modules/ReagentWatch.lua,TOGProfessionMaster.lua, all six TOCs,Tests/.The Missing Recipes help told players to click a "+" that does not exist. Its "Row actions" line said "+" adds the scroll to your Reagent Watch; the tab has no such button (and the watch list is gone, above). It now describes the row's real [Bank] and [AH] buttons. Location:
GUI/MainWindow.lua.LibDBIcon-1.0 is a required dependency; the embedded copies are gone.
libs/LibDBIcon-1.0.luaandlibs/LibDataBroker-1.1.luaare deleted, all five TOCs listLibDBIcon-1.0in## Dependencies, and.pkgmetaaddslibdbicon-1-0torequired-dependencies.WoW Forever support, with LibDBIcon embedded there only (temporary). New
TOGProfessionMaster_Camelot.toc(Interface 16001). LibDBIcon-1.0 is not yet published for Forever, so that TOC alone loadsLibs/LibDataBroker-1.1/LibDataBroker-1.1.luathenLibs/LibDBIcon-1.0/LibDBIcon-1.0.lua(byte-for-byte copies of the standalone v12.0.3: MINOR 4 and 56) before any addon code, and lists LibDBIcon-1.0 under## OptionalDepsinstead of## Dependencies, so a standalone copy still wins when a player has one. The other five TOCs are unchanged.Tests/loadorder_spec.luapins it. To undo once LibDBIcon ships for Forever: delete the two TOC lines and bothLibsfolders, move LibDBIcon-1.0 back into_Camelot's## Dependencies, and emptyFOREVER_EMBEDSin that spec. Not tested in a Forever client.Specs. New
Tests/skilltiers_spec.luacovers the unanchored-tier rule.Tests/craftingtab_draw_spec.luachecks that the Craft button's only anchor is the holder, that it lands at the panel's spot, and that it follows the panel when the panel moves.Tests/craftqueue_spec.luachecks the Craft All run end and prompt.Tests/sisterguild_spec.luachecks the pull gate's argument order.Tests/shoppingbank_spec.luaclicks the Missing Reagents [Bank] button and checks the dialog's name and link.Tests/gui_draw_spec.luachecks that the Professions header bar is the same frame across redraws, and that the disabled Scan AH button shows its tooltip and releases a clean frame (red with the flag removed); that a Cooldowns redraw creates no frames and returns every kit (red with the free list bypassed); and that a reused kit keeps no click, reagent column, mail button or bank item from its previous row.Tests/cooldowndraw_spec.luachecks that reopening the popup builds no frames, and its one-mail-button spec now counts SHOWN buttons, since pooled row slots own hidden ones.Tests/tooltiphooks_spec.luanow counts onlyShowhooks, because the Ace lifecycle's ownhooksecurefunccalls atPLAYER_LOGINwere inflating the count. The harness pin moved from 82eb99e to 7008976 in its own commit, then to f7742d1: on 7008976 the full suite ran past ten minutes without finishing while every spec file passed alone. The harness named d77ed4f (its reset now drops the old UIParent's children, a ~5x slowdown in one process) as the likely cause; the commits were not bisected. Whole suite 1639/0 on f7742d1.LibAceGUIWidgets-1.0 is a required dependency (MINOR 36), and the window code it replaces is gone (adoption in progress). All five TOCs and
.pkgmetalist it;addon.Wholds it when MINOR >= 36. Moved to the library so far:addon.GUI.AttachTooltipandaddon.AceGUIFrameScriptsare pass-throughs toAttachWidgetTooltip/WidgetFrameScripts. The old tooltip helper enabled the mouse on a pooled Dropdown/EditBox frame and never turned it off; the library restores it. The label-offset, search-box, header-glow and sort-icon cleanups rideW:OnWidgetRelease, so none of them takes the widget's oneOnReleasecallback; the sort-icon cleanup that was copied into the AH Profit and Cooldowns tabs now lives once in the icon helper.- Main window:
PersistWindow+SetWindowProfile(the resizable tabs share one profile; the oldbrowserWidth/browserHeightmove into it once),SetWindowScale,SetWindowOpacity(re-applied after each tab draw, and now reaching the tab panes too),DressBottomRowfor the help and gear icons (help tooltiptipMinWidth280), andEscapeLayerfor Escape (popup first, then the window). Behaviour change: closing the window now also closes the [Bank] request dialog and the Cooldowns popup; the dialog used to outlive the window it came from. The window stays clamped to the screen while open (PersistWindowclamps only on restore), and the pooled frame's own clamp setting is put back on release. TheOnSizeChangedresize hook was aHookScripton the pooled frame, added again on every open and never removable; it is now aWidgetFrameScriptsscript restored on Release. The gear's proxy-clearing workaround is gone: the options panel is an AceConfigDialog window, and the start of itsOpenwrapsCloseSpecialWindowsrather than calling it (Ace3AceConfigDialog-3.0.lua:1853-1860; the rest ofOpennot read, and not yet tried in game). A resizable profile's size is saved on Release by the library itself since MINOR 36 (LibAceGUIWidgets thread 7481d3a5); theMainWindow:_SaveResizableSizestand-in written earlier in this release is removed. - The Cooldowns group popup is placed by
AnchorPopup;addon.Tooltip.AnchorFrameand its four specs are removed. - The crafter-online edge flash is
W:FlashScreen(four flat edge bars in place of the low-health texture). NewTests/crafteralert_spec.lua. - The Reagent Tracker is a RowList: cropped icons,
hoverHighlight, a[Bank]button column (show/text/tip, with the per-banker lines from the newaddon.Bank.StatusText), andfitContentsizing the window. New specs inTests/reagenttracker_spec.lua. - The Guild tab's profession -> specialisation -> member tree is
CreateExpandableList, built once per session and parked in each draw's fill-height group (AttachRawFrameshands it back on release). A toggle re-lays the list instead of redrawing the tab, and the expand state now survives a guild-data refresh. The tab's scroll position is no longer saved across a /reload (the list scrolls itself; it wasPersistentScroll).BuildTreespecs inTests/guildtab_spec.lua. - The Profit Planner's rows are a RowList, built once per session on a host
the tab owns and parked in each draw's group (new
addon.GUI.ParkList, becauseRowList:Newhooks its parent's scripts for good and must never be given a pooled AceGUI frame). The list draws the headers, sort arrow, banding, hover highlight and scrollbar; it isexternalSort, soSortRowsstill orders the rows (source by its label, ties by recipe). The raw 36-row pool, the hand-built header bar, the scrollbarOnValueChangedswap and ascroll.LayoutFinishedoverride on a pooled ScrollFrame are gone. Each subtab's scroll position is kept in rows by the newaddon.GUI.ListScroll. The row tooltip now goes throughaddon.Tooltip.Owner, so it opens above a row in the lower half of the screen instead of always below. Specs inTests/gui_draw_spec.lua;env.drawTabnow anchors its container, since nothing inside an unanchored one had a real height. - Missing Recipes is a RowList the same way:
externalSortover the existingSortList,[Bank]and[AH]as button columns (show/text/tip; the scan'sonRefreshjust repaints the list), and a row tooltip that also names the selected profession for the recipe block in a single-profession view (it passed nil before). Gone: the 35-row pool,UpdateVirtualRows, the hand-built header,AnchorAll, and two overrides on pooled AceGUI widgets -- the result ScrollFrame'sLayoutFinishedand the sub-tab TabGroup's, which is now AceGUI's ownSetAutoAdjustHeight(false)(reset by AceGUI on release). Behaviour change: the "12 Missing Recipes" count moved from the first column's header to the window's status bar, as the Profit Planner's row count already is, because a header whose text changes on every search would rebuild the list's header and every row's cells. Specs inTests/gui_draw_spec.lua, including the row tooltip driven for real. - Cooldowns is a RowList too, which replaces the row kits added earlier in
this release (
NewRowKit/AcquireRowKit/DrawRow), theComputeCol2InnerWidthswidth budget and its specs, the hand-built headers, the ScrollFrame and the per-row AceGUI SimpleGroups. Each part of the old row is a column: the TBC/Wrath spec-bonus icon, the character (right-click anywhere on a row still whispers), the cooldown icon, the name as a button cell ([+]on a group row, a left click opens the popup under it), the reagent (white or grey by bag stock, repainted onREAGENT_WATCH_UPDATED, left click links it), then[AH],[Bank], mail and the "!" alarm as button columns, and the time. The reagent-tint registry is gone; a cold reagent name or icon asks the client once and repaints the list when it answers. The group popup is unchanged. The Cooldowns cold draw on the reporter's saved database fell from 266-407 ms (over its 250 ms budget on this machine) to 25 ms.cooldowndraw_spec's 27 specs, which assert on the drawn text, colours and the popup click, pass against the new list unchanged; the two kit specs ingui_draw_specare now a no-new-frames-on-redraw spec and a pooled-row spec, and the name column's 80 px floor is pinned from the declared widths.env.drawTabnow gives its containerSetAutoAdjustHeight(false)before its size, as the window's all-sides-anchored TabGroup behaves: the harness firesOnSizeChangedsynchronously and an empty auto-height SimpleGroup shrank itself back to 0. - Crafting: the recipe list is a RowList with the profession's category
rows as group headers (
sortCycle = "three"gives the third header click back to the tree), the queue is a reorderable RowList handing drops toCraftQueue:Move, the panels sit in aNewDockLayoutthe tab owns for the session (no morecontainer.LayoutFinishedor scrollLayoutFinishedoverrides, noDetachPool), the quantity box isCreateStepper, and the Craft button isCreateSecureActionButton, built lazily because the factory refuses in combat. The Profit Planner jump now usesScrollToEntry(..., { context = 2, ifNeeded = true }), andTests/craftscroll_spec.luadrives it end to end instead of pinning the retiredScrollToRowarithmetic. Suite 1624/0 at that point; not run in game. - The Professions list, the Bank dialog and the toolbars are still hand-rolled.
Location:
TOGProfessionMaster.lua,Compat.lua,GUI/MainWindow.lua,GUI/SharedWidgets.lua,GUI/CooldownsTab.lua,GUI/AHProfitTab.lua,GUI/ReagentTracker.lua, all five TOCs,.pkgmeta,Tests/env_togpm.lua.26 weak spec assertions fixed (Writ's peer review, thread feed68df):
is_nilon a literal key now names its key once, shared by the setup and the assertion, with a positive assertion first where the key could have been absent.Tests/pricefacade_spec.luanow seeds the orphanedahPricesstore, so its removal is exercised.
[v1.1.1] (2026-09-16) - The [Bank] request dialog follows TOGBank's enforced request limit and places shop orders
Bug Fixes
- The [Bank] dialog offered more than TOGBank would accept, then blamed
syncing. TOGBank's
Guild:AddRequestnow ENFORCES the officer's maximum request % (its SETTINGS-CANON-001, peer-review thread 17a1f2c9): per bank, less the requester's OPEN orders of that item from that bank. Our dialog capped at that percent of the WHOLE GUILD's stock and ignored open orders, so with Abe holding 10 and Zed 30 at 50% it offered 20 from Abe, where TOGBank allows 5 (fewer with orders open), and Send printed "Request failed. Check that TOGBankClassic is synced." in place of the reason. The ceiling is nowTOGBankClassic_Guild:RequestAllowancefor the selected banker, recomputed when the banker dropdown changes; an order over it is refused in the dialog with TOGBank's ownRequestLimitTextsentence; and any refusal fromAddRequest(limit, ordering closed, not for sale) prints the sentence it returned. At 100% the ceiling is that banker's stock. Against a TOGBank withoutRequestAllowancethe old whole-guild percent is kept, since nothing there enforces one. Newaddon.Bank.RequestAllowance. Location:Compat.lua. - Every [Bank] order was refused while TOGBank's shop was selling.
AddRequestrefuses an order not markedshopOrder = truewhile the shop sells (TOGBank SHOP-NOFREE-001), and this dialog never marked one. It now mergesTOGBankClassic_Guild:ShopOrderFields(itemId)(TOGBank's SHOP-ORDER-API-001, built for this button) into the request -- the mark and the estimate -- and shows itspromptline in the dialog, which grows to fit. The fields are taken when the dialog opens, so the estimate written is the one the player was shown. Found by an independent review of the change above. Location:Compat.lua. - Three smaller dialog defects from the same review. (a) The allowance was
computed only at open; an order filled while the dialog sat open raised it,
and the stale figure refused an order TOGBank would take -- it is re-checked
at Send now, keeping the typed quantity. (b) The stock line read "Bank stock:
40 | Max requestable: 5 (50%)", pairing the whole guild's stock with one
banker's cap; it shows the stock the percent is of. (c) A view-only banker
(visible, not requestable -- TOGBank VIEWBANK-001) could be the pre-selected
banker and every Send refused; view-only bankers are no longer offered, and
when only they hold the item the dialog says so instead of opening.
Location:
Compat.lua. - Sixteen specs in
Tests/compat_spec.luadrive the real dialog (open, pick a banker, type, Send, read chat). Mutation runs: with the allowance and the reason switched off 6 go red; with the four review fixes switched off 6 go red; the rest pin behaviour that was already right and stay green by design.
[v1.1.0] (2026-09-14) - Prices and the Auction House scan move to ItemDB; one mail per cooldown, every reagent split and attached from one click
New Features
- Every [Bank] button carries TOGBank's staleness dot. The user, of the
green dots beside each banker in TOGBank's own Browse list: "i would like
to add those dots next to the items in TOGPM as well if they have the bank
button available, so folks can tell at a glance if it's stale or not." The
dot in front of [Bank] is TOGBank's own per-banker verdict
(
TOGBankClassic_Guild:GetAltStaleness, the accessor its Browse and Bankers lists read): green when our copy of that bank is current, red when a newer copy is published and being fetched (or the copy is in the old format), yellow when a peer has offered a newer copy, grey when the only newer copy is on a client that cannot send it. One button stands for every banker holding the item, so its dot is the WORST of theirs -- a red among greens is what the glance is for -- and hovering the button lists each banker with its count and status word. The colours are read from TOGBank'sTOGBankClassic_UI_Browse.STATE_COLOR/STATE_TEXTwhen that window is present, so a palette change there reaches these dots without an edit here; against a TOGBank that predates the accessor the button reads exactly as before, with no dot. Every site that used to hard-code the green[Bank]label now goes through oneaddon.Bank.Decorate(the Professions detail and recipe rows, the shopping-list reagent rows, the Cooldowns main row and its transmute popup, the Crafting tab's reagents, the Missing Recipes scrolls, the Reagent Tracker, and the Shopping List's two AceGUI buttons); the buttons are ~10px wider for the bullet. Location:Compat.lua(GetBanksWithItemnow carriesstate;ItemState,StateColor,StateText,ButtonText,Decorate,AddStatusLines), the eight GUI files above. Specs:Tests/compat_spec.lua("the staleness dot on [Bank]", nine examples -- worst-wins, the palette read-through and its fallback, the no-dot degrade, that an AceGUI widget is never left carrying the item id, and the tooltip rows).
Improvements
The Cooldowns tab's mail button sends ONE mail per cooldown per character, with every reagent of that cooldown on it. The user's report: an Arcanite transmute (Thorium Bar + Arcane Crystal) cost the sender two mails, because the transmute popup drew a mail button on every reagent row and each one mailed its own reagent. The reagent rows stay -- each keeps its own [AH] and [Bank] request, which is what the rows are for -- but the mail button now sits on the first row of each cooldown only and carries the whole reagent list; the main (single-reagent) rows go through the same path with a one-item list. In the user's words: "split ALL the components for the cooldown and attach ALL the components to the mail ... ONE mail/order fulfill button", then "it's the one shot button, but per character".
One click does the whole thing. The click plans every reagent against the bags first and touches nothing until the plan is whole: a reagent that cannot be covered blocks the send and EVERY shortfall is printed together (not just the first), and a plan needing more stacks than a mail's twelve slots is refused up front. Then every stack that needs splitting is split from that click -- 0.25s apart, each piece placed into an empty bag slot chosen before the first split (a placed piece is invisible to the container API until its deferred pickup fires, so choosing per split would hand every piece the same slot), 0.1s after its split so the cursor has cleared before the next split loads it. The attach step runs after the last placement, waits a few ticks for any piece that has not committed to its slot, and then attaches the pieces and the whole stacks in order. The confirmation popup ("Split N from stack of M?") and its "click Mail again" second click are gone. The sequence is TOGBankClassic's fulfil path (MULTIFILL-002), which is in players' hands.
All-or-nothing at the attach too. A stack the plan counted on that is no longer where it was (moved between the click and the attach) fails its pickup; rather than send the rest under a summary naming the whole plan, everything already attached is taken back off the mail (the client's right-click detach) and the click is reported as failed, so the next click re-plans against the bags as they are. The occupancy test is
HasSendMailItemover every slot -- the client's own (Classic EraMailFrame.lua:881); the oldif GetSendMailItem(1)read the item NAME, which is nil for an item the client has not cached, and read slot 1 only. Locations:GUI/CooldownsTab.lua(CdMail_PlanSupplyMail,CdMail_PrepareSupplyMail,CdMail_AttachSupplyMail,ShowGroupPopup),Locale/enUS.lua,docs/FEATURES.md. Specs:Tests/cooldownmail_spec.lua(new, 23 examples over the harness's real bags, cursor and send-mail slots),Tests/cooldowndraw_spec.lua(a two-reagent transmute draws two reagent rows and ONE mail button, and clicking it puts both on one mail).
Bug Fixes
The mail body from the transmute popup named the spell where the product belongs. The user's first in-game mail read "Please use these materials to make Transmute: Arcanite. Please send me the Transmute: Arcanite" -- the popup handed the row's display name (the spell) to the body. It now resolves the recipe's crafted item first ("Arcanite Bar") and falls back to the old chain only for an entry with no recipe. Pinned in
Tests/cooldowndraw_spec.lua. Location:GUI/CooldownsTab.lua(ShowGroupPopup).The [Bank] button counted stock held by EX-bankers.
addon.Bank.GetStockwalked every record in TOGBankClassic'sInfo.alts, which keeps a character's stored inventory after their bank note is removed -- so a retired banker still holding 20 Linen Cloth lit the button and reported 20 in stock against a request nobody could fill.GetBanksWithItemalready walkedGetBanks()(the current bankers);GetStockis now the sum of it, one implementation. TOGBankClassic's peer review (thread 5eef0788, F2); its F1 -- read throughGetAltItemTotalrather than the legacy rows -- was already in place since v1.0.10. Location:Compat.lua,Tests/compat_spec.lua.The planner's "nothing fits, split the largest" branch was dead, and nothing pinned the property the split call depends on. The branch before it already returns for every case it handled (any stack larger than the need qualifies there), so it could never run; removed. The split call refuses
amount >= count, and the mail path now leans on the planner never asking for that -- so a spec walks every layout of up to four stacks of 1..6 against every need up to 12 (10,000+ cases) and asserts the split, when there is one, is strictly inside its stack and the fulfilled total is exact. Location:GUI/CooldownsTab.lua(CdMail_CalculateFulfillmentPlan),Tests/cooldownrows_spec.lua.The "Attached ..." chat line was erroring on the zhTW, zhCN and koKR clients. Those three translations had the
%sand%dx %sarguments in the other order, sostring.formatwas handed a name where it expected a number. The line is now one list string ("Attached 1x Thorium Bar, 1x Arcane Crystal for Bob.") under a new key that every locale falls back to enUS for; the old key and the unused "no empty bag slot" key are removed from all 13 locale files. Location:Locale/*.lua.The Crafting tab's "AH price" line for the crafted item ignored Auctioneer and TSM. It read
Price.Getand then accepted the answer only when its source id was Auctionator's or the own scan's, so a player whose only price source was Auctioneer or TSM never saw a sale price or a profit there, while the Profit Planner (which readsGetSaleLive) showed both. Found while porting the ladder; the line now readsGetSaleLivelike the planner does. Location:GUI/CraftingTab.lua(RefreshDetail).
Price sources move to ItemDB
- Pricing, the AH scanner and every third-party price bridge now live in
ItemDB (LibItemDB-1.0 MINOR 25), and this addon reads them from there.
The user's directive: "we need to move those 3rd party integrations into
itemDB now", then, once ItemDB was tested in game (its scan priced 524
items;
/itemdb priceanswered "Item 8952: 20 (TradeSkillMaster, Market value)" on Old Blanchy), "i think you can get the pricing data from ItemDB now. also, the [AH] button funcitonality needs to tie into that for pricing and seaching as well". One copy of the Auctionator / Auctioneer / TSM adapters, one realm+faction scan store and one set of source toggles now serve every TOG addon; TOGBankClassic's storefront reads the same numbers. What this addon keeps is its POLICY over those numbers, which the library deliberately does not carry: a vendor-sold reagent is costed at the vendor price whatever the AH says (the 2026-09-11 directive), the crafting-cost sum with its BoP exclusion and lower-bound / stale flags, the vendor SELL row, and the source labels and colours every tab paints provenance with.Modules/Price.luais a facade:Get/GetSaleLive/GetSaleHistoricalreadDB:GetPrice(id, "best" | "historical"),GetVendorBuyreadsDB:GetVendorBuyPrice,MoneyusesDB:FormatMoney. The three-return shape (copper, source, age) is unchanged, so the ~20 call sites across the tabs did not move. Everything is feature-gated on the METHOD, never a MINOR: against an older ItemDB the AH tiers answer nil, the static vendor tier still answers throughGetVendorBasePrice, and nothing raises.Modules/AHScanner.luais theaddon.AHfacade over ItemDB's scanner --IsOpen,SearchFor,StartScan,StartFullScan,CancelScan, the progress reads andGetListingsFor-- installed only when the ItemDB in play carriesStartTargetedScan, which is what every tab'sif addon.AHdegrade was written for. The per-row [AH] buttons therefore gate on ItemDB's scan results and search through ItemDB, and the shared Scan AH button drives ItemDB's targeted scan. The two addon events the tabs subscribe to (AH_OPEN_STATE_CHANGED,AH_SCAN_COMPLETE) are re-fired from the library'sLibItemDB_AuctionHouse/LibItemDB_ScanCompletecallbacks in one place. The "TOGPM Scan" button on the AH frame is gone (ItemDB draws its own at the same anchor; the two had rendered on top of each other in the user's screenshot). Two defects ItemDB's suite found while porting went with the code: a cancelled targeted scan left its next-item timer armed, and the Auctionator "historical" read reached for an API that does not exist and silently fell back to the live price.- Settings: the nine price-source toggles and the scan delay are one
button, "Price sources (ItemDB)...", which opens ItemDB's own window
(
/itemdb) -- sources, precedence and statistic are set there once for every TOG addon. A player's earlier choices carry over on first login:addon:MigratePriceSettingsToItemDBwrites each profile value that differs from ItemDB's default intoDB:SetPriceSetting(parents before their fallbacks, since ItemDB turns a fallback off with its parent; never afalsefor the TSM App Helper toggle, whose ItemDB default is ON and which gates TSM's region-wide figures -- this addon's old default was OFF, so a stored false was the default, not a choice), ONCE PER ACCOUNT (stamped indb.global; ItemDB's settings are per account and ours were per profile, so the first character to log in after the update is the one whose choices carry), then clears the profile's copies and drops the orphanedfactionrealm.ahPrices/vendorPricesscan store. Scanned prices are not migrated -- one scan refills ItemDB's store. - The Profit Planner's source filter reads
DB:GetPriceSources()-- the library's registry, detection and enablement -- instead of probing the third-party globals itself; the Historical subtab offers only sources that carry ahistoricalstatistic. A filter saved by an older build folds its per-statistic keys onto ItemDB's provider ids (togpm-ah->scan,auctioneer-live/-cached/-app->auctioneer,tsm-live/-history->tsm,auctionator-history->auctionator).addon.PriceSourceLabels/PriceSourceColors, the Crafting tab's[SCAN]/[AUC]/[AUCN]/[TSM]badges and the help-panel legend are keyed by the same ids. - Locations:
Modules/Price.lua,Modules/AHScanner.lua,TOGProfessionMaster.lua(source tables,dumpprice,MigratePriceSettingsToItemDB),GUI/Settings.lua,GUI/AHProfitTab.lua,GUI/CraftingTab.lua,GUI/MainWindow.lua,Locale/*.lua(sixteen settings keys retired, two added),docs/FEATURES.md. Specs:Tests/price_spec.lua(rewritten over the REAL ItemDB price files via the newenv.priceDB(); the bridge, store and merchant-capture cases moved to ItemDB'sTests/price_spec.luawith the code),Tests/pricefacade_spec.lua(new: theaddon.AHfacade, the migration, the Profit Planner's filter, the Settings button),Tests/craftingtab_spec.lua;Tests/ahfullscan_spec.luaandTests/ahscanner_spec.luadeleted (65 examples in ItemDB'sTests/pricescan_spec.lua). 100% line coverage on both modules. - Requires ItemDB carrying LibItemDB-1.0 MINOR 25 for any auction price; the [AH] buttons and the Scan AH button do not appear against an older one. Contract threads with ItemDB: 6b52f51f (the trigger, quoting the user's in-game test), 5bb73440 (the per-account stamp).
Test harness
Tests/wowapimoves from813f3d2to82eb99e(a month of Adoption log). What it cost here: the harness now dispatchesOnSizeChanged, socraftscroll_spec.lua's fixture -- which set a content height by hand on an AceGUI ScrollFrame with no children -- had AceGUI's own layout reset the height to 0 and four examples went red. The fixture now installs the no-opLayoutFinishedexactly asCraftingTab:Drawdoes beforeFillList; the addon code was already right. The pin is what makes the cursor and send-mail slots above available offline. Location:Tests/craftscroll_spec.lua.
Releases v1.0.10 and earlier are in CHANGELOG_ARCHIVE.md.
This mod has no additional files

