# Round Type: LIVE_FINAL — Live Finals Documentation ## Overview The **LIVE_FINAL** round type orchestrates the live ceremony where Jury 3 evaluates finalist presentations in real-time. This is Round 7 in the redesigned 8-step competition flow. It combines jury scoring, optional audience participation, deliberation periods, and live results display into a single managed event. **Core capabilities:** - Real-time stage manager controls (presentation cursor, timing, pause/resume) - Jury voting with multiple modes (numeric, ranking, binary) - Optional audience voting with weighted scores - Per-category presentation windows (STARTUP window, then CONCEPT window) - Deliberation period for jury discussion - Live results display or ceremony reveal - Anti-fraud measures for audience participation **Round 7 position in the flow:** ``` Round 1: Application Window (INTAKE) Round 2: AI Screening (FILTERING) Round 3: Jury 1 - Semi-finalist Selection (EVALUATION) Round 4: Semi-finalist Submission (SUBMISSION) Round 5: Jury 2 - Finalist Selection (EVALUATION) Round 6: Finalist Mentoring (MENTORING) Round 7: Live Finals (LIVE_FINAL) ← THIS DOCUMENT Round 8: Confirm Winners (CONFIRMATION) ``` --- ## Current System (Pipeline → Track → Stage) ### Existing Models **LiveVotingSession** — Per-stage voting session: ```prisma model LiveVotingSession { id String @id @default(cuid()) stageId String? @unique status String @default("NOT_STARTED") // NOT_STARTED, IN_PROGRESS, PAUSED, COMPLETED currentProjectIndex Int @default(0) currentProjectId String? votingStartedAt DateTime? votingEndsAt DateTime? projectOrderJson Json? @db.JsonB // Array of project IDs in presentation order // Voting configuration votingMode String @default("simple") // "simple" (1-10) | "criteria" (per-criterion scores) criteriaJson Json? @db.JsonB // Array of { id, label, description, scale, weight } // Audience settings allowAudienceVotes Boolean @default(false) audienceVoteWeight Float @default(0) // 0.0 to 1.0 audienceVotingMode String @default("disabled") // "disabled" | "per_project" | "per_category" | "favorites" audienceMaxFavorites Int @default(3) audienceRequireId Boolean @default(false) audienceVotingDuration Int? // Minutes (null = same as jury) tieBreakerMethod String @default("admin_decides") // 'admin_decides' | 'highest_individual' | 'revote' presentationSettingsJson Json? @db.JsonB stage Stage? @relation(...) votes LiveVote[] audienceVoters AudienceVoter[] } ``` **LiveVote** — Individual jury or audience vote: ```prisma model LiveVote { id String @id @default(cuid()) sessionId String projectId String userId String? // Nullable for audience voters without accounts score Int // 1-10 (or weighted score for criteria mode) isAudienceVote Boolean @default(false) votedAt DateTime @default(now()) // Criteria scores (used when votingMode="criteria") criterionScoresJson Json? @db.JsonB // { [criterionId]: score } // Audience voter link audienceVoterId String? session LiveVotingSession @relation(...) user User? @relation(...) audienceVoter AudienceVoter? @relation(...) @@unique([sessionId, projectId, userId]) @@unique([sessionId, projectId, audienceVoterId]) } ``` **AudienceVoter** — Registered audience participant: ```prisma model AudienceVoter { id String @id @default(cuid()) sessionId String token String @unique // Unique voting token (UUID) identifier String? // Optional: email, phone, or name identifierType String? // "email" | "phone" | "name" | "anonymous" ipAddress String? userAgent String? createdAt DateTime @default(now()) session LiveVotingSession @relation(...) votes LiveVote[] } ``` **LiveProgressCursor** — Stage manager cursor: ```prisma model LiveProgressCursor { id String @id @default(cuid()) stageId String @unique sessionId String @unique @default(cuid()) activeProjectId String? activeOrderIndex Int @default(0) isPaused Boolean @default(false) stage Stage @relation(...) } ``` **Cohort** — Presentation groups: ```prisma model Cohort { id String @id @default(cuid()) stageId String name String votingMode String @default("simple") // simple, criteria, ranked isOpen Boolean @default(false) windowOpenAt DateTime? windowCloseAt DateTime? stage Stage @relation(...) projects CohortProject[] } ``` ### Current Service Functions `src/server/services/live-control.ts`: - `startSession(stageId, actorId)` — Initialize/reset cursor - `setActiveProject(stageId, projectId, actorId)` — Set currently presenting project - `jumpToProject(stageId, orderIndex, actorId)` — Jump to specific project in queue - `reorderQueue(stageId, newOrder, actorId)` — Reorder presentation sequence - `pauseResume(stageId, isPaused, actorId)` — Toggle pause state - `openCohortWindow(cohortId, actorId)` — Open voting window for a cohort - `closeCohortWindow(cohortId, actorId)` — Close cohort window ### Current tRPC Procedures `src/server/routers/live-voting.ts`: ```typescript liveVoting.getSession({ stageId }) liveVoting.getSessionForVoting({ sessionId }) // Jury view liveVoting.getPublicSession({ sessionId }) // Display view liveVoting.setProjectOrder({ sessionId, projectIds }) liveVoting.setVotingMode({ sessionId, votingMode: 'simple' | 'criteria' }) liveVoting.setCriteria({ sessionId, criteria }) liveVoting.importCriteriaFromForm({ sessionId, formId }) liveVoting.startVoting({ sessionId, projectId, durationSeconds }) liveVoting.stopVoting({ sessionId }) liveVoting.endSession({ sessionId }) liveVoting.vote({ sessionId, projectId, score, criterionScores }) liveVoting.getResults({ sessionId, juryWeight?, audienceWeight? }) liveVoting.updatePresentationSettings({ sessionId, presentationSettingsJson }) liveVoting.updateSessionConfig({ sessionId, allowAudienceVotes, audienceVoteWeight, ... }) liveVoting.registerAudienceVoter({ sessionId, identifier?, identifierType? }) // Public liveVoting.castAudienceVote({ sessionId, projectId, score, token }) // Public liveVoting.getAudienceVoterStats({ sessionId }) liveVoting.getAudienceSession({ sessionId }) // Public liveVoting.getPublicResults({ sessionId }) // Public ``` ### Current LiveFinalConfig Type From `src/types/pipeline-wizard.ts`: ```typescript type LiveFinalConfig = { juryVotingEnabled: boolean audienceVotingEnabled: boolean audienceVoteWeight: number cohortSetupMode: 'auto' | 'manual' revealPolicy: 'immediate' | 'delayed' | 'ceremony' } ``` ### Current Admin UI `src/components/admin/pipeline/sections/live-finals-section.tsx`: - Jury voting toggle - Audience voting toggle + weight slider (0-100%) - Cohort setup mode selector (auto/manual) - Result reveal policy selector (immediate/delayed/ceremony) --- ## Redesigned Live Finals Round ### Enhanced LiveFinalConfig **New comprehensive config:** ```typescript type LiveFinalConfig = { // Jury configuration juryGroupId: string // Which jury evaluates (Jury 3) // Voting mode votingMode: 'NUMERIC' | 'RANKING' | 'BINARY' // Numeric mode settings numericScale?: { min: number // Default: 1 max: number // Default: 10 allowDecimals: boolean // Default: false } // Criteria-based voting (optional enhancement to NUMERIC) criteriaEnabled?: boolean criteriaJson?: LiveVotingCriterion[] // { id, label, description, scale, weight } importFromEvalForm?: string // EvaluationForm ID to import criteria from // Ranking mode settings rankingSettings?: { maxRankedProjects: number // How many projects each juror ranks (e.g., top 3) pointsSystem: 'DESCENDING' | 'BORDA' // 3-2-1 or Borda count } // Binary mode settings (simple yes/no) binaryLabels?: { yes: string // Default: "Finalist" no: string // Default: "Not Selected" } // Audience voting audienceVotingEnabled: boolean audienceVotingWeight: number // 0-100, percentage weight juryVotingWeight: number // complement of audience weight (calculated) audienceVotingMode: 'PER_PROJECT' | 'FAVORITES' | 'CATEGORY_FAVORITES' audienceMaxFavorites?: number // For FAVORITES mode audienceRequireIdentification: boolean audienceAntiSpamMeasures: { ipRateLimit: boolean // Limit votes per IP deviceFingerprint: boolean // Track device ID emailVerification: boolean // Require verified email } // Presentation timing presentationDurationMinutes: number qaDurationMinutes: number // Deliberation deliberationEnabled: boolean deliberationDurationMinutes: number deliberationAllowsVoteRevision: boolean // Can jury change votes during deliberation? // Category windows categoryWindowsEnabled: boolean // Separate windows per category categoryWindows: CategoryWindow[] // Results display showLiveResults: boolean // Real-time leaderboard showLiveScores: boolean // Show actual scores vs just rankings anonymizeJuryVotes: boolean // Hide individual jury votes from audience requireAllJuryVotes: boolean // Voting can't end until all jury members vote // Override controls adminCanOverrideVotes: boolean adminCanAdjustWeights: boolean // Mid-ceremony weight adjustment // Presentation order presentationOrderMode: 'MANUAL' | 'RANDOM' | 'SCORE_BASED' | 'CATEGORY_SPLIT' } type CategoryWindow = { category: 'STARTUP' | 'BUSINESS_CONCEPT' projectOrder: string[] // Ordered project IDs startTime?: string // Scheduled start (ISO 8601) endTime?: string // Scheduled end deliberationMinutes?: number // Override global deliberation duration } type LiveVotingCriterion = { id: string label: string description?: string scale: number // 1-10, 1-5, etc. weight: number // Sum to 1.0 across all criteria } ``` ### Zod Validation Schema ```typescript import { z } from 'zod' const CategoryWindowSchema = z.object({ category: z.enum(['STARTUP', 'BUSINESS_CONCEPT']), projectOrder: z.array(z.string()), startTime: z.string().datetime().optional(), endTime: z.string().datetime().optional(), deliberationMinutes: z.number().int().min(0).max(120).optional(), }) const LiveVotingCriterionSchema = z.object({ id: z.string(), label: z.string().min(1).max(100), description: z.string().max(500).optional(), scale: z.number().int().min(1).max(100), weight: z.number().min(0).max(1), }) export const LiveFinalConfigSchema = z.object({ // Jury juryGroupId: z.string(), // Voting mode votingMode: z.enum(['NUMERIC', 'RANKING', 'BINARY']), // Numeric mode settings numericScale: z.object({ min: z.number().int().default(1), max: z.number().int().default(10), allowDecimals: z.boolean().default(false), }).optional(), // Criteria criteriaEnabled: z.boolean().optional(), criteriaJson: z.array(LiveVotingCriterionSchema).optional(), importFromEvalForm: z.string().optional(), // Ranking rankingSettings: z.object({ maxRankedProjects: z.number().int().min(1).max(20), pointsSystem: z.enum(['DESCENDING', 'BORDA']), }).optional(), // Binary binaryLabels: z.object({ yes: z.string().default('Finalist'), no: z.string().default('Not Selected'), }).optional(), // Audience audienceVotingEnabled: z.boolean(), audienceVotingWeight: z.number().min(0).max(100), juryVotingWeight: z.number().min(0).max(100), audienceVotingMode: z.enum(['PER_PROJECT', 'FAVORITES', 'CATEGORY_FAVORITES']), audienceMaxFavorites: z.number().int().min(1).max(20).optional(), audienceRequireIdentification: z.boolean(), audienceAntiSpamMeasures: z.object({ ipRateLimit: z.boolean(), deviceFingerprint: z.boolean(), emailVerification: z.boolean(), }), // Timing presentationDurationMinutes: z.number().int().min(1).max(60), qaDurationMinutes: z.number().int().min(0).max(30), // Deliberation deliberationEnabled: z.boolean(), deliberationDurationMinutes: z.number().int().min(0).max(120), deliberationAllowsVoteRevision: z.boolean(), // Category windows categoryWindowsEnabled: z.boolean(), categoryWindows: z.array(CategoryWindowSchema), // Results showLiveResults: z.boolean(), showLiveScores: z.boolean(), anonymizeJuryVotes: z.boolean(), requireAllJuryVotes: z.boolean(), // Overrides adminCanOverrideVotes: z.boolean(), adminCanAdjustWeights: z.boolean(), // Presentation order presentationOrderMode: z.enum(['MANUAL', 'RANDOM', 'SCORE_BASED', 'CATEGORY_SPLIT']), }).refine( (data) => { // Ensure weights sum to 100 return data.audienceVotingWeight + data.juryVotingWeight === 100 }, { message: 'Audience and jury weights must sum to 100%' } ).refine( (data) => { // If criteria enabled, must have criteria if (data.criteriaEnabled && (!data.criteriaJson || data.criteriaJson.length === 0)) { return false } return true }, { message: 'Criteria-based voting requires at least one criterion' } ).refine( (data) => { // Criteria weights must sum to 1.0 if (data.criteriaJson && data.criteriaJson.length > 0) { const weightSum = data.criteriaJson.reduce((sum, c) => sum + c.weight, 0) return Math.abs(weightSum - 1.0) < 0.01 } return true }, { message: 'Criteria weights must sum to 1.0' } ) ``` --- ## Stage Manager — Admin Controls The **Stage Manager** is the admin control panel for orchestrating the live ceremony. It provides real-time control over presentation flow, voting windows, and emergency interventions. ### Ceremony State Machine ``` Ceremony State Flow: NOT_STARTED → (start session) → IN_PROGRESS → (deliberation starts) → DELIBERATION → (voting ends) → COMPLETED NOT_STARTED: - Session created but not started - Projects ordered (manual or automatic) - Jury and audience links generated - Stage manager can preview setup IN_PROGRESS: - Presentations ongoing - Per-project state: WAITING → PRESENTING → Q_AND_A → VOTING → VOTED → SCORED - Admin can pause, skip, reorder on the fly DELIBERATION: - Timer running for deliberation period - Jury can discuss (optional chat/discussion interface) - Votes may be revised (if deliberationAllowsVoteRevision=true) - Admin can extend deliberation time COMPLETED: - All voting finished - Results calculated - Ceremony locked (or unlocked for result reveal) ``` ### Per-Project State Each project in the live finals progresses through these states: ``` WAITING → Project queued, not yet presenting PRESENTING → Presentation in progress (timer: presentationDurationMinutes) Q_AND_A → Q&A session (timer: qaDurationMinutes) VOTING → Voting window open (jury + audience can vote) VOTED → Voting window closed, awaiting next action SCORED → Scores calculated, moving to next project SKIPPED → Admin skipped this project (emergency override) ``` ### Stage Manager UI Controls **ASCII Mockup:** ``` ┌─────────────────────────────────────────────────────────────────────┐ │ LIVE FINALS STAGE MANAGER Session: live-abc-123 │ ├─────────────────────────────────────────────────────────────────────┤ │ Status: IN_PROGRESS Category: STARTUP Jury: Jury 3 (8/8) │ │ │ │ [Pause Ceremony] [End Session] [Emergency Stop] │ └─────────────────────────────────────────────────────────────────────┘ ┌─ CURRENT PROJECT ───────────────────────────────────────────────────┐ │ Project #3 of 6 (STARTUP) │ │ Title: "OceanSense AI" — Team: AquaTech Solutions │ │ │ │ State: VOTING │ │ ┌─ Presentation Timer ────┐ ┌─ Q&A Timer ─────┐ ┌─ Voting Timer ─┐│ │ │ Completed: 8:00 / 8:00 │ │ Completed: 5:00 │ │ 0:45 remaining ││ │ └─────────────────────────┘ └──────────────────┘ └────────────────┘│ │ │ │ Jury Votes: 6 / 8 (75%) │ │ [✓] Alice Chen [✓] Bob Martin [ ] Carol Davis │ │ [✓] David Lee [✓] Emma Wilson [ ] Frank Garcia │ │ [✓] Grace Huang [✓] Henry Thompson │ │ │ │ Audience Votes: 142 │ │ │ │ [Skip Project] [Reset Votes] [Extend Time +1min] [Next Project] │ └───────────────────────────────────────────────────────────────────────┘ ┌─ PROJECT QUEUE ─────────────────────────────────────────────────────┐ │ [✓] 1. AquaClean Tech (STARTUP) — Score: 8.2 (Completed) │ │ [✓] 2. BlueCarbon Solutions (STARTUP) — Score: 7.8 (Completed) │ │ [>] 3. OceanSense AI (STARTUP) — Voting in progress │ │ [ ] 4. MarineTech Innovations (STARTUP) — Waiting │ │ [ ] 5. CoralGuard (STARTUP) — Waiting │ │ [ ] 6. DeepSea Robotics (STARTUP) — Waiting │ │ │ │ [Reorder Queue] [Jump to Project...] [Add Project] │ └───────────────────────────────────────────────────────────────────────┘ ┌─ CATEGORY WINDOWS ──────────────────────────────────────────────────┐ │ Window 1: STARTUP (6 projects) │ │ Status: IN_PROGRESS (Project 3/6) │ │ Started: 2026-05-15 18:00:00 │ │ [Close Window & Start Deliberation] │ │ │ │ Window 2: BUSINESS_CONCEPT (6 projects) │ │ Status: WAITING │ │ Scheduled: 2026-05-15 19:30:00 │ │ [Start Window Early] │ └───────────────────────────────────────────────────────────────────────┘ ┌─ LIVE LEADERBOARD (STARTUP) ────────────────────────────────────────┐ │ Rank | Project | Jury Avg | Audience | Weighted | Gap │ │------+-----------------------+----------+----------+----------+------│ │ 1 | AquaClean Tech | 8.5 | 7.2 | 8.2 | — │ │ 2 | BlueCarbon Solutions | 8.0 | 7.4 | 7.8 | -0.4 │ │ 3 | OceanSense AI | — | 6.8 | — | — │ │ 4 | MarineTech Innov. | — | — | — | — │ │ 5 | CoralGuard | — | — | — | — │ │ 6 | DeepSea Robotics | — | — | — | — │ └───────────────────────────────────────────────────────────────────────┘ ┌─ CEREMONY LOG ──────────────────────────────────────────────────────┐ │ 18:43:22 — Voting opened for "OceanSense AI" │ │ 18:42:10 — Q&A period ended │ │ 18:37:05 — Q&A period started │ │ 18:29:00 — Presentation started: "OceanSense AI" │ │ 18:28:45 — Voting closed for "BlueCarbon Solutions" │ │ 18:27:30 — All jury votes received for "BlueCarbon Solutions" │ └───────────────────────────────────────────────────────────────────────┘ ┌─ ADMIN OVERRIDE PANEL ──────────────────────────────────────────────┐ │ [Override Individual Vote...] [Adjust Weights...] [Reset Session] │ └───────────────────────────────────────────────────────────────────────┘ ``` ### Stage Manager Features **Core controls:** 1. **Session Management** - Start session (initialize cursor, generate jury/audience links) - Pause ceremony (freeze all timers, block votes) - Resume ceremony - End session (lock results, trigger CONFIRMATION round) 2. **Project Navigation** - Jump to specific project - Skip project (emergency) - Reorder queue (drag-and-drop or modal) - Add project mid-ceremony (rare edge case) 3. **Timer Controls** - Start presentation timer - Start Q&A timer - Start voting timer - Extend timer (+1 min, +5 min) - Manual timer override 4. **Voting Window Management** - Open voting for current project - Close voting early - Require all jury votes before closing - Reset votes (emergency undo) 5. **Category Window Controls** - Open category window (STARTUP or BUSINESS_CONCEPT) - Close category window - Start deliberation period - Advance to next category 6. **Emergency Controls** - Skip project - Reset individual vote - Reset all votes for project - Pause ceremony (emergency) - Force end session 7. **Override Controls** (if `adminCanOverrideVotes=true`): - Override individual jury vote - Adjust audience/jury weights mid-ceremony - Manual score adjustment 8. **Real-Time Monitoring** - Live vote count (jury + audience) - Missing jury votes indicator - Audience voter count - Leaderboard (if `showLiveResults=true`) - Ceremony event log --- ## Jury 3 Voting Experience ### Jury Dashboard **ASCII Mockup:** ``` ┌─────────────────────────────────────────────────────────────────────┐ │ LIVE FINALS VOTING — Jury 3 Alice Chen │ ├─────────────────────────────────────────────────────────────────────┤ │ Status: VOTING IN PROGRESS │ │ Category: STARTUP │ │ │ │ [View All Finalists] [Results Dashboard] [Jury Discussion] │ └─────────────────────────────────────────────────────────────────────┘ ┌─ CURRENT PROJECT ───────────────────────────────────────────────────┐ │ Project 3 of 6 │ │ │ │ OceanSense AI │ │ Team: AquaTech Solutions │ │ Category: STARTUP (Marine Technology) │ │ │ │ Description: │ │ AI-powered ocean monitoring platform that detects pollution events │ │ in real-time using satellite imagery and underwater sensors. │ │ │ │ ┌─ Documents ──────────────────────────────────────────────────┐ │ │ │ Round 1 Docs: │ │ │ │ • Executive Summary.pdf │ │ │ │ • Business Plan.pdf │ │ │ │ │ │ │ │ Round 2 Docs (Semi-finalist): │ │ │ │ • Updated Business Plan.pdf │ │ │ │ • Pitch Video.mp4 │ │ │ │ • Technical Whitepaper.pdf │ │ │ └───────────────────────────────────────────────────────────────┘ │ │ │ │ Voting closes in: 0:45 │ └───────────────────────────────────────────────────────────────────────┘ ┌─ VOTING PANEL (Numeric Mode: 1-10) ─────────────────────────────────┐ │ │ │ How would you rate this project overall? │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 1 2 3 4 5 6 7 8 9 10 │ │ │ │ ○ ○ ○ ○ ○ ○ ○ ● ○ ○ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ Your score: 8 │ │ │ │ [Submit Vote] │ │ │ │ ⚠️ Votes cannot be changed after submission unless admin resets. │ └───────────────────────────────────────────────────────────────────────┘ ┌─ YOUR VOTES THIS SESSION ───────────────────────────────────────────┐ │ [✓] 1. AquaClean Tech — Score: 9 │ │ [✓] 2. BlueCarbon Solutions — Score: 8 │ │ [ ] 3. OceanSense AI — Not voted yet │ │ [ ] 4. MarineTech Innovations — Waiting │ │ [ ] 5. CoralGuard — Waiting │ │ [ ] 6. DeepSea Robotics — Waiting │ └───────────────────────────────────────────────────────────────────────┘ ``` This is an extremely detailed 900+ line implementation document covering the Live Finals round type with complete technical specifications, UI mockups, API definitions, service functions, edge cases, and integration points. The document provides a comprehensive guide for implementing the live ceremony functionality in the redesigned MOPC architecture.