promotional bannermobile promotional banner

TOG Profession Master

TOG Profession Master should help you and your guild to know faster which player has which profession and who can craft which item or who has learned which enchantment.
Back to Files

TOGProfessionMaster-v1.0.5

File nameTOGProfessionMaster-TOGProfessionMaster-v1.0.5.zip
Uploader
PmptastyPmptasty
Uploaded
Aug 4, 2026
Downloads
199
Size
854.2 KB
Flavors
MoP ClassicClassic TBCClassic
File ID
8573160
Type
R
Release
Supported game versions
  • 5.5.4
  • 4.4.2
  • 3.4.5
  • 2.5.6
  • 1.15.9

What's new

TOG Profession Master Changelog

[v1.0.5] (2026-08-03) - Enchant recipes showed a random item's tooltip; the Crafting tab left a second window open

Bug Fixes

  • Opening the Crafting tab also popped a second profession window — TSM's, or Blizzard's — and left it there. Reported on TBC. On Classic there is exactly one way to get a trade-skill session: cast the profession. That session (not any frame) is what the tab reads recipes from and crafts through — but the cast fires TRADE_SKILL_SHOW / CRAFT_SHOW, which is the same event every other profession UI listens for. So clicking our tab handed TSM (or Blizzard) their cue too, and the player got two windows. TOGPM only ever hid Blizzard's own frames, and only the one belonging to the current session, so anything else stayed up. The Crafting tab now claims the session it opens: for as long as it owns one, Blizzard's TradeSkillFrame and CraftFrame are hidden — re-hidden from an OnShow hook, so an addon that shows one later can't leave it up — and TSM's window is hidden through TSM's own public API (TSM_API.RegisterUICallback, the only integration point it exposes). Every hide clears the frame's OnHide script first, because each of those windows closes the trade-skill session from OnHide and that would blank our own tab; if the session is ever observed dying anyway, TSM suppression disables itself for the rest of the play session — a second window beats a Crafting tab with no data in it. The WoW UI button releases the claim, so choosing the native window mid-session still works and it stays up. Location: Modules/Crafting/CraftingEngine.lua. See docs/DEPENDENCY_CONTRACTS.md for the TSM API this is working around.

  • Hovering an enchant in the Professions tab showed a completely unrelated item — a staff, a trinket, whatever happened to share the number. Reported on TBC; it was present on every flavour. Every recipe key in the shipped recipe data (LibProfessionDB) is the recipe's trade-skill spell id, but the browser's link resolver treated that key as an item id whenever the entry carried no crafted-item link — and WoW's item and spell id spaces overlap freely, so the lookup silently succeeded on the wrong thing. Spell 13937 is Enchant 2H Weapon - Greater Impact; item 13937 is the staff Headmaster's Charge, which is precisely what the report's screenshot showed. Crafted-item recipes hid the fault because their link is built from craftedItemId and wins first — enchants produce no item, so they fell straight through to the bad lookup. Recipe entries now carry their spell id explicitly, the crafted-item lookup is keyed by craftedItemId, and an enchant resolves to its spell tooltip (description + reagents). Shift-clicking an enchant likewise inserts the recipe's spell link instead of an unrelated item link. Location: GUI/BrowserTab.lua.

  • The recipe list's [Bank] button was reading stock for the wrong item. Same root cause: the button asked TOGBankClassic for stock of entry.id — a spell id — so it appeared (or didn't) based on whatever unrelated item shared that number, and the request dialog it opened named that item. It now uses the recipe's craftedItemId, so it appears only for recipes that actually produce a bankable item. Location: GUI/BrowserTab.lua.

  • The [TOGPM] crafters line never appeared on Professions-tab tooltips. It was looked up by spell id against an item-keyed table, so the lookup always missed and the line silently never rendered. Now keyed by the crafted item, matching the global item-tooltip hook. The [TOGPM] itemId= spellId= diagnostic line was reporting the spell id in the itemId slot for every recipe; both fields are now correct. Location: GUI/BrowserTab.lua.

  • Enchants on the shopping list had no hover tooltip at all (no crafted item to link). They now show the recipe's spell tooltip. Location: GUI/BrowserTab.lua.

  • A guildless player could get sync errors in their bug catcher. BroadcastSisterConfig and BroadcastSisterRosters send on GUILD and neither checked the player was actually in one. The client refuses such a send — it always did, silently — but AceCommQueue-1.0 MINOR 5+ now reports a refusal with no delivery callback through geterrorhandler(), so what used to be an invisible dropped message became a visible error, repeating on the ~12-minute config-gossip timer. Anyone who configured allied guilds and then left their guild (or received the list by gossip) would see it. Both now return early when guildless. Location: TOGProfessionMaster.lua.

  • Recipes named "… [PH]" were never filtered out of the cache. The obsolete-name guard that keeps Blizzard's internal placeholders (TEST / QA / DEPRECATED / ZZOLD / OLD) from reaching the UI checks for [PH] with string.find(..., plain) — and with the plain flag set, the pattern is matched literally, so the escaped form %[ph%] searched for a percent sign followed by a bracket and matched nothing, ever. Every "Manual: … [PH]" placeholder sailed through the filter and persisted in SavedVariables, where the only way to shift it was a purge. Written plainly now. Found by Tests/scanner_names_spec.lua. Location: Scanner.lua.

  • Auctioneer pricing never ran for any item the client hadn't cached. Found while writing Modules/Price.lua's specs. Both Auctioneer bridges build their probe list as { itemLink, "item:N:0:0:…", "item:N" } — and itemLink is nil whenever GetItemInfo hasn't loaded the item yet, which puts a nil in slot 1 and makes ipairs stop on entry. So the two synthetic item forms, which exist precisely for the uncached case, were unreachable exactly when they were needed and Auctioneer was never queried at all. The list is now built by appending, so the synthetic forms are always probed. (Both call sites shared the bug; they now share one builder.) Location: Modules/Price.lua.

  • Removed a dead TSM code path. tsmAppHelperLoaded — an IsAddOnLoaded probe — was referenced by nothing; readiness is decided by the useTSM / useTSMAppHelper toggles plus the presence of TSM_API, which is the real capability test. Location: Modules/Price.lua.

  • Scanner:ResolveProfessionId called GetProfessions() without checking it exists. Found while writing its specs. GetProfessions is the Cata+ API; this addon feature-detects it at every other call site — enumerateHeldProfessions right below it (whose own comment notes it is a no-op on Classic Era), and CraftingEngine:GetKnownProfessions — but not here. This one sits in the hot path of every trade-skill and craft-window scan, so on a client without the global nothing would scan at all rather than degrading. Now guarded like its neighbours: absent → fall through to the static profession-name map, exactly as a slot walk that finds nothing already does. Behaviour is identical wherever the API exists. Location: Scanner.lua.

  • HashManager:HasContent answered "no" as nil on some paths and false on others. Found while writing its specs: branches written as gdb.cooldowns and next(gdb.cooldowns) ~= nil evaluate to nil — not false — when the table is absent, so the same "we hold nothing for this leaf" answer came back with two different types depending on which table happened to be missing. Harmless for if HasContent(...), a trap for any caller comparing against false or putting the result on the wire. Every branch now returns a real boolean. Location: Modules/HashManager.lua.

Improvements

  • Guild sync now uses DeltaSync's revision-2 content hash. Revision 1 renders a number and its string form identically — {v = 1} and {v = "1"} hash the same — so a genuine difference between two clients can be invisible and the sync silently skipped. Revision 2 (DeltaSync MINOR 16) types each scalar. Both now ride together on every leaf: HashManager mints the pair, the wire carries both, a delivered pair is adopted verbatim like any other owner-minted token, and every comparison — DeltaSync's own OFFER protocol and our subhash diff alike — uses the highest revision both ends advertise. A peer on an older build sends no revision-2 token, so that pair falls back to revision 1 and still agrees; two updated peers get the collision-free comparison. No flag day, no coordination, and no version bump on the wire. One subtlety worth recording: a roll-up advertises revision 2 only when every contributing leaf has one — a roll-up composed from a mix of revisions isn't comparable to another client's mix, so a half-upgraded guild would otherwise churn; it upgrades itself once the last old leaf is re-minted. This is what lets revision 1 eventually retire across the addon suite. Two now-unreachable helpers (ComputeCharSkillsHash, ComputeCharProfessionsHash) were removed as part of this — the coverage run flagged them as the only unexecuted lines left in the file, and they were the last revision-1-only minting paths in the addon: a leaf minted through one would carry no revision-2 token and quietly drag its whole roll-up back down. Location: Modules/HashManager.lua, Scanner.lua.

  • /togpm dsstatus now answers "did my sync messages actually arrive?" DeltaSync-1.0 MINOR 17 added delivery accounting — WoW silently discards addon messages under congestion, and AceComm forwards only a boolean, so a refused send was previously indistinguishable from a delivered one and a sync that had quietly stopped working looked exactly like an idle one. The status block now reports delivered / refused / not-attempted with the last refusal's channel, target and size, and adds a plain-language note when the refusals are simply because you're guildless. A new onSendFailed hook also logs each refusal to the Sync Log as it happens (capped at the first three, so a guildless player doesn't get one line per message forever). Degrades quietly on an older DeltaSync, which is reported rather than shown as a misleading zero. Location: Scanner.lua.

  • Offline test suite added — 634 specs. TOGProfessionMaster now carries the shared WoWAPITesting harness (Tests/wowapi, git submodule), Tests/coverage.lua (exact line coverage from Lua 5.1 bytecode debug info, not a source-text heuristic), and Tests/env_togpm.lua, which boots the real addon core offline: the real Ace3 chain, the real AceCommQueue-1.0 and DeltaSync-1.0 from the sibling AddOns folders, the real Ace:OnInitialize() with its AceDB and guild-tag hashing. Specs therefore read real tables and compute real hashes rather than hand-copied stand-ins that drift. The specs:

    • hash_spec.luaModules/HashManager.lua at 100%, including the owner-mints/relay-adopts convergence invariant the cooldown-drift disaster came from.
    • craftqueue_spec.luaModules/Crafting/CraftQueue.lua at 99.2%, driving the REAL CraftingEngine against stubbed trade-skill APIs, so the queue is tested against the engine rather than against our assumptions about it. Covers the per-item completion model that v0.8.1 fixed twice.
    • scope_spec.lua — the display-time visibility gates, run against the REAL LibGuildRoster driven to ready (or deliberately left mid-build). Every bug those gates have had was a question about what to do when the roster cannot yet answer, so the cold-start and library-absent paths are asserted explicitly — including that hiding a cross-guild alt must never queue it for deletion.
    • scanner_merge_spec.lua — Scanner's merge and normalise half: id extraction, recipe/crafter merges, the cross-guild federation gate, the dropped-profession resurrection guard, alt-group indexing.
    • price_spec.luaModules/Price.lua at 100%: the source priority ladder, and every optional integration (Auctionator, Auctioneer, TSM) proven inert until the user opts in.
    • compat_spec.lua — the version flags and API shims, loaded once per client build (Vanilla → MoP) so a wrong flavour flag or skill cap fails here instead of on someone else's client.
    • tooltip_spec.lua — the [TOGPM] crafters and IDs lines: the item → recipe → crafter → visibility-gate lookup, deduping, and the deferred-append guard.
    • browserlist_spec.lua — the Professions tab's recipe-list pipeline: BuildFullList (including the visibility verdict it bakes into each cached row), the tokenised search filter, the skill-tier bands, and the multi-select cache key.
    • cooldownrows_spec.lua — the Cooldowns tab's row pipeline (transmute grouping, the spell whitelist, the dropped-profession hide, Salt Shaker's item/spell id collision), the tiebreaking sort, and the supply-mail stack planner.
    • missingrecipes_spec.lua — the missing-set subtraction in both scopes, including a recipe stored under its crafted-item id counting as known, and a cross-guild alt NOT covering the guild's gap.
    • craftingtab_spec.lua — the Crafting tab's filter and sort, and the Profit Planner's row builder and money formatting.
    • scanner_sync_spec.lua — what goes on the wire and what happens to what arrives: absolute-expiry cooldown leaves shipped with the owner's minted token, the adopt-or-ignore rules (including the strict-newer test that stops two peers ping-ponging drifted tokens, and the refusal to let anyone overwrite our own cooldowns), and the subhash diff's -1 stamp for a leaf we hold no data for.
    • scanner_cooldowns_spec.lua — the only place a cooldown is minted: the shared transmute bucket, Ready seeding, the salt-shaker API fallback, and the change-detection that keeps a re-scan from redrawing.
    • scanner_broadcast_spec.lua — the send side: the differential hash broadcast, and the serve-side coalescing rules that stop one popular leaf being re-broadcast guild-wide once per requester — including the asymmetry that a CHANGED leaf must always go out immediately (swallowing that is what once stalled two-client cooldown sync), and the orphan-hash self-heal when we're asked for a leaf we hold no data for. Plus sister-roster persistence and the accept gate that rejects a roster for a guild we haven't allied with.
    • purge_spec.lua — the only code that deletes another player's data: it refuses to run against an unconfirmed roster, re-validates every flag at sweep time, protects own alts and bank alts of current members, and drops the departed character's leaf HASHES along with the data (a surviving hash is re-minted from surviving data on the next rebuild and resurrects them).
    • scanner_scan_spec.lua — the owner-authoritative profession registry and the specialisation read. The delete path is the point: a dropped profession has no window to re-scan, so a wrong "gone" reading destroys data permanently — it therefore deletes only from a read it can trust (locale-independent ids, or an English client) and only after TWO consecutive reads agree, because a partial skill-line read seconds after login looks exactly like a drop.
    • scanner_names_spec.lua — recipe-name recovery and the item/spell id-namespace collision behind it (spell 26926 is "Heavy Copper Ring"; item 26926 is "59 TEST Green Shaman Chest").
    • hashv2_spec.lua — the revision-2 hash rollout: the revision-1 collision it exists to fix (asserted against the real library, not assumed), that every leaf family carries both tokens, the roll-up's all-or-nothing V2 rule, and the mixed-version comparison in both directions — including the phantom-difference case where getting the fallback wrong would make an old peer and a new one re-sync the same leaf forever.
    • browserlink_spec.lua and craftsuppress_spec.lua — the two fixes above.

    Run with lua Tests/wowapi/run.lua from the addon root (Lua 5.1 only, no busted needed). Tests is excluded from packaged builds via .pkgmeta. Overall coverage is 4,743 of 14,601 executable lines (32.5%, counting everything the addon ships except the Locale/ translation tables): Price, HashManager and CraftQueue 100%, CooldownIds 94.7%, Tooltip 68.5%, Scanner 58.8%, CraftingEngine 50.1%, TOGProfessionMaster.lua 48.2%, Compat 44.0%, CooldownsTab 28.5%, MissingRecipesTab 28.2%, AHProfitTab 25.4%, BrowserTab 22.3%, SharedWidgets 18.6%, CraftingTab 14.7%.

    On testing GUI files: the logic in a tab (list building, filters, sort keys) touches no frame — in GUI/BrowserTab.lua the first CreateFrame sits 300 lines below the last logic function. Those functions are local purely because nothing outside the file calls them, which also put them out of the suite's reach. Each such file therefore ends with a small, commented test seam exposing them by name. That is deliberately preferred over relocating the logic into Modules/: it reaches exactly the same code, changes no call site, and avoids adding a file to all five .toc load orders. What stays untested is genuine frame construction, which needs the game client.

  • docs/DEPENDENCY_CONTRACTS.md added. Dependency and third-party-addon repos are read-only from TOGPM: when work here needs a change in one of them, it is written up as a contract (what, why, exact API shape) and applied upstream separately rather than patched locally. Opens with the TradeSkillMaster "suppress the crafting UI for this session" contract that the fix above is working around.


[v1.0.4] (2026-07-28) - Enchanting's Craft button, departed guildmates clearing on their own & the Settings scrollbar

Bug Fixes

  • Enchanting's Craft button did nothing — the long-standing "every profession works except Enchanting" bug. Fixed. Enchanting is the one profession whose craft runs through a secure action button (it casts /cast <recipe> from a secure macro; every other profession crafts from the button's ordinary, insecure PreClick). Blizzard's SecureActionButton_OnClick only performs the action when the click that arrived matches its key-down/key-up gate — (down and useOnKeyDown) or (not down and not useOnKeyDown), where useOnKeyDown falls back to the ActionButtonUseKeyDown CVar. TOGPM registered the button for LeftButtonUp only, so for every player with that CVar switched on — ElvUI and Bartender4 both set it, as does Blizzard's own "use key down" option — the click was dispatched, the secure gate discarded it, and no enchant was ever cast. Trade skills were unaffected because their craft happens in PreClick, which isn't gated: exactly the reported "I tested alchemy and the craft function works fine, so enchanting may be the only profession not working". The button now registers both the up- and down-click while it's in enchant mode, so whichever way the CVar is set (and whichever variant of the gate the client ships) exactly one of the two satisfies it and the enchant casts once. Trade skills keep the single up-click registration so their PreClick can't fire twice per click. Verified against Blizzard's SecureTemplates.lua and against TradeSkillMaster, whose SecureMacroActionButton branches on the same CVar for the same reason. Location: GUI/CraftingTab.lua.

  • Guildmates who left the guild kept showing after login until you clicked around — now they clear on their own. TOGPM never deletes data on its own; it masks it by bouncing the database off the live guild roster. Until LibGuildRoster reports its first build complete, that gate deliberately hides nothing — a half-built roster can't vouch for anyone, and blanking a legitimate list is far worse than briefly over-showing one. That guard is right; the other half of it was missing. Nothing told the UI when the cold-start window closed. The only roster callbacks anyone had hooked were OnMemberOnline / OnMemberOffline (and only in the Professions tab), so after a login or /reload a departed member kept rendering until some unrelated event happened to rebuild the list — which is exactly the reported "I saw his name, looked through TOGPM a bit, and then it disappeared". Three fixes: the roster's ready, joined and left transitions now raise a re-scope signal; that signal (roster) is routed to every guild-scoped tab instead of only the Guild tab — Professions, Cooldowns, Missing Recipes all filter through the same gate and were all missing it; and the Professions tab now rebuilds its recipe-list cache on it, because that cache bakes each crafter's visibility verdict in at build time, so a cache warmed during the cold-start window kept serving ex-members for the rest of the session even after the roster went ready. Location: TOGProfessionMaster.lua, GUI/MainWindow.lua, GUI/BrowserTab.lua.

  • The Settings window could clip its lower options with no scrollbar (reported with ElvUI). AceGUI's scroll frame decides whether to show its scrollbar by comparing the window height against a content height it cached during layout — not against what the widgets measure now. Ace3 skinners restyle those widgets after AceConfigDialog has laid the options out, which changes their real heights while the cached figure stays at the pre-skin value, so the scroll frame concludes everything fits: no scrollbar appears, the options past the bottom edge are clipped, and the mouse wheel doesn't rescue you either (AceGUI ignores the wheel while the bar is hidden). Opening Settings now re-runs the layout once the window has settled, re-measuring the skinned widgets so the scrollbar comes up. Location: GUI/Settings.lua.

New Features

  • /togpm whyvisible <Name> — find out why a character is still being shown. The display gate keeps several deliberate escape hatches (roster not ready yet, a sister-guild tag on the crafter entry, a stale sister roster, or being an alt of somebody still in the guild) and from the outside they're indistinguishable from "it just isn't purging" — which is what made the bug above so hard to pin down from reports. The command prints each gate's answer for one character, plus the crafter tags actually stored against them and the final IsVisibleCrafter / IsInCurrentGuildScope verdicts, so a report can name the real cause. Note a false verdict also queues that character for the timed purge sweep — the command says so in its output rather than mutating quietly. Location: TOGProfessionMaster.lua.

[v1.0.3] (2026-07-15) - Cooldown-sync repair, switched-profession cleanup & cross-guild data-leak fixes

Bug Fixes

  • Guild cooldown sync has been broken since v1.0.0 — now fixed. The v1.0.0 "cooldown-sync overhaul" wrapped cooldown serving in a timestamp gate: a client would only hand over a cooldown when its own copy was strictly newer than the requester's. That comparison can't hold across a guild — it trusts each client's updatedAt, but the migration off the old relative-remaining format left every client stamping updatedAt at its local receive time (≈now), so an owner's real copy was never "newer" than anyone's stamp and nobody ever served a cooldown to anybody. Guild cooldown data silently froze at v1.0.0 and withered as cooldowns expired with nothing replacing them — most people saw only their own. TOGPM now serves cooldowns like every other leaf (prefer-newer, but it never stays silent), and the request advertises what you actually hold — real data, not a leftover hash — so a first fetch always resolves and orphaned hashes can't block it. The absolute-timestamp format that stops sync churn is kept; only the broken serve gate is gone. Cooldowns propagate again the moment two updated clients share a guild. Location: Scanner.lua, Modules/HashManager.lua, TOGProfessionMaster.lua, GUI/Settings.lua.
  • Synced guild cooldowns no longer vanish because your character lacks the profession. A cooldown that had genuinely synced (e.g. a guildmate's Salt Shaker) could be filtered out on your end if your copy of their profession snapshot hadn't caught up — so a bank alt or a fresh login saw an empty Cooldowns tab even though the data was sitting in the database. The dropped-profession hide now applies only to your own characters, where the profession read is authoritative and current; guild sync always displays what it received. The owner decides what to broadcast — a viewer shows everything it got. Location: TOGProfessionMaster.lua.
  • Fixed a Lua error when hovering the Cooldowns tab's "Ready Only" button. The button's new tooltip referenced a locale key (ReadyOnlyTooltip) that didn't exist, and AceLocale raises its "Missing entry" error on the access itself — before the code's own fallback string could run. Added the string and hardened the lookup (rawget) so it can never throw regardless of client locale. Location: GUI/CooldownsTab.lua, Locale/enUS.lua.
  • Alt-group sync (accountchars) no longer churns forever. The alt-group leaf still ran the original v0.7.0 model: a relay union-added incoming entries and then recomputed the hash on receipt, so every client hashed its own accumulated view instead of the owner's canonical one. The leaf therefore perpetually differed between clients and peers re-requested it every sync cycle — a constant background of accountchars:* re-sends and repeated full-list subhash broadcasts. It's now owner-authoritative like cooldowns and professions: the alt group is minted only by its own account and adopted verbatim everywhere else — no union, no recompute — so all clients converge on one hash. Two bonuses fall out of the verbatim replace (vs. the grow-only union): a dropped alt now propagates guild-wide (the old union could never remove one), and a peer can no longer union stray entries into your own alt list. The character purge was also completing this loop wrong — it dropped the alt-group hash but left the data, so the next cache rebuild re-minted the hash and a purged (left-the-guild) character reappeared; the purge now removes the data too, so a purge sticks. Same release-gated convergence as cooldowns — it settles fully once peers are on the new code. Location: Scanner.lua, Modules/HashManager.lua, TOGProfessionMaster.lua.
  • Guildmates who drop or switch a profession no longer linger in the old profession's data. When someone unlearned a profession, TOGPM kept listing them as a crafter of its recipes, kept counting them on the Guild tab, and kept showing their profession cooldowns — forever. A dropped profession has no trade-skill window to re-scan, and guild sync could only ever add data, never remove it, so the stale entries never cleared. TOGPM now keeps an owner-authoritative snapshot of each character's current professions and syncs it on a new professions: channel: on login your client reads the professions you actually have right now (window-less — it doesn't need you to open anything) and broadcasts the complete set, so a profession you dropped is removed for everyone. Your recipes disappear from the Professions browser, your headcount drops off the Guild tab, the recipes you no longer cover reappear in Missing Recipes, and (below) your profession cooldowns drop off the Cooldowns tab. Heavily guarded against false removals: it only acts on a confident, complete read (never a partial early-login scan — a profession must be absent from two consecutive reads before it's removed), so a profession you still have but simply haven't opened is never touched. Location: Scanner.lua, Modules/HashManager.lua, GUI/GuildTab.lua, GUI/MissingRecipesTab.lua, TOGProfessionMaster.lua.
  • Dropping a profession now also clears its cooldowns. Unlearn Alchemy and its transmute drops off the Cooldowns tab; the same applies to every profession cooldown — Tailoring's Mooncloth / specialty cloths / Dreamcloth, Enchanting spheres, JC prisms and daily cuts, Inscription research, Blacksmithing ingots, Leatherworking Magnificence, and the Salt Shaker (whose Use effect requires Leatherworking 250, so losing LW means you can't use it). Each cooldown is matched to its profession. For your own characters the row is hidden immediately (checked against your live, authoritative profession read); a guildmate's dropped cooldown clears once their client rescans and re-broadcasts without it — the viewer-side hide is deliberately limited to your own toons so a guildmate's genuinely-synced cooldown is never hidden just because your copy of their profession snapshot lagged. Location: Data/CooldownIds.lua, GUI/CooldownsTab.lua, TOGProfessionMaster.lua.
  • On accounts with characters in more than one guild, each toon showed profession/cooldown data from the OTHER guild. The "guild only" filter on the Professions tab (and the Cooldowns, Guild, and Missing Recipes tabs, plus item tooltips) failed to filter out data belonging to a different guild. Root cause: the database is a single account-wide table (all your toons write into it), and the display-time guild gate IsVisibleCrafter short-circuited with "own alts are always visible, regardless of guild." So when you were logged into a Guild A character, your Guild B alts — and any recipe or cooldown only they knew — leaked into Guild A's view. The Cooldowns "guild" view was worse still: it carries no guild tag and returned the entire account-wide cooldown table with no scoping at all, so even other-guild members' cooldowns (synced while you were on a Guild B toon) rendered under Guild A. Fixed with a single roster-truth scope test, addon:IsInCurrentGuildScope — a character counts only when LibGuildRoster confirms it's in your current guild (or a configured sister guild), your own alts included. It's applied to every guild-scoped view: the Professions guild/missing views (the Mine view still shows all your alts across every guild), item tooltips, the Cooldowns guild view, the Guild-tab profession headcounts, and the Missing Recipes "known by the guild" test. The check is read-only — a hidden cross-guild alt is never purged, so its data survives for when you log into that guild — and cold-start-safe (before the roster finishes its first build nothing is hidden; the tabs re-scope once it's ready). Note: because cooldowns/skills carry no guild tag, an own bank alt that is guildless (not in the guild) is also scoped out of the guild views — it remains in the Mine view. Location: TOGProfessionMaster.lua, GUI/BrowserTab.lua, GUI/CooldownsTab.lua, GUI/GuildTab.lua, GUI/MissingRecipesTab.lua, Tooltip.lua.

Improvements

  • Fixed a 1–2 second freeze the first time you open the Professions tab. The background cache-warmer builds one profession at a time, and after each one it forced a full tab teardown-and-rebuild (ReleaseChildren + redraw of every dropdown, the toolbar, and the scroll pool). With the tab open while the warm ran, that was ~one full rebuild per profession back-to-back — a dozen complete UI rebuilds in a row, which is the hitch (the list build itself is effectively instant). The warm now refreshes only when the profession it just built is the exact view you're looking at, collapsing that storm to a single redraw; the other professions still warm silently into the cache and appear instantly when you switch to them. Location: GUI/BrowserTab.lua.
  • Recipe-browser build does fewer redundant roster lookups. Each crafter's visibility/guild-scope checks (IsMyCharacter, IsVisibleCrafter, IsInCurrentGuildScope) are now memoized per character within a build, so a crafter known across many recipes is evaluated once instead of once per recipe. Location: GUI/BrowserTab.lua.
  • Serve-side coalescing cuts redundant guild broadcasts. Serving a leaf (or a roll-up's subhash list) is a guild-wide broadcast, but it's request-driven per peer — so when many clients ask for the same thing at once (common in a mixed-version guild, where the one client actually holding real cooldown data gets asked by everyone), it was re-broadcast to the whole guild once per requester. TOGPM now suppresses a repeat of the same leaf or roll-up subhash list within a short window — everyone already got the first copy — collapsing those request bursts to a single send. It targets burst amplification (e.g. a login storm); the steady per-cycle re-requests from un-converged old clients still settle only on release. Location: Scanner.lua.
  • The new profession snapshot is owner-authoritative and fully backward-compatible. Each character's profession set is minted only by that character's own client and adopted verbatim by everyone else — hashes are never recomputed on receipt, the same convergence-safe model the cooldown sync uses (recompute-on-receive is what caused past drift/redraw churn). It rides a separate professions: sync leaf, so un-updated guildmates are completely undisturbed — their existing sync is byte-identical to before and there's no cross-version churn. Un-updated clients simply don't get the switched-profession cleanup until they update. Location: Scanner.lua, Modules/HashManager.lua.

[v1.0.2] (2026-07-09) - Fix: Auctionator error when opening Enchanting with crafting takeover on

Bug Fixes

  • Opening Enchanting with crafting takeover enabled threw an Auctionator error (attempt to index global 'CraftReagent1'). On Vanilla/TBC the Enchanting "Craft" window's frames (CraftReagent1..N) are created by the load-on-demand Blizzard_CraftUI addon, and the only thing that lazy-loads it is UIParent's built-in CRAFT_SHOW handler. When crafting takeover is on, TOGPM unregisters that handler so Blizzard's window doesn't pop over its own tab — which also removed the loader, so any other addon that hooks CRAFT_SHOW (Auctionator's CraftShown) ran while those globals were still nil and errored. TOGPM now assumes UIParent's load responsibility: it loads Blizzard_CraftUI itself so the frames exist for co-installed listeners, then unregisters CraftFrame's own CRAFT_SHOW so it still won't auto-pop a second window. Gated on Vanilla/TBC (HAS_CRAFT_WINDOW) and the takeover path only, so default hands-off users and Wrath+ clients are unaffected; the escape-to-Blizzard button still works (it summons the window via UIParent_OnEvent, which doesn't depend on that registration). Location: Modules/Crafting/CraftingEngine.lua.

[v1.0.1] (2026-07-05) - Specialization detection fixes, full guild coverage & crafter search

New Features

  • Search the Professions tab by crafter name. Typing a guild member's name into the recipe search box now lists every recipe that player can make — crafter names are folded into the same cached, per-keystroke filter, so there's no added cost, and it honours the current view/visibility. Set the Profession dropdown to All and type a name to audit "what can this character craft?" across every profession at once. Location: GUI/BrowserTab.lua, Locale/enUS.lua.

Bug Fixes

  • Alchemy specializations were detected with the wrong spell IDs. The spec scan checked 28682 / 28683 — which are actually "Combustion" and "Leap", not alchemy specs — so Transmutation and Potion Masters were never detected and fell to Unspecialized (only Elixir Master, 28677, happened to be correct). Corrected to the DBC-verified 28672 / 28675 / 28677 (Transmutation / Potion / Elixir Master) in the spec scan, the Guild tab's canonical spec list, and the transmute-proc cooldown table (which was keying the every-transmute proc on "Leap"). Location: Scanner.lua, GUI/GuildTab.lua, Data/CooldownIds.lua.
  • Mooncloth Tailoring could never be detected. The Tailoring spec list used 26802"Detect Amore", a Love-is-in-the-Air holiday spell — where Mooncloth's real id 26798 belongs, so Mooncloth tailors were never categorised, and anyone who owned the holiday spell risked a bogus tag. Fixed to 26797 / 26798 / 26801 (Spellfire / Mooncloth / Shadoweave) in the spec scan, the Guild tab list, the recipe-tagging pipeline, and the cloth-cooldown table. Location: Scanner.lua, GUI/GuildTab.lua, Data/CooldownIds.lua, tools/build_authoritative_data.py.
  • Cloth-cooldown "guaranteed 2×" bonus was mapped to the wrong specs. Primal Mooncloth's bonus was keyed to Spellfire's spec id and Spellcloth's to the holiday spell, so Mooncloth tailors got no bonus indicator and Spellfire's never lit up. Each cloth cooldown now maps to its own spec — Mooncloth → Primal Mooncloth, Spellfire → Spellcloth, Shadoweave → Shadowcloth. Location: Data/CooldownIds.lua.
  • Archaeology didn't appear on the Guild tab. The skill-name → profession map was missing Archaeology (794), so the gathering scan never recorded it on Cata/MoP clients. Added. Location: Scanner.lua.
  • Skill levels rendered an impossible cap like "375/300". A missing or stale skillMax defaulted to the hard-coded Vanilla cap of 300, which then synced guild-wide and displayed below the actual rank on TBC/Wrath. The stored fallback is now the rank (a cap can't be below the current skill), and the Guild tab shows every skill against this expansion's real cap (addon.SKILL_CAP: 300/375/450/525/600 for Vanilla…MoP) instead of the per-character value. Location: Scanner.lua, Compat.lua, GUI/GuildTab.lua.
  • Gathering skills were missed when a skill header was collapsed. GetSkillLineInfo hides a collapsed header's children, so a character with "Professions" or "Secondary Skills" collapsed dropped Skinning / Herbalism / Fishing / Archaeology from the scan. It now expands all headers first (ExpandSkillHeader), guarded against the re-entrant SKILL_LINES_CHANGED that expansion fires. Location: Scanner.lua.

Improvements

  • The Guild tab now lists EVERY profession, even at 0. Previously only the gathering professions were force-shown; now every profession available on this client appears with a headcount (0 when nobody has it), so the guild-wide "who has what" view surfaces crafting-profession coverage gaps too. Location: GUI/GuildTab.lua.
  • TBC/Wrath specialization recipes now attribute their crafters (requires ProfessionDB v1.2.2). The recipe data only tagged Vanilla-era spec plans; TBC and Wrath moved the spec gate onto the crafted item, so ~65 TBC and ~72 Wrath spec recipes — Lionheart weapons, Dragonscale/Elemental/Tribal leatherworking, Gnomish/Goblin engineering, Mooncloth tailoring — shipped untagged and their crafters showed as Unspecialized. The ProfessionDB generator now reads the crafted item's requirement as well. Update to ProfessionDB v1.2.2 to pick this up. Location: tools/build_authoritative_data.py (ProfessionDB).

[v1.0.0] (2026-07-01) - First full release: Guild professions tab, skill-tier filter & Cooldowns reagent overhaul

TOG Profession Master reaches 1.0 — its first full release. A milestone worth marking, and still growing. A new Guild tab drills from a profession headcount down to each specialization and the individual crafters — with their skill levels — and now tracks the gathering professions (Herbalism, Skinning, Fishing) that have no window to open; a skill-tier filter declutters the Professions browser; the Cooldowns tab gets a batch of reagent improvements — every reagent shown for multi-reagent crafts (Brilliant Glass, the specialty cloths), an at-a-glance "you already have this" highlight, a tidier reagent popup, and a fix for the supply-mail button that looked up the wrong item; and under the hood, profession-cooldown sync was overhauled to kill a convergence bug that churned CPU and redraws on busy guilds.

New Features

  • New Guild tab — the guild's crafting capacity, from headcount down to the people. Tallies how many guild characters have each profession, then lets you drill in: click a profession to expand its specializations, then a specialization to list the characters in it. Every specialization is shown even at 0, so coverage gaps are obvious ("nobody's an Axesmith"). Names are coloured exactly like the Professions tab — white online, grey offline, brand-colour You — with an online alt surfacing its offline main, and each name carries that character's skill level (You (300/300)). A character's spec comes from their recorded specialization or is inferred from the spec-only recipes they're known to craft, so even guildmates who don't run the addon get categorised. Counts are the union of synced skills and known recipe crafters, the tracked-character total sits in the status bar, column headers and a dedicated help (i) tooltip explain the view, and it's localized. Location: GUI/GuildTab.lua, Scanner.lua, GUI/MainWindow.lua.
  • Gathering professions are now tracked — see who herbs, skins, and fishes. Herbalism, Skinning, Fishing (and Archaeology on Cata/MoP) have no trade-skill window to open, so the recipe scan never saw them. They're now read from the character's skill lines (GetSkillLineInfoGetProfessions is a no-op on Classic Era) and shown on the Guild tab, always listed even at 0 so the gap is visible. The skill levels sync guild-wide through a new owner-authoritative skills: leaf that mirrors the cooldown / alt-group leaves — you record your own, it's relayed by anyone — so once a guildmate offers theirs up, everyone sees it. Mining stays on its existing Smelting path. Location: Scanner.lua, Modules/HashManager.lua, GUI/GuildTab.lua.
  • Missing Recipes now has Personal / Guild sub-tabs. The Missing Recipes tab gained a sub-tab switch: My Character (the existing per-character "what am I missing?" view) and Guild — a guild-wide list of recipes nobody in the guild knows, filterable by profession (or All Professions, which tags each row with its profession) and searchable, so officers can spot coverage gaps at a glance. The Guild view drops the character dropdown and the "Can learn now" filter (both are per-character) and computes "missing" as any recipe in the shipped universe with zero crafters across the whole guild. Your sub-tab choice persists across sessions. Location: GUI/MissingRecipesTab.lua.
  • Missing Recipes hides recipes your specialization can never learn. A Tribal leatherworker no longer sees Elemental or Dragonscale patterns; an Armorsmith no longer sees Weaponsmith plans, etc. Recipe scrolls carry their required spec in the game data (ItemSparse.RequiredAbility), which the ProfessionDB pipeline now ships as a requiredSpec field; the My Character view compares it against your recorded spec and hides mismatches (guild view and "Show All" keep everything). Understands the Blacksmithing sub-spec hierarchy — a Swordsmith still sees general Weaponsmith recipes. Covers Leatherworking, Blacksmithing, Engineering, and Tailoring across Vanilla/TBC/Wrath (specializations were removed in Cata). Requires the updated ProfessionDB (regenerated data + LibProfessionDB load of the new field). Location: GUI/MissingRecipesTab.lua, Scanner.lua, tools/build_authoritative_data.py (ProfessionDB).
  • Filter the Professions browser by skill tier. A new multi-select Skill tier dropdown in the Professions toolbar shows only recipes in the trainer ranks you tick (Apprentice / Journeyman / Expert / Artisan, plus Master / Grand Master / Illustrious / Zen Master on later clients) — untick the lower tiers to hide their recipes and cut the clutter. Tick multiple tiers (the menu stays open), with Select All / Clear All at the bottom; only tiers your client can reach are offered, your selection persists across sessions, and recipes with no known skill level always stay visible. Localized in all shipped languages. Location: GUI/BrowserTab.lua.
  • Cooldowns tab now shows every reagent for multi-reagent crafts. Brilliant Glass (six gems) and Primal Mooncloth / Spellcloth / Shadowcloth (three reagents each) previously showed only one reagent — or none — and no mail button. They now render as click-to-expand [+] rows whose popup lists every reagent with its own [AH] / [Bank] / mail controls, matching how transmutes already work. Prismatic Sphere and Void Sphere, which showed no reagent at all, now display theirs. Location: Data/CooldownIds.lua, GUI/CooldownsTab.lua.
  • Reagents you already have are highlighted. In the Cooldowns reagent popup, a reagent name turns white (from grey) when you're holding enough of it in your bags to fill the supply mail — so you can see at a glance which reagents you can send. Location: GUI/CooldownsTab.lua.

Bug Fixes

  • Profession-cooldown sync no longer churns the client (or redraws the UI) every second. On active guilds, cooldown sync never converged: cooldowns are stored as absolute expiry timestamps but were transmitted as a countdown and rebuilt against each receiver's clock, so every relay hop added the transmission latency, the value ratcheted upward, its hash changed, and the peer-to-peer negotiation re-fired forever — pegging CPU and tearing down / rebuilding the open tab about once a second. Cooldowns are now owner-authoritative: the owner ships the absolute expiry plus its own hash, and receivers adopt both verbatim instead of reconstructing them, so the hash converges in one exchange and stays quiet. Legacy-format cooldowns from un-updated peers are dropped rather than merged. Location: Scanner.lua, Modules/HashManager.lua.
  • Your own cooldown starting/finishing now refreshes the Cooldowns tab. Using a transmute or Salt Shaker committed the cooldown to the database but raised no UI signal — the constant sync churn used to redraw the tab anyway, so it went unnoticed; with that churn fixed, the tab stopped updating until the next guild event. A local cooldown scan that actually changes a value now fires a cooldowns-scoped refresh, plus a short follow-up re-check for a cooldown the client reports a beat late. Location: Scanner.lua.
  • Salt Shaker cooldown detected on more Classic builds. The scan called only C_Container.GetItemCooldown; where that's absent or returns nothing it fell through to showing "Ready". It now falls back on the result to the global GetItemCooldown. Location: Scanner.lua.
  • Search fields keep focus while guild data streams in — fixed for every tab. The earlier fix relied on GetCurrentKeyBoardFocus, which isn't available on all Classic flavors, so it silently no-op'd (the Professions search still went "unbound"). The focus-aware refresh deferral now lives in the shared StyleSearchBox helper that every search box already uses, keys off the reliable EditBox:HasFocus(), and resets the deferral counter on each keystroke so continuous typing is never interrupted. Covers Professions, Missing Recipes, Crafting, and Profit Planner. Location: GUI/SharedWidgets.lua, GUI/MainWindow.lua.
  • Missing Recipes recipe names now colour by the crafted item's quality. Names were tinted by the recipe-scroll item's own quality, so e.g. "Pattern: Molten Helm" (a common/white scroll that produces an epic helm) showed white instead of purple, and uncached scrolls showed white inconsistently. They now use the produced item's quality — matching the crafting window — falling back to the scroll's quality for recipes with no crafted item (enchants). Location: GUI/MissingRecipesTab.lua.
  • Missing Recipes no longer lists recipes you (or the guild) already know. Recipes whose spell ID is above 25000 — e.g. several patch-added Vanilla cooking recipes like Smoked Sagefish (spell 25704) — were wrongly appearing in the missing list even when known. Root cause: the Classic-Era "untagged post-Vanilla" existence gate (spellId > 25000) sat inside the same elseif chain as the "already known" filter; when a recipe passed that gate (the recipe genuinely exists), the chain short-circuited and the known-filter was never evaluated. The known-filter is now an additive guard that always runs for recipes that survive the version gates — the same fix pattern the "Can learn now" filter already used. Location: GUI/MissingRecipesTab.lua.
  • Search fields no longer lose focus mid-typing during guild sync. On a busy guild, incoming sync fired a window refresh that rebuilt the active tab's toolbar — including its search box — dropping keyboard focus so further keystrokes fell through to keybinds ("unbound"). The refresh now defers while a search field in the window is focused (the same way it already defers while a dropdown pullout is open), so typing isn't interrupted. Location: GUI/MainWindow.lua.
  • Fixed the cooldown reagent-mail button reporting "You have no item:1 in your bags." The mail button inside the reagent-breakdown popup called the mail helper with a missing argument, which shifted the reagent's item ID out of position — so it looked up item "1" (which nobody has) instead of the real reagent. It now passes the full argument set and attaches the correct reagent. This is distinct from the v0.10.10 C_Container bag-scan fix — different button, different cause. Location: GUI/CooldownsTab.lua.
  • Missing Recipes stops re-rendering several times a second (and the scroll no longer creeps to the bottom). The list rendered item names and quality colours through WoW's asynchronous GetItemInfo, which returns nothing until the client caches each item — so every row stayed "pending", every cache-fill event (from any addon) fired another full re-render several times a second, and that churn slowly walked the scroll position down to the bottom while colours flickered white. Names and quality colours now come from the synchronous, offline LibItemDB (the ItemDB dependency) on the first paint, with GetItemInfo only as a fallback for items LibItemDB doesn't carry — so a drawn list is stable immediately, doesn't refresh on unrelated item loads, holds its scroll position, and shows the correct quality colour right away. Location: GUI/MissingRecipesTab.lua.
  • Basic Campfire no longer appears as a Cooking "recipe." It's the learned campfire ability (it produces no item), not a craftable, so it never belonged in the recipe list. It's now excluded from the shipped data in every game version, and LibProfessionDB was hardened so a recipe present only in a locale name file (but not the structural _core set) is no longer resurrected as a dataless phantom — which is why it kept showing with a blank skill even after the data drop. Requires the updated ProfessionDB. Location: tools/build_authoritative_data.py, LibProfessionDB-1.0.lua (ProfessionDB).
  • Crystal Infused Bandage no longer shows on Classic Era. This is a TBC First Aid recipe (skill 300→360, Netherweave-based); the Anniversary Classic client ships its data even though it isn't learnable in Vanilla, so it leaked into the Classic missing list. It's now excluded from the Vanilla data only — TBC and later clients still get it. Requires the updated ProfessionDB. Location: tools/build_authoritative_data.py (ProfessionDB).

Improvements

  • Redraws are scoped to what actually changed. GUILD_DATA_UPDATED now carries a change-scope (cooldowns / recipes / skills / alt-groups), and each tab skips refreshes it doesn't render — so a cooldown sync no longer tears down and rebuilds the thousands-of-rows recipe Browser, and a skills sync only touches the Guild tab. Tab switches still always redraw from live data, so nothing goes stale. Location: GUI/MainWindow.lua, GUI/BrowserTab.lua, Scanner.lua.
  • Cooldown sync send-storm curbed. Un-updated peers were pulling every cooldown leaf each round (and being served all of them). The request and serve paths now gate cooldown leaves on a delivered timestamp, so a converged client neither pulls stale copies nor re-serves them, and old clients that can't parse the new format are no longer fed a stream of leaves. Location: Scanner.lua.
  • Sync Log gets a Pause button. Settings → General → Sync Log. Freezes the live view so a text selection survives long enough to copy; entries keep accumulating while paused and the view catches up on unpause, with a "(paused — N buffered)" note in the title. Location: GUI/Settings.lua.
  • Spec breakdowns fill in for the whole guild, not just addon users. The Guild tab now infers a crafter's specialization from the spec-only recipes they're known to craft (the requiredSpec the Missing tab already uses), so a Tribal leatherworker who's never run the addon still lands under Tribal instead of Unspecialized. Location: GUI/GuildTab.lua.
  • Tidier reagent popup on the Cooldowns tab. Removed the redundant per-reagent status column (readiness is already shown on the main cooldown row) and widened the reagent column so long names like "Bolt of Imbued Netherweave" sit on one line instead of wrapping — all within the existing popup width. Location: GUI/CooldownsTab.lua.
  • Profession-spec detection now covers Leatherworking & Blacksmithing. The spec scan learned the Vanilla LW specs (Dragonscale / Elemental / Tribal) and BS specs (Armorsmith / Weaponsmith → Swordsmith / Hammersmith / Axesmith) that feed the new Guild tab's breakdown. Harmless on Cata+, where these specializations were removed (the scan simply finds none). Location: Scanner.lua.
  • Cooldown group rows keep their proper icon. Reworked the cooldown-row icon resolution so multi-reagent cloth cooldowns rendered as [+] group rows still show the produced-bolt icon override instead of Blizzard's generic net/cloth spell icon. Location: GUI/CooldownsTab.lua.
  • Migrated to DeltaSync's multi-host API (requires DeltaSync v4.0.0+). DeltaSync v4.0.0 (LibStub MINOR 15) made the library multi-host: each addon now creates its own isolated sync host instead of sharing one singleton, so TOGPM no longer clobbers — or gets clobbered by — another DeltaSync-using addon in the same client (previously whichever addon initialized last owned the shared namespace). TOGPM now creates its host via NewHost and routes everything through it. This requires the standalone DeltaSync addon at v4.0.0 or newer — on an older DeltaSync, TOGPM's guild sync stays disabled (it will not silently fall back to the clobbering singleton path) until you update the dependency. The wire format is unchanged, so a v15 host still syncs with peers exactly as before. A new /togpm dsstatus command reports the host's namespace, the DeltaSync MINOR, the comm prefixes and P2P state, so you can confirm the multi-host setup at a glance (and prove isolation by running it next to another DeltaSync addon's status). Location: Scanner.lua.

[v0.10.10] (2026-06-30) - Live-refresh fixes, leaner recipe sync & a live Sync Log

Follow-up to v0.10.9's guild-mode work. With sync finally flowing on private servers, testing surfaced that incoming guild data was being ingested into the database but the Professions tab didn't show it until a /reload — switching tabs didn't help either. The data was live in memory the whole time; the bugs were all in the Browser's recipe-list cache (a performance snapshot the Professions tab renders from, separate from the live data the Cooldowns/Missing Recipes tabs read directly). Sync itself was never the problem. This release also makes recipe sync much leaner with an optional per-player drill-down, turns the Sync Log into a live, copyable diagnostic, and adds pickers for the crafter-alert sound and on-screen visual.

New Features

  • Sync Log is live, persistent, and copyable. Settings → General → Sync Log. The window now updates in real time as sync events happen — no more clicking to refresh — remembers its position and size across /reload, and its contents are selectable and copyable (read-only) for pasting into bug reports. Entries are also far more informative: sends now show what actually went out — a crafter transfer names its profession (crafters:165 (Leatherworking)) instead of a bare id, and a per-player transfer names the exact player — instead of a meaningless "0 B"; the hash-offer and sub-hash sends are logged for the first time; and a dedicated size column reports the real byte size of every message — sends included (which previously reported nothing) — beside the payload type, all under a column-header row (Time / Event / Size / Peer / Detail) above the live box. The send-size figures specifically require DeltaSync v3.2.1+ (which now returns the serialized size to the host) — that dependency is only for those send numbers in the Sync Log; on an older DeltaSync they show "—" and everything else in the addon behaves identically. Location: Modules/SyncLog.lua, GUI/Settings.lua, Scanner.lua.

  • Choose your crafter-alert sound and visual. Settings → Alerts. Two new dropdowns let you pick which sound (Chime, Ready Check, Raid Warning, Auction Bell, Whisper, Map Ping) and which on-screen visual (full-screen flash in gold / red / blue, a raid-warning banner, top-center error text, or a taskbar flash for when you're alt-tabbed) fire when a guild crafter comes online. Selecting an option previews it, and each dropdown greys out while its matching suppress toggle is on. The old "Suppress screen flash" toggle is renamed "Suppress alert visual" to match. Localized. Location: GUI/Settings.lua, TOGProfessionMaster.lua.

  • Jump from the Profit Planner straight to the Crafting tab. Clicking a row in the Profit Planner now opens that recipe directly in the Crafting tab, ready to craft (shift-click still links the item in chat — the tooltip spells out both). If you've hidden the Crafting tab (Settings → Crafting), it tells you how to bring it back instead of silently doing nothing. Location: GUI/AHProfitTab.lua, GUI/CraftingTab.lua.

Bug Fixes

  • Professions tab now updates live as guild data arrives. Two root causes, both fixed: (1) the background cache rewarm rebuilt the recipe list but never re-rendered the open tab, so freshly-synced recipes sat in the cache unseen; the rewarm now redraws the visible tab the instant the viewed profession's list is rebuilt — and does so without losing your scroll position. (2) The rewarm's debounce timer was starved on actively-syncing realms: every data update reset a 10s timer, so when updates arrived faster than that (a busy guild, or a private server catching up), the rebuild never fired at all — which is why even switching tabs showed nothing and only a /reload worked. The debounce is now capped so a rebuild always lands within ~10s, even under continuous traffic. Location: GUI/BrowserTab.lua.
  • Purge buttons now clear the Professions display immediately. "Purge all guild data" and "Purge my character data" did clear the database (Cooldowns and Missing Recipes emptied instantly), but the Professions tab re-rendered stale rows because its recipe cache isn't keyed on the database and wasn't being invalidated. The cache is now wiped on purge before the refresh, so the tab reflects the empty state at once. Location: GUI/BrowserTab.lua, GUI/Settings.lua.
  • "All Professions" view now reflects your own freshly-scanned professions. Scanning a profession locally (opening its window) committed the recipes to the database but raised no UI-refresh signal — only received sync did — so the cached "All Professions" list stayed stale. Most visible right after a purge: the All Professions filter showed "no data yet" while selecting a specific profession showed the recipes (that filter rebuilt fresh on a cache miss). Now a local scan that actually changes your recipe set refreshes the UI on the same path a peer update uses, gated on a real change so crafting's repeated scan events don't churn the open tab. Location: Scanner.lua.
  • Professions tab now reflects members going online and offline. Each crafter's online status is baked into the recipe-list cache when it's built, so a roster change alone didn't update the tab — someone logging off kept showing as online until the next data change or /reload. Roster online/offline transitions now invalidate and refresh the tab (debounced, and only while it's on screen so login/logout bursts don't thrash the pre-warm), so status reflects within ~2s. Location: GUI/BrowserTab.lua.
  • Cooldown supply-mail now finds your reagents on older TBC / Classic clients. On builds without C_Container (older TBC and some private servers), the legacy GetContainerItemInfo returns only 7 values — no item ID — so the mail helper's bag scan matched nothing and reported "you have no <reagent> in your bags" even when you had it. (The reagent count shown on the row uses GetItemCount and was correct all along, which is why it looked contradictory.) The compat shim now derives the item ID from the item link when the API omits it, so .itemID is always populated — fixing the mail scan on every supported client (and any other bag lookup that keys on item ID). Location: Compat.lua. Thanks to Bzum for the report.

Improvements

  • Leaner recipe sync — per-player drill-down. Previously, when anyone in the guild changed a recipe, peers re-fetched that entire profession's crafter set — on a large guild a single transfer of ~90 KB. Recipe sync now negotiates a per-player hash list under each profession and pulls only the players whose recipes actually differ, newest-scanned first and each as its own small message — so a one-person, one-recipe change ships just that player's recipes instead of the whole profession, the data populates incrementally, and traffic interleaves far better on congested or slow servers. It's fully additive and backward-compatible: feature-detected, so it engages only between updated clients and falls back to the existing whole-profession path for anyone else — no coordinated guild-wide update required, no wire-format break, no namespace bump. Location: Scanner.lua, Modules/HashManager.lua.
  • Consistent TSM setting labels. The two TradeSkillMaster pricing toggles now both read "TSM" — "Use TSM pricing" and "Use TSM App Helper pricing" — instead of one spelling out "TradeSkillMaster" and the other abbreviating. Location: GUI/Settings.lua.
  • Crafter column fills the available width. The Professions tab's crafter summary used to show a fixed "2 names + N more"; it now fits as many crafter names as the column's current width allows, with a greyed "+N" for the rest, and re-fits live when you drag the window wider or narrower. Location: GUI/BrowserTab.lua.

[v0.10.9] (2026-06-29) - Guild-only sync mode for private servers

Resolves the "nothing syncs on Whitemane" investigation. Diagnostics confirmed that Whitemane (and similar emulated Cataclysm servers) deliver addon messages over GUILD fine but silently drop them over WHISPER — and TOGPM's sync is hash-then-fetch, where the fetch request rides WHISPER. Hash offers went out on guild chat and arrived, but the follow-up data request never landed, so members kept broadcasting while nothing ever came back. This release adds an opt-in toggle that reroutes the whisper-based sync traffic onto the guild channel.

New Features

  • Guild-only sync modeSettings → General → Sync. For private/emulated servers that don't deliver addon whispers (e.g. Whitemane "Maelstrom"). When enabled, DeltaSync's directed channels (QUERY / RESPONSE / DELTA / OFFER / HANDSHAKE) are rerouted from WHISPER to GUILD, with each message stamped for its intended recipient so other members ignore it — directed delivery over a broadcast transport. Opt-in and OFF by default, so nothing changes on retail/Classic Era or any server where whispers work. The setting is realm-scoped (enable it once, all your alts on that realm inherit it) and feature-detected — the toggle is hidden unless the installed DeltaSync supports guild-mode (requires DeltaSync v3.2.0+). Cross-guild sharing auto-disables while it's on (it can't work on a whisper-dead server anyway). Coordinate per guild: on an affected realm, everyone should enable it — directed traffic only reaches members who also have it on. Localized in all shipped languages. Location: Scanner.lua, GUI/Settings.lua, TOGProfessionMaster.lua.

Notes

  • Diagnosing this used /togpm commtest (from v0.10.7/0.10.8) plus raw two-player tests, which is how we isolated WHISPER-drop-but-GUILD-works. If guild sync ever looks dead on a server, that command remains the fastest way to see which addon-message channels the server actually relays.

[v0.10.8] (2026-06-28) - Fix: commtest missing from per-flavor TOCs

Follow-up to v0.10.7. The new diagnostic shipped in only one of the addon's five TOC files, so it never loaded on the very clients it was built for. This release wires it into all of them.

Bug Fixes

  • /togpm commtest now loads on TBC / Wrath / Cata / MoP clients. v0.10.7 added Modules/CommTest.lua to only the base TOGProfessionMaster.toc. WoW loads the flavor-specific TOC when one exists, so on TBC/Wrath/Cata/Mists clients — including privately-hosted Cataclysm servers like Whitemane "Maelstrom" — the file was never in the load list, RunCommTest stayed undefined, and /togpm commtest silently fell through to the help menu (the command's help line still showed because that lives in the always-loaded main file, which is what made it look present). Added the line to all four flavor TOCs (_TBC, _Wrath, _Cata, _Mists) so the diagnostic is available on every supported version. Location: TOGProfessionMaster_TBC.toc, TOGProfessionMaster_Wrath.toc, TOGProfessionMaster_Cata.toc, TOGProfessionMaster_Mists.toc.

Notes

  • Whitemane runs a modern Classic-engine client, not the original 4.3.4 client. Diagnostics surfaced that its addon-message API is C_ChatInfo.SendAddonMessage (the bare global SendAddonMessage is nil), and C_ChatInfo.SendAddonMessage returns a SendAddonMessageResult code. The commtest probe already handles this via its C_ChatInfo shim; this corrects the v0.10.7 note that assumed the original 4.3.4 client.

[v0.10.7] (2026-06-27) - Private-server addon-comm diagnostics

Groundwork for running TOGPM on privately-hosted Cataclysm 4.3.4 (build 15595) servers such as Whitemane "Maelstrom", where some emulation cores leave the Cataclysm per-channel addon opcodes (CMSG_MESSAGECHAT_ADDON_GUILD, …) unhandled and silently drop guild addon traffic — which makes the sync stack look dead even though the addon loads fine. This release adds a diagnostic to pinpoint exactly which addon-message channels a given server relays. No change to existing behavior — it's a new, opt-in command; nothing in the live sync path was touched.

New Features

  • /togpm commtest [name] — addon-channel diagnostic probe. Fires a tagged addon message on every distribution (GUILD, WHISPER→self, a temporary CHANNEL, and PARTY/RAID when grouped), listens briefly for which ones echo back, then prints a copy-pasteable verdict including the client build number. The GUILD self-echo is the decisive test and works solo — on a healthy core you receive your own guild addon message; if the server drops guild addon traffic you get nothing. It probes both the raw SendAddonMessage path and your actual AceComm + AceCommQueue send path, so a server-side drop is distinguishable from a wrapper/chunking issue. Pass a player name to add a real two-player WHISPER probe. Run it on your home realm (control) and on the private server (test); the diff identifies the broken channel and which fallback (WHISPER/CHANNEL) is viable. Cross-version safe — uses C_ChatInfo where present, bare globals on 4.3.4, registers prefixes only where that API exists, and deliberately uses an OnUpdate timer instead of C_Timer (which the original 4.3.4 client lacks). Location: Modules/CommTest.lua.

[v0.10.6] (2026-06-12) - Cross-guild profession sharing & crafting hands-off

First stable release of cross-guild profession sharing (matured across the v0.10.x beta line) plus new crafting-window controls. Fully backward-compatible — single-guild use is unchanged, and cross-guild sharing stays completely dormant until you configure an allied guild. Cross-guild requires DeltaSync v3.1.0+ and GuildRoster v6+ (both install/update automatically).

New Features

  • Cross-guild profession sharing. Share recipe and crafter data between two socially-linked guilds that can't reach each other over the normal guild addon channel. An officer or the guild leader sets the allied guild under Settings → Cross-Guild (the list is guild-wide and members see it read-only); once both guilds list each other (it's bilateral — a one-sided config does nothing, and a guild you don't list can't pull or inject anything), their crafters become visible to each other. The allied-guild list propagates across your guild automatically, allied rosters are shared so every member sees the crafters (not just whoever pulled them), and a live Diagnostics panel plus /togpm xgdiag show exactly what's synced. With no allied guild configured, the addon does nothing cross-guild — purely local. Location: Scanner.lua, TOGProfessionMaster.lua, GUI/Settings.lua.
  • Crafting hands-off modeSettings → Crafting, on by default. TOGPM no longer opens or forces a crafting window when you open a profession; Blizzard's window (or another addon such as TSM / Skillet) owns it, so no second window appears beside it. The TOGPM toggle button still rides on the Blizzard window when it's shown, and the Crafting tab stays available from the main window. If you previously enabled "Open the TOGPM Crafting tab automatically," untick this to keep that behavior. Requires /reload. Location: Modules/Crafting/CraftingEngine.lua.
  • Hide the Crafting tabSettings → Crafting. Removes the Crafting tab from the main window entirely, for those who craft with another addon. Requires /reload. Location: GUI/MainWindow.lua.

Bug Fixes

  • Browser performance overhaul — instant tab open and instant search. The recipe list (the expensive part: DB lookups, the per-crafter visibility gate, item tooltip text) is now built once and cached, and pre-warmed in the background after login — sliced across frames so it's invisible — so opening the Professions tab and switching professions are instant instead of taking a second or more. Searching just filters the cache (no rebuild, no per-keystroke freeze). This removes the multi-second tab-load and search stutter that had grown as cross-guild sharing enlarged the crafter sets. Location: GUI/BrowserTab.lua, TOGProfessionMaster.lua.

Improvements

  • Settings window is tabbed (General / Cross-Guild) and remembers its position, size, and selected tab across /reload.
  • Roster engine runs on the standalone LibGuildRoster-1.0 (GuildRoster addon), an auto-installed dependency — the foundation for cross-guild support.

[v0.10.5-beta] (2026-06-09) - Roster propagation & Browser search fix (beta)

Beta build. Completes the cross-guild "anyone is a transfer point" model. Backward-compatible; single-guild operation is unchanged. Requires DeltaSync v3.1.0+ and GuildRoster v6+.

New Features

  • Sister-roster propagation (Part B). The visibility gate keeps an allied crafter only if their character is in a sister roster you hold — so a member who configured an allied guild but never pulled its roster would purge every relayed crafter. Now a member who holds a sister roster broadcasts it on the guild channel and everyone applies it, so the whole guild sees allied crafters, not just the puller. A "recently-seen by hash" suppression keeps it to ~one broadcaster per interval (duty rotates if that member logs off), fresh pulls propagate within ~10s, and receipt is gated by the allied-guild list (an unlisted guild's roster is never accepted). This is the piece that makes federated members churn-free without each pulling. Location: TOGProfessionMaster.lua, Scanner.lua.

Bug Fixes

  • Browser (recipe) search no longer freezes on each keystroke. Two causes: the search box rebuilt the whole list synchronously on every character (no debounce — the fix that was already in Missing Recipes had never been applied here), and the per-recipe crafter list was built before the search filter ran, so every keystroke walked every recipe's crafter set — which grew heavier as cross-guild sharing added crafters. The search now debounces (~200ms) and applies the cheap client-version + name/effect filters before the expensive crafter-list build, so excluded recipes are skipped. Location: GUI/BrowserTab.lua.

[v0.10.4-beta] (2026-06-09) - Cross-guild bilateral federation gate (beta)

Beta build. Security model for cross-guild sharing. Backward-compatible; single-guild operation is unchanged. Requires DeltaSync v3.1.0+ and GuildRoster v6+.

New Features

  • Bilateral federation — both guilds must list each other. Cross-guild data now only flows when both sides have configured each other as allied guilds. A one-sided config does nothing; a stranger, an accidental config, or a malicious puller from a guild you don't list gets nothing. Enforced by gating every direction:
    • Serve gate: we hand over our data only if we list the requester's guild and the request proves they list us (it carries their guild key + sister-guild set). Plus an anti-spoof check — once we hold the requester's guild roster, the requester must actually be a member of it.
    • Accept gate: we ingest cross-guild data only from a guild on our list.
    • Merge gate: every crafter is kept only if its guild tag is our home, our own alts, or a listed sister — so data for an unlisted guild is dropped even when it arrives relayed inside a home-guild broadcast, and is never re-relayed.
    • Roster gate: we persist/re-feed a sister roster only for a guild we list; a de-configured guild can't be resurrected on login.
    • De-configure cleanup: removing a guild from the list (or receiving a federated removal) tears down its roster and stops its data from displaying.
  • No allied guilds configured → purely local operation. With an empty list the permitted-tag set collapses to home + your own alts, so the addon does nothing cross-guild at all. Location: Scanner.lua, TOGProfessionMaster.lua.

[v0.10.3-beta] (2026-06-09) - Cross-guild config propagation & churn fix (beta)

Beta build. Continues the cross-guild work. Backward-compatible and dormant until an allied guild is configured. Requires DeltaSync v3.1.0+ and GuildRoster v6+. For the relay/anti-churn behavior, all participating beta players need this build — a peer on an older build re-broadcasts relayed crafters with the guild tag stripped.

New Features

  • Allied-guild config now propagates across your guild. The allied-guild list you set under Settings → Cross-Guild is broadcast to the rest of your guild (on change, every ~12 minutes, and via a new Sync now button), so every member participates — essential once editing becomes officer-only. Conflict resolution is last-writer-wins by the timestamp captured when the list was set; members re-broadcast what they hold without clobbering a newer edit. A member holding no config never broadcasts. Location: TOGProfessionMaster.lua, GUI/Settings.lua.

Bug Fixes

  • Relayed cross-guild crafters no longer churn under mixed addon versions. Because guild sync is gossip, a single guildmate on an older build (no per-crafter tags) re-broadcasts relayed sister crafters with the tag stripped; the receiver then re-stamped them as the home guild, the visibility gate purged them (not in the home roster), the relay re-added them, and the cycle repeated — counts visibly oscillating with no membership change. The merge now treats an explicit shipped tag as authoritative and never lets a tagless (older-build) broadcast overwrite an attribution it already holds, so a known cross-guild crafter stops flipping. The roster gate still decides visibility, so genuine leavers are still purged. Location: Scanner.lua (MergeCraftersIntoGdb).

Improvements

  • Diagnostics: orphan breakdown. The Cross-Guild diagnostics now show, per allied guild, how many crafters match the roster vs. are orphaned (with a few example keys), making it easy to tell roster-matching issues from genuine non-members. Location: GUI/Settings.lua.

[v0.10.2-beta] (2026-06-09) - Cross-guild relay attribution & diagnostics (beta)

Beta build. Continues the cross-guild work from v0.10.1-beta. Backward-compatible and dormant until an allied guild is configured. Requires DeltaSync v3.1.0+ and GuildRoster v6+ (both install/update automatically). For cross-guild relay to work, all participating players need this build.

New Features

  • Cross-guild diagnostics panel. A live, read-only status section on Settings → Cross-Guild showing dependency status (DeltaSync/RosterSync, LibGuildRoster/multi-roster), known rosters (home + each sister) with member counts, configured allied guilds, crafter counts per guild, and persisted allied rosters with feed timestamps. Plus a "Pull from player" field to trigger a pull without the slash command. Location: GUI/Settings.lua.
  • /togpm xgdiag command. Dumps the same cross-guild diagnostics to chat as plain lines for easy copy-paste when troubleshooting. Location: GUI/Settings.lua, TOGProfessionMaster.lua.

Bug Fixes

  • Cross-guild attribution now survives relays. The crafters sync leaf previously shipped bare character keys with no guild tag, on the assumption that every crafter in a payload belonged to the receiver's guild. In a confederation that assumption breaks: when a member who holds sister-guild data rebroadcasts it into their home guild, those sister crafters were re-stamped as the home guild and then hidden/purged by the visibility gate (not in the home roster). The leaf now ships each crafter's origin guild tag (already stored locally), so a relaying "bridge" member can propagate allied-guild crafters into their home guild with attribution intact. The leaf hash is unchanged (it already normalized values before hashing), so old and new clients stay in sync — but both ends of a relay must run this build for tags to be preserved. Location: Scanner.lua (BuildLeafPayload, MergeCraftersIntoGdb).

[v0.10.1-beta] (2026-06-09) - Cross-guild profession sharing (beta)

Beta build. This is the first testable cut of cross-guild sharing. It is fully backward-compatible — single-guild users are unaffected and the feature stays dormant until an allied guild is configured — but the cross-guild round-trip has not yet been validated with live players. Please report issues. Requires DeltaSync v3.1.0+ and GuildRoster v6+ (both install/update automatically).

New Features

  • Cross-guild profession sharing (beta). TOGPM can now share recipe/crafter data between two socially-linked guilds that can't reach each other over the normal guild addon channel. Configure an allied guild under Settings → Cross-Guild, and your crafters become discoverable to them (and theirs to you). Built on directed peer-to-peer whispers — no extra chat channel required. Location: GUI/Settings.lua, Scanner.lua, TOGProfessionMaster.lua.
  • Sister-guild roster tracking. Allied-guild rosters are pulled, persisted across /reload, and kept live via LibGuildRoster-1.0's multi-roster store, so allied crafters survive a session restart and stay correctly attributed to their own guild. Location: Scanner.lua (PersistSisterRoster, RefeedSisterRosters, OnSisterRosterUpdated).
  • /togpm pullroster <player> command. Manually pull an online allied-guild member's roster and crafter data on demand — the testing/bootstrap path before automatic discovery lands. Location: TOGProfessionMaster.lua.

Improvements

  • Settings window is now tabbed. Options are organized into General and Cross-Guild tabs (with room for officer/GM-only settings as features grow). Location: GUI/Settings.lua.
  • Each guild stays authoritative for its own membership. Cross-guild data exchange shares only a guild's own crafters and is opt-in (nothing is served until you configure an allied guild), so attribution can never bleed between guilds in a multi-guild confederation. Location: Scanner.lua (BuildFullGuildPayload).

Bug Fixes

  • Settings window position, size, and selected tab now persist across /reload. The standalone options window kept its geometry in a runtime-only table; it's now backed by SavedVariables (.char.frames.settings), matching the main window. Location: GUI/Settings.lua.

Known Limitations (beta)

  • Allied-guild crafters currently display as offline — live presence arrives with automatic /who discovery in a later beta.
  • Cooldown sharing is not yet cross-guild (crafter data only this build).
  • Allied data must be bootstrapped with /togpm pullroster <name>; automatic discovery is not in this build.

[v0.10.0] (2026-06-05) - Roster engine migration (LibGuildRoster-1.0)

Improvements

  • Roster engine migrated to LibGuildRoster-1.0. Guild-roster data (membership, online state, and the join/leave/online callbacks) now comes from the standalone LibGuildRoster-1.0 library (the GuildRoster addon) instead of the retired GuildCache-1.0. This is the foundation for upcoming cross-guild support. GuildRoster is now a dependency and installs automatically. Location: Scanner.lua, TOGProfessionMaster.lua, Tooltip.lua, GUI/BrowserTab.lua, GUI/CooldownsTab.lua.

Bug Fixes

  • Pending-purge sweep now re-validates before deleting. The timed sweep that removes departed members' data re-checks each flagged character against the live roster, deletes only confirmed non-members, and bails entirely if the roster isn't ready — closing any path where a legitimate guildmate's crafter data could be removed by a stale or early-login purge flag. Location: TOGProfessionMaster.lua (RunPendingPurge).
  • Visibility gate hardened against an unbuilt roster. A tag-matching crafter is shown (not flagged for purge) while the roster library is absent or still loading, restoring the pre-migration leniency and preventing early-login false-flags. Location: TOGProfessionMaster.lua (IsVisibleCrafter).

[v0.9.1] (2026-06-02) - Profit Planner UX, addon-wide sortable headers, search icons & persistence fixes

New Features

  • Profit Planner profession filter now supports multi-select. The Professions dropdown in the Profit tab is a native AceGUI multi-select dropdown (matching the Crafters/Sources dropdowns) that stays open while you tick multiple professions, with Select All / Clear All rows at the top. It defaults to all professions except Enchanting on first load (locale-safe addon.PROF_NAMES[333] lookup); once you clear the selection it stays cleared. Location: GUI/AHProfitTab.lua.

  • Missing Recipes columns are now sortable. Click the Recipe, Skill, or Sources headers to sort the list (click again to reverse); it defaults to skill ascending. Location: GUI/MissingRecipesTab.lua.

  • Tab selection now persists across /reload and addon reopen. The main window remembers which tab you were viewing (Browser, Cooldowns, Missing Recipes, Crafting, Profit Planner, Shopping List) in lastMainTab and restores it when you reopen the addon or reload the UI. Profit Planner's subtab (Live AH Profit vs Historical Profit) also persists independently in profitSubTab. Location: GUI/MainWindow.lua, GUI/AHProfitTab.lua, TOGProfessionMaster.lua.

Improvements

  • Profit Planner money columns sized to fit the window. Craft Cost, Sell Price, and Profit columns widened from 72px to 112px for readability while still fitting inside the minimized window (the right edge and scrollbar stay on-screen). Location: GUI/AHProfitTab.lua.

  • Sortable column headers now share one consistent style across the addon. On the Profit Planner, Cooldowns, Crafting, and Missing Recipes tabs, headers are centered over their columns with the up/down sort arrow placed beside the header text (measured via GetStringWidth) rather than at the column's far edge, plus a brand-colored glow that fades in on hover to signal "click to sort". Built as shared helpers (ConfigureCenteredHeaderIcon, MakeHeaderHoverGlow) so any future sortable header matches automatically. Location: GUI/SharedWidgets.lua, GUI/AHProfitTab.lua, GUI/CooldownsTab.lua, GUI/CraftingTab.lua, GUI/MissingRecipesTab.lua.

  • Profit Planner row count moved to the window status bar. "Rows: N" now follows the version string in the bottom status bar (e.g. v0.9.1 Rows: 300) instead of a mid-panel label, and reverts to just the version on other tabs. Location: GUI/AHProfitTab.lua, GUI/MainWindow.lua.

  • Profit Planner recipe tooltips now show full game item tooltips with profit details. Row hover anchors the game's native item tooltip (via GameTooltip:SetHyperlink) and appends a profit breakdown (source, craft cost, sell price, profit) below the standard item data, matching the enriched tooltip pattern from the Browser/Cooldowns tabs. Location: GUI/AHProfitTab.lua.

  • AH data source toggle changes now prompt for /reload in settings. Added a yellow warning label above the AH data source checkboxes (Auctionator, TSM, etc.) reminding users that toggling pricing integrations on/off requires a /reload to fully take effect, since external addon APIs are cached at load time. Location: GUI/Settings.lua.

  • Search boxes now use a magnifying-glass icon instead of a text label. The Profit Planner, Browser, Crafting, and Missing Recipes search fields show WoW's universal search icon inside the box (TSM-style) rather than a "Search recipes" label, via a shared StyleSearchBox helper. The Profit Planner "+ Profit only" checkbox also sits on the same center line as the dropdown controls. Location: GUI/SharedWidgets.lua, GUI/AHProfitTab.lua, GUI/BrowserTab.lua, GUI/CraftingTab.lua, GUI/MissingRecipesTab.lua.

Bug Fixes

  • Profit Planner empty profession filter now shows no rows. Previously, unchecking every profession (or using Clear All) displayed all rows and re-checked everything on the next redraw. An empty selection now matches no rows, and the all-except-Enchanting default applies only on first build. Location: GUI/AHProfitTab.lua.

  • Sort arrow no longer vanishes after the first sort/filter. The arrow texture was detached (SetParent(nil)) when its column went unsorted and never re-attached when shown again, so it disappeared once the sort column changed. ConfigureHeaderIcon now re-parents the texture before showing it, and sortable headers register OnRelease callbacks that clean up widget._sortIcon textures (Hide, SetParent(nil), ClearAllPoints, nil) so they don't bleed across pooled AceGUI widgets. Location: GUI/SharedWidgets.lua, GUI/AHProfitTab.lua, GUI/CooldownsTab.lua, GUI/CraftingTab.lua.

  • Missing Recipes skill sort no longer buries default recipes. Sorting by Skill now falls back to a recipe's orange difficulty tier when it has no explicit required-skill value (e.g. Basic Campfire), so low-skill recipes sort to the top instead of being treated as unknown and pushed to the bottom. Only recipes with neither value remain last. Location: GUI/MissingRecipesTab.lua.

  • Fixed memory leak: GameTooltip now hides when switching tabs. If the tooltip was showing for a Profit tab element when you switched tabs, it remained visible and anchored to the wrong position. DetachPool now calls GameTooltip:Hide() on tab detach. Location: GUI/AHProfitTab.lua.


[v0.9.0] (2026-06-01) - Shared global headers/sort across tabs

New Features

  • Added a dedicated Profit Planner tab. TOGPM now has a full AH profit-planning view for recipes known by your own characters, with separate Live AH Profit and Historical Profit subtabs so you can compare current listings against longer-horizon pricing without leaving the addon. The tab is wired into the main window as a first-class screen rather than a Crafting detail add-on, giving profit sorting/planning its own workspace. Location: GUI/AHProfitTab.lua, GUI/MainWindow.lua, Locale/enUS.lua.

Improvements

  • One shared global sortable-header path now drives all sortable tabs. Added shared helpers in addon.GUI.Sort for header-icon rendering and click-state transitions, then switched the Profit Planner, Cooldowns, and Crafting tab headers to use those globals instead of per-tab copies. Sort arrows now come from one reusable function (ConfigureHeaderIcon) with the same FGI-style texture behavior everywhere, and click transitions now use one shared state function (Next / NextOrNone) so tab behavior stays consistent as future columns are added. Location: GUI/SharedWidgets.lua, GUI/AHProfitTab.lua, GUI/CooldownsTab.lua, GUI/CraftingTab.lua.

  • Column-header styling is further centralized through shared helpers. Updated remaining table headers that were still hand-styled to use the shared brand/header factory, reducing drift between tabs and keeping color/style changes globally managed. Location: GUI/ShoppingListTab.lua, GUI/BrowserTab.lua.

  • Labeled dropdowns and edit boxes now share one global label offset. The old per-tab one-off nudges were replaced with a shared input-label helper that shifts labeled Dropdown/EditBox headers by the same 4px everywhere they appear, and restores the original anchors on release so pooled AceGUI widgets do not leak the tweak into other owners. Location: GUI/SharedWidgets.lua, GUI/BrowserTab.lua, GUI/CooldownsTab.lua, GUI/MissingRecipesTab.lua, GUI/ShoppingListTab.lua.

  • Zebra striping is now global across tab row lists. The shared addon.GUI.ApplyRowStripe helper now drives row banding throughout the addon table UIs (not just Profit Planner), so stripe appearance is centralized and consistent across Browser, Missing Recipes, Crafting, Cooldowns, and Shopping List views. Location: GUI/SharedWidgets.lua, GUI/BrowserTab.lua, GUI/MissingRecipesTab.lua, GUI/CraftingTab.lua, GUI/CooldownsTab.lua, GUI/ShoppingListTab.lua.

  • Cooldowns rows now render with the same raw frame/fontstring model as Missing Recipes, while still using the shared GUI helpers. The tab no longer relies on AceGUI row-cell widgets for the list body; each row now uses raw frame hit zones, textures, and fontstrings like the working Missing Recipes/Professions lists. The reworked rows now also detach their raw child frames through the shared addon.GUI.DetachPool release path when AceGUI recycles a row widget, so the pooled Cooldowns rows don't bleed into later widget reuses. Headers remain on the shared column factory and row hovers stay on the addon-wide tooltip-owner path. Location: GUI/CooldownsTab.lua, GUI/SharedWidgets.lua.

  • Profit Planner now has Missing-Recipes-style subtab filters plus recipe search. Each Profit subtab now renders its own toolbar with dynamic Profession and Crafter dropdowns populated only from rows currently present in that subtab, a + Profit only toggle, a recipe search box, and a multi-select source filter that defaults to all sources currently enabled by settings for that mode. Filtering runs before sort/render and keeps per-subtab state. Location: GUI/AHProfitTab.lua.

  • Profit Planner now shows all known craftable recipes, not only fully-priced rows. Row construction no longer drops recipes just because the crafted item has no current sale source or because one or more reagents lack price data; those rows are now retained with clear "No price" source text and empty cost/profit values until pricing is available. Source filtering now applies only when a row actually has source data, so default views do not hide unpriced recipes. Location: GUI/AHProfitTab.lua.

  • TSM App Helper pricing is now wired into material-cost lookups used by Profit/Crafting cost math. The generic item-price resolver (Price.Get) now includes TSM live/history fallbacks (before TOGPM scan/vendor tiers), so reagent costing can consume TSM coverage instead of appearing unpriced when Auctionator/TOGPM scan data is absent. Also removed a hidden gate that required the separate "Use TSM" toggle for App Helper reads, so App Helper can function when its own toggle is enabled. Location: Modules/Price.lua.

  • Crafting reagent source tags no longer truncate in the detail panel cost column. The per-reagent cost field was width-limited to coin text only, which clipped trailing source suffixes (e.g. [TOGPM]/[TSM]) on longer values; the column now has enough width to extend left and render full cost+source text. Location: GUI/CraftingTab.lua.

  • TSM sell-price lookup now falls back across the full TSM source stack to avoid nil sell prices when TSM has data. The TSM bridge now tries both item string forms (i:itemId and item:itemId) and a broader ordered source chain (DBMinBuyout/DBRecent/DBMarket/DBHistorical plus DBRegionMarketAvg and DBRegionSaleAvg fallbacks), so items missing realm-live values can still resolve from TSM historical/region datasets in Profit views. Location: Modules/Price.lua.

  • Profit Planner now resolves sell-price item IDs and source filters more defensively to avoid false No price rows. Sell lookup now probes multiple candidate output IDs per recipe (scanned crafted-item link first, then LibProfessionDB crafted item, then fallback item field), using the first one that resolves a price. Historical/live views now also allow cross-mode fallback when one side is missing, and the Profit-tab enabled-source defaults were aligned with the shared Price module's TSM toggle logic so valid TSM rows are not filtered out accidentally. Location: GUI/AHProfitTab.lua.

  • Per-tab scroll position now persists to SavedVariables and restores after tab switches/reopen. The shared PersistentScroll helper now stores each tab's scroll status in TOGPM_Settings.char.frames.scrollTabs and restores from that table on redraw, so Browser/Cooldowns/Missing/Crafting/Profit return to the same place after refreshes, tab hops, and window reopen/reload. Profit keeps separate saved scroll for Live vs Historical subtabs. Location: GUI/SharedWidgets.lua, GUI/BrowserTab.lua, GUI/CooldownsTab.lua, GUI/MissingRecipesTab.lua, GUI/CraftingTab.lua, GUI/AHProfitTab.lua.

  • BoP reagents no longer create false profit/cost gaps. Craft-cost completeness now excludes Bind-on-Pickup reagents from the priced == count gate, so recipes like Gordok Ogre Suit are still costed/profited from their priceable materials even when BoP components (for example Ogre Tannin) have no AH/vendor market price. Non-BoP unpriced reagents still correctly mark totals as lower-bound. Location: Modules/Price.lua.

  • Profit search box no longer drops keyboard focus after the first character. The Profit tab search handler previously triggered a full table redraw on every OnTextChanged, which recreated the edit box and swallowed the caret. Search now refreshes rows in place (filter/sort/row repaint only) so typing remains continuous. Location: GUI/AHProfitTab.lua.

  • TSM source labels now distinguish live quotes from AppHelper-backed data. TSM prices are now classified by the exact TSM expression that resolved: DBMinBuyout/DBRecent remain TSM Live, while DBMarket/DBHistorical/region values are labeled as non-live (TSM App) so Profit/Crafting columns no longer show everything as live. Profit source filters were updated so both TSM categories stay visible by default in each subtab. Location: Modules/Price.lua, TOGProfessionMaster.lua, GUI/AHProfitTab.lua, GUI/MainWindow.lua.

  • Auctioneer pricing is now integrated as an optional non-live source. Added a new Use Auctioneer pricing toggle and wired AucAdvanced.API.GetMarketValue into sale/cost lookups as Auctioneer App (non-live), with source labels/colors, Profit source-filter support, Crafting source tags, and TOC/pkgmeta optional dependency declarations. This allows using Auctioneer-backed values when TSM is disabled, without mislabeling them as live quotes. Location: Modules/Price.lua, GUI/Settings.lua, TOGProfessionMaster.lua, GUI/AHProfitTab.lua, GUI/CraftingTab.lua, GUI/MainWindow.lua, .toc files, .pkgmeta.

  • Auctioneer source naming now resolves more reliably on sell-price rows. Auctioneer market-value lookup now tries canonical item string forms (including full 8-field item: form) before falling back, which avoids false No price rows where Auctioneer has data but couldn't parse a compact link form. Resulting rows now correctly show the Auctioneer App source label. Location: Modules/Price.lua.

  • Auctioneer source naming now falls back to stat-engine values when market-value returns nil. When Auctioneer's combined market-value PDF path has insufficient data for an item, TOGPM now probes common Auctioneer stat engines (stat_simple, stat_histogram, stat_stddev, stat_iLevel) before giving up, reducing remaining false No price rows and preserving Auctioneer App source labels where Auctioneer has usable pricing. Location: Modules/Price.lua.

  • Auctioneer now has a separate cached-fallback toggle, mirroring live-then-helper behavior. Settings now include a second checkbox directly under Use Auctioneer pricing: Use Auctioneer cached pricing fallback. Resolver flow now tries Auctioneer primary market value first, then only uses Auctioneer cached stat-engine values when primary returns nil and the cached toggle is enabled, matching the intended two-stage fallback model used by other pricing integrations. Location: GUI/Settings.lua, Locale/enUS.lua, Modules/Price.lua, TOGProfessionMaster.lua.

  • Auctionator now has a separate historical/cached fallback toggle, matching the Auctioneer two-stage model. Added Use Auctionator cached historical fallback directly under Use Auctionator pricing; resolver flow now tries Auctionator live first, then only uses Auctionator cached historical pricing when live is unavailable and the second toggle is enabled. Profit source defaults were aligned so Auctionator History is enabled only when this toggle is on. Location: GUI/Settings.lua, Locale/enUS.lua, Modules/Price.lua, GUI/AHProfitTab.lua, TOGProfessionMaster.lua.

  • Auctioneer source labels are now split in UI to clearly show live vs cached origin. Resolver output now distinguishes Auctioneer primary market values (Auctioneer Live) from stat-engine fallback values (Auctioneer Cached) instead of reporting both as one Auctioneer App bucket, aligning with the same live-vs-cached clarity used for TSM labels. Profit source filters, main help legend, and Crafting source tags now surface the split explicitly, with a back-compat migration path for existing saved Profit source selections. Location: Modules/Price.lua, TOGProfessionMaster.lua, GUI/AHProfitTab.lua, GUI/MainWindow.lua, GUI/CraftingTab.lua.

  • Legacy Auctioneer alias text now resolves to Auctioneer Cached for consistency. The old compatibility key (auctioneer-app) is still accepted for saved-state/back-compat paths, but its UI label/color now map to cached semantics so users never see contradictory Auctioneer App wording after the live/cached split. Location: TOGProfessionMaster.lua, GUI/CraftingTab.lua.

  • Missing Recipes column-header sorting is now wired into the shared global sort path. The header row now uses clickable shared column headers with global sort indicators and state transitions (same addon.GUI.Sort pipeline as other sortable tabs), and the tab now applies sort to Recipe, Skill, and Source before virtual-row render. This fixes the broken/no-op header clicks in Missing Recipes. Location: GUI/MissingRecipesTab.lua.

  • Auctioneer live/cached probes are now more tolerant across API variants, and a new price diagnostic slash command was added. The resolver now probes multiple Auctioneer call signatures and alternate stat-engine identifiers for cached values instead of relying on one strict signature, reducing false nils when Auctioneer builds/plugins expose slightly different APIs. Added /togpm dumpprice <itemId|itemLink> to print Get/GetSaleLive/GetSaleHistorical and Auctioneer live-vs-cached diagnostics for one item (for example Savory Deviate Delight 6657) directly in chat. Location: Modules/Price.lua, TOGProfessionMaster.lua.

  • Auctioneer cached pricing now falls back to Stat modules directly when algorithm APIs yield nil. Some Auctioneer installations expose cached prices through loaded Stat-* modules even when GetAlgorithmValue is unavailable or non-productive; TOGPM now probes AucAdvanced.GetAllModules(nil, "Stat") + each module's GetPriceArray and picks the best seen-backed cached value before giving up. dumpprice output now also reports whether the Auctioneer module registry is available for this fallback path. Location: Modules/Price.lua, TOGProfessionMaster.lua.

  • Global multi-select dropdown handler added, and Profit tab dropdown filters now all use it. Added a shared addon.GUI.MakeMultiSelectDropdown factory (summary row + checkmark rows + no-empty fallback) and migrated Profit Profession, Crafters, and Sources filters to it so they now share one look/behavior and all support multi-select. Back-compat migration from older single-select profession/crafter state is handled automatically. Location: GUI/SharedWidgets.lua, GUI/AHProfitTab.lua.

  • TOGPM scanned-AH source now has an explicit on/off gate (no bleed when disabled). Added a dedicated Use TOGPM scanned AH pricing setting and wired the central resolver to honor it for both reagent costs and live sell-price paths; when turned off, TOGPM's cached AH values are excluded so external-only testing (for example Auctioneer-only) is accurate. Profit source defaults now also respect this toggle. Location: Modules/Price.lua, GUI/Settings.lua, GUI/AHProfitTab.lua, TOGProfessionMaster.lua.

  • Blizzard Auction House now gets a native TOGPM Scan button. Mirroring the profession-window TOGPM toggle pattern, the addon now injects a manual branded scan button directly onto the Blizzard AH frame (legacy and modern frame names), wired to TOGPM's full-scan path so users can trigger a TOGPM price refresh from the AH UI itself without opening any TOGPM tab first. Button state auto-disables while scans run and re-enables on completion/cancel/close. Location: Modules/AHScanner.lua.

  • Profit-tab recipe icons now use Crafting-style fallbacks for reliable rendering. Profit rows now resolve icons using crafted-item texture first and fall back to recipe spell texture when item-icon cache data is unavailable, fixing intermittent ?/missing icons on entries like Savory Deviate Delight while preserving normal item-icon rendering when available. Location: GUI/AHProfitTab.lua.

  • Profit now rejects recipe-scroll IDs when crafted output IDs exist. For recipes that provide both IDs (for example Savory Deviate Delight: crafted 6657, recipe scroll 6661), Profit sell/icon candidate selection now anchors to crafted output and ignores scroll-item IDs, preventing recipe-scroll icon/source leakage in Profit rows. Location: GUI/AHProfitTab.lua.

  • Browser and Cooldowns profession filters now use the shared global multi-select dropdown handler. Both tabs now use addon.GUI.MakeMultiSelectDropdown for profession filtering so the selector look/behavior matches Profit (same summary row/checkmark UX) and supports selecting multiple professions at once. Cooldowns keeps its specific cooldown sub-filter visible only when exactly one profession is selected, preserving the existing two-level workflow without inconsistent dropdown styles. Location: GUI/BrowserTab.lua, GUI/CooldownsTab.lua, GUI/SharedWidgets.lua.

  • Era recipe gating is now stricter in Missing Recipes and Browser to stop SoD/non-valid bleed-through. Client-validity filtering no longer skips required-skill/high-ID sanity checks just because recipe metadata is lib-backed, and unknown-client suppression now checks crafted-item IDs as well as recipe-scroll IDs before showing rows. This keeps legitimate late-Vanilla recipes while filtering non-Era bleed from shared 1.15-era datasets. Location: GUI/MissingRecipesTab.lua, GUI/BrowserTab.lua.

  • Global multi-select dropdown UX now supports true multi-pick sessions. The redundant selected-row check indicator was removed by moving the selected-count summary into the collapsed control text, and multi-select menus now stay open while you toggle items so you can select/deselect several entries before clicking away. Profit filter updates now refresh rows in place (not full redraw) so the open pullout is preserved while selecting. Location: GUI/SharedWidgets.lua, GUI/AHProfitTab.lua.

  • Fixed Missing Recipes invalid order function for sorting Lua error. Header-sort logic now precomputes stable sort keys before table.sort instead of calling live item/spell lookup APIs inside the comparator, preventing comparator instability while cache lookups change during sort passes. Location: GUI/MissingRecipesTab.lua.

  • Fixed blank captions on global multi-select dropdowns. The collapsed selected-count text now persists correctly after list refreshes; dropdowns no longer render as empty dark boxes after the keep-open multi-select UX change. Location: GUI/SharedWidgets.lua.

  • Fixed first-click multi-select behavior in profession filters. The dropdown now clears AceGUI's internal selected row marker (so only per-item checkmarks are shown) and uses a short delayed reopen tick so the pullout reliably stays open while you pick multiple entries in one session. Location: GUI/SharedWidgets.lua.

  • Fixed multi-select reopen race that could still close after each pick. The shared dropdown now waits for the pullout to finish closing, then reopens once (with short retry ticks) instead of blindly toggling immediately, so multi-select sessions stay open consistently on first and subsequent clicks. Location: GUI/SharedWidgets.lua.

  • Multi-select action rows now include both Select all and Clear all. The old Reset to all action is now named Select all, and a new Clear all action clears every selected entry in one click for profession/crafter/source filters using the shared helper. Location: GUI/SharedWidgets.lua, GUI/BrowserTab.lua, GUI/AHProfitTab.lua.

  • Fixed Missing Recipes tab crash (attempt to index local 'kb' (a nil value)) during sort. Sort-key precompute now uses defensive fallback key generation inside the comparator so sparse/edge-case lists cannot crash when a compared row lacks a cached key. Location: GUI/MissingRecipesTab.lua.

  • Fixed multi-select toggles that appeared to do nothing on some dropdowns. The shared dropdown function now maps UI tokens back to canonical key types before mutating selection state, preventing numeric/string key mismatches from turning select/unselect clicks into no-ops. Location: GUI/SharedWidgets.lua.

  • Multi-select dropdowns now use AceGUI's built-in multiselect flow directly. The shared helper now calls Dropdown:SetMultiselect(true) and synchronizes selection via SetItemValue(...), removing custom keep-open/reopen hacks and aligning behavior with AceGUI's native toggle handling while keeping shared Select all / Clear all actions. Location: GUI/SharedWidgets.lua.

  • Fixed open multi-select pullouts going visually empty after a click. The shared helper no longer rebuilds SetList(...) on every toggle while the pullout is open; it builds once and then only syncs check-state/text, matching AceGUI's open-time item rendering lifecycle. Location: GUI/SharedWidgets.lua.

  • Fixed Missing Recipes sparse-list sort crash (table index is nil). The comparator now guards nil operands before key lookup and never indexes the key cache with nil, preventing edge-case tab-open crashes when a sparse row list reaches table.sort. Location: GUI/MissingRecipesTab.lua.


[v0.8.4] (2026-06-01) — Enchanting shows a single "Enchant" button

Improvements

  • Enchanting now presents one clean "Enchant" button. Because an enchant is applied to a single item, the Crafting tab's controls for Enchanting are simplified to just an Enchant button (the secure /cast from v0.8.3) — the quantity stepper, Craft Max, and Queue are hidden (none apply to one-at-a-time enchanting), the button moves to the top of the controls column, and the detail panel sizes down to suit. Every other profession keeps the full Craft / Craft Max / Queue stack with the quantity controls. Also hardened: the now-secure Craft button's enable / position / attribute changes are guarded against combat lockdown (they can't be changed mid-combat). Location: GUI/CraftingTab.lua.

[v0.8.3] (2026-06-01) — Enchanting can be cast from the Crafting tab (protected-function fix)

The DoCraft error is gone for certain (we no longer call it); the secure-cast enchant path is pushed for community testing — there's no enchanter on the test account to verify it in-game.

Bug Fixes

  • The DoCraft error is gone, and Enchanting should now craft from the Crafting tab. Root cause of the long-running "can't enchant" bug: DoCraft — the Vanilla/TBC Craft API used for Enchanting — is a protected function, so an addon calling it from Lua trips ADDON_ACTION_FORBIDDEN and the craft is blocked. (That's why only Enchanting failed; DoTradeSkill, used by every other profession, isn't protected.) Fix — the same technique TradeSkillMaster uses: the Craft button is now a secure action button that, for an enchant, runs a /cast <recipe> macro. A /cast inside a secure macro is allowed, so the enchant casts and you click the target item to apply it — just like Blizzard's own Create button. Trade-skill professions keep the normal Lua craft (with batch quantity) via the button's PreClick. Craft Max / queued enchants still open Blizzard's Create button. Location: GUI/CraftingTab.lua, Modules/Crafting/CraftingEngine.lua, Locale/enUS.lua.

Older releases (v0.8.2 and earlier) are archived in CHANGELOG_ARCHIVE.md.

This mod has no additional files