feat(migrations): reconcile cards columns missed by initial-schema backfill (B1 of reconcile-historical-add-scripts) #148

Merged
varutasu merged 1 commit from convoy/reconcile-b1-cards-columns into main 2026-06-14 09:11:08 -04:00
varutasu commented 2026-06-14 09:03:25 -04:00 (Migrated from github.com)

Summary

Brief 1 (B1) of the reconcile-historical-add-scripts convoy. Folds the effects of scripts/add-card-columns.js into the migration history so a brand-new Neon branch onboarded by npm installnpm run setup-db ends up structurally equivalent to prod on the cards table.

Adds exactly one new migration: migrations/1781000000001_reconcile-cards-columns.js (82 lines including docstring + down() stub).

Architect reference

  • Plan: .convoys/reconcile-historical-add-scripts.md on convoy/reconcile-historical-add-scripts (architect commit 22ebef2)
  • Brief outline § B1 — "Reconcile cards columns" (captures script #1; script #7's updated_at is already in initial-schema)
  • Decision D2 — raw pgm.sql(...) + IF NOT EXISTS guards matching 1779853647564_initial-schema.js's style
  • Decision D7 — pre-assigned timestamp 1781000000001 (avoids parallel-implementer Date.now() collisions with B2-B6)
  • Finding 3 — cards.quantity + cards.favorited are dead columns flagged in docs/SCHEMA_MAP.md § "Known schema smells" #3; reproduce-then-deprecate via follow-up

What changed

Two new columns added to cards with idempotency guards:

Column Type Default Source Rationale
quantity INTEGER 0 scripts/add-card-columns.js lines 22-25 Live in prod; flagged unused (live semantics on user_cards); folded for fresh-env parity
favorited BOOLEAN false scripts/add-card-columns.js lines 29-32 Live in prod; flagged unused (live semantics on user_favorites); folded for fresh-env parity

Both use ALTER TABLE cards ADD COLUMN IF NOT EXISTS …, byte-equivalent in intent to the historical script.

down() is a hard-stub throw new Error(...) matching the shape of 1779853647564_initial-schema.js::down() — these are reconciliation migrations; prod schema state is the source of truth and rolling back would create fresh-vs-prod inconsistency. Retirement of either column belongs to the queued drop-dead-cards-columns convoy (see Finding 3 follow-up).

Idempotency proof (D2)

Static grep against the migration tree confirms neither column exists in any prior migration:

$ 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,

Those three hits are on user_cards, collection_cards, and deck_cards respectively (lines 72-83, 99-107, 122-131 — each is inside a different CREATE TABLE block). The CREATE TABLE cards block in initial-schema spans lines 45-69 and contains neither column.

This means the new migration is safe to run against:

  1. Fresh Neon branch where initial-schema just ran → columns do not exist → ALTER adds them.
  2. Long-lived prod env where scripts/add-card-columns.js ran pre-migration-tool → columns already exist → ALTER is a no-op (only pgmigrations row recorded).
  3. Already-applied this migration → ALTER is a no-op as in (2).

Verification

Check Result
node --check migrations/1781000000001_reconcile-cards-columns.js exit 0
npm run lint 0 errors, 1 pre-existing warning unchanged from main (components/CollectionsPageView.js's unused eslint-disable; unrelated to this change)
npm run test:run 131 tests pass across 26 files
npm run migrate up end-to-end against a throwaway Neon branch Deferred to operator post-merge verification per D5 (D5 manual runbook in .convoys/reconcile-historical-add-scripts.md § Verification plan; B7 promotes it to docs/operations/RECONCILE-VERIFICATION.md)

Notes on PR #32's NEW post-architect migrations on cards

The architect plan (commit 22ebef2) was drafted before PR #32 landed 1781440700404_add-scryfall-bulk-columns.js, which adds 13 Scryfall bulk-data columns to cards (oracle_id, illustration_id, color_identity, keywords, legalities, flavor_text, artist, released_at, layout, edhrec_rank, reserved, reprint, finishes).

Per the brief's "Read FIRST" step 5: verified by reading the migration in full + grepping for quantity / favorited. No overlap with B1's scope. No scope reduction required; B1 ships as-planned with both columns.

1781440721350_add-tagger-tables.js (also new since architect) is on Tagger join tables — not on cards directly. No overlap.

Test plan

  • node --check clean
  • npm run lint — no new problems (pre-existing warning unchanged)
  • npm run test:run — 131/131 pass
  • Static idempotency proof: neither quantity nor favorited is in any prior cards-touching migration
  • Operator (post-merge): branch a fresh Neon DB, npm run setup-db, run D5 verification plan to confirm structural parity with prod's cards table

References

  • Brief 1 prompt + architect plan: .convoys/reconcile-historical-add-scripts.md (commit 22ebef2)
  • Historical source (no-go-zone, NOT edited): scripts/add-card-columns.js
  • SCHEMA_MAP smell to clear up via follow-up: docs/SCHEMA_MAP.md § "Known schema smells" #3

Made with Cursor

## Summary Brief 1 (B1) of the `reconcile-historical-add-scripts` convoy. Folds the effects of `scripts/add-card-columns.js` into the migration history so a brand-new Neon branch onboarded by `npm install` → `npm run setup-db` ends up structurally equivalent to prod on the `cards` table. Adds exactly one new migration: `migrations/1781000000001_reconcile-cards-columns.js` (82 lines including docstring + `down()` stub). ## Architect reference - Plan: `.convoys/reconcile-historical-add-scripts.md` on `convoy/reconcile-historical-add-scripts` (architect commit `22ebef2`) - Brief outline § B1 — "Reconcile cards columns" (captures script #1; script #7's `updated_at` is already in initial-schema) - Decision D2 — raw `pgm.sql(...)` + `IF NOT EXISTS` guards matching `1779853647564_initial-schema.js`'s style - Decision D7 — pre-assigned timestamp `1781000000001` (avoids parallel-implementer `Date.now()` collisions with B2-B6) - Finding 3 — `cards.quantity` + `cards.favorited` are dead columns flagged in `docs/SCHEMA_MAP.md` § "Known schema smells" #3; reproduce-then-deprecate via follow-up ## What changed Two new columns added to `cards` with idempotency guards: | Column | Type | Default | Source | Rationale | | --- | --- | --- | --- | --- | | `quantity` | `INTEGER` | `0` | `scripts/add-card-columns.js` lines 22-25 | Live in prod; flagged unused (live semantics on `user_cards`); folded for fresh-env parity | | `favorited` | `BOOLEAN` | `false` | `scripts/add-card-columns.js` lines 29-32 | Live in prod; flagged unused (live semantics on `user_favorites`); folded for fresh-env parity | Both use `ALTER TABLE cards ADD COLUMN IF NOT EXISTS …`, byte-equivalent in intent to the historical script. `down()` is a hard-stub `throw new Error(...)` matching the shape of `1779853647564_initial-schema.js::down()` — these are reconciliation migrations; prod schema state is the source of truth and rolling back would create fresh-vs-prod inconsistency. Retirement of either column belongs to the queued `drop-dead-cards-columns` convoy (see Finding 3 follow-up). ## Idempotency proof (D2) Static grep against the migration tree confirms neither column exists in any prior migration: ``` $ 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, ``` Those three hits are on `user_cards`, `collection_cards`, and `deck_cards` respectively (lines 72-83, 99-107, 122-131 — each is inside a different `CREATE TABLE` block). The `CREATE TABLE cards` block in initial-schema spans lines 45-69 and contains neither column. This means the new migration is safe to run against: 1. **Fresh Neon branch** where initial-schema just ran → columns do not exist → ALTER adds them. 2. **Long-lived prod env** where `scripts/add-card-columns.js` ran pre-migration-tool → columns already exist → ALTER is a no-op (only `pgmigrations` row recorded). 3. **Already-applied this migration** → ALTER is a no-op as in (2). ## Verification | Check | Result | | --- | --- | | `node --check migrations/1781000000001_reconcile-cards-columns.js` | exit 0 | | `npm run lint` | 0 errors, 1 pre-existing warning unchanged from `main` (`components/CollectionsPageView.js`'s unused eslint-disable; unrelated to this change) | | `npm run test:run` | 131 tests pass across 26 files | | `npm run migrate up` end-to-end against a throwaway Neon branch | **Deferred to operator post-merge verification per D5** (D5 manual runbook in `.convoys/reconcile-historical-add-scripts.md` § Verification plan; B7 promotes it to `docs/operations/RECONCILE-VERIFICATION.md`) | ## Notes on PR #32's NEW post-architect migrations on `cards` The architect plan (commit `22ebef2`) was drafted before PR #32 landed `1781440700404_add-scryfall-bulk-columns.js`, which adds 13 Scryfall bulk-data columns to `cards` (`oracle_id`, `illustration_id`, `color_identity`, `keywords`, `legalities`, `flavor_text`, `artist`, `released_at`, `layout`, `edhrec_rank`, `reserved`, `reprint`, `finishes`). Per the brief's "Read FIRST" step 5: verified by reading the migration in full + grepping for `quantity` / `favorited`. **No overlap with B1's scope.** No scope reduction required; B1 ships as-planned with both columns. `1781440721350_add-tagger-tables.js` (also new since architect) is on Tagger join tables — not on `cards` directly. No overlap. ## Test plan - [x] `node --check` clean - [x] `npm run lint` — no new problems (pre-existing warning unchanged) - [x] `npm run test:run` — 131/131 pass - [x] Static idempotency proof: neither `quantity` nor `favorited` is in any prior `cards`-touching migration - [ ] **Operator (post-merge):** branch a fresh Neon DB, `npm run setup-db`, run D5 verification plan to confirm structural parity with prod's `cards` table ## References - Brief 1 prompt + architect plan: `.convoys/reconcile-historical-add-scripts.md` (commit `22ebef2`) - Historical source (no-go-zone, NOT edited): `scripts/add-card-columns.js` - SCHEMA_MAP smell to clear up via follow-up: `docs/SCHEMA_MAP.md` § "Known schema smells" #3 <!-- pipeline: brief=1, convoy=reconcile-historical-add-scripts --> Made with [Cursor](https://cursor.com)
vercel[bot] commented 2026-06-14 09:03:31 -04:00 (Migrated from github.com)

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tcg-vault Ready Ready Preview, Comment Jun 14, 2026 1:03pm

Request Review

[vc]: #ZyRLt9AQOS0dzGw0WhCEM2IkSvKLneaHjfwac854gGo=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0Y2ctdmF1bHQiLCJwcm9qZWN0SWQiOiJwcmpfRjZXOEVvRkd3Y0g3aWVGcnRvRlNlOXdVVkFhNSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0Y2ctdmF1bHQtZ2l0LWNvbnZveS1yZWNvbmNpLWM3MGU1OC1yYW5kYWxsLXN0aWxsd2VsbHMtcHJvamVjdHMudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcmFuZGFsbC1zdGlsbHdlbGxzLXByb2plY3RzL3RjZy12YXVsdC81dTNBQWVIUWVCNWtVUUVQOXZXZ1RhbVZSNTY2IiwicHJldmlld1VybCI6InRjZy12YXVsdC1naXQtY29udm95LXJlY29uY2ktYzcwZTU4LXJhbmRhbGwtc3RpbGx3ZWxscy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dLCJyZXF1ZXN0UmV2aWV3VXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3ZlcmNlbC1hZ2VudC9yZXF1ZXN0LXJldmlldz9vd25lcj1zdHdsLWxhYnMmcmVwbz10Y2ctdmF1bHQmcHI9MTQ4In0= The latest updates on your projects. Learn more about [Vercel for GitHub](https://vercel.link/github-learn-more). | Project | Deployment | Actions | Updated (UTC) | | :--- | :----- | :------ | :------ | | [tcg-vault](https://vercel.com/randall-stillwells-projects/tcg-vault) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/randall-stillwells-projects/tcg-vault/5u3AAeHQeB5kUQEP9vWgTamVR566) | [Preview](https://tcg-vault-git-convoy-reconci-c70e58-randall-stillwells-projects.vercel.app), [Comment](https://vercel.live/open-feedback/tcg-vault-git-convoy-reconci-c70e58-randall-stillwells-projects.vercel.app?via=pr-comment-feedback-link) | Jun 14, 2026 1:03pm | <a href="https://vercel.com/vercel-agent/request-review?owner=stwl-labs&repo=tcg-vault&pr=148" rel="noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://agents-vade-review.vercel.sh/request-review-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://agents-vade-review.vercel.sh/request-review-light.svg"><img src="https://agents-vade-review.vercel.sh/request-review-light.svg" alt="Request Review"></picture></a>
github-actions[bot] commented 2026-06-14 09:03:39 -04:00 (Migrated from github.com)

Pipeline Health

Build + CI gates

Gate Status
Vercel build (Preview) pass
CI: Lint pass
CI: Schema map fresh skipped
Preview smoke in progress
Visual diff ⏭ skipped or pending

Build runs on Vercel; this CI runs lint and schema-map drift only (no duplicate build).

Role reports

Role Status
Reviewer report pending
A11y audit pending
Design system audit pending

See individual comments above for details. This rollup updates automatically.

<!-- pipeline-rollup --> ## Pipeline Health ### Build + CI gates | Gate | Status | | --- | --- | | Vercel build (Preview) | ✅ pass | | CI: Lint | ✅ pass | | CI: Schema map fresh | ❌ skipped | | Preview smoke | ⏳ in progress | | Visual diff | ⏭ skipped or pending | _Build runs on Vercel; this CI runs lint and schema-map drift only (no duplicate build)._ ### Role reports | Role | Status | | --- | --- | | Reviewer report | ⏳ pending | | A11y audit | ⏳ pending | | Design system audit | ⏳ pending | See individual comments above for details. This rollup updates automatically.
Sign in to join this conversation.
No description provided.