From a weekly order to a public record
A tournament bracket is easy to draw. The harder job begins when the first real matchup arrives.
Someone has to record what was actually ordered, collect votes without exposing the administration panel, freeze the result, advance a winner, and leave behind something a visitor can understand later. If those pieces live in separate forms and private screens, the bracket may technically work while the story disappears.
That was the gap in Brackets, our cheerfully over-serious system for settling food arguments by tournament. The first full pizza season proved that the competition model worked: eight pizzas, thin and deep-dish divisions, seven decided matchups, and one champion. It also showed where the product stopped short. Weekly orders were awkward to save as a pair, and the public bracket showed who advanced without explaining why.
The fix turned each matchup into a small, durable record.

Save the matchup, not two unrelated forms
A pizza matchup has two sides, but it is one event. The old administration screen treated each weekly order as its own submission. That made the interface harder to read and created an ugly failure mode: one side could save while the other failed validation or upload processing.
The replacement is one "Save both weekly orders" action. Both orders are checked together for the right matchup, current revision, valid fields, and allowed state. If one side fails, neither side changes. Newly uploaded files are reclaimed when the transaction rolls back, while photos referenced by current or historical records remain in place.
This sounds like a database detail. It is really a product rule expressed in code. An administrator thinks, "I am recording this week's matchup," not, "I am performing two independent edits and hoping they agree afterward." The transaction now matches that mental model.
The same change forced us to confront a practical limit. Each side can include a photo, so a request must hold two valid images plus the form data around them. The application and proxy limits were raised together to a bounded 12 MiB. That is enough for two 5 MiB uploads without quietly turning the route into an unlimited file drop.
Let private voting stay private
Opening a vote and creating an invitation are deliberately separate actions. Starting the voting period does not mint a secret link behind the scenes. The administrator chooses when to create an invitation, sees it once, and gets a copy button with clear feedback.
That small copy button found one of the more interesting bugs in the release. If invitation A was replaced by invitation B while an earlier clipboard request was still finishing, the interface could show stale "Link copied" feedback for the wrong invitation. The final implementation ties the feedback to the current invitation and ignores late clipboard completions.
The links themselves use a URL fragment. Browsers do not send fragments in ordinary HTTP requests, so the invitation value is exchanged through a deliberate same-origin action rather than appearing in server access logs or query strings. Revocation and expiry still invalidate the voter session. Ballots remain separate from administration, and final advancement remains an explicit decision.
The lesson is less about clever token handling than about keeping state transitions visible. "Voting is open," "an invitation exists," "results are final," and "a winner was advanced" are different facts. Combining them would save a click and make the system harder to reason about.
Publish the evidence after the decision
Before this release, the public bracket was a useful graph with a missing middle. It showed the contenders and winner, but each pairing was static text. A visitor could see that Barnabys advanced past Village Inn, yet not see the pizzas ordered that week, the seven criterion averages, or the weighted totals behind the decision.
Every populated public pairing now links to a matchup recap. A decided recap shows the division and round, both places and pizzas, public order facts, finalized scores, weighted totals, and the explicitly advanced winner. The first thin-division matchup, for example, records a final weighted total of 8.43 against 7.86. That figure is no longer trapped in an administrator's workflow.
The public projection is intentionally smaller than the private record. Draft brackets, unpublished places, malformed result snapshots, and mismatched routes return nothing useful. Open voting does not leak live aggregates. Invitations, voter sessions, ballots, private notes, storage keys, and superseded history are not part of the recap query.
This separation matters because "make it public" should not mean "serialize the private object and hide a few fields in the template." The public recap has its own allowlisted shape. It contains what a reader needs to understand the matchup and nothing from the machinery used to run it.

Verification should follow the story
The release was tested at several levels: 148 automated tests, a clean Svelte check, a production build, a dependency audit, and 64 built-application HTTP checks. An independent review also caught the clipboard race before the final deployment.
The live check mattered just as much. The production matchup page showed one atomic form instead of two legacy forms. A temporary invitation used the fragment-only route, stayed out of reloaded HTML, and was revoked after the test. The completed pizza season remained seven-for-seven decided, and the public bracket linked all seven matchups to readable recaps.
That last check is the one I like most. The database can be consistent and the tests can be green while the product still fails to tell its story. Opening the public season, following a matchup, and reading the result proved that the work reached the reader.
Brackets now treats a weekly food argument as one coherent object from order entry through public record. Both sides save together. Voting stays private. Results become public only after they are final. The bracket no longer ends at a line connecting two names; each line opens into the evidence behind it.
