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

316 lines
10 KiB
Markdown
Raw Normal View History

# 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).
```prisma
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. |
```prisma
enum DeadlinePolicy {
HARD
FLAG
GRACE
}
```
### SubmissionFileRequirement
Each window defines required file slots:
```prisma
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:
```prisma
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
```prisma
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
```prisma
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
```prisma
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
```prisma
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)
```typescript
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](./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](./03-competition-flow.md) for the complete round-by-round specification.