TOGBankClassic-v1.8.1
What's new
TOGBankClassic Changelog
[v1.8.1] (2026-10-09) - Oldest Order First
Bug Fixes
- BANKCOLLECT-SHARE-001: at the bank, Fulfill Oldest skipped a newer order for the same item.
crimsonmane, Discord 2026-10-08: Fulfill Oldest "does not fulfill the oldest order first" when a
second player orders the same item plus more.
Mail:FindOldestBankFillableOrder(Modules/Mail.lua) measured every order against the whole bag count, so the stack I pulled for the older order also "covered" a newer order of the same item, and the newer one was never collected for -- it then could not be filled at the mailbox, and only that player's other items went out. The bags are now handed to open orders oldest-first, and each order's shortfall is what is left after the older orders' share.Tests/bankcollect_spec.luapins both the search and the click-through. Not yet checked in a game client. - FCFS-001: at the mailbox, Fulfill Oldest mailed a newer order ahead of an older one for the same
item. The operator, 2026-10-09: "you are supposed to fill the oldest order first. first come
first server".
Mail:FindOldestServiceableOrder(Modules/Mail.lua) picked the oldest order the bags could fully fill and skipped any it could not, so a newer, smaller order for the same item went out first and spent the stock the older order was waiting on. I had described that skip as by design in this entry and in my Discord reply to crimsonmane; it contradicted the operator's rule. Now the first order for an item that cannot be filled yet holds its place: no newer order for that item is sent until it is. Orders for other items still go out, since they do not use the same stacks.Mail:FindMoreOrdersFor(the same person's other orders riding on one mail) now leaves the stock other people's older orders need, so riding along cannot jump the queue either. The three fulfillment searches share one "oldest" ordering and one item key. The envelope's tooltip and the Requests "?" help inModules/UI/Requests.luasaid unfillable orders "are skipped"; both now describe the first-come-first-served rule, as does the Fulfill Oldest feature line on the CurseForge page. Four examples inTests/bankcollect_spec.lua, including crimsonmane's case. Not yet checked in a game client.
Internal
- SUITE-SPEED-001: the whole forward test run fits in one run again. Every spec builds a fresh
copy of the addon, and libraries that load once per run (AceComm, AceEvent and the other Ace
mixins, AceAddon's queue of addons waiting to load, VersionCheck's listener list) kept a strong
reference to every earlier copy, so the run held tens of thousands of dead frames and each reset's
garbage collection grew slower; late files took 40 seconds or more that take under one alone.
env.resetinTests/env_togbank.luanow clears those references, and a test window's close releases its tab contents instead of only hiding them. Measured: objects left afterbrowse_spec30,800 to 5,400. The forward run passes whole (1970 examples, 2026-10-09) and matches the three reverse parts (545 + 722 + 703). Clearing the handler lists means no spec can rely on an addon copy from an earlier file still hearing messages; the reverse parts passing is the check on that. No addon code changed.
[v1.8.0] (2026-10-08) - Donations Setting
The "Enable donations" setting is back, off by default, and the Donation popup can be turned off again. Checked in a game client by the operator on 2026-10-08 (the Donation popup, the tooltip toggle and the outstanding in-game checks from earlier releases all passed).
New Features
- TOOLTIP-TOGGLE-001: a setting to turn the bank stock off in item tooltips. The operator,
2026-10-07: "someone asked to be able to turn it off" / "we need to add one to settings, and have
it on by default". There was no such setting:
Modules/TooltipBankerInfo.luadrew its "Bankers:" block on every item tooltip unconditionally. General settings gain "Show bank stock in item tooltips" (db.global.bank.itemTooltipBankers, account-wide like the Mailbox switches, AceDB defaulttrue;Options:IsItemTooltipBankersEnabledalso reads a missing key or no DB as on). The check is inTooltipBankerInfo:AppendTo, the one renderer, so it also covers TOGProfessionMaster's recipe tooltips, which call it.Tests/tooltipbankerinfo_spec.luapins the default, the off state and the shipped toggle.
Bug Fixes
MAILBOX-POPUP-001: the Donation popup could not be stopped on a bank character. GUNNEY, Discord 2026-10-07: "donation window pops up on all bankers even when unchecked and I cannot get it to stop poping up". Confirmed in the code:
Mail:Scan(Modules/Mail.lua) stood aside only while the Mailbox window auto-opened, so unticking the General setting "Open the Mailbox window at a mailbox" (a global setting, so every banker at once) brought the per-mail Donation popup back in its place on every mailbox visit -- and v1.5.2 (UX-WATERFALL-001) had removed the per-character "Enable donations" box that used to turn the popup off. The only remaining off switch was "Enable for <player>", which also stops bank scanning. That "the popup returns when unticked" rule was my design choice in UX-WATERFALL-001, not a request, and it contradicted the setting's own description ("Untick to stop that").DONATIONS-BOX-001: the "Enable donations" setting is back, unticked by default. The operator, 2026-10-07: "we should have the setting to enable/disable donations back where it was, and have it unchecked by default." The per-character box returns to the Bank panel at its old place (order 2,
db.char.bank.donations,Options:GetDonationEnabled), and it alone decides whether the Donation popup opens at a mailbox:Mail:Scanno longer consults the Mailbox window's setting or verdict, so the two settings are independent. The AceDB default changes fromtruetofalse; AceDB never wrote the old default to disk, so every character that never touched the box starts unticked. This replaces the first fix above, which had tied the popup to the Mailbox window's setting a second way. KNOWN COST: a banker who ticks both gets both windows, as before v1.5.2; gifts taken through the Mailbox window are credited either way.Tests/donations_spec.luapins the popup following the box alone, the box's place, key and OFF default, and the real accessor.PKG-EMBED-001: the CurseForge app stopped installing LibDBIcon for Classic Era, TBC and MoP. The copy of LibDBIcon bundled for WoW Forever in v1.7.1 (
Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua, taken from TOGProfessionMaster) began with the packager's--@curseforge-project-slug: libdbicon-1-0@marker. The BigWigs packager greps every packaged.luafor that marker and records the slug asembeddedLibrary(release.sh:1882-1889), overriding.pkgmeta's required dependency, so the upload told CurseForge LibDBIcon was embedded and the app did not install the standalone addon -- while the Era, TBC and MoP TOCs list it under## Dependenciesand never load the bundled copy. Marker removed; the harness.pkgmetachecker now reportslibdbicon-1-0=requiredDependencyfor the upload, andTests/wiring_spec.luafails if the marker comes back. Whether v1.7.1's CurseForge file actually shows LibDBIcon as embedded was not checked on the site (not verified). The design is the operator's (2026-10-07): bundled ONLY for WoW Forever, an external required dependency everywhere else..pkgmetaalso lost all its comments, per the operator's no-comments rule of 2026-10-03; release.sh skips whole-line comments, so that change alters nothing uploaded.
Internal
- HARNESS-PIN c2fd446: Tests/wowapi moved from a49127f. Taken for WoWAPITesting's fix to
harness thread
1a91c3ca. Under load the runner's per-file TIMEOUT used to escape as a barelua.exe: (error object is not a string), with no file named; it is now a named failure. Adoption fix: twoTests/searchbox_spec.luafixtures built a FontString with no font, which the harness now refuses as the client does (FontString:SetText(): Font not set); they useGameFontHighlightSmall, the real cells' font. Production was already right (Requests.luagives every glow copy a font object). - GATE-LOAD-001: the gate survives a busy machine. run.lua's per-file limit is wall time on
Windows (WoWAPITesting, same thread), so it tripped whenever other desk runs shared the machine.
Every forward part now passes
--timeout=240, andTests/run_reverse.luapasses--timeout=600(browse_spec ran past 240 s late in the reverse order with three other runs in flight, and passed 697/0 at 600 s). Forward B2 is split into B3a and B3b, because under load its 52 files outran the desk's 15 minutes.CLAUDE.mdnames the new parts. - BOARDS-REMOVED-001: the last boards are gone.
docs/LIBRARY_CONTRACTS.md,docs/DEPENDENCY_CONTRACTS.md,Tests/HARNESS_CONTRACT.md,docs/contracts.mdanddocs/AUDIT.mdwere imported into the writ inbox and deleted. Writ, 2026-10-07: the operator's directive is "the boards should be removed after a migration"; git history keeps them. All 81 imported items were triaged the same day: 79 were already delivered, fixed or board prose. The other two were closed with a comment-level change: LOG-001's self-logging is kept on purpose and now says so (Modules/Output.lua), and Q1's false "nil when there is no link" claim was corrected (Modules/MailInventory.lua).
[v1.7.1] (2026-09-29) - WoW Forever
TOG Bank on WoW Forever, the fourth supported client. Checked in a Forever client by the operator on 2026-09-29: "everything seems to be working right now ... the display and everything is working, no more lua errors", and order highlighting "works now as intended". What that session could NOT check, for lack of other guild members running the addon: sync between players on Forever, and other players' names arriving over addon messages there.
New Features
- FOREVER-001: WoW Forever is a supported flavour (
TOGBankClassic_Camelot.toc, new, Interface 16001; the operator, 2026-09-28: "i have wow forever now, we need to onboard to make that a supporeted version with it's own .toc and updating the .ps1"). The fourth TOC is the Era TOC with one deliberate difference: LibDBIcon-1.0 is not published for Forever (it is absent from the_classic_beta_install; TOGProfessionMaster's Forever TOC embeds it for the same reason), so a hard## Dependencies:entry would stop the whole addon loading there. That TOC lists it under## OptionalDeps:instead and loads a bundledLibs/LibDBIcon-1.0/LibDBIcon-1.0.lua(MINOR 56, the copy TOGProfessionMaster ships) straight after LibDataBroker; LibStub keeps the higher MINOR if a standalone copy is also installed. TEMPORARY: once LibDBIcon-1.0 ships for Forever, make it a dependency again and delete the bundled copy. Every other dependency (Ace3, AceCommQueue-1.0, VersionCheck-1.0, DeltaSync, LibAceGUIWidgets, GuildRoster, ItemDB, ProfessionDB) already declares Interface 16001 or ships a_CamelotTOC. Not yet checked against the Forever client API (F:\Blizzard API Docs\wow-ui-source-forever) and not yet run in a Forever client. - FOREVER-HIGHLIGHT-001: order highlighting reaches the WoW Forever bank.
Modules/ItemHighlight.lualooked only for Classic'sBankFrameItemNbuttons and the-1vault, neither of which Forever has. Its bank is the tabbedBankPanel(ForeverCamelot/BankFrame.xml:85), which draws one tab from a pool of buttons.UpdateBankPanelHighlightingwalksBankPanel:EnumerateValidItems()and reads each button's ownGetBankTabID()/GetContainerSlotID(). A tab switch re-inits the pooled buttons with no bag event (GenerateItemSlotsForSelectedTab), so that is hooked to refresh, and the refresh clears every button it dimmed first. The dim-or-clear step for one slot is now one helper,HighlightBankButton, which the Classic vault and bank-bag loops also use. Spec'd inTests/itemhighlight_spec.lua. Not run in a Forever client. The icon key on Forever's bank item button (iconorIcon) was not found in the Forever source; both are tried, and a button with neither is left undimmed rather than raising. - FOREVER-HIGHLIGHT-002: the bags too. The operator, on Forever with no orders open, ticked
"Highlight needed items" and nothing in the Combined Backpack greyed. That is not a Forever
difference in what is needed: every item should grey. The bag path looked up Classic's
ContainerFrameNItemMbuttons, and Forever's modern container frames (ContainerFrameCombinedBags, andContainerFrameContainer.ContainerFramesfor split bags) draw pooled buttons with no such names.UpdateModernBagHighlightingwalks each shown frame'sEnumerateValidItems()and reads each button'sGetBagID()/GetID()(ForeverMainline/ContainerFrame.lua:560,:566-567). It answers false on a client without those frames, so Classic Era, TBC and MoP keep the Classic path unchanged. Not run in a Forever client. Whether Forever's own button refresh resets the grey before our next refresh is not known.
Bug Fixes
- FOREVER-002: TOG Bank raised a Lua error at login on WoW Forever (the operator, from game,
2026-09-28:
bad argument #2 to '?' (Usage: local success = self:HookScript(scriptTypeName, script [, bindingType]))atModules/TooltipBankerInfo.lua:117). The Forever client's GameTooltip has noOnTooltipSetItemscript -- nothing inwow-ui-source-foreverreferences it -- and builds item tooltips throughTooltipDataProcessor.AddTooltipPostCall(Enum.TooltipDataType.Item, ...)(Blizzard_SharedXMLGame/Tooltip/TooltipDataHandler.lua:199).TooltipBankerInfo:Initializenow tries the old hook underpcallfirst, so every client that has it keeps exactly its behaviour, and falls back to the item post-call, limited to GameTooltip (the only tooltip the old hook reached).pcallrather thanHasScript: the offline harness does not claim the client'sHasScriptsemantics.Tests/tooltipbankerinfo_spec.lua: a client refusing the script gets the post-call, which renders throughAppendToon GameTooltip and leaves any other tooltip alone.TooltipDataProcessoradded to.luacheckrcand.luarc.json. Not yet re-run in a Forever client. - FOREVER-NAME-001: a banker on WoW Forever was not a banker to itself, so the Bank settings page
never appeared (the operator, 2026-09-28: "i set myself as gbank, and show up on the roster ...
but i don't have the options in the settings menu to make myself visible as a banker";
/togbanklisted them asQuebeldorf Flamehammer). Forever characters have a first name and a surname: the roster listsFirst Surname, andUnitName("player")returns the two as separate values (Blizzard'sBlizzard_FrameXMLUtil/Camelot/NameUtil.lua:4-12joins them withConstants.CharacterNameSeparatorConsts.CHARACTERNAME_SURNAME_SEPARATOR).Guild:GetPlayerused only the first, so the player wasQuebeldorf-Realm, matched no roster key,IsBank(self)was false andOptions:InitGuildnever registered the page. The same wrong name reached every self-check (own scans, own requests, own-echo guards). NewGuild:GetPlayerFullName()(Modules/Guild.lua) appends the second value as a surname only on a client that has that separator constant and only when it is not the realm (the documented second return isunitServer, andUnitPopupUtils.lua:126shows the slot can carry either); it is used byGetPlayerand the request dialog's fallback (Modules/UI/Search.lua). Era, TBC and MoP have no such constant and keep the bare name.Tests/guild_spec.lua: four examples (surname joined and IsBank true; realm in the second slot ignored; a surname already in the first slot not doubled; no constant, second value ignored).Constantsadded to.luarc.json. Not yet re-run in a Forever client, and other players' names arriving over addon messages on Forever have not been checked for the same split. - PENDING-STATE-001: a banker's own bank read "Old format" between its scan and its first
publish (the operator, 2026-09-29, on a brand-new Forever banker's Bankers tab: "there is only
one format of data on newer clients, right?").
Guild:GetAltStalenessreturns"v1"for any held copy with no canon, and a canon is minted only AT publish -- so a banker whose scan was stored but held by the MULTIPC-001 publish gate (the guild had not answered yet; the status bar's countdown) was labelled old-format. New state"pending": our OWN bank, no canon, andBank.deferredset. The Bankers tab says "Not published yet" in yellow (Modules/UI/Browse.luaSTATE_TEXT / STATE_COLOR / STATE_RANK), the Inventory tab is not painted red and its tooltip explains the hold (Modules/UI/Inventory.lua). A later version named by another PC still wins ("behind"), and anyone else's canon-less copy is still "v1". Every flavour, not only Forever -- any new banker in a quiet guild saw it.Tests/tabstaleness_spec.lua: one example walking pending -> behind wins -> another banker stays v1 -> published is current. - FOREVER-BANK-001: on WoW Forever every bank-character scan raised an error, so a banker's items
never reached the Guild Bank window (the operator, 2026-09-29, with BANK debug on:
OnUpdateStop called/Calling Scan()and nothing after, thenInventory/Scan.lua:77: bad argument #1 to 'GetContainerNumFreeSlots'fromBank:Scanon closing the bank). Forever has noBANK_CONTAINER,NUM_BANKGENERIC_SLOTSorNUM_BANKBAGSLOTS-- the Forever tree defines onlyNUM_BAG_SLOTS-- because its bank is the modern bank-TAB system: each purchased character tab is a container whose id comes fromC_Bank.FetchPurchasedBankTabData(Enum.BankType.Character), which is how Forever's ownBlizzard_UIPanels_Game/Camelot/BankFrame.lua:91-98counts them. NewTOGBankClassic_Constants.BankContainers()(Modules/Constants.lua) is the one list of the character's bank containers: on Classic Era, TBC and MoP the vault then the bank-bag range, the same ids in the same order as before; on Forever the character tabs. The ACCOUNT (warband) bank is left out on purpose -- it is shared by every character on the account, so each alt would publish the same items as its own.Inventory/Scan.luawalks the tabs when there is noBANK_CONTAINER(Scan:ScanBankTabs; the Classic branch is untouched) and treats tabs reporting no slots as "bank out of sight", keeping the stored bank rather than emptying it (INV2-VAULT-001).Bank:FindItemsInBankandMail:FindEmptyBankSlotwalk the same list; on Forever both would have raised the same way.Tests/scan_spec.lua: four examples (both character tabs read and the account bank not; no tab or no slots returns nil; a whole ScanAll completes; the Classic list is the vault then BankBagRange).Tests/itemhighlight_spec.lua's BANKSLOT-001 one-spelling guard acceptsBankContainersas the shared spelling and pins that it is built onBankBagRange.C_Bankadded to.luacheckrcand.luarc.json. FOREVER-BANK-002 (the same session's self-audit): the tabs are read only while the bank window is open (Bank.atBank, set onBANKFRAME_OPENED, cleared after the close's own scan inModules/Events.lua), because a tab reporting slots is not proof its contents are readable away from the bank, and an empty read there would have replaced the stored bank on the next vendor or mailbox scan;scan_specpins nil away from the bank. Item highlighting on Forever's bank followed as FOREVER-HIGHLIGHT-001, above. - FOREVER-NAME-002: on WoW Forever a character's name carries no realm, and TOG Bank added one
(the operator, 2026-09-28: "could it have something to do with the fact that Forever doesn't
have <name><realm> it has <first name><last name>"). Forever runs regional unique names
(
RegionalUniqueNamesEnabled(), which exists only in the Forever tree): the full name is the identity, with no realm. The guild roster returns it bare, Forever's own AceDB keys the character asFirst Surname(AceDB-3.0.lua:263-278in the_classic_beta_install), and LibGuildRoster keys its roster by the bare full name (LibGuildRoster-1.0.lua:1264,:1345,:1659). TOGBank'smemberRosteris keyed by the library's names (Guild:_RefreshFromRosterLib), but every lookup went throughNormalizeName, which appended-<realm>, so on Forever every direct roster lookup (IsBank's fast path, IsInCurrentGuildRoster, IsPlayerOnline's fallback, whisper targets) missed. On a regional-names clientNormalizeNamenow keeps a bare name bare and drops a realm suffix, andGetPlayeris the bare full name -- the library's rule, in the library's words. The earlier FOREVER-NAME-001 surname join is now gated on the same switch instead of on the separator constant. Era, TBC and MoP have noRegionalUniqueNamesEnabledand take the old path unchanged.Tests/guild_spec.lua: five examples (the player is the bare full name and IsBank finds them under the library's key; a realm suffix is dropped; a surname already in the first slot is not doubled; no switch, or a switch answering false, changes nothing). The zero-data banker record saved under the old-ClassicBetaPvEkey is removed by the existing stale-stub cleanup at the next roster rebuild. Not yet re-run in a Forever client. - FOREVER-EVENTS-001: on WoW Forever the addon stopped half-way through setting up its event
handling, so a banker's bags were never stored and the Guild Bank window stayed empty (the
operator, 2026-09-28: bags full, Guild Bank window "0 items across 0 banks", no debug output with
BANK and uncategorized debug on).
Events:RegisterEventscalled the bareChatFrame_AddMessageEventFilter, which on Era and Forever alike is only a deprecation alias ofChatFrameUtil.AddMessageEventFilter(Blizzard_DeprecatedChatInfo/Deprecated_ChatFrame.lua:51, loaded only while theloadDeprecationFallbacksCVar is on). On the operator's Forever client it was nil, so the call raised and everything after it was skipped: the mailbox hooks, the share timer andeventsRegistered. It now callsChatFrameUtil.AddMessageEventFilter, the same function the alias points at on Era, TBC and MoP (ChatFrameFilters.lua:195in each tree), with the alias only as the fallback. A sweep of every global Forever defines only in itsBlizzard_Deprecated*files found two more, both in the debug chat tab (Modules/Output.lua):ChatFrame_RemoveAllMessageGroupsandChatFrame_RemoveAllChannels, now called as the chat frame's ownChatFrameMixinmethods, present on all four clients.Tests/events_spec.lua: RegisterEvents finishes with the alias absent and installs the offline-whisper filter throughChatFrameUtil. Not yet re-run in a Forever client; whether this is the whole reason the scan stored nothing there is NOT established -- the empty debug output is consistent with it, but the scan's own path was not shown to raise.
Internal
- FOREVER-001: the lockstep specs cover the Forever TOC.
Tests/wiring_spec.luapins it as the Era TOC with only its Interface line and the LibDBIcon difference changed, so a module added to one TOC only still fails; the per-TOC "is this module loaded" examples inbrowse,item,logapi,mailbox,pricelist,sendresult,togtoolsbridge,usable,windowalphaandguildroster_integrationnow include it. LIBDBICON-DEP-001's "not vendored" example keeps the three other TOCs and names Forever as the exception. wow-version-replication.ps1also mirrors into_classic_beta_, the WoW Forever install.- Harness pin
ee56a45->a49127f;frames.setScreenSizeadopted.Tests/browsepersist_spec.lua'sstandUpnow makes oneframes.setScreenSize(2560, 1440)call. It replaces three hand writes, one of them into the harness's privateUIParent._fixedRect(harness thread36d5a55f). The undragged-window scale example is back at scale 2. Its 1.25 workaround was hiding a gap in my own hand-rolled resize, not a harness defect. - REVERSE-TIMEOUT-001: the reverse runner passes
--timeout=240. This pin brings in a 60 s per-file CPU limit. In the a-end reverse slice,browse_spectook 97 s where it takes 9 s alone, because everyframes.reset()fully collects what earlier files left alive (~450 MB, ~150k frames by then). All 696 examples passed once the limit was raised.Tests/run_reverse.luanow also forwards flags after<part> <parts>torun.lua(--times), so a slice can be measured. The timeout was escaping as(error object is not a string)with no file named; reported to WoWAPITesting on thread5f29b50aand fixed there in2b63397(now a namedTIMEOUTline and one failure). The harness's new.pkgmetachecker (pkgmeta.lua) reports no FAIL here; its six WARNs are the defensive ignore entries that match no tracked file today (tmpclaude-*,*.bakand the like), kept on purpose. KNOWN COST: a file that slows down in reverse order is now caught at 240 s, not 60. The retained heap is still SUITE-HEAP-001's. - SPEC-NIL-001: 19 weak
is_nilassertions strengthened in the five spec files above that writ's new spec-shape check flagged (Writ inbox thread004cbf2f:is_nilon a literal key passes for ever if the literal is misspelt). Removed members are now pinned by an enumerated key set (browse,item); a state that is cleared is first asserted present (mailbox,pricelist,guildroster_integration). The PriceList debounce example now holds the timer, so it sees the latch set, one publish per burst, and the latch released -- it previously ran the timer synchronously and never observed the latch at all. The remaining 59 sites in 22 more spec files followed the same day, so writ's spec-shape check is clean repo-wide (Tests/wowapiexcepted, per the thread). The method, in order of preference: a whole-tableassert.samewhere the table should hold exactly something (L.opening,info.alts, the answer kinds a responder sent); an enumeration of keys by pattern where a removed member must not come back under any name (snapshot helpers, the Escape and store settings, the old-wire observer, vendored libraries); anassert.is_not_nilon the same expression BEFORE the state that clears it (batch state, pending sends, the legacy rows); a local constant shared by fixture and check for bracketed string keys, which the checker does not pair; and the public API where one answers the question (D:Entries(),Guild:IsBank,Switches:IsEnabled). - LAGW-PERSIST-SCALE-001 + LAGW-FORGET-001: TOGBank's window-scale stand-ins retired onto
LibAceGUIWidgets MINOR 35/36 (released as v0.2.7). The library's
PersistWindowfollows a live scale change itself for an AceGUI Frame and re-evaluates a FUNCTION floor on each change, soUI:PersistWindownow hands it the max(1, scale) floor rule asfloorAt(px)and registers no listener of its own;Persist_OnScaleChanged, thewindow._togPersistrecord andUI:SetPersistedAnchor(SCALE-DOCK-001's re-dock hook, and its two callers inRequests.luaandSearch.lua) are deleted. The re-dock hook existed only because the old listener re-ranPersistWindow, whoseApplyStatuscleared a hand-made dock; the library's listener raises size and bounds only, so a docked window keeps its anchor with nothing registered.UI:ForgetPersistedWindowis nowW:ForgetWindow(which also restores a ClearFrame handle's own floor, SCALE-FLOOR-002). An older library copy withoutForgetWindowkeeps the pre-35 hand-removal of the library record, and gets NO floor (LAGW-OLD-FLOOR-001, below).browsepersist_spec: the live-scale examples pass unchanged on the library's listener alone; the released-window example checks the library's record; SCALE-DOCK-001's three examples now pin a dock surviving a scale change with nothing registered, a control provingApplyStatuswould clear it, and a released docked window left alone by the next scale change. Not run in a client. - DS-422-001 / CLAMP-SCREEN-001: 11 offline failures fixed, all from two sibling-library releases
the specs had not caught up with (the addon code was right in each case). Whole suite now
1939 forward and 1939 reverse, 0 failed.
- DeltaSync v4.4.0 (MINOR 22) parks a broadcast whose numbers a listener cannot yet resolve and
replays it once the table lands. A viewer now requests straight from the banker's replayed login
broadcast, with no offer and no version query, seconds after adopting the table.
fullsync_specpinned the old offer + query round (4 examples): the first-sync example now asserts the shorter path and that it goes to the author; "four cold viewers" counts the slots while they drain; "already current" counts offers from the second broadcast on; SYNCED-001 reads "pending" as the broadcast leaves, not two seconds later when the receipt has genuinely arrived.congestion_spec's park example stamped its offerv = 99, and a table version is a timestamp, so 99 now reads as a table BEHIND the viewer's and is dropped rather than parked; it uses the viewer's own version. - LibAceGUIWidgets v0.2.7 (MINOR 36):
PersistWindowclamps a saved window to the screen.browsepersist_specsaved windows 1200-2000 wide on the harness's 1024x768 screen (6 examples). Its fixture now stands up a 2560x1440 screen (tunable, size AND the anchor rectangle, guarded byscreenMismatch), saved tops sit on-screen, and the undragged-window example uses a 1.25 scale: that window's CENTER still resolved against a 1024x768 parent after the resize (cause not established), so at 2x it hung off the left edge and the clamp correctly moved it. TOGBank's own scale listeners were searched for the cause: only the standalone Requests window re-runsPersistWindow(Requests_OnUIScaleChanged,Modules/UI/Requests.lua:1345); Browse's (Browse.lua:1181) only re-lays its lists. A harness helper that resizes the screen coherently is requested on WoWAPITesting's inbox (harness thread36d5a55f). Delivered and adopted later in this release (the harness-pin entry above): the_fixedRectwrite and the 1.25 scale are gone. - Coverage moved, stated so nobody looks for it here:
fullsync_spec's "four cold viewers" now drives four requesters through DeltaSync's replay path, so the simultaneous-login-broadcast storm is covered only bycongestion_spec's storm example.
- DeltaSync v4.4.0 (MINOR 22) parks a broadcast whose numbers a listener cannot yet resolve and
replays it once the table lands. A viewer now requests straight from the banker's replayed login
broadcast, with no offer and no version query, seconds after adopting the table.
- LAGW-OLD-FLOOR-001: an outdated LibAceGUIWidgets (no
ForgetWindow, before MINOR 35) is handed no window floor (Peer Review on self-audit587af509, finding 2). Such a copy never gives a released ClearFrame's resize handle its own floor back, and AceGUI's frame pool is shared by every addon, so TOGBank's floor would have ridden a pooled frame into another addon's window. With no floor set there is nothing to leak. KNOWN COST: on an outdated library TOGBank windows can be dragged below their minimum;UI:PersistWindowprints one warning per session asking the player to update the library.browsepersist_spec: an outdated stand-in is handed width and height only and warns once across two windows; the control shows a current library still gets the floor as a function.
[v1.7.0] (2026-09-26) - The Shopping List
GuildShoppingList merged into TOG Bank (GSL-MERGE-001; the operator, 2026-09-25: "ok, we can do the gsl
integration in v1.7.0"). Design and build order: docs/GSL_MERGE.md. The operator's standing rule
for the whole merge: "i don't want to build new stuff as much as possible i want to use the
libraries" -- LibProfessionDB, LibItemDB through Inventory/Resolve, LibAceGUIWidgets.
New Features
- GSL-MERGE-001 step 2: the shopping list's model (
Modules/CraftList.lua, new;TOGBankClassic_CraftList). What the guild wants crafted (a LibProfessionDB recipe) or gathered (a plain item, GuildShoppingList's Miscellaneous bucket), and the gather window. Writers (D2, the operator: "so the GSL player is a tag like gbank, use what is in the GSL app for that. the officers/gm should be able to edit the shopping list"): the GM, officers, and a home member whose PUBLIC note carries[GSL](GuildShoppingList's own marker,GuildShoppingList.lua:799). Sync: four fields on the existing guild-settings payload --craftListandcraftListStampsmerged entry by entry (a removal keeps its stamp; thebankerOwnersrule),gatherStart/gatherEndfield by field.BroadcastSettingsnow sends from a[GSL]member, andApplyRemoteSettingstakes ONLY the list from one. The four fields are left out ofSettingsCanon, so a v1.6.1 client with the same settings still matches (KNOWN COST in the code). A sister guild's list never crosses. Demand: a recipe expands into its reagents (LibProfessionDB, by item id) for what the HOME bank does not already hold of the crafted item; each reagent row nets the home bank (Guild:GetAltItemTotal) and your bags (C_Item.GetItemCount); items are named throughInventory/Resolve. ProfessionDB is a required dependency in every TOC and.pkgmeta(sluglibprofessiondb). No window yet (step 3). Offline:Tests/craftlist_spec.lua(new, 20, on the real ProfessionDB Vanilla Alchemy data; the canon example goes red with the exclusion removed). Locations:Modules/CraftList.lua,Modules/Guild.lua, the TOCs,.pkgmeta,.luacheckrc,.luarc.json. - GSL-MERGE-001 step 3: the two tabs (
Modules/UI/CraftList.lua, new;TOGBankClassic_UI_CraftList). In the Guild Bank window, after the existing tabs so none moves: Shopping List for everyone (a View dropdown between what the guild wants, with its profession, how many the home bank holds and how many are still needed, and the reagents that takes, with Needed, Bank, You and Missing), and List Setup for the list's writers only (a search across LibProfessionDB recipes and, with no profession picked, LibItemDB items; a How many box; left-click puts an entry on the list or changes its count, right-click takes it off; the gather window on LibAceGUIWidgets' date picker). Built on the existing machinery, per the operator's rule: Browse's tab group andBuildSimpleStrip,UI/RowList, the shared chrome and status line, items named throughInventory/Resolve. Profession names and icons come from the client'sC_TradeSkillUI.GetTradeSkillDisplayName/GetTradeSkillTexture(present in the Era, TBC and MoP trees).Browse:SyncTabsre-renders the tab strip on open when the set of tabs changed, since the writer role moves with the roster, andBrowse:RememberedTabnow validates its fallback too, so a demoted writer is not reopened on a tab the strip no longer carries. A refused edit says whether the player may not edit the list or the list already holds that count. The quality-coloured item name is one helper,UI:QualityText, which the Browse rows now use as well. Offline:Tests/craftlistwindow_spec.lua(new, 13, on the real Guild, ProfessionDB Alchemy data, LibItemDB for the item search, AceGUI, Browse and the date pickers; red with List Setup shown to everyone, and red with every open rebuilding the tab strip).C_TradeSkillUIcomes from the harness (WoWAPITestingee56a45, thread b856f5a5).Modules/UI/CraftList.lua,Modules/UI/Browse.lua, the three TOCs,.luacheckrc,.luarc.json. - GSL-MERGE-001 step 4: the Reagent Tracker (
Modules/UI/CraftTracker.lua, new;TOGBankClassic_UI_CraftTracker; D5). GuildShoppingList's detached overlay as a small window of its own, showing the Shopping List tab's reagent roll-up (UI_CraftList:ReagentRows, the same columns), sorted by Missing, persisted astracker. It repaints on a list change, onBAG_UPDATE_DELAYEDwhile shown (registered on open, dropped on close, as ItemHighlight does), when bank data lands (UI_Inventory:RefreshSoonnow fans out to it on its own debounce, since it is often up with neither bank window open), and on a roster change (Browse:Refresh). It opens from a Tracker button on the Shopping List strip and from the new/togbank tracker. It is deliberately left out of Escape: a gathering overlay that the Escape stand-in counted would close on every Escape pressed in the world instead of the game menu opening. Offline: six examples inTests/craftlistwindow_spec.lua; the bank-data one was red before theRefreshSoonfan-out existed, and the bag-update one fires the real event through the harness and asserts the registration on open and its removal on close (Peer Review F5, red with the registration taken out).Modules/UI/CraftTracker.lua,Modules/UI/Inventory.lua,Modules/UI/Browse.lua,Modules/UI/CraftList.lua,Modules/Chat.lua, the TOCs,.luacheckrc,.luarc.json,README.txt. - GSL-MERGE-001 step 5: GuildShoppingList's list comes across, once (D9;
CraftList:ImportGSL,CraftList.ResolveGSLName). On a roster refresh (Guild:RefreshOnlineCache, both the library and the legacy path, beside the officer-floor publish), a client that may edit the list and whose guild has never had one (no entries and no stamps) readsGuildShoppingList_SavedItems("<name> x<N>") and its gather dates (GuildShoppingList_Config.GatherStartDate/GatherEndDate), writes the entries, and publishes once. Each name is matched through LibProfessionDB by recipe name, then effect, then both with a trailing parenthetical stripped, and last as a plain item by its exact name through LibItemDB (GuildShoppingList's Miscellaneous turn-in items). A spelling two recipes or two items share maps to neither (REQ-001); an ambiguous name does not fall through to the effect. Repeats are summed. The entries are stampedCraftList.IMPORT_STAMP(1), older than any TOGBank edit, because the first roster refresh can come before this client has heard the guild's list: stamped "now", an import would have overwritten the guild's counts and brought back entries it had removed (Peer Review F1 on self-audit 13180dbf). What matches nothing is reported withOutput:Response, which no setting mutes, and the names are kept indb.global.gslImport.unmappedNames(F2). GuildShoppingList's variables are account-wide, so the import runs once per account. KNOWN COST in the code: on an account with writers in two guilds the list goes to whichever guild such a character logs into first; the gather dates ride the ordinary settings stamp, so the race can still let imported dates replace a guild's newer ones; and on a client that has never held the guild's settings, an import racing an existing list is merged into it rather than refused (Peer Review on daa08281). The migration lives inModules/CraftList.lua, beside the list's other writers, rather thanModules/Database.luaas the design first said, because it writes through the list's own rules. Offline: sixTests/craftlist_spec.luaexamples on the real ProfessionDB Alchemy and Enchanting data and the real LibItemDB, including the race (red with the import stamped now), a recipe effect two recipes share (Health +5, red with the ambiguity marking removed) and a name nine items share (Punctured Voodoo Doll, red with the two-ids refusal removed); the importing examples went red with the roster hook removed. And oneTests/xguildfleet_spec.luaexample: a home[GSL]bank character's list reaches its guild, and neither a whisper of it nor a federation pull puts it in the sister guild (red with the XGUILD-SETTINGS-001 gate taken out).Modules/CraftList.lua,Modules/Guild.lua. - GSL-MERGE-001 step 7: order from a Shopping List row (D6;
UI_CraftList:OnListRowClick,UI_CraftList:OrderSource). A left click on a row in either view (a wanted item, the item a wanted recipe makes, or a reagent) opens the existing request dialog on the bank character holding the most of it. The row comes fromBrowse:BuildRows, the Browse tab's own rows, so a sister guild's bank character counts as it does there, and the click goes throughBrowse:OnBrowseRowClick, so ordering closed, not-for-sale and the request limit refuse it exactly as on the Browse tab. A view-only bank and a hidden row are skipped when choosing. An enchant (no item) and an item no bank character holds say so on the status line; Shift/Ctrl-click links or previews; a right-click does nothing. A tie goes to the first bank character in name order. Offline: eightTests/craftlistwindow_spec.luaexamples (six red before the change). The spec's bank fixture now runs the realGuild:RebuildBankerRosterfrom the banker's gbank note, the path a live client takes onGUILD_ROSTER_UPDATE, rather than a hand-set roster (Peer Review on self-audit 93c01c04).Modules/UI/CraftList.lua. - WANTED-BREATH-001: Still needed breathes (the operator: "can you have the wanted stuff
'breath' using the function from the widgets library?"). On the Shopping List tab's Wanted view
the Still needed number pulses through LibAceGUIWidgets'
W:Breathe(the call the help icon, the cancelled date and the stay-online line make) while it is above zero, and stops at full alpha when the bank covers it. The breath animates a frame, so the column builds its own cell (a frame holding the number) and the list'sonRowRenderpaints it; a pooled row reused for a met entry is stopped, not left dim. Offline: oneTests/craftlistwindow_spec.luaexample (red before).Modules/UI/CraftList.lua. - SETUP-SPLIT-001: List Setup split, TOGPM's shopping-list box on top (the operator: "split
the list setup into whats on the list on top and the selection bit on the bottom. lets make it
look like TOGPM's 'shopping list' bit at the top of the professions page"). What is on the list is
now a bordered box at the top of the tab (
UI_CraftList.SetupBox, a RowList): icon, quality-coloured name, type, then TOGPM's-count+and redx. It is capped at 40% of the tab's height and scrolls beyond that, as TOGPM's is. The search results sit under it, and an empty search no longer lists the list there.-from 1 andxtake an entry off; every control goes through the list's own writers and the same may-edit gate as a result click (UI_CraftList:StepEntry/RemoveEntry/WriteEntry). TOGPM's "!" alert and reagent expander are not carried: they are per-character craft features with no meaning for a guild list.OnSetupRowClick's branch for on-list rows could no longer be reached and is removed.Browse:AnchorListscallsUI_CraftList:AnchorSetupto put the box over the results. Offline: the old "empty search lists the list" example is replaced by four (box over results, the controls, a member refused, the height cap).Modules/UI/CraftList.lua,Modules/UI/Browse.lua. - GATHER-BREATH-001: the gather dates breathe (the operator, of the Shopping List tab's
"3 wanted -- gathering 2026-09-26 to 2026-10-03": "the date isn't breathing"). That line was what
"the wanted stuff" in WANTED-BREATH-001 meant, and I built the Still needed pulse instead; the
Still needed pulse stays unless the operator wants it gone. The status bar gains
Instance:SetLeftBreathing(on): the left text is AceGUI's FontString on the pooled status background, which cannot host the breath (BREATH-001), so the first request moves it onto a frame of the bar's own (cached on the pooled background, as the centre host is, POOL-CHROME-001) and the bar's release hands it back, still.UI_CraftList:Drawasks for the breath while the Shopping List tab shows a gather window;HideLists, which every tab switch passes through, stops it, so no other tab's line breathes. Offline: twoTests/statusbaryield_spec.luaexamples on the real bar (the breath and its release) and oneTests/craftlistwindow_spec.luaexample (on, both views, off on Browse).Modules/UI/StatusBar.lua,Modules/UI/CraftList.lua. - SETUP-SPLIT-002: the box's scrollbar came in at 3 rows (the operator: "i don't mind the
scrollbar, but we need to expand more than 3 items before we introduce it. how does TOGPM do
it?"). TOGPM shows every row until its 40% cap and pads the section past the rows. My box was
sized to exactly its rows, and RowList counts rows with
floor, so one pixel lost to the client's pixel snapping dropped the last row and showed the scrollbar. The box now carriesUICL.SETUP_BOX_SLACK(2). Offline: aTests/craftlistwindow_spec.luaexample shaves a pixel off the box and requires three rows and no scrollbar (red before). Not yet seen in a client.Modules/UI/CraftList.lua. - GSL-SENDTO-001: the Shopping List tab says who to send items to (the operator: "on the
shopping list tab, we need to show who is the GSL character, so people know who to send items
to"). A "Send items to:" line on the tab's strip names every home-guild member with
[GSL]in their public note (CraftList:GSLPlayers, the same test asIsGSLPlayer), online ones first in green, offline ones grey and marked, and tells a guild with none how to name one. Painted byUI_CraftList:Draw, which a roster change reaches, so online state stays current. Offline: oneTests/craftlistwindow_spec.luaexample.Modules/CraftList.lua,Modules/UI/CraftList.lua. - ITEM-SEARCH-001: every item is searchable on List Setup, whatever the profession (the
operator: "we need to ensure the eko's that make juju's are on the list, why don't we just have
the itemdb searchable on the GSL add list?"). The search skipped LibItemDB whenever the Profession
dropdown named a profession, so an item no profession makes (the E'kos are Quest items) could not
be found from a profession view. A profession now narrows the recipes only. Offline: a
Tests/craftlistwindow_spec.luaexample loads LibItemDB's shipped Quest data and finds all seven E'kos (12430-12436) with Alchemy picked. KNOWN LIMIT: LibItemDB's search is a plain substring, so "eko" without the apostrophe still finds nothing; a punctuation-insensitive match is requested from ItemDB (contracte860cddb848a).Modules/UI/CraftList.lua. - SETUP-REAGENTS-001: made items show their reagents, TOGPM's expander (the operator: "i would
like to do a TOGPM like thing, where each 'made' item shows the mats to make it. so we can say we
need 5 flask OR x reagent y reagent etc. then it can generate a complete reagent list based on the
end items required"). In the List Setup box a made item carries TOGPM's
+; a click opens its reagents under it (indented, at per-craft count times the count wanted, no controls) and-closes them. The box is no longer sortable, as TOGPM's is not, so a reagent stays under its item. The complete roll-up across every end item is the Shopping List tab's Reagents view (CraftList:Demand), unchanged; a raw reagent can still be put on the list directly as an item. Offline: oneTests/craftlistwindow_spec.luaexample.Modules/UI/CraftList.lua. - SHOPLIST-SPLIT-001: the Shopping List tab laid out like List Setup (the operator: "the + and
the shopping list totals need to be on the shopping list tab as well. the guild needs to see how
many dreamfoil etc they need to send ... the shopping list tab needs to be like the list setup
tab, the items on the top, but instead of a search bit on the bottom, the bottom rows should be
all the reagents we need to make the items in the list"). The wanted items are a bordered box on
top (unsorted, so an item's reagents stay under it) and the full reagent roll-up
(
CraftList:Demand, Needed / Bank / You / Missing) is always shown under it; the Show dropdown that switched between the two is removed. A crafted item's+opens its reagents at the count still to make (wanted less what the bank holds of it), so an opened item agrees with the roll-up. Because a row click orders the item (step 7), the+is a button of its own in the first column (buildArrowCell), not a row click as on List Setup. Both boxes are now one builder, one height rule (UICL:BoxHeight) and one expander (UICL.WithReagents), so the two cannot drift. The status line reads "N wanted, N reagents, N still missing". Offline: two newTests/craftlistwindow_spec.luaexamples; the four that drove the old dropdown now open the tab as it is.Modules/UI/CraftList.lua. - GSL-BANK-001: the [GSL] character is the shopping list's banker (the operator: "we also need
what is on the GSL player (bank/bags/mail) just like a banker. they are the 'special' banker for
the shopping list" / "the missing tab should be what the GSL banker is missing"). One rule now
decides what a guild note makes a character a bank,
noteBankRoleinModules/Guild.lua, and the six sites that decide who is a bank ask it instead of testing forgbankon their own:GetBanks' fallback,_SisterBankers,RebuildBankerRoster,IsViewOnlyBank's fallback, and both roster builds (library and legacy) plus the sister roster.SenderHasGbankNotedeliberately staysgbank-only, because it grants settings and donation authority, not bank status. An earlier draft of this entry said "every site", which missed it; and because the sister roster stores a[GSL]-only character asisBank, its sister-guild branch gave such a character that authority until it was changed to re-read the note forgbank(newTests/xguild_spec.luaexample).[GSL]in the PUBLIC note makes a banker flaggedgsl(the field onmemberRoster, read by the newGuild:IsGSLBank), so the GSL character's own client scans and publishes its bags, bank and mail through the banker pipeline. It is view-only unless the note also saysgbank: the guild sends to it, and does not order the shopping list's stock back out. It therefore shows on the Browse and Bankers tabs as a view-only bank, andBankerStoresstrips[GSL]from its note. On the Shopping List tab the reagent roll-up gains a GSL column (CraftList:GSLHolds), Bank no longer counts the GSL character (CraftList:BankHolds, both throughHomeBankersHold), and Missing is the need less what the GSL character holds; Bank and You are shown beside it, not netted out. The Reagent Tracker shows the same columns. Found while wiring it: my new rule madeIsViewOnlyBank's fallback depend on being a banker, which broke the existing "GBANK VIEWONLY" case (the banker test is case-sensitive); the fallback keeps the marker's own answer. Offline: twoTests/craftlistwindow_spec.luaexamples (the note rule, and the GSL column); the roll-up and tracker examples incraftlistwindow_specandcraftlist_specnow expect the new Missing.Modules/Guild.lua,Modules/CraftList.lua,Modules/UI/CraftList.lua,Modules/UI/CraftTracker.lua. - GSL-YOU-001: You and GSL disagreed on the GSL character (the operator 2026-09-26, with a
screenshot of 120 against 160 Dreamfoil: "when i'm on the GSL toon, the GSL and You columns should
match").
CraftList:YouHoldwasC_Item.GetItemCount(itemID), bags only. A bank character (the[GSL]one included) now reads its own scanned total (Guild:GetAltItemTotal: bags, bank and mail), the number the GSL and Bank columns read for it; anyone else gets bags plus bank (includeBank, Classic EraItemDocumentation.lua:242). The mail half is only as fresh as the character's last mailbox visit, since the inbox can be read only while it is open (Modules/Bank.lua:303). Offline: aTests/craftlistwindow_spec.luaexample with bag and mail stock on the GSL character.Modules/CraftList.lua. - TRACE-REALM-001:
/togbank dev trace <name>traced the wrong realm (the operator's own trace, 2026-09-26). A bare name normalised to the PLAYER's realm, so "trace Togcloth" typed on Old Blanchy traced Togcloth-OldBlanchy, which does not exist, and printed the real Togcloth-Azuresong as "banker on the roster: NO". A bare name now resolves to the one bank character (then the one roster member) with that name on any connected realm; an ambiguous name keeps the old reading. Offline: aTests/trace_spec.luaexample.Modules/Chat.lua. - ESC-SPLIT-001: one window list was doing two jobs (Peer Review, inbox a0556886).
UI.ALPHA_WINDOWSwas both the transparency-slider list and the Escape registry, so the Reagent Tracker could not get a slider without also being closed by Escape. Escape now reads its ownUI.ESCAPE_WINDOWS(the seven existing windows); the tracker is inALPHA_WINDOWSonly, so it has its own Appearance slider and is also moved by Recenter Windows. Offline: aTests/windowalpha_spec.luaexample pins the two lists against each other, and aTests/craftlistwindow_spec.luaexample drives the tracker's slider and checks the Escape stand-in stays hidden with only the tracker open; both went red with the tracker added back to the Escape list.Modules/UI.lua,Modules/UI/CraftTracker.lua.
Bug Fixes
- XGUILD-SETTINGS-002: a sister guild's officer still had settings authority behind one branch
(Peer Review F4 on self-audit 13180dbf, traced by them).
SenderHasGbankNote,SenderIsGMandSenderIsOfficereach read the SENDER'S guild roster, so they answer true for a sister guild's bank characters, officers and GM -- right where they authorize that guild's bank data, wrong for settings. The XGUILD-SETTINGS-001 branch at the top ofApplyRemoteSettingswas the only thing between another guild and every officer setting.SettingsSenderAuthorizednow refuses a sender placed in another guild (the same test that branch uses), andApplyRemoteSettingsasks it rather than repeating the three helpers, so the refusal holds even without that branch. The helpers are unchanged. Offline: theTests/xguildfleet_spec.lualist example asserts the home bank character has a gbank note but no settings standing from the sister client (red without the new line), and with the sister branch disabled the home list still stays out.Modules/Guild.lua. - SETTINGS-TIE-001: two clients holding different settings at the same stamp never agreed
(Peer Review F1 on the step-2 self-audit). An equal stamp was taken from whichever payload arrived
last, so two officers' writes in the same second flipped each client back and forth; the
[GSL]path also spelled the same rule differently. Both paths now go through oneadoptSettingsField: a newer stamp wins, an unset field is seeded, and an equal stamp goes to the greater canonical value, so every client picks the same winner. Found while wiring it:ApplyRemoteSettingsmoved the held version BEFORE judging the fields, so a field with no stamp of its own read as exactly the payload's stamp; it now moves after. KNOWN COST in the code: a plain member that sees a tie while the writer holding the greater value is offline asks the other writer once a minute until the two meet. The shopping list's entry merge (CraftList:Merge) and the bank-character owner merge (Guild:MergeBankerOwners, Peer Review on the follow-up audit) had the same defect per entry: on an equal stamp the list now takes the greater count and the owners the greater name. KNOWN COST in both: a removal or a clear counts as nothing and so always loses a tie, and comes back.Modules/Guild.lua,Modules/CraftList.lua;Tests/craftlist_spec.lua(five examples; the settings and owner ones go red on the old rule). - POOL-CHROME-001: a released TOG Bank window left its "?", gear, share button and status-bar
text on AceGUI's pooled frame (Peer Review F5, which asked for a trace; the trace found the
defect, wider than asked).
UI:DressWindowcached the icons on the frame and never hid them, so the next window built on that frame, another addon's included, showed our help icon and a working gear. The share button's OnShow hook still showed it on a bank character.StatusBar:Attachbuilt a NEW centre frame and right-hand text on every attach, left the old ones shown with their last text, and stacked one more OnShow hook per life that redrew a dead bar. Now: one release dispatcher (UI:OnWindowRelease, because AceGUI holds a single OnRelease per widget) runs the chrome's and the status bar's clean-up. The bar's sections are cached on the pooled bar and reused, and its show/hide hooks are set once per frame and read the frame's current bar. Seen offline only, not in a client.Modules/UI.lua,Modules/UI/StatusBar.lua;Tests/windowchrome_spec.lua(red without the release step).
Internal
- Changelog archive: the whole v1.6.0 section moved to
CHANGELOG_ARCHIVE.md. Forward part A8a (A5a pluscraftlist_specandcraftlistwindow_spec) replaces A5a in the gate. - The real LibItemDB in
craftlist_spec(Peer Review F2): one describe loads the installed ItemDB with its shipped Vanilla Miscellaneous, Consumable and enUS name data and asserts both entries resolve through it; the existing naming example pins the no-library client fallback. - CurseForge page checked with the harness's
cfhtml.lua(declared as the writ suitecfhtml): well-formed, 64,542 characters. It flagged two CurseForge links (TOG Profession Master, TOG Tools) against the Discord-only rule for player pages; both names are now plain bold text.docs/Curseforge_Description.html. - The whole-client test env was missing
Modules/CraftList.lua.Tests/env_togbank.lua'sMODULE_ORDERis a second copy of the TOC's load order, and every fleet client built from it silently dropped a heard shopping list, so a fleet example about the list would have passed for the wrong reason. Added where the TOC loads it, withModules/Usable.lua, which it also lacked. MODULE-ORDER-001 (Peer Review F6): aTests/wiring_spec.luaexample now reads the Era TOC and requiresMODULE_ORDERto equal its non-UI module lines in order (red withUsable.luaremoved). - Release docs for v1.7.0:
README.txtbrought level with the CurseForge page and the code -- version 1.7.0, the shopping list in Key Features and the tab lists, a[GSL]setup section, the You column counting bags and bank, List Setup (not the Shopping List tab) setting the gather dates, the Reagent Tracker's transparency slider, a v1.7.0 compatibility note, Changelog Highlights and ProfessionDB under Libraries. The CurseForge page gains ProfessionDB under Required (it was a TOC dependency and missing there), List Setup in both tab lists, the[GSL]setup step, the You column wording, and two New notes: older versions keep syncing but do not see the list or the[GSL]stock, and ProfessionDB is required. - GSL-MERGE-001 step 6, TOGBank's half: a "Guild Shopping List" section in the CurseForge
page's Core Features (telling a GuildShoppingList guild to keep that addon enabled until a list
writer has logged in once, since the import can only read its saved variables while it loads),
the Shopping List tab in the page's tab lists, and a Shopping List section in
README.txt. GuildShoppingList's final release is requested from its own workspace (contract27dae11a3e42).docs/Curseforge_Description.html,README.txt,docs/GSL_MERGE.md. - Harness pin
23d11b1->ee56a45(WoWAPITesting deliveredC_TradeSkillUI, thread b856f5a5). The spec-localC_TradeSkillUIstand-in inTests/craftlistwindow_spec.luais deleted; the spec now runs on the harness's. - Gate: forward 470 + 504 + 961 = 1,935, reverse 537 + 711 + 687 = 1,935, none failed.
[v1.6.1] (2026-09-25) - Sister-Guild Failover
New Features
- ESC-001: Escape closes the Guild Bank window, and a setting turns Escape off. A player: "Bank
doesnt close with esc press, but togpm does"; the operator: "it should have esc close the window
by default." The escape frame was only CREATED by the first open of Inventory, Search, Requests
or Donations, so the Guild Bank window opened on its own never had Escape; closing Inventory also
hid it and took the Guild Bank window down. Now one stand-in (
TOGBankClassicEscProxy) on UISpecialFrames, shown while any window is open, closes them all. Appearance setting "Close windows with Escape" (db.global.closeOnEscape, default on)._G.TOGBankClassicis gone. Offline:Tests/escape_spec.lua(new, 10). Locations:Modules/UI.lua,Modules/Options.lua, everyModules/UI/*.luawindow. - XGUILD-SEARCH-001: the Bankers search matches a sister guild's name. The operator, 2026-09-25:
"the guild tag name needs to be searchable, so i can search for bankers in the sister guild."
Browse:FilterBankerRowspasses the row'sguildNametoSearchMatch. Offline:Tests/browse_spec.lua(+1). Locations:Modules/UI/Browse.lua. - SSYNC-001:
/togbank ssync [<name>]starts a sister-guild sync now. The operator, 2026-09-25: "do we have a /togbank command to start a cross guild sync? i need that, especially for testing, and if i could do /togbank ssync <name> that would be helpful. it could do a roster lookup so i can be lazy with the realm." With no name, each listed sister guild's usual peer is asked at once, past the cycle's pending and silent-for-an-hour waits. With a name, that sister member is found in the sister rosters (realm optional, any case;Guild:FindSisterMembers) and asked directly -- the hash-list ask and the requests index behind it, the cycle's ownAskFederationPeer-- with any "left alone" mark on it cleared. It refuses, sending nothing and saying why, when the Sister-guild bank is off, no sister guild is listed, the name is not a sister member, it matches characters on more than one realm, or the member is offline. Offline:Tests/xguild_spec.lua(one example). Locations:Modules/Guild.lua(SisterSyncCommand),Modules/Chat.lua,README.txt. - XGUILD-NUDGE-001: a GreenWall guild hears you open the bank, and its members pull at once
(
docs/XGUILD_SYNC.md4.6, build-order step 7 -- the optional last step, gated on steps 2-6 being seen in game, which the operator closed on 2026-09-16). GreenWall's bridge cannot carry the sync (a CHANNEL send is hardware-gated on Classic, a segment is 255 bytes, the handler is not told the guild -- 1.1 of the design note), but ONE line from a click can ride it:Browse:Toggle-- the minimap button,/togbank, the legacy window's Browse button -- sendshlq:<addon version>throughGreenWallAPI.SendMessagewhen the sister-guild bank is on, GreenWall is loaded and its guild channel is JOINED (a segment GreenWall cannot send is parked for its next flush, which may run from its timer, so an un-joined channel refuses rather than risks a blocked send), at most once per sync cycle. A federated client hearing it (GreenWallAPI.AddMessageHandler, registered from Core's init) notes the version and treats the line as a TOGBank message from the sender (Guild:UpdateOnlineMember(..., "greenwall-nudge")), soOnFederationPeerProvenasks that guild for its hash list NOW instead of on the next ten-minute cycle -- after Guild Roster places the sender in a LISTED sister roster; echo, a home guildmate and a name only GreenWall's confederation knows are ignored. Nothing runs without GreenWall (## OptionalDeps: GreenWallin every TOC, so it loads first when present). Offline:Tests/xguild_spec.luastep 7 (three examples; the API's signatures pinned against the installedGreenWall/API.lua). Not seen in a client. Locations:Modules/GreenWall.lua(new),Modules/UI/Browse.lua,Modules/Guild.lua,Core.lua, the TOCs,Tests/env_togbank.lua,.luarc.json,.luacheckrc.
Bug Fixes
- POOL-CLUSTER-001: a released Requests window handed on its broom and envelope.
ReleaseWindow(role change, or the body moving into the Guild Bank tab) returned the frame to AceGUI's shared pool with the cluster and officer overlay still shown; they now hide first. Not seen in game.Tests/requestsactions_spec.lua+1. Locations:Modules/UI/Requests.lua. - REQ-GATE-001: a stranger could pull the guild's request history (Peer Review on audit
31294783, F5).requests-index/requests-by-idnow answer onlyIsInCurrentGuildRostersenders (a guildmate, or a sister member while the switch is on). Also F4:SendBankerOwnersToskips the receiver's own guild's entries.Tests/xguild_spec.lua+1. Locations:Modules/Chat.lua,Modules/Guild.lua. - XGUILD-SETTINGS-001: officer settings crossed between sister guilds, and who runs each bank
character mostly did not. The operator, 2026-09-25: "the banker metadata isn't syncing though",
then "we should NOT be syncing the officer settings between sister guilds, this would allow any
guild to target another guild and force it into being a sister." A sister guild's bank character
passed
SenderHasGbankNote(the sister roster's note), its officersSenderIsOfficer(member.isOfficer) and its GMSenderIsGM(rank 0), soApplyRemoteSettingsadopted that guild's request limit, shop, discount, Sister-guild bank switch and rank floor as OURS whenever its stamp was newer. AndAnswerFederatedAskersent settings only when the answering client was a bank character or officer, while a pull usually asks a plain member -- so the owners rarely crossed. Now a sister member's payload yields only its own guild's bank characters' owners (merged entry by entry, the field stamp moved with it), from ANY member; the answer to a sister guild's ask is the owners alone (Guild:SendBankerOwnersTo), from any client. Supersedesdocs/XGUILD_SYNC.mdD1/D4/D6 on settings. Offline:Tests/xguild_spec.lua(+2, one existing example rewritten),Tests/xguildlive_spec.luastep 6 (the request limit no longer crosses). Locations:Modules/Guild.lua,docs/XGUILD_SYNC.md. - SCALE-DOCK-001: a Window and Text Size change pulled Search and Requests off the Inventory
window. Peer Review's residual on 593238c3: the scale listener re-applies AceGUI's status table,
whose
ClearAllPointsdropped the hand-made dock.UI:SetPersistedAnchorlets a window re-dock right after. Offline:Tests/browsepersist_spec.lua. Locations:Modules/UI.lua,Modules/UI/Search.lua,Modules/UI/Requests.lua. - SCALE-FLOOR-002: a released window could still be resized by a Window and Text Size change.
LibAceGUIWidgets now registers its own scale listeners -- the resize handle's and
PersistWindow's record (widget._lagwPersist) -- and both went into AceGUI's addon-wide pool with the frame.UI:ForgetPersistedWindownow removes the record's listener (through the library's publicOnScaleChanged(owner, nil); the library has no forget call, asked for on its inbox3d96003a) and puts a ClearFrame's handle back on the bounds it had beforeUI:PersistWindowraised them (recorded there ashandle._togOrigBounds). The handle is NOT destroyed: it is made once in the ClearFrame constructor, and my first version of this fix destroyed it, which would have left the frame's next owner with no resize grips -- caught by this session's own audit before release. Alsobrowsepersist_spec's SCALE-DOCK example used a width under the doubled floor, which the library now correctly raises. Offline:Tests/browsepersist_spec.lua(+1 ClearFrame example). Locations:Modules/UI.lua,Tests/browsepersist_spec.lua. - SETTINGS-STALE-001: an officer whose client still held an old or default setting could push it to
the whole guild by changing a DIFFERENT setting. Found live 2026-09-25: the Sister-guild bank was
unticked guild-wide, so the sister guild's inventory handshakes were answered "busy" by every client
(a non-member fails
isValidPeer,Modules/P2P.lua:136) while its request queries -- which have no membership gate -- kept crossing. The operator: "we need to make sure someone logging on for the first time doesn't uncheck it, as that's the default. how do we guard this?" SETTINGS-004's per-field stamps diff a write's payload against a snapshot of the last settings sent or applied, and that snapshot was EMPTY at two moments -- a session's first write before any settings were heard or sent, and every write after settings were RECEIVED (ApplyRemoteSettingscleared it) -- and an empty snapshot stamped EVERY field as freshly written. Now the snapshot is the held settings from the moment the record is bound (Guild:Init) and whenever settings are applied (Guild:SnapshotSettings), tied to that settings table; a write that still finds none diffs against the DEFAULTS (Guild:DefaultSettingsFields), so a default value is never stamped by a write that did not change it. KNOWN COST: v1.6.0 clients still have the old behaviour until they update, so an officer on v1.6.0 can still do this. Offline:Tests/settings_spec.lua, two examples, both red with the fix reverted. Locations:Modules/Guild.lua. - XGUILD-BOUNCE-001: a sister-guild bank character who logged off stayed "online" to the other
guild, which kept asking them for the bank. The operator, straight after v1.6.0: "build complete
end to end tests for the sister guild sync, still having issues with that". Guild Roster never
hears "X has gone offline." for another guild's member: its presence for one is a sighting stamp
that only ages out (
PRESENCE_TTL, 900 s) -- and its guild relay carries every name still inside that window and the receiver re-stamps them at receive time (LibGuildRoster-1.0.lua:3976,:4194,:3297, on a 270 s relay interval), so a member who logged off reads online for as long as two guildmates keep relaying (filed to Guild Roster's inbox 2026-09-17). The one authoritative "not online" a client gets for them is the server bouncing a whisper (ERR_CHAT_PLAYER_NOT_FOUND_S), whichEvents:CHAT_MSG_SYSTEMalready turned intoUpdateOnlineMember(X, false)-- a flag onmemberRosterthatIsPlayerOnline,FederationPeerand_AddSisterMembersnever read for a sister member. So the sister viewer whispered its hash-list ask to the logged-off banker, waited the whole cycle, marked them silent for an hour and only then asked the other member, while the Bankers tab said "yes". Now the bounce is remembered (Guild.sisterBounced, frame clock) and outranks the library's sighting for the library's own window (Guild:SisterSightingHolds, read by all three sites; the library's stamps are NOT compared against it, because a relay re-stamps within the minute -- measured); a TOGBank message from the member clears it at once; and when the bounced whisper was our ask, the ask is withdrawn, the member is left alone forFEDERATION_SILENT_FOR, and the guild is asked throughFederationPeer's next choice NOW (Guild:OnSisterMemberBounced). Design:docs/XGUILD_SYNC.mdD9. Offline:Tests/xguild_spec.lua(bounce, re-ask, clear, window) andTests/xguildlive_spec.luastep 8 on the real transport stack. Locations:Modules/Guild.lua. - LINKCLICK-001: a modified click on an item link does what it does everywhere else in the game.
The operator: "what shift+click and ctrl+click does to the links in the addon? i'd like it to
mirror game functionality". Read in both client trees this addon ships (
classic_era,classic_anniversary): a modified click on any item link runsHandleModifiedItemClick(Blizzard_ItemButton/Classic/ItemButtonTemplate.lua:137) --IsModifiedClick("CHATLINK")(Shift by default, but the PLAYER'S binding) inserts the link throughChatFrameUtil.InsertLink(the open chat box, else the auction house's search box, else a macro being edited),IsModifiedClick("DRESSUP")(Ctrl by default) previews it withDressUpItemLink. The bareChatEdit_InsertLinkexists on both clients ONLY as an alias of the real function inBlizzard_DeprecatedChatInfo/Deprecated_ChatFrame.lua:43, behindloadDeprecationFallbacks, and nothing in either tree calls it. Four defects against that: (1) the item slots (UI:EventHandler, Inventory and Search windows) readIsShiftKeyDown/IsControlKeyDown, so a player who rebound "chat link" or "dress up" got nothing; (2) the Browse and Shop rows handled Shift only, and a Ctrl-click REQUESTED the item; (3) every insert called the deprecated alias, nil with that CVar off; (4)Events:RegisterEventshooked the alias for the "shift-click fills the Search box" feature (UI:OnInsertLink), so the hook never saw a shift-click on a bag item or a chat line -- only this addon's own slots -- and raised outright with the CVar off. NowUI:HandleLinkClick(link)runs the client's dispatcher (the same logic spelled out for a client without it), consumes the click whenever a link modifier is held so no plain-click action runs under one, and is what every site calls;UI:InsertLinkprefersChatFrameUtil.InsertLink; the hook is on the function the client calls, the alias only as a fallback, and neither present raises nothing. The mail window's shift-click (take everything, MAILCLICK-002) is unchanged: the game's own inbox has no modified-click handling (MailFrame.lua:828), so there is nothing to mirror. Offline:Tests/linkclick_spec.lua(new) andTests/browse_spec.lua. Locations:Modules/UI.lua,Modules/UI/Inventory.lua,Modules/UI/Search.lua,Modules/UI/Browse.lua,Modules/Events.lua,.luarc.json,.luacheckrc. - SCALE-FLOOR-001: with Window and Text Size under 100%, a window at its minimum had its filter
strip past the border. A player's report against v1.6.0 (2026-09-17, the slider at 80%): "Window
size at minimum has widget overflow after resizing/reloading text in config". Two defects, both
mine from VISIBILITY-001. (1) LibAceGUIWidgets'
PersistWindowmultiplies a window's floor by the accessibility scale -- right for a window made of its own widgets, whose every region scales -- but TOGBank's floors are sums of FIXED widths too: the Browse strip's stock AceGUI dropdowns, edit boxes, checkbox and button (their template art is fixed;ScaleStockWidgetgrows only the text), the frame's insets, RowList's gutter. At 80% the Guild Bank window's floor fell from 784 to 627 px while the strip's first row still needed 738, so a window dragged to its minimum, or reloaded at a saved size the floor no longer raised, spilled. Now the pixel floor is never below the normal-size one (minW x max(1, scale), handed to the library asminW x max(1, scale) / scaleso its own multiply lands there). KNOWN COST: under 100% a window cannot be dragged smaller than it can at 100%; the text shrinks, the minimum does not. (2) The library re-applies a floor on a scale change only through a resize handle, which an AceGUI Frame (every TOGBank window) has none of, so the bounds set at draw time stayed at the old scale until the next reload -- a window at the 100% minimum kept it at 200% with twice the text inside (finding filed to LibAceGUIWidgets' inbox 2026-09-17; TOGBank's own listener is the stand-in).UI:PersistWindownow re-runs itself on the scale signal for every window -- Inventory, Search, Requests, Browse, Mailbox -- raising a window sitting under the new floor and re-applying the bounds; nothing shrinks on a scale-down. Offline:Tests/browsepersist_spec.lua(three examples against the real library and the real Requests floor; two fail without the fix at 627 vs 784); forward A4 882/0, forward B2 936/0, reverse-order 1818/0 after it. Locations:Modules/UI.lua,README.txt.
Internal
- SUITE-HEAP-002, harness pin 23d11b1.
browse_spec,requestsactions_specandmailwindow_specrelease their windows;env.releaseWindowdrops Requests' frame caches. TheGetClassColorstand-in andbrowse_spec's fake LibDBIcon are gone. Heap not measured. - UI-OWN-TABLE-001:
TOGBankClassic_UIis its own table, reading through to AceGUI-3.0; it was the shared library table itself (Peer Review 35130c29). Escape also closes the owner dialog. - OWNERS-STAMP-001, harness pin 35f697b. One
Guild:AdvanceOwnersStampfor both owners-merge routes (Peer Review 31294783). Harness 17d5211 -> 35f697b:GetClassColorstand-in, two specs on the rich layer,canonhash_specloadsInventory/Sync.lua, windows released in two specs. - USABLE-LIBITEMDB-001: "Usable by me" asks LibItemDB's
ClassProficient(ItemDB v0.10.0, MINOR 27; ItemDB inbox 10a85af75c12). The five proficiency tables copied from Dibs andUsable.ArmorCapare deleted. One answer changed on purpose: a class off the tables may hold a shield. Offline:Tests/usable_spec.lua,Tests/browse_spec.lua(the installed library). Locations:Modules/Usable.lua. - TOC-MISTS-001: a MoP Classic TOC, and the TBC TOC renamed
_BCC->_TBC(the operator, 2026-09-25: "make a mop toc", "update the _BCC toc to be _TBC"). NewTOGBankClassic_Mists.tocat Interface 50504: the Era TOC byte for byte apart from the Interface line. Every required dependency already ships MoP (Ace3, VersionCheck-1.0, AceCommQueue-1.0, DeltaSync, LibAceGUIWidgets and LibDBIcon-1.0 list 50504; GuildRoster and ItemDB ship their own_Mists.toc, ItemDB withData\Mists\item data). Loads in a MoP client with no Lua errors (the operator, 2026-09-25, on a character in no guild); NOT VERIFIED: every guild feature -- nothing past loading has run in MoP, and the code has not been read against the MoP API.TOGBankClassic_BCC.tocis nowTOGBankClassic_TBC.toc(same content), matching the fleet'sGuildRoster_TBC.toc/ItemDB_TBC.toc. New lockstep guardTests/wiring_spec.lua(TOC-LOCKSTEP-001): every TOC must equal the Era TOC apart from its Interface line; the ten specs that looped over two TOCs now loop over three.wow-version-replication.ps1now also mirrors into_classic_(MoP). Locations:TOGBankClassic_Mists.toc(new),TOGBankClassic_TBC.toc(renamed),.pkgmeta,wow-version-replication.ps1,CLAUDE.md,Tests/*_spec.lua. - LIBDBICON-DEP-001: LibDBIcon-1.0 is a required dependency, no longer vendored (TOGTools inbox
contract
1eab38f9, the operator's decision of 2026-09-25). An embedded copy only updates when TOGBank releases; as a CurseForge required dependency it updates for every player on its own.Libs/LibDBIcon-1.0/is deleted, every TOC declaresLibDBIcon-1.0in## Dependenciesand no longer load the copy, and.pkgmetalists the sluglibdbicon-1-0. LibDataBroker-1.1 stays bundled on the operator's word ("change it to not get rid of libdatabroker"), although the standalone LibDBIcon also loads one (LibDBIcon-1.0/embeds.xml:6); LibStub keeps whichever copy has the higher MINOR. No Lua changed --Modules/UI/Minimap.luastill resolves both through LibStub. KNOWN COST: a player installing by hand must also install LibDBIcon-1.0. Seen in an Era client 2026-09-25: the standalone (TOC## LoadOnDemand: 1) loads and the minimap button shows; TBC and MoP not checked. Pinned byTests/sendresult_spec.lua("de-vendored LibDBIcon-1.0"). Locations: the TOCs,.pkgmeta,README.txt,CLAUDE.md. - Peer review of this session's own audit, acted on (inbox
593238c3, reply8e933d44). Three of its four points changed code. (1)c.offlineis deleted fromTests/env_fleet.lua: one concept with two spellings, where the write-only one was the plausible-looking one, so a future guard written against it would have been true for a client taken offline throughF.offlineand false for one created withopts.offline. The reviewer also spotted thatTests/fullsync_spec.lua:864was a hand-inlinedF.offline-- the one call site that could not benefit from the change that gaveF.offlineits presence call -- so it now calls the helper and one of the three deletions came free. (2)GreenWall:Bridgeno longer claimsGetChannelNumbers()[1]is the guild channel, and the six-line caveat defending that claim is gone; it now accepts any joined channel. The reviewer's suggested#numbers == 0was NOT taken, with a reason:GwChannel:is_connectedcalls a channel connected only when its number is non-zero, so a channel that exists un-joined reports 0 and a send on it is PARKED for the next flush -- the later non-hardware send this gate exists to prevent. The non-zero test is load-bearing; only the claim about which entry it is was wrong. (3)Nudgenow stamps the cooldown on a refusal FROM GreenWall, so the one configuration that always fails there costs one attempt per cycle instead of one per window open; a refusal before the send still leaves the cooldown alone, so a client whose GreenWall loads a moment later does not wait a cycle. New example inTests/xguild_spec.lua. The fourth point was routed rather than fixed: the desk'sfile move-spanreports success after moving an incomplete span, which is writ's defect and is filed to its inbox. Locations:Modules/GreenWall.lua,Tests/env_fleet.lua,Tests/fullsync_spec.lua,Tests/xguild_spec.lua. - GSL-MERGE-001, first step: GuildShoppingList read end to end, and the merge design written
(
docs/GSL_MERGE.md, new). The operator, 2026-09-16: "add to the todo list to merge the GSL addon into TOGBank, put it at the bottom", with the item's own first step being the read and the design before any code. Nothing is built and no code was touched. What the read settled: GuildShopping- List is a second, weaker implementation of machinery this addon already has -- its own guild-note role marker ([GSL]), its own whole-state newest-timestamp-wins broadcast, its own name-keyed cache of ONE player's bags and bank, its own 290-line calendar picker, its own two frames -- wrapped around one genuinely new idea, expanding a recipe into reagent demand. So the merge is a new Craft List tab on the rails that already exist (the stamped guild-settings payload for the list, the request log for orders,Guild:GetAltItemTotalfor what the bank holds), not a port of its code.LibProfessionDB-1.0already carries the recipe data keyed by spell id with reagents keyed by item id, which deletes GSL's eight vendoredRecipes\*.luatables and removes its name-matching at the root -- the same defect class as REQ-001. Build step 1 was then run rather than assumed (section 1.5): GSL's eight tables diffed against the library's Vanilla enUS data, 924 of 1,106 names matching exactly, and the 182 that do not are GSL's own shorthand (Crusader (enchant)against the library'sEnchant Weapon - Crusader, whoseeffectfield is literallyCrusader;Blight (polearm)againstBlight), names the Vanilla data does not carry and which look like later-expansion recipes (recall, not a lookup -- flagged in the document as its weakest claim), and the Miscellaneous bucket, which is seven Winterspring E'ko turn-in items and not recipes at all. So the shorthand cause is verified and large, and the migration gained two name fallbacks. Both open decisions were answered 2026-09-25: GM, officers and the[GSL]note tag edit the list, on a Shopping List tab plus an officer setup tab; the build is the release after v1.6.1. Locations:docs/GSL_MERGE.md. - Harness pin
bb80c1b->17d5211; both XGUILD-LIVE-001 stand-ins are deleted, and the fleet now takes a REAL bounce. (Two deliveries in the same move:58ff098below, and4ca9566, which bumpedLibItemDB-1.0'stocOmits98 -> 99 after the drift this repo's newly-declaredverify-libssuite caught at the previous pin.verify-libsis now 9/9, 0 failed.) WoWAPITesting delivered the two halves contracted yesterday (58ff098). (1) A logged-out client sends nothing: all three of the harness's send seams are gated on the sender being online, soTests/env_fleet.luano longer shadows each client's ownC_ChatInfowhile it is offline. (2) The server answers the sender of a whisper to a logged-out character withERR_CHAT_PLAYER_NOT_FOUND_S, soF.bounceIfOffline-- which MANUFACTURED that line -- is gone. What replaced it is one frame per fleet client registered forCHAT_MSG_SYSTEMthat hands the event toEvents:CHAT_MSG_SYSTEM, because a fleet client never runsEvents:RegisterEvents; the bounce is now real and only its delivery is wired here, with every line kept onclient.sysLinesso a spec can assert what the SERVER said separately from how the addon reacted. Two things cost time and are worth the next session knowing: that frame must be held on the client rather than in a local, because the frames registry keeps its candidate lists WEAK and a frame nothing references is collected and silently stops hearing its event (it received nothing at all, which reads exactly like the harness not firing); andTests/xguildlive_spec.luastep 8 had to stop asserting WHICH whisper bounced. The stand-in only ever bounced whispers handed to it by TOGBank's own Core wrapper, so the first bounce was always that cycle's hash-list ask; the server bounces every whisper to a logged-out character, and the earliest here is the requests-index ask an earlier cycle staggered behind itself, so the viewer learns sooner and never spends the cycle's ask on the banker who is gone. The step now pins the outcome -- exactly one real bounce naming the banker, nothing further asked of it, every failover ask answered, the bank intact -- and the "our own ask bounced, re-ask at once" leg keeps its focused example inTests/xguild_spec.luastep 4. Green in all three orders: forward A4 882/0, forward B2 939/0, reverse-order 1821/0. Locations:Tests/wowapi(pointer),Tests/env_fleet.lua,Tests/xguildlive_spec.lua. - Harness pin
6181ba5->bb80c1b; the LAGW-RESIZE-FILE-001 stand-in is deleted. The contract filed earlier today (threada6cfd6a8) was answered ALREADY SHIPPED: WoWAPITesting had landed2aac908--LibAceGUIWidgets-Resize.luain theLibAceGUIWidgets-1.0manifest, in TOC order,tocOmitsunchanged -- hours before the message, so the fix was a pin move and nothing else. The wrapperTests/env_togbank.luaput roundlibs.loadis gone. Three more behaviour changes ride the same move and needed nothing here, each checked rather than assumed:8b36d23routes the addon-message chatType case-insensitively (TOGBank's own report, inbox22460a4b-- the recorded spelling is unchanged, so FILL-ALERT-001's "GUILD" assertions are untouched);bcd81e3keysSettings.GetCategoryon the numeric category id, which is what makes anAddToBlizOptionsSUB-panel resolvable offline at all;82387faaddsC_Spell.GetSpellNameandC_Seasons. The Adoption log's warning for a library that splits a file -- a per-file load line is needed in any spec usingwow.loadAddonFileas well as the manifest line -- does not reach this addon: the one by-path satellite load here (browse_spec.lua:326, the DatePicker) runs afterlibs.load. Green on the new pin in all three orders: forward A4 882/0, forward B2 939/0, reverse-order 1821/0. Locations:Tests/wowapi(pointer),Tests/env_togbank.lua. - XGUILD-LIVE-001: the sister-guild sync, whole lifecycle, on the real transport stack
(
Tests/xguildlive_spec.lua, new). Four whole clients in two guilds over each client's real AceCommQueue, AceComm and ChatThrottleLib, in ONE example so each leg starts from the state the previous one left: both guilds log in on the clamp; one "Pull from" by name crosses the rosters, thegbanknotes and presence on the library's own whispered pull and click-sent /who; both Bankers tabs list the other guild's bank, tagged; the sister viewer's cycle asks by whisper and holds the home bank with the author's canon and nothing between the guilds on GUILD; a sister member's order and the banker's fill (fromMail:ApplyPendingSend) cross by whisper at ALERT and are relayed once each way until all four clients agree; an officer's settings write and a who-runs entry ride the next ask without wiping the sister guild's own entry; the sister banker, which never pulled, reaches the home bank through its guildmate's relay and the home banker holds the sister bank; the home banker logs off (step 8, above); a quiet cycle opens no data leg. What its first runs found, 2026-09-17: (a) theinvalid key to 'next'the previous session stopped on was MY spec bug --next(F.held(hb, SB))passedF.held's second return, the money total, as the key -- and steps 5-7 were passing behind it; (b) the harness lets a logged-out client's timers keep SENDING (six Guild Roster messages measured in the 660 s after logout, each re-stamping the banker online on the sister side) and drops a whisper to an offline character silently where the server bounces it -- both stood in byTests/env_fleet.lua(F.offlineshadows the client's ownC_ChatInfosend seams,F.bounceIfOfflinehands the bounce toEvents:CHAT_MSG_SYSTEM) and filed as harness contract XGUILD-LIVE-001;F.presenceno longer clears the library's stamps by hand, which had let the failover leg pass without the bounce the addon has to act on; (c) the Guild Roster relay finding and (d) XGUILD-BOUNCE-001, above. Two more spec gaps the same runs surfaced, both mine from earlier sessions:bankcollect_specnever loadedInventory/RecordandInventory/Scan, which Bank's matchers have read since LINK-AUDIT-001 step 4 -- it passed in the declared halves on a Scan an earlier file left behind and failed alone (REQ-003, two examples); and LibAceGUIWidgets v0.2.0 moved its resize framework intoLibAceGUIWidgets-Resize.lua, which the harness's hand-copied manifest does not list, soPersistWindowcalled a nilGetResizeHandlein 103 window examples --Tests/env_togbank.lualoads the satellite when the library comes up without it (LAGW-RESIZE-FILE-001, harness contract filed; delete on adoption). The WHOLE forward run no longer finishes inside the desk's 15-minute budget, so the full-suite claim is the declared halves: forward A4 (altcompat..logwho incl.linkclick_spec, declared today) 879/0, forward B2 (mailbox..xguildlive, declared today) 936/0, reverse-order 1815/0 -- after LINKCLICK-001 as well. - MDLINT-BACKLOG-001: every Markdown file in the repo lints clean. A whole-repo
markdownlint(60 files) reported 100 violations, all pre-existing and all in five design docs nobody had linted since they were written:docs/MAIL_INVENTORY_DESIGN.md(31),docs/ORDER_FULFILLMENT_LOGIC.md(18),docs/MAIL_PERSISTENCE_INVESTIGATION.md(16),docs/TESTING.md(a UTF-8 byte-order mark, 8 headings, a table's pipe spacing, a double blank line) anddocs/fulfill-button-plan.md(10 headings, a table). Every one was a blank line missing after a heading (MD022) or a table row's pipe spacing (MD060) bar the BOM and the blank; the text is unchanged. None of these files ships (docsis in.pkgmeta's ignore); the point is that the whole-tree lint is now a usable gate.
Releases v1.6.0 and older have been moved to
CHANGELOG_ARCHIVE.mdto keep this file under GitHub's 125,000-character release-body limit. Nothing was deleted; each section moved whole, at a version boundary.
This mod has no additional files