MOPC-App/docs/unified-architecture-redesign/06-mentoring-and-document-l...

10 KiB

Mentoring & Document Lifecycle

Overview

This document covers two interconnected systems: (1) the multi-round document lifecycle that governs how submissions flow through the competition, and (2) the mentoring workspace that provides a collaboration layer for finalist teams with assigned mentors.


Part 1: Document Lifecycle

SubmissionWindow

Each round that requires document collection has an associated SubmissionWindow. A competition can have multiple windows (e.g., Round 1 application docs, Round 2 semifinal docs).

model SubmissionWindow {
  id            String   @id @default(cuid())
  competitionId String
  roundId       String
  label         String   // "Round 1 Application Documents", "Round 2 Semifinal Documents"

  opensAt       DateTime
  closesAt      DateTime
  deadlinePolicy DeadlinePolicy @default(HARD)
  gracePeriodMinutes Int?  // only used with GRACE policy
  lockOnClose       Boolean @default(true)  // automatically lock submissions when window closes
  isLocked          Boolean @default(false) // manual lock toggle (admin can lock before close)

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

  competition  Competition @relation(...)
  round        Round @relation(...)
  requirements SubmissionFileRequirement[]

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

DeadlinePolicy

Policy Behavior
HARD Submissions are cut off at closesAt. No uploads after the deadline.
FLAG Submissions after closesAt are accepted but marked as Late. Admin can see late status.
GRACE A grace period (gracePeriodMinutes) after closesAt during which submissions are still accepted without penalty. After grace, behaves as HARD.
enum DeadlinePolicy {
  HARD
  FLAG
  GRACE
}

SubmissionFileRequirement

Each window defines required file slots:

model SubmissionFileRequirement {
  id                 String   @id @default(cuid())
  submissionWindowId String
  slotKey            String   // "executive_summary", "business_plan", "pitch_video"
  label              String   // "Executive Summary"
  description        String?  // guidance text for applicants
  required           Boolean  @default(true)
  maxFileSize        Int      @default(10485760)  // 10MB default
  acceptedTypes      String[] // ["application/pdf", "video/mp4"]
  sortOrder          Int      @default(0)

  submissionWindow SubmissionWindow @relation(...)

  @@unique([submissionWindowId, slotKey])
}

Multi-Round Document Visibility

Actor Current Round Docs Previous Round Docs
Applicant Can upload/edit until window closes Read-only (view/download only)
Judge Sees current round docs in review Sees previous round docs (clearly separated in UI)
Admin Full control (upload/remove/replace) Full control (upload/remove/replace)
Mentor Sees within mentor workspace Sees within mentor workspace

When a submission window closes or a round advances:

  • Applicant's editing permissions for that window's docs are revoked
  • Docs remain visible for download
  • Judges in subsequent rounds can view all prior docs

RoundSubmissionVisibility

Controls which prior round docs are visible to judges:

model RoundSubmissionVisibility {
  id            String   @id @default(cuid())
  roundId       String   // the round where the judge is reviewing
  visibleRoundId String  // the round whose docs are visible
  separateInUi  Boolean  @default(true) // show in separate section

  round        Round @relation("reviewingRound", ...)
  visibleRound Round @relation("visibleRound", ...)

  @@unique([roundId, visibleRoundId])
}

For example, Jury 2 (R5) would have visibility entries for R1 (Intake docs) and R4 (Semifinal docs), both with separateInUi: true.


Part 2: Mentoring Workspace

Purpose

Mentoring is NOT a judging stage. It's a collaboration layer for finalist teams who have requested mentoring. Mentors help teams polish their submissions before live finals.

Who Gets Mentoring

  • Only finalist teams (or whatever stage admin configures) that have "requested mentor" enabled
  • Mentor assignment works similarly to judge assignment (from a mentor pool)
  • Each mentored team gets one assigned mentor

Workspace Features

The mentor-team workspace provides three capabilities:

1. Messaging / Chat

model MentorMessage {
  id          String   @id @default(cuid())
  projectId   String   // the mentored project
  mentorId    String   // the assigned mentor
  senderId    String   // who sent the message
  senderRole  MentorMessageRole
  content     String   @db.Text
  createdAt   DateTime @default(now())

  project Project @relation(...)
  mentor  User    @relation("mentorMessages", ...)
  sender  User    @relation("sentMentorMessages", ...)

  @@index([projectId, mentorId])
}

enum MentorMessageRole {
  MENTOR
  APPLICANT
  ADMIN
}

Messaging allows real-time communication between mentor and team. Admins can also send messages into the workspace.

2. File Upload

model MentorFile {
  id          String   @id @default(cuid())
  projectId   String
  mentorId    String
  uploadedById String
  uploaderRole MentorMessageRole
  fileName    String
  fileKey     String   // MinIO storage key
  fileSize    Int
  mimeType    String
  description String?

  // Promotion tracking
  promotedToSlot String?   // slotKey if promoted to official submission
  promotedAt     DateTime?
  promotedById   String?

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

  project    Project @relation(...)
  comments   MentorFileComment[]

  @@index([projectId, mentorId])
}

Both mentor and team can upload files. Files are stored in MinIO and accessed via pre-signed URLs.

3. Threaded File Comments

model MentorFileComment {
  id          String   @id @default(cuid())
  fileId      String
  authorId    String
  authorRole  MentorMessageRole
  content     String   @db.Text
  parentId    String?  // for threading
  createdAt   DateTime @default(now())

  file    MentorFile @relation(...)
  author  User @relation(...)
  parent  MentorFileComment? @relation("commentThread", ...)
  replies MentorFileComment[] @relation("commentThread")

  @@index([fileId])
}

Comments are threaded (parentId for replies). Each comment is tied to a specific file.

Privacy

All mentoring workspace content is private by default:

  • Visible to: the assigned mentor, the team members, and admins
  • NOT visible to: other teams, other mentors, judges, audience

File Promotion to Official Submission

A mentoring file can be "promoted" to become an official submission for a required document slot in the active submission round.

Example flow:

  1. Team uploads "Business Plan Draft v3.pdf" to mentor workspace
  2. Mentor reviews and approves
  3. Team (or admin) marks it as the official "Business Plan" submission for Round 2
  4. System creates a SubmissionPromotionEvent with immutable provenance
model SubmissionPromotionEvent {
  id            String   @id @default(cuid())
  projectId     String
  roundId       String
  slotKey       String   // the doc slot being filled
  sourceType    PromotionSourceType
  sourceFileId  String   // MentorFile.id or admin-uploaded file ID
  promotedById  String   // who triggered the promotion
  createdAt     DateTime @default(now())

  project Project @relation(...)
  round   Round @relation(...)
  promotedBy User @relation(...)

  @@index([projectId, roundId])
}

enum PromotionSourceType {
  MENTOR_FILE        // promoted from mentor workspace
  ADMIN_REPLACEMENT  // admin replaced the file directly
}

Promotion authority:

  • Team lead: can promote their own mentor workspace files
  • Admin: can promote any file or directly replace with admin-uploaded file
  • Mentor: can promote IF admin enables this per competition (configurable)

Provenance: Every promotion records who promoted, when, from which source file, and the source type. This audit trail is immutable.

MentoringConfig (Round Type Configuration)

const MentoringConfig = z.object({
  eligibility: z.enum(['FINALISTS_ONLY', 'SEMIFINALISTS_AND_ABOVE', 'CONFIGURABLE']),
  requireMentorRequest: z.boolean().default(true), // team must opt-in
  assignmentMethod: z.enum(['MANUAL', 'AI_SUGGESTED', 'AI_AUTO', 'ALGORITHM']),
  workspaceFeatures: z.object({
    messagingEnabled: z.boolean().default(true),
    fileUploadEnabled: z.boolean().default(true),
    fileCommentsEnabled: z.boolean().default(true),
  }),
  promotionTarget: z.string().optional(), // roundId where promoted files go
  allowMentorPromotion: z.boolean().default(false), // can mentors promote files?
  deadlinePolicy: z.nativeEnum(DeadlinePolicy).default('FLAG'),
})

Mentor Dashboard

Mentors have a dedicated dashboard showing:

  • Assigned teams: list of all teams they're mentoring
  • Per-team workspace: click through to messaging, file exchange, comments
  • Deadline tracking: upcoming deadlines for the mentored teams
  • Progress indicators: which teams have submitted their required docs, which are pending
  • File review queue: files uploaded by teams that need mentor review

See 08-platform-integration-matrix.md for the full mentor page mapping.


Admin Document Controls

Admins have full control over documents across all rounds:

Action Scope Audit
View any submission All rounds, all projects Read-only, no audit needed
Upload file for a project Any round, any slot Logged with sourceType: ADMIN_REPLACEMENT
Remove/replace a file Any round, any slot Logged with previous file reference
Lock a submission window early Specific window Logged
Extend a deadline Specific window Logged
Promote a mentor file Any project's workspace Creates SubmissionPromotionEvent

Integration Points

  • R1 (Intake): First SubmissionWindow with R1 document requirements
  • R4 (Semifinal Submission): Second SubmissionWindow with R2 requirements; R1 docs locked for applicants
  • R5 (Jury 2): Judges see R1 + R2 docs clearly separated via RoundSubmissionVisibility
  • R6 (Mentoring): Mentor workspace active, file promotion targets the active submission window
  • R7 (Live Finals): Jury 3 sees all submitted docs from all rounds

See 03-competition-flow.md for the complete round-by-round specification.