Skip to content

Audit fixes: hardening, R32 seating guards, and super-admin analytics - #139

Merged
dommango merged 5 commits into
mainfrom
worktree-audit-fixes
Jul 10, 2026
Merged

dommango merged 5 commits into
mainfrom
worktree-audit-fixes

Conversation

@dommango

@dommango dommango commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

Batches accumulated audit and hardening work that never had a PR (9 commits, 31 files net vs main):

  • Abuse/ops hardening: rate limits, claim cap, odds-API quota guard, cron.
  • Match-state correctness: decided matches no longer double-count or pin as "live"; games reach COMPLETE and stop advertising ended windows.
  • R32 third-place seating integrity guards; API finals oriented to the correct bracket slot (Annex C).
  • Challenge profile gating + UI polish.
  • Platform super-admin analytics dashboard with engagement event tracking (+ migration ordering fix).

Test plan

  • CI green on this branch
  • Verify R32 third-place seating against a known bracket
  • Confirm odds-quota guard stops early snapshots past the credit limit
  • Spot-check the super-admin analytics dashboard renders engagement events

🤖 Generated with Claude Code

dommango-sys added 5 commits July 9, 2026 00:54
…tracking

Adds an admin-only analytics dashboard (/admin/analytics) tracking user
engagement across all pools, plus the event-logging layer that feeds it.

- AnalyticsEvent model + enum (audit-style plain-id FKs, no relations/cascade,
  indexed by day/type/user) — additive and non-breaking, mirroring the
  PrizeAward/Feedback precedent. Migration + regenerated client included.
- logEvent: best-effort write that never throws or breaks the triggering action
  (mirrors notifyPool). Instrumented at sign-in/up, pool create, pool join
  (code + invite, new members only), bracket submit, chat message, reaction.
- Pure, unit-tested rollups (UTC day bucketing, DAU/WAU/MAU, type breakdown,
  top pools, stickiness) split from the DB query layer.
- Dashboard gated by getTournamentAdmin (fail-closed), custom Tailwind bar
  charts (no new deps), linked from the admin page.

MAU/stickiness fetch window covers the full rolling 30d boundary so late-day
actives aren't clipped.
… the bracket slot

The answer key stores third-place advancers as an ordered list whose order
drives R32 seating (resolveR32Slots backtracks in list order). A standings
re-save with the same eight teams in a different order silently re-seated the
live bracket — orphaning recorded winners, breaking pair-map fixture mapping,
and corrupting every seed-derived surface (this happened in prod mid-R32).

- setGroupStandings now preserves the stored order when the submitted set is
  unchanged (mergeThirdAdvance), and rejects any seating that contradicts a
  recorded knockout winner (findKnockoutSeatingConflict).
- setKnockoutResultLocked now orients API-reported scores/pens to the bracket
  slot (orientScoresToSlot) before mirroring, matching the live-row path — the
  FINAL row could previously flip against winnerCode whenever the provider's
  home differed from the bracket's home (exactly the pens ties in play now).
- getKnockoutChallengeProfile now requires board membership (which carries the
  verified-email anti-Sybil gate): it previously rendered gated entries' full
  picks with a fabricated rank, and its pick-share denominators now match the
  public board population.
- MD3 form/breakdown pin kickoff day/time labels to DISPLAY_TZ (the form was a
  hydration mismatch, the breakdown rendered UTC times and split late fixtures
  across wrong day headers).
- Chat pins its own scroll box instead of scrollIntoView, which scrolled every
  ancestor and yanked live match pages down to the embedded chat on mount.
- R32Countdown seeds its passed state from the clock so a past kickoff no
  longer flashes a stale countdown banner on first paint.
The complete-eight-thirds set is now seated via FIFA's Annex C table (#124),
which is deterministic by team, not by thirdAdvance list order — so rotating
the order no longer re-seats the bracket and the test's premise no longer holds.
mergeThirdAdvance and its unit tests remain (order still drives partial-draft
seating via backtracking).
@dommango
dommango force-pushed the worktree-audit-fixes branch from dfe29f3 to fc864c0 Compare July 9, 2026 05:20
@dommango
dommango merged commit 304a936 into main Jul 10, 2026
1 check passed
@dommango
dommango deleted the worktree-audit-fixes branch July 10, 2026 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant