MOPC-App/docs/claude-architecture-redesign/04-round-intake.md

75 KiB
Raw Blame History

Round Type: INTAKE — Application Window

1. Overview

The INTAKE round is the first phase of any MOPC competition. It represents the application submission window where teams apply to participate by submitting their ocean conservation projects along with required documentation.

Purpose

  • Collect project applications from teams worldwide
  • Capture essential project information (title, description, team, ocean issue addressed)
  • Receive required documentation (executive summaries, business plans, videos)
  • Support draft/save-and-continue workflow for incomplete applications
  • Enforce submission deadlines with configurable late submission policies
  • Enable both authenticated (user login) and public (anonymous) application flows

Position in Competition Flow

Competition 2026
  └─ Round 1: "Application Window" ────── [INTAKE]
      └─ Round 2: "AI Screening" ───────── [FILTERING]
           └─ Round 3: "Jury Evaluation 1" ── [EVALUATION]
                └─ Round 4: "Semi-finalist Docs" ── [SUBMISSION]
                     └─ ...

The intake round creates the initial pool of projects that flow through subsequent rounds. All projects begin here.


2. Current System (Pipeline/Track/Stage)

How Intake Works Today

In the current architecture, intake is implemented as a Stage with StageType: INTAKE inside the MAIN track.

// Current structure
Pipeline: "MOPC 2026"
  └─ Track: "Main Competition" (kind: MAIN)
      └─ Stage: "Intake" (stageType: INTAKE)
          ├─ windowOpenAt: 2026-01-15T00:00:00Z
          ├─ windowCloseAt: 2026-03-01T23:59:59Z
          ├─ configJson: {
              submissionWindowEnabled: true,
              lateSubmissionPolicy: "flag",
              lateGraceHours: 24,
              fileRequirements: [...]
            }
          └─ FileRequirement records (linked to stageId)

Current Config Fields (src/lib/pipeline-defaults.ts)

type IntakeConfig = {
  submissionWindowEnabled: boolean     // Whether submission is open
  lateSubmissionPolicy: 'reject' | 'flag' | 'accept'
  lateGraceHours: number              // Hours after close for late submissions
  fileRequirements: FileRequirementConfig[]
}

type FileRequirementConfig = {
  name: string                        // "Executive Summary"
  description?: string                // Help text
  acceptedMimeTypes: string[]         // ["application/pdf"]
  maxSizeMB?: number                  // 50
  isRequired: boolean                 // true
}

Current Applicant Flow

  1. Access: Applicant visits /applicant/pipeline/{slug} or receives invite link
  2. Auth: User logs in (email magic link or password) or proceeds as guest
  3. Form: Fills out project form (title, description, category, ocean issue, team info)
  4. Files: Uploads required files (exec summary, business plan, video)
  5. Draft: Can save as draft and return later
  6. Submit: Final submission creates Project record with status "SUBMITTED"
  7. Email: Confirmation email sent if configured

Current Admin Experience

Admins configure the intake stage via the pipeline wizard:

  • Set open/close dates
  • Define file requirements (name, mime types, size limits, required/optional)
  • Choose late submission policy (reject, flag, accept)
  • Set grace period for late submissions

Current Database Schema

model Stage {
  id            String      @id
  trackId       String
  stageType     StageType   // INTAKE
  name          String
  slug          String
  status        StageStatus
  configJson    Json?       // IntakeConfig stored here
  windowOpenAt  DateTime?
  windowCloseAt DateTime?
  ...
}

model FileRequirement {
  id                String   @id
  stageId           String   // Links to intake stage
  name              String
  acceptedMimeTypes String[]
  maxSizeMB         Int?
  isRequired        Boolean
  sortOrder         Int
  ...
}

model ProjectFile {
  id            String   @id
  projectId     String
  roundId       String?  // Legacy field
  requirementId String?  // FK to FileRequirement
  fileType      FileType
  fileName      String
  mimeType      String
  size          Int
  bucket        String
  objectKey     String
  isLate        Boolean  @default(false)
  version       Int      @default(1)
  ...
}

model Project {
  id                String             @id
  programId         String
  roundId           String?            // Legacy — which round project was submitted for
  status            ProjectStatus      @default(SUBMITTED)
  title             String
  teamName          String?
  description       String?
  competitionCategory CompetitionCategory?  // STARTUP | BUSINESS_CONCEPT
  oceanIssue        OceanIssue?
  country           String?
  wantsMentorship   Boolean            @default(false)
  isDraft           Boolean            @default(false)
  draftDataJson     Json?              // Form data for drafts
  draftExpiresAt    DateTime?
  submissionSource  SubmissionSource   @default(MANUAL)
  submittedByEmail  String?
  submittedAt       DateTime?
  submittedByUserId String?
  ...
}

Current Limitations

Issue Impact
Single submission window Can't require new docs from semi-finalists
No form builder All fields hardcoded in application code
No category quotas at intake Can't limit "first 50 startups, first 50 concepts"
Generic configJson Unclear what fields exist for intake stages
File requirements per stage Awkward: "intake stage" is the only stage with file requirements
No dynamic forms Can't add custom questions per competition year
No public form branding External applicants see generic MOPC form

3. Redesigned Intake Round

New Round Structure

// Redesigned
Competition: "MOPC 2026"
  └─ Round 1: "Application Window" (roundType: INTAKE)
      ├─ competitionId: competition-2026
      ├─ name: "Application Window"
      ├─ slug: "application-window"
      ├─ roundType: INTAKE
      ├─ status: ROUND_ACTIVE
      ├─ sortOrder: 0
      ├─ windowOpenAt: 2026-01-15T00:00:00Z
      ├─ windowCloseAt: 2026-03-01T23:59:59Z
      ├─ submissionWindowId: "sw-1"          // NEW: Links to SubmissionWindow
      ├─ configJson: IntakeConfig {          // NEW: Typed, validated config
          applicationFormId: "form-2026",
          deadlinePolicy: "GRACE",
          gracePeriodMinutes: 180,
          allowDraftSubmissions: true,
          requireTeamProfile: true,
          maxTeamSize: 5,
          minTeamSize: 1,
          autoConfirmReceipt: true,
          publicFormEnabled: true,
          categoryQuotas: { STARTUP: 100, BUSINESS_CONCEPT: 100 }
        }
      └─ SubmissionWindow: "Round 1 Docs"
          ├─ id: "sw-1"
          ├─ competitionId: competition-2026
          ├─ name: "Round 1 Application Docs"
          ├─ slug: "round-1-docs"
          ├─ roundNumber: 1
          ├─ windowOpenAt: 2026-01-15T00:00:00Z
          ├─ windowCloseAt: 2026-03-01T23:59:59Z
          ├─ deadlinePolicy: GRACE
          ├─ graceHours: 3
          └─ FileRequirements: [
              ├─ "Executive Summary" (PDF, required)
              ├─ "Business Plan" (PDF, required)
              └─ "Video Pitch" (video/*, optional)
             ]

IntakeConfig Type (Zod-validated)

type IntakeConfig = {
  // Application Form
  applicationFormId: string                     // Links to ApplicationForm template (future)

  // Submission Window (linked via Round.submissionWindowId)
  submissionWindowId: string                    // Which SubmissionWindow to use

  // Deadline Behavior
  deadlinePolicy: DeadlinePolicy                // HARD | FLAG | GRACE
  gracePeriodMinutes: number                    // For GRACE policy (e.g., 180 = 3 hours)

  // Draft System
  allowDraftSubmissions: boolean                // Save-and-continue enabled
  draftExpiryDays: number                       // Auto-delete abandoned drafts after N days

  // Team Profile
  requireTeamProfile: boolean                   // Require team member info
  maxTeamSize: number                           // Max team members (including lead)
  minTeamSize: number                           // Min team members (default: 1)

  // Notifications
  autoConfirmReceipt: boolean                   // Email confirmation on submission
  reminderEmailSchedule: number[]               // Days before deadline: [7, 3, 1]

  // Public Access
  publicFormEnabled: boolean                    // Allow external application link
  publicFormSlug?: string                       // Custom slug for public URL

  // Category Quotas (STARTUP vs BUSINESS_CONCEPT)
  categoryQuotasEnabled: boolean
  categoryQuotas?: {
    STARTUP: number                             // Max startups accepted
    BUSINESS_CONCEPT: number                    // Max concepts accepted
  }
  quotaOverflowPolicy?: 'reject' | 'waitlist'   // What happens when quota full

  // Custom Fields (future: dynamic form builder)
  customFields?: CustomFieldDef[]               // Additional form fields
}

enum DeadlinePolicy {
  HARD    // Submissions blocked after deadline, no exceptions
  FLAG    // Submissions accepted but flagged as late
  GRACE   // Accept for N minutes after deadline with warning
}

type CustomFieldDef = {
  id: string
  label: string
  type: 'text' | 'textarea' | 'select' | 'multiselect' | 'date' | 'number'
  required: boolean
  options?: string[]           // For select/multiselect
  validation?: {
    min?: number
    max?: number
    regex?: string
  }
}

Zod Schema for Validation

import { z } from 'zod'

export const intakeConfigSchema = z.object({
  applicationFormId: z.string().cuid(),
  submissionWindowId: z.string().cuid(),

  deadlinePolicy: z.enum(['HARD', 'FLAG', 'GRACE']),
  gracePeriodMinutes: z.number().int().min(0).max(1440), // Max 24 hours

  allowDraftSubmissions: z.boolean().default(true),
  draftExpiryDays: z.number().int().min(1).default(30),

  requireTeamProfile: z.boolean().default(true),
  maxTeamSize: z.number().int().min(1).max(20).default(5),
  minTeamSize: z.number().int().min(1).default(1),

  autoConfirmReceipt: z.boolean().default(true),
  reminderEmailSchedule: z.array(z.number().int()).default([7, 3, 1]),

  publicFormEnabled: z.boolean().default(false),
  publicFormSlug: z.string().optional(),

  categoryQuotasEnabled: z.boolean().default(false),
  categoryQuotas: z.object({
    STARTUP: z.number().int().min(0),
    BUSINESS_CONCEPT: z.number().int().min(0),
  }).optional(),
  quotaOverflowPolicy: z.enum(['reject', 'waitlist']).optional(),

  customFields: z.array(z.object({
    id: z.string(),
    label: z.string().min(1).max(200),
    type: z.enum(['text', 'textarea', 'select', 'multiselect', 'date', 'number']),
    required: z.boolean(),
    options: z.array(z.string()).optional(),
    validation: z.object({
      min: z.number().optional(),
      max: z.number().optional(),
      regex: z.string().optional(),
    }).optional(),
  })).optional(),
})

export type IntakeConfig = z.infer<typeof intakeConfigSchema>

4. Application Form System

ApplicationForm Model (Future Enhancement)

For now, the application form is hardcoded. In the future, a dynamic form builder will replace this.

model ApplicationForm {
  id              String   @id @default(cuid())
  competitionId   String
  name            String   // "MOPC 2026 Application"
  description     String?
  fieldsJson      Json     @db.JsonB  // Array of field definitions
  version         Int      @default(1)
  isActive        Boolean  @default(true)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)

  @@index([competitionId])
  @@index([isActive])
}

Standard Form Fields (Hardcoded for MVP)

type ApplicationFormData = {
  // Project Info
  title: string                          // Required, 1-500 chars
  teamName?: string                      // Optional
  description: string                    // Required, max 5000 chars
  competitionCategory: 'STARTUP' | 'BUSINESS_CONCEPT'  // Required
  oceanIssue: OceanIssue                 // Required enum

  // Location
  country: string                        // Required
  geographicZone?: string                // "Europe, France"
  institution?: string                   // Required for BUSINESS_CONCEPT

  // Founding
  foundedAt?: Date                       // When project/company started

  // Mentorship
  wantsMentorship: boolean               // Default: false

  // Referral
  referralSource?: string                // "LinkedIn", "Email", etc.

  // Team Members (if requireTeamProfile: true)
  teamMembers?: TeamMemberInput[]

  // Custom Fields (future)
  customFieldValues?: Record<string, unknown>
}

type TeamMemberInput = {
  name: string
  email: string
  role: 'LEAD' | 'MEMBER' | 'ADVISOR'
  title?: string                        // "CEO", "CTO"
}

Field Validation Rules

Field Validation Error Message
title Required, 1-500 chars "Project title is required"
description Required, max 5000 chars "Description must be under 5000 characters"
competitionCategory Required enum "Please select Startup or Business Concept"
oceanIssue Required enum "Please select an ocean issue"
country Required string "Country is required"
institution Required if category = BUSINESS_CONCEPT "Institution is required for student projects"
teamMembers[].email Valid email format "Invalid email address"
teamMembers.length >= minTeamSize, <= maxTeamSize "Team must have 1-5 members"

Conditional Logic

  • Institution field: Only shown/required when competitionCategory = BUSINESS_CONCEPT
  • Team members section: Only shown if requireTeamProfile = true in config
  • Mentorship checkbox: Always shown, default unchecked

5. Submission Window Integration

SubmissionWindow Model (from 03-data-model.md)

model SubmissionWindow {
  id              String @id @default(cuid())
  competitionId   String
  name            String       // "Round 1 Application Docs"
  slug            String       // "round-1-docs"
  roundNumber     Int          // 1 (first window), 2 (second window), etc.
  sortOrder       Int @default(0)

  windowOpenAt    DateTime?
  windowCloseAt   DateTime?

  deadlinePolicy  DeadlinePolicy @default(FLAG)
  graceHours      Int?           // For GRACE policy

  lockOnClose     Boolean @default(true)  // Prevent edits after close

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  competition      Competition              @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  fileRequirements SubmissionFileRequirement[]
  projectFiles     ProjectFile[]
  rounds           Round[]                  // INTAKE rounds using this window
  visibility       RoundSubmissionVisibility[]

  @@unique([competitionId, slug])
  @@unique([competitionId, roundNumber])
  @@index([competitionId])
}

model SubmissionFileRequirement {
  id                  String @id @default(cuid())
  submissionWindowId  String
  name                String           // "Executive Summary"
  description         String?          @db.Text
  acceptedMimeTypes   String[]         // ["application/pdf"]
  maxSizeMB           Int?
  isRequired          Boolean @default(true)
  sortOrder           Int     @default(0)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  submissionWindow SubmissionWindow @relation(fields: [submissionWindowId], references: [id], onDelete: Cascade)
  files            ProjectFile[]

  @@index([submissionWindowId])
}
// Round creation
const round = await prisma.round.create({
  data: {
    competitionId: competition.id,
    name: "Application Window",
    slug: "application-window",
    roundType: "INTAKE",
    sortOrder: 0,
    windowOpenAt: new Date("2026-01-15T00:00:00Z"),
    windowCloseAt: new Date("2026-03-01T23:59:59Z"),
    submissionWindowId: submissionWindow.id,  // Link to window
    configJson: {
      applicationFormId: "form-2026",
      submissionWindowId: submissionWindow.id, // Redundant but explicit
      deadlinePolicy: "GRACE",
      gracePeriodMinutes: 180,
      // ... rest of IntakeConfig
    }
  }
})

File Requirements for Intake

Admin configures file requirements at the SubmissionWindow level, not the round level.

// Example: Create file requirements for Round 1 docs
const requirements = await prisma.submissionFileRequirement.createMany({
  data: [
    {
      submissionWindowId: "sw-1",
      name: "Executive Summary",
      description: "A PDF executive summary of your project (max 2 pages)",
      acceptedMimeTypes: ["application/pdf"],
      maxSizeMB: 10,
      isRequired: true,
      sortOrder: 0,
    },
    {
      submissionWindowId: "sw-1",
      name: "Business Plan",
      description: "Full business plan or project proposal (PDF)",
      acceptedMimeTypes: ["application/pdf"],
      maxSizeMB: 50,
      isRequired: true,
      sortOrder: 1,
    },
    {
      submissionWindowId: "sw-1",
      name: "Video Pitch",
      description: "Optional video pitch (max 5 minutes, MP4 or MOV)",
      acceptedMimeTypes: ["video/mp4", "video/quicktime"],
      maxSizeMB: 500,
      isRequired: false,
      sortOrder: 2,
    },
  ]
})

Deadline Enforcement

When an applicant tries to upload a file or submit the form:

async function canSubmitToWindow(submissionWindow: SubmissionWindow): Promise<{
  canSubmit: boolean
  reason?: string
  isLate?: boolean
}> {
  const now = new Date()

  // Not yet open
  if (submissionWindow.windowOpenAt && now < submissionWindow.windowOpenAt) {
    return {
      canSubmit: false,
      reason: `Window opens on ${submissionWindow.windowOpenAt.toLocaleDateString()}`
    }
  }

  // Window closed
  if (submissionWindow.windowCloseAt && now > submissionWindow.windowCloseAt) {
    const { deadlinePolicy, graceHours } = submissionWindow

    if (deadlinePolicy === 'HARD') {
      return {
        canSubmit: false,
        reason: "Deadline has passed. Submissions are no longer accepted."
      }
    }

    if (deadlinePolicy === 'GRACE' && graceHours) {
      const graceDeadline = new Date(submissionWindow.windowCloseAt.getTime() + graceHours * 60 * 60 * 1000)
      if (now > graceDeadline) {
        return {
          canSubmit: false,
          reason: `Grace period ended on ${graceDeadline.toLocaleString()}`
        }
      }
      return {
        canSubmit: true,
        isLate: true
      }
    }

    if (deadlinePolicy === 'FLAG') {
      return {
        canSubmit: true,
        isLate: true
      }
    }
  }

  return { canSubmit: true }
}

6. Draft System

Auto-Save Behavior

When allowDraftSubmissions: true, the form auto-saves every 30 seconds (or on field blur).

// Draft auto-save
const saveDraft = async (formData: Partial<ApplicationFormData>) => {
  const project = await trpc.applicant.saveDraft.mutate({
    programId: programId,
    projectId: existingProjectId,  // null for new draft
    draftData: formData,
  })

  return project
}

// Draft expiry
const draftExpiresAt = new Date()
draftExpiresAt.setDate(draftExpiresAt.getDate() + config.draftExpiryDays)

await prisma.project.upsert({
  where: { id: projectId },
  update: {
    isDraft: true,
    draftDataJson: formData,
    draftExpiresAt: draftExpiresAt,
    updatedAt: new Date(),
  },
  create: {
    programId,
    isDraft: true,
    draftDataJson: formData,
    draftExpiresAt: draftExpiresAt,
    status: 'SUBMITTED',  // Will change to DRAFT status in redesign
    submittedByUserId: userId,
    submissionSource: 'PUBLIC_FORM',
  }
})

Resume Draft Flow

  1. User returns to application page
  2. System checks for existing draft:
    const draft = await prisma.project.findFirst({
      where: {
        programId,
        submittedByUserId: userId,
        isDraft: true,
        draftExpiresAt: { gt: new Date() }  // Not expired
      }
    })
    
  3. If found, pre-populate form with draft.draftDataJson
  4. User can continue editing or discard draft

Validation States

type FormValidationState = {
  isValid: boolean
  canSaveDraft: boolean      // Always true
  canSubmit: boolean         // All required fields filled + files uploaded
  errors: Record<string, string>
  warnings: Record<string, string>
}

// Example states:
// 1. Empty form → isValid: false, canSaveDraft: true, canSubmit: false
// 2. Partial form → isValid: false, canSaveDraft: true, canSubmit: false
// 3. Complete form → isValid: true, canSaveDraft: true, canSubmit: true

7. Applicant Experience

Landing Page Flow

┌─────────────────────────────────────────────────────────────┐
│  MOPC 2026 Application                                      │
│  ══════════════════════════════════════════════════════     │
│                                                              │
│  Application Window: Jan 15 - Mar 1, 2026                   │
│  ⏱️  23 days remaining                                       │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  🌊  Ocean Conservation Innovation Challenge       │     │
│  │                                                     │     │
│  │  We're looking for breakthrough ocean projects     │     │
│  │  from startups and student teams worldwide.        │     │
│  │                                                     │     │
│  │  📋 Requirements:                                   │     │
│  │   • Executive Summary (PDF)                        │     │
│  │   • Business Plan (PDF)                            │     │
│  │   • Video Pitch (optional)                         │     │
│  │                                                     │     │
│  │  ⏱️  Application takes ~30 minutes                 │     │
│  │                                                     │     │
│  │  [ Login to Start ]  [ Apply as Guest ]            │     │
│  └────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────┘

Multi-Step Application Form

┌─────────────────────────────────────────────────────────────┐
│  Step 1 of 4: Project Information                           │
│  ●───────○───────○───────○  [Save Draft]  [Continue →]     │
│                                                              │
│  Project Title *                                             │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ Ocean Plastic Recycling Platform                     │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Team Name                                                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ PlastiClean Solutions                                │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Competition Category *                                      │
│  ○ Startup (existing company)                                │
│  ● Business Concept (student/graduate project)               │
│                                                              │
│  Institution * (required for Business Concept)               │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ Stanford University                                  │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Ocean Issue Addressed *                                     │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 🔍 Pollution Reduction               [Dropdown ▼]   │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Description * (max 5000 characters)                         │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ PlastiClean is an AI-powered sorting system...      │   │
│  │                                                      │   │
│  │ 247 / 5000                                           │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Country *                                                   │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 🌍 United States                     [Dropdown ▼]   │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Founding Date                                               │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 2024-06-01                           [Date Picker]  │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ☑ I'm interested in mentorship from industry experts        │
│                                                              │
│  [ ← Back ]              [ Save Draft ]    [ Continue → ]   │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Step 2 of 4: Team Members                                   │
│  ○───────●───────○───────○  [Save Draft]  [Continue →]     │
│                                                              │
│  Team Members (1-5 members) *                                │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  👤 Sarah Johnson (You)                            │     │
│  │     sarah.johnson@stanford.edu                     │     │
│  │     Role: Lead | Title: CEO                        │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  👤 Mark Chen                        [Remove ✕]    │     │
│  │     Name: ┌──────────────────────┐                 │     │
│  │           │ Mark Chen            │                 │     │
│  │           └──────────────────────┘                 │     │
│  │     Email: ┌──────────────────────┐                │     │
│  │            │ mark@stanford.edu    │                │     │
│  │            └──────────────────────┘                │     │
│  │     Role: ● Member  ○ Advisor                      │     │
│  │     Title: ┌──────────────────────┐                │     │
│  │            │ CTO                  │                │     │
│  │            └──────────────────────┘                │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  [ + Add Team Member ]                                       │
│                                                              │
│    Team members will receive an email invite to view       │
│     your project status.                                     │
│                                                              │
│  [ ← Back ]              [ Save Draft ]    [ Continue → ]   │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Step 3 of 4: Document Upload                                │
│  ○───────○───────●───────○  [Save Draft]  [Continue →]     │
│                                                              │
│  Required Documents                                          │
│                                                              │
│  📄 Executive Summary (PDF, max 10 MB) *                     │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ✓ PlastiClean_Executive_Summary.pdf               │     │
│  │    Uploaded 2 hours ago | 2.3 MB                   │     │
│  │    [ View ] [ Replace ] [ Delete ]                 │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  📄 Business Plan (PDF, max 50 MB) *                         │
│  ┌────────────────────────────────────────────────────┐     │
│  │  📎 Drag & drop or click to upload                 │     │
│  │                                                     │     │
│  │  Accepted formats: PDF                              │     │
│  │  Max size: 50 MB                                    │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Optional Documents                                          │
│                                                              │
│  🎥 Video Pitch (MP4/MOV, max 500 MB)                        │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ⚡ PlastiClean_Pitch.mp4                          │     │
│  │    Uploading... 67% complete                        │     │
│  │    ▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░                             │     │
│  │    [ Cancel ]                                       │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ⚠️  Please upload all required documents before submitting. │
│                                                              │
│  [ ← Back ]              [ Save Draft ]    [ Continue → ]   │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  Step 4 of 4: Review & Submit                                │
│  ○───────○───────○───────●  [Save Draft]  [Submit]         │
│                                                              │
│  Review Your Application                                     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Project Information                  [Edit Step 1] │     │
│  │  ───────────────────────────────────────────────── │     │
│  │  Title: Ocean Plastic Recycling Platform           │     │
│  │  Team: PlastiClean Solutions                        │     │
│  │  Category: Business Concept                         │     │
│  │  Institution: Stanford University                   │     │
│  │  Ocean Issue: Pollution Reduction                   │     │
│  │  Country: United States                             │     │
│  │  Founded: June 2024                                 │     │
│  │  Mentorship: Yes                                    │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Team Members                         [Edit Step 2] │     │
│  │  ───────────────────────────────────────────────── │     │
│  │  • Sarah Johnson (Lead) - sarah.johnson@stanford.edu│     │
│  │  • Mark Chen (Member) - mark@stanford.edu           │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Documents                            [Edit Step 3] │     │
│  │  ───────────────────────────────────────────────── │     │
│  │  ✓ Executive Summary (2.3 MB)                       │     │
│  │  ✓ Business Plan (8.7 MB)                           │     │
│  │  ✓ Video Pitch (124 MB)                             │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ☑ I confirm that all information is accurate and that I    │
│     have the authority to submit this application.           │
│                                                              │
│  ☑ I agree to the MOPC Terms & Conditions                    │
│                                                              │
│  ⏱️  Deadline: March 1, 2026 at 11:59 PM UTC                │
│                                                              │
│  [ ← Back ]              [ Save Draft ]    [ Submit ✓ ]     │
└─────────────────────────────────────────────────────────────┘

Post-Submission Confirmation

┌─────────────────────────────────────────────────────────────┐
│  ✓ Application Submitted Successfully                        │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │                                                     │     │
│  │              🎉 Thank You!                          │     │
│  │                                                     │     │
│  │  Your application has been submitted successfully.  │     │
│  │                                                     │     │
│  │  Confirmation #: MOPC-2026-00123                    │     │
│  │  Submitted: Feb 15, 2026 at 3:42 PM UTC             │     │
│  │                                                     │     │
│  │  ✉️  A confirmation email has been sent to:         │     │
│  │     sarah.johnson@stanford.edu                      │     │
│  │                                                     │     │
│  │  What's Next?                                       │     │
│  │  ───────────────────────────────────────────        │     │
│  │  1. AI Screening (March 2-5)                        │     │
│  │  2. Jury Evaluation (March 10-31)                   │     │
│  │  3. Semi-finalist Notification (April 5)            │     │
│  │                                                     │     │
│  │  You'll receive email updates at each stage.        │     │
│  │                                                     │     │
│  │  [ View My Dashboard ]  [ Download Receipt ]        │     │
│  │                                                     │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Applicant Dashboard (After Submission)

┌─────────────────────────────────────────────────────────────┐
│  My Application                            sarah@stanford.edu│
│  ══════════════════════════════════════════════════════     │
│                                                              │
│  PlastiClean Solutions                                       │
│  Ocean Plastic Recycling Platform                            │
│  Status: Under Review                    [🟡 In Progress]    │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Application Progress                               │     │
│  │                                                     │     │
│  │  ✓ Submitted        Feb 15, 2026                    │     │
│  │  ───────────────────────────────────────────        │     │
│  │  ⏳ AI Screening     Expected: Mar 2-5              │     │
│  │  ───────────────────────────────────────────        │     │
│  │  ○ Jury Review      Expected: Mar 10-31             │     │
│  │  ───────────────────────────────────────────        │     │
│  │  ○ Decision         Expected: Apr 5                 │     │
│  │                                                     │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Deadline Countdown                                 │     │
│  │  13 days remaining until March 1, 2026              │     │
│  │  ▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░                               │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Quick Actions                                               │
│  [ 📄 View Application ] [ 📎 Documents ] [ 👥 Team ]        │
│                                                              │
│  Recent Activity                                             │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ✓ Application submitted        Feb 15, 3:42 PM     │     │
│  │  ✓ Video pitch uploaded          Feb 15, 3:38 PM     │     │
│  │  ✓ Business plan uploaded        Feb 15, 2:15 PM     │     │
│  │  ✓ Executive summary uploaded    Feb 15, 1:47 PM     │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

8. Admin Experience

Intake Round Configuration Wizard

┌─────────────────────────────────────────────────────────────┐
│  Configure Round: Application Window                         │
│  ══════════════════════════════════════════════════════     │
│                                                              │
│  Basic Settings                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Round Name *                                       │     │
│  │  ┌──────────────────────────────────────────────┐  │     │
│  │  │ Application Window                           │  │     │
│  │  └──────────────────────────────────────────────┘  │     │
│  │                                                     │     │
│  │  Submission Window *                                │     │
│  │  ┌──────────────────────────────────────────────┐  │     │
│  │  │ 🔍 Round 1 Docs          [Select Window ▼] │  │     │
│  │  └──────────────────────────────────────────────┘  │     │
│  │  [ + Create New Window ]                            │     │
│  │                                                     │     │
│  │  Open Date *                                        │     │
│  │  ┌──────────────────────────────────────────────┐  │     │
│  │  │ 2026-01-15  00:00 UTC      [Date Picker]    │  │     │
│  │  └──────────────────────────────────────────────┘  │     │
│  │                                                     │     │
│  │  Close Date *                                       │     │
│  │  ┌──────────────────────────────────────────────┐  │     │
│  │  │ 2026-03-01  23:59 UTC      [Date Picker]    │  │     │
│  │  └──────────────────────────────────────────────┘  │     │
│  └────────────────────────────────────────────────┘     │
│                                                              │
│  Deadline Policy                                             │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ○ Hard Deadline                                    │     │
│  │     Block submissions after close time               │     │
│  │                                                     │     │
│  │  ○ Flag Late Submissions                            │     │
│  │     Accept but mark as late                         │     │
│  │                                                     │     │
│  │  ● Grace Period                                     │     │
│  │     Accept for: ┌────┐ minutes after deadline      │     │
│  │                 │ 180│                              │     │
│  │                 └────┘                              │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Draft Settings                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ☑ Allow draft submissions (save & continue)        │     │
│  │                                                     │     │
│  │  Auto-delete drafts after: ┌────┐ days             │     │
│  │                             │ 30 │                  │     │
│  │                             └────┘                  │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Team Profile                                                │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ☑ Require team member information                  │     │
│  │                                                     │     │
│  │  Min team size: ┌───┐   Max team size: ┌───┐      │     │
│  │                 │ 1 │                    │ 5 │      │     │
│  │                 └───┘                    └───┘      │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Notifications                                               │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ☑ Send confirmation email on submission            │     │
│  │                                                     │     │
│  │  Send deadline reminders:                           │     │
│  │  ☑ 7 days before   ☑ 3 days before   ☑ 1 day before│     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Category Quotas (Optional)                                  │
│  ┌────────────────────────────────────────────────────┐     │
│  │  ☐ Enable category quotas                           │     │
│  │                                                     │     │
│  │  Max Startups:         ┌─────┐                      │     │
│  │                        │ 100 │                      │     │
│  │                        └─────┘                      │     │
│  │                                                     │     │
│  │  Max Business Concepts: ┌─────┐                     │     │
│  │                         │ 100 │                     │     │
│  │                         └─────┘                     │     │
│  │                                                     │     │
│  │  When quota full: ○ Reject  ● Add to waitlist      │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  [ Cancel ]                              [ Save Round ]      │
└─────────────────────────────────────────────────────────────┘

Submissions Dashboard

┌─────────────────────────────────────────────────────────────┐
│  Round: Application Window              Admin Dashboard      │
│  ══════════════════════════════════════════════════════     │
│                                                              │
│  Overview                                                    │
│  ┌────────────────────────────────────────────────────┐     │
│  │  123 Total Submissions                              │     │
│  │  ┌──────────┬──────────┬──────────┬──────────┐     │     │
│  │  │   68     │   55     │   12     │   11     │     │     │
│  │  │ Startups │ Concepts │  Drafts  │   Late   │     │     │
│  │  └──────────┴──────────┴──────────┴──────────┘     │     │
│  │                                                     │     │
│  │  Deadline: March 1, 2026 (13 days remaining)        │     │
│  │  ▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░                               │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Filters: [ All ] [ Startups ] [ Concepts ] [ Late ] [ Drafts ]│
│  Search: ┌─────────────────────────────────────┐  [Export ↓] │
│          │ 🔍 Search by project or team...    │              │
│          └─────────────────────────────────────┘              │
│                                                              │
│  Recent Submissions                                          │
│  ┌────────────────────────────────────────────────────┐     │
│  │  PlastiClean Solutions                              │     │
│  │  Ocean Plastic Recycling Platform                   │     │
│  │  Business Concept | United States | Feb 15, 3:42 PM │     │
│  │  [ View ] [ Edit ] [ Override Deadline ]            │     │
│  ├────────────────────────────────────────────────────┤     │
│  │  AquaTech Innovations                               │     │
│  │  Sustainable Aquaculture Monitoring                 │     │
│  │  Startup | Norway | Feb 15, 2:18 PM                 │     │
│  │  [ View ] [ Edit ] [ Override Deadline ]            │     │
│  ├────────────────────────────────────────────────────┤     │
│  │  OceanSense Labs                          🔴 LATE   │     │
│  │  AI-Powered Ocean Pollution Detection               │     │
│  │  Startup | Singapore | Mar 2, 1:15 AM (+3 hours)    │     │
│  │  [ View ] [ Edit ] [ Override Deadline ]            │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  [ Previous ]  Page 1 of 7  [ Next ]                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Override Deadline Modal

┌─────────────────────────────────────────────────────────────┐
│  Override Deadline: PlastiClean Solutions                    │
│  ══════════════════════════════════════════════════════     │
│                                                              │
│  Current Status: Submitted on time                           │
│  Original Deadline: March 1, 2026 11:59 PM UTC               │
│                                                              │
│  Extend submission window for this applicant:                │
│                                                              │
│  New Deadline                                                │
│  ┌──────────────────────────────────────────────┐            │
│  │ 2026-03-08  23:59 UTC      [Date Picker]    │            │
│  └──────────────────────────────────────────────┘            │
│                                                              │
│  Reason for Override *                                       │
│  ┌──────────────────────────────────────────────┐            │
│  │ Technical issue during original submission   │            │
│  │                                              │            │
│  └──────────────────────────────────────────────┘            │
│                                                              │
│  ⚠️  This will create a GracePeriod record and allow the     │
│     applicant to edit their submission until the new deadline│
│                                                              │
│  [ Cancel ]                        [ Grant Extension ]       │
└─────────────────────────────────────────────────────────────┘

9. Deadline Behavior

Deadline Policy Comparison

Policy Before Deadline After Deadline Grace Period Flagged
HARD Accept Block N/A N/A
FLAG Accept Accept N/A Yes
GRACE Accept Accept (for N min) Yes Yes (after grace)

HARD Policy Behavior

Configuration:

{
  deadlinePolicy: "HARD",
  gracePeriodMinutes: null  // Ignored
}

User Experience:

  • Before deadline: Form is fully functional, all uploads allowed
  • At deadline: Form locks immediately at windowCloseAt
  • After deadline: Form displays:
    ❌ Deadline Passed
    
    The application deadline was March 1, 2026 at 11:59 PM UTC.
    Submissions are no longer accepted.
    
    Contact admin@monaco-opc.com for assistance.
    

Admin Override:

  • Admin can create a GracePeriod record for specific applicant
  • This extends their personal deadline (doesn't affect global deadline)

FLAG Policy Behavior

Configuration:

{
  deadlinePolicy: "FLAG",
  gracePeriodMinutes: null  // Ignored
}

User Experience:

  • Before deadline: Normal submission
  • After deadline: Warning banner shown:
    ⚠️ Late Submission
    
    The deadline was March 1, 2026. Your submission will be marked as late.
    You can still submit, but late submissions may be deprioritized.
    
  • Submission still works, but ProjectFile.isLate set to true

Database Effect:

await prisma.projectFile.create({
  data: {
    projectId,
    submissionWindowId,
    requirementId,
    fileName,
    mimeType,
    size,
    bucket,
    objectKey,
    isLate: true,  // Flagged
    // ...
  }
})

GRACE Policy Behavior

Configuration:

{
  deadlinePolicy: "GRACE",
  gracePeriodMinutes: 180  // 3 hours
}

User Experience:

  • Before deadline: Normal submission
  • 0-3 hours after deadline: Warning banner:
    ⏱️ Grace Period Active
    
    Deadline: March 1, 2026 11:59 PM UTC (passed)
    Grace period ends: March 2, 2026 2:59 AM UTC (1 hour 23 minutes remaining)
    
    Your submission will be marked as late. Please submit as soon as possible.
    
  • After grace period: Hard block (same as HARD policy)

Grace Period Calculation:

const graceDeadline = new Date(windowCloseAt.getTime() + gracePeriodMinutes * 60 * 1000)

if (now > windowCloseAt && now <= graceDeadline) {
  // In grace period
  return {
    canSubmit: true,
    isLate: true,
    graceEndsAt: graceDeadline,
    remainingMinutes: Math.floor((graceDeadline - now) / 60000)
  }
}

10. Category Quotas

How Category Quotas Work

When categoryQuotasEnabled: true, the system tracks submissions per category and enforces limits.

Configuration:

{
  categoryQuotasEnabled: true,
  categoryQuotas: {
    STARTUP: 100,
    BUSINESS_CONCEPT: 100
  },
  quotaOverflowPolicy: "reject"  // or "waitlist"
}

Quota Enforcement Flow

async function canSubmitInCategory(
  competitionId: string,
  category: 'STARTUP' | 'BUSINESS_CONCEPT',
  config: IntakeConfig
): Promise<{ canSubmit: boolean; reason?: string }> {
  if (!config.categoryQuotasEnabled || !config.categoryQuotas) {
    return { canSubmit: true }
  }

  const quota = config.categoryQuotas[category]
  if (!quota) {
    return { canSubmit: true }
  }

  const submittedCount = await prisma.project.count({
    where: {
      competitionId,
      competitionCategory: category,
      isDraft: false,  // Don't count drafts
    }
  })

  if (submittedCount >= quota) {
    if (config.quotaOverflowPolicy === 'waitlist') {
      return {
        canSubmit: true,
        reason: `Quota full (${submittedCount}/${quota}). You're on the waitlist.`
      }
    } else {
      return {
        canSubmit: false,
        reason: `Quota full (${submittedCount}/${quota}). No more ${category} applications accepted.`
      }
    }
  }

  return { canSubmit: true }
}

Quota Dashboard for Admins

┌─────────────────────────────────────────────────────────────┐
│  Category Quota Status                                       │
│  ══════════════════════════════════════════════════════     │
│                                                              │
│  Startups                                                    │
│  ┌────────────────────────────────────────────────────┐     │
│  │  68 / 100 submissions                               │     │
│  │  ▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░  68%                          │     │
│  │                                                     │     │
│  │  32 slots remaining                                 │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Business Concepts                                           │
│  ┌────────────────────────────────────────────────────┐     │
│  │  55 / 100 submissions                               │     │
│  │  ▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░  55%                          │     │
│  │                                                     │     │
│  │  45 slots remaining                                 │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  Waitlist (if quota full)                                    │
│  ┌────────────────────────────────────────────────────┐     │
│  │  Startups: 0 on waitlist                            │     │
│  │  Concepts: 0 on waitlist                            │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Overflow Handling

Reject Policy:

  • Form shows error: "Category quota reached. No more startups/concepts accepted."
  • User cannot submit
  • Draft is saved but cannot be finalized

Waitlist Policy:

  • Submission accepted, but Project.status = WAITLISTED (new status)
  • User sees message: "You're on the waitlist (position #12). We'll notify you if a slot opens."
  • If someone withdraws, next waitlist entry promoted to SUBMITTED

11. Email Notifications

Receipt Confirmation Email

Trigger: autoConfirmReceipt: true + project submitted

Template:

Subject: Application Received — MOPC 2026

Dear Sarah Johnson,

Thank you for submitting your application to the Monaco Ocean Protection Challenge 2026.

Application Details:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Project: Ocean Plastic Recycling Platform
Team: PlastiClean Solutions
Category: Business Concept
Confirmation #: MOPC-2026-00123
Submitted: February 15, 2026 at 3:42 PM UTC

What's Next?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. AI Screening: March 2-5, 2026
   Your application will be automatically screened for eligibility.

2. Jury Evaluation: March 10-31, 2026
   Expert judges will review eligible projects.

3. Semi-finalist Notification: April 5, 2026
   Selected teams will be invited to the next round.

Track Your Progress:
View your application status anytime at:
https://monaco-opc.com/applicant/dashboard

Questions?
Contact us at admin@monaco-opc.com

Best regards,
MOPC Team

Deadline Reminder Emails

Trigger: Configured days before deadline (e.g., [7, 3, 1])

7-Day Reminder:

Subject: MOPC 2026 Application Deadline — 7 Days Remaining

Dear Applicant,

This is a friendly reminder that the MOPC 2026 application deadline is approaching.

Deadline: March 1, 2026 at 11:59 PM UTC
Time Remaining: 7 days

Have you started your application?
☐ Draft saved
☐ Documents uploaded
☐ Final submission

Complete your application:
https://monaco-opc.com/apply/mopc-2026

Need help? Contact admin@monaco-opc.com

Best regards,
MOPC Team

1-Day Reminder:

Subject: ⏰ MOPC 2026 Application Deadline — Tomorrow!

Dear Applicant,

The MOPC 2026 application deadline is tomorrow!

Deadline: March 1, 2026 at 11:59 PM UTC
Time Remaining: 23 hours 17 minutes

Don't miss out! Complete your application now:
https://monaco-opc.com/apply/mopc-2026

Best regards,
MOPC Team

12. API Changes (tRPC Procedures)

New/Modified Procedures

All procedures are in src/server/routers/ with these key changes:

applicant.getSubmissionBySlug — Get intake round info by slug (for public access) applicant.getMySubmission (enhanced) — Get current user's application (draft or submitted) applicant.saveDraft (new) — Auto-save form data as draft applicant.submitApplication (new) — Finalize draft and mark as submitted file.getUploadUrl (enhanced) — Get pre-signed URL for file upload file.confirmUpload (new) — Mark file upload as complete after successful S3 upload admin.getIntakeSubmissions — Admin dashboard for intake round admin.extendDeadline (new) — Create grace period for specific applicant


13. Service Functions

Key service functions in src/server/services/intake-round.ts:

  • canSubmitToIntakeRound() — Check if submission window is accepting
  • checkCategoryQuota() — Validate category quota
  • validateApplicationData() — Form validation
  • validateFileUpload() — File requirement validation
  • checkRequiredFiles() — Verify all required files uploaded

14. Edge Cases

Edge Case Behavior Solution
User starts draft, deadline passes Draft is preserved but cannot submit Show banner: "Deadline passed. Contact admin if you need extension." Admin can grant GracePeriod.
User submits at exact deadline second Accept if server time <= windowCloseAt Use database server time for consistency
Category quota reached mid-submission Check quota again on final submit Race condition: if quota hit between form start and submit, show error "Quota just filled"
File upload fails mid-submission ProjectFile record exists but no S3 object Cleanup orphaned records via cron job; allow re-upload
User replaces file after deadline Check deadline on upload, not just submit Each file upload checks canSubmitToWindow()
Team member email already registered Invite sent, user can claim Email contains link: "Join team or login to existing account"
Applicant deletes draft Hard delete or soft delete? Soft delete: set deletedAt field, hide from UI but keep for audit
Admin extends deadline globally Update Round.windowCloseAt All applicants benefit; no GracePeriod records needed
Duplicate submissions (same email) One email = one project per competition Upsert logic: update existing project instead of creating new
File version conflict User uploads same requirement twice Create new ProjectFile, link to old via replacedById
Draft expires while user editing Auto-save fails with "Draft expired" Extend expiry on each auto-save (rolling window)

15. Integration Points

Connects to Filtering Round (Next Round)

When intake round closes:

  1. Create ProjectRoundState records
  2. Trigger filtering job

File System (MinIO)

  • All uploads go to MinIO bucket: mopc-submissions
  • Object keys: projects/{projectId}/submissions/{submissionWindowId}/{filename}_{timestamp}.{ext}
  • Pre-signed URLs expire after 1 hour (uploads) or 24 hours (downloads)

Notification System

Events emitted:

  • INTAKE_SUBMISSION_RECEIVED — confirmation email + in-app notification
  • INTAKE_DEADLINE_APPROACHING — reminder emails (7d, 3d, 1d before)
  • INTAKE_LATE_SUBMISSION — flag for admin review
  • INTAKE_QUOTA_REACHED — notify admins

Audit Logging

All actions logged in DecisionAuditLog:

  • intake.draft_saved — auto-save triggered
  • intake.submission_finalized — final submit
  • intake.file_uploaded — file added
  • intake.file_replaced — file updated
  • intake.deadline_extended — admin override
  • intake.quota_reached — category quota hit

Document Complete

This specification defines the INTAKE round type for the redesigned MOPC architecture. Key takeaways:

  1. Typed Config: IntakeConfig replaces generic JSON with validated, documented fields
  2. SubmissionWindow: Decouples file requirements from round, enables multi-round submissions
  3. Deadline Policies: HARD, FLAG, GRACE with clear behavior differences
  4. Draft System: Auto-save + expiry for incomplete applications
  5. Category Quotas: Limit startups/concepts with overflow handling
  6. Email Automation: Confirmation + reminders built-in
  7. Admin Controls: Dashboard, deadline extensions, quota monitoring

Next documents:

  • 05-round-filtering.md — AI screening and eligibility
  • 06-round-evaluation.md — Jury review with multi-jury support
  • 07-round-submission.md — Additional docs from advancing teams