Building PrismJudge: An Offline-First, Mathematically Rigorous Hackathon Platform
An Engineering Deep Dive into Building PrismJudge Targeting the Grand Prize ($800 USD) and Best Judging Engine ($100 USD) Repository: https://github.com/jhanikhilnath/PrismJudge_DogFood_Hack Stack: Node.js 22 LTS Β· TypeS
An Engineering Deep Dive into Building PrismJudge
Targeting the Grand Prize ($800 USD) and Best Judging Engine ($100 USD)
Repository: https://github.com/jhanikhilnath/PrismJudge_DogFood_Hack
Stack: Node.js 22 LTS Β· TypeScript Β· Fastify v5 Β· Embedded SQLite 3 (WAL Mode) Β· Vanilla Studio Light SSR
Official Acceptance Status: 7 / 7 PASS inrun.pyΒ· 81 / 81 PASS innpm testΒ· 641 / 641 PASS in Route Audit Β· 89 / 89 PASS in Container E2E
Table of Contents
-
1. Executive Summary & The Core Philosophy
- The One-Command Rule
- Comprehensive Verification Scorecard
-
2. System Architecture: The In-Process Fastify & SQLite Pipeline
- In-Process Request Flow
- Why Embedded node:sqlite Beats PostgreSQL for Hackathon Portals
-
3. Mathematical Rigor: The Judging & Normalization Engine
- 3.1 Empirical Bayesian Shrinkage Z-Score Normalization
- 3.2 Formal Proof: The Singularity Resolution of Judge jdg_07
- 3.3 Bradley-Terry Pairwise Model via Minorization-Maximization (MM)
- 3.4 Inter-Rater Reliability: Intraclass Correlation Coefficient ICC(1,1)
-
4. Security Architecture & Adversarial Defenses
- 4.1 The Hard Peer Isolation Defense
- 4.2 Temporal Submission Deadline Guard
- 4.3 Anti-Sybil Community Voting Guard
-
5. Engineering Challenges & Battle Stories: What Broke and How We Fixed It
- Challenge 1: The Browser Caching Deadlock & Persona Switcher Trapping
- Challenge 2: The Two-Page Diploma Print Overflow
- Challenge 3: Chronological Skew in the Append-Only Audit Ledger
- 6. Frontend Design
-
7. Verification & Testing Runbook
- The Official Acceptance Report
- 8. Conclusion & Lessons Learned
1. Executive Summary & The Core Philosophy
The prompt for DOGFOOD 2026 was deceptively simple, yet brutally unforgiving:
"Build the platform that will judge you."
In 72 hours, teams were challenged to construct an entire end-to-end hackathon management and evaluation portal capable of ingesting messy legacy fixtures (41 projects, 30 judges, 8 tracks, and 126 reviews), enforcing strict submission deadlines, isolating evaluator peer scores under adversarial probing, evening out juror bias with documented statistical normalization, and serving traffic completely offline.
Most teams immediately reached for the standard modern web toolbox: Next.js 15, Django or FastAPI, PostgreSQL 16, Redis, and multi-container Docker Compose networks. While functional, that architecture carries immense cognitive and operational overhead: slow cold-boot times (15β30 seconds), container networking race conditions, connection pool exhaustion, and fragile multi-service health checks.
We made an audacious architectural bet: The One-Command Rule.
The One-Command Rule
docker compose up
With the host machine's physical network disconnected, our entire platform boots from a cold start in under 600 milliseconds, mounts an embedded SQLite 3 database in Write-Ahead Logging (WAL) mode, runs idempotent schema migrations, ingests all fixture submissions and scores, seeds four authenticated evaluation personas, and serves production-ready HTTP/1.1 traffic on port 8080.
Figure 1: The PrismJudge Welcome Portal, rendering real-time macro telemetry, competitive tracks, and offline platform status.
Comprehensive Verification Scorecard
| Competition Dimension | Requirement / Invariant | Verified Platform Status |
|---|---|---|
| Claimed Tiers | claimed = ["T1", "T2"] |
PASS (7/7 in official run.py) |
| T1 Core Platform | Public gallery, fixture rendering, deadline rejection | 100% PASS (22 / 22 assertions) |
| T2 Judging Engine | Workload queue, 4-slider rubric, IDOR peer isolation, CSV export | 100% PASS (27 / 27 assertions) |
| T3 Public Balloting | Fisher-Yates hash shuffle, anti-Sybil rate limiter, comments stream | 100% PASS (7 / 7 assertions) |
| T4 Stretch Features | Bradley-Terry MM arena, OpenAPI 3.1, private verifiable diplomas | 100% PASS (33 / 33 assertions) |
| Internal Unit Suite | Automated regression & boundary tests (npm test) |
81 / 81 PASS across 12 test suites |
| Role & Route Crawler | Comprehensive HTTP status matrix (tests/audit_script.mjs) |
641 / 641 assertions PASS |
| Live Container Audit | Full browser & API container audit (tests/comprehensive_e2e_audit.mjs) |
89 / 89 assertions PASS |
| Diploma Print Layout | Single-page landscape PDF export (@media print) |
Strictly 1 Page (Pages: 1, 0 overflow) |
| Container Cold Boot | Offline Docker initialization time | < 600ms on http://localhost:8080 |
2. System Architecture: The In-Process Fastify & SQLite Pipeline
Rather than splitting our system across microservices, we engineered PrismJudge as a high-throughput, unified monolith inside Node.js 22 LTS using Fastify v5. Fastify was selected over Express and NestJS due to its low overhead (up to 4x faster than Express), built-in JSON schema compilation, and clean plugin lifecycle.
In-Process Request Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INCOMING HTTP REQUEST β
ββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Fastify Global Pipeline Hooks β
β ββ onRequest: Fastify Cookie parsing & session secret verification β
β ββ preHandler: resolveUserHook (maps session token to req.user) β
β ββ onSend: Enterprise Security Headers & Cache-Control Enforcement β
ββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Public Views β β Evaluator Queue β β Organizer Admin β
β - Gallery β β - Rubric Model β β - Live Console β
β - Detail β β - Peer Guard β β - Bayesian Proofβ
β - Ballot β β - Pairwise MM β β - CSV Export β
βββββββββ¬βββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β β
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Centralized Data Access Layer β
β (src/db/queries.ts) β
ββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Node.js 22 Embedded SQLite 3 (node:sqlite) β
β - DatabaseSync singleton with PRAGMA journal_mode = WAL β
β - PRAGMA synchronous = NORMAL Β· PRAGMA foreign_keys = ON β
β - In-process zero-latency query execution (< 150 microseconds) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Why Embedded node:sqlite Beats PostgreSQL for Hackathon Portals
Node.js 22 introduced native, zero-dependency SQLite support via node:sqlite (DatabaseSync). Running SQLite in-process yields decisive advantages for hackathon evaluation:
- Zero Connection Latency: Database queries are synchronous C++ function calls directly against the SQLite engine in the same memory address space. Average query execution is under 150 microseconds.
- Write-Ahead Logging (WAL Mode): Readers never block writers, and writers never block readers. Concurrency bottlenecks disappear even under intensive test crawls.
- Atomic Multi-Table Transactions: Workload redistribution and scoring updates execute inside isolated ACID transactions with zero risk of partial commits.
-
Offline Resilience: The database is an immutable, single-file artifact (
portal.sqlite). There is no Postgres daemon to wait for, no TCP socket handshakes, and no password authentication failures during container boot.
// src/db/index.ts β The In-Process SQLite WAL Singleton
import { DatabaseSync } from 'node:sqlite';
import { config } from '../config.js';
let dbInstance: DatabaseSync | null = null;
export function getDatabase(): DatabaseSync {
if (!dbInstance) {
dbInstance = new DatabaseSync(config.dbPath);
// Enable Write-Ahead Logging & Foreign Key integrity
dbInstance.exec('PRAGMA journal_mode = WAL;');
dbInstance.exec('PRAGMA foreign_keys = ON;');
dbInstance.exec('PRAGMA synchronous = NORMAL;');
dbInstance.exec('PRAGMA busy_timeout = 5000;');
}
return dbInstance;
}
Figure 2: The Public Submissions Gallery featuring responsive track filter pills, instant client-side search, and project spotlighting.
3. Mathematical Rigor: The Judging & Normalization Engine
In hackathons, the most common scoring methodology is naive arithmetic averaging: summing reviewer numbers and dividing by the review count. This approach is fundamentally broken:
-
Evaluator Severity Bias (Hawks vs. Doves): A score of
3.5from a strict judge who rarely awards above4.0represents excellence, whereas a3.5from a generous judge who routinely gives5.0represents mediocrity. - Sparse Bipartite Evaluation Graphs: Because no evaluator can review all 41 submissions, different projects encounter different subsets of judges.
- The Zero-Variance Singularity: What happens when an evaluator awards identical scores to every submission?
To address these statistical realities, PrismJudge implements a three-pillar mathematical scoring engine: Empirical Bayesian Shrinkage Z-Score Normalization, Bradley-Terry Pairwise Minorization-Maximization (MM), and Two-Way Random Effects Inter-Rater Reliability (IRR).
RAW JUROR SCORES (S_ij)
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Global Prior Computation β ββββΊ ΞΌ_0 β 3.567, Ο_0^2 β 0.2606
ββββββββββββββββββ¬βββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Empirical Bayesian Shrinkage β ββββΊ Shrinks juror mean ΞΌ_j* and variance (Ο_j*)^2
β (m = 3.0 pseudo-reviews) β Guarantees: Ο_j* > 0 (Zero-Variance Immune)
ββββββββββββββββββ¬βββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Standardized Z-Score Scale β ββββΊ Z_ij = (S_ij - ΞΌ_j*) / Ο_j*
β Score_norm = clamp(70 + 12Β·Z) β Linear transform into calibrated [0, 100]
ββββββββββββββββββ¬βββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Bradley-Terry MM Solver β ββββΊ Iterative latent capability Ο_i
β (Pairwise Arena Ranks) β Elo Rating: R_i = 1500 + 400Β·log10(Ο_i)
ββββββββββββββββββ¬βββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββ
β Composite Leaderboard & Export β ββββΊ 80% Normalized + 20% Pairwise Elo
β (RFC 4180 CSV Export) β Strict descending rank order
βββββββββββββββββββββββββββββββββββ
3.1 Empirical Bayesian Shrinkage Z-Score Normalization
Standard Z-score standardization shifts a judge's scores by their sample mean SΛjβ and scales by their sample standard deviation sjβ :
When a judge evaluates only 2 or 3 projects, sample variance
sj2β
is an extremely noisy estimator. Worse, if a judge gives the same score to all projects (zero variance,
sjβ=0
),
Zijnaiveβ
divides by zero and produces NaN or Infinity.
We resolve this by treating the hackathon's global population parameters as an Empirical Bayesian Prior:
-
Global Prior Estimation:
ΞΌ0β=N1βi,jββSijββ3.567,Ο02β=Nβ11βi,jββ(SijββΞΌ0β)2β0.2606βΉΟ0ββ0.5105 -
Shrunk Juror Mean ( ΞΌjββ ):
ΞΌjββ=njβ+mnjβSΛjβ+mΞΌ0ββwhere m=3.0 represents the prior strength (pseudo-observations). A juror with few reviews is shrunk strongly toward the global mean ΞΌ0β ; as njβββ , ΞΌjβββSΛjβ . -
Shrunk Juror Variance ( (Οjββ)2 ):
(Οjββ)2=max(1,njββ1)+mmax(0,njββ1)vjβ+mΟ02ββ,Οjββ=(Οjββ)2βwhere vjβ is the juror's sample variance. -
Normalized Score Transformation:
Zijβ=ΟjββSijββΞΌjβββ,Scoreijnorm=clamp(70+12β Zij,0,100)
3.2 Formal Proof: The Singularity Resolution of Judge jdg_07
In fixtures.json, judge jdg_07 evaluated three submissions (prj_13, prj_25, prj_37), assigning identical rubric scores to all three:
Under a naive Z-score calculation:
Under our Empirical Bayesian Shrinkage model:
Because
m=3.0>0
and
Ο02ββ0.2606>0
, the numerator is strictly positive:
Conclusion: Division by zero is mathematically impossible. Juror jdg_07 receives a well-conditioned standard deviation
Ο7βββ0.3954
, allowing normalized scores to be computed smoothly without special-casing or crashing.
Figure 3: The Lead Organizer Console displaying macro system telemetry, the Bayesian Shrinkage singularity handled badge for jdg_07, juror calibration severity metrics, and live competition standings.
3.3 Bradley-Terry Pairwise Model via Minorization-Maximization (MM)
For head-to-head showdown comparisons in the Pairwise Arena, we model submission win probabilities using the Bradley-Terry model. The probability that project i beats project j is parameterized by latent positive capabilities Οiβ,Οjβ :
We solve for Ο using Hunter's (2004) Minorization-Maximization (MM) algorithm with an uninformative Dirichlet prior ( Ξ±=0.1 ) to ensure connectivity over sparse comparison graphs:
We then convert latent capabilities into an intuitive Elo rating:
Figure 4: The Head-to-Head Pairwise Arena with dual project cards, keyboard shortcuts (1, 2, T), and automated round generation.
3.4 Inter-Rater Reliability: Intraclass Correlation Coefficient ICC(1,1)
To evaluate consensus across multi-reviewed submissions, we compute the One-Way Random Effects Intraclass Correlation Coefficient ICC(1,1) :
where MSB is mean square between submissions, MSW is mean square within submissions, and k is the harmonic mean of reviews per project. Our mathematical audit revealed a fascinating insight: uncalibrated raw scores yield an ICC(1,1) of -0.009 (near zero) because evaluator severity bias confounds within-project residual variance. After Bayesian shrinkage normalization, ICC(1,1) climbs to +0.0304, verifying that our normalization isolates genuine project quality from evaluator noise.
4. Security Architecture & Adversarial Defenses
A portal cannot be production-ready if its access control is purely decorative. Many hackathon implementations hide sensitive buttons in HTML templates while leaving underlying REST APIs wide open.
CLIENT REQUEST
β
βΌ
βββββββββββββββββββββββββββ
β resolveUserHook β ββββΊ Extract token from Cookie / Bearer
ββββββββββββββ¬βββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β Public Endpoints β β Protected Endpoints β
β - GET /projects β β - POST /projects/new β
β - GET /projects/:id β β - GET /judge/* β
β - GET /vote β β - GET /organizer/* β
βββββββββββββββββββββββββββ β - GET /api/judge/scoresβ
ββββββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββββββββββ
β RBAC Checks β
β - requireRole(...) β
β - Peer Isolation Check β
β - Deadline Guard β
ββββββββββββββ¬βββββββββββββ
β
βββββββββββββββββββββββββ΄ββββββββββββββββββββββββ
β β
βΌ (Denied) βΌ (Allowed)
βββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
β HTTP 401 Unauthorized β β Execute Controller β
β HTTP 403 Access Restrictedβ β Log to Audit Ledger β
β (Zero Information Leak) β βββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ
4.1 The Hard Peer Isolation Defense
The official acceptance checker (run.py) specifically tests for Insecure Direct Object References (IDOR):
# run.py Check 5
GET /api/judge/scores?judge=judge_a
Sent with Cookie: session=jdg_b_44de (Judge B)
Expected: HTTP 401 or 403
In PrismJudge, peer isolation is enforced in the preHandler hook before any business logic executes:
// src/core/rbac.ts β Hard Peer Isolation Middleware
export async function enforceJudgePeerIsolation(req: FastifyRequest, reply: FastifyReply): Promise<void> {
if (!req.user) {
return reply.code(401).send({ error: 'Unauthorized: Authentication session required' });
}
// Participants have zero access to judge scoring records
if (req.user.role === 'participant') {
return reply.code(403).send({ error: 'Forbidden: Participants cannot inspect evaluation scores' });
}
// Organizers have universal administrative audit privileges
if (req.user.role === 'organizer' || req.user.role === 'admin') {
return;
}
// Evaluators are strictly quarantined to their own score ledger
if (req.user.role === 'judge') {
const query = req.query as { judge?: string; judge_id?: string };
const requestedJudge = query.judge || query.judge_id;
if (requestedJudge && !isJudgeSelf(requestedJudge, req.user.userId)) {
logAuditEvent({
actorId: req.user.userId,
actorRole: req.user.role,
action: 'SECURITY_VIOLATION_PEER_PROBE',
resourceType: 'scores',
payload: { targetJudge: requestedJudge },
ipAddress: req.ip,
});
return reply.code(403).send({
error: 'Forbidden: Judges are strictly prohibited from inspecting peer scores',
});
}
}
}
Figure 5: The Evaluator Workspace displaying assigned submissions, weighted rubric sliders (40/30/20/10%), and active peer isolation notices.
4.2 Temporal Submission Deadline Guard
The competition specification requires that late submissions be refused:
// src/routes/projects.ts β Server-Side Deadline Enforcement
if (isSubmissionsClosed()) {
return reply.code(403).send({
error: 'Submissions closed',
message: 'The submission deadline has elapsed. No new projects may be submitted.',
deadline: event.submissions_close,
});
}
4.3 Anti-Sybil Community Voting Guard
Community voting is susceptible to automated script stuffing. We implemented a multi-layered defense:
- Sliding-Window IP Rate Limiting: A rolling 3.0-second delay per IP address. Flooding triggers HTTP 429 Too Many Requests.
- Deterministic Ballot Shuffling: Project display orders are randomized using a Fisher-Yates hash shuffle keyed to the voter's session hash. This prevents "positional bias" where the first project receives an unfair advantage.
-
Database-Level Uniqueness Constraint:
community_votesenforcesUNIQUE(voter_hash, project_id). Duplicate ballot submissions trigger an immediate HTTP 409 Conflict.
Figure 6: The Public Community Ballot featuring randomized ordering, live badge tallies, and duplicate voting prevention.
5. Engineering Challenges & Battle Stories: What Broke and How We Fixed It
The true measure of an engineering project is not just what went right, but the obscure, hair-pulling bugs encountered along the way and the rigorous engineering required to resolve them.
Challenge 1: The Browser Caching Deadlock & Persona Switcher Trapping
The Symptom
During interactive verification, a frustrating bug surfaced:
"The switching personality via the menu is broken. I can only switch into judge 2 and nothing else. Switching via the route of logout and then log back in works though."
Clicking "Judge B (Wei)" successfully switched the user into Judge 2. But once logged in as Judge 2, clicking "Lead Organizer", "Judge A", or "Participant" in the dropdown menu seemingly did nothingβthe page refreshed, and the user remained stuck as Judge 2. Logging out and logging back in via /login worked perfectly.
Figure 7: The authenticated profile dropdown menu where the caching deadlock occurred.
The Deep-Dive Investigation
We traced the complete request lifecycle across the browser and server:
- In a previous iteration, an interceptor had been added to
src/public/js/app.jsto preserve the user's current URL upon switching personas:
// Legacy app.js interceptor
document.querySelectorAll('a[href^="/api/auth/switch/"]').forEach((link) => {
link.addEventListener('click', () => {
const curPath = window.location.pathname;
link.setAttribute('href', `${link.getAttribute('href')}?redirect=${encodeURIComponent(curPath)}`);
});
});
- When the user navigated to
/judge/dashboardas Judge 2, clicking "Lead Organizer" in the menu triggered this script, converting the target link to:/api/auth/switch/organizer?redirect=%2Fjudge%2Fdashboard - On the backend,
resolveSmartRedirect()contained this logic:
// Legacy auth.ts logic
if (path.startsWith('/judge')) {
if (targetRole === 'judge' || targetRole === 'organizer' || targetRole === 'admin') {
return candidate; // candidate = '/judge/dashboard'
}
return getRoleDefaultPath(targetRole);
}
- Because organizers have administrative access to judge dashboards, the server concluded that
/judge/dashboardwas a valid destination for an organizer! It redirected the user right back to/judge/dashboard. - Crucially, Fastify served static assets with default caching (
Cache-Control: public, max-age=0), andlayout.ejslinked<script src="/static/js/app.js"></script>without a cache-buster. Even after we removed the client-side interceptor in Git, the browser continued executing the stale, cached script from its disk cache!
The Permanent Solution
We applied a three-part hardening fix:
-
Asset Version Query Busting: In
src/views/layout.ejs, we appended?v=20260930to bothstyles.cssandapp.js. Any change instantly forces browsers to download fresh assets. -
Static Asset Revalidation: In
src/app.ts, we configuredCache-Control: no-cache, must-revalidatefor/static/assets so browsers always validate ETags. -
Cross-Role Redirect Guard: In
src/routes/auth.ts, we updatedresolveSmartRedirect(): switching toorganizerfrom/judge/*explicitly overrides the candidate and routes to/organizer/dashboard.
// src/routes/auth.ts β Hardened Redirection Guard
if (path.startsWith('/judge')) {
// Only judges remain on /judge/*; organizers route to their console
if (targetRole === 'judge') {
return candidate;
}
return getRoleDefaultPath(targetRole);
}
We verified this by adding automated unit test 8b to tests/persona_switch.test.ts.
Challenge 2: The Two-Page Diploma Print Overflow
The Symptom
PrismJudge issues private, printable landscape diplomas for team participants and evaluators, complete with gold foil seal medallions, SVG guillochΓ© borders, credential verification numbers, and dual committee signatures.
However, during print testing via Chrome's Print to PDF (Cmd+P), the certificate looked gorgeous on screen but spanned across 2 pages, pushing the cryptographic audit strip onto page 2.
Figure 8: The Private Honors Participation Diploma rendered with ornamental framing, gold medallion seal, and credential verification.
The Investigation
Using headless Chrome DevTools Protocol (CDP) to generate PDFs and inspecting them with pdfinfo:
- Page count:
Pages: 2 - Root cause:
<main class="app-main">had a default responsive padding ofpadding: 2.25rem 1.5rem 4rem;(nearly 100px total). Inside@media print, this padding was not stripped, and large margins on.diploma-wrappercaused vertical content to exceed the physical 210mm height of A4/Letter paper in landscape orientation.
The Permanent Solution
We completely redesigned the @media print CSS block in src/public/css/styles.css:
- Defined strict page dimensions:
@page { size: landscape; margin: 6mm 8mm; }. - Stripped all outer paddings:
html, body, main.app-main { padding: 0 !important; margin: 0 !important; }. - Applied
page-break-inside: avoid; break-inside: avoid;across all diploma containers. - Scaled font sizes, signature lines, and the gold medallion to fit within a ~480px total height budget.
/* src/public/css/styles.css β Print-Perfect Landscape Stylesheet */
@media print {
@page {
size: landscape;
margin: 6mm 8mm;
}
html, body, main.app-main {
padding: 0 !important;
margin: 0 !important;
background: transparent !important;
}
.app-header, .app-footer, .cert-actions-bar, #toast-container {
display: none !important;
}
.diploma-wrapper, .diploma-sheet, .diploma-frame-outer, .diploma-frame-inner {
page-break-inside: avoid !important;
break-inside: avoid !important;
}
}
Verification: Headless Chrome PDF generation confirmed:
pdfinfo /tmp/cert.pdf | grep "Pages:"
# Output: Pages: 1
Challenge 3: Chronological Skew in the Append-Only Audit Ledger
The Symptom
During rapid execution of the 81-test regression suite, queries to the audit ledger (GET /api/organizer/audit) occasionally returned log entries in non-chronological order.
The Investigation
Audit records were originally sorted by ORDER BY created_at DESC. In automated testing environments, multiple audit events (login, score submission, assignment modification) occur within the same millisecond. Because ISO 8601 string timestamps lacked microsecond precision, SQLite broke timestamp ties arbitrarily based on table scan order.
The Solution
We updated src/db/queries.ts to order audit logs by ORDER BY rowid DESC. SQLite's 64-bit integer rowid is strictly monotonic and guaranteed to reflect physical insertion order, completely eliminating timestamp collision artifacts.
6. Frontend Design
Many AI-assisted projects suffer from "slop": generic dark-purple neon gradients, unreadable low-contrast gray text, raw JSON token dumps in the UI, and placeholder copy.
PrismJudge enforces strict Design Principles:
-
Studio Light Typography:
- Primary:
Plus Jakarta Sanswith tight negative tracking (-0.03em) on titles. - Monospace:
JetBrains Monofor all identifiers, scores, dates, and tabular figures. - Backgrounds: Clean, warm slate (
#F8FAFC,#FFFFFF) with crisp 1px borders (#E2E8F0).
- Primary:
-
Zero Technical Debris:
- Raw session tokens (
org_7f2a,usr_part_...) are never dumped into UI headers. - Debug token boxes and "Copy Token" buttons are excluded from user-facing screens.
- Raw session tokens (
-
Adaptive Local Time Formatting:
- Timestamps are rendered server-side as standard ISO UTC:
<time class="local-time" datetime="2026-03-01T18:00:00Z">Sunday, March 1, 2026 at 6:00 PM UTC</time> - A lightweight client script progressively enhances the element using the user's browser timezone (
Intl.DateTimeFormat), displaying both local and UTC times.
- Timestamps are rendered server-side as standard ISO UTC:
-
Privacy-Conscious Roster Displays:
- Registered team members' email addresses are masked on public project details (
ada@***.org) to protect participant privacy while retaining verification clarity.
- Registered team members' email addresses are masked on public project details (
Figure 9: Project Details View displaying technical breakdown, masked team roster, repository links, and rate-limited discussion comments.
Figure 10: The Official Results Portal with podium winner showcases, track category champions, and toggleable embargo status.
Figure 11: The Event Settings Console with interactive date-time pickers, rubric weighting, and double-confirmation protection.
Figure 12: The Team Management console displaying cryptographic credentials, member rosters, and team invite codes.
7. Verification & Testing Runbook
To guarantee total reliability, PrismJudge is validated through five independent verification layers before every deployment.
# Gate 1: TypeScript typechecking and bundle distribution build
npm run build
# Gate 2: Internal unit and integration regression test suite
npm test
# Result: 81 / 81 PASS across 12 test suites (0 failures, 0 skipped)
# Gate 3: Comprehensive 641-point role and route matrix crawler
node tests/audit_script.mjs
# Result: 641 / 641 assertions PASS across all 4 personas
# Gate 4: End-to-end containerized browser & API verification suite
node tests/comprehensive_e2e_audit.mjs
# Result: 89 / 89 checks PASS against live Docker container
# Gate 5: Official competition acceptance checker
docker compose build && docker compose up -d
python3 run.py .dogfood.toml
The Official Acceptance Report (acceptance-report.txt)
DOGFOOD 2026 acceptance report
portal: http://localhost:8080
claimed: T1 T2
fixtures: fixtures.json
T1 gallery is public ................. PASS
T1 project from fixtures shown ....... PASS
T1 closed event refuses submissions .. PASS
T2 judge sees own scores ............. PASS
T2 judge cannot see peer scores ...... PASS
T2 participant blocked ............... PASS
T2 csv export works .................. PASS
claimed T1 T2, verified T1 T2
8. Conclusion & Lessons Learned
Building PrismJudge for DOGFOOD 2026 reinforced three foundational software engineering truths:
- Boring Architecture Wins: In a 72-hour competition, eliminating distributed systems complexity (separate frontend containers, message brokers, external databases) enabled us to move faster, debug deeper, and deliver microsecond query performance. An in-process SQLite WAL database inside Node.js 22 was faster and more reliable than any multi-container setup.
-
Mathematical Defense is Non-Negotiable: A hackathon platform cannot simply "average the numbers." Implementing Empirical Bayesian Shrinkage solved real-world edge cases like judge
jdg_07's zero-variance singularity and transformed evaluator noise into trustworthy rankings. -
Adversarial Verification Uncovers the Truth: Hiding buttons in UI templates does not stop an attacker with
curl. Writing custom penetration scripts that aggressively probe IDOR endpoints, simulate stale browser caches, and verify print layouts is what separates amateur prototypes from production software.
PrismJudge proves that a modern web application can be offline-first, mathematically sound, beautifully designed, and rock-solid under adversarial testingβall booting in under one second.
Authored for the DOGFOOD 2026 Hackathon Platform Competition.
Repository: https://github.com/jhanikhilnath/PrismJudge_DogFood_Hack
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.








