Allow consecutive scans without refresh by resetting trackers and counting vision rate limits once per card. Add a fixed card guide, widen detection bounds, and correct object-cover overlay math.
Co-authored-by: Cursor <cursoragent@cursor.com>
Give /scanner a md+ camera, live match inspector, and history strip
(with device picker, batch scan, and tips) without regressing the
mobile immersive checkout.
Co-authored-by: Cursor <cursoragent@cursor.com>
Flatten authenticated sidebar IA, move admin to the profile menu, right-align TopSearchBar actions, and replace placeholder dashboard panels with data from /api/user/stats and /api/user-cards.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace @vercel/postgres, Blob, and Upstash with lib/sql.js, MinIO object
storage, and CT 102 Redis rate limits. Add Dockerfile for Dokploy deploy,
homelab runbooks, Neon data-copy helper, and point CI smoke/visual at the
homelab URL instead of Vercel previews.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add pgvector embeddings on cards, server-side cohere/embed-v4.0 via AI
Gateway, kNN identify route, and L0→L1→L2 client orchestration with
empty-index fast escalate and id-cursor backfill job.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Start scanner-mobile-checkout convoy for the cart-then-commit phone flow.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Ship a cart-then-commit mobile scanner so phone sessions stay on the camera.
Scan matches enqueue locally instead of auto-writing ownership, checkout happens in a sheet, and audit fixes cover stale commit detection, returnUrl open redirects, nested Escape, and ember detection chrome.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Commander basic-land checks and deck aggregate metrics into
lib/deck-builder-stats.js with unit tests; render the summary row via
DeckBuilderStatsBar to shrink the page god-component.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move scanner session persistence, queue merge helpers, and destination
routing fetch calls into lib/scanner-session.js and lib/scanner-route-api.js.
Load collections/decks on mount (were defined but never invoked).
Remove unused mana-symbol imports and dead select-all helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move Layer-1/Layer-2 identify flow, outcome resolution, disambiguation
refine helpers, and scan-for-review API calls into lib/scanner-card-identify.js.
Remove unused manaSymbolSettings state from CameraScanner.
Co-authored-by: Cursor <cursoragent@cursor.com>
Move OpenCV shape detection, coordinate conversion, overlap checks,
and tracked-card merge logic from CameraScanner into lib/scanner-card-detection.js
with unit tests for the pure helpers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared 401/403 gate into permission-middleware and sweep the
four inline admin checks (import MTG/Pokemon, sync-catalog, card-submissions).
Co-authored-by: Cursor <cursoragent@cursor.com>
Correct dashboard title (My Collection overview, not Lists), sweep
remaining marketing/auth copy, update system-list seed description,
add vocabulary unit tests, and close the convoy record.
Co-authored-by: Cursor <cursoragent@cursor.com>
Share card-number normalization across reconcile and identify paths, retry set/name matches when OCR uses leading-zero collector numbers, and extend in-flight locks to all scanner destination actions with disabled Mark Owned feedback.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace pokemontcg.io API discovery and import with raw JSON from PokemonTCG/pokemon-tcg-data; format collector numbers as number/printedTotal and drop the API key dependency for catalog sync.
Co-authored-by: Cursor <cursoragent@cursor.com>
When a set lands via runCatalogSync, match pending card_submissions by set/name/number to catalog rows and approve them with promoted_card_id instead of leaving them in the admin queue.
Co-authored-by: Cursor <cursoragent@cursor.com>
The cron was importing oldest MTG sets first and never reaching recent Pokémon releases like Perfect Order; merge MTG and Pokémon by release date descending instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
Closes AGENTS.md gotcha #11 (well, the relevant half of it — "Testing:
None yet" line in §6 is now stale).
Installs vitest@^3.2.4 (single devDep, no UI / coverage / jsdom) and
adds 16 unit tests across 3 files that lock in post-Brief-1/2/4
behavior:
test/lib/auth-secret.test.js (3 tests)
- JWT_SECRET exports the env value
- JWT_TOKEN_TTL is canonical 24h
- Module throws at load when JWT_SECRET is empty
test/lib/permission-middleware.test.js (8 tests)
- getUserFromRequest returns null for: missing header, non-Bearer
scheme, malformed token, wrong-secret token, expired token,
valid-token-no-user-row
- Returns user object for valid token + user row
- Brief 2 regression lock: does NOT return the synthetic admin
shape { userId: 1, email: 'admin@tcgvault.com', role: 'admin' }
when no Authorization header is present
test/api/auth-utils.test.js (5 tests)
- generateToken issues 24h JWT (exp - iat === 86400)
- Payload includes userId, email, role
- verifyToken round-trips valid tokens
- Returns null for malformed / wrong-secret tokens
CI: re-enabled the previously commented-out test: job in
.github/workflows/ci.yml. Blocking (no || true wrapper) — vitest is
the first runner in this repo and we want CI red on test regression.
JWT_SECRET is set via a CI-only fake; production secret is unaffected.
Rate-limit (Brief 4) coverage deferred to a future expand-auth-tests
convoy per architect's call (R11). package.json has "type": "module"
so vitest's default Vite-based transform handles .js ESM out of the
box — no transform config needed.
Convoy: fix-auth-bypass / Brief 5 (last brief)
Co-authored-by: Cursor <cursoragent@cursor.com>