- Setup: nominate judges, copy personal links, QR codes (generated on
demand in the browser), revoke/restore, and an open-link toggle.
- GET /api/judge/[token] returns only that judge's own scores; PUT
.../scores runs every check from ARCHITECTURE.md in checkJudgeWrite
(pure, unit-tested) after one parallel lookup, plus a rate limit.
- /j/[token]: mobile cards per team, spotlight pinned first, autosave
with per-card status, read-only when locked/done, link-off when revoked.
- /jo/[token]: name form -> personal link, remembered in localStorage so
a re-scan doesn't create a second judge.
- Control room: averages with on-request overrides, judge progress
matrix, per-judge breakdown, spotlight select and lock toggle.
- usePendingEdits shares the optimistic autosave logic between the admin
grid and the judge page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure functions in src/lib/scoring implementing docs/SCORING.md: cell values
(override > judge average), weighted raw totals, raw and placement points
with tie averaging, and competition-ranked standings. Coverage thresholds
are enforced at 100% for the module. Also silences the hydration warning
caused by browser extensions modifying <html>.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Types are generated from the live database's API schema by
scripts/gen-types.mjs (npm run gen:types), in the same shape as
supabase gen types, so no Supabase CLI login is needed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tokens.ts validates link tokens (32 hex chars) before any DB query.
check:rls proves the anon key cannot read or insert on any table, and
fails loudly if the migration has not been applied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Next.js 16 App Router with strict TypeScript, Tailwind 4 theme tokens from
docs/UI.md, Vitest, and the three Supabase clients. Env vars are validated
with zod in next.config.ts so dev and build fail fast with a readable error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>