Files
gacha-event-tracker/.claude/skills/add-game-source/SKILL.md
T
Lucas WintherandClaude Opus 5 025605aa09 docs: move the working agreement to AGENTS.md
The guidance in here is not Claude-specific — it is what this project is,
what it refuses to do, and the rules that are invisible from the code. Any
agent working here needs it, and AGENTS.md is the name they all look for.

CLAUDE.md stays as a pointer so Claude Code still finds it, and holds no
guidance of its own; two copies would drift and the wrong one would be read.
Every cross-reference in the source comments, docs, workflow and skills now
names AGENTS.md, and the image ignores both.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-17 22:16:40 +02:00

4.9 KiB

name, description
name description
add-game-source End-to-end workflow for adding a new game (or a new source for an existing game) to the event tracker — legal check, fixture capture, adapter, tests, registration, and first ingest run. Use when asked to add a game, add a second source, or wire up a data source for the calendar.

Adding a game source

The design goal is that a new game costs an adapter, a fixture, and a test — no schema change and no client change. If you find yourself editing src/shared/schema.ts or a React component to make a game fit, stop: either the data model is wrong (raise it) or the game is being forced into a shape it does not have.

Read docs/INGESTION.md and docs/DATA-MODEL.md before starting.

Before anything else:

  • Fetch <host>/robots.txt. Confirm the target path is not disallowed.
  • Skim the site's terms for a prohibition on automated access or scraping.
  • Prefer an official API or a community wiki with a permissive license over scraping an official site directly.

If the source forbids automated access, stop and report it. Do not write the adapter and do not look for a workaround. Suggest an alternative source instead.

2. Register the game

If this is a new game rather than a new source for an existing one, add it to GameId in src/shared/schema.ts and give it a display name and lane color in the games registry. This is the only schema edit a new game should require. If it needs more, that is a finding worth reporting.

3. Capture a fixture

Fetch the source page once and save the raw HTML:

fixtures/<game>/<site>-events-<YYYY-MM-DD>.html

The <site> prefix is load-bearing: build-feed picks fixtures by site within the game directory, so a game with two sources whose files share a prefix will hand one site's page to the other's parser.

Everything after this point works offline against that file. Do not re-fetch while iterating on the parser.

4. Build the adapter

Delegate to the adapter-author agent, or do it inline for a simple table source. Either way the requirements are the same:

  • parse is pure over its input — no network, no Date.now(). Time comes from ctx.now.
  • Reuse an existing parser from src/ingest/parsers/ if the site is already covered — most new sources are a single entry in SOURCES, with no new parsing code.
  • Source-timezone → UTC, region offsets, and ID construction happen in the parser's event builder.

The three domain rules that break new adapters, from AGENTS.md:

  1. Everything stored as UTC ISO 8601.
  2. Banners are usually one global end instant; story and login events usually end at each region's daily reset. Set regionScoped and regionEnds accordingly.
  3. An unstated end date is endsAt: null with endPrecision: "unknown". Never derive a plausible end from typical patch length. A confidently wrong end date is the failure this product exists to prevent.

5. Test it

Write fixtures/<game>/<source-id>-<YYYY-MM-DD>.expected.json with the exact expected GachaEvent[], and a test asserting deep equality with a pinned ctx.now.

Run bun test. It must pass with no network access.

Then hand-check three or four events against the live page. The test only proves the parser agrees with an expected file you wrote yourself; it does not prove either is right. Say in your report that you did this.

6. Wire it up

  • Add a SourceSpec entry to SOURCES in src/ingest/adapters/index.ts: id, game, url, parserId, and optionally priority (higher wins when sources disagree).
  • Insert the matching sources row: id, game, url, parser_id, priority, min_interval_ms.

7. Rebuild the feed

bun run build:feed     # regenerates public/data/events.v1.json
bun run dev            # build and serve on :3000

Check the event count and any conflicts the merge reports. A game with two sources will surface disagreements — those are the gate working, not a bug.

The scheduler, quarantine table and /review queue described in docs/INGESTION.md are not built yet; today the feed is generated offline from fixtures.

Checklist

  • robots.txt and ToS permit it
  • Game registered in GameId (new games only)
  • Fixture captured, page fetched exactly once
  • Adapter implemented; parse pure, no clock access
  • Timestamps UTC; regionScoped correct; unstated ends are null
  • Expected-output file + passing test, offline
  • Manually spot-checked against the live page
  • Registered in SOURCES and the sources table
  • Dry run inspected, then real run, then quarantine reviewed

When something does not fit

Report it rather than working around it. A game with a fourth server region, an event type the enum lacks, or a source that publishes only relative dates ("starts next Tuesday") are all real possibilities the current model does not cover. Those are design questions, not adapter bugs — say what you found and what it would take.