fix(daily): reset Endfield's European day on the server it's on

Endfield has two server groups, not three: Europe is served off the
Americas machine on a fixed UTC-5, so a European player's day rolls at
09:00 UTC. We were resetting it six hours early, at 03:00, which ticked
the wrong box every morning between those two instants.

Adds GameMeta.resetOffsets, a sparse per-region override, and threads an
optional `game` through every day-key function. Per region rather than
per game on purpose: a blanket offset would drag Asia — which does have
its own Endfield server — onto the Americas clock, moving day keys for
readers who never had the bug. A regression test pins Asia's output as
identical to before.

Day keys are localStorage keys, so this re-labels ticks logged between
03:00 and 09:00 UTC by European Endfield players, one day backward. No
tick is deleted and past days stay editable, but a streak can read as
broken for a day. That is the cost of correcting a wrong reset; leaving
it wrong is worse.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-16 20:37:47 +02:00
co-authored by Claude Opus 5
parent 6c5546ba53
commit e6e4f7085a
10 changed files with 259 additions and 64 deletions
+8 -1
View File
@@ -83,7 +83,7 @@ src/client/ React app, service worker, manifest
state/ progress, daily log, ignores, prefs, sort — all localStorage
scripts/ build-feed.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches)
serve.ts static server + /api/health
test/ 257 tests
test/ 301 tests
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
snapshots/ current page per source, rewritten by refresh — see its README
```
@@ -150,6 +150,13 @@ Two more key spaces have the same property, for the same reason:
- **Game-day keys** (`dayKey`) are `YYYY-MM-DD` in *server-reset space*, not UTC — the day rolls at
04:00 local server time. They are storage keys *and* they are compared with `<` and sorted, so the
format is fixed. Changing the reset hour or the offsets moves every reader's streak by a day.
A game whose server map differs lists the affected regions in `resetOffsets` (`games.ts`) —
Endfield serves Europe off the Americas machine, so `europe` is UTC-5 there and its reset is
09:00 UTC, not 03:00. Keep that override **per region**: a blanket per-game offset drags the
regions that do have their own server onto someone else's clock. Every day-key function takes an
optional `game`**anything reading or writing a tick must pass it**, or it writes under one
clock and reads under another. A day that drops out of `dailyDays` renders no pip, so a tick on it
becomes unreachable; check real fixture windows before changing an offset.
The sanitizer at the ingest boundary recomputes an event ID only when a sanitized title actually
changed *and* the ID was minted the standard way. If a change to it starts moving IDs on real