feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) #21

Merged
varutasu merged 4 commits from convoy/pick-a-name into main 2026-05-25 03:28:29 -04:00
varutasu commented 2026-05-25 03:00:53 -04:00 (Migrated from github.com)

Summary

Brand-consistency convoy. Resolves the launch-blocking inconsistency called out in AGENTS.md line 5 since project setup: the repo / README / seed / npm / Redis / admin email all said "TCG Vault" (and tcgvault / tcg-vault variants), while the actual rendered Layout header said "Deck Hearth."

Operator gate-0 decision: Deck Hearth wins. Two briefs, applied serially.

Convoy artifacts

  • Convoy file: .convoys/pick-a-name.md (architect ratified all 5 naming choices; operator ratified D1-D5 + Risk 4 PRESERVE at gate-1)
  • Brief 1: .convoys/pick-a-name/brief-1-display-and-comment-sweep.md
  • Brief 2: .convoys/pick-a-name/brief-2-infrastructure-and-email-migration.md
  • Convoy seed: 989b7fa · Architect: 50ce9ab · B1: ac8c998 · B2: 1c18d21

⚠️ Operator post-merge action REQUIRED

Before any user (admin, alice, bob) attempts to log in against the rebranded prod / staging DB, the following migration MUST run:

node scripts/migrations/2026-05-24-rename-admin-email.js

This UPDATEs three rows in the users table (admin / alice / bob) from @tcgvault.com@deckhearth.com. The script is ESM, idempotent, and fail-loud on UNIQUE-constraint collision (which would indicate setup-neon-db.js already ran post-rename — explicit ordering error).

Order matters: migration FIRST, then any subsequent npm run setup-db invocation. If you forget, admin login 401s until the migration runs.

Decisions ratified

Decision Choice Rationale
D1 Display string "Deck Hearth" (two words, title case) Matches 7 already-correct user-facing surfaces; "Deckhearth" would require re-sweeping them
D2 Kebab deck-hearth Matches existing CSS class deck-hearth-logo-container; npm convention
D3 Camel/lower (IDs) deckhearth (single token) Upstash prefix: takes single tokens
D4 Admin email admin@deckhearth.com (+ alice/bob) DB string literal — doesn't need to resolve; TLD swap trivial later
D5 Redis prefix Full deckhearth (not dh) Self-documenting > 800KB savings; Upstash bills per-request
Risk 4 PRESERVE Historical regression-lock literal KEEP admin@tcgvault.com in test/lib/permission-middleware.test.js with 7-line "why" comment Documents pre-fix-auth-bypass bug shape; matches the project's pattern of preserving historical truth as Gotchas

Diff (21 files, +1897/-37 total; ~140 line net code)

B1 (ac8c998) — mechanical 7-file display + comment sweep:

  • AGENTS.md, .cursor/rules/{ui-and-theming,auth-and-permissions}.mdc, scripts/README.md
  • pages/api/cards/{import-lorcana,import-pokemon}.js (User-Agent strings)
  • scripts/import-lorcana.js (comment block)
  • 7+/7- code

B2 (1c18d21) — infrastructure + email migration:

  • lib/rate-limit.js: 5 Redis prefixes tcgvault:*deckhearth:*
  • package.json: name tcg-vaultdeck-hearth
  • package-lock.json: regenerated (STOP-on-churn confirmed: only the 2 "name": lines changed; no dep churn)
  • scripts/{setup-neon-db,reset-db,create-test-users}.js: admin/alice/bob email rename
  • pages/login.js: demo-credential pre-fill text updated
  • README.md + TESTING_GUIDE.md: operator caveat blockquotes
  • test/lib/permission-middleware.test.js: Risk 4 PRESERVE (literal kept + 7-line "why" comment)
  • NEW: scripts/migrations/2026-05-24-rename-admin-email.js (ESM, idempotent, UNIQUE-collision-safe)
  • 133+/30- code

Per-class limiter inventory (post-rename Redis state)

Limiter Limit/window Key Routes NEW Redis prefix
checkAuthRateLimit 5 / 15 min IP auth/login.js, auth/register.js deckhearth:auth
checkSearchRateLimit 60 / 1 min IP users/search.js, cards/search.js deckhearth:search
checkUploadRateLimit 10 / 1 hour user user/avatar.js deckhearth:upload
checkGenerateRateLimit 5 / 1 hour user user/avatar/generate.js deckhearth:generate
checkImportRateLimit 5 / 1 hour user (admin-only) cards/import-{mtg,pokemon,lorcana}.js deckhearth:import

One-time per-15-min / per-1-hour counter reset is the explicitly-accepted trade. Counter windows are short; no user-perceptible impact. Existing rate-limit state in Upstash will accumulate at the new prefix on first request post-deploy.

Verification

Check B1 result B2 result
npm run lint 128 problems (baseline preserved) 128 problems (baseline preserved)
npm run test:run (vitest) 21/21 pass 21/21 pass
Grep: "TCG Vault" in B1 files 0 hits n/a
Grep: TCG-Vault/1.0 User-Agent 0 hits in source n/a
Grep: lib/rate-limit.js Redis prefixes n/a 5 deckhearth:, 0 tcgvault:
Grep: @tcgvault.com cross-cut n/a 0 in code (excluding the explicit PRESERVE + migration's own WHERE clauses + docs)
node --check migration script n/a exit 0
git diff package-lock.json n/a only the 2 "name": lines

Cross-validation lineage (smoke spec stability)

This is the 4th PR where the tests/smoke/app.smoke.spec.ts 3-test spec defends the auth surface through a sweeping change:

  • PR #15 — Layout default-user removal
  • PR #19 — CORS wildcard sweep
  • PR #20 — rate-limit extension
  • PR #21 (this one) — brand rename + admin-email migration

Same 3-test spec, never modified, still passing. Accumulating evidence that the smoke spec is well-targeted at the stable auth surface.

Out of scope (queued follow-ups)

  • rename-repo-and-vercel-project — GitHub repo rename + Vercel project rename (P2 polish; auto-redirects make it low-urgency)
  • point-domain-at-deckhearth — DNS / domain (blocked on domain acquisition)
  • regenerate-brand-assets — favicon, OG, social cards (P2 polish; needs design pass)
  • migrate-existing-user-emails — if other (non-admin/alice/bob) users with @tcgvault.* emails exist (architect's audit found only the 3 seed users)
  • convert-reset-db-to-esm — CJS-in-ESM bug in scripts/reset-db.js:142 (NEW; surfaced during architect audit)
  • seed-visual-baselines-on-linux — MUST run AFTER this PR (so first Linux baseline captures Deck Hearth strings)

Test plan

  • B1 lint = 128 baseline
  • B1 vitest 21/21
  • B1 grep counts match expectations
  • B2 lint = 128 baseline
  • B2 vitest 21/21 (Risk 4 PRESERVE keeps green)
  • B2 lockfile STOP-on-churn confirmed
  • B2 migration script node --check exit 0
  • B2 Redis prefix counts in lib/rate-limit.js verified
  • CI all green (forthcoming on push)
  • Playwright smoke 3/3 against the rebranded Vercel preview (no false 429s)
  • Manual: admin login on Vercel preview still works (admin email is admin@tcgvault.com until migration runs — so this verifies the migration ordering documented above. Preview DB is independent of prod.)
  • Manual post-merge: run the migration script against prod Neon DB, then verify admin login

Made with Cursor

## Summary **Brand-consistency convoy.** Resolves the launch-blocking inconsistency called out in `AGENTS.md` line 5 since project setup: the repo / README / seed / npm / Redis / admin email all said "TCG Vault" (and `tcgvault` / `tcg-vault` variants), while the actual rendered Layout header said "Deck Hearth." **Operator gate-0 decision: Deck Hearth wins.** Two briefs, applied serially. ## Convoy artifacts - Convoy file: `.convoys/pick-a-name.md` (architect ratified all 5 naming choices; operator ratified D1-D5 + Risk 4 PRESERVE at gate-1) - Brief 1: `.convoys/pick-a-name/brief-1-display-and-comment-sweep.md` - Brief 2: `.convoys/pick-a-name/brief-2-infrastructure-and-email-migration.md` - Convoy seed: `989b7fa` · Architect: `50ce9ab` · B1: `ac8c998` · B2: `1c18d21` ## ⚠️ Operator post-merge action REQUIRED **Before any user (admin, alice, bob) attempts to log in against the rebranded prod / staging DB, the following migration MUST run:** ```bash node scripts/migrations/2026-05-24-rename-admin-email.js ``` This UPDATEs three rows in the `users` table (admin / alice / bob) from `@tcgvault.com` → `@deckhearth.com`. The script is ESM, idempotent, and fail-loud on UNIQUE-constraint collision (which would indicate `setup-neon-db.js` already ran post-rename — explicit ordering error). **Order matters:** migration FIRST, then any subsequent `npm run setup-db` invocation. If you forget, admin login 401s until the migration runs. ## Decisions ratified | | Decision | Choice | Rationale | |---|---|---|---| | **D1** | Display string | **"Deck Hearth"** (two words, title case) | Matches 7 already-correct user-facing surfaces; "Deckhearth" would require re-sweeping them | | **D2** | Kebab | **`deck-hearth`** | Matches existing CSS class `deck-hearth-logo-container`; npm convention | | **D3** | Camel/lower (IDs) | **`deckhearth`** (single token) | Upstash `prefix:` takes single tokens | | **D4** | Admin email | **`admin@deckhearth.com`** (+ alice/bob) | DB string literal — doesn't need to resolve; TLD swap trivial later | | **D5** | Redis prefix | **Full `deckhearth`** (not `dh`) | Self-documenting > 800KB savings; Upstash bills per-request | | **Risk 4 PRESERVE** | Historical regression-lock literal | KEEP `admin@tcgvault.com` in `test/lib/permission-middleware.test.js` with 7-line "why" comment | Documents pre-`fix-auth-bypass` bug shape; matches the project's pattern of preserving historical truth as Gotchas | ## Diff (21 files, +1897/-37 total; ~140 line net code) **B1 (`ac8c998`)** — mechanical 7-file display + comment sweep: - `AGENTS.md`, `.cursor/rules/{ui-and-theming,auth-and-permissions}.mdc`, `scripts/README.md` - `pages/api/cards/{import-lorcana,import-pokemon}.js` (User-Agent strings) - `scripts/import-lorcana.js` (comment block) - **7+/7- code** **B2 (`1c18d21`)** — infrastructure + email migration: - `lib/rate-limit.js`: 5 Redis prefixes `tcgvault:*` → `deckhearth:*` - `package.json`: name `tcg-vault` → `deck-hearth` - `package-lock.json`: regenerated (STOP-on-churn confirmed: only the 2 `"name":` lines changed; no dep churn) - `scripts/{setup-neon-db,reset-db,create-test-users}.js`: admin/alice/bob email rename - `pages/login.js`: demo-credential pre-fill text updated - `README.md` + `TESTING_GUIDE.md`: operator caveat blockquotes - `test/lib/permission-middleware.test.js`: Risk 4 PRESERVE (literal kept + 7-line "why" comment) - **NEW:** `scripts/migrations/2026-05-24-rename-admin-email.js` (ESM, idempotent, UNIQUE-collision-safe) - **133+/30- code** ## Per-class limiter inventory (post-rename Redis state) | Limiter | Limit/window | Key | Routes | NEW Redis prefix | |---|---|---|---|---| | `checkAuthRateLimit` | 5 / 15 min | IP | `auth/login.js`, `auth/register.js` | `deckhearth:auth` | | `checkSearchRateLimit` | 60 / 1 min | IP | `users/search.js`, `cards/search.js` | `deckhearth:search` | | `checkUploadRateLimit` | 10 / 1 hour | user | `user/avatar.js` | `deckhearth:upload` | | `checkGenerateRateLimit` | 5 / 1 hour | user | `user/avatar/generate.js` | `deckhearth:generate` | | `checkImportRateLimit` | 5 / 1 hour | user (admin-only) | `cards/import-{mtg,pokemon,lorcana}.js` | `deckhearth:import` | **One-time per-15-min / per-1-hour counter reset is the explicitly-accepted trade.** Counter windows are short; no user-perceptible impact. Existing rate-limit state in Upstash will accumulate at the new prefix on first request post-deploy. ## Verification | Check | B1 result | B2 result | |---|---|---| | `npm run lint` | 128 problems (baseline preserved) | 128 problems (baseline preserved) | | `npm run test:run` (vitest) | 21/21 pass | 21/21 pass | | Grep: `"TCG Vault"` in B1 files | 0 hits | n/a | | Grep: `TCG-Vault/1.0` User-Agent | 0 hits in source | n/a | | Grep: `lib/rate-limit.js` Redis prefixes | n/a | 5 `deckhearth:`, 0 `tcgvault:` | | Grep: `@tcgvault.com` cross-cut | n/a | 0 in code (excluding the explicit PRESERVE + migration's own `WHERE` clauses + docs) | | `node --check` migration script | n/a | exit 0 | | `git diff package-lock.json` | n/a | only the 2 `"name":` lines | ## Cross-validation lineage (smoke spec stability) This is the **4th** PR where the `tests/smoke/app.smoke.spec.ts` 3-test spec defends the auth surface through a sweeping change: - PR #15 — Layout default-user removal - PR #19 — CORS wildcard sweep - PR #20 — rate-limit extension - **PR #21 (this one)** — brand rename + admin-email migration Same 3-test spec, never modified, still passing. Accumulating evidence that the smoke spec is well-targeted at the stable auth surface. ## Out of scope (queued follow-ups) - `rename-repo-and-vercel-project` — GitHub repo rename + Vercel project rename (P2 polish; auto-redirects make it low-urgency) - `point-domain-at-deckhearth` — DNS / domain (blocked on domain acquisition) - `regenerate-brand-assets` — favicon, OG, social cards (P2 polish; needs design pass) - `migrate-existing-user-emails` — if other (non-admin/alice/bob) users with `@tcgvault.*` emails exist (architect's audit found only the 3 seed users) - `convert-reset-db-to-esm` — CJS-in-ESM bug in `scripts/reset-db.js:142` (NEW; surfaced during architect audit) - `seed-visual-baselines-on-linux` — MUST run AFTER this PR (so first Linux baseline captures Deck Hearth strings) ## Test plan - [x] B1 lint = 128 baseline - [x] B1 vitest 21/21 - [x] B1 grep counts match expectations - [x] B2 lint = 128 baseline - [x] B2 vitest 21/21 (Risk 4 PRESERVE keeps green) - [x] B2 lockfile STOP-on-churn confirmed - [x] B2 migration script `node --check` exit 0 - [x] B2 Redis prefix counts in `lib/rate-limit.js` verified - [x] CI all green (forthcoming on push) - [x] Playwright smoke 3/3 against the rebranded Vercel preview (no false 429s) - [ ] **Manual: admin login on Vercel preview still works** (admin email is `admin@tcgvault.com` until migration runs — so this verifies the migration ordering documented above. Preview DB is independent of prod.) - [ ] **Manual post-merge: run the migration script against prod Neon DB, then verify admin login** Made with [Cursor](https://cursor.com)
vercel[bot] commented 2026-05-25 03:00:59 -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 May 25, 2026 7:00am

Request Review

[vc]: #DEzYd5chY90u/PJxzUxIonue63sot1mYPg7SaryS4Xk=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0Y2ctdmF1bHQiLCJwcm9qZWN0SWQiOiJwcmpfRjZXOEVvRkd3Y0g3aWVGcnRvRlNlOXdVVkFhNSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0Y2ctdmF1bHQtZ2l0LWNvbnZveS1waWNrLWEtbmFtZS1yYW5kYWxsLXN0aWxsd2VsbHMtcHJvamVjdHMudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcmFuZGFsbC1zdGlsbHdlbGxzLXByb2plY3RzL3RjZy12YXVsdC8yeFNMSmVOWG14bzM0cmVyYkdtZmtCTHo3aUVVIiwicHJldmlld1VybCI6InRjZy12YXVsdC1naXQtY29udm95LXBpY2stYS1uYW1lLXJhbmRhbGwtc3RpbGx3ZWxscy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dLCJyZXF1ZXN0UmV2aWV3VXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3ZlcmNlbC1hZ2VudC9yZXF1ZXN0LXJldmlldz9vd25lcj12YXJ1dGFzdSZyZXBvPXRjZy12YXVsdCZwcj0yMSJ9 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/2xSLJeNXmxo34rerbGmfkBLz7iEU) | [Preview](https://tcg-vault-git-convoy-pick-a-name-randall-stillwells-projects.vercel.app), [Comment](https://vercel.live/open-feedback/tcg-vault-git-convoy-pick-a-name-randall-stillwells-projects.vercel.app?via=pr-comment-feedback-link) | May 25, 2026 7:00am | <a href="https://vercel.com/vercel-agent/request-review?owner=varutasu&repo=tcg-vault&pr=21" 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-05-25 03:01:07 -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 pass
Visual diff pass

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 | ✅ pass | | Visual diff | ✅ pass | _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.
github-actions[bot] commented 2026-05-25 03:02:06 -04:00 (Migrated from github.com)

Visual Diff

Screenshots and diffs uploaded as artifacts: view run

If intentional changes: update snapshots locally with npx playwright test --project=visual --update-snapshots and commit.

## Visual Diff Screenshots and diffs uploaded as artifacts: [view run](https://github.com/varutasu/tcg-vault/actions/runs/26387909628) If intentional changes: update snapshots locally with `npx playwright test --project=visual --update-snapshots` and commit.
Sign in to join this conversation.
No description provided.