Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
eee1f4f
feat(metrics): publish live hackathon counts for the weekend
aamoghS Oct 7, 2026
aaad2da
feat(panel): start a standalone judging core
aamoghS Oct 7, 2026
c3070d8
chore(deps): bump actions/upload-artifact from 4 to 6
dependabot[bot] Oct 8, 2026
429dba0
chore(deps): bump actions/download-artifact from 4 to 7
dependabot[bot] Oct 8, 2026
f4fa8fa
chore(deps-dev): bump vitest from 5.0.1 to 5.0.3
dependabot[bot] Oct 8, 2026
7f79a63
Merge pull request #478 from DataScience-GT/feat/telemetry-prod-grading
aamoghS Oct 8, 2026
907b688
Merge pull request #486 from DataScience-GT/dependabot/npm_and_yarn/d…
aamoghS Oct 8, 2026
3434dc2
chore(deps): bump @typescript-eslint/eslint-plugin from 8.70.1 to 8.7…
dependabot[bot] Oct 8, 2026
212c00a
Merge pull request #481 from DataScience-GT/dependabot/github_actions…
aamoghS Oct 8, 2026
cc354b3
chore(deps-dev): bump dotenv from 18.0.3 to 18.0.5 (#484)
dependabot[bot] Oct 8, 2026
98dda70
chore(deps-dev): bump turbo from 2.11.3 to 2.11.7 (#483)
dependabot[bot] Oct 8, 2026
4019558
Merge pull request #480 from DataScience-GT/dependabot/github_actions…
aamoghS Oct 8, 2026
5ee6873
feat(panel): run judging on the shared database
aamoghS Oct 8, 2026
e2d1bf5
fix(panel): keep the SQL migrations in the repo
aamoghS Oct 8, 2026
bfd6d02
refactor(panel): fold judging into the monorepo
aamoghS Oct 8, 2026
152e261
feat(panel): run judging inside the portal
aamoghS Oct 8, 2026
95446da
perf(panel): keep judging inside the Neon free plan
aamoghS Oct 8, 2026
24a43fd
feat(panel): tell a judge about a recall or a voided visit
aamoghS Oct 8, 2026
bba0354
feat(panel): judge each hackathon edition from the portal
aamoghS Oct 8, 2026
554b561
feat(judging): rework judging around rooms and add hackathon points
aamoghS Oct 13, 2026
ca24470
Merge remote-tracking branch 'origin/dev' into feat/panel
aamoghS Oct 13, 2026
0c21906
fix(metrics): satisfy lint in metrics service and its test
aamoghS Oct 13, 2026
395ee00
feat(judging): judging tables for general challenges
aamoghS Oct 14, 2026
9984601
feat(hacklytics): mobile header CTA, portal and judge links, hacker s…
aamoghS Oct 14, 2026
e3ffdd1
feat(hackathons): organizer console fixes for reviewing and running a…
aamoghS Oct 14, 2026
8742e0a
feat(check-in): continuous pass scanner and clearer check-in paths
aamoghS Oct 14, 2026
6a592ef
feat(hackathons): event page that follows the hacker through the weekend
aamoghS Oct 14, 2026
1c1c431
feat(teams): manage teams on the Teams tab, read-only submission for …
aamoghS Oct 14, 2026
534b5db
feat(judging): judge desk that holds up on a phone over a long weekend
aamoghS Oct 14, 2026
a08e657
feat(judging): judge applicant flow and judging emails
aamoghS Oct 14, 2026
515a4af
feat(hackathons): clearer judging and results tabs for hackers
aamoghS Oct 14, 2026
8a7bc98
test(api): stub email sends in router tests without loading the trans…
aamoghS Oct 14, 2026
05f8b3f
feat(hackathons): attendance confirmation, pending edits and desk admit
aamoghS Oct 14, 2026
2990173
feat(hackathons): in-app announcements and safer mass email
aamoghS Oct 14, 2026
1eff092
feat(hackathons): next-step strip and a focused nav during the event
aamoghS Oct 14, 2026
fe31f35
perf(judging): CP-SAT builds on the schedule it is given
aamoghS Oct 14, 2026
27c5604
perf(judging): faster greedy scheduler that places tight events
aamoghS Oct 14, 2026
e5b98c6
feat(judging): run CP-SAT on the server when Python is configured
aamoghS Oct 14, 2026
466474f
feat(judging): CP-SAT panel in the schedule planner
aamoghS Oct 14, 2026
b7bf0ed
fix(portal): subtle text meets 4.5:1 contrast in the light theme
aamoghS Oct 14, 2026
cf4e0de
style(hackathons): teams and submit read cleanly on a phone
aamoghS Oct 14, 2026
5f95985
style(judging): tidier rooms and tables admin
aamoghS Oct 14, 2026
038b446
style(auth): sign-in and code entry polish
aamoghS Oct 14, 2026
7a814f4
style(hacklytics): public site polish from a phone render review
aamoghS Oct 14, 2026
618f453
fix(portal): tab strips keep their width on phones; readable states o…
aamoghS Oct 14, 2026
06be1b5
style(hackathons): event page and dashboard polish from a phone review
aamoghS Oct 14, 2026
b40e959
style(hackathons): pass, schedule and points read better on a phone
aamoghS Oct 14, 2026
08b1ffa
style(judging): judge desk polish for phones in a loud hall
aamoghS Oct 14, 2026
a6cd8cd
style(check-in): door scanner and manual check-in polish
aamoghS Oct 14, 2026
e452e63
style(hackathons): judging, results and /hacklytics polish
aamoghS Oct 14, 2026
b151821
feat(hacklytics): globe and blossom favicon
aamoghS Oct 14, 2026
2c130f1
feat(judging): cancel CP-SAT with the request; schedule copy for tables
aamoghS Oct 15, 2026
484414a
fix(hacklytics): address review findings from #477
aamoghS Oct 15, 2026
e60be8d
Merge pull request #479 from DataScience-GT/feat/panel
aamoghS Oct 15, 2026
fd65379
fix(points): refuse event-code check-in for banned hackers
aamoghS Oct 15, 2026
a746a68
fix(announcements): stop counting in-app posts as emailed
aamoghS Oct 15, 2026
1c42309
fix(submit): encode the hackathon id in submit page links
aamoghS Oct 15, 2026
4bec229
fix(registration): keep sensitive answers out of the saved draft
aamoghS Oct 15, 2026
1c867da
feat(points): refuse overlapping and back-to-back event check-ins
aamoghS Oct 15, 2026
b195729
feat(points): record refused check-ins as flags for organisers
aamoghS Oct 15, 2026
a79cd7f
feat(judging): show check-in flags to organisers in the projects list
aamoghS Oct 15, 2026
4e752c0
feat(judging): keep teams pulled from prizes out of results
aamoghS Oct 15, 2026
d0fb5f2
feat(judging): mark flagged teams on the judge desk
aamoghS Oct 15, 2026
df9ec60
chore(deps): bump eslint-plugin-turbo from 2.11.3 to 2.11.7 (#482)
dependabot[bot] Oct 15, 2026
d49c74d
feat(judging): make the presentation deadline a hard end for judges
aamoghS Oct 15, 2026
f74a033
feat(judging): give judge writes a 15 second grace past the deadline
aamoghS Oct 15, 2026
982773b
chore(test): keep agent worktrees out of root test runs
aamoghS Oct 15, 2026
7d6a163
feat(judging): grade each project once per challenge
aamoghS Oct 15, 2026
6e735a0
feat(judging): rank placings on judge-calibrated scores
aamoghS Oct 15, 2026
7d8b63c
feat(judging): keep judges to their queue and sponsors to their rooms
aamoghS Oct 16, 2026
600c788
feat(judging): count auto-submits, reopen grades, retune drift
aamoghS Oct 16, 2026
964f93e
feat(judging): assign one judge to every presentation
aamoghS Oct 15, 2026
67d6cd2
fix(judging): tighten the CP-SAT judge model and keep goals it cuts s…
aamoghS Oct 15, 2026
0997687
feat(judging): give assigned judges their presentations first
aamoghS Oct 16, 2026
51aaca1
feat(judging): show assigned judges' progress and clear stale placings
aamoghS Oct 16, 2026
b613e55
fix(judging): count scheduled presentations and linked rooms correctly
aamoghS Oct 16, 2026
5d94c82
fix(check-in): keep rule-break flags when their events are deleted
aamoghS Oct 16, 2026
e836d6b
fix(judging): refresh public results after Projects list changes
aamoghS Oct 16, 2026
c5e127a
fix(judging): auto-submit drafts first saved in the write grace window
aamoghS Oct 16, 2026
3fe3d4f
fix(judging): refresh public results after danger zone resets
aamoghS Oct 16, 2026
7e9deb2
fix(check-in): skip the event lookup when every flagged event is gone
aamoghS Oct 16, 2026
8f1e13c
fix(check-in): type the empty event list in the flag read
aamoghS Oct 16, 2026
50ad65b
Merge pull request #494 from DataScience-GT/feat/panel
aamoghS Oct 19, 2026
ea104c4
chore(deps): bump actions/upload-artifact from 6 to 7
dependabot[bot] Oct 15, 2026
260f048
chore(deps): bump actions/download-artifact from 7 to 8
dependabot[bot] Oct 15, 2026
b794443
chore(deps): bump globals from 17.12.0 to 17.13.0
dependabot[bot] Oct 15, 2026
dd908e1
chore(deps): bump sanitize-html and @types/sanitize-html
dependabot[bot] Oct 15, 2026
0955476
chore(deps): bump stripe from 22.6.2 to 23.0.0
dependabot[bot] Oct 15, 2026
0e9df53
chore(deps): bump next from 16.3.7 to 16.4.0
dependabot[bot] Oct 15, 2026
8bd06e4
Merge pull request #498 from DataScience-GT/chore/deps-bumps
aamoghS Oct 19, 2026
d717687
chore(deps): bump sanitize-html from 2.18.0 to 2.18.1 (#499)
dependabot[bot] Oct 21, 2026
30dc637
fix(deps): override source-map-js to ^1.2.2 (#502)
aamoghS Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
run: pnpm turbo run build

- name: Upload Hacklytics build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: hacklytics-out
path: sites/hacklytics2027/out
Expand All @@ -69,7 +69,7 @@ jobs:
steps:
- uses: actions/checkout@v6

- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: hacklytics-out
path: sites/hacklytics2027/out
Expand All @@ -94,7 +94,7 @@ jobs:
steps:
- uses: actions/checkout@v6

- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: hacklytics-out
path: sites/hacklytics2027/out
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ build
.DS_Store
*.pem
nul
__pycache__/

# Debug
npm-debug.log*
Expand Down
8 changes: 6 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ Edition-scoped event operations. Everything hangs off `hackathon`.

- Editions, interest list, registration, teams, project submission
- Weekend schedule (`hackathon_event`) and badge scans
- Judging (`judge`, `judging_project`, `judge_vote`, `judge_queue`, `hackathon_result`)
- Judge roster (`judge`, `judge_assignment`): applications and approval
- Judging: room-based judging with Devpost import — `judging_*` tables (`judging.ts`). The older pool-dispatch tables (`judging_project`, `judge_vote`, `judge_queue`, `hackathon_result`) are retired and kept for past editions' results
- Points: `hackathon_event` points, staff adjustments and the point store (`points.ts`)
- Event check-in rules: nobody is checked into two events that run at the same time, or into two less than 30 minutes apart (meals are exempt). `recordEventAttendance` enforces this for pass scans, event codes and manual check-in
- Check-in rule-break flags: each refused attempt is recorded (`hackathon_check_in_flag`, `services/check-in-flags.ts`) and shown in judging on the hacker's team, in full to organisers and as a bare marker to judges. Organisers decide whether to pull the team from prizes (`judging_entry.prize_ineligible_at`). A flag outlives the events it names (deleting one clears its id and the organiser view shows "A deleted event"). Hackers never see flags, and they stay out of public and cached payloads
- Announcements and acceptance waves

Hackathon participation is **open to non-members**. Membership is not a registration requirement.
Expand All @@ -68,7 +72,7 @@ Hackathon participation is **open to non-members**. Membership is not a registra
3. Browser calls `/api/trpc/*` via `@trpc/react-query`. Superjson is the transformer.
4. `createContext` loads the NextAuth session (when `db` exists), attaches `userId`, client IP, and the in-process cache.
5. Procedures run through DB-required, sanitizer, content-type, rate-limit / DDoS, and (for mutations) cache invalidation middleware.
6. Role gates (`isAdmin`, `isScanner`, `isJudge`, `isProjectLeader`, `isSuperAdmin`) live in `packages/api/src/middleware/procedures.ts`. There is no `adminProcedure` alias that skips a role check.
6. Role gates (`isAdmin`, `isScanner`, `isProjectLeader`, `isSuperAdmin`) live in `packages/api/src/middleware/procedures.ts`. There is no `adminProcedure` alias that skips a role check.

## Auth

Expand Down
2 changes: 1 addition & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Code owners: `.github/CODEOWNERS` assigns `*` to `@aamoghS`.
1. Branch from `dev` unless you are fixing production.
2. Keep club and hackathon concerns separate. Do not add `hackathon_id` to club tables.
3. Put shared rules in the package that every caller can import. Membership grant/link logic belongs in `@query/db/services/membership`, not copied into auth, Stripe, and tRPC.
4. Role checks go through `isAdmin` / `isScanner` / `isJudge` / `isProjectLeader` / `isSuperAdmin`. Do not invent an `adminProcedure` that is only `protectedProcedure`.
4. Role checks go through `isAdmin` / `isScanner` / `isProjectLeader` / `isSuperAdmin`. Do not invent an `adminProcedure` that is only `protectedProcedure`.
5. Dangerous HTML in tRPC input is **rejected**, not stripped. See [Security](./operations/security.md).
6. Prices live in `packages/api/src/services/pricing.ts`. Do not hard-code dollar amounts in UI or Stripe calls.

Expand Down
4 changes: 2 additions & 2 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
| **Staff** | Active admin whose role is not `volunteer`. |
| **Project leader** | `project_leader` row. Runs **club projects**. Not a staff role. |
| **Club project** | `initiative` row. Members apply to join with a pitch and an optional resume. Never judged. Distinct from a hackathon **project**, which is a judged submission. The UI says "club project"; the table is still `initiative`. |
| **Hackathon project** | Team/solo submission (`hackathon_project`). Promoted into `judging_project` for scoring. |
| **Hackathon project** | Team/solo submission (`hackathon_project`). Promoted into a `judging_entry` for judging, alongside projects imported from Devpost. |
| **Interest** | “Tell me when registration opens” (`hackathon_interest`). Requires a signed-in user. |
| **Current edition** | In-progress hackathon if one exists; otherwise the newest edition that is not `draft` or `announced`. |
| **Announced** | Public landing + interest, registration closed, **not** current for membership resolution. |
| **Wave** | Batch accept of oldest pending applicants; acceptance email is stamped per participant so retries are safe. |
| **Judging queue** | Per-judge ordered tables. `startedAt` is a short claim; `arrivedAt` is the QR scan at the table. |
| **Judging room** | `judging_room`. Judges sit in a room that serves one challenge; projects present to it on the schedule. The QR code on its door seats a judge, or starts a guest judge's session. |
| **Results snapshot** | `hackathon_result` — frozen placing. Live z-score is only used when computing that snapshot. |
| **Bootcamp term** | String like `2026-fall`. Access checks this, not the never-expiring `bootcamp_member` boolean. |
7 changes: 7 additions & 0 deletions docs/operations/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ Without `DATABASE_URL`, `db` is null, sessions fall back to JWT, and tRPC proced
| `RESUME_BUCKET` | Cloud Storage bucket holding resume PDFs (App Hosting sets `dsgt-resumes`). Unset means uploads return 503 rather than failing obscurely. Credentials are ADC — the runtime service account needs `roles/storage.objectAdmin`. See [Resume book](../resume-book.md) |
| `BOOTCAMP_BUCKET` | Cloud Storage bucket holding bootcamp workshop ZIPs (App Hosting sets `dsgt-bootcamp`). Unset means uploads and downloads return 503. Same ADC and same `roles/storage.objectAdmin` as above. Downloads are proxied and gated on the caller's bootcamp term, so the bucket keeps public access prevention on |

## Judging schedule

| Variable | Default / notes |
| --- | --- |
| `JUDGING_CPSAT_PYTHON` | A Python with OR-Tools (`python3` or a path). Set, the schedule section can run `packages/api/scripts/judging-schedule/cpsat.py` on the server (`judging.schedule.solveCpsat`), and `judging.schedule.assignJudges` uses it to pick judges. Unset means CP-SAT is download, run on a laptop, upload, and the app assigns judges itself |
| `JUDGING_CPSAT_SCRIPT` | Path to `cpsat.py`. Default: found by walking up from the working directory |

## Security / proxy

| Variable | Default / notes |
Expand Down
1 change: 0 additions & 1 deletion docs/operations/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ Roles are rows, not JWT claims.
| `isScanner` | Any active `admin` including `volunteer` |
| `isAdmin` | Active admin whose role is **not** `volunteer` |
| `isSuperAdmin` | `super_admin` |
| `isJudge` | Active `judge` for the resolved edition |
| `isProjectLeader` | Active `project_leader` or staff |

Volunteers can staff check-in desks. They cannot delete editions, grant memberships, or pass `isAdmin`.
Expand Down
3 changes: 2 additions & 1 deletion docs/operations/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ Config: `packages/api/vitest.config.ts`. Includes `src/**/*.test.ts` and `src/.i
| Hackathon flow | `hackathon-flow.test.ts`, `hackathon-admin-edge.test.ts`, `hackathon-interest.test.ts` |
| Payments | `stripe-payments.test.ts` |
| QR / check-in | `qr-checkin.test.ts` |
| Check-in rule-break flags | `services/check-in-flags.test.ts`, `.internal-tests/check-in-flags-*.test.ts` |
| Security / sanitizer / rate limit | `security.test.ts`, `resilience.test.ts` |
| Judges / participants / initiatives / bootcamp / announcements | matching `*-edge.test.ts` / `bootcamp.test.ts` / `announcements.test.ts` |
| Portal context | `services/portal-context.test.ts`, `routers/user/portal-context.test.ts` |
| Judge helpers | `routers/judge/helpers.test.ts` |
| Judging scoring, scope, room links | `services/judging/*.test.ts` |

Tests use `src/test/create-mock-context.ts` and `.internal-tests/_db-tx-mock.ts` rather than a live database.

Expand Down
17 changes: 12 additions & 5 deletions docs/packages/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ Exports: `.`, `./context`, `./trpc`, `./pricing`, plus a few middleware paths (s
| `member` | `routers/member.ts` | Membership profile, pass, admin grant/revoke/search |
| `hackathon` | `routers/hackathon/*` | Editions, registration, interest, events, content, announcements, staff ops |
| `events` | `routers/events.ts` | Club events and QR check-in |
| `judge` | `routers/judge/*` | Judge portal, rankings/results, admin judging |
| `judge` | `routers/judge/*` | Judge roster: apply, approve, add, remove |
| `judging` | `routers/judging/*` | Room judging: `admin`, `schedule`, `desk`, `hacker`, `results` |
| `points` | `routers/points/*` | Hackathon points: `admin`, `store`, `me` |
| `stripe` | `routers/stripe.ts` | Checkout, PaymentIntent, link, reconcile |
| `audit` | `routers/audit.ts` | Audit log list (admin) |
| `team` | `routers/team.ts` | Teams and project submission |
Expand All @@ -37,7 +39,6 @@ Defined in `src/trpc.ts` and `src/middleware/procedures.ts`.
| `isScanner` | Active `admin` row | Volunteers included. Badge / door scanning only |
| `isAdmin` | Active staff `admin` | Rejects `volunteer` |
| `isSuperAdmin` | `role === "super_admin"` | Staff create/remove, judge activate/remove, set project leader |
| `isJudge` | Active `judge` for the resolved hackathon | Resolves edition from input `hackathonId` / `projectId` / `queueId`, else current edition |
| `isProjectLeader` | Active `project_leader` **or** staff | Staff can cover a quiet leader; leaders cannot use this to pass `isAdmin` |

There is deliberately **no** `adminProcedure` that equals `protectedProcedure`. That name shipped open admin endpoints in the past.
Expand Down Expand Up @@ -86,11 +87,17 @@ Batch status updates allow up to **2500** ids. The global array cap in `scrubMar

## Judge procedures

**Portal** (`portal.ts`): `isJudge`, assignments, applications, QR start, next table, vote, skip, overtime skip, progress. `toggleJudging` is admin.
**Portal** (`portal.ts`): `isJudge` (for the named or current edition), `myApplications`, `register` (apply to judge).

**Rankings** (`rankings.ts`): `getRankings`, `computeResults`, draft/publish/unpublish. Results are a **frozen snapshot** (`hackathon_result`), not a live z-score on every page load.
**Admin** (`admin.ts`): `list`, `create` (add by email), `setActive` (approve or suspend), `remove` (super admin).

**Admin** (`admin.ts`): CRUD judges, promote submissions into judging tables, initialize queues, assign tracks/projects, table cards, live progress, analytics. `setActive` and `remove` are super admin. `register` is protected (apply to judge).
Judging itself is the `judging` namespace; its shared helpers are in `services/judging/` (`room-link`, `scope`, `scoring`, `config`). `hackathon.getResults` returns published placings from both the retired `hackathon_result` table and `judging_placement`, in one shape. It leaves out entries pulled from judging (`deleted_at`) or from prizes (`prize_ineligible_at`, set by `judging.admin.setPrizeEligibility`). Check-in rule-break flags (`services/check-in-flags.ts`) reach organisers through `judging.admin.listEntries` and `judging.results.list`, and judges through `judging.desk` as a bare `flagged` boolean; no hacker-facing, public or cached procedure returns them.

Placings rank on scores adjusted for judge bias (`services/judging/calibration.ts`): each evaluation is calibrated within its root challenge for its judge's strictness, spread and drift, on the assumption that `judging.desk.nextProject` (`services/judging/queue.ts`) hands every judge a random sample of projects. `judging_placement.raw_score` keeps the plain mean; only `judging.results.list` (organisers) returns it and the per-judge calibration, and `judging.desk` stays on raw means. An evaluation counts when it holds a rating for every rating item (`scoring.hasEveryRating`, and `scoring-sql.holdsEveryRating` where the database counts), whether or not a written response is missing, so a draft submitted at the hard end still places its project. Organiser counts, the board and hacker itinerary status, hacker feedback, and the desk's scores, judged count and shortlist all use that rule. `judging.results.list` keeps the calibration for 60 seconds per edition, keyed on the edition's evaluation count and latest save and on its ranked entries, while `judging.results.compute` always calibrates fresh. `judging.admin.reopenGrade` deletes one evaluation and the drafts for its project and challenge so another judge can grade it, and clears the edition's unpublished placings, so publishing needs a fresh compute; it is refused while results are published.

At an untimed challenge that random sample is enforced (`services/judging/queue-store.ts`): `judging.desk.nextProject` offers only the first `QUEUE_WINDOW` (3) open projects of the judge's own queue and takes a `skip` list for a team away from its table, and a judge's first `startGrading`, `saveDraft` or `submit` on a project outside that window is refused. `judging.desk.startGrading` holds a project from the moment it is opened, with an empty draft. A table judge grades a sponsor's challenge only from a seat in a room that serves it (`scope.isSponsorChallenge`); `judging.desk.context` lists only the challenges they may pick.

A scheduled presentation can have one assigned judge (`judging_appointment.assigned_judge_id`, set by `judging.schedule`; `services/judging/schedule/judges.ts` decides who may be assigned it). The desk never writes it and never tells a judge which other judge is assigned to, holds or graded a project. From the presentation's start until `TAKEOVER_AFTER_MS` (5 minutes) later only the assigned judge may open it (`services/judging/timing.ts`), and other judges see it as `reserved` with the time it opens. After that any judge who may grade it there can take it over, unless the assigned judge has started: their draft holds it (`grading-hold.ts`). Every claim checks this under the grading lock. `judging.desk.nextProject` offers the judge's own running assignments first; the queue window does not apply to scheduled challenges. `judging.desk.itinerary` lists the caller's assigned presentations that have not ended, soonest first, with the room and whether they are seated there. `judging.desk.releaseGrading` deletes the caller's own draft while it is still the empty one `startGrading` made, so a skipped or accidentally opened project is free again. `judging.schedule.get` gives organisers each started assignment's `judgeProgress` (grading, graded, taken over, not started).

## Club procedures (selected)

Expand Down
7 changes: 5 additions & 2 deletions docs/packages/db.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ Files in `src/schemas/`, re-exported from `schemas/index.ts`. `drizzle.config.ts
| `auth.ts` | `user`, `account`, `session`, `verificationToken` |
| `members.ts` | `user_profile`, `member`, `membership_history` |
| `admins.ts` | `admin` |
| `hackathons.ts` | `hackathon`, `hackathon_team`, `hackathon_participant`, `hackathon_project`, `hackathon_interest`, `hackathon_event`, `hackathon_event_attendee`, `hackathon_announcement`, `hackathon_announcement_recipient` |
| `judge.ts` | `judge`, `judge_assignment`, `judging_project`, `judge_vote`, `judge_queue`, `hackathon_result` |
| `hackathons.ts` | `hackathon`, `hackathon_team`, `hackathon_participant`, `hackathon_project`, `hackathon_interest`, `hackathon_event`, `hackathon_event_attendee`, `hackathon_check_in_flag`, `hackathon_announcement`, `hackathon_announcement_recipient` |
| `judge.ts` | `judge`, `judge_assignment` (live roster); `judging_project`, `judge_vote`, `judge_queue`, `hackathon_result` (retired, kept for past editions) |
| `judging.ts` | `judging_*`: config, entries and members, challenges, buildings, rooms and room links, judges, guest sessions, presence, schedule and appointments, rubric, evaluations, deliberation, placements |
| `points.ts` | `hackathon_point_adjustment`, `hackathon_store_item`, `hackathon_store_purchase` |
| `retired.ts` | Enum types the retired panel judging service left in the database, declared so push does not try to drop them |
| `initiatives.ts` | `project_leader`, `initiative`, `initiative_application` |
| `events.ts` | `event`, `event_check_in` |
| `bootcamp.ts` | `bootcamp_workshop` (metadata; the ZIPs are in Cloud Storage). `bootcamp_material` is legacy and unused |
Expand Down
8 changes: 6 additions & 2 deletions docs/sites/hacklytics2027.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,20 @@ Schedule copy lives in `components/sections/Schedule/data.ts`.

## Interest CTA

`lib/links.ts` is the only outbound destination. The Typeform this replaced was pasted in four files and drifted.
`lib/links.ts` is the only outbound destination. The Typeform URL was once pasted in four files and drifted.

Interest requires a portal account (verified email). The CTA is:
Every Notify me button and link (header, mobile menu, hero, FAQ, footer) points at `INTEREST_URL`, a Typeform, for now. On phones 356px and wider the header keeps a compact Notify me button beside the menu toggle, so the CTA does not hide inside the hamburger menu.

Once registration opens on the portal, swap `INTEREST_URL` back to `PORTAL_INTEREST_URL`, which signs the visitor in first:

```
{PORTAL_ORIGIN}/login?callbackUrl=/hacklytics
```

`callbackUrl` is encoded so it survives the email-code hop through `/verify`. The portal only honors same-origin paths.

The footer also links to the portal for the two audiences that are not waiting on the list: **Hacker portal** (`PORTAL_HACKATHON_URL`, `{PORTAL_ORIGIN}/hacklytics`) and **Judge Hacklytics** (`JUDGE_URL`, sign-in with `callbackUrl=/judge`).

## Deploy

Firebase Hosting target `hacklytics`, public dir `sites/hacklytics2027/out` (`firebase.json`).
Expand Down
8 changes: 4 additions & 4 deletions docs/sites/mainweb.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ Unauthenticated and authenticated product UI. `proxy.ts` marks these prefixes `p
| `/club/bootcamp` | Bootcamp (paid add-on, term-gated) |
| `/initiatives` | Browse / apply |
| `/lead`, `/lead/[id]` | Project leader |
| `/hackathons`, `/hackathons/[id]` | Edition pages (info, schedule, teams, projects, results) |
| `/hackathons/[id]/judge` | Judging for that edition |
| `/hackathons`, `/hackathons/[id]` | Edition pages (info, schedule, teams, projects, judging, points, results) |
| `/hacklytics` | Current Hacklytics interest / portal landing |
| `/submit` | Project submission |
| `/judge`, `/judge/register` | Judge home / apply |
| `/judge/[hackathonId]` | Judge desk for one edition |
| `/scan` | QR scanning |
| `/admin` | Staff home |
| `/admin/hackathons`, `/admin/hackathons/[id]` | Edition admin (attendees, waves, announcements, analytics, events). `[id]` is the name slug (`hacklytics-digital-bloom`), not a percent-encoded name. |
| `/admin/hackathons`, `/admin/hackathons/[id]` | Edition admin (attendees, waves, announcements, analytics, events, judges, points, store). `[id]` is the name slug (`hacklytics-digital-bloom`), not a percent-encoded name. |
| `/admin/members` | Membership admin |
| `/admin/attendees` | Attendee tools |
| `/admin/judging` | Judging admin (sync submissions + assign judges live here) |
| `/admin/judging`, `/admin/judging/[hackathonId]` | Pick an edition, then run its judging: import, rooms, schedule, results |
| `/admin/initiatives` | Initiative / proposal review |
| `/admin/bootcamp` | Bootcamp attendance grid and the weekly workshop table |
| `/admin/staff` | Admin users |
Expand Down
Loading
Loading