Initial agent auxilliary files.
This commit is contained in:
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user