Skip to content

fix: admin roster returns one row per profile, not per performance#620

Merged
BreakableHoodie merged 1 commit into
mainfrom
fix/618-admin-roster-truncation
Jul 17, 2026
Merged

fix: admin roster returns one row per profile, not per performance#620
BreakableHoodie merged 1 commit into
mainfrom
fix/618-admin-roster-truncation

Conversation

@BreakableHoodie

Copy link
Copy Markdown
Owner

Summary

The admin roster silently truncated: the all-bands query returned one row per performance (LEFT JOIN, no GROUP BY), ordered newest-event-first, and applied LIMIT 200 — so bands whose only performances were on older events fell past the cutoff before RosterTab's client-side dedupe ever saw them. Reported by Dre: Adelleda (sole set on lwbc07, Feb 2024) was visible on the public site but uneditable in admin. The branch now does GROUP BY bp.id — one row per active profile — so the result is bounded by roster size, not performance count.

Closes #618

What changed

File Change
functions/api/admin/bands.js No-event_id branch: GROUP BY bp.id; "last played" context (event/venue name, date) sourced from the band's most recent performance via SQLite's bare-column-with-MAX() semantics — validated against production D1 directly (Burnaby, on Vols 1+2, resolves to Vol 2 with matching event name; Suplex with 3 sets resolves to Vol 17); synthetic id is now always profile_N (verified no consumer reads it as a performance id — RosterTab and ArtistPicker key off band_profile_id); default limit 200 → 500 (prod already holds 218 active profiles, over the old default); the false "no pagination-boundary risk" comment rewritten to state the real guarantee
functions/api/admin/bands/__tests__/bands.test.js 3 regression tests: old-event band survives when performance rows exceed the limit (fails on the old query); one row per profile for a 3-set band; inactive profile still excluded (pins current behaviour — surfacing inactive is #619)

Deliberate behaviour change: RosterTab's Delete now always sends profile_N, which hits the existing "has performances → 409" guard instead of deleting one arbitrary performance row — strictly safer, and consistent with the guard bulk.js already enforces.

Out of scope: the event_id branch (untouched); is_active = 1 filter stays (#619 covers surfacing inactive profiles).

Security / correctness notes

Admin-only endpoint behind checkPermission("viewer"), unchanged. Correctness: follower_count correlated subquery verified under GROUP BY via the existing dedicated test.

Verification

  • Tests: backend 755 pass | 6 todo (74 files); bands.test.js 81/81 (78 + 3 new); follower-count test green
  • ESLint: 0 errors
  • Format check: clean
  • Build: n/a backend-only; frontend suite sanity-run green (519) with no frontend changes
  • validate:openapi: n/a — response shape field-compatible (same columns, per-profile rows)
  • Manual smoke: the exact GROUP BY + MAX() query shape executed against production D1 read-only — per-group max-row resolution confirmed on real data

Built by Sonny · Reviewed by Theo · 🤖 Claude Code

The all-bands admin query LEFT JOINed to performances with no GROUP
BY, ordered newest-event-first, and applied LIMIT 200 — so any band
whose only performances were on older events sorted past the limit
and silently vanished from the roster (Adelleda, whose sole set was
lwbc07 2024, was uneditable). GROUP BY bp.id bounds the result by
roster size; last-played context comes from the band's most recent
performance via SQLite's bare-column-with-MAX semantics (validated
against production D1). Default limit 200 -> 500 (prod already has
218 active profiles). Roster deletes now always send profile_N ids,
which hit the existing has-performances 409 guard instead of deleting
an arbitrary performance row.

Closes #618

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 13:52
@BreakableHoodie BreakableHoodie added bug Something isn't working priority:p2 Medium priority labels Jul 17, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@BreakableHoodie
BreakableHoodie enabled auto-merge (squash) July 17, 2026 13:52
@BreakableHoodie
BreakableHoodie merged commit 56e5bfe into main Jul 17, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority:p2 Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(admin): roster silently truncates — per-performance LIMIT 200 hides bands from older events

2 participants