Commit graph

406 commits

Author SHA1 Message Date
Randall Stillwell
a84316f9e2 fix(mtg): support Scryfall oracle bulk JSONL.gz download
Some checks failed
CI / Lint (push) Has been cancelled
CI / Schema map up to date (push) Has been cancelled
CI / Forbidden patterns (9 checks) (push) Has been cancelled
CI / Migrations apply (node-pg-migrate) (push) Has been cancelled
CI / Unit tests (vitest) (push) Has been cancelled
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 12:20:28 -05:00
Randall Stillwell
c6cc637f8a fix(pricing): test fixes and post-merge tooling
Some checks are pending
CI / Lint (push) Waiting to run
CI / Schema map up to date (push) Waiting to run
CI / Forbidden patterns (9 checks) (push) Waiting to run
CI / Migrations apply (node-pg-migrate) (push) Waiting to run
CI / Unit tests (vitest) (push) Waiting to run
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 11:51:08 -05:00
Randall Stillwell
2984185e6b docs(homelab): pricing migration order fix and cutover checklist
Document homelab Neon teardown steps, add fix-pricing-pgmigration-order script, and refresh migrate-neon convoy status for Dokploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 11:47:53 -05:00
d80c20c75e Merge pull request 'feat(pricing): convoy for online prices and local stores' (#158) from convoy/pricing-online-local into main
Some checks are pending
CI / Lint (push) Waiting to run
CI / Schema map up to date (push) Waiting to run
CI / Forbidden patterns (9 checks) (push) Waiting to run
CI / Migrations apply (node-pg-migrate) (push) Waiting to run
CI / Unit tests (vitest) (push) Waiting to run
Reviewed-on: #158
2026-09-09 00:08:19 -04:00
Randall Stillwell
d0468015eb feat(pricing): convoy for online prices and local stores
Some checks failed
CI / Lint (pull_request) Has been cancelled
CI / Schema map up to date (pull_request) Has been cancelled
CI / Forbidden patterns (9 checks) (pull_request) Has been cancelled
CI / Migrations apply (node-pg-migrate) (pull_request) Has been cancelled
Visual diff / Should run? (pull_request) Has been cancelled
CI / Unit tests (vitest) (pull_request) Has been cancelled
Convoy metrics gate / Require role-event telemetry on convoy PRs (pull_request) Has been cancelled
PR Health rollup / Aggregate gate status (pull_request) Has been cancelled
Preview smoke / Should run? (pull_request) Has been cancelled
Preview smoke / Playwright smoke (pull_request) Has been cancelled
Visual diff / Screenshot diff (pull_request) Has been cancelled
Add pricing schema, free API integrations (MTG bulk, TCGdex, Foolio),
valuation and sort-by-price, card detail pricing UI, and nearby store
discovery via home zip and OSM geo clients.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 22:47:46 -05:00
70be99421d Wire scanner feedback toasts and restore rescan uploads (#157)
Some checks are pending
CI / Lint (push) Waiting to run
CI / Schema map up to date (push) Waiting to run
CI / Forbidden patterns (9 checks) (push) Waiting to run
CI / Migrations apply (node-pg-migrate) (push) Waiting to run
CI / Unit tests (vitest) (push) Waiting to run
2026-09-08 20:08:01 -04:00
8a5ec11573 Merge pull request 'perf(scanner): three high-impact speed optimizations' (#156) from perf/scanner-speed-optimizations into main
Some checks failed
CI / Lint (push) Has been cancelled
CI / Schema map up to date (push) Has been cancelled
CI / Forbidden patterns (9 checks) (push) Has been cancelled
CI / Migrations apply (node-pg-migrate) (push) Has been cancelled
CI / Unit tests (vitest) (push) Has been cancelled
2026-09-01 18:18:13 -04:00
Randall Stillwell
04504903bf perf(scanner): three high-impact speed optimizations
Some checks failed
CI / Lint (pull_request) Has been cancelled
CI / Schema map up to date (pull_request) Has been cancelled
CI / Forbidden patterns (9 checks) (pull_request) Has been cancelled
CI / Migrations apply (node-pg-migrate) (pull_request) Has been cancelled
CI / Unit tests (vitest) (pull_request) Has been cancelled
Convoy metrics gate / Require role-event telemetry on convoy PRs (pull_request) Has been cancelled
PR Health rollup / Aggregate gate status (pull_request) Has been cancelled
Preview smoke / Should run? (pull_request) Has been cancelled
Preview smoke / Playwright smoke (pull_request) Has been cancelled
1. Non-blocking scan capture upload (emitScannedCard)
   - Card result shows immediately; upload fires in background
   - Removes ~500ms blocking S3/MinIO roundtrip from the result path

2. Parallel OCR workers for name + number strips (ocr-worker)
   - Two independent Tesseract workers run simultaneously
   - Cuts Layer 1 OCR time ~50% on mobile (name + number in parallel)
   - terminateOcrWorker cleans up both workers on teardown

3. Faster pre-verification timing (scanner-card-detection)
   - VERIFICATION_INTERVAL_MS: 1000 → 500ms
   - MIN_FIRST_SEEN_MS_FOR_VERIFY: 800 → 500ms
   - Net ~300ms faster from first detection to verification start

All 58 scanner tests pass (2.20s).
2026-09-01 17:17:20 -05:00
e5cb8569e5 Merge pull request 'feat(scanner): add debug instrumentation for vision pipeline timing' (#155) from feat/scanner-debug-mode into main
Some checks are pending
CI / Lint (push) Waiting to run
CI / Schema map up to date (push) Waiting to run
CI / Forbidden patterns (9 checks) (push) Waiting to run
CI / Migrations apply (node-pg-migrate) (push) Waiting to run
CI / Unit tests (vitest) (push) Waiting to run
2026-09-01 18:11:03 -04:00
Randall Stillwell
106bd9d592 feat(scanner): add debug instrumentation for vision pipeline timing
Some checks failed
CI / Lint (pull_request) Has been cancelled
CI / Schema map up to date (pull_request) Has been cancelled
CI / Forbidden patterns (9 checks) (pull_request) Has been cancelled
Visual diff / Should run? (pull_request) Has been cancelled
CI / Migrations apply (node-pg-migrate) (pull_request) Has been cancelled
Visual diff / Screenshot diff (pull_request) Has been cancelled
CI / Unit tests (vitest) (pull_request) Has been cancelled
Convoy metrics gate / Require role-event telemetry on convoy PRs (pull_request) Has been cancelled
PR Health rollup / Aggregate gate status (pull_request) Has been cancelled
Preview smoke / Should run? (pull_request) Has been cancelled
Preview smoke / Playwright smoke (pull_request) Has been cancelled
- Add isDebugMode() + debugLog() helpers to scanner-card-identify.js and use-scanner-identification.js
- Instrument Layer 0 (pgvector), Layer 1 (Tesseract OCR + pg_trgm), Layer 2 (Vision API) with per-layer timing
- Log shutter press, verification outcomes, rate-limit cooldowns, and pipeline totals
- Activate via localStorage.setItem('SCANNER_DEBUG', 'true') or window.__SCANNER_DEBUG = true
- Zero runtime overhead when debug mode is off (isDebugMode() check inlined)
- Add docs/SCANNER_DEBUG_MODE.md with full usage guide and log pattern examples
2026-09-01 17:02:01 -05:00
Randall Stillwell
a81c6dc21b fix(tests): wire DEFAULT_LAYOUT export, fix custom-frames mocks, and add validateLayout tests
- Export DEFAULT_LAYOUT from lib/frame-palette.js so the custom-frames
  route can import it at runtime (not just the hardcoded copy in tests).
- Fix vitest mock isolation in test/api/custom-frames.test.js: beforeEach now
  uses mockReturnValue instead of mockResolvedValue to avoid resolving the
  default mock in each test; test cases provide specific mock chains with
  mockResolvedValueOnce. Fixes 4 tests that were bleeding state between
  cases due to leftover queued mock values.
- Fix validateLayout test coordinates: art w+h=0.924 and 0.398 are both
  within the 0-1 fraction range so x+w=0.962<1 and y+h=0.982<1 pass.
- Add dedicated validateLayout unit tests (accepts, rejects missing zone,
  rejects out-of-bounds).
- Fix update test mock chain: PUT calls SELECT (found) then SELECT (clash)
  then UPDATE (RETURNING) — provide all three in order.
- Fix DELETE test: owns via SELECT then executes DELETE (2 calls).
2026-09-01 09:40:04 -05:00
Randall Stillwell
027ddcf83e feat(designer): image-based frame zones, ZoneEditor, custom frame/game APIs
Adds DEFAULT_LAYOUT + IMAGE_FRAME_ROWS constants (fractional art/text window
anchors), a ZoneEditor component for bounding-box layout editing in the
designer, and expands the custom-frames/games CRUD API surface to support
frame image storage and retrieval. Custom card designer pages wire these
together with the existing PNG export pipeline.

See .convoys/card-designer-image-frames.md for scope tracking.
2026-09-01 09:23:16 -05:00
Randall Stillwell
5b9a278ca2 feat(designer): inline symbol icons, frame textures, community sharing, print sheets
- {CODE} tokens in description/actions/flavor render as inline symbol
  icons (RichText), including inside cost pips
- custom frames gain an optional background texture (upload/replace/
  remove via /api/custom-frames/[id]/texture); renders behind panels
- custom games can be shared to the community (is_public): toggle in the
  game space, public listing at /community/games, read-only game view,
  /api/public/games endpoints (no auth, public rows only)
- /designer/print: multi-card print sheets on US Letter at 300dpi
  (63x88mm cards, 3x3 or 2x2, dashed cut guides, full-sheet PNG export)
- migration 1787711511000
2026-08-24 21:48:52 -05:00
Randall Stillwell
fe1695f7ad feat(designer): custom frame editor and cost symbols
- custom_frames: per-user frames with full 9-slot palette (JSONB),
  unique names; designer frame picker lists them alongside starters,
  click to use, edit/delete via inline editor with live preview
- custom_symbols: upload cost icons (PNG/WebP/SVG, 2MB) keyed by short
  code; re-uploading a code replaces the old icon; ManaPips renders
  icon pips for {CODE} tokens with graceful text fallback
- custom_cards.custom_frame_id links designs to custom frames; API GETs
  join and nest the palette; deleting a frame falls back to starter
- migration 1787700511000
2026-08-24 21:13:12 -05:00
Randall Stillwell
c0051dd6d5 feat(designer): game targeting and custom game spaces
- game system selector: standalone, existing system (MTG/Pokemon/Lorcana/
  SWU/FaB/One Piece/Sorcery/Grand Archive — codes match catalog imports),
  or a user's custom game
- custom_games table + CRUD API (private per user, unique names)
- /games hub with create form; /games/[id] space with rename, delete,
  card gallery, and ?game= deep-link into the designer
- catalog twin resolves game: system code, custom game name, or 'Custom'
- my-designs shows each design's game association
- migration 1787693311000
2026-08-24 21:01:04 -05:00
Randall Stillwell
147041d292 feat(designer): rarity badges, flavor quotes with dividers, full-art mode
- rarity now renders as shape+color symbol anchored inside the type bar
  (circle/diamond/pentagon/star per rarity) — fixes straddling gem alignment
- new flavor_quote field: centered italic quotation with ornamental
  diamond dividers between description/actions/quote
- framed | fullart toggle: full-art bleeds artwork edge-to-edge with
  title/cost top scrim and type/text bottom scrim
- shared pickDesignFields lib so create/update routes cannot drift
- migration 1787685911000: art_mode + flavor_quote columns
2026-08-24 16:19:24 -05:00
Randall Stillwell
421c5e5ee5 feat(designer): add card designer with starter frames, live preview, and PNG export
- custom_cards migration + CRUD API with catalog twin sync so designs
  appear in My Cards, lists, and decks via normal card joins
- artwork upload to MinIO under card-art/
- /designer page: form-driven live preview, 4 starter frames, PNG export
- /my-designs gallery with edit/delete
- Designer nav entry in sidebar + mobile drawer
2026-08-24 14:53:06 -05:00
Randall Stillwell
f2ba333daf docs: sweep README + AGENTS.md to post-homelab reality
- README: Dokploy/CT102 Postgres stack, lib/sql.js, six rate-limit
  classes over REDIS_URL, Playwright smoke+visual, MinIO/S3 vars,
  inline .env.local contract (no .env.example exists), deckhearth.git
  clone URL, project-structure refresh.
- AGENTS.md: infra banner for the Vercel/Neon -> homelab move, repo
  rename to stwl-labs/deckhearth, Data/Auth/Hosting overview bullets,
  DB-access convention re-pointed at lib/sql.js, Gotcha #12 rewritten
  for REDIS_URL + scan limiter (+ row in limiter table), test counts
  refreshed (231/231 across 43 files), §5 env contract, §7 marked
  legacy-pending-decommission with DOKPLOY_DEPLOY runbook pointer.
2026-08-23 22:34:54 -05:00
Randall Stillwell
95c2f6003b docs(convoy): flip shipped statuses (scanner-desktop-layout, reconcile-historical-add-scripts)
- scanner-desktop-layout: open -> shipped; adds As-shipped section
  (PR #165, squash 938c161, 2026-08-15) with post-merge follow-up refs.
- reconcile-historical-add-scripts: open -> shipped to match its
  existing As-shipped section and ship-readiness RESOLVED entry.
2026-08-23 22:27:54 -05:00
Randall Stillwell
ca3b8a78c2 fix(scanner): satisfy react-hooks/immutability and jsdom ResizeObserver in tests
- Move disambiguation-cancel tracker reset into useCameraScanner's new
  resetTrackedCard(cardId) (immutable map + setTrackedCards), wired via
  onTrackerReset — the identification hook no longer mutates state-derived
  objects, clearing the blocking react-hooks/immutability lint error.
- Stub ResizeObserver in test/setup.js so ScannerCamera's workstation
  tests render under jsdom.
- Drop unused eslint-disable directive in CollectionsPageView.

npm run lint: 0 problems; vitest 231/231.
2026-08-23 22:27:49 -05:00
varutasu
6fab22d315
Add manual tap-to-scan with shutter button on mobile. (#167)
Default mobile to manual scan mode with guide tap and center shutter for explicit feedback. Split auto-detect pause from hard verification pause and fall back to guide bounds when shape detection misses.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 20:11:54 -05:00
varutasu
28bdd6aa5b
Fix scanner multi-card flow, frame overlay, and rate limits. (#166)
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>
2026-08-15 18:48:05 -05:00
varutasu
938c161a26
feat(scanner): add desktop workstation layout (#165)
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>
2026-08-15 17:21:23 -05:00
Randall Stillwell
fdf8f5ece4 Mark dashboard-home-realignment convoy shipped (PR #164).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 16:48:07 -05:00
varutasu
b9e840ff3a
Realign dashboard home nav and wire honest ownership stats (#164)
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>
2026-08-15 16:47:59 -05:00
varutasu
0d52858bbd
docs: post-hoc B7 closure for reconcile-historical-add-scripts (#163)
Ships Brief 7 (documentation + verification runbook) of the
reconcile-historical-add-scripts convoy, ~2 months post-hoc. The
6 implementer briefs (B1-B6) landed 2026-06-14 to 2026-07-06 via
PRs #148, #149, #150, #151, #152, #153. This PR closes the loop:

- Brings the architect's parent convoy file + 6 brief files onto
  main (they only existed on the stale convoy/reconcile-historical-
  add-scripts branch, never merged)
- Adds § As-shipped to the parent convoy file documenting all 6
  squash SHAs + PR numbers + merge dates + the reservation-timestamp
  rename (1781000000001-006 → 1781442330001-006 in ec9bb2b, except
  B3 which kept its original) + the B6 shipped-as-tiny-migration
  deviation from the collapse-to-docs plan
- Fixes docs/SCHEMA_MAP.md § user_favorites (was stale
  (user_id, card_id); actual polymorphic (item_type, item_id) per
  B4's migration)
- Adds docs/MIGRATION_VERIFICATION_RUNBOOK.md — manual
  fresh-Neon-branch vs prod pg_dump diff runbook per architect D5
- Flips .convoys/ship-readiness.md entries:
  - reconcile-historical-add-scripts → RESOLVED
  - retire-graveyard-scripts-after-audit → UNBLOCKED
- Adds two new queued follow-ups surfaced by the architect:
  - unify-user-avatar-column (P3 — dual avatar column smell)
  - drop-dead-cards-columns (P3 — cards.quantity + cards.favorited)

No source-code changes. Docs only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 16:31:37 -05:00
Randall Stillwell
c6a0225e54 Document post-ship scan_attempts telemetry and close Phase 1–2 convoys.
Record Aug 15 measurement showing Phases 1–2 missed success targets, Phase 3 code is live with 15% embedding backfill and zero L0 traffic yet.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 16:19:30 -05:00
Randall Stillwell
6ada83507c docs(dokploy): point deploy runbook at main branch
Homelab migration merged to main; Dokploy should track production there.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 09:59:17 -05:00
Randall Stillwell
636bcd35f5 Merge origin/main into homelab migration branch 2026-08-15 09:59:08 -05:00
Randall Stillwell
1cc2e28423 Migrate Deck Hearth off Vercel/Neon to homelab Dokploy stack.
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>
2026-08-15 09:32:13 -05:00
varutasu
7007eae3ba
fix(ci): Phase 3 post-merge CI gates (#162)
* fix(ci): allow card-embed.js and skip pgvector on non-superuser CI

Add lib/card-embed.js to the server-only LLM allowlist (forbidden-patterns
Check 3). Make the pgvector migration degrade gracefully when CT 102 CI
cannot CREATE EXTENSION vector so migrate up still passes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(convoy): post-ship metrics and operator checklist for Phase 3

Record Neon migration applied, scan_attempts snapshot, and backfill gate.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:53:34 -05:00
Randall Stillwell
a9d16d2e4d docs(convoy): post-ship metrics and operator checklist for Phase 3
Record Neon migration applied, scan_attempts snapshot, and backfill gate.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:48:58 -05:00
Randall Stillwell
b2c02cb45b fix(ci): allow card-embed.js and skip pgvector on non-superuser CI
Add lib/card-embed.js to the server-only LLM allowlist (forbidden-patterns
Check 3). Make the pgvector migration degrade gracefully when CT 102 CI
cannot CREATE EXTENSION vector so migrate up still passes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:48:25 -05:00
varutasu
8f09ed1ef6
feat(scanner): Layer-0 visual catalog search (Phase 3) (#160)
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>
2026-08-14 21:46:39 -05:00
varutasu
484bd02f9a
Polish scanner checkout glass surfaces and remove dead code. (#161)
Align toast, sheet, and Review N pill with GlassSurface tokens, use solid cart rows to avoid stacked blurs, and delete pre-rebuild scanner components no longer referenced by /scanner.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:42:59 -05:00
varutasu
f6305f96f2
Stop CI migrate-up from indexing collections.visibility before the column exists. (#159)
B3 ran before the B2 column add on a fresh database. Move the index to a later migration so collaboration tables still apply and the index lands after visibility is present.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:32:10 -05:00
varutasu
c52891a6b2
feat(scanner): perspective warp card detection (Phase 2) (#158)
Add quad corner refinement and homography warp for rectified identify
crops; wire warped capture through identifyTrackedCardCapture with
validation and axis-aligned fallback; add detection frame a11y labels.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:31:45 -05:00
varutasu
0b4f419f49
Scanner identify upgrade — Phase 1 hot path (#156)
* docs(convoy): seed scanner identify upgrade epic and sub-convoys

Baseline scan_attempts telemetry and three-phase plan for faster, more
accurate card identification without touching scanner chrome.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(scanner): tighten Layer-1 identify hot path (Phase 1)

Cut verify hold-still gates, OCR collector numbers on Layer 1, request
structured Gemini JSON, and skip automatic L2 refine when L1 opens the
printing picker. Includes convoy UX/architecture briefs and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:21:20 -05:00
varutasu
73424aae59
Mobile scanner checkout: scan first, commit later (#157)
* 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>
2026-08-14 20:20:43 -05:00
varutasu
c6c1364dd6
chore(agent-pipeline): sync 0.6.0/0.7.0 artifacts (#155)
* Sync agent pipeline artifacts to 0.6.0.

Add model routing defaults, L2 role updates, convoy telemetry, and manifest tracking without touching unrelated in-progress work.

* Record tcg-vault interactive sync (kept local L1/L3 customizations).

Update last_synced_at after reviewing behind/conflict artifacts; no overwrites applied.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(agent-pipeline): sync 0.6.0 — implementer Mode 2 fix pass + UI designer

- role-implementer Mode 2 (fix pass) and role-reviewer hand-off
- role-ui-designer + model-routing updates from upstream 783e2a3
- manifest last_synced_at bumped

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(agent-pipeline): sync v0.7.0 model routing

Composer Standard for architect, Grok for audit fan-out, Sonnet 5 escalation path.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(agent-pipeline): finish 0.6.0/0.7.0 sync on branch

Install security baseline, convoy planning, security-audit and ui-ux-pro-max
skills; align PR template and metrics docs with convoy-metrics-gate; refresh
manifest hashes while keeping tcg-vault-local L1/L3 customizations.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 18:56:01 -05:00
varutasu
15e02ee01b
feat(migrations): reconcile collaboration tables missed by initial-schema backfill (B3) (#151)
Captures the DDL half of scripts/add-collaboration-features.js that the
initial-schema backfill missed:

  - collection_permissions (collaboration roles + invite tokens)
  - collection_activity (audit-trail with JSONB details)
  - users.is_pending (column for invited-but-not-yet-accepted users)
  - 4 indexes for query hot-paths

lib/permission-middleware.js reads collection_permissions in
withCollectionPermission and writes collection_activity from
logCollectionActivity, so a fresh Neon branch onboarded by
`npm run setup-db` MUST land these tables. Prod was brought to this
shape by add-collaboration-features.js running historically; this
migration brings fresh envs to parity per
.convoys/reconcile-historical-add-scripts.md Brief outline -> B3.

Idempotent against fresh and existing envs:
CREATE TABLE IF NOT EXISTS / ADD COLUMN IF NOT EXISTS / CREATE INDEX
IF NOT EXISTS so re-application against any post-historical-script env
is a no-op except recording the pgmigrations row.

Owner-permission DML backfill is intentionally NOT captured - fresh
envs have no pre-existing collections needing backfill, and prod's
backfill is already applied.

down() is a hard stub matching the initial-schema pattern.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 11:27:46 -05:00
varutasu
71757faa90
chore: sync agent pipeline v0.6.0 (keep local L1/L3) (#154)
* Sync agent pipeline artifacts to 0.6.0.

Add model routing defaults, L2 role updates, convoy telemetry, and manifest tracking without touching unrelated in-progress work.

* Record tcg-vault interactive sync (kept local L1/L3 customizations).

Update last_synced_at after reviewing behind/conflict artifacts; no overwrites applied.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 11:27:33 -05:00
Randall Stillwell
8ee5e7bf05 chore(metrics): capture role event from multi-game-bulk-sync brief 2
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 15:34:04 -05:00
Randall Stillwell
ec9bb2b93e feat(catalog): unified multi-game bulk sync + schema map update
Ship Pokemon and Lorcana bulk import libs/scripts, unified weekly cron
sync across MTG/Pokemon/Lorcana with per-game error isolation and
catalog_sync_log telemetry. Admin UI adds Unified/Incremental/Bulk MTG modes.

- Rename reconcile migrations to 1781442330* timestamps so they apply
  after bulk-data migrations without node-pg-migrate ordering conflicts
- Add Lorcana set-code normalization + orphan cleanup migrations
- Drop stricter user_cards_user_card_unique (keep 3-column foil unique)
- Update docs/SCHEMA_MAP.md for tags, card_tags, catalog_sync_log, bulk columns

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:22:19 -05:00
varutasu
40402eb287
feat(migrations): reconcile user_cards UNIQUE constraint per Option A (B6) (#149)
* feat(migrations): reconcile user_cards UNIQUE constraint per Option A (B6)

Convoy: reconcile-historical-add-scripts Brief 6.

Operator decision on Finding 1 (ratified 2026-06-14): Option A — keep
the canonical 3-col UNIQUE(user_id, card_id, is_foil) declared by
migrations/1779853647564_initial-schema.js. Drop / treat-as-no-op the
stricter 2-col UNIQUE(user_id, card_id) that the historical
scripts/fix-user-cards-constraints.js job would have installed. Foil
and non-foil copies of the same card are semantically separate rows.

Defensive idempotent shape; safe against all three prod states (fresh
Neon branch, long-lived env that never ran the script, long-lived env
that did run it). down() is a hard-stub throw — re-installing the
2-col constraint would forbid the foil distinction runtime code
relies on.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(migrate): pre-check pg_constraint instead of catching duplicate_object

ADD CONSTRAINT UNIQUE creates a supporting index under the hood; when
the index name already exists from initial-schema's inline UNIQUE,
Postgres raises SQLSTATE 42P07 (duplicate_table), not 42710
(duplicate_object) — so the EXCEPTION block didn't catch it and CI's
Migrations apply gate failed with `relation
"user_cards_user_id_card_id_is_foil_key" already exists`.

Swap to a pg_constraint pre-check: bulletproof against both SQLSTATEs
without overreaching to WHEN OTHERS.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:18:22 -05:00
varutasu
ee7da9ac3a
feat(migrations): reconcile user profile missed by initial-schema backfill (B5) (#153)
Folds the union of DDL effects from `scripts/add-user-profile-columns.js`
(#8) and `scripts/add-user-profile-fields.js` (#9) into the migration
history so a brand-new Neon branch reaches structural parity with prod.

Captures:
- 16 ALTER TABLE users ADD COLUMN IF NOT EXISTS (first_name, last_name,
  username UNIQUE, profile_image_url, bio, avatar_url, favorite_games,
  collection_visibility, preferred_currency, cards_per_page, default_view,
  notifications_email, notifications_marketing, two_factor_enabled, theme,
  language). Per Finding 2, BOTH profile_image_url and avatar_url are kept
  (cleanup deferred to queued unify-user-avatar-column).
- 2 CREATE TABLE IF NOT EXISTS (user_settings, user_avatars) — FK to users
  with ON DELETE CASCADE; user_settings has UNIQUE(user_id, setting_key).
- 6 CREATE INDEX IF NOT EXISTS (idx_users_username, idx_users_email,
  idx_user_settings_user_id, idx_user_settings_key, idx_user_avatars_user_id,
  idx_user_avatars_active).
- 6 CHECK constraints wrapped in DO $$ EXCEPTION WHEN duplicate_object
  blocks (Postgres pre-15 has no ADD CONSTRAINT IF NOT EXISTS for CHECK):
  check_collection_visibility, check_preferred_currency, check_cards_per_page,
  check_default_view, check_theme, check_language.

Defaults-backfill DML from the historical script is intentionally NOT
replicated; column DEFAULTs handle fresh-env semantics and prod rows
already have the values from the historical run.

down() is a hard stub (rolling back would drop columns runtime code reads).

Convoy: reconcile-historical-add-scripts (Brief 5/7).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:18:20 -05:00
varutasu
e8619c5836
feat(migrations): reconcile collections columns missed by initial-schema backfill (B2) (#150)
Captures the `collections`-table DDL that historical scripts added to prod
but `migrations/1779853647564_initial-schema.js` did not capture:

  - `visibility VARCHAR(20) DEFAULT 'private'`  (scripts/add-collaboration-features.js, lines 15-21 — collections half only)
  - `tcg VARCHAR(50) DEFAULT 'MTG'`             (scripts/add-collaboration-features.js, lines 15-21 — collections half only)
  - `tags TEXT`                                 (scripts/add-collaboration-features.js, lines 15-21 — collections half only)
  - `slug VARCHAR(100) UNIQUE`                  (scripts/add-collection-slugs.js, lines 17-20)
  - `idx_collections_slug` UNIQUE INDEX         (scripts/add-collection-slugs.js, line 79)
  - `check_slug_format` CHECK constraint        (scripts/add-collection-slugs.js, line 91)
  - `image TEXT`                                (scripts/add-image-column.js, lines 14-17)

Idempotency (D2): every statement is `IF NOT EXISTS`-guarded
(ADD COLUMN IF NOT EXISTS, CREATE UNIQUE INDEX IF NOT EXISTS, plus a DO $$
pg_constraint guard for the CHECK since Postgres has no native IF NOT
EXISTS clause for named constraints). Safe against fresh, prod, and
re-apply.

Down() is a hard stub matching initial-schema style — these columns hold
visibility flags, slugs, tcg labels, tags, and images that production
collections rely on at every page render.

Out-of-scope per architect plan (B3 territory): collection_permissions,
collection_activity, users.is_pending, idx_collections_visibility, and
the 3 idx_collection_* indexes. Out-of-scope per architect plan (already
captured): is_system_collection (in 1780378340194).

Deferred DML: per-row slug backfill from `name` via
`lib/slug-utils.js::generateUniqueSlug`. Generating slugs on a fresh env
is moot (no pre-existing collections); operators of long-lived envs
already ran the backfill historically.

Static idempotency proof — grep confirms each B2 column/constraint is
defined exactly ONCE across all 8 existing migrations:

  $ grep -nE "(visibility|tcg|^.*tags TEXT|slug VARCHAR|^.*image TEXT|check_slug_format|idx_collections_slug)" migrations/*.js
  migrations/1781000000002_reconcile-collections-columns.js  (sole owner)

The `image_url` / `stock_image_url` matches in `1779853647564_initial-schema.js`
are on the `cards` table, not `collections`. The `tags` table created in
`1781440721350_add-tagger-tables.js` is a separate table from this
migration's `collections.tags` column.

Verification: node --check ✅, npm run lint ✅ (0 errors, baseline 1
unrelated warning), npm run test:run ✅ (131/131). Live Neon-branch
verification deferred to operator runbook (D5 of the convoy plan).

Convoy: reconcile-historical-add-scripts
Brief: B2
Pre-assigned timestamp: 1781000000002

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:18:05 -05:00
varutasu
ef2cfb8547
feat(migrations): reconcile favorites system missed by initial-schema backfill (B4) (#152)
Captures the DDL effects of scripts/add-favorites-system.js (a historical
no-go-zone script) so a fresh Neon branch onboarded via npm run setup-db
has the same user_favorites table + 4 indexes that prod has via the
historical script. Brings fresh envs to parity with prod for the
favorites surface used by pages/api/favorites.js.

Shape matches the historical script and the runtime API verbatim:
  - user_favorites(id, user_id FK CASCADE, item_type VARCHAR(50),
    item_id INTEGER, created_at, UNIQUE(user_id, item_type, item_id))
  - idx_user_favorites_user_id / _item_type / _item_id / _user_type

CREATE TABLE / CREATE INDEX guarded with IF NOT EXISTS per convoy
decision D2 — re-running against any env where the historical script
already ran is a documented no-op (only the pgmigrations row is new).

down() is a hard stub: rolling back would drop user_favorites and every
row in it; removal deserves its own scoped convoy.

Part of .convoys/reconcile-historical-add-scripts (commit 22ebef2),
Brief 4 of 7. Base PR is main, not the parent convoy branch, per the
parallel-implementer dispatch pattern.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:11:26 -05:00
varutasu
a35ce01ba0
feat(migrations): reconcile cards columns missed by initial-schema backfill (B1) (#148)
Folds `scripts/add-card-columns.js` into the migration history as B1 of
the `reconcile-historical-add-scripts` convoy (architect plan at
commit 22ebef2). Adds two columns to `cards` that the initial-schema
backfill (1779853647564) did not capture in its bootstrap CREATE TABLE:

  - cards.quantity   INTEGER  DEFAULT 0
  - cards.favorited  BOOLEAN  DEFAULT false

Both columns exist in every long-lived env (the historical script ran
pre-migration-tool) but were missing from fresh-env onboarding via
`npm run setup-db` until now. They are flagged "Unused" in
docs/SCHEMA_MAP.md § "Known schema smells" #3; the follow-up
`drop-dead-cards-columns` convoy will retire them once a query-trace
audit confirms zero readers. Reproduced verbatim here to bring fresh
envs to prod-parity per the convoy's D3 ratification.

Idempotency (D2): both statements use ADD COLUMN IF NOT EXISTS, so
the migration is safe to run against fresh Neon branches, long-lived
prod envs where add-card-columns.js already ran, or re-applications.
Matches the raw `pgm.sql()` style of `1779853647564_initial-schema.js`.
`down()` is a hard-stub throw consistent with the rest of the
migration corpus's reconciliation/destructive guards.

Static idempotency proof — the `cards` CREATE TABLE block in
initial-schema (lines 45-69) does NOT contain `quantity` or
`favorited`; the three `quantity` hits in that file at lines 76, 103,
127 are on `user_cards`, `collection_cards`, and `deck_cards`. No
other migration mentions either column:

  $ rg -n "quantity|favorited" migrations/
  migrations/1779853647564_initial-schema.js:76:      quantity INTEGER DEFAULT 1,
  migrations/1779853647564_initial-schema.js:103:      quantity INTEGER DEFAULT 1,
  migrations/1779853647564_initial-schema.js:127:      quantity INTEGER DEFAULT 1,

PR #32's NEW post-architect migration `1781440700404_add-scryfall-bulk-columns.js`
adds 13 unrelated Scryfall bulk columns (oracle_id, illustration_id,
color_identity, keywords, legalities, flavor_text, artist, released_at,
layout, edhrec_rank, reserved, reprint, finishes) — verified to not
include quantity/favorited; no scope reduction required.

Verification:
  - `node --check migrations/1781000000001_reconcile-cards-columns.js` → exit 0
  - `npm run lint` → 0 errors, 1 pre-existing warning on main
    (components/CollectionsPageView.js, unrelated to this change)
  - `npm run test:run` → 131/131 tests pass across 26 files
  - End-to-end `npm run migrate up` against a fresh Neon branch:
    deferred to operator post-merge verification per D5 (D5 runbook
    lives in .convoys/reconcile-historical-add-scripts.md § Verification plan)

Refs: - Architect plan: .convoys/reconcile-historical-add-scripts.md (commit 22ebef2)
  - Historical script (no-go-zone, not edited): scripts/add-card-columns.js
  - SCHEMA_MAP smell entry: docs/SCHEMA_MAP.md § "Known schema smells" #3

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 08:11:08 -05:00
Randall Stillwell
67073aab7f feat(catalog): Scryfall bulk data import + Tagger community tags
Add full Scryfall bulk data pipeline:

- Migration: 13 new columns on `cards` (oracle_id, illustration_id,
  color_identity, keywords, legalities, flavor_text, artist, released_at,
  layout, edhrec_rank, reserved, reprint, finishes) with GIN indexes
  for JSONB search.
- Migration: `tags` + `card_tags` tables for Tagger community data.
- Script: `bulk-import-scryfall.js` — downloads Oracle Cards bulk file
  (168 MB) and upserts all 36k+ MTG cards with rich metadata.
- Script: `import-scryfall-tags.js` — imports oracle tags (4.5k tags,
  227k taggings) and art tags (11k tags, 458k taggings).
- Lib: `bulk-sync.js` — runtime bulk sync callable from the admin API.
- Admin UI: mode toggle (incremental vs bulk) on catalog sync panel.

Enables Commander deck validation (color_identity), format legality
checks, keyword search, EDHREC popularity ranking, and functional
card tagging ("removal", "ramp", "draw") for deck building assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 07:51:39 -05:00