3ca7e7f650
Queue from to_add/to_update minus upload_log.csv (append-per-attempt, so runs resume); per-game failure isolation with 2-4s pacing; --dry-run/--verify/--retry-failed/--limit; stub-fixture marker blocks real runs, dry-run warns. Headed browser by default: live recon showed Cloudflare Turnstile hard-blocks headless, and BGG never reaches networkidle. Login selectors verified anonymously; version-picker pagination and the collection-row update flow remain unverified until real data exists. Client collection fetches gain a refresh passthrough so --verify sees the live collection, not cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.2 KiB
5.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
bggpipe — a resumable, idempotent CLI pipeline that turns shelf photos into a BoardGameGeek collection, in six stages:
| # | Command | What it does | Status |
|---|---|---|---|
| 1 | bggpipe extract |
Claude vision reads titles + edition cues from photos/ |
working |
| 2 | bggpipe resolve |
match titles to BGG IDs and versions via XML API2 | working (stub data — see hard rules) |
| 3 | bggpipe review |
human review of ambiguous/unmatched items; --web serves a FastAPI UI on port 8377 |
working |
| 4 | bggpipe diff |
diff approved matches against the existing BGG collection | working |
| 5 | bggpipe upload |
add games via a logged-in Playwright session | built; browser flows unverified until real data exists (--dry-run works now) |
| 6 | bggpipe enrich |
fetch full game/version metadata into games.json |
working |
Full design lives in bgg-shelf-pipeline-spec.md (read it before changing pipeline semantics); the upload-stage walkthrough is in docs/bgg-upload-flow.md.
Commands
uv sync— install deps (Python 3.12+, managed by uv; useuv add, never pip). Playwright needs a one-timeuv run playwright install chromium.uv run bggpipe <stage>— run a pipeline stage. Non-secret settings come fromconfig.toml(username, dirs, vision model, rate limit);--configoverrides the path.uv run pytest— 105 tests, all offline against fixtures. Tests markedlivehit the real BGG API (read-only) and are skipped unless you pass--run-live.uv run ruff check/uv run ruff format— lint (rules E, F, I, UP, B, SIM) and format.
Layout
src/bggpipe/—cli.py(typer app), one module per stage (extract,resolve,review+webreview,diff,upload,enrich), plusbgg_client.py(rate-limited XML API2 client that caches responses todata/bgg_cache/),normalize.py(title normalization),models.py(dataclasses),config.py.scripts/—write_stub_fixtures.py/write_photo_fixtures.pygenerate synthetic fixtures;record_fixtures.pyre-records real API responses once a token exists.tests/fixtures/bgg_cache/— stub XML fixtures the offline tests run against.data/— pipeline state (CSV/JSON artifacts are committed; caches are not — see Git).
Hard rules (from spec — never violate)
- ≤1 request every 2 seconds to any BGG endpoint; jittered backoff on 429/503. Upload stage: 2–4 s randomized delay between games.
- Credentials never touch disk or logs.
ANTHROPIC_API_KEY,BGG_USERNAME,BGG_PASSWORD,BGG_API_TOKENcome from env vars only. Playwright storage state is credential-adjacent — keep it gitignored. - The XML API requires a registered app token (
Authorization: Bearer, fromBGG_API_TOKEN) — unregistered requests get 401. Until Eric's registration at boardgamegeek.com/applications is approved, tests run on the stub fixtures intests/fixtures/bgg_cache/; re-record them withscripts/record_fixtures.pyonce the token exists. - Every stage is idempotent and resumable — killing mid-run and restarting must lose no work; re-runs skip already-processed items.
- Use only the XML API2 and the public website — no undocumented BGG endpoints (BGG tightened access policies in 2025).
- BGG has no write API: writes drive the real website with a logged-in Playwright session.
- Stub-resolved data is never upload-ready. All version_ids (and some game data) in
matches.csv,to_add.csv, andto_update.csvcurrently come from SYNTHETIC stub fixtures — placeholders until real fixtures exist. WhenBGG_API_TOKENarrives: delete both cache dirs, re-record fixtures,resolve --force, re-review. The caches carry aSTUB_FIXTURES.markerprovenance file (written by the fixture generators); the upload stage MUST refuse to run whiledata/bgg_cache/STUB_FIXTURES.markerexists.
Domain gotchas
- Base game vs. expansion vs. new edition is the top failure mode — bias matching toward
ambiguousover auto-match ("Wingspan Europe" must not match base Wingspan). - Editions/versions matter: Eric owns multiple editions of some games — each is a separate collection entry (keyed by
collidon BGG). Never guess a version: no legible cues →version_unknownand a version-less collection entry. - Normalize titles (casefold, strip punctuation/articles, special chars like é/&/:) identically on both sides of a match; dedupe across photos but keep
source_photosprovenance. - Detailed BGG API behavior (202 queueing, collection-endpoint quirks, endpoints): use the
bgg-apiskill. If the spec's BGG behavior changes, update thebgg-apiskill to match — they must not drift.
Git
- Remote is self-hosted Gitea 1.26 (
git.kestrelsnest.social/eric/bggpipe), not GitHub —ghCLI does not work here. - Commit
data/matches.csv,data/to_add.csv,data/to_update.csv,data/upload_log.csv,data/titles.json,data/unidentified.json,data/unidentified_dismissed.json,data/games.json, and the collection snapshot XMLs. Never commitdata/bgg_cache/,data/extract_raw/,photos/, Playwright storage state, or.env.