Off-BGG games become local library citizens

An unmatched title that's a REAL game BGG doesn't have dead-ended:
manual id or reject. The RPG local-citizen pattern generalizes to a
human decision — review (web + TUI, key l) gains "not on BGG — keep
locally": match_status "local" clears any BGG identity, diff routes it
to local_only (never queued), and enrich synthesizes a library entry
from the game's own photo reads (name, year, publisher cue — no API
call, so even a blocked run lands them; pruning keeps local keys).
Library and Titles show a "local — not on BGG" chip; Help's legend,
review description, and shortcuts cover the new verb, distinguishing
it from reject (bad read / not a game).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
Eric Wagoner
2026-08-05 18:59:56 -04:00
co-authored by Claude Fable 5
parent 59f4b8c43c
commit e187ed4f3f
15 changed files with 184 additions and 24 deletions
+11
View File
@@ -1128,3 +1128,14 @@ def test_open_versions_endpoint_routes_row_into_review(tmp_path):
json={"title_raw": "Dungeon!", "source_photos": "shelf.jpg"},
)
assert res.status_code == 502
def test_local_decision_via_web(tmp_path):
web, cfg = make_client(tmp_path)
state = web.post(
"/api/decision",
json={"title_raw": "Mystery", "source_photos": "shelf.jpg", "action": "local"},
).json()
saved = {r["title_raw"]: r for r in read_matches(cfg.matches_path)}
assert saved["Mystery"]["match_status"] == "local"
assert state["summary"]["local"] == 1