Initial agent auxilliary files.

This commit is contained in:
Lucas Winther
2026-08-14 23:45:35 +02:00
parent 308f288e6a
commit 5db608563f
16 changed files with 1662 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
---
name: add-game-source
description: 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.
## 1. Legal and conduct check — a hard gate
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>/<source-id>-<YYYY-MM-DD>.html
```
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`.
- Prefer a deterministic parser. Use `llm` strategy only when the markup genuinely cannot be parsed
reliably, and say why.
- `normalize` handles source-timezone → UTC, region reset offsets, and ID construction.
**The three domain rules that break new adapters**, from `CLAUDE.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
- Register the adapter in `src/ingest/adapters/index.ts`.
- Insert the `sources` row: id, game, url, strategy, `min_interval_ms` (default 6h).
## 7. First run
```
INGEST_ENABLED=true bun run ingest --source <source-id> --dry-run
```
Inspect what it *would* publish before letting it write. Then run for real and check the review
queue at `http://127.0.0.1:$ADMIN_PORT/review` — a new source commonly lands events in quarantine
on its first pass, because nothing corroborates it yet and LLM-extracted events start at 0.70
confidence. That is the gate working, not a bug. Review and approve them.
## 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 the adapter index and `sources`
- [ ] 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.
+83
View File
@@ -0,0 +1,83 @@
---
name: review-quarantine
description: Work through the quarantined-event queue — triage held events by reason, verify dates against sources, and approve, correct, or reject each. Use when the quarantine queue has grown, when /api/health shows held events, or when an expected event is missing from the calendar.
---
# Reviewing the quarantine queue
Held events are candidates the pipeline declined to publish. Working the queue is both a data task
(get these events onto the calendar) and a diagnostic one (**a growing queue means something
upstream broke**).
Read `docs/INGESTION.md` § The review gate first.
The review UI is at `http://127.0.0.1:$ADMIN_PORT/review`, on the admin listener only. If it is not
reachable, the server is not running or `ADMIN_PORT` differs — it is not a permissions problem, and
there is no auth to get past.
## Triage by reason first
Do not work the queue in date order. Group by `reason` — the four causes need different responses,
and two of them are pipeline bugs rather than review work.
| Reason | What it means | Do this |
|---|---|---|
| `date_conflict` | A published end date moved by >24h | **Highest priority.** Users may already have planned around the old date |
| `sanity_failed` | Broke a hard validator rule | Usually an extraction or parser bug — check the pattern before approving anything |
| `low_confidence` | Scored below `CONFIDENCE_THRESHOLD` | Routine. Verify against the source and approve |
| `novel_shape` | Something the schema does not model | A design question, not a review decision — escalate |
**Before reviewing individual items, look for a pattern.** Fifteen `sanity_failed` events from one
source is not fifteen review decisions; it is one broken adapter. Fixing the adapter and re-running
is the correct action, and approving them one by one hides the breakage. Say so rather than grinding
through the queue.
## Reviewing one event
For each held event the UI shows the parsed fields, the reason and detail, the source link, and the
cleaned text excerpt the extraction came from. Work in this order:
1. **Read the evidence span.** Does it actually contain the dates claimed? If the quoted text does
not support the extracted dates, reject — and note it, because that is a hallucination and the
extraction prompt needs evaluating.
2. **Open the source.** Confirm the dates against the live page, not just the excerpt.
3. **Check the timezone.** Most gacha sources publish in UTC+8. Confirm the conversion. A silently
wrong timezone is the most common real error here, and it is 8 hours of wrongness that looks
plausible.
4. **Check `regionScoped`.** Is this a global banner end or a per-region reset? Getting this wrong
makes the countdown wrong for two thirds of users.
5. **Check for a guessed end date.** If the source says TBD or gives no end and the candidate has a
concrete `endsAt`, that is a fabrication. Correct it to `null` with `endPrecision: "unknown"`
before approving, and flag the extraction.
For `date_conflict` specifically: the UI shows the published event beside the candidate. Determine
which is right by going to the source. If the source genuinely changed, approve the correction. If
the candidate is a misparse, reject it — the published event stands.
## Decisions
- **Approve** — publishes with `extraction_method: 'manual'`, `confidence: 1.0`.
- **Approve with edits** — correct a field first; the corrected value is what publishes. Use this
freely; a nearly-right event with one bad field is worth fixing rather than rejecting.
- **Reject** — not published. The same candidate will be held again on the next run if the source
has not changed, which is intended: a rejection is not a permanent suppression.
When uncertain, reject. An event missing from the calendar sends someone to a wiki; a wrong end date
makes them miss content. That asymmetry is the product rule (`docs/PRD.md` § Quality bar) and it is
what breaks the tie.
## Close the loop
The queue is a signal. After working it, report what caused it:
- Repeated `sanity_failed` from one source → the adapter needs repair. Use the **adapter-author**
agent.
- Hallucinated dates or fabricated evidence → the prompt regressed. Use the **extraction-evaluator**
agent before changing anything.
- Many `low_confidence` items that all turn out correct → the threshold may be too high, or the
source needs a second corroborating source. Do not just lower `CONFIDENCE_THRESHOLD` to make the
queue shorter; that trades a visible queue for invisible wrong dates.
- `novel_shape` → escalate as a data-model question with the specific example.
Report how many you approved, corrected, and rejected, and what you think caused the batch. A review
session that empties the queue without explaining why it filled has done half the job.