Competition/Round architecture: full platform rewrite (Phases 1-9)
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m45s

Replace Pipeline/Stage system with Competition/Round architecture.
New schema: Competition, Round (7 types), JuryGroup, AssignmentPolicy,
ProjectRoundState, DeliberationSession, ResultLock, SubmissionWindow.
New services: round-engine, round-assignment, deliberation, result-lock,
submission-manager, competition-context, ai-prompt-guard.
Full admin/jury/applicant/mentor UI rewrite. AI prompt hardening with
structured prompts, retry logic, and injection detection. All legacy
pipeline/stage code removed. 4 new migrations + seed aligned.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 23:04:15 +01:00
parent 9ab4717f96
commit 6ca39c976b
349 changed files with 69938 additions and 28767 deletions

507
CLAUDE.md
View File

@@ -1,388 +1,171 @@
# MOPC Platform - Claude Code Context
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**MOPC (Monaco Ocean Protection Challenge)** is a secure jury online voting platform for managing project selection rounds. The platform enables jury members to evaluate submitted ocean conservation projects, with Phase 1 supporting two selection rounds:
- **Round 1**: ~130 projects → ~60 semi-finalists
- **Round 2**: ~60 projects → 6 finalists
**Domain**: `monaco-opc.com`
The platform is designed for future expansion into a comprehensive program management system including learning hub, communication workflows, and partner modules.
## Key Decisions
| Decision | Choice |
|----------|--------|
| Evaluation Criteria | Fully configurable per round (admin defines) |
| CSV Import | Flexible column mapping (admin maps columns) |
| Max File Size | 500MB (for videos) |
| Observer Role | Included in Phase 1 |
| First Admin | Database seed script |
| Past Evaluations | Visible read-only after submit |
| Grace Period | Admin-configurable per juror/project |
| Smart Assignment | AI-powered (GPT) + Smart Algorithm fallback + geo-diversity, familiarity, COI scoring |
| AI Data Privacy | All data anonymized before sending to GPT |
| Evaluation Criteria Types | `numeric`, `text`, `boolean`, `section_header` (backward-compatible) |
| COI Workflow | Mandatory declaration before evaluation, admin review |
| Evaluation Reminders | Cron-based email reminders with countdown urgency |
## Brand Identity
| Name | Hex | Usage |
|------|-----|-------|
| Primary Red | `#de0f1e` | CTAs, alerts |
| Dark Blue | `#053d57` | Headers, sidebar |
| White | `#fefefe` | Backgrounds |
| Teal | `#557f8c` | Links, secondary |
**Typography**: Montserrat (600/700 for headings, 300/400 for body)
## Tech Stack
| Layer | Technology | Version |
|-------|-----------|---------|
| **Framework** | Next.js (App Router) | 15.x |
| **Language** | TypeScript | 5.x |
| **UI Components** | shadcn/ui | latest |
| **Styling** | Tailwind CSS | 3.x |
| **API Layer** | tRPC | 11.x |
| **Database** | PostgreSQL | 16.x |
| **ORM** | Prisma | 6.x |
| **Authentication** | NextAuth.js (Auth.js) | 5.x |
| **AI** | OpenAI GPT | 4.x SDK |
| **Animation** | Motion (Framer Motion) | 11.x |
| **Notifications** | Sonner | 1.x |
| **Command Palette** | cmdk | 1.x |
| **File Storage** | MinIO (S3-compatible) | External |
| **Email** | Nodemailer + Poste.io | External |
| **Containerization** | Docker Compose | 2.x |
| **Reverse Proxy** | Nginx | External |
## Architecture Principles
1. **Type Safety First**: End-to-end TypeScript from database to UI via Prisma → tRPC → React
2. **Mobile-First Responsive**: All components designed for mobile, enhanced for desktop
3. **Full Control**: No black-box services; every component is understood and maintainable
4. **Extensible Data Model**: JSON fields for future attributes without schema migrations
5. **Security by Default**: RBAC, audit logging, secure file access with pre-signed URLs
## File Structure
```
mopc-platform/
├── CLAUDE.md # This file - project context
├── docs/
│ └── architecture/ # Architecture documentation
│ ├── README.md # System overview
│ ├── database.md # Database design
│ ├── api.md # API design
│ ├── infrastructure.md # Deployment docs
│ └── ui.md # UI/UX patterns
├── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── (auth)/ # Public auth routes (login, verify)
│ │ ├── (admin)/ # Admin dashboard (protected)
│ │ ├── (jury)/ # Jury interface (protected)
│ │ ├── api/ # API routes
│ │ │ ├── trpc/ # tRPC endpoint
│ │ │ └── cron/
│ │ │ └── reminders/ # Cron endpoint for evaluation reminders (F4)
│ │ ├── layout.tsx # Root layout
│ │ └── page.tsx # Home/landing
│ ├── components/
│ │ ├── ui/ # shadcn/ui components
│ │ ├── admin/ # Admin-specific components
│ │ │ └── evaluation-summary-card.tsx # AI summary display
│ │ ├── forms/ # Form components
│ │ │ ├── evaluation-form.tsx # With progress indicator (F1)
│ │ │ ├── coi-declaration-dialog.tsx # COI blocking dialog (F5)
│ │ │ └── evaluation-form-with-coi.tsx # COI-gated wrapper (F5)
│ │ ├── layouts/ # Layout components (sidebar, nav)
│ │ └── shared/ # Shared components
│ │ └── countdown-timer.tsx # Live countdown with urgency (F4)
│ ├── lib/
│ │ ├── auth.ts # NextAuth configuration
│ │ ├── prisma.ts # Prisma client singleton
│ │ ├── trpc/ # tRPC client & server setup
│ │ ├── minio.ts # MinIO client
│ │ └── email.ts # Email utilities
│ ├── server/
│ │ ├── routers/ # tRPC routers by domain
│ │ │ ├── program.ts
│ │ │ ├── round.ts
│ │ │ ├── project.ts
│ │ │ ├── user.ts
│ │ │ ├── assignment.ts
│ │ │ ├── evaluation.ts
│ │ │ ├── audit.ts
│ │ │ ├── settings.ts
│ │ │ ├── gracePeriod.ts
│ │ │ ├── export.ts # CSV export incl. filtering results (F2)
│ │ │ ├── analytics.ts # Reports/analytics (observer access, F3)
│ │ │ └── mentor.ts # Mentor messaging endpoints (F10)
│ │ ├── services/ # Business logic services
│ │ │ ├── smart-assignment.ts # With geo/familiarity/COI scoring (F8)
│ │ │ ├── evaluation-reminders.ts # Email reminder service (F4)
│ │ │ └── ai-evaluation-summary.ts # GPT summary generation (F7)
│ │ └── middleware/ # RBAC & auth middleware
│ ├── hooks/ # React hooks
│ ├── types/ # Shared TypeScript types
│ └── utils/ # Utility functions
├── prisma/
│ ├── schema.prisma # Database schema
│ ├── migrations/ # Migration files
│ └── seed.ts # Seed data
├── public/ # Static assets
├── docker/
│ ├── Dockerfile # Production build
│ ├── docker-compose.yml # Production stack
│ └── docker-compose.dev.yml # Development stack
├── tests/
│ ├── unit/ # Unit tests
│ └── e2e/ # End-to-end tests
└── config files... # package.json, tsconfig, etc.
```
## Coding Standards
### TypeScript
- Strict mode enabled
- Explicit return types for functions
- Use `type` over `interface` for consistency (unless extending)
- Prefer `unknown` over `any`
### React/Next.js
- Use Server Components by default
- `'use client'` only when needed (interactivity, hooks)
- Collocate components with their routes when specific to that route
- Use React Query (via tRPC) for server state
### Naming Conventions
- **Files**: kebab-case (`user-profile.tsx`)
- **Components**: PascalCase (`UserProfile`)
- **Functions/Variables**: camelCase (`getUserById`)
- **Constants**: SCREAMING_SNAKE_CASE (`MAX_FILE_SIZE`)
- **Database Tables**: PascalCase in Prisma (`User`, `Project`)
- **Database Columns**: camelCase in Prisma (`createdAt`)
### Styling
- Tailwind CSS utility classes
- Mobile-first: base styles for mobile, `md:` for tablet, `lg:` for desktop
- Use shadcn/ui components as base, customize via CSS variables
- No inline styles; no separate CSS files unless absolutely necessary
### API Design (tRPC)
- Group by domain: `trpc.program.create()`, `trpc.round.list()`
- Use Zod for input validation
- Return consistent response shapes
- Throw `TRPCError` with appropriate codes
**MOPC (Monaco Ocean Protection Challenge)** a secure jury voting platform for managing multi-round project selection. Jury members evaluate ocean conservation projects through configurable competitions with rounds (intake, filtering, evaluation, submission, mentoring, live finals, deliberation). Domain: `monaco-opc.com`.
## Common Commands
```bash
# Development
npm run dev # Start Next.js dev server
npm run db:studio # Open Prisma Studio
npm run db:push # Push schema changes (dev only)
npm run db:migrate # Run migrations
npm run db:seed # Seed database
npm run dev # Next.js dev (Turbopack)
npm run build # Production build (always test before push)
npm run typecheck # tsc --noEmit
# Testing
npm run test # Run unit tests
npm run test:e2e # Run E2E tests
npm run test:coverage # Test with coverage
# Database
npx prisma generate # Regenerate client after schema changes
npx prisma migrate dev # Create + apply migration
npx prisma studio # GUI database browser
npm run db:seed # Run seed (tsx prisma/seed.ts)
# Build & Deploy
npm run build # Production build
npm run start # Start production server
docker compose up -d # Start Docker stack
docker compose logs -f app # View app logs
# Testing (vitest, not jest)
npx vitest # Run all tests (watch mode)
npx vitest run # Run all tests once
npx vitest run tests/unit/round-engine.test.ts # Single file
npx vitest run -t 'test name' # Single test by name
# Code Quality
npm run lint # ESLint
npm run format # Prettier
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run format # Prettier
# Docker (production)
docker compose -f docker/docker-compose.yml up -d
docker compose -f docker/docker-compose.dev.yml up -d # Dev stack
```
## Windows Development Notes
## Tech Stack
**IMPORTANT**: On Windows, all Docker commands AND all npm/node commands must be run using PowerShell (`powershell -ExecutionPolicy Bypass -Command "..."`), not bash/cmd. This is required for proper Docker Desktop integration and Node.js execution.
Next.js 15 (App Router) | TypeScript 5 (strict) | Tailwind CSS 4 | shadcn/ui | tRPC 11 (superjson) | Prisma 6 | PostgreSQL 16 | NextAuth 5 (Auth.js) | Vitest 4 | OpenAI SDK 6 | MinIO (S3) | Nodemailer + Poste.io
**IMPORTANT**: When invoking PowerShell from bash, always use `-ExecutionPolicy Bypass` to skip the user profile script which is blocked by execution policy:
```bash
powershell -ExecutionPolicy Bypass -Command "..."
## Architecture
### Data Flow: Prisma → tRPC → React
End-to-end type safety: `prisma/schema.prisma` defines models → `src/server/routers/*.ts` expose tRPC procedures with Zod validation → `src/lib/trpc/client.ts` provides typed React hooks → components call `trpc.domain.procedure.useQuery()`.
### tRPC Middleware Hierarchy (`src/server/trpc.ts`)
All role-based access is enforced via procedure types:
| Procedure | Roles Allowed |
|-----------|---------------|
| `publicProcedure` | Anyone (no auth) |
| `protectedProcedure` | Any authenticated user |
| `adminProcedure` | SUPER_ADMIN, PROGRAM_ADMIN |
| `superAdminProcedure` | SUPER_ADMIN only |
| `juryProcedure` | JURY_MEMBER only |
| `mentorProcedure` | SUPER_ADMIN, PROGRAM_ADMIN, MENTOR |
| `observerProcedure` | SUPER_ADMIN, PROGRAM_ADMIN, OBSERVER |
| `awardMasterProcedure` | SUPER_ADMIN, PROGRAM_ADMIN, AWARD_MASTER |
| `audienceProcedure` | Any authenticated user |
### Route Groups (Next.js App Router)
- `src/app/(auth)/` — Public auth pages (login, verify, accept-invite, onboarding)
- `src/app/(admin)/` — Admin dashboard, competition management, round config
- `src/app/(jury)/` — Jury evaluation interface, round assignments, live voting
- `src/app/(applicant)/` — Applicant dashboard, competition progress, document uploads
- `src/app/(mentor)/` — Mentor workspace
### Competition System
The core domain model. A **Competition** represents a complete evaluation cycle:
```
Competition → has many Rounds (ordered)
```
Examples:
```bash
# npm commands
powershell -ExecutionPolicy Bypass -Command "npm install"
powershell -ExecutionPolicy Bypass -Command "npm run build"
powershell -ExecutionPolicy Bypass -Command "npx prisma generate"
- **Competition** (`src/server/routers/competition.ts`): Top-level competition config with program link, status, and settings
- **Round** (`RoundType: INTAKE | FILTERING | EVALUATION | SUBMISSION | MENTORING | LIVE_FINAL | DELIBERATION`): Each competition has ordered rounds with type-specific config in `configJson`
# Docker commands
powershell -ExecutionPolicy Bypass -Command "docker compose -f docker/docker-compose.dev.yml up -d"
```
Key models:
- `JuryGroup` — Named jury panel with chair/member/observer roles
- `AdvancementRule` — Auto-advance, score threshold, top-N, admin selection between rounds
- `SubmissionWindow` — File submission deadlines per round
- `AssignmentPolicy` / `AssignmentIntent` — Governance layer for jury assignment
- `ProjectRoundState` — Per-project state within each round
- `DeliberationSession` / `DeliberationVote` / `ResultLock` — Structured deliberation and result finalization
```powershell
# Docker commands on Windows (use PowerShell)
docker compose -f docker/docker-compose.dev.yml up -d
docker compose -f docker/docker-compose.dev.yml build --no-cache app
docker compose -f docker/docker-compose.dev.yml logs -f app
docker compose -f docker/docker-compose.dev.yml down
```
Key services:
- `src/server/services/round-engine.ts` — State machine for round transitions
- `src/server/services/round-assignment.ts` — Jury assignment generation with policy enforcement
- `src/server/services/submission-manager.ts` — File submission + filtering with duplicate detection
- `src/server/services/deliberation.ts` — Deliberation session management and vote tallying
- `src/server/services/result-lock.ts` — Result finalization and unlock governance
- `src/server/services/live-control.ts` — Live ceremony cursor management
- `src/server/services/competition-context.ts` — Cross-cutting competition context resolver
Competition types: `src/types/competition.ts`, `src/types/competition-configs.ts`
### AI Services (`src/server/services/ai-*.ts`)
All AI calls anonymize data before sending to OpenAI. Services:
- `ai-filtering.ts` — AI-powered project screening with rubric
- `ai-assignment.ts` — GPT-suggested jury-project matching
- `ai-evaluation-summary.ts` — Strengths/weaknesses synthesis from evaluations
- `ai-tagging.ts` — Auto-tagging projects
- `ai-award-eligibility.ts` — Award eligibility assessment
- `ai-shortlist.ts` — AI-powered shortlist recommendations
- `anonymization.ts` — Strips PII before AI calls
### Auth System
NextAuth v5 with two providers: **Email** (magic links) and **Credentials** (password + invite token). Failed login tracking with 5-attempt lockout (15 min). Session includes `user.role` for RBAC.
### Docker Entrypoint (`docker/docker-entrypoint.sh`)
On container start: retry `prisma migrate deploy``prisma generate` → auto-seed if User table is empty → `node server.js`. A `docker compose down -v && docker compose up -d` will run all migrations from scratch and seed.
## Testing Infrastructure
- **Framework**: Vitest 4 with `fileParallelism: false` and `pool: 'forks'` (tests run sequentially)
- **Setup**: `tests/setup.ts` provides `prisma` client (uses `DATABASE_URL_TEST` or `DATABASE_URL`), `createTestContext(user)`, `createCaller(router, user)`
- **Factories**: `tests/helpers.ts` has `createTestUser()`, `createTestProgram()`, `createTestCompetition()`, `createTestRound()`, `uid()` helper, and `cleanupTestData()`
- **Pattern**: Create data with factories → build caller with `createCaller(routerModule, user)` → call procedures → assert → cleanup in `afterAll`
## User Roles
`SUPER_ADMIN` | `PROGRAM_ADMIN` | `JURY_MEMBER` | `OBSERVER` | `MENTOR` | `APPLICANT` | `AWARD_MASTER` | `AUDIENCE`
## Coding Standards
- **TypeScript**: Strict mode, `type` over `interface`, prefer `unknown` over `any`
- **Files**: kebab-case. **Components**: PascalCase. **DB models**: PascalCase in Prisma
- **React**: Server Components by default, `'use client'` only when needed
- **Styling**: Tailwind utility classes, mobile-first (`md:`, `lg:` breakpoints), shadcn/ui as base
- **tRPC**: Group by domain (`trpc.competition.create()`), Zod input validation, `TRPCError` for errors
- **Brand colors**: Primary Red `#de0f1e`, Dark Blue `#053d57`, White `#fefefe`, Teal `#557f8c`
- **Typography**: Montserrat (600/700 headings, 300/400 body)
## Key Constraints
1. Jury can only see assigned projects (enforced at query level)
2. Voting windows are strict — submissions blocked outside active window
3. All admin actions are audited via `DecisionAuditLog`
4. Files accessed via MinIO pre-signed URLs only (no public bucket)
5. COI declaration required before evaluation (blocking dialog)
6. Smart assignment skips COI-declared jurors, applies geo-diversity penalty and familiarity bonus
7. Cron endpoints protected by `CRON_SECRET` header
8. Round notifications never throw — all errors caught and logged
## Security
- CSRF: tRPC uses `application/json` (triggers CORS preflight). Do NOT add permissive CORS headers.
- Rate limiting: 100 req/min tRPC, 10 req/min auth, 5-attempt lockout
- AI privacy: All data anonymized before OpenAI calls
## Seed Data
`prisma/seed.ts` imports from `docs/Candidatures 2026 *.csv` with special handling for non-breaking spaces (U+00A0) around French guillemets. Includes ALL CSV rows (no filtering/dedup) — duplicate detection happens in `submission-manager.ts` at runtime.
## Environment Variables
```env
# Database
DATABASE_URL="postgresql://user:pass@localhost:5432/mopc"
Required: `DATABASE_URL`, `NEXTAUTH_URL`, `NEXTAUTH_SECRET`, `MINIO_ENDPOINT`, `MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`, `MINIO_BUCKET`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `EMAIL_FROM`, `OPENAI_API_KEY`, `CRON_SECRET`
# NextAuth
NEXTAUTH_URL="https://monaco-opc.com"
NEXTAUTH_SECRET="your-secret-key"
External services (pre-existing on VPS): MinIO `:9000`, Poste.io `:587`, Nginx reverse proxy with SSL.
# MinIO (existing separate stack)
MINIO_ENDPOINT="http://localhost:9000"
MINIO_ACCESS_KEY="your-access-key"
MINIO_SECRET_KEY="your-secret-key"
MINIO_BUCKET="mopc-files"
## Git
# Email (Poste.io - existing)
SMTP_HOST="localhost"
SMTP_PORT="587"
SMTP_USER="noreply@monaco-opc.com"
SMTP_PASS="your-smtp-password"
EMAIL_FROM="MOPC Platform <noreply@monaco-opc.com>"
# OpenAI (for smart assignment and AI evaluation summaries)
OPENAI_API_KEY="your-openai-api-key"
# Cron (for scheduled evaluation reminders)
CRON_SECRET="your-cron-secret-key"
```
## Key Architectural Decisions
### 1. Next.js App Router over Pages Router
**Rationale**: Server Components reduce client bundle, better data fetching patterns, layouts system
### 2. tRPC over REST
**Rationale**: End-to-end type safety without code generation, excellent DX with autocomplete
### 3. Prisma over raw SQL
**Rationale**: Type-safe queries, migration system, works seamlessly with TypeScript
### 4. NextAuth.js over custom auth
**Rationale**: Battle-tested, supports magic links, session management built-in
### 5. MinIO (external) over local file storage
**Rationale**: S3-compatible, pre-signed URLs for security, scalable, already deployed
### 6. JSON fields for extensibility
**Rationale**: `metadata_json`, `settings_json` allow adding attributes without migrations
### 7. Soft deletes with status fields
**Rationale**: Audit trail preservation, recovery capability, referential integrity
## User Roles (RBAC)
| Role | Permissions |
|------|------------|
| **SUPER_ADMIN** | Full system access, all programs, user management |
| **PROGRAM_ADMIN** | Manage specific programs, rounds, projects, jury |
| **JURY_MEMBER** | View assigned projects only, submit evaluations, declare COI |
| **OBSERVER** | Read-only access to dashboards, all analytics/reports |
| **MENTOR** | View assigned projects, message applicants via `mentorProcedure` |
| **APPLICANT** | View own project status, upload documents per round, message mentor |
## Important Constraints
1. **Jury can only see assigned projects** - enforced at query level
2. **Voting windows are strict** - submissions blocked outside active window
3. **Evaluations are versioned** - edits create new versions
4. **All admin actions are audited** - immutable audit log
5. **Files accessed via pre-signed URLs** - no public bucket access
6. **Mobile responsiveness is mandatory** - every view must work on phones
7. **File downloads require project authorization** - jury/mentor must be assigned to the project
8. **Mentor endpoints require MENTOR role** - uses `mentorProcedure` middleware
9. **COI declaration required before evaluation** - blocking dialog gates evaluation form; admin reviews COI declarations
10. **Evaluation form supports multiple criterion types** - `numeric`, `text`, `boolean`, `section_header`; defaults to `numeric` for backward compatibility
11. **Smart assignment respects COI** - jurors with declared conflicts are skipped entirely; geo-diversity penalty and prior-round familiarity bonus applied
12. **Cron endpoints protected by CRON_SECRET** - `/api/cron/reminders` validates secret header
13. **Project status changes tracked** - every status update creates a `ProjectStatusHistory` record
14. **Per-round document management** - `ProjectFile` supports `roundId` scoping and `isLate` deadline tracking
## Security Notes
### CSRF Protection
tRPC mutations are protected against CSRF attacks because:
- tRPC uses `application/json` content type, which triggers CORS preflight on cross-origin requests
- Browsers block cross-origin JSON POSTs by default (Same-Origin Policy)
- NextAuth's own routes (`/api/auth/*`) have built-in CSRF token protection
- No custom CORS headers are configured to allow external origins
**Do NOT add permissive CORS headers** (e.g., `Access-Control-Allow-Origin: *`) without also implementing explicit CSRF token validation on all mutation endpoints.
### Rate Limiting
- tRPC API: 100 requests/minute per IP
- Auth endpoints: 10 POST requests/minute per IP
- Account lockout: 5 failed password attempts triggers 15-minute lockout
## External Services (Pre-existing)
These services are already running on the VPS in separate Docker Compose stacks:
- **MinIO**: `http://localhost:9000` - S3-compatible storage
- **Poste.io**: `localhost:587` - SMTP server for emails
- **Nginx**: Host-level reverse proxy with SSL (certbot)
The MOPC platform connects to these via environment variables.
## Phase 1 Scope
### In Scope
- Round management (create, configure, activate/close)
- Project import (CSV) and file uploads
- Jury invitation (magic link)
- Manual project assignment (single + bulk)
- Evaluation form (configurable criteria)
- Autosave + final submit
- Voting window enforcement
- Progress dashboards
- CSV export
- Audit logging
- **F1: Evaluation progress indicator** - sticky status bar with percentage tracking across criteria, global score, decision, feedback
- **F2: Export filtering results as CSV** - dynamic AI column flattening from `aiScreeningJson`
- **F3: Observer access to reports/analytics** - all 8 analytics procedures use `observerProcedure`; observer reports page with round selector, tabs, charts
- **F4: Countdown timer + email reminders** - live countdown with urgency colors; `EvaluationRemindersService` with cron endpoint (`/api/cron/reminders`)
- **F5: Conflict of Interest declaration** - `ConflictOfInterest` model; blocking dialog before evaluation; admin COI review page
- **F6: Bulk status update UI** - checkbox selection, floating toolbar, `ProjectStatusHistory` tracking
- **F7: AI-powered evaluation summary** - `EvaluationSummary` model; GPT-generated strengths/weaknesses, themes, scoring stats
- **F8: Smart assignment improvements** - `geoDiversityPenalty`, `previousRoundFamiliarity`, `coiPenalty` scoring factors
- **F9: Evaluation form flexibility** - extended criterion types (`numeric`, `text`, `boolean`, `section_header`); conditional visibility, section grouping
- **F10: Applicant portal enhancements** - `ProjectStatusHistory` timeline; per-round document management (`roundId` + `isLate` on `ProjectFile`); `MentorMessage` model for mentor-applicant chat
### Out of Scope (Phase 2+)
- Typeform/Notion integrations
- WhatsApp notifications
- Learning hub
- Partner modules
- Public website
## Testing Strategy
- **Unit Tests**: Business logic, utilities, validators
- **Integration Tests**: tRPC routers with test database
- **E2E Tests**: Critical user flows (Playwright)
- **Manual Testing**: Responsive design on real devices
## Documentation Links
- [Architecture Overview](./docs/architecture/README.md)
- [Database Design](./docs/architecture/database.md)
- [API Design](./docs/architecture/api.md)
- [Infrastructure](./docs/architecture/infrastructure.md)
- [UI/UX Patterns](./docs/architecture/ui.md)
Remote: `code.monaco-opc.com/MOPC/MOPC-Portal`. Branch: `main`. Always `npm run build` before pushing.