DropAssignmentDialog with required reason (10-1000 chars) calls
mentor.dropAssignment, redirects to /mentor on success. Button surfaces
in the project header only when the viewer is the assigned mentor and
the assignment is neither dropped nor completed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- /admin/logistics page with shadcn Tabs (3 active + 5 disabled "(soon)"
placeholder tabs for Visas / Lunch / Documents / Email Templates / Settings).
- Sidebar entry "Logistics" between Mentors and Awards (Plane icon).
- Confirmations tab: read-only table with status filter pills, browser-
local-time deadline display, attendee count, decline reason snippet.
- Hotels tab: single-hotel form (name/address/link/notes) with live
email-preview card showing what teams will see.
- Travel tab: per-attendee flight tracker with arrival/departure
datetimes, flight numbers, IATA airports, click-to-toggle status badge,
edit Sheet, and unfilled/pending/confirmed filter pills.
Smoke-tested end-to-end: navigation, sidebar entry, all three tabs
render, hotel save persists to DB and renders in preview card.
- New components/admin/grand-finale/finalist-slots-card: per-category
quota editor with confirmed/pending counts, dirty-tracking, save button.
Renders an empty editor for both Startup and Business Concept categories
even when no quota exists yet.
- New components/admin/grand-finale/waitlist-card: per-category ranked
waitlist display with status badges + manual-promote AlertDialog
(audit-logged via FINALIST_MANUAL_PROMOTE).
- Round detail page: embeds both cards conditionally when
roundType === 'LIVE_FINAL'.
- New finalist router queries: listQuotas, listCategoryCounts (groupBy
on category+status), listWaitlist (rank-ordered with project relation).
Smoke-tested: setting Startup quota to 3 persists to DB; UI renders
quota editor and waitlist card cleanly with empty state.
- /finalist/confirm/[token] under (public) route group
- Browser-local-time deadline + zone label + live countdown
- Default-selects up to defaultAttendeeCap team members
- Per-member "Needs visa?" toggle that surfaces only when selected
- Decline AlertDialog with optional reason textarea
- Distinct friendly states for invalid / expired / already-confirmed /
already-declined / superseded tokens (not generic errors)
- Smoke-tested end-to-end against live dev server: confirmation row
flipped to CONFIRMED, AttendingMember row created with correct visa flag
- expirePendingPastDeadline service: scans PENDING confirmations past
deadline, marks each EXPIRED + audit-logs, then promotes the next
waitlist entry per affected category (using each program's grand-final
round configJson for windowHours).
- /api/cron/finalist-confirmations: hourly cron entrypoint (CRON_SECRET
header gate), wraps the service.
- finalist.addToWaitlist: insert at a specific rank, shifting later
entries down (transactional).
- finalist.reorderWaitlist: rewrite a category's rank order in one go,
using a temp-rank trick to avoid unique-constraint conflicts mid-update.
- finalist.manualPromote: out-of-rank-order admin promote with audit log
(FINALIST_MANUAL_PROMOTE) + fresh confirmation email.
2 new tests. Suite at 14/14 for finalist-confirmation.
Adds a project-centric ops view for mentor management:
- New mentor.getMenteeActivity tRPC procedure aggregates every project
with wantsMentorship=true and derives a status (unassigned / assigned
/ active / stalled) from the latest message + file activity.
- /admin/mentors becomes a tabbed page: existing Mentor list +
new Mentees & Activity table with status pills, search, and a
per-row Assign/Open CTA linking to /admin/projects/[id]/mentor.
- Includes 2 unit tests covering classification + program scoping.
Also: ignore .remember/ (plugin scratch dir).
formatEnumLabel was leaving inputs uppercase ("TECHNOLOGY_INNOVATION"
became "TECHNOLOGY INNOVATION"); lowercasing first yields proper
title case ("Technology Innovation") and improves labels app-wide.
Apply it on the project mentor page for Ocean Issue + Category.
mentor.getRecentMessages: last N unread messages from teams across all
of a mentor's assignments. Drives a Recent Messages card on /mentor.
applicant.getMentorConversationPreview: last 3 messages + unread count
for a given project. Drives a 'Conversation with [Mentor]' card on
/applicant — auto-hides when no mentor is assigned.
Both procedures use the existing MentorMessage(projectId, createdAt)
composite index — no new index needed.
Plan: docs/superpowers/plans/2026-04-28-pr6-multi-role-and-workspace-previews.md
Extract ROLE_SWITCH_OPTIONS + switchableRoles computation from the two
duplicated copies (role-nav.tsx + admin-sidebar.tsx) into a single
src/components/layouts/role-switcher.tsx module.
Adds a RoleSwitcherPill component placed top-right of every dashboard:
- Hidden for single-role users
- Hidden during impersonation
- Same visual + click target across /jury, /mentor, /applicant,
/observer, /award-master AND /admin (admin layout gains a small
top-bar to host the pill)
Removes the duplicate role-switcher items from the admin sidebar's
bottom user-menu — one source of truth instead of three.
Plan: docs/superpowers/plans/2026-04-28-pr6-multi-role-and-workspace-previews.md
user.getDefaultDashboard returns the highest-priority role for which the
user has actionable work right now — pending eval in active round, active
mentoring assignment, applicant project in active round, etc. — falling
back to static priority order if nothing is actionable.
src/app/page.tsx now reads roles[] (multi-role array) instead of just the
primary role, fixing the bug where mentor+juror users always landed on
their primary role's dashboard. Uses static priority for simplicity in
the server component; the context-aware procedure remains available for
client surfaces.
Tests cover six cases: super-admin, juror with active eval, juror+observer
fallback, mentor+juror in mentoring round, both-active-priority-tiebreak,
observer-only.
Plan: docs/superpowers/plans/2026-04-28-pr6-multi-role-and-workspace-previews.md
Replaces the redirect-to-/admin/members stub with a sortable, searchable
list of all MENTOR-role users powered by mentor.getMentorPool. Columns:
name, expertise tags, country, active count, completed count, capacity
remaining, last activity. Header summary cards show pool size, total
active assignments, and average load.
Row links continue to /admin/members/[id]; /admin/mentors/[id] remains
a redirect (mentor-detail view deferred to a future PR).
Plan: docs/superpowers/plans/2026-04-28-pr5-mentor-round-overview.md
Replaces single-section AI-only stub with three sections (Project Context,
Currently Assigned, Pick a Mentor). Pick a Mentor is a tab strip:
- Manual Picker (default): all MENTOR-role users sorted by expertise
overlap %, with search + load/capacity columns. Assign sends
method=MANUAL.
- AI Suggestions: existing pane, with an amber 'AI matching unavailable'
banner + 'Tag overlap' pills when OPENAI_API_KEY is unset.
Plan: docs/superpowers/plans/2026-04-28-pr4-mentor-assignment-ux.md
Adds a PROJECT_TEAM recipient type to the message router (resolver
returns team members + project lead) and an "Email Team" button on
the admin project detail page that opens a self-contained dialog
matching the look of /admin/messages: subject, body (pre-filled
with "Hello [Project Title] team,\n\n"), live HTML preview iframe,
"Send test to me" + "Send to N" actions.
The composer reuses the existing message.previewEmail and
message.send tRPC procedures end-to-end — no parallel email
infrastructure introduced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces every MentoringConfigSchema field on the round Config tab:
- Adds "Mentoring Request Window" card with mentoringRequestDeadlineDays
numeric input (1-90 days, default 14) and passThroughIfNoRequest toggle
(default ON; OFF holds projects PENDING until manual mentor assignment).
- Adds inline help-text for the Eligibility dropdown explaining each
option's effect on auto-PASS behavior.
- Hides the General Settings card on MENTORING rounds (it only renders
Advancement Targets, which don't apply to a pass-through round).
- Relaxes the Launch Readiness "File requirements set" gate for MENTORING
rounds without filePromotionEnabled + a target window — file requirements
only matter when files will be promoted to a downstream submission window.
Spec: docs/superpowers/specs/2026-04-28-mentor-round-readiness-design.md §A
Plan: docs/superpowers/plans/2026-04-28-pr3-mentoring-config-completeness.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds generateMentorObjectKey helper producing
<projectName>/mentorship/<timestamp>-<file>. Replaces the
client-supplied bucket/objectKey on workspaceUploadFile with an
HMAC-signed upload token that binds bucket, objectKey, uploader,
and a 1h expiry — paths can no longer be forged from the client.
Adds workspaceGetUploadUrl, workspaceGetFiles,
workspaceGetFileDownloadUrl, workspaceDeleteFile procedures with
mentor-or-team-member auth. Builds <WorkspaceFilesPanel> and
wires it into the mentor workspace Files tab and the applicant
/applicant/mentor page. Replaces the file-promotion-panel mock
array with a real workspaceGetFiles query.
Tests cover token sign/verify (5), key construction (5), and
end-to-end procedure flow including auth + tampered tokens (7).
Spec: docs/superpowers/specs/2026-04-28-mentor-round-readiness-design.md §F.1
Plan: docs/superpowers/plans/2026-04-28-pr2-mentor-workspace-files.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The observer full project page used to call getProjectDetail without
a round, getting cross-round contaminated stats. It now resolves a
default — the currently OPEN round the project is in, falling back
to the most recently CLOSED one — and renders a selector chip in
the score card whenever the project participated in more than one
candidate round. Initial selection respects the ?round= query param.
A new observer procedure (getProjectRoundsForObserver) returns the
project's open or closed rounds for the picker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Top-level selector in the URL (?round=...) drives every single-round tab
(Overview, Analytics, Juror Consistency, Diversity) and narrows the Pipeline
tab to the selected program. Cross-Round keeps its own multi-select because
it compares multiple rounds by design.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a shared juror-balancing utility (z-score normalization per juror,
rescaled back onto the raw 1-10 scale) and wires it into:
- Admin reports page: Top-10 project table now shows "Raw Avg" and
"Balanced" columns side by side, and the summary stats row shows a
balanced-average tile. Sort defaults to balanced so harsh and lenient
graders no longer skew the ranking.
- Ranking dashboard: each project row shows a green/amber balanced-score
chip next to the raw average when the two differ by ≥0.05, making it
obvious when juror calibration moved a project's effective ranking.
Also adds AI Juror Calibration Advisory — a mutation that takes
anonymized per-juror stats, calls OpenAI, and produces a plain-language
explanation of the cohort's grading patterns plus per-juror severity
(normal / notable / outlier) with a one-sentence narrative. The advisory
describes the statistical balance that already runs; it does not
introduce a new weighting layer. Rendered as a panel in the Juror
Consistency tab when a specific round is selected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seven scenarios covering the new admin procedures:
- non-admin users are rejected on adminStart/adminSubmitOnBehalf
- admin can list a juror's assignments with COI flag surfaced
- admin can complete the full draft→autosave→submit cycle with the
voting window already closed, confirming bypass works
- COI-declared assignments still block admin submission
- feedback minimum length is enforced on admin submit
- adminAutosave refuses to overwrite a SUBMITTED evaluation
- audit log captures admin id, juror id, and bypassedWindow flag
Also swaps a lucide icon alias (FileEdit → Pencil) in the juror
assignments page to avoid the deprecated alias form.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a juror cannot connect during an evaluation round, an admin
can now submit evaluations for them.
Router — new admin procedures:
- adminStart / adminAutosave: create and save drafts for any juror.
- adminSubmitOnBehalf: submit bypassing ROUND_ACTIVE and voting-window
checks. COI block and feedback/criterion validation still enforced.
Audit log records both admin and juror IDs plus bypassedWindow flag.
- getJurorAssignmentsForRound: list a juror's assignments + eval state.
UI — two new admin pages under /admin/rounds/[roundId]/jurors/[userId]/:
- evaluate: list of pending + completed assignments, COI flagged.
- evaluate/[projectId]: evaluation form reusing the juror's scoring UI,
with an "acting on behalf" banner and confirmation dialog before
submit. Back button returns to the assignments list.
Entry point: FilePen icon on each juror row in JuryProgressTable.
Refactor: extracted the scoring form JSX into shared
EvaluationFormFields component so the juror page and the admin proxy
page render identical inputs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Jury dashboard now shows "Submitted" badge (green) with "Edit
Rankings" button when juror has already voted, instead of always
showing "Vote Now" — prevents confusion about whether vote saved
- Award-master page now shows project logos next to project names
- Backend getMyAwardDetailEnhanced now returns logo URLs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace custom download-only file list with full FileViewer component
that supports inline preview (PDF, video, images, Office docs),
open in new tab, and download
- Add project logos next to project names in award voting cards
- Backend now returns full file metadata (mimeType, size, pageCount,
language detection) and project logo URLs
- Award jurors can access files for eligible projects (access control
already added in prior commit)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Jury dashboard now shows active award voting banners with project
count, deadline countdown, and direct link to vote
- Award voting page shows full project details: description, team
members, tags, and downloadable files in expandable cards
- Award jurors can now download files for eligible projects (added
awardJuror access check to file.getDownloadUrl)
- Backend query enhanced to include files and team members
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The juror dropdown was always empty because the page requested
perPage: 200 but the user.list API caps at 100 (Zod validation).
Fixed to perPage: 100 with role filter for JURY_MEMBER/AWARD_MASTER.
Added "Import from Jury Group" section to the awards juror tab:
select a jury group, see members with checkboxes (already-assigned
shown as disabled), bulk-add selected members via bulkAddJurors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email lookups used findUnique (case-sensitive on PostgreSQL) but user
input was lowercased, causing login failures for users with mixed-case
emails stored in the DB (e.g. Laurent_Faure@dietsmann.com). Also
normalized 7 affected emails to lowercase on the production DB.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The admin upload flow accepted roundId but never wrote it to the
ProjectFile record, causing all admin-uploaded files to appear under
"General". Fixed the create call, the listByProject filter, and the
listByProjectForStage grouping to also use the direct roundId field.
Jury assignments on the project detail page are now grouped by round
with per-round completion counts instead of a flat list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds an "Additional Roles" checkbox section below the primary Role
dropdown. Admins can now grant a user multiple dashboard views (e.g.,
Observer + Jury Member) without changing their primary role. The roles
array is saved alongside the primary role and used by the role switcher.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds bulkInviteMembers procedure to juryGroup router and integrates
BulkInviteForm into the jury group members tab. Also removes the
JURY_MEMBER-only filter from the user search — any user can now be
added to a jury group.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously showed "Select a jury group below" but the selector was
buried at the bottom (or not rendered at all when no jury was assigned).
Now shows the actual dropdown + "New Jury" button right where the
empty state message was.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ability to define completely different evaluation criteria for each
competition category. Admins toggle "Separate Criteria per Category" in
round config, then configure criteria independently via tabbed editor.
- Schema: add nullable `category` to EvaluationForm with updated constraints
- Config: add `perCategoryCriteria` boolean to EvaluationConfigSchema
- Helper: new `findActiveForm()` with category-aware resolution + fallback
- Backend: getForm, upsertForm, getStageForm, startStage all category-aware
- AI services: use project category for form lookup in summaries + ranking
- Export/ranking: merge criteria from all active forms for cross-category reports
- Admin UI: toggle switch + tabbed criteria editor with per-category builders
- Jury UI: auto-selects correct form based on project category (invisible to juror)
- Fully backwards compatible: toggle defaults OFF, existing forms unchanged
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- z.any() replaced with z.record(z.string()) on webhook headers
- availabilityJson typed with z.array(z.object({ start, end }))
- Frontend webhook headers converted from array to Record before API call
- Docker HEALTHCHECK added to Dockerfile (health endpoint already existed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Webhook HMAC: added consumer verification JSDoc with Node.js example using crypto.timingSafeEqual
- CSRF rate limiting: 20 requests/15min per IP on NextAuth /csrf endpoint
- Renamed withRateLimit to withPostRateLimit/withGetRateLimit for clarity
- 429 responses include Retry-After header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extends CountryDisplay component usage to all remaining pages that showed
raw country codes: mentor dashboard/projects, jury competitions/awards,
admin awards/project detail, applicant team, and project-list-compact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix project list returning empty for users with both SUPER_ADMIN and
JURY_MEMBER roles (jury filter now skips admins) in project, assignment,
and evaluation routers
- Add CountryDisplay component showing flag emoji + name everywhere
country is displayed (admin, observer, jury, mentor views — 17 files)
- Add countdown deadline banner on applicant dashboard for INTAKE,
SUBMISSION, and MENTORING rounds with live timer
- Remove quick action buttons from applicant dashboard
- Fix competition timeline sidebar: green dots/connectors only up to
current round, yellow dot for current round, red connector into
rejected round, grey after
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Convert all Back buttons platform-wide (38 files) to use router.back()
for natural browser-back behavior regardless of entry point
- Add read-only view for submitted evaluations in closed rounds with
blue banner, disabled inputs, and contextual back navigation
- Add auth audit logs: MAGIC_LINK_SENT, PASSWORD_RESET_LINK_CLICKED,
PASSWORD_RESET_LINK_EXPIRED, PASSWORD_RESET_LINK_INVALID
- Learning Hub links navigate in same window for all roles
- Update settings descriptions to reflect all-user scope
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Observer projects: default sort by status (rejected last), sortable status column
- Observer projects: search by country, institution, geographic zone
- Observer project detail: vertical timeline connectors between rounds
- Fix React key warning in ExpandableJurorTable and FilteringReportTabs
- Fix ScoreBadge text always white for better contrast on all backgrounds
- Remove misleading /30 denominator from heatmap juror reviewed count
- INTAKE stats: show Start-ups, Business Concepts, Countries (not States/Categories)
- DiversityMetrics: extractCountry() for country-only display in charts
- Fix nested button hydration error in filtering report mobile view
- Color project titles by outcome in filtering report (green/red/amber)
- Redesign CrossStageComparisonChart: funnel viz + metrics table with attrition %
- Center doughnut chart in StatusBreakdownChart
- Remove redundant RoundTypeStatsCards from evaluation report
- Move evaluation tab bar below overview header, rename to "Juror Assignments"
- Dev email override system (DEV_EMAIL_OVERRIDE env var)
- Session refresh on role change without re-login
- Role switcher in user dropdown menu
- formatCategory() utility for consistent category display
- Activity feed max height constraint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Communication hub now supports selecting multiple rounds when sending
to Round Jury or Round Applicants (checkbox list instead of dropdown)
- Recipients are unioned across selected rounds with deduplication
- Recipient details grouped by round when multiple rounds selected
- Added MOPC logo above "Welcome back" on login page
- Added matt@letsbe.solutions as seeded APPLICANT account
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New roundLanguageSummary query in file router aggregates per-round document
language data from existing detectedLang/langConfidence fields
- Document Languages card on round overview tab shows analysis status and
flags non-English documents grouped by project with confidence scores
- Green border when all documents are English, amber when issues detected
- Project names link to project detail page for easy navigation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>