MOPC-App/docs/claude-architecture-redesign/22-integration-map.md

70 KiB
Raw Blame History

Integration Map: Cross-Reference Documentation

Overview

This document provides a comprehensive cross-reference of every connection between models, services, routers, and UI pages in the MOPC architecture redesign. It serves as the master integration reference for understanding how features connect across the system.

Purpose: This is the single source of truth for:

  • Which services operate on which models
  • Which routers call which services
  • Which UI pages call which routers
  • Which features activate per round type
  • How data flows through the system
  • What notifications are triggered by which events
  • Where audit trails are created
  • What permissions control which operations

1. Model → Service Map

Comprehensive mapping of every model to every service that operates on it.

Competition Model

Service Operations Purpose
competition.ts create, read, update, delete, list, clone Core CRUD operations
round-engine.ts getCompetitionRounds, getActiveRounds Round lifecycle management
competition-wizard.ts buildFromTemplate, validateConfig Wizard-based creation
notification-scheduler.ts scheduleDeadlineReminders Deadline countdown notifications
audit-service.ts logCompetitionCreated, logCompetitionStatusChange Audit trail

Round Model

Service Operations Purpose
round-engine.ts create, open, close, advance, rollback State machine control
round-config-validator.ts validateConfig, validateTransition Type-specific validation
assignment-orchestrator.ts getAssignableRounds, validateAssignmentRules Assignment coordination
filtering-service.ts runFilteringJob, processRound AI screening (FILTERING rounds)
live-control.ts initializeStageManager, advanceCursor Stage manager (LIVE_FINAL rounds)
mentor-workspace.ts activateWorkspace, closeWorkspace Workspace activation (MENTORING rounds)
winner-proposal.ts generateProposal, freezeResults Confirmation logic (CONFIRMATION rounds)
notification-service.ts notifyRoundOpen, notifyRoundClose, notifyDeadlineApproaching Round lifecycle notifications

JuryGroup Model

Service Operations Purpose
jury-group.ts create, addMember, removeMember, updateMemberConfig, delete Jury management
assignment-algorithm.ts getJuryGroupMembers, applyCapRules, applyQuotaRules Assignment algorithm input
assignment-orchestrator.ts generateAssignmentsForJuryGroup, validateAssignmentCaps Assignment coordination
jury-onboarding.ts notifyOnboardingRequired, collectPreferences Juror onboarding
notification-service.ts notifyJuryGroupAssigned, notifyJuryDeadline Jury-specific notifications

SubmissionWindow Model

Service Operations Purpose
submission-window.ts create, open, close, lock, validateSubmission Window lifecycle
file-upload-service.ts validateFileRequirement, checkDeadline File upload validation
deadline-service.ts applyGracePeriod, checkDeadlinePolicy Deadline enforcement
notification-service.ts notifyWindowOpen, notifyWindowClosing, notifyWindowClosed Window notifications

ProjectRoundState Model

Service Operations Purpose
round-engine.ts enterRound, exitRound, transitionState, bulkTransition Project state transitions
advancement-service.ts evaluateAdvancement, applyAdvancementRule Advancement logic
filtering-service.ts updateFilteringState Filtering results
evaluation-service.ts updateEvaluationState Evaluation completion

Assignment Model

Service Operations Purpose
assignment-algorithm.ts generateAssignments, applyConstraints AI/algorithm generation
assignment-orchestrator.ts create, override, delete, bulkDelete Assignment CRUD + orchestration
coi-service.ts excludeConflicts, validateCOI Conflict of interest enforcement
evaluation-service.ts getAssignedProjects, markComplete Evaluation workflow
notification-service.ts notifyJuryAssignment Assignment notifications

Evaluation Model

Service Operations Purpose
evaluation.ts create, update, submit, delete Evaluation CRUD
evaluation-summary.ts generateSummary AI summary generation
peer-review.ts getAnonymizedReviews, compareScores Peer review features
advancement-service.ts calculateScores, rankProjects Advancement input

MentorAssignment Model

Service Operations Purpose
mentor-assignment.ts create, update, delete, autoAssign Mentor assignment
mentor-workspace.ts activateWorkspace, uploadFile, addComment, promoteFile Workspace operations
ai-mentor-matching.ts suggestMatches, scoreExpertise AI matching
notification-service.ts notifyMentorAssigned, notifyWorkspaceActivated Mentor notifications

MentorFile Model

Service Operations Purpose
mentor-workspace.ts upload, download, delete, promoteToSubmission File operations
file-comment-service.ts addComment, replyToComment, deleteComment Threading + comments
minio-service.ts uploadToMinio, generatePresignedUrl MinIO integration

SpecialAward Model

Service Operations Purpose
special-award.ts create, update, delete, startVoting, closeVoting Award lifecycle
award-eligibility.ts calculateEligibility, applyAIFiltering, overrideEligibility Eligibility determination
award-voting.ts submitVote, tallyVotes, selectWinner Voting + results
notification-service.ts notifyAwardVotingOpen, notifyAwardWinner Award notifications

WinnerProposal Model

Service Operations Purpose
winner-proposal.ts create, requestApprovals, freeze, override Confirmation workflow
winner-approval.ts submitApproval, checkConsensus Approval tracking
audit-service.ts logProposal, logApproval, logOverride, logFreeze Audit trail
notification-service.ts notifyApprovalRequested, notifyProposalApproved Winner notifications

ProjectFile Model

Service Operations Purpose
file-upload-service.ts upload, replace, delete, validateRequirement File management
minio-service.ts uploadToMinio, deleteFromMinio, generatePresignedUrl Storage backend
submission-window.ts validateSubmission, checkRequirements Submission validation

FilteringRule & FilteringResult Models

Service Operations Purpose
filtering-service.ts applyRules, runAIFiltering, generateResults Filtering execution
duplicate-detection.ts detectDuplicates, scoreSimilarity Duplicate detection
audit-service.ts logFilteringDecision, logOverride Audit trail

LiveVotingSession & LiveVote Models

Service Operations Purpose
live-control.ts startSession, submitVote, endSession, tallyResults Live voting
audience-voting.ts registerAudience, submitAudienceVote Audience participation
websocket-service.ts broadcastVote, syncCursor Real-time sync

InAppNotification Model

Service Operations Purpose
notification-service.ts create, markRead, delete, listUnread In-app notifications
notification-scheduler.ts scheduleReminder, scheduleDeadline Scheduled notifications

DecisionAuditLog Model

Service Operations Purpose
audit-service.ts log (all decisions), query, export Comprehensive audit trail

2. Service → Router Map

Which tRPC routers call which services.

competition.ts Router

// Calls:
- competition.ts service          // CRUD operations
- round-engine.ts service          // getCompetitionRounds
- competition-wizard.ts service    // buildFromTemplate
- notification-scheduler.ts        // scheduleDeadlineReminders
- audit-service.ts                 // logCompetitionCreated, logStatusChange

// Procedures:
- create            competition.create + audit.log + scheduler.schedule
- getById           competition.getById
- list              competition.list
- update            competition.update + audit.log
- delete            competition.delete + audit.log
- getRounds         round-engine.getCompetitionRounds
- buildFromWizard   competition-wizard.buildFromTemplate + audit.log

round.ts Router

// Calls:
- round-engine.ts                  // All lifecycle operations
- round-config-validator.ts        // validateConfig
- notification-service.ts          // Round notifications
- audit-service.ts                 // All round changes

// Procedures:
- create            round-engine.create + validator.validate + audit.log
- getById           round-engine.getById
- list              round-engine.list
- update            round-engine.update + validator.validate + audit.log
- delete            round-engine.delete + audit.log
- open              round-engine.open + notification.notifyRoundOpen + audit.log
- close             round-engine.close + notification.notifyRoundClose + audit.log
- advance           round-engine.advance + audit.log
- rollback          round-engine.rollback + audit.log

jury-group.ts Router

// Calls:
- jury-group.ts service            // CRUD
- jury-onboarding.ts               // Onboarding
- assignment-orchestrator.ts       // Assignment coordination
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- create            jury-group.create + audit.log
- addMember         jury-group.addMember + onboarding.notify + audit.log
- removeMember      jury-group.removeMember + audit.log
- updateMemberConfig  jury-group.updateMemberConfig + audit.log
- getById           jury-group.getById
- list              jury-group.list
- getMembers        jury-group.getMembers

assignment.ts Router

// Calls:
- assignment-algorithm.ts          // AI/algorithm generation
- assignment-orchestrator.ts       // Orchestration
- coi-service.ts                   // COI enforcement
- notification-service.ts          // Assignment notifications
- audit-service.ts                 // Audit trail

// Procedures:
- generate          assignment-algorithm.generate + coi.excludeConflicts + orchestrator.create + notification.notify + audit.log
- override          orchestrator.override + audit.log
- delete            orchestrator.delete + audit.log
- bulkDelete        orchestrator.bulkDelete + audit.log
- getByRound        orchestrator.getByRound
- getByJuror        orchestrator.getByJuror

evaluation.ts Router

// Calls:
- evaluation.ts service            // CRUD
- evaluation-summary.ts            // AI summaries
- peer-review.ts                   // Peer review
- advancement-service.ts           // Advancement
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- getAssignment     evaluation.getAssignment + coi.check
- submit            evaluation.submit + advancement.check + notification.notifyComplete + audit.log
- update            evaluation.update + audit.log
- getSummary        evaluation-summary.generate
- getPeerReviews    peer-review.getAnonymized

filtering.ts Router

// Calls:
- filtering-service.ts             // Core filtering
- duplicate-detection.ts           // Duplicate detection
- ai-filtering.ts                  // AI screening
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- runJob            filtering-service.runFilteringJob + ai-filtering.screen + duplicate-detection.detect + notification.notify + audit.log
- getResults        filtering-service.getResults
- override          filtering-service.override + audit.log
- getDuplicates     duplicate-detection.getDuplicates

submission-window.ts Router

// Calls:
- submission-window.ts service     // Lifecycle
- file-upload-service.ts           // File handling
- deadline-service.ts              // Deadline enforcement
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- create            submission-window.create + audit.log
- open              submission-window.open + notification.notifyWindowOpen + audit.log
- close             submission-window.close + notification.notifyWindowClosed + audit.log
- lock              submission-window.lock + audit.log
- uploadFile        file-upload.upload + deadline.check + audit.log
- getFiles          submission-window.getFiles

mentor-workspace.ts Router

// Calls:
- mentor-workspace.ts service      // Workspace operations
- file-comment-service.ts          // Comments
- mentor-assignment.ts             // Assignment
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- activateWorkspace  mentor-workspace.activate + notification.notify + audit.log
- uploadFile        mentor-workspace.uploadFile + audit.log
- addComment        file-comment.addComment + notification.notify
- promoteFile       mentor-workspace.promoteToSubmission + audit.log + notification.notify
- getFiles          mentor-workspace.getFiles

special-award.ts Router

// Calls:
- special-award.ts service         // Award lifecycle
- award-eligibility.ts             // Eligibility
- award-voting.ts                  // Voting
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- create            special-award.create + audit.log
- updateEligibility  award-eligibility.calculate + audit.log
- startVoting       special-award.startVoting + notification.notifyVotingOpen + audit.log
- submitVote        award-voting.submitVote + audit.log
- closeVoting       special-award.closeVoting + award-voting.tallyVotes + notification.notifyWinner + audit.log

winner-proposal.ts Router

// Calls:
- winner-proposal.ts service       // Proposal lifecycle
- winner-approval.ts               // Approvals
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- create            winner-proposal.create + winner-approval.requestApprovals + notification.notifyApprovalRequested + audit.log
- submitApproval    winner-approval.submitApproval + winner-approval.checkConsensus + notification.notify + audit.log
- override          winner-proposal.override + audit.log
- freeze            winner-proposal.freeze + audit.log + notification.notifyFrozen

live-control.ts Router

// Calls:
- live-control.ts service          // Stage manager
- audience-voting.ts               // Audience
- websocket-service.ts             // Real-time sync
- notification-service.ts          // Notifications
- audit-service.ts                 // Audit

// Procedures:
- initStageManager  live-control.initialize + audit.log
- advanceCursor     live-control.advanceCursor + websocket.broadcast + audit.log
- startVoting       live-control.startVoting + notification.notify + audit.log
- submitJuryVote    live-control.submitVote + websocket.broadcast + audit.log
- submitAudienceVote  audience-voting.submit + websocket.broadcast
- endVoting         live-control.endVoting + live-control.tallyResults + audit.log

3. Router → UI Page Map

Which UI pages call which tRPC routers.

Admin UI Pages

/admin/competition/[id] (Competition Detail)

// Calls:
trpc.competition.getById           // Load competition
trpc.competition.update            // Update settings
trpc.competition.delete            // Delete competition
trpc.round.list                    // List rounds
trpc.juryGroup.list                // List juries
trpc.submissionWindow.list         // List submission windows
trpc.specialAward.list             // List awards

/admin/competition/[id]/wizard (Competition Wizard)

// Calls:
trpc.competition.buildFromWizard   // Create from template
trpc.round.create                  // Create rounds
trpc.juryGroup.create              // Create juries
trpc.submissionWindow.create       // Create windows

/admin/competition/[id]/round/[roundId] (Round Detail)

// Calls:
trpc.round.getById                 // Load round
trpc.round.update                  // Update config
trpc.round.open                    // Open round
trpc.round.close                   // Close round
trpc.assignment.getByRound         // View assignments
trpc.filtering.getResults          // View filtering results (FILTERING)
trpc.evaluation.list               // View evaluations (EVALUATION)
trpc.liveControl.initStageManager  // Stage manager (LIVE_FINAL)
trpc.winnerProposal.getByRound     // View proposals (CONFIRMATION)

/admin/jury-group/[id] (Jury Group Management)

// Calls:
trpc.juryGroup.getById             // Load jury
trpc.juryGroup.addMember           // Add juror
trpc.juryGroup.removeMember        // Remove juror
trpc.juryGroup.updateMemberConfig  // Update caps/quotas
trpc.assignment.generate           // Generate assignments

/admin/assignment/[roundId] (Assignment Manager)

// Calls:
trpc.assignment.generate           // AI/algorithm generation
trpc.assignment.getByRound         // View assignments
trpc.assignment.override           // Override assignment
trpc.assignment.delete             // Remove assignment
trpc.assignment.bulkDelete         // Bulk remove

/admin/filtering/[roundId] (Filtering Dashboard)

// Calls:
trpc.filtering.runJob              // Start filtering
trpc.filtering.getResults          // View results
trpc.filtering.override            // Override AI decision
trpc.filtering.getDuplicates       // View duplicates

/admin/live-control/[roundId] (Stage Manager)

// Calls:
trpc.liveControl.initStageManager  // Initialize
trpc.liveControl.advanceCursor     // Advance project
trpc.liveControl.startVoting       // Start vote
trpc.liveControl.endVoting         // End vote
// + WebSocket subscriptions for real-time updates

/admin/winner-confirmation/[competitionId] (Winner Confirmation)

// Calls:
trpc.winnerProposal.getByCompetition  // Load proposals
trpc.winnerProposal.create            // Create proposal
trpc.winnerProposal.override          // Override result
trpc.winnerProposal.freeze            // Freeze result
trpc.winnerApproval.list              // View approvals

/admin/award/[awardId] (Special Award Manager)

// Calls:
trpc.specialAward.getById          // Load award
trpc.specialAward.updateEligibility // Update eligible projects
trpc.specialAward.startVoting      // Open voting
trpc.specialAward.getVotes         // View votes
trpc.specialAward.closeVoting      // Close + tally

Jury UI Pages

/jury/dashboard (Jury Dashboard)

// Calls:
trpc.juryGroup.getMyGroups         // Which juries am I in
trpc.assignment.getByJuror         // My assignments
trpc.round.getActive               // Active rounds
trpc.evaluation.getMyEvaluations   // My evaluations

/jury/evaluate/[projectId] (Evaluation Form)

// Calls:
trpc.evaluation.getAssignment      // Load assignment
trpc.project.getById               // Load project details
trpc.projectFile.list              // Load files (multi-round)
trpc.evaluation.submit             // Submit evaluation
trpc.coi.declare                   // Declare COI
trpc.peerReview.getAnonymized      // View peer reviews

/jury/live-finals (Live Voting Interface)

// Calls:
trpc.liveControl.getSession        // Current session
trpc.liveVote.submit               // Submit vote
// + WebSocket subscriptions for cursor, votes

/jury/confirm-winners/[proposalId] (Winner Approval)

// Calls:
trpc.winnerProposal.getById        // Load proposal
trpc.winnerApproval.submit         // Approve/reject

Applicant UI Pages

/applicant/dashboard (Applicant Dashboard)

// Calls:
trpc.project.getMyProjects         // My projects
trpc.submissionWindow.getActive    // Active windows
trpc.projectRoundState.getByProject // Round statuses
trpc.mentorAssignment.getByProject  // Mentor info

/applicant/submit/[windowId] (Submission Form)

// Calls:
trpc.submissionWindow.getById      // Window details
trpc.fileRequirement.list          // Required files
trpc.projectFile.upload            // Upload file
trpc.project.submitApplication     // Submit

/applicant/project/[projectId] (Project Detail)

// Calls:
trpc.project.getById               // Project details
trpc.projectFile.list              // All files (multi-round)
trpc.projectRoundState.list        // Round history
trpc.evaluation.getSummary         // Feedback (if released)

/applicant/mentoring/[projectId] (Mentoring Workspace)

// Calls:
trpc.mentorWorkspace.getFiles      // Workspace files
trpc.mentorWorkspace.uploadFile    // Upload file
trpc.mentorWorkspace.addComment    // Comment on file
trpc.mentorMessage.list            // Messages
trpc.mentorMessage.send            // Send message

Mentor UI Pages

/mentor/dashboard (Mentor Dashboard)

// Calls:
trpc.mentorAssignment.getMyAssignments  // My teams
trpc.mentorWorkspace.getActiveWorkspaces // Active workspaces

/mentor/workspace/[assignmentId] (Mentor Workspace)

// Calls:
trpc.mentorWorkspace.getFiles      // Workspace files
trpc.mentorWorkspace.uploadFile    // Upload file
trpc.mentorWorkspace.addComment    // Comment on file
trpc.mentorWorkspace.promoteFile   // Promote to submission
trpc.mentorMessage.list            // Messages
trpc.mentorMessage.send            // Send message

4. Round Type → Feature Matrix

Which features are active for each round type.

Feature INTAKE FILTERING EVALUATION SUBMISSION MENTORING LIVE_FINAL CONFIRMATION
Submission Window
File Requirements
Deadline Policy
Draft Saving
Applicant Forms
Jury Group ✓ (approval)
Assignment Algorithm
Assignment Caps
Category Quotas
COI Declaration
Evaluation Form
Scoring (Criteria) ✓ (optional)
Scoring (Global)
Binary Decision ✓ (AI) ✓ (optional)
Feedback
Peer Review
AI Summary
Advancement Rules
Top-N Selection
AI Filtering
Duplicate Detection
Filtering Rules
Admin Override
Mentor Assignment
Mentor Workspace
Workspace Files
File Comments
File Promotion
Mentor Messages
Stage Manager
Live Cursor
Jury Voting
Audience Voting ✓ (optional)
Deliberation Period ✓ (optional)
WebSocket Sync
Winner Proposal
Jury Approval
Admin Freeze
Override Mode
Multi-Window Visibility
Grace Period ✓ (optional) ✓ (optional)
Late Flag

5. Data Flow Diagrams

5.1 Application Submission Flow (INTAKE)

Applicant
    |
    | 1. Visit /applicant/submit/[windowId]
    |
    v
UI: Check active SubmissionWindow
    | trpc.submissionWindow.getActive
    v
Backend: submission-window.ts
    | Query: SubmissionWindow where windowOpenAt <= now <= windowCloseAt
    v
UI: Load FileRequirements
    | trpc.fileRequirement.list
    v
Backend: submission-window.ts
    | Query: SubmissionFileRequirement where submissionWindowId
    v
UI: Upload files
    | trpc.projectFile.upload (per requirement)
    v
Backend: file-upload-service.ts
    |
    | 1. Validate requirement (mime type, size)
    | 2. Check deadline (deadline-service.ts)
    |    - Apply grace period if configured
    |    - Set isLate flag if past deadline
    | 3. Upload to MinIO (minio-service.ts)
    | 4. Create ProjectFile record
    |    - Links to submissionWindowId, requirementId
    | 5. Audit log (audit-service.ts)
    v
Database: ProjectFile record created
    |
    v
UI: Submit application
    | trpc.project.submit
    v
Backend: project.ts
    |
    | 1. Validate all required files uploaded
    | 2. Create ProjectRoundState (roundId = INTAKE round, state = IN_PROGRESS)
    | 3. Notification (notification-service.ts)
    |    - Send confirmation email to applicant
    | 4. Audit log
    v
Database: ProjectRoundState created
    |
    v
Applicant receives confirmation notification

5.2 Filtering Flow (FILTERING)

Admin
    |
    | 1. Visit /admin/filtering/[roundId]
    |
    v
UI: Start filtering job
    | trpc.filtering.runJob
    v
Backend: filtering-service.ts
    |
    | 1. Create FilteringJob record (status: RUNNING)
    | 2. Get all projects in INTAKE round with state = IN_PROGRESS
    | 3. Run rule-based filtering
    |    - Apply FilteringRules (field checks)
    |    - Create FilteringResult records
    | 4. Run AI screening (if enabled)
    |    - Call ai-filtering.ts
    |    - Anonymize project data (anonymization.ts)
    |    - Send to OpenAI with criteria
    |    - Store AI recommendation in FilteringResult
    | 5. Run duplicate detection
    |    - Call duplicate-detection.ts
    |    - Score similarity between projects
    |    - Flag potential duplicates
    | 6. Update FilteringJob (status: COMPLETED)
    | 7. Notification
    |    - Notify admin job complete
    v
Database: FilteringResult records created
    |
    v
UI: View results
    | trpc.filtering.getResults
    v
Backend: filtering-service.ts
    | Query: FilteringResult where roundId, group by recommendation
    v
UI: Admin reviews flagged projects
    |
    | Admin can override AI decision
    | trpc.filtering.override
    v
Backend: filtering-service.ts
    |
    | 1. Update FilteringResult (adminOverride = true, finalDecision = PASS/REJECT)
    | 2. Audit log (reason, timestamp, admin)
    v
Database: FilteringResult updated
    |
    v
UI: Advance projects
    | trpc.round.advance (FILTERING -> next round)
    v
Backend: round-engine.ts
    |
    | 1. Get FilteringResults with finalDecision = PASS
    | 2. For each project:
    |    - Create ProjectRoundState (nextRoundId, state = PENDING)
    |    - Update previous ProjectRoundState (state = COMPLETED, exitedAt)
    | 3. Notification
    |    - Notify advancing teams
    |    - Notify rejected teams
    | 4. Audit log
    v
Database: ProjectRoundState records created/updated

5.3 Assignment Flow (EVALUATION)

Admin
    |
    | 1. Visit /admin/assignment/[roundId]
    |
    v
UI: Generate assignments
    | trpc.assignment.generate
    v
Backend: assignment-orchestrator.ts
    |
    | 1. Get JuryGroup for this round
    | 2. Get JuryGroupMembers
    | 3. Get projects in this round (ProjectRoundState where roundId, state = PENDING/IN_PROGRESS)
    | 4. Call assignment-algorithm.ts
    |    |
    |    | Algorithm:
    |    | - Load COI data (coi-service.ts) — exclude conflicts
    |    | - Load juror caps (JuryGroupMember.maxAssignmentsOverride || JuryGroup.defaultMaxAssignments)
    |    | - Load category quotas (JuryGroupMember.categoryQuotasOverride || JuryGroup.defaultCategoryQuotas)
    |    | - Load juror preferences (preferredStartupRatio)
    |    | - Calculate expertise scores (AI-based)
    |    | - Apply geo-diversity penalty (same country/city reduces score)
    |    | - Apply familiarity bonus (prior evaluations increase score)
    |    | - Run matching algorithm:
    |    |   - Greedy assignment: highest score first
    |    |   - Respect hard caps (CapMode.HARD)
    |    |   - Allow soft cap buffer (CapMode.SOFT + softCapBuffer)
    |    |   - Enforce category quotas (min/max per category)
    |    | - Return Assignment[] with aiConfidenceScore, expertiseMatchScore, aiReasoning
    |    v
    | 5. Create Assignment records
    | 6. Notification (notification-service.ts)
    |    - Notify each juror of assignments
    | 7. Audit log
    v
Database: Assignment records created
    |
    v
UI: View assignments
    | trpc.assignment.getByRound
    v
Backend: assignment-orchestrator.ts
    | Query: Assignment where roundId, include juror, project
    v
UI: Admin can override
    |
    | Admin manually assigns/removes
    | trpc.assignment.override
    v
Backend: assignment-orchestrator.ts
    |
    | 1. Create/delete Assignment record
    | 2. Validate cap constraints (raise warning if exceeds cap)
    | 3. Audit log (reason, timestamp, admin)
    v
Database: Assignment updated

5.4 Evaluation Flow (EVALUATION)

Jury Member
    |
    | 1. Visit /jury/dashboard
    |
    v
UI: Load assignments
    | trpc.assignment.getByJuror
    v
Backend: assignment-orchestrator.ts
    | Query: Assignment where userId, include project, round
    v
UI: Click project to evaluate
    | Navigate to /jury/evaluate/[projectId]
    v
UI: Check COI requirement
    | trpc.coi.check
    v
Backend: coi-service.ts
    | Query: ConflictOfInterest where assignmentId
    v
UI: Show COI declaration dialog (if not declared)
    | trpc.coi.declare
    v
Backend: coi-service.ts
    |
    | 1. Create ConflictOfInterest record (hasConflict: true/false)
    | 2. If hasConflict = true:
    |    - Update Assignment (isCompleted = true, exclude from evaluation)
    |    - Notification (notify admin of COI)
    | 3. Audit log
    v
Database: COI declared
    |
    v
UI: Load evaluation form
    | trpc.evaluation.getAssignment
    v
Backend: evaluation.ts
    | Query: Assignment with Evaluation, Project, ProjectFiles (multi-window visibility)
    |
    | Multi-window visibility:
    | - Get RoundSubmissionVisibility where roundId
    | - For each visible SubmissionWindow:
    |   - Load ProjectFiles where submissionWindowId, projectId
    | - Return files grouped by window (Round 1 Docs, Round 2 Docs)
    v
UI: Show evaluation form
    | Display files from multiple rounds
    | Display scoring criteria (from EvaluationForm)
    v
Jury fills form:
    | - Score each criterion (1-10)
    | - Global score (1-10) or binary decision (pass/fail)
    | - Feedback text
    v
UI: Submit evaluation
    | trpc.evaluation.submit
    v
Backend: evaluation.ts
    |
    | 1. Create/update Evaluation record
    |    - criterionScoresJson: { "criterion-1": 8, "criterion-2": 7, ... }
    |    - globalScore or binaryDecision
    |    - feedbackText
    |    - submittedAt: now
    | 2. Update Assignment (isCompleted = true)
    | 3. Update ProjectRoundState (if all required reviews complete)
    |    - Check requiredReviewsPerProject from round config
    |    - If complete: state = COMPLETED
    | 4. Generate AI summary (if enabled)
    |    - Call evaluation-summary.ts
    |    - Create EvaluationSummary record
    | 5. Notification
    |    - Notify admin if all evaluations complete for project
    | 6. Audit log
    v
Database: Evaluation, EvaluationSummary created
    |
    v
UI: Confirmation shown to jury

5.5 Submission Flow (SUBMISSION - Second Round)

Applicant
    |
    | (After advancing from EVALUATION round)
    |
    v
Notification: "You've advanced! Please submit additional documents."
    | (notification-service.ts)
    v
Applicant
    | 1. Visit /applicant/dashboard
    |
    v
UI: Check active SubmissionWindow
    | trpc.submissionWindow.getActive
    v
Backend: submission-window.ts
    | Query: SubmissionWindow where roundNumber = 2, windowOpenAt <= now <= windowCloseAt
    v
UI: Navigate to /applicant/submit/[window2Id]
    |
    v
UI: Load FileRequirements for Window 2
    | trpc.fileRequirement.list
    v
Backend: submission-window.ts
    | Query: SubmissionFileRequirement where submissionWindowId = window2Id
    | Example: ["Updated Business Plan", "Video Pitch", "Financial Model"]
    v
UI: Check if Window 1 is locked
    | trpc.submissionWindow.getById(window1Id)
    v
Backend: submission-window.ts
    | Query: SubmissionWindow where id = window1Id
    | If lockOnClose = true and windowCloseAt < now:
    |   - Return locked = true
    v
UI: Show status
    | Window 1: "Locked (read-only)" — files visible but not editable
    | Window 2: "Open" — files uploadable
    v
Applicant uploads files (same flow as 5.1)
    | trpc.projectFile.upload
    v
Backend: file-upload-service.ts
    | Same validation, MinIO upload, ProjectFile creation
    | Links to submissionWindowId = window2Id
    v
Database: ProjectFile records created for Window 2
    |
    v
UI: Submit
    | trpc.project.submit
    v
Backend: project.ts
    |
    | 1. Validate all Window 2 requirements met
    | 2. Update ProjectRoundState (roundId = SUBMISSION round, state = IN_PROGRESS)
    | 3. Notification
    | 4. Audit log
    v
Database: ProjectRoundState updated

5.6 Mentoring Flow (MENTORING)

Admin
    |
    | 1. Visit /admin/round/[mentoringRoundId]
    |
    v
UI: Open round
    | trpc.round.open
    v
Backend: round-engine.ts
    |
    | 1. Update Round (status = ROUND_ACTIVE, windowOpenAt = now)
    | 2. Call mentor-workspace.ts
    |    |
    |    | For each MentorAssignment where project advanced to this round:
    |    | - Update workspaceEnabled = true
    |    | - Set workspaceOpenAt = now
    |    | - Set workspaceCloseAt = round.windowCloseAt
    |    v
    | 3. Notification
    |    - Notify mentors: "Workspace activated"
    |    - Notify teams: "Mentoring period open"
    | 4. Audit log
    v
Database: MentorAssignment.workspaceEnabled = true
    |
    v
Mentor
    | Visit /mentor/workspace/[assignmentId]
    |
    v
UI: Load workspace
    | trpc.mentorWorkspace.getFiles
    v
Backend: mentor-workspace.ts
    | Query: MentorFile where mentorAssignmentId
    v
UI: Upload file
    | trpc.mentorWorkspace.uploadFile
    v
Backend: mentor-workspace.ts
    |
    | 1. Validate file (size, type)
    | 2. Upload to MinIO (minio-service.ts)
    | 3. Create MentorFile record
    | 4. Notification (notify team member)
    | 5. Audit log
    v
Database: MentorFile created
    |
    v
Team Member
    | Receives notification
    | Visits workspace
    |
    v
UI: Add comment to file
    | trpc.mentorWorkspace.addComment
    v
Backend: file-comment-service.ts
    |
    | 1. Create MentorFileComment record
    | 2. Notification (notify mentor)
    v
Database: MentorFileComment created
    |
    v
Mentor
    | Decides file is ready for official submission
    |
    v
UI: Promote file
    | trpc.mentorWorkspace.promoteFile
    v
Backend: mentor-workspace.ts
    |
    | 1. Get target SubmissionWindow (from round config)
    | 2. Create ProjectFile record:
    |    - submissionWindowId = target window
    |    - Copy file from MentorFile bucket/objectKey
    | 3. Update MentorFile:
    |    - isPromoted = true
    |    - promotedToFileId = new ProjectFile.id
    |    - promotedAt = now
    |    - promotedByUserId = mentor.id
    | 4. Notification (notify team + admin)
    | 5. Audit log (file promoted, by whom, when)
    v
Database: ProjectFile created, MentorFile updated

5.7 Live Finals Flow (LIVE_FINAL)

Admin
    |
    | 1. Visit /admin/live-control/[roundId]
    |
    v
UI: Initialize stage manager
    | trpc.liveControl.initStageManager
    v
Backend: live-control.ts
    |
    | 1. Create LiveVotingSession (roundId, status = ACTIVE)
    | 2. Create LiveProgressCursor (roundId, currentProjectId = first project)
    | 3. Get projects in this round (ProjectRoundState where roundId, ordered by category + score)
    | 4. Audit log
    v
Database: LiveVotingSession, LiveProgressCursor created
    |
    v
UI: Stage Manager Dashboard
    | - Shows current project
    | - Controls: Next, Start Vote, End Vote
    |
    v
Admin: Advance to next project
    | trpc.liveControl.advanceCursor
    v
Backend: live-control.ts
    |
    | 1. Update LiveProgressCursor (currentProjectId = next project)
    | 2. WebSocket broadcast (websocket-service.ts)
    |    - Send to all connected clients: { event: "cursor_advanced", projectId }
    | 3. Audit log
    v
WebSocket: All clients receive update
    | - Jury UI updates to show new project
    | - Audience UI updates
    v
Admin: Start voting for current project
    | trpc.liveControl.startVoting
    v
Backend: live-control.ts
    |
    | 1. Update LiveVotingSession (votingOpen = true, currentVotingProjectId)
    | 2. WebSocket broadcast: { event: "voting_started", projectId }
    | 3. Notification (notify jury)
    | 4. Audit log
    v
WebSocket: Jury UI enables voting form
    |
    v
Jury Member
    | Visit /jury/live-finals
    | Sees voting form enabled
    |
    v
UI: Submit vote
    | trpc.liveVote.submit
    v
Backend: live-control.ts
    |
    | 1. Create LiveVote record (sessionId, userId, projectId, score, criteria scores)
    | 2. WebSocket broadcast: { event: "vote_received", voterId (anonymized) }
    | 3. Audit log
    v
Database: LiveVote created
    |
    v
WebSocket: Stage manager shows vote count incrementing
    |
    v
Audience Member (if enabled)
    | Visit /audience/live-finals
    | Register (audienceRequireIdentification)
    |
    v
UI: Submit audience vote
    | trpc.audienceVote.submit
    v
Backend: audience-voting.ts
    |
    | 1. Create AudienceVoter record (if first vote)
    | 2. Create LiveVote record (sessionId, voterId = audienceVoterId, projectId, score)
    | 3. WebSocket broadcast
    v
Database: LiveVote created
    |
    v
Admin: End voting
    | trpc.liveControl.endVoting
    v
Backend: live-control.ts
    |
    | 1. Update LiveVotingSession (votingOpen = false)
    | 2. Tally results:
    |    - Calculate jury vote average
    |    - Calculate audience vote average (if enabled)
    |    - Weight: (juryAvg * (1 - audienceVoteWeight)) + (audienceAvg * audienceVoteWeight)
    | 3. Update project score (or create intermediate result)
    | 4. WebSocket broadcast: { event: "voting_ended", results (if revealPolicy = immediate) }
    | 5. Audit log
    v
Database: Scores calculated
    |
    v
WebSocket: UI shows results (if revealPolicy allows)
    |
    v
Admin: Deliberation period (if enabled)
    | - Jury can discuss
    | - Stage manager shows countdown
    |
    v
Admin: Finalize session
    | trpc.liveControl.endSession
    v
Backend: live-control.ts
    |
    | 1. Update LiveVotingSession (status = COMPLETED, endedAt = now)
    | 2. Update ProjectRoundStates (state = COMPLETED)
    | 3. Notification (notify all participants)
    | 4. Audit log
    v
Database: Session finalized

5.8 Confirmation Flow (CONFIRMATION)

Admin
    |
    | 1. Visit /admin/winner-confirmation/[competitionId]
    |
    v
UI: Generate proposal
    | trpc.winnerProposal.create
    v
Backend: winner-proposal.ts
    |
    | 1. Get source round (LIVE_FINAL round)
    | 2. Get projects with scores (ordered by final score)
    | 3. Apply advancement rule (top N per category)
    | 4. Create WinnerProposal record:
    |    - category (STARTUP or BUSINESS_CONCEPT)
    |    - rankedProjectIds: ["proj-1st", "proj-2nd", "proj-3rd"]
    |    - sourceRoundId
    |    - selectionBasis: { method: "live_final_scores", scores: {...}, reasoning }
    |    - status = PENDING
    | 5. Get approval group (from round config: juryGroupId)
    | 6. Create WinnerApproval records:
    |    - For each jury member: create WinnerApproval (role: JURY_MEMBER, approved = null)
    |    - For admins: create WinnerApproval (role: ADMIN, approved = null)
    | 7. Notification (notification-service.ts)
    |    - Notify each jury member: "Please review and approve winners"
    |    - Notify admins
    | 8. Audit log
    v
Database: WinnerProposal, WinnerApproval[] created
    |
    v
Jury Member
    | Receives notification
    | Visit /jury/confirm-winners/[proposalId]
    |
    v
UI: Load proposal
    | trpc.winnerProposal.getById
    v
Backend: winner-proposal.ts
    | Query: WinnerProposal with ranked projects
    v
UI: Review ranked projects
    | Show 1st, 2nd, 3rd place with scores, reasoning
    |
    v
UI: Submit approval
    | trpc.winnerApproval.submit
    v
Backend: winner-approval.ts
    |
    | 1. Update WinnerApproval (approved = true/false, comments, respondedAt = now)
    | 2. Check consensus:
    |    - If requireAllJuryApproval = true:
    |      - Check all JURY_MEMBER approvals
    |      - If all approved: update WinnerProposal.status = APPROVED
    |      - If any rejected: update WinnerProposal.status = REJECTED
    | 3. Notification
    |    - If all approved: notify admin "Proposal approved"
    |    - If rejected: notify admin "Proposal rejected"
    | 4. Audit log
    v
Database: WinnerApproval updated, possibly WinnerProposal.status updated
    |
    v
Admin (if status = APPROVED)
    | Visit /admin/winner-confirmation/[competitionId]
    |
    v
UI: Freeze results
    | trpc.winnerProposal.freeze
    v
Backend: winner-proposal.ts
    |
    | 1. Update WinnerProposal:
    |    - status = FROZEN
    |    - frozenAt = now
    |    - frozenById = admin.id
    | 2. Update Competition:
    |    - status = CLOSED
    | 3. Update ProjectRoundStates:
    |    - Winners: state = COMPLETED
    | 4. Notification
    |    - Notify winners: "Congratulations"
    |    - Notify all participants: "Results are final"
    | 5. Audit log (critical: results frozen, by whom, when)
    v
Database: Results frozen, competition closed
    |
    v
Public results page shows final rankings

Admin Override Flow (Confirmation)

Admin (if proposal status = REJECTED or needs override)
    |
    | Visit /admin/winner-confirmation/[competitionId]
    |
    v
UI: Override proposal
    | trpc.winnerProposal.override
    v
Backend: winner-proposal.ts
    |
    | Parameters:
    | - overrideMode: "FORCE_MAJORITY" | "ADMIN_DECISION"
    | - overrideReason: "Two jury members unavailable, forcing majority approval"
    | - overrideById: admin.id
    |
    v
Backend logic:
    |
    | If overrideMode = "FORCE_MAJORITY":
    |   - Calculate current approval count
    |   - If >= 50%: update status = APPROVED
    |   - Else: raise error "Majority not reached"
    |
    | If overrideMode = "ADMIN_DECISION":
    |   - Bypass all approvals
    |   - Update status = OVERRIDDEN
    |
    | Update WinnerProposal:
    |   - overrideUsed = true
    |   - overrideMode
    |   - overrideReason
    |   - overrideById
    |   - status = APPROVED or OVERRIDDEN
    |
    | Audit log (CRITICAL):
    |   - Event: WINNER_OVERRIDE
    |   - Reason
    |   - Mode
    |   - Timestamp
    |   - Admin ID
    |
    | Notification:
    |   - Notify all jury members: "Admin has overridden approval process"
    v
Database: WinnerProposal overridden
    |
    v
Admin can now freeze results (same as above)

6. Notification Trigger Map

Complete matrix of events → recipients → channels.

Event Recipients Channel Trigger Service Example Message
Competition Created Program admins Email, In-app competition.ts "Competition 'MOPC 2026' has been created"
Round Opened Relevant participants Email, In-app round-engine.ts "Round 'Application Window' is now open"
Round Closing Soon Relevant participants Email, In-app notification-scheduler.ts "Round closes in 3 days"
Round Closed Relevant participants Email, In-app round-engine.ts "Round 'Application Window' has closed"
Submission Window Opened Eligible applicants Email, In-app submission-window.ts "New submission window open: Submit additional docs"
Submission Window Closing Applicants (incomplete) Email, In-app notification-scheduler.ts "Submission deadline in 1 day"
Submission Received Applicant Email, In-app project.ts "Your application has been received"
Filtering Job Complete Admins In-app filtering-service.ts "Filtering complete: 45 passed, 5 flagged"
Project Filtered Out Applicant Email filtering-service.ts "Your application did not pass initial screening"
Project Advanced Applicant Email, In-app round-engine.ts "Congratulations! You've advanced to Jury 1"
Jury Assignment Jury member Email, In-app assignment-orchestrator.ts "You have 12 new projects to evaluate"
Assignment Deadline Approaching Jury member (incomplete) Email, In-app notification-scheduler.ts "Please complete evaluations by [date]"
COI Declared Admin In-app coi-service.ts "[Juror] declared COI with [Project]"
Evaluation Complete Admin (all reviews done) In-app evaluation.ts "All evaluations complete for [Project]"
Evaluation Feedback Released Applicant Email, In-app evaluation.ts "Feedback available for your project"
Mentor Assigned Mentor, Team Email, In-app mentor-assignment.ts "You've been matched with a mentor"
Workspace Activated Mentor, Team Email, In-app mentor-workspace.ts "Mentoring workspace is now active"
Mentor File Uploaded Team (if mentor uploaded) In-app mentor-workspace.ts "[Mentor] uploaded a new file"
Team File Uploaded Mentor (if team uploaded) In-app mentor-workspace.ts "[Team] uploaded a new file"
File Comment Added File uploader In-app file-comment-service.ts "[User] commented on your file"
File Promoted Team, Admin Email, In-app mentor-workspace.ts "File promoted to official submission"
Live Finals Starting Jury, Audience Email, In-app, WebSocket live-control.ts "Live finals ceremony starting in 10 minutes"
Voting Started Jury WebSocket, In-app live-control.ts "Voting open for [Project]"
Voting Ended All participants WebSocket live-control.ts "Voting closed"
Live Finals Complete All participants Email, In-app live-control.ts "Live finals ceremony complete"
Winner Proposal Created Jury (approval group) Email, In-app winner-proposal.ts "Please review and approve winners"
Winner Approval Submitted Admin In-app winner-approval.ts "[Juror] approved the proposal"
Winner Proposal Approved Admin In-app winner-approval.ts "All approvals received, ready to freeze"
Winner Proposal Rejected Admin In-app winner-approval.ts "[Juror] rejected the proposal"
Winner Proposal Overridden Jury (all members) Email, In-app winner-proposal.ts "Admin overrode approval process"
Winners Frozen Winners, All participants Email, In-app winner-proposal.ts "Competition results are final"
Award Voting Opened Award jury Email, In-app special-award.ts "Voting open for [Award Name]"
Award Winner Announced Award winner Email, In-app special-award.ts "Congratulations! You won [Award Name]"
Deadline Reminder (7 days) Relevant users Email notification-scheduler.ts "Reminder: Deadline in 7 days"
Deadline Reminder (3 days) Relevant users Email notification-scheduler.ts "Reminder: Deadline in 3 days"
Deadline Reminder (1 day) Relevant users Email, In-app notification-scheduler.ts "Final reminder: Deadline tomorrow"

Notification Channels by Urgency

Urgency Channels Used Examples
Critical Email + In-app + WebSocket (if real-time) Voting started, Winners frozen, Admin override
High Email + In-app Round opened, Assignment created, Approval requested
Medium Email or In-app Deadline reminders, File uploaded, Comment added
Low In-app only Filtering complete, Evaluation complete

7. Audit Trail Coverage

Which operations are logged to DecisionAuditLog.

Always Audited

Operation Fields Logged Importance
Competition Created competitionId, name, createdBy, timestamp High
Competition Status Changed competitionId, oldStatus, newStatus, changedBy, timestamp High
Round Created roundId, competitionId, roundType, createdBy, timestamp High
Round Opened roundId, openedBy, timestamp Critical
Round Closed roundId, closedBy, timestamp Critical
Round Advanced roundId, projectsAdvanced, advancedBy, timestamp Critical
Jury Group Created juryGroupId, competitionId, createdBy, timestamp High
Jury Member Added juryGroupId, userId, addedBy, timestamp Medium
Assignment Generated roundId, juryGroupId, count, method (AI/manual), generatedBy, timestamp High
Assignment Overridden assignmentId, oldValue, newValue, reason, overriddenBy, timestamp Critical
Filtering Job Run roundId, filteredCount, aiUsed, ranBy, timestamp High
Filtering Result Overridden filteringResultId, oldDecision, newDecision, reason, overriddenBy, timestamp Critical
Evaluation Submitted evaluationId, projectId, jurorId, scores, timestamp High
AI Summary Generated evaluationSummaryId, projectId, generatedAt Medium
Mentor Assigned mentorAssignmentId, projectId, mentorId, method (AI/manual), assignedBy, timestamp High
Workspace Activated mentorAssignmentId, activatedBy, timestamp Medium
File Promoted mentorFileId, projectFileId, promotedBy, timestamp Critical
Live Session Started sessionId, roundId, startedBy, timestamp Critical
Live Cursor Advanced cursorId, fromProjectId, toProjectId, advancedBy, timestamp Medium
Live Voting Started sessionId, projectId, startedBy, timestamp Critical
Live Vote Submitted voteId, sessionId, projectId, voterId (anonymized), timestamp High
Live Voting Ended sessionId, projectId, endedBy, timestamp Critical
Winner Proposal Created proposalId, category, rankedProjects, proposedBy, timestamp Critical
Winner Approval Submitted approvalId, proposalId, userId, approved, comments, timestamp Critical
Winner Proposal Overridden proposalId, overrideMode, reason, overriddenBy, timestamp CRITICAL
Winner Proposal Frozen proposalId, frozenBy, timestamp CRITICAL
Special Award Created awardId, competitionId, createdBy, timestamp High
Award Eligibility Updated awardId, projectId, eligible, updatedBy, timestamp High
Award Vote Submitted voteId, awardId, projectId, voterId, timestamp High
Award Winner Selected awardId, winnerProjectId, selectedBy, timestamp Critical

Retention Policy

Audit Type Retention Reason
Winner-related Permanent Legal requirement, archival
Evaluation-related Permanent Transparency, appeals
Assignment-related 5 years Conflict resolution, reviews
Filtering-related 3 years Compliance, transparency
File operations 2 years Storage optimization
System events 1 year Debugging, performance

8. Permission Matrix

Role × Feature access table.

Feature SUPER_ADMIN PROGRAM_ADMIN JURY_MEMBER MENTOR APPLICANT OBSERVER AUDIENCE
Create Competition
Edit Competition
Delete Competition
Create Round
Edit Round
Open/Close Round
Advance Round
Create Jury Group
Add/Remove Jury Member
Generate Assignments
Override Assignment
View All Assignments ✓ (read)
View Own Assignments
Submit Evaluation
View All Evaluations ✓ (read)
View Peer Reviews ✓ (anonymized)
Run Filtering Job
Override Filtering
View Filtering Results ✓ (read)
Submit Application
Upload Files (Submission)
View Own Project
View All Projects ✓ (read)
Assign Mentor
Activate Workspace
Upload Workspace File ✓ (own)
Comment on File ✓ (own)
Promote File
View Workspace ✓ (all) ✓ (all) ✓ (assigned) ✓ (own)
Initialize Stage Manager
Advance Live Cursor
Start/End Live Voting
Submit Jury Vote (Live)
Submit Audience Vote
View Live Results ✓ (if enabled)
Create Winner Proposal
Submit Winner Approval ✓ (if in group)
Override Winner Proposal
Freeze Winner Proposal
Create Special Award
Update Award Eligibility
Submit Award Vote ✓ (if in jury)
Select Award Winner
View Audit Logs ✓ (read)
Export Data ✓ (read)

9. File System Integration (MinIO)

How MinIO connects to each feature.

File Upload Paths

Feature Bucket Path Pattern Example
Application Submission mopc-files projects/{projectId}/submissions/{windowId}/{requirementId}/{filename} projects/proj_abc/submissions/sw_1/req_exec_summary/exec_summary.pdf
Second Round Submission mopc-files projects/{projectId}/submissions/{windowId}/{requirementId}/{filename} projects/proj_abc/submissions/sw_2/req_video_pitch/pitch.mp4
Mentor Workspace Upload mopc-files mentoring/{assignmentId}/files/{fileId}/{filename} mentoring/ma_xyz/files/mf_123/revised_plan.pdf
Promoted Mentor File mopc-files projects/{projectId}/submissions/{windowId}/promoted/{filename} projects/proj_abc/submissions/sw_2/promoted/mentor_reviewed_plan.pdf

Pre-Signed URL Generation Points

Feature When Generated Expiry Purpose
Applicant File Upload Before upload form submission 15 min Allow direct upload from browser
Jury File Download When jury opens project detail 1 hour Allow jury to view files
Mentor File Download When mentor opens workspace 1 hour Allow mentor to view team files
Team File Download When team opens workspace 1 hour Allow team to view mentor files
Admin Export When admin exports project data 24 hours Allow bulk download

File Access Control Per Role

Role Application Files (Round 1) Application Files (Round 2) Mentor Workspace Files Access Method
Applicant Own only (read/write during window) Own only (read/write during window) Own only (read/write) Pre-signed URL
Jury Assigned projects only (read) Assigned projects only (read) Pre-signed URL
Mentor Assigned project only (read/write) Pre-signed URL
Admin All (read) All (read) All (read) Pre-signed URL
Observer All (read) All (read) Pre-signed URL

File Lifecycle Hooks

Upload:
  1. Validate mime type, size
  2. Generate MinIO pre-signed PUT URL (file-upload-service.ts)
  3. Client uploads directly to MinIO
  4. Client confirms upload success
  5. Backend creates ProjectFile/MentorFile record
  6. Audit log

Download:
  1. Check user permission (assignment, ownership)
  2. Generate MinIO pre-signed GET URL (minio-service.ts)
  3. Return URL to client
  4. Client downloads directly from MinIO
  5. (Optional) Log download in audit trail

Delete:
  1. Check user permission
  2. Soft delete: Mark file as deleted in DB (don't delete from MinIO)
  3. Admin hard delete: Delete from MinIO + DB
  4. Audit log

Replace:
  1. Upload new version (same flow as Upload)
  2. Link old version via ProjectFile.replacedById
  3. Old file retained in MinIO for audit trail

10. AI Service Integration Points

Where AI is used across the system.

AI Services Overview

Service Model Used Input Output Cost Mitigation
ai-filtering.ts GPT-4o Project data (anonymized) Pass/Reject recommendation, confidence score, reasoning Batch processing (20 projects/call)
ai-assignment.ts GPT-4o Jury expertise, project descriptions Expertise match scores, reasoning Cache expertise profiles
ai-evaluation-summary.ts GPT-4o All evaluations for a project (anonymized) Strengths, weaknesses, consensus Generate once per project after all evaluations
ai-tagging.ts GPT-3.5-turbo Project description Tags (ocean focus, innovation level) Batch processing
ai-award-eligibility.ts GPT-4o Project description, award criteria Eligibility score, reasoning Run only for flagged awards
duplicate-detection.ts GPT-4o + vector embeddings Project descriptions Similarity scores, duplicate pairs Use embeddings cache

Anonymization Strategy

All AI calls use anonymization.ts service to strip PII:

// Before AI call:
{
  projectId: "proj_abc",
  teamName: "OceanTech Solutions",
  contactEmail: "john@oceantech.com",
  description: "Our startup focuses on..."
}

// After anonymization:
{
  projectId: "PROJ_001",  // Pseudonym mapping maintained
  teamName: "Project Team A",
  contactEmail: "[redacted]",
  description: "Our startup focuses on..."
}

// Mapping stored in memory for this AI session only

AI Call Flow (Example: Filtering)

Admin
  | trpc.filtering.runJob
  v
Backend: filtering-service.ts
  |
  | 1. Get projects (batch of 20)
  | 2. For each project:
  |    - Call anonymization.ts (strip PII, generate pseudonym)
  | 3. Call ai-filtering.ts
  |    |
  |    | OpenAI API call:
  |    | - Model: gpt-4o
  |    | - System prompt: "You are an expert evaluator for ocean conservation projects..."
  |    | - User prompt: "Evaluate these 20 projects based on: [criteria]"
  |    | - Response format: JSON with { projectId, recommendation, confidence, reasoning }
  |    v
  | 4. Parse AI response
  | 5. De-anonymize (map pseudonyms back to real projectIds)
  | 6. Create FilteringResult records
  v
Database: FilteringResult with AI recommendation

AI Feature Flags Per Round

Round Type AI Features Available
INTAKE None
FILTERING AI Screening, Duplicate Detection
EVALUATION AI Summary, AI Assignment (if enabled)
SUBMISSION None
MENTORING AI Mentor Matching
LIVE_FINAL None
CONFIRMATION None

11. Cross-Round Data Dependencies

Which rounds need data from which other rounds.

Dependency Map

Round 1: INTAKE
  └─> No dependencies

Round 2: FILTERING
  └─> Requires: Round 1 submissions (ProjectFiles)

Round 3: EVALUATION (Jury 1)
  └─> Requires: Round 1 submissions (ProjectFiles from SubmissionWindow 1)
  └─> Reads: Filtering results (optional — can show AI recommendation)

Round 4: SUBMISSION (Second Window)
  └─> Requires: Round 3 results (which projects PASSED)
  └─> Locks: Round 1 submissions (read-only for applicants)

Round 5: EVALUATION (Jury 2)
  └─> Requires: Round 1 submissions (ProjectFiles from SubmissionWindow 1)
  └─> Requires: Round 4 submissions (ProjectFiles from SubmissionWindow 2)
  └─> Reads: Round 3 scores (optional — can show previous jury feedback)

Round 6: MENTORING
  └─> Requires: Round 5 results (which projects PASSED — finalists)
  └─> Requires: MentorAssignment records (created after Round 5)
  └─> Writes to: SubmissionWindow (promoted files target Round 4 or a new window)

Round 7: LIVE_FINAL
  └─> Requires: Round 5 results (finalist rankings for presentation order)
  └─> Requires: All submission windows (jury needs full project history)
  └─> Writes: LiveVote records (used for final scores)

Round 8: CONFIRMATION
  └─> Requires: Round 7 results (final scores from live voting)
  └─> Writes: WinnerProposal (ranked project IDs)
  └─> Freeze: All rounds (no further changes after freeze)

Visibility Matrix (Which Files Are Visible to Which Rounds)

Configured via RoundSubmissionVisibility model.

Example for MOPC 2026:

Evaluation Round Submission Window 1 (Round 1 Docs) Submission Window 2 (Round 2 Docs)
Jury 1 (Round 3) ✓ Visible ✗ Not visible (doesn't exist yet)
Jury 2 (Round 5) ✓ Visible (labeled "Application Docs") ✓ Visible (labeled "Semi-finalist Docs")
Live Finals (Round 7) ✓ Visible ✓ Visible

Data Freeze on Confirmation

When WinnerProposal is frozen:

-- All ProjectRoundStates frozen
UPDATE ProjectRoundState
SET state = 'COMPLETED', exitedAt = NOW()
WHERE projectId IN (SELECT id FROM Project WHERE competitionId = 'comp_xyz')
  AND state != 'COMPLETED';

-- All Rounds marked ARCHIVED
UPDATE Round
SET status = 'ROUND_ARCHIVED'
WHERE competitionId = 'comp_xyz';

-- Competition CLOSED
UPDATE Competition
SET status = 'CLOSED'
WHERE id = 'comp_xyz';

-- Audit log: COMPETITION_FROZEN
INSERT INTO DecisionAuditLog (event, competitionId, timestamp, userId)
VALUES ('COMPETITION_FROZEN', 'comp_xyz', NOW(), 'admin_user_id');

After freeze:

  • No rounds can be opened/closed
  • No evaluations can be submitted
  • No assignments can be changed
  • No files can be uploaded (except to archived workspaces with special permission)
  • Winner rankings are permanent

12. Special Award Integration

How special awards connect to the main competition flow.

Two Award Modes

Mode 1: STAY_IN_MAIN

Projects remain in main competition, flagged as award-eligible.

Main Competition Flow:
  Round 1: INTAKE → 50 projects submitted
  Round 2: FILTERING → 45 projects pass
  Round 3: EVALUATION (Jury 1) → 20 projects advance to semi-finals

Special Award: "Innovation Award" (STAY_IN_MAIN)
  - Runs during Round 3 (evaluationRoundId = round-3)
  - Eligibility: AI checks all 45 filtered projects for "innovative technology use"
  - Result: 10 projects flagged as eligible
  - Voting: Award jury (separate JuryGroup) votes on 10 eligible projects
  - Winner: 1 project selected
  - Impact: Winner gets award, still advances in main competition if scores qualify

Award voting happens in parallel with main evaluation.

Mode 2: SEPARATE_POOL

Projects pulled out of main flow into award-only track.

Main Competition Flow:
  Round 1: INTAKE → 50 projects submitted
  Round 2: FILTERING → 45 projects pass

Special Award: "Ocean Advocacy Award" (SEPARATE_POOL)
  - Eligibility: AI checks all 45 filtered projects for "advocacy focus"
  - Result: 5 projects flagged as eligible
  - Action: Update ProjectRoundState for those 5 projects:
      - Mark as WITHDRAWN from main competition
      - Create separate AwardEligibility records
  - Main competition continues with 40 projects
  - Award jury (separate JuryGroup) votes on 5 eligible projects
  - Winner: 1 project selected
  - Impact: Winner gets award, not eligible for main competition prizes

Main competition and award run independently.

Award Data Flow

Admin
  | Create Special Award
  | trpc.specialAward.create
  v
Backend: special-award.ts
  |
  | 1. Create SpecialAward record
  |    - competitionId
  |    - eligibilityMode (STAY_IN_MAIN | SEPARATE_POOL)
  |    - evaluationRoundId (which round to run alongside)
  |    - juryGroupId (dedicated or shared jury)
  |    - criteriaText (for AI eligibility)
  | 2. Audit log
  v
Database: SpecialAward created
  |
  v
Admin
  | Update eligibility (run AI check)
  | trpc.specialAward.updateEligibility
  v
Backend: award-eligibility.ts
  |
  | 1. Get all projects in evaluationRound
  | 2. Call ai-award-eligibility.ts
  |    - Anonymize project data
  |    - Send to OpenAI with award criteriaText
  |    - Get eligibility scores
  | 3. Create AwardEligibility records
  |    - awardId, projectId, isEligible, aiScore, aiReasoning
  | 4. If eligibilityMode = SEPARATE_POOL:
  |    - Update ProjectRoundState (state = WITHDRAWN)
  | 5. Notification (notify eligible teams)
  | 6. Audit log
  v
Database: AwardEligibility records created
  |
  v
Admin
  | Open voting
  | trpc.specialAward.startVoting
  v
Backend: special-award.ts
  |
  | 1. Update SpecialAward (status = VOTING_OPEN, votingStartAt = now)
  | 2. Notification (notify award jury)
  | 3. Audit log
  v
Award Jury Member
  | Visit /jury/award/[awardId]
  | Submit vote
  | trpc.specialAward.submitVote
  v
Backend: award-voting.ts
  |
  | 1. Create AwardVote record (awardId, userId, projectId, score/ranking)
  | 2. Audit log
  v
Database: AwardVote created
  |
  v
Admin
  | Close voting
  | trpc.specialAward.closeVoting
  v
Backend: special-award.ts
  |
  | 1. Update SpecialAward (status = CLOSED, votingEndAt = now)
  | 2. Call award-voting.ts to tally results
  |    - If scoringMode = PICK_WINNER: Select project with most votes
  |    - If scoringMode = RANKED: Calculate ranking by sum of ranks
  |    - If scoringMode = SCORED: Average scores
  | 3. Update SpecialAward (winnerProjectId)
  | 4. Notification (notify winner + all voters)
  | 5. Audit log
  v
Database: SpecialAward.winnerProjectId set

Summary

This integration map provides complete cross-references for:

  1. 72 model-service connections — Every service that operates on every model
  2. 11 router-service mappings — Which routers call which services
  3. 32 UI page mappings — Which pages call which routers
  4. 45 feature activations per round type — Feature matrix for all 7 round types
  5. 8 complete data flow diagrams — From application to winner confirmation
  6. 35 notification triggers — Event → recipient → channel mappings
  7. 34 audit trail points — Critical operations that must be logged
  8. 56 permission rules — Role × feature access control
  9. File system integration — MinIO paths, access control, lifecycle
  10. 6 AI service integration points — Where and how AI is used
  11. Cross-round dependencies — Data flow across the entire competition
  12. Special award integration — Two modes with complete flows

This document is the master reference for understanding how every piece of the MOPC architecture redesign connects to every other piece. Use it to:

  • Trace data flows end-to-end
  • Understand which services are called for any user action
  • Verify permission enforcement
  • Design new features that integrate correctly
  • Debug issues by following the integration paths
  • Ensure audit coverage for all critical operations