Record the Honkai Impact 3rd source and what its dates are worth

SOURCES.md § 13 declined this game's only known source partly because every
boundary was a week bucket, and we have now built a different source with that
same objection standing. Leaving that contradiction unwritten is how a later
pass rediscovers the trade the expensive way, so § 14 records the decision
itself: the conduct evidence, which three of § 13's four objections arustats
answers, which one it does not, and that any source stating a date per event
retires it. § 13 is marked superseded rather than rewritten — its verdict on
marisaimpact.com still stands and was not re-tested.

AGENTS.md gets the same warning where a parser author will hit it, because the
estimate is not visible from the code: the numbers look like every other
day-precision date in the project.

Also corrects the counts this game made false — nine parsers, twenty sources,
nineteen games. The "stopped working at eighteen" passages are left alone: they
date the settings-panel redesign, and bumping them would falsify the history
rather than update a count.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-27 01:28:05 +02:00
co-authored by Claude Opus 5
parent c272d9e93d
commit 7864ba6241
5 changed files with 132 additions and 13 deletions
+54 -9
View File
@@ -37,8 +37,8 @@ A web app that aggregates live and upcoming events across popular gacha games, p
calendar, sorts them by end date or by what the reader is partway through, tracks day-by-day calendar, sorts them by end date or by what the reader is partway through, tracks day-by-day
progress on events that repeat daily, and lets a user mark events completed. progress on events that repeat daily, and lets a user mark events completed.
**Status: working app, refreshing itself on a schedule.** Schema, eight parsers, nineteen sources across **Status: working app, refreshing itself on a schedule.** Schema, nine parsers, twenty sources across
eighteen games, the full interface, offline support, a static server, a Docker image and CI all exist and nineteen games, the full interface, offline support, a static server, a Docker image and CI all exist and
are tested. The refresh runner (`bun run refresh`) fetches, caches raw snapshots and rebuilds the are tested. The refresh runner (`bun run refresh`) fetches, caches raw snapshots and rebuilds the
feed; `.github/workflows/refresh.yml` runs it twice a day and commits only when a page actually feed; `.github/workflows/refresh.yml` runs it twice a day and commits only when a page actually
changed. The SQLite layer and the review queue are still specified in `docs/` but not built, so the changed. The SQLite layer and the review queue are still specified in `docs/` but not built, so the
@@ -144,7 +144,7 @@ src/client/ React app, service worker, manifest
theme.ts — dark or light, and what a game hue reads as on each 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) scripts/ build-feed.ts, build-static.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches)
serve.ts static server + /api/health serve.ts static server + /api/health
test/ 831 tests test/ 898 tests
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
snapshots/ current page per source, rewritten by refresh — see its README snapshots/ current page per source, rewritten by refresh — see its README
``` ```
@@ -196,10 +196,13 @@ These come from how gacha games actually schedule things, and they cause most bu
year, month, or end. `readColumnTable` drops a row it cannot date. An omitted event is a year, month, or end. `readColumnTable` drops a row it cannot date. An omitted event is a
recoverable disappointment; a confidently wrong date is the failure this product exists to prevent. recoverable disappointment; a confidently wrong date is the failure this product exists to prevent.
- **Parsers are keyed by site, not game.** One `game8` parser serves nine sources and `fandom` four; - **Parsers are keyed by site, not game.** One `game8` parser serves nine sources and `fandom` four;
`wikigg`, `akwiki`, `bawiki`, `holodoriwiki`, `iopwiki` and `stellasorawiki` serve one each — the first two share a host family `wikigg`, `akwiki`, `bawiki`, `holodoriwiki`, `iopwiki`, `stellasorawiki` and `arustats` serve one
and have entirely different templates, and the last two are both Miraheze wikis whose page each — `wikigg` and `akwiki` share a host family and have entirely different templates, and
templates have nothing in common. Adding a source for a known site is one `SOURCES` entry; a new `bawiki`, `holodoriwiki` and `stellasorawiki` are all Miraheze wikis whose page templates have
site is a parser module. nothing in common. `arustats` is the odd one out and the only parser here that does not read
markup at all: that site server-renders its schedule into a Next.js `__NEXT_DATA__` blob, so the
parser reads JSON and the rendered grid is ignored. Adding a source for a known site is one
`SOURCES` entry; a new site is a parser module.
- **A source may publish more than one region's schedule.** Arknights' wiki lists CN and Global on - **A source may publish more than one region's schedule.** Arknights' wiki lists CN and Global on
every row, five months apart. Publish the one our readers are on and skip the row that lacks it — every row, five months apart. Publish the one our readers are on and skip the row that lacks it —
a CN date on a Global calendar is a confidently wrong date, not a near miss. a CN date on a Global calendar is a confidently wrong date, not a near miss.
@@ -309,11 +312,11 @@ Sources are community wikis. Treat them as a guest would:
the obligation. What makes it defensible is what it does *not* change: conditional headers still go the obligation. What makes it defensible is what it does *not* change: conditional headers still go
out, so a page that has not moved costs the host a `304` rather than a re-serve, and per-host out, so a page that has not moved costs the host a `304` rather than a re-serve, and per-host
spacing, robots, the one-request-per-source rule and the no-retry rule all still apply. Prefer it spacing, robots, the one-request-per-source rule and the no-retry rule all still apply. Prefer it
with `--only`: forcing nineteen sources to re-ask a question they answered an hour ago is the with `--only`: forcing twenty sources to re-ask a question they answered an hour ago is the
behaviour this bullet exists to prevent, whatever flag authorised it. The run names every source it behaviour this bullet exists to prevent, whatever flag authorised it. The run names every source it
asked early, and a run that was due anyway is never reported as forced. asked early, and a run that was due anyway is never reported as forced.
- **Space requests to one host**, honouring its `Crawl-delay` and defaulting to 2s. Nine of the - **Space requests to one host**, honouring its `Crawl-delay` and defaulting to 2s. Nine of the
nineteen sources are game8.co pages, so the per-source floor alone still permits one cycle to arrive twenty sources are game8.co pages, so the per-source floor alone still permits one cycle to arrive
as nine back-to-back requests to a single site — which is the shape an edge network throttles, and as nine back-to-back requests to a single site — which is the shape an edge network throttles, and
what a burst looks like from the far end regardless of our intent. what a burst looks like from the far end regardless of our intent.
- Send `If-None-Match` / `If-Modified-Since`; treat `304` as "skip, unchanged". - Send `If-None-Match` / `If-Modified-Since`; treat `304` as "skip, unchanged".
@@ -365,6 +368,7 @@ re-litigated each pass:
| `guardian-tales.fandom.com` | **Declined.** Parses fine and contains no 2026 date at all — newest dated entry is 2025. The `bluearchive.fandom.com` failure again: parses cleanly to nothing live | | `guardian-tales.fandom.com` | **Declined.** Parses fine and contains no 2026 date at all — newest dated entry is 2025. The `bluearchive.fandom.com` failure again: parses cleanly to nothing live |
| `blhx.fandom.com`, `azurlane-archive.fandom.com` | **Declined.** The two Fandom alternatives to the declined koumakan wiki are dead archives — `Event_Calendar` stops in **2021**, and the archive wiki's headings have nothing under them. Azur Lane still has no source | | `blhx.fandom.com`, `azurlane-archive.fandom.com` | **Declined.** The two Fandom alternatives to the declined koumakan wiki are dead archives — `Event_Calendar` stops in **2021**, and the archive wiki's headings have nothing under them. Azur Lane still has no source |
| Aether Gazer | **Do not build.** The developer confirmed no further content updates after 23 July 2026, with store listings removed 17 October 2026. The wiki dates nothing anyway — `Event_Guide_List` is an image gallery. A lane that will be empty by winter | | Aether Gazer | **Do not build.** The developer confirmed no further content updates after 23 July 2026, with store listings removed 17 October 2026. The wiki dates nothing anyway — `Event_Guide_List` is an image gallery. A lane that will be empty by winter |
| `arustats.com` (Honkai Impact 3rd) | **Built** (2026-08-27), and the only source here that publishes **estimated** dates — see § Honkai Impact 3rd below before touching it. `robots.txt` is `Allow: /` for `*` with `/hi3/*` and `/en-us/*` named explicitly, no `Disallow`, no `Content-Signal`, `Crawl-delay: 1`. Solves the per-version URL problem `marisaimpact` failed: `/en-us/hi3/timeline` answers `307` to the live version |
| `marisaimpact.com` (Honkai Impact 3rd) | **Declined** (2026-08-19). Conduct is clear — its `robots.txt` is comments only, with no directive and no `Content-Signal`, and the page answers our own `User-Agent` with a `200`. The data is the problem: the schedule is a grid of week columns headed `Estimated date for Regional Servers` under a page that says `Based on CN server`, it states **no year anywhere**, and it lives at a per-version URL — `/calendar89` is v8.9 and expires on 20 August, with no stable route to the current one. `docs/SOURCES.md` § 13 | | `marisaimpact.com` (Honkai Impact 3rd) | **Declined** (2026-08-19). Conduct is clear — its `robots.txt` is comments only, with no directive and no `Content-Signal`, and the page answers our own `User-Agent` with a `200`. The data is the problem: the schedule is a grid of week columns headed `Estimated date for Regional Servers` under a page that says `Based on CN server`, it states **no year anywhere**, and it lives at a per-version URL — `/calendar89` is v8.9 and expires on 20 August, with no stable route to the current one. `docs/SOURCES.md` § 13 |
| game8.co hubs for Black Beacon, Brawl Stars, Destiny: Rising, Diablo Immortal, Epic Seven, Fire Emblem Shadows, Gundam UC Engage, Mongil: Star Dive, Pokémon Champions, Pokémon UNITE, Tower of Fantasy | **Declined** (2026-08-19). All thirteen hubs in that sweep exist and answer `200`; these eleven have no usable schedule. Six are abandoned wikis whose newest page is 20212025 — the Infinity Nikki failure mode, a source that parses perfectly and publishes history. Gundam's calendar prints ends with no starts, so no event ID; Pokémon UNITE is fresh but its template fails `canParse`, which is the check working. Per-game evidence in `docs/SOURCES.md` § 12 | | game8.co hubs for Black Beacon, Brawl Stars, Destiny: Rising, Diablo Immortal, Epic Seven, Fire Emblem Shadows, Gundam UC Engage, Mongil: Star Dive, Pokémon Champions, Pokémon UNITE, Tower of Fantasy | **Declined** (2026-08-19). All thirteen hubs in that sweep exist and answer `200`; these eleven have no usable schedule. Six are abandoned wikis whose newest page is 20212025 — the Infinity Nikki failure mode, a source that parses perfectly and publishes history. Gundam's calendar prints ends with no starts, so no event ID; Pokémon UNITE is fresh but its template fails `canParse`, which is the check working. Per-game evidence in `docs/SOURCES.md` § 12 |
| `game8.co/games/MementoMori`, `game8.co/games/fire-emblem-heroes` | **Assessed, not yet built** (2026-08-19) — the two live finds of that sweep, and proposals rather than decisions. MementoMori parses today with no parser change; FEH has the freshest page of any source here and needs a ninth Game8 column shape plus a ruling on the 180-day rule, which one real seven-month banner breaks. See `docs/SOURCES.md` §§ 12a12b | | `game8.co/games/MementoMori`, `game8.co/games/fire-emblem-heroes` | **Assessed, not yet built** (2026-08-19) — the two live finds of that sweep, and proposals rather than decisions. MementoMori parses today with no parser change; FEH has the freshest page of any source here and needs a ninth Game8 column shape plus a ruling on the 180-day rule, which one real seven-month banner breaks. See `docs/SOURCES.md` §§ 12a12b |
@@ -740,6 +744,47 @@ Stella Sora takes no `resetOffsets` either, and for the opposite reason to most:
outright, and the offset is `-07:00` — US Pacific, which shifts by an hour twice a year. That is the outright, and the offset is `-07:00` — US Pacific, which shifts by an hour twice a year. That is the
Fate/Grand Order problem arriving through a source that looks like it answered the question. Fate/Grand Order problem arriving through a source that looks like it answered the question.
**Honkai Impact 3rd: the one source here whose dates are estimates, and the only one whose URL
tracks itself.** `arustats.com` publishes a per-version timeline, and both halves of that sentence
matter.
- **The URL is version-less on purpose, and must stay that way.**
`/en-us/hi3/timeline` answers `307` to the live version — `/en-us/hi3/timeline/9.0` today — so the
site names its own current version server-side and the runner's `redirect: "follow"` lands on it.
That is the stable route `docs/SOURCES.md` § 13 recorded `marisaimpact.com` as lacking, and it is
why this game needs no scheduled URL edit. **Pinning `/9.0` in `SOURCES` would publish a finished
schedule as current the day 9.1 ships** — § 11's stale-source failure on a six-week clock. A test
asserts the registered URL carries no version.
- **Every boundary it publishes is an estimate, which nothing else here does.** The page schedules by
*week bucket*: an event is a bar spanning whole week columns, no event states a date of its own,
and the header over the grid reads `GLB/SEA` / `ESTIMATED WEEK`. So a start or end from this source
is the edge of a bucket the site estimated, not a date anybody announced. That is a weaker claim
than the day-precision reading Game8 and Infinity Nikki get, where the page did print a date per
event and we only declined to invent a time of day for it.
- **It was built with that cost named, not overlooked.** `docs/SOURCES.md` § 13 declined
`marisaimpact.com` on this exact ground; § 14 records the decision to take the trade here anyway
(repository owner, 2026-08-27) and what would retire it. `ESTIMATE_CONFIDENCE` (0.4, in
`parsers/arustats.ts`) is what carries the fact into the data, well below the 0.850.95 a
date-stating source earns — so a real Honkai Impact 3rd source outranks this one on `mergeEvents`
automatically, with nobody having to remember to remove it. **Do not raise that number** without
re-opening the decision.
- **Nothing on screen says "estimated" yet.** `confidence` is not read anywhere in `src/client/`, so
the reader currently sees this lane exactly as they see a wiki-sourced one. That is a known gap
rather than an oversight: saying so in the UI needs a schema field or a client change, which
§ Adding a game source calls a design question rather than adapter work. If this source stays,
that gap is the thing to close next.
- **Two shapes to know.** `startsAt` is half of every event ID, so an estimated start the site
revises moves IDs and orphans completion marks — a hazard the wiki sources do not carry. And
`endWeek` is *exclusive* and runs one past the grid to mean "to the end of the version"; an index
further out than that is skipped rather than clamped, because pinning an unreadable bar to the
version's edge would be inventing the boundary.
Also unlike everything else here, this parser reads no markup: the site is Next.js and
server-renders the schedule into `__NEXT_DATA__`, so the parser reads that JSON and ignores the
grid. `canParse` asserts the blob, the week array and its dates, so a redesign fails the source
rather than emptying the lane. The host sends neither `ETag` nor `Last-Modified`, so conditional
requests buy nothing and the 6h floor is the only thing sparing it — do not lower it here.
`scripts/refresh-sources.ts` enforces all of the above in code — the 6h floor (except under the `scripts/refresh-sources.ts` enforces all of the above in code — the 6h floor (except under the
opt-in `--force` above), one request, no retries, conditional headers, per-host spacing, robots opt-in `--force` above), one request, no retries, conditional headers, per-host spacing, robots
(failing closed when `robots.txt` cannot be read, except under the opt-in `--assume-robots-on-403` (failing closed when `robots.txt` cannot be read, except under the opt-in `--assume-robots-on-403`
+1 -1
View File
@@ -10,7 +10,7 @@ import { z } from "zod";
export const GameId = z.enum([ export const GameId = z.enum([
"genshin", "hsr", "zzz", "wuwa", "arknights", "endfield", "nte", "nikki", "p5x", "r1999", "genshin", "hsr", "zzz", "wuwa", "arknights", "endfield", "nte", "nikki", "p5x", "r1999",
"ba", "fgo", "holodori", "gfl2", "stellasora", "czn", "uma", "nikke", "ba", "fgo", "holodori", "gfl2", "stellasora", "czn", "uma", "nikke", "hi3",
]); ]);
export const EventType = z.enum([ export const EventType = z.enum([
+7
View File
@@ -56,11 +56,18 @@ Consequences worth internalising:
| `holodoriwiki` | holodori.wiki's rendered `/wiki/Events``Current Events` and `Past Events` wikitables over `Event \| Type \| Start Date \| End Date` | hololive Dreams | | `holodoriwiki` | holodori.wiki's rendered `/wiki/Events``Current Events` and `Past Events` wikitables over `Event \| Type \| Start Date \| End Date` | hololive Dreams |
| `iopwiki` | iopwiki.com's `gf-table event-period` tables — `Title \| Period (start/end) \| Server \| Type \| Comment`, one table per event and one row per server | Girls' Frontline 2 | | `iopwiki` | iopwiki.com's `gf-table event-period` tables — `Title \| Period (start/end) \| Server \| Type \| Comment`, one table per event and one row per server | Girls' Frontline 2 |
| `stellasorawiki` | stellasora.miraheze.org's front-page `Current Banners` module — `<time datetime>` pairs inside `stellasora-home-banner` blocks | Stella Sora | | `stellasorawiki` | stellasora.miraheze.org's front-page `Current Banners` module — `<time datetime>` pairs inside `stellasora-home-banner` blocks | Stella Sora |
| `arustats` | arustats.com's Next.js `__NEXT_DATA__` blob — a per-version week grid, with events carrying `startWeek`/`endWeek` **integers** rather than dates. **Estimated boundaries** — see AGENTS.md § Honkai Impact 3rd | Honkai Impact 3rd |
`wikigg` is the better shape by a distance: it emits ISO timestamps with one timer per server `wikigg` is the better shape by a distance: it emits ISO timestamps with one timer per server
region, so its events carry exact precision and real `regionEnds`. Prefer a source like that over a region, so its events carry exact precision and real `regionEnds`. Prefer a source like that over a
prose wiki when both exist, and give it a higher `priority`. prose wiki when both exist, and give it a higher `priority`.
`arustats` is the outlier on precision rather than on markup, and it is the only source in the
project that does not publish a date per event: its page schedules by week bucket and labels the grid
`ESTIMATED WEEK`, so every boundary it emits is an estimate. It carries `confidence: 0.4` for that
reason, against the 0.850.95 everything else earns, which is also what makes `mergeEvents` prefer
any real source that replaces it. Read AGENTS.md § Honkai Impact 3rd before changing it.
`fandom` is the only parser whose body is not HTML: it reads an `action=parse` JSON envelope and `fandom` is the only parser whose body is not HTML: it reads an `action=parse` JSON envelope and
takes the rendered wikitext out of `parse.text`. That is not a preference — the rendered page is takes the rendered wikitext out of `parse.text`. That is not a preference — the rendered page is
behind a Cloudflare challenge and the API is the surface the wiki's `robots.txt` allows, so the behind a Cloudflare challenge and the API is the surface the wiki's `robots.txt` allows, so the
+2 -2
View File
@@ -59,7 +59,7 @@ have a source — this table is the launch scope, not a roster to keep in sync.
Adding a game must require no change to `GachaEvent` — only a `GameId` entry, its `games.ts` Adding a game must require no change to `GachaEvent` — only a `GameId` entry, its `games.ts`
metadata, and a source registration. That is the test of whether the data model is right, and it has metadata, and a source registration. That is the test of whether the data model is right, and it has
held: the eighteen games here have cost the event schema nothing. Per-game *metadata* does occasionally held: the nineteen games here have cost the event schema nothing. Per-game *metadata* does occasionally
grow (Reverse: 1999 needed `resetHourLocal` for a 05:00 reset), which is a different file and moves grow (Reverse: 1999 needed `resetHourLocal` for a 05:00 reset), which is a different file and moves
no stored key. A game may have several sources; see `docs/INGESTION.md` § Three layers. no stored key. A game may have several sources; see `docs/INGESTION.md` § Three layers.
@@ -90,7 +90,7 @@ default `"game"` — the board every existing reader already has). Merged, the o
`endingSoonestFirst`, the same comparator behind the list's "Ending soonest" (F2), so the two views `endingSoonestFirst`, the same comparator behind the list's "Ending soonest" (F2), so the two views
cannot mean different things by the same words and an unannounced end still sorts behind every dated cannot mean different things by the same words and an unannounced end still sorts behind every dated
one rather than claiming a place in the queue. Merged there is also no lane heading to say whose one rather than claiming a place in the queue. Merged there is also no lane heading to say whose
event a bar is, and hue alone cannot answer that once eighteen games share a stack — so each bar event a bar is, and hue alone cannot answer that once nineteen games share a stack — so each bar
carries its game's short name, with the full name in its tooltip. A bar too narrow for a tag and a carries its game's short name, with the full name in its tooltip. A bar too narrow for a tag and a
title both keeps the title: a chopped game name reads as a broken word, and the hue and the tooltip title both keeps the title: a chopped game name reads as a broken word, and the hue and the tooltip
still answer it. still answer it.
+68 -1
View File
@@ -58,6 +58,7 @@ Two things this method cannot tell you, and both matter:
| Punishing: Gray Raven, Guardian Tales, Aether Gazer | Declined — see §§ 6, 7, 9 | | Punishing: Gray Raven, Guardian Tales, Aether Gazer | Declined — see §§ 6, 7, 9 |
| Infinity Nikki | **Rebuilt** (2026-08-19) on `infinity-nikki.fandom.com` at day precision, replacing a Game8 page stale since August 2025 — see § 11 | | Infinity Nikki | **Rebuilt** (2026-08-19) on `infinity-nikki.fandom.com` at day precision, replacing a Game8 page stale since August 2025 — see § 11 |
| Silver Palace | Unreleased | | Silver Palace | Unreleased |
| Honkai Impact 3rd | **Built** (2026-08-27) on `arustats.com`, and the only lane here whose dates are **estimates** — see § 14 |
## Summary of findings ## Summary of findings
@@ -467,7 +468,7 @@ table.
**One cost applies to both builds and should be in the commit message, not discovered later.** Both **One cost applies to both builds and should be in the commit message, not discovered later.** Both
candidates are game8.co, which does not answer the Actions runner (`AGENTS.md` § Scraping conduct). candidates are game8.co, which does not answer the Actions runner (`AGENTS.md` § Scraping conduct).
Nine of the nineteen sources are game8 pages today, now that Infinity Nikki has moved to Fandom Nine of the twenty sources are game8 pages today, now that Infinity Nikki has moved to Fandom
(§ 11); these would make ten and eleven. Each is a lane fixture-backed in CI from day one and only (§ 11); these would make ten and eleven. Each is a lane fixture-backed in CI from day one and only
ever as fresh as someone's last manual `bun run refresh`, and one more request to a single host every ever as fresh as someone's last manual `bun run refresh`, and one more request to a single host every
cycle — the per-host arithmetic § Scraping conduct already calls uncomfortable. cycle — the per-host arithmetic § Scraping conduct already calls uncomfortable.
@@ -674,6 +675,72 @@ an estimate over the CN one — plus a stable URL for the current version, or an
Any two of the three still leaves the other. No alternative HI3 source was looked for in this pass, so Any two of the three still leaves the other. No alternative HI3 source was looked for in this pass, so
the game is unsourced rather than declined outright. the game is unsourced rather than declined outright.
**Superseded 2026-08-27.** The alternative this section did not look for exists — `arustats.com`,
§ 14 below — and it answers three of the four objections above. `marisaimpact.com` itself stays
declined on all four; nothing here has been re-tested and this verdict is not the live one for the
game.
## 14. Honkai Impact 3rd — BUILT 2026-08-27, on estimated week buckets, by decision
**Source:** `https://www.arustats.com/en-us/hi3/timeline` — AruStats' per-version timeline. Assessed
on request as an alternative to the `marisaimpact.com` page § 13 declined, and built with one of
§ 13's four objections still standing.
**Conduct is clean, and better than most things here.** `robots.txt` is `User-agent: *` / `Allow: /`
with `/hi3/*` and `/en-us/*` named explicitly, no `Disallow` anywhere, no `Content-Signal` header and
none of the named AI-crawler blocks; `Crawl-delay: 1`, far below our 6h floor. Verified through the
real `RobotsCache` and the runner's own User-Agent rather than `curl``robots.txt ok`,
`crawlDelayMs: 1000` — per AGENTS.md § Fandom on why the instrument matters. The page carries its own
"not affiliated with miHoYo" attribution line, and the host advertises a sitemap.
**Three of § 13's four objections are answered:**
| § 13 objection | Here |
|---|---|
| Dates are CN estimates redrawn for regions | The grid is headed `GLB/SEA` — our readers' server, not a CN schedule with an estimate over it |
| No year appears anywhere | `scheduleDates` states full dates with years — `2026-8-20`, `2026-10-22` |
| The URL is per-version with no stable route | **`/en-us/hi3/timeline` answers `307` to the live version.** The site names its own current version server-side; the runner follows it and no URL ever needs editing |
| The unit is a week column, not an event boundary | **Still true.** Unchanged, and it is the whole cost below |
**The objection that stands.** No event on the page states a date. Events are bars carrying
`startWeek` / `endWeek` *integers* into a nine-column week grid, and the header over that grid reads
`ESTIMATED WEEK`. So every boundary this source publishes is the edge of a bucket the site estimated.
That is weaker than the day-precision reading Game8 and Infinity Nikki get — there the page printed a
date per event and we declined only to invent a time of day for it — and § 13 declined
`marisaimpact.com` on exactly this ground.
**It was built anyway, and that is a decision rather than an oversight** (repository owner,
2026-08-27, with the cost stated before the adapter was written). The reasoning: Honkai Impact 3rd
had no source at all, this page is otherwise healthy and current, and a lane of approximate
six-week windows was judged better than no lane. Be exact about what that buys and what it costs:
- **What is honest about it.** The week grid's *outer* boundaries are real and independently
checkable: v8.9 reads `2026-6-25``2026-8-20` and v9.0 picks up at `2026-8-20``2026-10-22`,
which are the version's actual maintenance dates and chain exactly. Sixteen bars on the page yield
sixteen events, so nothing is silently dropped.
- **What is not.** Where a banner swaps *inside* a version is the estimated part, and those are most
of the boundaries. `startsAt` is also half of every event ID, so a week grid the site revises moves
IDs and orphans completion marks — a hazard no wiki source here carries.
- **How the fact is carried.** `ESTIMATE_CONFIDENCE` (0.4) on every event, against the 0.850.95 a
date-stating source earns. `mergeEvents` prefers the higher number, so a real HI3 source outranks
this one the moment one is added, with no cleanup step to forget.
- **The gap left open.** `confidence` is read nowhere in `src/client/`, so nothing on screen tells a
reader this lane is estimated while every other lane is announced. Closing that needs a schema
field or a client change — a design question, not adapter work — and it is the obvious next step
if this source stays.
**What would retire it:** any HI3 source that states a date per event. That is a strictly lower bar
than § 13's list, and this source outranks nothing — it is the floor, not the ceiling.
**The markup, so nobody re-derives it.** The site is Next.js and server-renders the whole schedule
into `__NEXT_DATA__` (`props.pageProps.timeline`), so `parsers/arustats.ts` reads JSON and ignores the
rendered grid entirely — the bars carry their geometry in Tailwind `col-span-N` classes and their
offsets in bare spacer `<div>`s, which a DOM reader would have to count positionally. `endWeek` is
exclusive and runs one past the grid to mean "to the end of the version". `scheduleBosses` is the
only exactly-dated material on the page (Abyss and Memorial Arena openings, three a week) and is
deliberately unread: a recurring rotation with no end is not a deadline, and `endsAt: null` on each
would render them live-with-unknown-end forever.
## What every one of these costs, beyond the source ## What every one of these costs, beyond the source
Adding a game is never only a `SOURCES` entry: Adding a game is never only a `SOURCES` entry: