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>
- Audit log: user names link to /admin/members/{id}, entity IDs link to
relevant detail pages (projects, rounds, awards, users)
- Communication hub: expandable "View Recipients" section in sidebar shows
actual users/projects that will receive the message, with collapsible
project-level detail for applicants and juror assignment counts
- Email link type selector: choose between no link, messages inbox, login
page, or invite/accept link (auto-detects new vs existing members)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Version guard:
- Replace API route with prebuild-generated public/build-id.json
- Captures build ID on first load, only notifies on mismatch
- Fixes false positive refresh prompts from env mismatch
Members table (applicants):
- Show project name + round badge instead of round name + state
- Red badge for rejected, gray for withdrawn, green for passed,
outline for active rounds
- Include projectName in applicantRoundInfo from backend
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- /error?error=Verification: shows "Link Expired" with amber icon,
auto-redirects to /login?expired=1 after 5 seconds
- /accept-invite: expired/invalid/already-accepted tokens auto-redirect
to login after 4 seconds with "Redirecting..." message
- /login: amber banner when ?expired=1 explains the link expired and
prompts to sign in again or request a new magic link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Inject NEXT_PUBLIC_BUILD_ID at build time via next.config.ts
- /api/version static route returns current build ID
- VersionGuard client component checks on tab focus + every 5 min
- Shows persistent toast with Refresh button (no auto-reload)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New previewRecipients query shows live project/applicant counts as you
compose, with per-state breakdown for Round Applicants
- Exclude Rejected/Withdrawn checkboxes filter out terminal-state projects
- Compose form now has 2/3 + 1/3 layout with always-visible recipient
summary sidebar showing project counts, applicant counts, state badges
- Preview dialog enlarged (max-w-3xl) with split layout: email preview
on left, recipient/delivery summary on right
- Send button now shows recipient count ("Send to N Recipients")
- resolveRecipients accepts excludeStates param for ROUND_APPLICANTS
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sort files by round sortOrder first (via requirement.round.sortOrder)
- Admin project detail now uses grouped file view with round headers
- Files without a round requirement appear under "General" group
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Admin projects: status summary badges are clickable to filter by round state
with ring highlight, opacity fade, and clear button
- Add roundStates filter param to project.list backend query
(filters by latest round state per project, consistent with counts)
- Observer status dropdown now uses ProjectRoundState values
(Pending/In Progress/Completed/Passed/Rejected/Withdrawn)
- Observer status derived from latest ProjectRoundState instead of stale Project.status
- Observer CSV export fetches all matching projects, not just current page
- Add PENDING and PASSED styles to StatusBadge component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace flat team names list with proper cards showing roles and badges
- Hide TeamMembers from metadata display, remove Withdraw from header
- Add inline-editable project description (admin-toggleable setting)
- Move applicant feedback visibility from per-round config to admin settings
- Support EVALUATION, LIVE_FINAL, DELIBERATION round types in feedback
- Backwards-compatible: falls back to old per-round config if no settings exist
- Add observer team tab toggle and 10 new SystemSettings seed entries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Team tab to observer project detail (configurable via admin settings)
- Move applicant jury feedback visibility from per-round config to admin settings
- Add per-round-type controls: evaluation, live final, deliberation
- Support anonymous LiveVote and DeliberationVote display for applicants
- Add fine-grained toggles: scores, criteria, written feedback, hide from rejected
- Backwards compatible: falls back to old per-round config if admin settings not set
- New admin settings section under Analytics tab with all visibility controls
- Seed new SystemSettings keys for observer/applicant visibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add updateTeamMemberRole mutation for admins to change team member roles
- Allow any team member (not just lead) to change project logo
- Add visible "Add logo"/"Change" label under logo for discoverability
- Pre-check email existence before sending magic link (show error)
- Add "forgot which email" contact link on login page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Award-specific rounds (e.g. Spotlight on Africa) were mixed into the
main pipeline as a flat list, making them look like sequential steps
after Deliberation. Now they render in their own amber-tinted card
sections below the main pipeline, each with a header showing award
name, pool size, eligibility mode, and status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>