feat(migrations): reconcile favorites system missed by initial-schema backfill (B4) #152

Merged
varutasu merged 1 commit from convoy/reconcile-b4-favorites-system into main 2026-06-14 09:11:27 -04:00
varutasu commented 2026-06-14 09:05:13 -04:00 (Migrated from github.com)

Summary

Brief B4 of the reconcile-historical-add-scripts convoy (commit 22ebef2).

Adds migrations/1781000000004_reconcile-favorites-system.js, an
idempotent reconciliation migration that captures the DDL effects of
the historical scripts/add-favorites-system.js job (a no-go-zone
script per .cursor/rules/no-go-zones.mdc). After this lands, a
brand-new Neon branch onboarded via npm installnpm run setup-db
has the same user_favorites table + four supporting indexes that
prod has acquired via the historical script.

Shape parity

Byte-equivalent in intent to the historical script and to the runtime
API at pages/api/favorites.js:

  • user_favorites(id SERIAL PK, user_id INTEGER FK CASCADE, item_type VARCHAR(50) NOT NULL, item_id INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, item_type, item_id))
  • idx_user_favorites_user_id
  • idx_user_favorites_item_type
  • idx_user_favorites_item_id
  • idx_user_favorites_user_type (composite on user_id, item_type)

FK style follows migrations/1779853647564_initial-schema.js
(INTEGER REFERENCES users(id) ON DELETE CASCADE).

Idempotency (D2)

Every statement is guarded with IF NOT EXISTS. Re-running npm run migrate up against any long-lived env where the historical script
already ran is a documented no-op — only the new pgmigrations row is
written. The UNIQUE constraint is declared inline on the table so it
is created at the same moment as the table and never needs a separate
ADD CONSTRAINT path.

Down migration

Hard-stub throw. This is a reconciliation migration whose purpose is
to bring fresh envs to parity with prod; rolling back would drop
user_favorites (and every row in it) on whatever env it runs
against. Removing the favorites surface deserves its own scoped
convoy that audits readers first.

Files changed

  • migrations/1781000000004_reconcile-favorites-system.js (new)

Acceptance criteria

  • Captures the DDL of scripts/add-favorites-system.js (table +
    4 indexes) — verified line-by-line against the source script.
  • Idempotent against fresh AND prod-shaped envs
    (CREATE TABLE IF NOT EXISTS + 4 × CREATE INDEX IF NOT EXISTS).
  • Matches the runtime shape used by pages/api/favorites.js
    (polymorphic item_type ∈ {'card','collection','deck'} +
    polymorphic item_id, keyed by
    (user_id, item_type, item_id)).
  • Pre-assigned timestamp 1781000000004 (no collision with
    sibling B1/B2/B3/B5/B6 timestamps per convoy § Brief outline
    "Timestamp coordination").
  • down() is a hard-stub throw.
  • Scope is exactly one file per brief frontmatter
    (files: [migrations/<ts4>_reconcile-favorites-system.js]).
  • No edits to scripts/add-favorites-system.js or any other
    no-go-zone artifact.
  • No edits to the architect's convoy file.

Test plan

Static verification (run locally on the worktree):

  • node --check migrations/1781000000004_*.js → exit 0.
  • npm run lint → 0 errors (baseline; 1 pre-existing warning in
    components/CollectionsPageView.js unrelated to this change).
  • npm run test:run → 26 files / 131 tests pass (baseline; no
    new tests needed — pure DDL reconciliation, no JS-level code).
  • Static idempotency proof: every CREATE statement uses
    IF NOT EXISTS; the inline UNIQUE is created at table-creation
    time so it cannot collide on re-run.
  • Confirmed user_favorites is not declared by any existing
    migration (migrations/ grep for user_favorites /
    favorites returns zero matches pre-this-PR).

Optional Neon-branch test (per convoy § Verification plan D5) — defer
to B7's post-B1-B6 prod-vs-fresh-branch diff runbook; not blocking
this PR.

Notes

  • Runtime evidence audit: pages/api/favorites.js issues five
    distinct SQL statements against user_favoritesSELECT (with
    optional item_type filter), SELECT for the duplicate-detection
    check, INSERT, and DELETE. Every column it references
    (user_id, item_type, item_id, created_at, plus uf.*) is
    covered by this migration. The polymorphic item_id is correctly
    typed as INTEGER and joins against collections.id, cards.id,
    and decks.id — all SERIAL PKs from initial-schema.

  • The .cursor/rules/schema-map.mdc § "Common reference" entry lists
    favorites (user_id, card_id) — that's a minimal shorthand and
    does NOT match the actual prod table (user_favorites, polymorphic
    on item_type + item_id). The rule's wording will be corrected
    by B7 of this convoy alongside the docs/SCHEMA_MAP.md refresh;
    this migration follows the runtime evidence.

  • Per convoy § Slice dependencies, this brief's depends_on: [] and
    the only file in files: is the new migration — disjoint from
    B1/B2/B3/B5/B6, so the implementer dispatched in parallel with the
    other briefs.

  • Base is main (not the parent convoy branch) per the
    parallel-implementer dispatch pattern documented in the brief.

Made with Cursor

## Summary Brief **B4** of the [`reconcile-historical-add-scripts`](.convoys/reconcile-historical-add-scripts.md) convoy (commit `22ebef2`). Adds `migrations/1781000000004_reconcile-favorites-system.js`, an idempotent reconciliation migration that captures the DDL effects of the historical `scripts/add-favorites-system.js` job (a no-go-zone script per `.cursor/rules/no-go-zones.mdc`). After this lands, a brand-new Neon branch onboarded via `npm install` → `npm run setup-db` has the same `user_favorites` table + four supporting indexes that prod has acquired via the historical script. ### Shape parity Byte-equivalent in intent to the historical script and to the runtime API at `pages/api/favorites.js`: - `user_favorites(id SERIAL PK, user_id INTEGER FK CASCADE, item_type VARCHAR(50) NOT NULL, item_id INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, item_type, item_id))` - `idx_user_favorites_user_id` - `idx_user_favorites_item_type` - `idx_user_favorites_item_id` - `idx_user_favorites_user_type` (composite on `user_id, item_type`) FK style follows `migrations/1779853647564_initial-schema.js` (`INTEGER REFERENCES users(id) ON DELETE CASCADE`). ### Idempotency (D2) Every statement is guarded with `IF NOT EXISTS`. Re-running `npm run migrate up` against any long-lived env where the historical script already ran is a documented no-op — only the new `pgmigrations` row is written. The `UNIQUE` constraint is declared inline on the table so it is created at the same moment as the table and never needs a separate `ADD CONSTRAINT` path. ### Down migration Hard-stub throw. This is a reconciliation migration whose purpose is to bring fresh envs to parity with prod; rolling back would drop `user_favorites` (and every row in it) on whatever env it runs against. Removing the favorites surface deserves its own scoped convoy that audits readers first. ### Files changed - `migrations/1781000000004_reconcile-favorites-system.js` (new) ### Acceptance criteria - [x] Captures the DDL of `scripts/add-favorites-system.js` (table + 4 indexes) — verified line-by-line against the source script. - [x] Idempotent against fresh AND prod-shaped envs (`CREATE TABLE IF NOT EXISTS` + 4 × `CREATE INDEX IF NOT EXISTS`). - [x] Matches the runtime shape used by `pages/api/favorites.js` (polymorphic `item_type` ∈ {'card','collection','deck'} + polymorphic `item_id`, keyed by `(user_id, item_type, item_id)`). - [x] Pre-assigned timestamp `1781000000004` (no collision with sibling B1/B2/B3/B5/B6 timestamps per convoy § Brief outline "Timestamp coordination"). - [x] `down()` is a hard-stub throw. - [x] Scope is exactly one file per brief frontmatter (`files: [migrations/<ts4>_reconcile-favorites-system.js]`). - [x] No edits to `scripts/add-favorites-system.js` or any other no-go-zone artifact. - [x] No edits to the architect's convoy file. ### Test plan Static verification (run locally on the worktree): - [x] `node --check migrations/1781000000004_*.js` → exit 0. - [x] `npm run lint` → 0 errors (baseline; 1 pre-existing warning in `components/CollectionsPageView.js` unrelated to this change). - [x] `npm run test:run` → 26 files / 131 tests pass (baseline; no new tests needed — pure DDL reconciliation, no JS-level code). - [x] Static idempotency proof: every CREATE statement uses `IF NOT EXISTS`; the inline `UNIQUE` is created at table-creation time so it cannot collide on re-run. - [x] Confirmed `user_favorites` is not declared by any existing migration (`migrations/` grep for `user_favorites` / `favorites` returns zero matches pre-this-PR). Optional Neon-branch test (per convoy § Verification plan D5) — defer to B7's post-B1-B6 prod-vs-fresh-branch diff runbook; not blocking this PR. ### Notes - Runtime evidence audit: `pages/api/favorites.js` issues five distinct SQL statements against `user_favorites` — `SELECT` (with optional `item_type` filter), `SELECT` for the duplicate-detection check, `INSERT`, and `DELETE`. Every column it references (`user_id`, `item_type`, `item_id`, `created_at`, plus `uf.*`) is covered by this migration. The polymorphic `item_id` is correctly typed as `INTEGER` and joins against `collections.id`, `cards.id`, and `decks.id` — all SERIAL PKs from `initial-schema`. - The `.cursor/rules/schema-map.mdc` § "Common reference" entry lists `favorites (user_id, card_id)` — that's a minimal shorthand and does NOT match the actual prod table (`user_favorites`, polymorphic on `item_type` + `item_id`). The rule's wording will be corrected by B7 of this convoy alongside the `docs/SCHEMA_MAP.md` refresh; this migration follows the runtime evidence. - Per convoy § Slice dependencies, this brief's `depends_on: []` and the only file in `files:` is the new migration — disjoint from B1/B2/B3/B5/B6, so the implementer dispatched in parallel with the other briefs. - Base is `main` (not the parent convoy branch) per the parallel-implementer dispatch pattern documented in the brief. <!-- pipeline: brief=4, convoy=reconcile-historical-add-scripts --> Made with [Cursor](https://cursor.com)
vercel[bot] commented 2026-06-14 09:05:20 -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:05pm

Request Review

[vc]: #MYzB8cSCl3+EVWKjnUPzXmBKSxPgOAbK4nPq/z+Bcao=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0Y2ctdmF1bHQiLCJwcm9qZWN0SWQiOiJwcmpfRjZXOEVvRkd3Y0g3aWVGcnRvRlNlOXdVVkFhNSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0Y2ctdmF1bHQtZ2l0LWNvbnZveS1yZWNvbmNpLTFhMDYxZC1yYW5kYWxsLXN0aWxsd2VsbHMtcHJvamVjdHMudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcmFuZGFsbC1zdGlsbHdlbGxzLXByb2plY3RzL3RjZy12YXVsdC9IRlVkVkZBQk1wNHZoU3Z5NjNqOHBYd1NjZ1FaIiwicHJldmlld1VybCI6InRjZy12YXVsdC1naXQtY29udm95LXJlY29uY2ktMWEwNjFkLXJhbmRhbGwtc3RpbGx3ZWxscy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dLCJyZXF1ZXN0UmV2aWV3VXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3ZlcmNlbC1hZ2VudC9yZXF1ZXN0LXJldmlldz9vd25lcj1zdHdsLWxhYnMmcmVwbz10Y2ctdmF1bHQmcHI9MTUyIn0= 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/HFUdVFABMp4vhSvy63j8pXwScgQZ) | [Preview](https://tcg-vault-git-convoy-reconci-1a061d-randall-stillwells-projects.vercel.app), [Comment](https://vercel.live/open-feedback/tcg-vault-git-convoy-reconci-1a061d-randall-stillwells-projects.vercel.app?via=pr-comment-feedback-link) | Jun 14, 2026 1:05pm | <a href="https://vercel.com/vercel-agent/request-review?owner=stwl-labs&repo=tcg-vault&pr=152" 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:06:30 -04:00 (Migrated from github.com)

Pipeline Health

Build + CI gates

Gate Status
Vercel build (Preview) pass
CI: Lint in progress
CI: Schema map fresh skipped
Preview smoke ⏭ skipped or pending
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 | ⏳ in progress | | CI: Schema map fresh | ❌ skipped | | Preview smoke | ⏭ skipped or pending | | 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.