Glassbox had a misleading screen at exactly the moment a judging platform needs to be clearest.
An organizer published the results. The server correctly refused further score changes. But the judge’s page still looked editable.
A judge could spend time revising a comment, press Update, and discover that the action had already become impossible.
This turned up during the build’s browser walkthrough. The fix is small enough to read in one commit. The underlying lesson reaches beyond hackathons: a state transition has to change both what the system permits and what it tells people they can do.
Building around the moment results become final
Glassbox is an open-source, self-hosted submission and judging portal built for DOGFOOD 2026. It uses Node 24, TypeScript, built-in SQLite, and server-rendered HTML, with no runtime npm dependencies.
Its most consequential button is Publish.
Before publication, judges can revise their assigned scores and organizers can inspect raw and calibrated rankings. After publication, people need a stable answer to “What were the results?”
The implementation makes that transition explicit. Publication computes the ranking, serializes an aggregate results snapshot, hashes it with SHA-256, stores it, marks the event published, and appends an audit entry in a transaction. Public results are served from the stored snapshot rather than recalculated on every request. See the publication service.
That gives the public page a specific artifact to display. It also keeps individual judges’ score rows out of the public payload.
The browser had missed that transition.
A regression test for the promise on the screen
The fix derives the score form’s locked state from the event phase. Once published, the page displays a locked notice, disables the score fields, and removes the save and recusal actions.
The backend remains responsible for rejecting forbidden writes. The disabled form makes the restriction understandable before someone wastes effort.
The regression test follows the transition:
- Open an assigned score page as a judge and confirm that saving is available.
- Publish the event through the organizer API.
- Fetch the judge page again.
- Check for the locked notice and disabled fieldset.
- Check that save and recusal controls are gone.
That test checks rendered HTML. The build’s separate browser retest also records checking that the fields could no longer be changed and that the saved score stayed unchanged.
Both checks matter. An API test can prove that an invalid write fails while missing a screen that encourages the write.
A ranking also needs an explanation
Publication freezes a number, but the number still needs a defensible path from the ballots.
Glassbox converts each review into a weighted total and offers shrunk z-score calibration. A judge’s mean and spread are blended with the event-wide pool, using three pseudo-reviews by default. Sparse judging histories therefore receive more pooling than large ones.
The important product choice is that the adjusted ranking does not erase the raw ranking. Organizers can compare them, inspect judge offsets and spreads, and export review-level values. The dashboard, exports, and publication use the same normalization implementation.
This is a model with assumptions. If a judge sees unusually strong projects, high scores can resemble leniency. Showing the adjustment makes that assumption inspectable; it does not establish that every ranking change is correct.
What the evidence establishes
The committed acceptance report records seven passing checks and verifies the claimed T1 and T2 tiers. Those checks cover public gallery behavior, deadline rejection, score access isolation, and CSV export. This is the build’s historical report; no new test run is claimed here.
They do not establish production readiness or real-world fairness.
Glassbox remains a single-node SQLite application. It has no community voting, email delivery, or built-in login rate limiting. The hash chain detects inconsistent edits, but an administrator controlling the database could rewrite the chain; there is no external trust anchor.
The reusable lesson
For an irreversible-looking moment such as publication, test three things together: the forbidden write, the newly rendered screen, and the artifact people will rely on afterward.
That small “Update” button exposed a gap between a correct backend and an understandable product. Closing that gap made the judging lifecycle much easier to defend.
Built for DOGFOOD 2026, organized by Hackathon Raptors.
#HackathonRaptors
Top comments (0)