Persisted, but never shown
Daily Challenge players can now replay for a better score, deduped by a token that never leaves the browser. A design review caught the feature quietly failing one of its own stated goals.
The Daily Challenge has always stopped a player after one attempt, an “already played today” screen with nothing left to do but wait for tomorrow’s puzzle. Looking into what it would take to lift that turned up something unexpected: nothing on the server actually enforced the one-play limit. The pipeline that validates each attempt was already stateless and scoped only to the day’s puzzle, not to any single play, so a determined player could already replay endlessly. The limit was a client-side screen, not a rule. Replay is now the point instead of an accident: a player can play today’s puzzle again, chasing a better score, and the leaderboard keeps only their best attempt for the day.
Same player, same day, remembered only today#
Deduping “best attempt” needs a way to recognize the same player across their own replays without building anything that identifies them across days, a constraint the privacy page already commits to in writing. Three ways to establish that were on the table: a signed cookie, tamper-resistant but a direct contradiction of the page’s “no cookies” statement; a server-derived hash of IP and user agent, needing no client cooperation but treating IP as personal data and merging different players sharing a rotating IP into one identity, a worse failure than under-deduping; and a random token the client generates itself and rotates every day. The token won, sent alongside each leaderboard submission and grouped at read time rather than upserted at write time, so every attempt still lands its own permanent row for later analysis and the leaderboard’s own query keeps only the best-scoring row per token. The per-IP submission rate limit was loosened well past its old cap to accommodate genuine rapid replay, rather than being redesigned around the new token; the token exists to deduplicate, not to throttle abuse.
Four slices, one merge#
The full spec split cleanly along its own testing plan: server-side dedup and the rate limit change, client-side persistence of the day’s token and a new all-time-best record, the wiring that turns a finished attempt into an offered replay instead of a dead end, and the recap screen’s copy and layout. Each slice extended exactly one existing test file rather than starting a new one, mirroring the boundaries the spec had already drawn. All four landed in a single pull request and merged together, closing the whole effort in one event rather than the slice-by-slice review that an earlier, larger localization effort had used.
A number nobody could see#
Stored, tested, and calculated correctly, and still invisible to the player it was for.
The fix added a third, stronger headline tier above the ordinary “beat your best” framing:
const titleText = $derived.by((): string => {
if (isNewAllTimeBest) return ui.newAllTimeBestTitle(score);
if (isNewBestOfDay) return ui.newBestOfDayTitle(score);
if (!isRevisit && score < bestOfDayScore) {
return ui.beatYourBestWithScoreTitle(score, bestOfDayScore);
}
return ui.beatYourBestTitle(bestOfDayScore);
});
Proving a score instead of guessing it#
Testing the dedup query needed fixtures with a specific, sometimes counterintuitive relationship: a row with a lower total score but a higher single best-word score than the row that should win. Hand-computing Norwegian and English word scores to hit that relationship was slow and error-prone, so the tests instead ran against a live dev server, posting candidate word sequences to the real scoring endpoint and reading back whatever score actually came out until a combination satisfied the needed inequality. Multi-word combinations turned out to plateau silently on occasion, ending the simulated game partway through even on a successful response, so the actual returned score had to be checked every time rather than assumed to add up the way the individual words suggested.
The all-time-best fix meant new headline copy in both English and Norwegian, and the Norwegian wording was still waiting on its own read before anyone could call the tone right. On the data side, the same token now feeds the internal stats dashboard’s own replay numbers, how often players replay and by how much they improve, a figure that will only ever cover players who choose to submit a score rather than everyone who plays. That the dedup can be sidestepped entirely by a player willing to reset it was accepted on purpose: the goal was a tidy leaderboard, not proof of identity, and a fresh legitimate attempt looks exactly like any other.