SwedgeTimer v2.2.0
What's new
[2.2.0]
Verification status: offline gate only — bash tests/run.sh 59 steps / 0 failed; luacheck . 4 warnings / 0 errors in 105 files. In-client behaviour on the Forever client is UNCONFIRMED; no in-client pass is recorded for 2.2.0.
Fixed
- Queued-shot and queued-Maul detection (
classes/hunter.lua,classes/druid.lua):api.IsCurrentSpellis the client predicate (C_Spell.IsCurrentSpell(spellIdentifier)), not a getter. Raptor Strike / Steady Shot / Maul detection now passes each queue token (api.IsCurrentSpell(token)) instead of calling it with no argument; the no-argument form throwsbad argument #1 to 'IsCurrentSpell'on Forever when a macro queues the shot (21 in-client errors), and returned nothing on the other clients. The test sandboxes now raise on a nil token, so the bad-argument contract is pinned. - Druid rage-cost probe (
classes/druid.lua): the cost read is guarded byspell_idbeforepcall(api.GetSpellPowerCost, spell_id); an unresolvable/absent rank no longer issues a nil spell query (the prior nil-safe rewrite made one probe per unresolved rank, failing the F4 no-query contract).
Changed
- Version 2.2.0:
SwedgeTimer.tocandmain.luareport version2.2.0(the TOC remains the single load order for every client). - Bar tick clock (
bars.lua): the tick reads the live client clock (st.api.get_time()) exactly once per tick. Accumulating theOnUpdatedelta into a cached clock was removed: a bar'sOnUpdatepauses while its frame is hidden and across loading screens, so the cached reading resumes stale and the fill sits at zero until it replays the hidden gap (the "bars display but never function" symptom). The single live reading is shared by the flash fade, the text timers and every paint path; the no-cycle early-return paint reuses it. The fill-last and 2026-10-28 resize-repaint guarantees are unchanged. - Aura icon rows (
aura_icons.lua,option_tables.lua,config_presets.lua): the buff and debuff rows now have separate width and height. Shipped default is 27 px wide × 20 px high (previously a square 27 × 27; the 2026-11-09 width raise is kept). The native group layout pusheselementWidth/elementHeight, newly created and already-pooled buttons useSetSize(width, height), and the reserved debuff band is computed from the icon height (so a width-only change no longer moves the rows). Options expose "Icon width" (aura_icons_size, 12–48) and "Icon height" (aura_icons_height, 12–48). - OnUpdate call contract (
main.lua): the installed frame script now adapts the client's(frame, elapsed)calling convention to the hand handler's singleelapsedargument (function(_, elapsed) onupdate(elapsed) end). The previous direct install passed the frame aselapsed, which threwattempt to perform arithmetic on a table valueatbars.lua:1145on the first warm tick in game. The diag counter wrapper keeps its one-argument contract and is adapted at the same boundary. - Vendored swing library (
Libs/LibClassicSwingTimerAPI/LibClassicSwingTimerAPI.lua): every attack-speed/timer read is now probed withcanaccessvaluebefore arithmetic orC_Timerscheduling, so a restricted speed read leaves the cycle on its scheduled stop instead of throwing. This is a local patch to a vendored file; see the 2.2.0 audit note about keepingLibs/upstream-clean. - Compat (
compat.lua):api.UnitAttackSpeednow returns only the main-hand and off-hand speeds (the ranged speed had no consumer;swing_source.luakeeps reading the raw client triple).
Hardening
- Secret-value access probes:
compat.lua,hunter_ui.lua,enemy_swing.lua,forever_guide.lua,swing_source.lua,LibGlobalCooldown.luaand the vendored swing library callcanaccessvalue/canaccessallvaluesbefore comparing, formatting or doing arithmetic on client reads (cooldown start/duration, cast start/end, attack speeds, target name/GUID, the cast-bar key). - Nil-safe guards: broad nil/type guards across
aura_icons.lua,bars.lua,designmode.lua,diag.lua,main.lua,option_tables.lua,power_bar.lua,practice.lua,practice_progress.lua,practice_progress_ui.lua,practice_ui.lua,rogue_guide.lua,weave_range.luaandwidgets.luaso a missing profile row, table or client answer returns early instead of throwing. - Aura rows (
aura_icons.lua): row construction tolerates a missing row/state/list and no longer sets an explicit frame strata on the native container at creation; the client default applies. - Design-mode reset (
designmode.lua): the bar reset seam targets the class defaults row (the same sourcehand_defaultreads) and hands the live row toLibWindow.RestorePosition. - Options (
option_tables.lua): hand option generation returns early for an unknown hand row, and order offsets tolerate a nil offset.
Tests
- 27 suites refreshed for the new contracts: every sandbox that loads
compat.lua/swing_source.lua/forever_guide.luanow provides the client'scanaccessvalue/canaccessallvaluesprobes (secret-aware where the suite models restricted values), the strictforever_guide.luaallow-list includestonumberand both probes, thebars.luatick drives the handler's declared single-elapsedcontract and pins the cold/warm cached-clock reads, theaura_icons.luastrata expectation follows the removed explicit set, thedesignmode.luareset checks follow the class-defaults-row target, and themain.luatimers_lockedsource pin follows the current(db_class or {})read.
This mod has no additional files

