From d044a76501d0df2d3d2e4e476e7b6f17f976c58d Mon Sep 17 00:00:00 2001 From: Joseph Gettings Date: Fri, 14 Aug 2026 17:38:26 -0500 Subject: [PATCH] feat: validate declarative heartbeat --- README.md | 32 +++++++++++++++++++------------- RGX-Hello.toc | 4 ++-- RGX-Hello_Cata.toc | 4 ++-- RGX-Hello_Mists.toc | 4 ++-- RGX-Hello_TBC.toc | 4 ++-- RGX-Hello_Vanilla.toc | 4 ++-- RGX-Hello_Wrath.toc | 4 ++-- data/core.lua | 18 ++++++++++-------- data/visualtest.lua | 13 +++++++------ docs/CHANGES.md | 21 ++++++++++++++++----- docs/changelogs/1.3.0.md | 11 +++++++++++ 11 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 docs/changelogs/1.3.0.md diff --git a/README.md b/README.md index 6604920..a4d0468 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,16 @@ The [RGX-Framework](https://github.com/DonnieDice/RGX-Framework) reference addon **and** its in-game testing suite, in one install. It exists for two audiences at once: +> This source branch targets the unreleased `v1.3.0` candidate and requires the +> RGX-Framework `v2.7.0` candidate. The latest published pair remains RGX-Hello +> `v1.2.1` with RGX-Framework `v2.6.2`. + - **Addon developers** — `data/core.lua` is the canonical "hello world": the smallest complete RGX addon, written in the declarative `RGXAddon` style you should copy when starting your own. - **Framework development** — `data/visualtest.lua` is the visual QA harness used to test RGX-Framework's features in-game before releases. As the framework grows, this suite grows with it; the goal is coverage of **every** framework feature. -## Built with rgx-mcp +## Source Contract Conformance -RGX-Framework temporarily maintains an MCP server at `tools/rgx-mcp/` as a source-tree contract-conformance fixture. It is excluded from the published Framework addon; public API/MCP/editor tooling belongs to RGX Studio. RGX-Hello remains wired into the transition fixture in both directions: +RGX-Framework temporarily maintains an MCP transport at `tools/rgx-mcp/` as a private source-tree contract-conformance fixture. It is not part of the framework runtime or its published addon; public API/MCP/editor tooling belongs to the later RGX Studio product. RGX-Hello remains wired into the fixture in both directions: - `rgx_generate_addon` can reproduce `data/core.lua`'s structure from a short spec — the hand-written file and the generator's output are kept convergent. - The framework's end-to-end test (`tools/rgx-mcp/test/test-rgx-hello.mjs`) runs the real MCP server against **this repo**: it validates `core.lua`'s opts table against the schema and audits every Lua file here for unsafe patterns. If this repo drifts from the contract, the framework's own test fails. @@ -27,6 +31,15 @@ RGXAddon "RGX-Hello" { volume = 50, }, + every = { + heartbeat = { 1, function(self, timer) + self.heartbeatTicks = (self.heartbeatTicks or 0) + 1 + if self.heartbeatTicks >= 3 then + self:CancelTimer(timer) + end + end }, + }, + options = { General = { { toggle = "enabled", label = "Enable Addon" }, @@ -36,20 +49,13 @@ RGXAddon "RGX-Hello" { onInit = function(self) self:Print("Hello from RGX-Hello!") - self.heartbeatTimer = self:Every(1, function(timer) - self.heartbeatTicks = (self.heartbeatTicks or 0) + 1 - if self.heartbeatTicks >= 3 then - self:CancelTimer(timer) - self.heartbeatTimer = nil - end - end, "RGX-Hello:heartbeat") end, welcome = "loaded -- /rgxhello for options", } ``` -That single call gives you saved settings with automatic persistence, a tabbed options panel with db-bound controls, a slash command, a minimap button, branded chat output, and framework-scoped startup/timer logic. No event frames, no `C_Timer`, no `SLASH_X` globals, no SavedVariables boilerplate. +That single call gives you saved settings with automatic persistence, a tabbed options panel with db-bound controls, a named repeating timer, a slash command, a minimap button, branded chat output, and framework-scoped startup logic. No event frames, no `C_Timer`, no `SLASH_X` globals, no SavedVariables boilerplate. ## The Testing Suite (`data/visualtest.lua`) @@ -73,13 +79,13 @@ Current coverage: | Auras | `RGXAuras` — `IterateAuras` scan, `WatchUnit` + `OnApplied`/`OnRemoved` live log with unsubscribe | | Minimap | `RGXMinimap` — `MM:Create` (icon, tooltip, drag, persistent angle), `Toggle`/`IsShown` | | Design | `RGX:Font` one-call styling, `RGXDesign` `CreateButton`/`CreateSectionHeader`/`CreateDivider`, theme tokens | -| System | reference-addon heartbeat via `addon:Every`, `RGX:After`, `RGX:Every`, `RGX:CancelTimer` | +| System | declarative `every.heartbeat` self-cancellation, `RGX:After`, `RGX:Every`, `RGX:CancelTimer` | Sound is intentionally untested here — the sound module is a per-addon registry that [BLU](https://github.com/DonnieDice/BLU) exercises in production, which is a more honest test than a synthetic registration. Standing pattern: when a framework module ships or changes, its test tab lands here in the same cycle. ## Installation -1. Install [RGX-Framework](https://github.com/DonnieDice/RGX-Framework) (required dependency, v2.4.1+). +1. Install [RGX-Framework](https://github.com/DonnieDice/RGX-Framework) (required dependency, v2.7.0+). 2. Copy the `RGX-Hello` folder to `World of Warcraft\_retail_\Interface\AddOns\`. 3. `/reload` or restart, and enable both addons. @@ -87,7 +93,7 @@ Sound is intentionally untested here — the sound module is a per-addon registr 1. Copy the repo; rename the folder, `RGX-Hello.toc`, and `RGX-Hello.xml` to your addon's name. 2. Edit the TOC header (`Title`, `Notes`, `Author`, `SavedVariables`). -3. Edit the `RGXAddon "..." { }` call in `data/core.lua` — `db` for settings, `options` for the panel, `on` for triggers, and `every` for repeating work. +3. Edit the `RGXAddon "..." { }` call in `data/core.lua` — `db` for settings, `options` for the panel, `every` for repeating work, and `onInit` for event/setup code while declarative `on` remains Tier 4. 4. Delete `data/visualtest.lua` (and its TOC/XML lines) — it tests the framework, not your addon. 5. Replace `media/icon.tga`, or drop the `minimap` key. diff --git a/RGX-Hello.toc b/RGX-Hello.toc index 225584b..c136049 100644 --- a/RGX-Hello.toc +++ b/RGX-Hello.toc @@ -1,12 +1,12 @@ ## Interface: 120100 -## Version: 1.2.1 +## Version: 1.3.0 ## Title: RGX-Hello ## Notes: The smallest complete RGX-Framework addon -- a reference/template for building on RGX, plus its visual QA harness (/rgxvisual, /rgxcolor). ## Author: DonnieDice ## Category: RGX ## Group: RGX ## RequiredDeps: RGX-Framework -## X-RGX-Framework-MinVersion: 2.6.0 +## X-RGX-Framework-MinVersion: 2.7.0 ## SavedVariables: RGXHelloDB, RGXVisualTestDB ## IconTexture: Interface\AddOns\RGX-Framework\media\logo.tga diff --git a/RGX-Hello_Cata.toc b/RGX-Hello_Cata.toc index 634f99c..f7c8b45 100644 --- a/RGX-Hello_Cata.toc +++ b/RGX-Hello_Cata.toc @@ -1,12 +1,12 @@ ## Interface: 40402 -## Version: 1.2.1 +## Version: 1.3.0 ## Title: RGX-Hello ## Notes: RGX-Framework reference and compatibility test addon ## Author: DonnieDice ## Category: RGX ## Group: RGX ## RequiredDeps: RGX-Framework -## X-RGX-Framework-MinVersion: 2.6.0 +## X-RGX-Framework-MinVersion: 2.7.0 ## SavedVariables: RGXHelloDB, RGXVisualTestDB ## IconTexture: Interface\AddOns\RGX-Framework\media\logo.tga diff --git a/RGX-Hello_Mists.toc b/RGX-Hello_Mists.toc index cc18e48..eaab52a 100644 --- a/RGX-Hello_Mists.toc +++ b/RGX-Hello_Mists.toc @@ -1,12 +1,12 @@ ## Interface: 50504 -## Version: 1.2.1 +## Version: 1.3.0 ## Title: RGX-Hello ## Notes: RGX-Framework reference and compatibility test addon ## Author: DonnieDice ## Category: RGX ## Group: RGX ## RequiredDeps: RGX-Framework -## X-RGX-Framework-MinVersion: 2.6.0 +## X-RGX-Framework-MinVersion: 2.7.0 ## SavedVariables: RGXHelloDB, RGXVisualTestDB ## IconTexture: Interface\AddOns\RGX-Framework\media\logo.tga diff --git a/RGX-Hello_TBC.toc b/RGX-Hello_TBC.toc index 71cc0b2..0857da2 100644 --- a/RGX-Hello_TBC.toc +++ b/RGX-Hello_TBC.toc @@ -1,12 +1,12 @@ ## Interface: 20506 -## Version: 1.2.1 +## Version: 1.3.0 ## Title: RGX-Hello ## Notes: RGX-Framework reference and compatibility test addon ## Author: DonnieDice ## Category: RGX ## Group: RGX ## RequiredDeps: RGX-Framework -## X-RGX-Framework-MinVersion: 2.6.0 +## X-RGX-Framework-MinVersion: 2.7.0 ## SavedVariables: RGXHelloDB, RGXVisualTestDB ## IconTexture: Interface\AddOns\RGX-Framework\media\logo.tga diff --git a/RGX-Hello_Vanilla.toc b/RGX-Hello_Vanilla.toc index 1b3e189..5d119f2 100644 --- a/RGX-Hello_Vanilla.toc +++ b/RGX-Hello_Vanilla.toc @@ -1,12 +1,12 @@ ## Interface: 11509 -## Version: 1.2.1 +## Version: 1.3.0 ## Title: RGX-Hello ## Notes: RGX-Framework reference and compatibility test addon ## Author: DonnieDice ## Category: RGX ## Group: RGX ## RequiredDeps: RGX-Framework -## X-RGX-Framework-MinVersion: 2.6.0 +## X-RGX-Framework-MinVersion: 2.7.0 ## SavedVariables: RGXHelloDB, RGXVisualTestDB ## IconTexture: Interface\AddOns\RGX-Framework\media\logo.tga diff --git a/RGX-Hello_Wrath.toc b/RGX-Hello_Wrath.toc index 55f9883..60f641b 100644 --- a/RGX-Hello_Wrath.toc +++ b/RGX-Hello_Wrath.toc @@ -1,12 +1,12 @@ ## Interface: 38002 -## Version: 1.2.1 +## Version: 1.3.0 ## Title: RGX-Hello ## Notes: RGX-Framework reference and compatibility test addon ## Author: DonnieDice ## Category: RGX ## Group: RGX ## RequiredDeps: RGX-Framework -## X-RGX-Framework-MinVersion: 2.6.0 +## X-RGX-Framework-MinVersion: 2.7.0 ## SavedVariables: RGXHelloDB, RGXVisualTestDB ## IconTexture: Interface\AddOns\RGX-Framework\media\logo.tga diff --git a/data/core.lua b/data/core.lua index 872f29c..9aa4200 100644 --- a/data/core.lua +++ b/data/core.lua @@ -14,7 +14,7 @@ -- - a minimap button (minimap) -- - branded chat output (welcome, self:Print) -- - startup and repeating timer logic routed through RGX paths -- never a --- manual event frame or C_Timer (onInit, self:Every) +-- manual event frame or C_Timer (onInit, declarative every) --===================================================================================== RGXAddon "RGX-Hello" { @@ -27,6 +27,15 @@ RGXAddon "RGX-Hello" { volume = 50, }, + every = { + heartbeat = { 1, function(self, timer) + self.heartbeatTicks = (self.heartbeatTicks or 0) + 1 + if self.heartbeatTicks >= 3 then + self:CancelTimer(timer) + end + end }, + }, + options = { General = { { toggle = "enabled", label = "Enable Addon" }, @@ -36,13 +45,6 @@ RGXAddon "RGX-Hello" { onInit = function(self) self:Print("Hello from RGX-Hello!") - self.heartbeatTimer = self:Every(1, function(timer) - self.heartbeatTicks = (self.heartbeatTicks or 0) + 1 - if self.heartbeatTicks >= 3 then - self:CancelTimer(timer) - self.heartbeatTimer = nil - end - end, "RGX-Hello:heartbeat") end, welcome = "loaded -- /rgxhello for options", diff --git a/data/visualtest.lua b/data/visualtest.lua index 45c8d1c..fc15d95 100644 --- a/data/visualtest.lua +++ b/data/visualtest.lua @@ -590,27 +590,28 @@ local function BuildSystemTab(frame) local add = Place(frame) add(UI:CreateLabel(frame, { - text = "Timers Visual Test (RGX:After / RGX:Every)", + text = "Timers Visual Test (declarative every / RGX:After / RGX:Every)", size = "large", color = "accent", })) local referenceAddon = R:GetAddon("RGX-Hello") local heartbeatLabel = UI:CreateLabel(frame, { - text = "Reference addon heartbeat: not checked", + text = "Declarative every.heartbeat: not checked", size = "small", color = "normal", width = 340, }) local function RefreshHeartbeatStatus() local ticks = referenceAddon and referenceAddon.heartbeatTicks or 0 - heartbeatLabel:SetText("Reference addon heartbeat: " .. ticks .. "/3 ticks" - .. (ticks == 3 and " (self-cancelled: PASS)" or " (wait and check again)")) + local result = ticks == 3 and " (self-cancelled: PASS)" + or (ticks > 3 and " (FAILED: timer did not stop)" or " (wait and check again)") + heartbeatLabel:SetText("Declarative every.heartbeat: " .. ticks .. "/3 ticks" .. result) end RefreshHeartbeatStatus() add(heartbeatLabel) - local heartbeatBtn = UI:CreateButton(frame, "Check reference heartbeat", 200, 26) + local heartbeatBtn = UI:CreateButton(frame, "Check declarative heartbeat", 210, 26) heartbeatBtn:SetScript("OnClick", RefreshHeartbeatStatus) add(heartbeatBtn, 12) @@ -645,7 +646,7 @@ local function BuildSystemTab(frame) add(tickLabel, 16) add(UI:CreateLabel(frame, { - text = "What to test: the reference addon's scoped heartbeat reaches 3 and self-cancels; After fires exactly once ~2s after the click; imperative Every increments the counter each second; Stop must freeze it immediately (CancelTimer). Sound is intentionally not tested here -- BLU exercises the sound registry in production.", + text = "What to test: declarative every.heartbeat reaches exactly 3 and self-cancels. Wait at least 5 more seconds, click Check again, and confirm it remains 3. After fires exactly once ~2s after the click; imperative Every increments each second; Stop freezes it immediately (CancelTimer). Sound is intentionally not tested here -- BLU exercises the sound registry in production.", size = "small", color = "muted", width = 340, diff --git a/docs/CHANGES.md b/docs/CHANGES.md index ef96bc2..33abda9 100644 --- a/docs/CHANGES.md +++ b/docs/CHANGES.md @@ -2,11 +2,22 @@ ## Current Development -- The canonical addon uses the shipped `onInit` and `addon:Every` APIs for its - three-tick heartbeat while declarative `on`/`every` remain future forms. -- The System tab displays the heartbeat result for in-game timer verification. -- Updated MCP distribution wording: Node tooling is maintained with the - framework source but excluded from the WoW player archive. +### [v1.3.0](changelogs/1.3.0.md) (Expected) + +- Moved the reference heartbeat from imperative `addon:Every` setup into + `every.heartbeat`, exercising Framework v2.7.0's shipped named timer contract. +- Added explicit in-game verification that the timer reaches 3 ticks, + self-cancels, and remains at 3. +- Raised the minimum Framework version to 2.7.0 across all six flavor TOCs. + +## Current Release + +### [v1.2.1](changelogs/1.2.1.md) - 2026-08-13 + +- Replaced premature declarative `on`/`every` usage with shipped imperative + APIs until the named timer vertical slice could be rebuilt cleanly. +- Preserved the three-tick heartbeat and corrected Framework/MCP distribution + wording and Retail metadata. ## [v1.2.0](changelogs/1.2.0.md) - 2026-07-04 diff --git a/docs/changelogs/1.3.0.md b/docs/changelogs/1.3.0.md new file mode 100644 index 0000000..7adc133 --- /dev/null +++ b/docs/changelogs/1.3.0.md @@ -0,0 +1,11 @@ +# RGX-Hello 1.3.0 + +Status: unreleased source candidate pending RGX-Framework 2.7.0 and the required +in-game heartbeat validation. + +- Requires RGX-Framework v2.7.0 or newer. +- Moves the three-tick reference heartbeat into the shipped declarative + `every.heartbeat` timer form. +- Keeps startup output in `onInit`; declarative `on` remains Tier 4. +- Updates the System test tab to verify the heartbeat reaches exactly 3, + self-cancels, and remains at 3 after at least five additional seconds.