Author: Cyrus Wong (黃俊彥) — Google Developer Expert - Cloud (AI), Senior Lecturer at HKIIT
1. Problem Statement: Defeating "Fake Video Playback", Proxy Classmates & Static Screen Decoys
In a 3-hour coding lab or a 100-student hybrid lecture, passive screen-share telemetry and traditional door-scan attendance have three classic blind spots:
- The Static Screen Decoy (AFK): A student opens VS Code or a lecture slide deck, starts screen sharing, and walks away from their workstation for 45 minutes while their screen capture continues uploading identical frames.
- The "Give Password to a Classmate + Play a Pre-Recorded Video" Proxy Lab Exploit: Even worse, an absent student gives their account password to a friend sitting in the computer lab. That friend logs into the absent student's account on an adjacent lab PC, starts screen sharing, and plays a pre-recorded coding video in full-screen so the screen-capture feed looks like someone is typing code! When a standard web-based Bingo quiz pops up on that second monitor, the classmate tries to click an answer on their friend's behalf.
- The "Tap Student ID & Run" Lecture Evasion Exploit (Why Attendance Must Be Verifiable Anytime): In modern higher education institutions , lecture halls often provide RFID card readers or kiosks at the entrance where students tap their physical student badge or enter an attendance code right at 9:00 AM. Once tapped, the campus system logs them as "Present"—whereupon disengaged students immediately leave the lecture hall to go to the cafeteria, hang out, or leave campus! A single attendance tap at the beginning of a 3-hour session is fundamentally vulnerable. Lecturers need the ability to take attendance anytime during class (mid-lecture, unannounced, or near the end).
Conversely, during a formal proctored test or examination, full-screen monitor capture, webcam/microphone investigation, and tamper-evident evidence retention must be strictly enforced.
Solving both modes effectively requires addressing three operational challenges:
-
Formative Teaching vs. High-Stakes Proctored Exams: During ordinary lectures and labs, instructors want lightweight, interactive comprehension checks that keep students engaged in learning; during formal tests and exams, invigilators require strict full-monitor capture (
displaySurface === 'monitor'), continuous webcam/audio investigation, and write-once Cloud Storage locks. - Distinguishing Honest Learning Mistakes from Absence: If an instructor sends a pop quiz during a lecture and docks attendance marks when a student selects the wrong answer, students are penalized for struggling with new technical material even though they were sitting attentively at their desks!
- Eliminating Idle Polling Bills: Running a Cloud Scheduler cron job every 60 seconds to scan pending attendance timers across 50 classes burns tens of thousands of idle Firestore reads per month.
In Part 9 of our Google Developer Expert series, we detail how we engineered two complementary classroom integrity systems:
-
Gamified "Classroom Bingo" Active Presence Verification (
BingoModal.jsx,BingoQuestionBankModal.jsx,BingoResultsView.jsx,LectureQrBingoModal.jsx, andbingoFlows.js) featuring 4 Sourced Verification Types (including 2 QR Code Attendance Modes to take attendance anytime), a Gemini 4 Argon (gemini-4-argon) Question Bank AI Studio, Fisher-Yates positional bias elimination, and an event-driven Non-Punitive Two-Strike State Machine powered by Google Cloud Tasks (dispatchBingoRetryTask). -
Zero-Trust Exam Mode Lockdown & Court-Ready Forensic Dossiers (
ExamReadinessWizard.jsx,storage.rules,getStudentVideoPlaybackUrl.js, andprocessReportJob.js), enforcing full-monitor capture (displaySurface === 'monitor'), write-once Cloud Storage metadata locks (isExam == 'true'), Student Portal confidentiality shielding during exam windows, and 1-click export of formal Microsoft Word (.docx) & RFC 4180 CSV Incident Dossiers.
2. Diagram 1: Four Classroom Bingo Verification Modes & The Gemini 4 Question Bank Studio
When a lecturer triggers a Classroom Bingo round (either manually with one click, via a Staggered Jitter Queue that challenges random subsets of students every few minutes so neighbours cannot shout out "Option B!", or via Auto-Bingo during live screen broadcasts), resolveBingoQuestion in functions/ai_flows/bingoFlows.js supports four distinct verification types:
-
Type 1 — Predefined Question Bank (
question_bank): ($0.00 / Zero AI Tokens) Instant, zero-cost comprehension checks pulled from an instructor-curated question bank (bulk-imported from Moodle Aiken format or batch-generated via Gemini 4 Argon). -
Type 2 — Multimodal Teacher Broadcast Screen + Subtitle AI Fusion (
teacher_screen): A single multimodal Gemini call (~$0.00015 USDtotal cohort cost!) that fuses the teacher's live broadcast frame (liveFrame) with the last 5 minutes of spoken captions (liveSubtitles) and an anti-duplication rule. -
Type 3 — Personalized Student Lab Screen Inspection (
student_screen): Targeted Gemini vision evaluation of an individual student's workstation monitor to verify active lab coding work and defeat static window decoys or pre-recorded video playback. -
Type 4 — WebAuthn Biometric QR Attendance (2 QR Modes to Take Attendance Anytime): Designed specifically to defeat the "Tap Student ID Card & Run" lecture evasion exploit. Instead of relying on a one-time badge tap at the entrance door, instructors can trigger attendance verification at any moment during the lesson via two complementary QR modes:
-
Mode 4A: Workstation Mobile Passkey QR (
mobile_passkey): Displayed on the student's lab PC monitor. The student must scan it with their paired smartphone and authenticate via on-device Face ID / Fingerprint. A proxy classmate sitting at an adjacent PC cannot click or verify on their behalf because WebAuthn enforces hardware device binding (1-Phone = 1-Student). -
Mode 4B: Lecture Hall Dynamic Rotating Projector QR (
lecture_passkey_qr): Displayed on the central classroom/lecture hall projector screen. The QR code dynamically rotates every 10 seconds with HMAC time-signatures. Students physically sitting in the hall point their smartphone camera at the projector screen to sign in. Students who tapped their card at the door and left cannot scan the projector, and remote friends cannot use a forwarded photo because the QR code expires and rotates in 10 seconds!
-
Mode 4A: Workstation Mobile Passkey QR (
Engineering Highlights of the Bingo Question Engine
-
Universal Model Agency in the Question Bank Studio (
BingoQuestionBankModal.jsx): Before class, teachers can openBingoQuestionBankModal.jsx, type a lesson topic (or customize the system prompt), and select 🧠 Gemini 4 Argon (gemini-4-argon), ⚡ Gemini 3.8 Flash (gemini-3.8-flash), or 🪶 Gemini 3.5 Flash-Lite (gemini-3.5-flash-lite) to generate 1 to 15 multiple-choice questions at once—or bulk-import existing quizzes in standard Moodle Aiken text format. -
Eliminating LLM Positional Bias (
shuffleOptionsAndCorrectIndex): When generating 4-option multiple-choice questions, LLMs exhibit a strong statistical bias toward placing the correct answer at index0(Option A) or index1(Option B). Every question generated bygenerateBingoQuestionBankorresolveBingoQuestionpasses through a deterministic Fisher-Yates permutation (shuffleOptionsAndCorrectIndex) that randomizes option order and updatescorrectIndexaccordingly. -
Multimodal Teacher Screen + 5-Minute Live Subtitle Fusion (
Mode 2): Inteacher_screenmode,resolveBingoQuestionmakes a single multimodal Gemini call for the entire 50-student class (~$0.00015 USDtotal cohort cost!). Even better, it fuses the teacher's live broadcast frame (classes/{classId}/screenBroadcast/liveFrame) with the last 5 minutes of the teacher's spoken commentary (classes/{classId}/liveSubtitles/current) and injects an Anti-Duplication Rule containing the last 5 asked questions (isDuplicateQuestionwith $80\%$ lexical Jaccard similarity rejection) so Gemini never repeats the same question while the teacher stays on a single slide! -
Zero-Leak Client Payload Sanitization: A savvy computer science student will immediately open Chrome DevTools (
Network/Firestoretab) when a quiz pops up. InbingoFlows.js,correctIndexis written exclusively toclasses/{classId}/bingoRecords/{bingoId}(which students are blocked from reading infirestore.rules). The payload pushed tostudentProperties/{studentUid}.activeBingocontains onlyquestion,options, andexpiresAtMillis—making client-side answer inspection impossible.
Production Code 1: Genkit Structured Schema Generation & Fisher-Yates Positional De-Biasing (bingoFlows.js)
Let's inspect generateBingoQuestionBank and shuffleOptionsAndCorrectIndex in functions/ai_flows/bingoFlows.js:
// Source: functions/ai_flows/bingoFlows.js
export async function generateBingoQuestionBank({ topic, count = 5, customPrompt = null, classId = null, model = null }) {
const targetModel = model || AI_MODEL;
const safeCount = Math.min(Math.max(Number(count) || 5, 1), 15);
const prompt = customPrompt
? customPrompt.replace(/\{\{topic\}\}/g, topic || '').replace(/\{\{count\}\}/g, String(safeCount))
: `You are a computer science instructor preparing quick-check multiple choice questions for a lab/lecture.
Topic / Lesson Material: "${topic}".
Generate exactly ${safeCount} multiple-choice questions testing immediate comprehension.
Each question MUST have exactly 4 options and a correctIndex (0, 1, 2, or 3).`;
const { response, modelUsed } = await generateWithResilience({
prompt,
output: {
schema: z.object({
questions: z.array(
z.object({
question: z.string().describe('The question prompt'),
options: z.array(z.string()).length(4).describe('4 multiple choice options'),
correctIndex: z.number().min(0).max(3).describe('Index of the correct option (0-3)'),
})
),
}),
},
}, targetModel);
const shuffledQuestions = response.output.questions.map((q) => {
const { options, correctIndex } = shuffleOptionsAndCorrectIndex(q.options, q.correctIndex);
return { ...q, options, correctIndex };
});
return { questions: shuffledQuestions };
}
export function shuffleOptionsAndCorrectIndex(options, correctIndex) {
const safeCorrectIndex = (typeof correctIndex === 'number' && correctIndex >= 0 && correctIndex < options.length)
? correctIndex
: 0;
const items = options.map((opt, idx) => ({ text: opt, isCorrect: idx === safeCorrectIndex }));
// Unbiased Fisher-Yates shuffle eliminates LLM Option-A bias
for (let i = items.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[items[i], items[j]] = [items[j], items[i]];
}
return {
options: items.map((item) => item.text),
correctIndex: items.findIndex((item) => item.isCorrect),
};
}
The 3 Prompts Powering Gamified Classroom Bingo (bingoFlows.js)
Here are the exact prompts executed across the three AI-powered Bingo modes in functions/ai_flows/bingoFlows.js and admin/prompts/images/:
1. Question Bank Studio Prompt (Bingo Question Bank Generator.md / generateBingoQuestionBank)
Used when a teacher opens BingoQuestionBankModal.jsx and generates a reusable bank of 1–15 multiple-choice questions with gemini-4-argon, gemini-3.8-flash, or gemini-3.5-flash-lite:
# Bingo Question Bank Generator
You are an instructor preparing quick-check multiple choice questions for a classroom lab or lecture.
Topic / Lesson Material: "{{topic}}".
## Rules & Requirements
1. Generate exactly {{count}} multiple-choice questions testing immediate comprehension.
2. Each question MUST have exactly 4 options and a `correctIndex` (0, 1, 2, or 3).
3. Keep questions concise, practical, and unambiguous.
4. Directly test core principles, code patterns, or command usage from the stated topic.
2. Mode 2 Prompt — 1-to-Many Teacher Broadcast Screen + 5-Minute Live Subtitle Fusion + Anti-Duplication Rule (resolveBingoQuestion)
When the lecturer triggers a teacher_screen Bingo check during a live broadcast, resolveBingoQuestion combines the current classes/{classId}/screenBroadcast/liveFrame image, the last 5 minutes of classes/{classId}/liveSubtitles/current spoken commentary, and the last 5 asked questions from classes/{classId}/bingoRecords into a single multimodal call for the entire cohort:
You are an invigilator verifying student attention during a live lecture/explanation.
Analyze the instructor's shared screen image alongside their recent spoken commentary/captions:
Recent Instructor Spoken Commentary: "{{captionContext}}"
Formulate a 4-option multiple-choice question testing whether a student was actively following the instructor's lecture explanation.
The question can reference what the instructor just explained verbally, key details visible on the shared screen, or the connection between them.
ONE option MUST be the true detail from the screen or verbal explanation.
THREE options MUST be plausible but incorrect distractors.
Respond with JSON matching the schema:
{
"question": "Which file was the instructor editing?",
"options": ["main.py", "test.js", "app.py", "utils.go"],
"correctIndex": 0,
"observedEvidence": "main.py is open in VS Code with Python code displayed"
}
CRITICAL ANTI-DUPLICATION RULE:
The instructor may be holding or explaining the same slide/window. The following questions were ALREADY recently asked to students:
1. "{{recentQuestion1}}"
2. "{{recentQuestion2}}"
3. "{{recentQuestion3}}"
You MUST NOT repeat or rephrase any of the above questions.
You MUST choose a completely DIFFERENT detail, spoken concept, code line, toolbar item, question angle, or UI element that was NOT covered above.
3. Mode 3 Prompt — Personalized Student Lab Screen Presence Verification (Bingo Student Screen Presence Question Generator.md)
Used during hands-on computer labs when each student is working at their own pace on their own workstation:
# Bingo Student Screen Presence Question Generator
You are a classroom invigilator checking student presence in a computer lab.
Analyze this student's computer screen screenshot.
## Rules & Requirements
1. Formulate a 4-option multiple-choice question testing immediate awareness of their screen state (e.g., active editor file, command in terminal, running app, or lab guide step).
2. ONE option MUST be the true visible detail on the student's screen.
3. THREE options MUST be plausible distractors.
4. Respond with valid JSON matching the schema:
{
"question": "What task or window is currently active on your screen?",
"options": ["Running test suite in terminal", "Reading documentation in browser", "Editing config file", "Debugging breakpoint"],
"correctIndex": 0,
"observedEvidence": "Terminal running pytest is in the foreground"
}
3. Diagram 2: The Non-Punitive Two-Strike State Machine with Google Cloud Tasks & 3-Color Attendance Heatmap
What happens after the high-priority BingoModal.jsx (z-index: 2147483647, accompanied by an optional Web Audio API chime) appears on the student's screen with a 45-second countdown timer?
Our Two-Strike State Machine (submitBingoResponse, enqueueBingoRetryTask, and handleDispatchBingoRetry in functions/ai_flows/bingoFlows.js) strictly decouples academic comprehension from physical desk presence:
-
Case 1 — Correct Answer (
passed): The student earns base points (100 pts), a speed bonus (up to+50 ptsscaled by remaining time ratio), and a top-3 podium rank bonus (+50 / +30 / +20 pts). Any prior strike is cleared (strikeNumber = 0). -
Case 2 — Wrong Answer (
failed_incorrect): Because the student actively clicked an option within the 45-second window, physical presence at the workstation is 100% verified! The student receives instant visual feedback on the correct answer, zero strikes are triggered, and zero attendance minutes are deducted. -
Case 3 — Strike 1 Timeout (
missed_timeoutonstrikeNumber === 1): What if the student stepped away to the washroom for 2 minutes? Instead of docking attendance on a single missed check,submitBingoResponsesetspendingRetryBingo: trueand enqueues a delayed task in Google Cloud Tasks (locations/asia-east2/functions/dispatchBingoRetryTask) scheduled forretryDelayMinuteslater (configurable 1–15 minutes, default 3 minutes, $0.00 idle polling cost). -
Case 4 — Strike 2 Consecutive Timeout (
missed_timeoutonstrikeNumber >= 2): When the Cloud Task wakes up after 3 minutes, it performs pre-flight idempotency checks (verifying the class is still active and the teacher hasn't cancelled the session) and dispatches a personal Strike 2 Follow-Up Challenge. If the student responds to Strike 2 (even with a wrong answer!), presence is recovered. Only if the student misses both Strike 1 and Strike 2 consecutively does the backend calculate the elapsed unverified minutes ($\Delta t = t_{\text{Strike 2}} - t_{\text{Strike 1}}$) and write anattendanceAdjustmentsdeduction record!
Production Code 2: Event-Driven Two-Strike Cloud Tasks Scheduling & Elapsed AFK Deduction (bingoFlows.js)
Here is the exact Two-Strike state transition and deterministic Cloud Task deduplication logic inside functions/ai_flows/bingoFlows.js:
// Source: functions/ai_flows/bingoFlows.js
// 3. Handle Two-Strike Attendance Protocol & Irregularities
if (result === 'passed') {
updatePayload.strikeNumber = 0;
updatePayload.pendingRetryBingo = false;
} else if (result === 'failed_incorrect') {
// Student was present at desk, answered wrong. Do NOT dock attendance.
updatePayload.lastInattentiveAt = FieldValue.serverTimestamp();
} else if (result === 'missed_timeout') {
const currentStrike = Number(record.strikeNumber) || 1;
if (currentStrike === 1) {
// Strike 1: Schedule follow-up retry in configured delayMinutes (default: 3 mins)
updatePayload.pendingRetryBingo = true;
updatePayload.retryBingoScheduledAtMillis = Date.now() + (retryDelaySeconds * 1000);
updatePayload.priorMissedBingoId = bingoId;
updatePayload.retryDelayMinutes = retryDelayMinutes;
// Enqueue to Google Cloud Tasks for serverless event-driven execution ($0 idle polling)
await enqueueBingoRetryTask({
classId,
studentUid,
priorBingoId: bingoId,
delaySeconds: retryDelaySeconds,
});
} else if (currentStrike >= 2) {
// Strike 2 Confirmed Absence! Calculate elapsed unverified window (t_Strike2 - t_Strike1)
updatePayload.pendingRetryBingo = false;
const priorBingoId = record.priorBingoId;
let startMillis = record.issuedAtMillis - (retryDelaySeconds * 1000);
if (priorBingoId) {
const priorDoc = await db.doc(`classes/${classId}/bingoRecords/${priorBingoId}`).get();
if (priorDoc.exists && priorDoc.data()?.issuedAtMillis) {
startMillis = priorDoc.data().issuedAtMillis;
}
}
const endMillis = Date.now();
const deductedMinutes = Math.max(1, Math.round((endMillis - startMillis) / 60000));
// Record attendance adjustment (renders as Red Bitmask Code 2 in AttendanceView.jsx)
await db.collection(`classes/${classId}/attendanceAdjustments`).add({
classId,
studentUid,
startTime: new Date(startMillis),
endTime: new Date(endMillis),
startMillis,
endMillis,
deductedMinutes,
voidedMinutesCount: deductedMinutes,
reason: `Missed 2 consecutive Bingo checks (AFK/Decoy)`,
strike1BingoId: priorBingoId || 'strike_1',
strike2BingoId: bingoId,
createdAt: FieldValue.serverTimestamp(),
});
}
}
In AttendanceView.jsx, every minute of a lesson is encoded as a 5-State Minute Bitmask (0–4):
-
1/3/4(🟢 Green): Verified Active Presence (Screen Shared / Passkey Verified / Teacher Override). -
2(🔴 Red): Voided AFK Window (attendanceAdjustmentsfrom 2 consecutive missed Bingo checks, showing exact tooltip timestamps and a 1-Click Teacher Excuse / Restore (excused: true) button). -
0(⚪ Grey): Offline / Disconnected.
4. Diagram 3: Zero-Trust Exam Mode Lockdown & Court-Ready .docx Forensic Dossiers
While Classroom Bingo keeps daily lectures and labs lively and non-punitive, formal practical examinations require Zero-Trust Assessment Security. When a class enters a scheduled examPeriods window (or lessonType === 'exam'), four defense-in-depth layers activate automatically across the client browser, Cloud Run FFmpeg, Cloud Storage Security Rules, and our Signed URL Broker:
5. Deep Dive into the Zero-Trust Exam Code: Storage Metadata Locks, Signed URL Shielding & .docx Dossiers
Production Code 3: Write-Once Exam Metadata Locks in Cloud Storage (storage.rules) & Signed URL Broker (getStudentVideoPlaybackUrl.js)
During a practical exam, students must not be able to delete uploaded screenshots/videos or replay their own exam recording afterward to leak exam paper questions to another class section.
We enforce this at two independent boundaries:
-
Direct Cloud Storage Rules (
storage.rules): Students havewrite-only access toscreenshots/andirregularities/(under2 MB/5 MBsize limits), zerodeletepermissions, andvideos/{classId}/{videoId}explicitly denies student reads wheneverresource.metadata.isExam == 'true':
// Source: storage.rules
match /videos/{classId}/{videoId} {
// Allow authenticated teachers to read any video in a class.
// Students can only read non-exam videos that belong to them.
allow read: if request.auth != null && (
request.auth.token.role == 'teacher' ||
(
resource.metadata != null &&
'studentUid' in resource.metadata &&
resource.metadata.studentUid == request.auth.uid &&
(!('isExam' in resource.metadata) || resource.metadata.isExam != 'true')
)
);
allow delete: if request.auth != null && request.auth.token.role == 'teacher';
}
-
Server-Side Signed URL Broker (
getStudentVideoPlaybackUrl.js): Even if a video was compiled before an instructor retroactively added anexamPeriodsrange to the class settings,evaluateStudentRecordingsAccesschecks both thejobData.isExamflag andisTimestampInExamPeriods(jobData.startTime, examPeriods, timeZone):
// Source: functions/media_processing/getStudentVideoPlaybackUrl.js
export function evaluateStudentRecordingsAccess({
examPeriods = [],
policy = 'always_enabled',
releaseDate = null,
jobData = {},
timeZone = 'Asia/Hong_Kong',
}) {
// Recordings for exam/test periods or stamped as exam are strictly confidential and not shared
const isExam = Boolean(
jobData.isExam ||
jobData.lessonType === 'exam' ||
isTimestampInExamPeriods(jobData.startTime, examPeriods, timeZone) ||
isTimestampInExamPeriods(jobData.createdAt, examPeriods, timeZone)
);
if (isExam) {
return {
allowed: false,
reason: 'Screen recordings for exam or test periods are confidential and not shared with students to protect assessment questions.',
};
}
// Also enforces 'disabled' and 'delayed_release' policies
return { allowed: true };
}
Production Code 4: Court-Ready Microsoft Word (.docx) & RFC 4180 CSV Forensic Dossier Compiler (processReportJob.js)
When an academic integrity board reviews a suspected exam irregularity, raw JSON logs in a database are difficult for non-technical committee members to inspect.
When an instructor clicks 📄 Export Formal Dossier (.docx) in IncidentDossierExportModal.jsx (accessed via IrregularitiesView.jsx), processReportJob.js (1 vCPU, 2 GiB RAM, 540s timeout) queries all time-scoped irregularities, audio transcripts (from Edge Whisper + Gemini verification), and screenshots for the class, classifies each student's risk level (HIGH RISK, MODERATE RISK, NORMAL / CLEAN), and uses the docx and csv-stringify libraries to compile a downloadable Microsoft Word (.docx) Incident Dossier complete with embedded ImageRun evidence snapshots, 3D head-pose/gaze telemetry tables, and verbatim speech transcripts:
// Source: functions/media_processing/processReportJob.js
export const processReportJob = onDocumentCreated(
{
document: 'reportJobs/{jobId}',
region: FUNCTION_REGION,
cpu: 1,
memory: '2GiB',
timeoutSeconds: 540,
},
async (event) => {
// ... Fetches classData, studentProperties, time-scoped irregularities, audioTranscripts & screenshots
const studentReports = targetStudentIds.map((uid) => {
const student = studentMap[uid] || { id: uid };
const studentIrregs = irregularities.filter((ir) => ir.studentUid === uid || ir.userId === uid);
const studentAudios = audioTranscripts.filter((a) => a.studentUid === uid);
const studentScreens = screenshots.filter((s) => s.studentUid === uid);
const highSeverityCount = studentIrregs.filter((i) => i.severity === 'high' || i.type === 'multiple_faces').length;
const totalIncidents = studentIrregs.length;
const riskLevel = highSeverityCount > 2 ? 'HIGH RISK' : totalIncidents > 3 ? 'MODERATE RISK' : 'NORMAL / CLEAN';
return { student, irregularities: studentIrregs, audios: studentAudios, screenshots: studentScreens, totalIncidents, highSeverityCount, riskLevel };
});
// Compiles structured Microsoft Word (.docx) with embedded ImageRun evidence & RFC 4180 CSV
// Uploads to reports/{classId}/{jobId}/ and updates reportJobs/{jobId} with signed download URLs
}
);
The 3 Zero-Trust Exam Mode Invigilation Prompts Feeding the .docx Dossier
During a proctored exam, whenever the Tier-1 Browser Edge Investigator or teacher-triggered cloud sweeps escalate a visual or acoustic anomaly, these three prompts in admin/prompts/ evaluate the evidence and invoke Genkit tools (recordIrregularity, recordAudioIrregularity, recordAudioAudit) to populate the .docx forensic dossier:
1. Screen Irregularity & Tool-Calling Invigilator (admin/prompts/images/AI invigilator.md)
**You are an AI invigilator. Your task is to analyze a student's screen, take appropriate action using tools, and then report the actions you took in a final text summary.**
## Guidelines
* **Minor Distraction:** Briefly looking away, non-exam apps visible but not in use.
* **Significant Distraction:** Actively using another app, phone use, talking to someone, unauthorized communication or AI tools during an exam.
## Action & Response Protocol
1. **Analyze the screen.**
2. **If there are no issues, your final answer MUST be the exact text:** "All students are focused and on track."
3. **If there is an issue:**
* First, call all necessary tools in parallel. You **MUST** call `sendMessageToStudent` and `sendMessageToTeacher`. If the issue is a **Significant Distraction**, you **MUST ALSO** call `recordIrregularity`.
* Then, as your final answer, you **MUST** provide a detailed summary of the issue and the actions you took.
## Output Guidelines
* When referring to a student in any output, you **MUST** use the format 'email (uid)'.
2. Cloud Fallback Face & Gaze Invigilator (admin/prompts/images/Cloud Fallback Face & Gaze Invigilator.md)
# Cloud Fallback Face & Gaze Invigilator
Analyze this classroom invigilation webcam photo of student {{studentEmail}} (UID: {{studentUid}}, class: {{classId}}).
Determine the student's face presence and gaze orientation.
## Detection Rules
1. Is there a human face present? If no face is visible, status is 'no_face'.
2. Are multiple faces present? If more than 1 person is in frame, status is 'multiple_faces'.
3. Is the student looking forward/centered at their computer screen? If their head or gaze is turned significantly away (left, right, looking at another device, looking away from exam), status is 'looking_away'.
4. If the student is sitting normally facing their screen/work, status is 'normal'.
## Output Format
Respond ONLY with valid JSON in this exact structure:
{
"faceStatus": "normal" | "looking_away" | "no_face" | "multiple_faces",
"confidence": 0.95,
"reason": "Brief explanation"
}
3. Live Rolling Audio Collusion & Whisper Invigilator (admin/prompts/audios/AI Voice Invigilator (Live Rolling Audio).md)
# AI Voice Invigilator (Live Rolling Audio)
You are an expert AI Classroom Invigilator analyzing a live student audio stream during an examination.
## Context
- Class ID: {{classId}}
- Student UID: {{studentUid}}
- Student Email: {{studentEmail}}
- Audio Duration: Rolling 20–45s window
## Audio Transcript
"""
{{transcript}}
"""
## Instructions
1. Analyze the transcript for unauthorized speech, exam collusion, question recitation, answer trading, or whispering to external parties/devices.
2. If multi-speaker discussion, unauthorized talking, or answer recitation is detected:
- Call the `recordAudioIrregularity` tool with `title`, `message`, `riskLevel` ('low' | 'medium' | 'high'), `speakerCount`, and `transcriptSnippet`.
3. Call the `recordAudioAudit` tool with `verdict` ('clean_exam' | 'suspicious_collaboration' | 'whisper_detected' | 'background_noise' | 'inconclusive'), `speakerCount`, `summary`, and `transcript`.
What's Next in Part 10?
We have now explored the entire pedagogical and multimodal AI stack—from Browser Edge AI (Gemma 4 E2B, MediaPipe, LiteRT Whisper) to Cloud Broadcasts, Gemini 4 Argon Video Grading, Capstone Mobile Supervision, and Zero-Trust Exams.
In Part 10 (our Series Finale), we look under the hood at Day-2 Institutional Operations: how we govern AI and storage budgets using Live Google Cloud Billing SKU Sync (syncGeminiPricing), 9-Category Event-Driven Storage Quota Enforcement (storageQuota.js), 4-Stage Zero-Orphan Cascading Class Purge (onClassDocDeleted), and 1-Command Terraform Infrastructure-as-Code (./setup-new-project.sh).



Top comments (1)
Thank you for sharing this thorough architectural breakdown, Cyrus. Transitioning from idle Cloud Scheduler polling to an event-driven Cloud Tasks state machine—while enforcing hardware-bound verification via WebAuthn—demonstrates a very well-engineered approach to both cost optimization and system integrity.
As a minor architectural consideration for the 10-second HMAC-rotating QR code (lecture_passkey_qr): in high-concurrency lecture halls, network jitter or minor clock skew between the projector client and the verification backend can lead to false-negative rejections right at the rotation boundary. Implementing a single-interval tolerance window on the server side (validating both the active TOTP/HMAC window and the immediately preceding 10-second step) would preserve strong anti-replay guarantees while significantly improving reliability under peak concurrent scans.
I look forward to reading the next installment in the series.