# SOURCES.md — event sources for the games still missing A source assessment for the games named in `docs/FEEDBACK.md` § P1 that still have no adapter. Written 2026-08-18. Nothing here has been built: no fixture was captured, no `SOURCES` entry added, no parser written. This is the reconnaissance that decides what is worth one pass of **add-game-source** and what is worth writing off. Verdicts here are proposals. Once one is acted on — built or declined — the *decision* belongs in `AGENTS.md` § Scraping conduct, whose table exists so a source is not re-litigated every pass and which `.github/ISSUE_TEMPLATE/feature_request.yml` points readers at. This file is the working, not the ruling. ## Method Every page below was fetched from this machine with the refresh runner's own `User-Agent` (`gacha-event-tracker/1.0 (+https://github.com/StereotypicalCat/gacha-event-tracker)`), one or two requests per host, `robots.txt` first. No browser-shaped headers, no proxy, no JS execution — anything that only answers a browser is treated as closed, per the `uma.moe` precedent. Where an existing parser could plausibly read a page, it was run against the fetched bytes offline (`src/ingest/parsers/index.ts`) rather than guessed at. Two things this method cannot tell you, and both matter: - **Whether a host answers the GitHub Actions runner.** game8.co does not (`AGENTS.md` § Scraping conduct). The only hard evidence we hold is what CI has actually committed: `git log` on `snapshots/` shows `github-actions[bot]` landing **arknights.wiki.gg**, **endfield.wiki.gg** and **bluearchive.wiki** (Miraheze). Fandom sources have never refreshed in CI, because `robots.txt` itself is challenged from a datacentre address and the gate fails closed. - **Whether a page will still look like this in six weeks.** Each recommendation below names the assertion `canParse` should make, so a redesign fails the source loudly instead of emptying a lane. ## Where P1 stands | Game | Status | |---|---| | Arknights, Reverse: 1999, Blue Archive, Persona 5X | **Built** | | Azur Lane, Umamusume | Declined on conduct (`azurlane.koumakan.jp` `ai-input=no`; `uma.moe` Turnstile) | | **Girls' Frontline 2, Nikke, Chaos Zero Nightmare, Stella Sora, Punishing: Gray Raven, Guardian Tales, Aether Gazer** | **No source — assessed below** | | Silver Palace | Unreleased | ## Summary of findings | Game | Best source found | Parser | Refreshes in CI? | Verdict | |---|---|---|---|---| | Girls' Frontline 2 | `iopwiki.com/wiki/GFL2_Events` | new (`iopwiki`) | likely (permissive robots, unproven) | **Build first** — best data quality of anything here | | Stella Sora | `stellasora.miraheze.org/wiki/Main_Page` | new (or extend `bawiki`) | yes (Miraheze proven) | **Build** — banners only, and read the main page, not the banner list | | Nikke | `nikke-…-international.fandom.com/api.php?…page=Event` | new shape | **no** — Fandom robots fails closed | **Build**, eyes open: fixture-backed in CI like r1999 | | Chaos Zero Nightmare | `game8.co/games/Chaos-Zero-Nightmare/archives/559899` | **existing** `game8` | **no** — game8 202s the runner | **Build** if you want a cheap win; it is a fixture-only lane | | Umamusume | `game8.co/games/Umamusume-Pretty-Derby/archives/536311` | `game8` + two header words | **no** | Possible, but it edits a parser eight sources share | | Punishing: Gray Raven | `grayravens.com/wiki/Events` | — | — | **Decline for now** — one dated string on the whole page | | Guardian Tales | `guardian-tales.fandom.com/wiki/Events` | — | — | **Decline** — wiki stopped dating events in 2025 | | Azur Lane | none | — | — | **Still declined** — the Fandom alternatives are 2021 archives | | Aether Gazer | — | — | — | **Do not build. The game is shutting down.** | | Silver Palace | — | — | — | Unreleased; beta only | --- ## 1. Girls' Frontline 2: Exilium — build this one first **Source:** `https://iopwiki.com/wiki/GFL2_Events` (IOP Wiki, the Girls' Frontline universe wiki) **Conduct.** `robots.txt` is two lines — `User-agent: *` / `Crawl-Delay: 20` — with no `Disallow` anywhere and no named-agent block. No `Content-Signal` header. Our 6-hour floor is three orders of magnitude inside a 20s crawl delay. The response carries `Last-Modified`, so `If-Modified-Since` will do real work here. **Shape.** An `

` per event, each followed by ``. All 46 of those tables share one header row, exactly: ``` Title | Period (start/end) | Server | Type | Comment ``` Cells look like this, and this is the best date material in the project after wiki.gg: ``` Amidst Wings of Gray | 2025-01-16 17:00 - 2025-02-06 02:59 (UTC) | EN | Character Event | … ``` Explicit UTC, exact precision on **both** boundaries, no timezone inference anywhere. Each period cell also carries an ICS widget with `icsStart` / `icsEnd` in `YYYYMMDDTHHMMSS` — a second, independently generated copy of the same instants, useful for cross-checking a parser rather than as the primary read. **Coverage.** 145 rows, of which 51 are `Server: EN`. A naive regex parsed **every** EN period cell — zero unparsed, which is the count check `AGENTS.md` § Silent drops asks for. Latest EN rows: | Event | Start | End | |---|---|---| | Dawnforger — Part 2 | 2026-07-16 13:00 | 2026-08-05 22:59 | | Unto the Radiance | 2026-07-16 13:00 | 2026-08-05 22:59 | | **Moonshroud Requiem** | **2026-08-06 13:00** | **2026-08-26 22:59** (live today) | **Hazards, and what to do about them.** - **CN and JP rows sit in the same tables as EN.** This is the `akwiki` CN-column problem verbatim: the Chinese schedule runs a year ahead and a CN date on a Global calendar is a confidently wrong date. The `Server` column decides — publish `EN`, skip everything else. Servers seen on the page: `CN`, `EN`, `JP`. - **`Betas` is a section, not an event type.** The page's `

`s are `Main Events`, `Minor Events`, `Betas`, `References`. Closed beta rows are dated and would parse cleanly into a calendar of things nobody can do. Fence on the heading. - **Titles are localised per row.** The `Title` cell on a CN row is Chinese; on the EN row it is the English name. Because we only take EN rows this resolves itself, but a parser that took the section heading as the title would inherit the CN/EN slash pair (`Exotic Cadence/Amidst Wings of Gray`). Take the title from the EN row's own cell. - **`canParse` should assert the header row** — `Title` + `Period (start/end)` + `Server` — so a template change fails the source rather than emptying the lane. **Work:** new parser module `src/ingest/parsers/iopwiki.ts`, a `gfl2` `GameId`, a `GAMES` entry, one `SOURCES` entry, a fixture and a test. `Type` maps onto our `EventType` reasonably — `Character Event` → banner, `Combat Event` / `Special Event` / `Main Story Event` → event, `Popularity Contest` → other. --- ## 2. Stella Sora — build the main page, not the banner list **Source:** `https://stellasora.miraheze.org/` — a Miraheze wiki, so the same call as Blue Archive and hololive Dreams: `/wiki/` is the surface `*` is allowed, `/w/` and `/*?action=` are disallowed. CC BY-SA 4.0, no `Content-Signal`, no `Crawl-delay` for us. Miraheze is the one host family we have proof answers the CI runner. Three pages, and the difference between them is the whole finding: - **`/wiki/Events`** — a list of event names with **no dates at all**. Useless on its own. - **`/wiki/Banner_List`** — two clean tables, `Image | Name | Start | End`, with full wall clocks: `2026-08-18 03:00:00` → `2026-09-08 02:59:00`. 28 and 29 rows, current. **But the page states no timezone anywhere** — no `UTC`, no offset, nothing. This is the `bluearchive.wiki` hazard exactly (`AGENTS.md` § Blue Archive, third bullet): reading a bare wall clock as UTC invents the fact that matters, and rounding to a day does not save it because the start's day is half an event ID. - **`/wiki/Main_Page`** — a `Current Banners` module that emits the same instants as real `