# Cerebro

## [v0.10.33](https://github.com/Shezzannn/Cerebro/tree/v0.10.33) (2026-03-21)
[Full Changelog](https://github.com/Shezzannn/Cerebro/compare/v0.10.32...v0.10.33) 

- chore: Bump version to 0.10.33  
    Fix RCLC import pipeline (date filter, timestamps, dedup), add dry-run simulation command; hide voided transactions in History tab; targeted sync PUSH to divergent peer only; scope Sync Health display to active online peers  
- fix(ui): use consistent name format for self-skip in Addon Compliance  
    Use GetFullPlayerName(UnitName("player")) instead of GetPlayerFullName()  
    for the myName self-skip guard. This matches the same normalizer used for  
    guild roster names (GetFullPlayerName(gName)), preventing self from being  
    double-counted due to name format mismatch.  
- fix(ui): unify Addon Compliance with GetAddonState, remove dead code  
    Replaced the entire manual addonStatus lookup + age + version comparison  
    in the Addon Compliance tooltip section with v2:GetAddonState(full) — the  
    same function that powers green/yellow/gray name colors in the player list.  
    Removed ~30 lines of dead code: v2INACTIVE, maxVer, v2modRef, addonStatus  
    variable — all now handled internally by GetAddonState. Self is counted  
    unconditionally before the loop; the loop uses full ~= myName guard.  
- fix(ui): always count self in Addon Compliance synced count  
    Self was being missed when GetPlayerFullName() and GetFullPlayerName(gName)  
    produced slightly different name strings, causing full == myName to fail.  
    Fixed by counting self unconditionally before the guild roster loop, and  
    skipping self inside the loop via full ~= myName guard.  
- fix(ui): normalize peerHashes short name to full Name-Realm for GetAddonState  
    AceComm passes sender as short name for same-realm peers, so peerHashes  
    is keyed by short name (e.g. "Bearlybleeds") while addonStatus uses full  
    Name-Realm format ("Bearlybleeds-Stormrage"). GetAddonState was always  
    returning "unknown" for same-realm peers, causing count to be 1/1.  
    Fix: normalize with GetFullPlayerName() before GetAddonState() in both  
    ComputeSyncHealth and ShowSyncHealthTooltip.  
- fix(ui): tighten active-peer filter to synced/outdated only  
    Previously included "inactive" peers (seen before, now offline) in the  
    Sync % and tooltip peer counts. Changed condition from != "unknown" to  
    == "synced" or == "outdated" so only players with an actively running  
    addon are counted as live peers.  
- fix(ui): fix active-peer name key mismatch in sync health display  
    peerHashes keys didn't match addonStatus keys, causing all peers to be  
    excluded (showing 1/1 instead of 3/3). Replaced addonStatus[name]  
    lookup with v2:GetAddonState(name) ~= "unknown" in both ComputeSyncHealth  
    and ShowSyncHealthTooltip — same proven logic used by Addon Compliance.  
- fix(ui): scope Sync % and tooltip top rows to active peers only  
    ComputeSyncHealth and ShowSyncHealthTooltip now filter peerHashes to  
    peers seen within INACTIVE\_THRESHOLD (90s). Offline/unknown peers no  
    longer deflate the Sync % label or the top 3 tooltip rows (Sync Health,  
    Roster Hash, Config Hash). Addon Compliance section is unchanged and  
    still shows all guild members.  
- fix(sync): targeted WHISPER push on raider mismatch instead of GUILD broadcast  
    When an officer detects a mismatch from a non-officer (raider/stale alt),  
    send a targeted WHISPER to only that peer instead of broadcasting to  
    RAID/GUILD. This prevents disrupting already-synced officers who would  
    temporarily drop from synced state while re-processing the broadcast.  
    - Reuses existing \_lastSnapshotTo per-sender cooldown (same as officer path)  
    - Removes \_lastRaidBroadcastAt scalar (no longer needed for this path)  
    - Net result: stale raider convergence is now invisible to synced peers  
- fix(ui): hide voided transactions in History tab; silence ghost stub debug spam  
    - MainFrame/UISandbox History tabs now skip voided transactions  
      (previously showed duplicates after RCLC import void+replace cycle)  
    - GetFilteredPlayersForSync: remove per-heartbeat debug log for ghost  
      stub skipping (Vrock-Eitrigg spam); expected behavior, not worth logging  
- fix(rclc-import): Fix import pipeline and add dry-run simulation  
    - Add ParseDateArg (MM-DD-YYYY → YYYY/MM/DD RCLC format)  
    - Remove duplicate ParseDateArg at EOF that returned Unix timestamp  
    - Fix date scope filter to compare RCLC date strings (not servertime)  
    - Add full Unix timestamp to NormalizeEntry from date+time fields  
    - Fix AddTransaction to honor overrideTimestamp from options  
    - Fix VoidTransactions to convert sinceDate string to Unix for comparison  
    - Fix audit log bug (date() called with string instead of number)  
    - Add SimulateImport dry-run (read-only, per-player GP report)  
    - Register /cb rclc dryrun MM-DD-YYYY command  
    - Bump version 0.10.32  
- fix(sync): fix persistent roster/selection hash divergence between officers  
    Two bugs caused officers to never fully converge after PR37:  
    Bug 1 — MergeSelectionData timestamp self-escalation (CommunicationV2.lua):  
      When incoming selection was accepted, sf:SetSelection() internally called  
      SaveSelection() which overwrote customEP.lastUpdate with time(). This ratcheted  
      the local timestamp forward on every successful merge, making the local client  
      perpetually \"newer\" than all peers. All future selection merges from those peers  
      were REJECTED (theirs < mine forever), producing the continuous REJECTED spam seen  
      in logs. Fix: restore incoming.lastUpdate after SetSelection() clobbers it.  
    Bug 2 — RepairDatabase lastUpdate bump on login (Database.lua):  
      The coreAdmittedAt backfill (data.coreAdmittedAt = 1) also bumped  
      data.lastUpdate = time(). RepairDatabase runs on every login at different  
      wall-clock seconds per officer, producing different lastUpdate values for  
      identical EP/GP/status records. Since lastUpdate is included in ComputeRosterHash(),  
      officers with identical data got different roster hashes → endless FETCH/PUSH loops.  
      Fix: remove the lastUpdate bump — this is a schema migration, not a data mutation.  
      Every officer independently applies this migration on login; LWW propagation  
      is unnecessary and harmful."  
