File Details
v0.10.29
- R
- Mar 20, 2026
- 5.77 MB
- 110
- 12.0.1+1
- Retail
File Name
Cerebro-v0.10.29.zip
Supported Versions
- 12.0.1
- 11.2.7
Cerebro
v0.10.29 (2026-03-20)
- Merge pull request #35 from Shezzannn/feature/guild-roster-sync-dashboard
feat(standings): Guild Roster Sync Dashboard with all PR review fixes - chore: Bump version to 0.10.29
Guild Roster Sync Dashboard: combat guard on guild scan and heartbeat UI refresh; LWW sentinel for auto-discovered players; semver version comparison; cached online-member lookup; DRY sync health helper; correct no_addon (red) / unknown (gray) colors; stale addonStatus pruning; config hash convergence fix (sync.lastUpdate excluded, fixes 25/25 to 1/25 every-5-min bounce) - fix(standings): apply all 13 PR #35 review fixes
Critical:- Fix 1: Add InCombatLockdown() guard in HandleHeartbeat addon-state refresh timer;
add PLAYER_REGEN_ENABLED post-combat flush in CommunicationV2 - Fix 2: Add not InCombatLockdown() guard before DiscoverFullGuildRoster in
GROUP_ROSTER_UPDATE (prevents mid-boss DB writes + RecalculateAllPR) - Fix 3: Correct comment GUILD_ROSTER_UPDATE -> GROUP_ROSTER_UPDATE
- Fix 4: Remove duplicate INACTIVE_THRESHOLD local; expose as
CommunicationV2.INACTIVE_THRESHOLD module constant - Fix 5: DiscoverFullGuildRoster creates Pending+lastUpdate=0 stubs (LWW sentinel)
instead of Core+real timestamp to prevent auto-discovery winning over officer data
Important: - Fix 6: Add CommunicationV2:CompareVersions() semver helper; replace all
lexicographic v < maxVersion comparisons in GetAddonState + ShowSyncHealthTooltip - Fix 7: Add CommunicationV2:GetOnlineGuildMembers() with 10s TTL cache;
replaces O(N) guild scan per standings row in GetAddonState - Fix 8: Add StandingsFrame:ComputeSyncHealth() DRY helper; refactor Draw()
and Refresh() to call it instead of duplicating the sync-pct block - Fix 9: Split no_addon (red, online+no addon) from unknown (dim gray,
offline/indeterminate) in GetAddonStateColor per design intent
Minor: - Fix 10: Prune addonStatus entries older than 7 days in InitSyncState
- Fix 11: Add GetMaxAddonVersion() with _cachedMaxVersion cache; invalidate on HB
- Fix 12: Anchor _dev pattern to end of string (_dev$) everywhere
- Fix 13: Remove tools/inspect_pb.ps1 (unrelated debug utility)
- Fix 1: Add InCombatLockdown() guard in HandleHeartbeat addon-state refresh timer;
- chore(tasks): add PR #35 fix task tracker and implementation plan
13 fixes across Core.lua, CommunicationV2.lua, StandingsFrame.lua:- Critical: combat guards, LWW safety, class on restore
- Important: semver helper, guild lookup cache, sync health DRY, color split
- Minor: addonStatus pruning, version cache, _dev anchor, ps1 removal
- fix(standings): resolve 4 bugs found in deep-dive PR review
Bug 1 (StandingsFrame.lua:184) — broken WoW color code
GetAddonStateTooltip used|cfffe8<000for the outdated state label.
The<is not valid hex; WoW ignores the color and renders as plain
text. Fixed to|cfffe8700(matching the tooltip row color).
Bug 2 (Core.lua:728) — DiscoverFullGuildRoster skips Pending stubs
Race condition: GROUP_ROSTER_UPDATE fires immediately on login and
DiscoverGuildGroupMembers creates Pending stubs (lastUpdate=0) for
all raid members. The 10s login timer then fires DiscoverFullGuildRoster,
which skipped anyone already in players — including those fresh stubs.
Result: visible-rank raiders stayed Pending, defeating the feature's
primary purpose. Fix: detect the sentinel (status=Pending, lastUpdate=0)
and upgrade stubs to Core, preserving ep/gp/class from the stub.
Bug 3 (StandingsFrame.lua:407) — Restore creates classless record
ShowRemovedPlayersPopup Restore path created the new Core record
without a class field. Fixed: look up class from guild roster before
creating the record (same pattern as MergeRosterData/Admit to Core).
Bug 4 (StandingsFrame.lua:1104) — _removedPlayersLabel shows "0 removed"
After the last purged/blocked player is restored/unblocked, Refresh()
kept updating the label text to "| 0 removed" with no way to hide it
short of closing the window. Fixed: clear the label text to "" when
count == 0 so the widget takes no visual space.
Luacheck: 0 warnings / 0 errors in 2 files - fix(sync): exclude sync.lastUpdate from config hash computation
sync.lastUpdate is a merge-metadata LWW timestamp stored inside
config.sync. ComputeConfigHash()'s recursive serializer was including
it, causing two officers with identical settings but different save
timestamps to compute permanently different config hashes — the
ping-pong loop seen in debug logs (0b565077 vs 690de2b8 vs c0583603).
Fix: add EXCLUDED_NESTED_KEYS = inside the
serialize() closure so LWW timestamps are never part of the content
hash, regardless of which config section contains them.
Luacheck: 0 warnings / 0 errors
validate_docs.py: all checks passed - fix(standings): sync bar now requires both roster AND config hash match
Previously only rosterHash was checked, causing 100% (9/9) to display
even when configHash diverged on 6 of 9 peers. Now a peer is only
counted as fully synced when both hashes match. The tooltip detailed
breakdown (roster vs config separately) is unchanged for diagnostics. - fix(standings): apply Offline steel blue-gray to guild rank column
no_addon and unknown states now both use {0.45, 0.50, 0.62} in
GetAddonStateColor, matching the tooltip Offline color. Red (no_addon)
and dim gray (unknown) are consolidated since both represent offline. - fix(standings): improve Offline tooltip color readability
Changed from near-black (0.28, 0.28, 0.28) to steel blue-gray (0.45, 0.50, 0.62).
Readable against dark tooltip background, cool tone suggests 'disconnected',
clearly distinct from the warm neutral gray of Inactive. - feat(standings): rewrite addon compliance tooltip counts
- Compliance now scoped to visible guild roster (visibleRanks filtered,
excluding purged/blocked) instead of raw addonStatus table - Self (local officer) now counted in 'Synced & current'
- Replaced 'No addon detected' with 'Offline' bucket (dimmer gray):
synced = in addonStatus, recent, current version
outdated = in addonStatus, recent, old version
inactive = in addonStatus, >90s ago (darker gray)
offline = never in addonStatus at all (darkest gray, 0.28) - Total now matches visible standings player count
- Sync: 100% label turns green in both Draw() and Refresh()"
- Compliance now scoped to visible guild roster (visibleRanks filtered,
- feat(standings): split sync bar into two separate hover zones
- ShowSyncHealthTooltip: removed the inline removed-players list
- ShowRemovedPlayersTooltip: new fn showing only purged/blocked names
- Draw(): sync bar is now two InteractiveLabels:
'Sync: X% (N/M)' -> hover = hash/compliance/epoch stats
'N removed' -> hover = removed players list, click = popup - Refresh(): updates both labels independently"
- fix(standings): include self in tooltip peer counts
ShowSyncHealthTooltip had its own peerHashes loop that also omitted
self, showing 3/3 peers instead of 4/4 when 4 officers were online.
Adds +1 to rMatch and cMatch after the loop (self never differs). - fix(standings): remove blank spacer between filter row and table
The spacer Label was invisible before the sync health bar was added.
Once the bar caused the filter row to wrap to two lines, the spacer
became a visible blank gap between the search bar and column headers. - fix(standings): include self in sync peer count
peerHashes only contains other players' heartbeats — the local officer
is never in it. Was showing 3/3 when 4 officers were online. Now adds
+1 to both syncedPeers and totalPeers after the loop so the local
player is always counted as synced (self-check already returns 'synced'). - fix(standings): self red color and stale sync bar
- GetAddonState: short-circuit to 'synced' for local player — HandleHeartbeat
self-excludes so the local officer never had an addonStatus entry, causing
them to fall through to the guild online check and return 'no_addon' (red) - Refresh(): update _syncHealthLabel text with live peerHashes data on every
call — the label was created once in Draw() and never updated, causing the
sync percentage to stay stale until a tab reopen"
- GetAddonState: short-circuit to 'synced' for local player — HandleHeartbeat
- fix(standings): sync dashboard bug fixes
- addonStatus keys now normalized to full Name-Realm before write
(AceComm passes short name on same-realm; lookup was always missing)
→ fixes all rank colors showing red (no_addon) - Removed Players popup: added ScrollFrame container so 10+ entries
scroll instead of overflowing - Removed Players popup: fixed 350px height keeps AceGUI close button
always visible regardless of entry count - Removed Players popup: added duplicate guard (self._removedPlayersPopup)
so re-clicking closes old popup before opening new one"
- addonStatus keys now normalized to full Name-Realm before write
- feat(standings): guild roster sync dashboard
- Auto-discover all visible-rank guild members on login (DiscoverFullGuildRoster,
10s delay, 60s throttle). Purge/block lists take absolute priority. Core
status assigned directly (visibleRanks IS the admission gate). - Guild rank text color now encodes 5-state addon compliance for officers:
green=synced, yellow=outdated, dark gray=inactive, red=no addon, dim=unknown.
Raiders continue to see the original binary green/gray color. - Sync health bar added to filter row top-right of Standings (officer-only).
Shows 'Sync: X% (N/M) | N removed'. Hover reveals per-hash breakdown,
addon compliance counts, epoch, last HB age, and named purged/blocked players. - Clicking the 'N removed' label opens a Removed Players popup with
per-row Restore/Unblock buttons (RBAC-gated, audit-logged). - Real-time UI reactivity: state-change detection in HandleHeartbeat
triggers a 1s debounced RefreshAllUI() — no window reopen needed. - addonStatus stored in syncStateV2 (NOT players) — hash-immune by design.
- _dev version suffixes excluded from max version computation."
- Auto-discover all visible-rank guild members on login (DiscoverFullGuildRoster,

