fix(ingest): retire a day-precision row on the clock the app reads it on

Three parsers decide currency against ctx.now, because their page has no
"ongoing" heading worth trusting: bawiki.ts, and the Fate/Grand Order and
Infinity Nikki branches of fandom.ts. All three compared Date.parse(endsAt) to
now, and all three publish day-precision ends — so the comparison was against
the 00:00Z placeholder, which § Domain rules says is not an instant and nothing
may read literally.

clockFor already knows that and resolves such a boundary to the reset opening
that game-day on the reader's server. The parsers did not, so the feed retired
a row up to nine hours before the app, the countdown and the game all agreed it
was over. On the pinned Infinity Nikki fixture, "Inspiration Burst" left the
feed at 2026-08-22T01:00Z while clockFor still called it live for an American
reader until 09:00Z, and the page said it ran to 03:59 server time. The reader
does not see a stale row; they see the deadline they were counting down to
disappear on its last day, which is the silent drop this codebase treats as the
dangerous failure.

latestBoundaryMs answers clockFor's question for the last region rather than
one reader's, so a row is history only once it is history everywhere. It follows
a game's own server map and reset hour for the same reason clockFor does —
Endfield and Reverse: 1999 both move. Nothing stored changes: this is one
comparison, not a resolved boundary written into the feed, and no expected.json
moves.

Being generous by nine hours costs an expired row at the bottom of a list.
Being strict costs a live one.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-19 04:35:19 +02:00
co-authored by Claude Opus 5
parent 0f9c141ea5
commit c782a265ca
7 changed files with 148 additions and 10 deletions
+7 -1
View File
@@ -119,7 +119,7 @@ src/client/ React app, service worker, manifest
theme.ts — dark or light, and what a game hue reads as on each
scripts/ build-feed.ts, build-static.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches)
serve.ts static server + /api/health
test/ 706 tests
test/ 711 tests
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
snapshots/ current page per source, rewritten by refresh — see its README
```
@@ -154,6 +154,12 @@ These come from how gacha games actually schedule things, and they cause most bu
*reading* of the printed date, not an invented time, and it changes nothing stored: the feed,
every event ID and the parsers are untouched, so it is one resolution at the point where the
region is finally known.
The same clock governs the *other* end of the pipeline. A parser whose page has no trustworthy
"ongoing" heading decides currency against `ctx.now` itself, and comparing the 00:00Z placeholder
to `now` retires a row hours before `clockFor` calls it over for anybody — the reader watches a
deadline they were counting down to vanish on its last day. So `latestBoundaryMs` answers the same
question for the *last* region, and `bawiki.ts` and two branches of `fandom.ts` ask it. Nothing
stored changes: it is one comparison, not a resolved boundary written to the feed.
- **Patch cycles are ~6 weeks.** Any event over 180 days is a parse error, not a long event. The
validator and the tests both reject it.