deckhearth/.convoys/pick-a-name.md

910 lines
50 KiB
Markdown
Raw Normal View History

feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) Resolves the launch-blocking 'TCG Vault vs Deck Hearth' inconsistency called out in AGENTS.md line 5 since project setup. Operator gate-0 decision: Deck Hearth wins. Two briefs applied serially. B1 (mechanical): 7-file display + comment sweep. B2 (infrastructure): Redis prefix rename in lib/rate-limit.js (5 prefixes, accept one-time counter reset), package.json + lockfile regen (STOP-on-churn confirmed only name lines changed), admin/alice/bob email rename in seed scripts + login pre-fill + NEW idempotent migration script scripts/migrations/2026-05-24-rename-admin-email.js. Risk 4 PRESERVE applied: test/lib/permission-middleware.test.js retains admin@tcgvault.com literal with 7-line architect-authored why comment (documents pre-fix-auth-bypass bug shape; preserves historical truth per project's gotcha-documentation convention). All 5 D-decisions ratified at gate-1 (Deck Hearth / deck-hearth / deckhearth / admin@deckhearth.com / full deckhearth Redis prefix). Local: lint 128 baseline (B1 + B2), vitest 21/21 (B1 + B2). CI all green: Playwright smoke 3/3 against rebranded preview in 1m4s, forbidden-cors-headers pass, forbidden-endpoints pass, Screenshot diff pass, Vercel deployment complete. Cross-validation lineage: 4th convoy where the same 3-test smoke spec defends auth surface through sweeping change (after PR #15 Layout default-user, PR #19 CORS, PR #20 rate-limit, now this PR #21 brand rename). OPERATOR POST-MERGE ACTION REQUIRED: run 'node scripts/migrations/2026-05-24-rename-admin-email.js' against prod Neon DB before next admin login (ordering: migration FIRST, then any subsequent setup-db invocation). Migration is ESM, idempotent, UNIQUE-collision-safe. PR #21 architect-commit 50ce9ab, B1 ac8c998, B2 1c18d21.
2026-05-25 03:28:29 -04:00
# pick-a-name (P1 — pre-launch brand consistency)
docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy Reflects the merged pick-a-name convoy (PR #21, squash commit 9abbab6) in repo documentation. **This is the first post-P0 cleanup** — add-rate-limiting (squash 708ef45) closed the LAST P0 ship-blocker on 2026-05-24, and pick-a-name immediately follows as the first post-P0 P1 quality-bar convoy, closing P1 #12 (brand-consistency — the inconsistency `AGENTS.md` line 5 had flagged since project setup). Two file-disjoint briefs landed serially (B1 commit ac8c998 display + comment sweep, B2 commit 1c18d21 infrastructure + email migration); five canonical-string D-decisions ratified verbatim at gate-1 plus Risk 4 PRESERVE on the negative regression-lock literal. Architect commit 50ce9ab; no scope expansions and no implementer deviations from the verbatim spec beyond the parallel-vs-serial dispatch choice (parallelizable in theory; serial in practice for lockfile-regen + migration-script audit cleanliness). .convoys/pick-a-name.md: - frontmatter Status: in-progress -> shipped (added Shipped: 2026-05-24 line, matching the file's existing **Status:** / **Opened:** markdown-line shape rather than YAML frontmatter). - new ## As-shipped section replacing the placeholder "(To be appended post-merge by role-doc-writer.)" stub. Opens with the post-P0 transition language pointing at ship-readiness's "8 of 8 RESOLVED" baseline + this convoy's role as the first P1 quality-bar closure. Decisions section captures all five D-ratifications verbatim (D1 Deck Hearth two-word internal-cap; D2 deck-hearth kebab matching the existing CSS class + Node convention; D3 deckhearth single-token for IDs/Redis; D4 admin@deckhearth.com placeholder TLD pre-domain-acquisition, swept across admin/alice/bob uniformly; D5 full deckhearth Redis prefix not abbreviated dh, with the architect's self-documenting-prefix-vs-memory-savings rationale preserved) plus the Risk 4 PRESERVE on test/lib/permission-middleware.test.js line 87's negative regression-lock literal — including the project-pattern justification (Gotchas #2/#3/#4/#5/#8 in AGENTS.md all preserve historical-shape locks for the same audit-trail reason). As-shipped surface broken into B1/B2 split with file-disjoint inventory + the serial-in-practice dispatch note (the conductor's parallel-via-multitask path stayed open as fallback). As-shipped diff captures B1 (7+/7-) + B2 (133+/30-) + lockfile STOP-on-churn confirmation (only the two `name` lines diffed; npm ci clean against regen). Empirical CI metrics from post-merge run 26387909625 (Aggregate gate 10s, Lint 45s 128 baseline, forbidden-endpoints + forbidden-cors-headers 4s each, Playwright smoke 1m4s 3/3, Screenshot diff 1m5s continue-on-error swallow per Decision 4, vitest 21/21 in 35s with Risk 4 PRESERVE test green). Cross-validation finding: 4th convoy in a row where the same 3-test smoke spec defends the auth surface through a sweeping change (PR #15 Layout -> #19 CORS -> #20 rate-limit -> #21 brand-rename). Implementer-vs-architect calibration note on the B2 forecast paragraph vs § Full surface inventory — both correct, cross-reference between them could be tighter for the next architect. Operator-action-required-going-forward: run the new migration script before the next admin login attempt (idempotent + UNIQUE-collision-safe). What did NOT change audit trail (~25 lines of historical .convoys/ tcgvault references stay byte-identical per no-go-zones, plus seed-collections-*.js historical scripts with stale WHERE-clauses that now no-op harmlessly post-migration, plus repo-name references deferred to rename-repo-and-vercel-project, plus the docs/agent-context + add-api-route SKILL.md repo-name references that sweep with the same downstream convoy). .convoys/ship-readiness.md: - § Status summary: P1-quality-bar-work-in-progress line added below the existing 8/8 RESOLVED milestone footer, citing pick-a-name as the first such convoy. - § P0 launch sequence step 5 (pick-a-name): marked RESOLVED 2026-05-24 with the squash commit + brief shape + smoke metrics + operator-action callout inline, mirroring the format of step 4 (add-rate-limiting RESOLVED) introduced by the previous doc-writer pass. - § Queued convoys: four new entries surfaced by pick-a-name inserted as a logical group after purge-weak-creds-from-helpers (the natural neighbor — all brand-bound + credential-bound post-pick-a-name follow-ups): - rename-repo-and-vercel-project (P2 polish; GitHub repo + Vercel project rename; auto-redirects make low-urgency; lists the 8 architect-verified literal-repo references that sweep together; Risk 5 .agent-context-manifest.yml source-tag re-evaluation noted). - point-domain-at-deckhearth (P2 polish; blocked on domain acquisition; DNS pointer + Vercel domain claim + admin email TLD swap if non-`.com`; current placeholder-email rationale preserved — auth uses email as identity not as mail target). - regenerate-brand-assets (P2 polish; favicon, OG images, social cards; requires design pass; cross-references the original Role-design-system-auditor finding about unifying DH logo + AnimatedFireLogo). - convert-reset-db-to-esm (P2 hygiene; CJS-in-ESM bug at scripts/reset-db.js:142; fold-into-purge-weak-creds-from-helpers noted since both convoys touch the same file). - seed-visual-baselines-on-linux entry: extended with the AFTER-pick-a-name ordering note (now satisfied — 9abbab6 merged 2026-05-24); first Linux baseline will capture Deck Hearth strings not pre-rename TCG Vault strings. - purge-weak-creds-from-helpers entry: scope narrowed — the @tcgvault.com email-half is now satisfied by pick-a-name B2; remaining scope is the admin123/alice123/bob123 weak password literals only (which survived pick-a-name deliberately per Brief 2's scope boundary). Fold-into-convert-reset-db-to-esm suggested (overlapping file). AGENTS.md: - line 5 (top-of-file branding note): rewritten in full from the B1-installed transitional shape ("Deck Hearth is the canonical product brand (ratified 2026-05-24...)") to the post-merge documented shape with the squash commit + PR # citation + the Vercel-project parenthetical + the Risk 4 PRESERVE caveat citing test/lib/permission-middleware.test.js. **This is the showstopper change** — it's the line that's been flagged as inconsistent since project setup; the rewrite finally closes the brand-consistency loop end-to-end. - line 13 (§ 1 Project overview Auth bullet): stale `admin@tcgvault.com` reference updated to `admin@deckhearth.com` to match the post-B2 seed state. Per Brief 2's "Files explicitly out of scope" line: "AGENTS.md line 13 = doc-writer scope". - Gotcha #4 (Default admin credentials in the seed): extended with a new paragraph below the existing operator-caveat copy documenting the post-pick-a-name email rename + the migration script + the migration-FIRST-then-setup-db ordering rule + the UNIQUE-collision-safe failure mode. Existing drop-public-setup operator-caveat copy preserved verbatim above the new content. - Gotcha #12 (Rate-limit env vars): per-class limiter table extended with a Redis-prefix column showing the renamed `deckhearth:*` values (was implicit/missing); one-liner about the rename + accepted counter reset added immediately below the table. Original env-var contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN, fail-closed-in-prod / warn-and-noop-in-dev) preserved verbatim above the new table. The defensive THROW + gate-ordering-rule + sixth-class-shape paragraphs below stay byte-identical from the add-rate-limiting milestone doc-writer pass. .cursor/rules/api-routes.mdc: - § Rate limiting opening lineage line: extended to add the pick-a-name attribution (squash 9abbab6, PR #21, 2026-05-24) and to note the rename is namespace-only — call shape, return shape, and gate-ordering rules below are byte-identical post-rename. The convoy-attribution lineage now reads: fix-auth-bypass Brief 4 (auth class) -> add-rate-limiting (the other four classes + 7 routes) -> pick-a-name (Redis prefix rename). No example code blocks in this rule had `tcgvault:` literals to swap (verified via re-read); the per-class table earlier in the rule doesn't enumerate prefix values either, so the lineage line is the only edit needed. Files explicitly NOT changed: package.json, package-lock.json, lib/rate-limit.js, lib/**, pages/**, components/**, scripts/**, test/**, tests/**, .github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js, eslint.config.mjs, scripts/migrations/2026-05-24-rename-admin-email.js. Doc-writer pass touches docs only. The four queued downstream convoys (rename-repo-and-vercel-project, point-domain-at-deckhearth, regenerate-brand-assets, convert-reset-db-to-esm) own the remaining brand-bound + helper-bound source-code work; this commit only records them in the queued-convoys ledger. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 04:04:29 -04:00
**Status:** shipped
feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) Resolves the launch-blocking 'TCG Vault vs Deck Hearth' inconsistency called out in AGENTS.md line 5 since project setup. Operator gate-0 decision: Deck Hearth wins. Two briefs applied serially. B1 (mechanical): 7-file display + comment sweep. B2 (infrastructure): Redis prefix rename in lib/rate-limit.js (5 prefixes, accept one-time counter reset), package.json + lockfile regen (STOP-on-churn confirmed only name lines changed), admin/alice/bob email rename in seed scripts + login pre-fill + NEW idempotent migration script scripts/migrations/2026-05-24-rename-admin-email.js. Risk 4 PRESERVE applied: test/lib/permission-middleware.test.js retains admin@tcgvault.com literal with 7-line architect-authored why comment (documents pre-fix-auth-bypass bug shape; preserves historical truth per project's gotcha-documentation convention). All 5 D-decisions ratified at gate-1 (Deck Hearth / deck-hearth / deckhearth / admin@deckhearth.com / full deckhearth Redis prefix). Local: lint 128 baseline (B1 + B2), vitest 21/21 (B1 + B2). CI all green: Playwright smoke 3/3 against rebranded preview in 1m4s, forbidden-cors-headers pass, forbidden-endpoints pass, Screenshot diff pass, Vercel deployment complete. Cross-validation lineage: 4th convoy where the same 3-test smoke spec defends auth surface through sweeping change (after PR #15 Layout default-user, PR #19 CORS, PR #20 rate-limit, now this PR #21 brand rename). OPERATOR POST-MERGE ACTION REQUIRED: run 'node scripts/migrations/2026-05-24-rename-admin-email.js' against prod Neon DB before next admin login (ordering: migration FIRST, then any subsequent setup-db invocation). Migration is ESM, idempotent, UNIQUE-collision-safe. PR #21 architect-commit 50ce9ab, B1 ac8c998, B2 1c18d21.
2026-05-25 03:28:29 -04:00
**Priority:** P1 (pre-launch; not a security blocker, but a brand-consistency
blocker that touches every user-facing surface)
**Convoy owner:** parent (orchestration); `role-architect` then
`role-implementer(s)` then `role-doc-writer`
**Opened:** 2026-05-24
docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy Reflects the merged pick-a-name convoy (PR #21, squash commit 9abbab6) in repo documentation. **This is the first post-P0 cleanup** — add-rate-limiting (squash 708ef45) closed the LAST P0 ship-blocker on 2026-05-24, and pick-a-name immediately follows as the first post-P0 P1 quality-bar convoy, closing P1 #12 (brand-consistency — the inconsistency `AGENTS.md` line 5 had flagged since project setup). Two file-disjoint briefs landed serially (B1 commit ac8c998 display + comment sweep, B2 commit 1c18d21 infrastructure + email migration); five canonical-string D-decisions ratified verbatim at gate-1 plus Risk 4 PRESERVE on the negative regression-lock literal. Architect commit 50ce9ab; no scope expansions and no implementer deviations from the verbatim spec beyond the parallel-vs-serial dispatch choice (parallelizable in theory; serial in practice for lockfile-regen + migration-script audit cleanliness). .convoys/pick-a-name.md: - frontmatter Status: in-progress -> shipped (added Shipped: 2026-05-24 line, matching the file's existing **Status:** / **Opened:** markdown-line shape rather than YAML frontmatter). - new ## As-shipped section replacing the placeholder "(To be appended post-merge by role-doc-writer.)" stub. Opens with the post-P0 transition language pointing at ship-readiness's "8 of 8 RESOLVED" baseline + this convoy's role as the first P1 quality-bar closure. Decisions section captures all five D-ratifications verbatim (D1 Deck Hearth two-word internal-cap; D2 deck-hearth kebab matching the existing CSS class + Node convention; D3 deckhearth single-token for IDs/Redis; D4 admin@deckhearth.com placeholder TLD pre-domain-acquisition, swept across admin/alice/bob uniformly; D5 full deckhearth Redis prefix not abbreviated dh, with the architect's self-documenting-prefix-vs-memory-savings rationale preserved) plus the Risk 4 PRESERVE on test/lib/permission-middleware.test.js line 87's negative regression-lock literal — including the project-pattern justification (Gotchas #2/#3/#4/#5/#8 in AGENTS.md all preserve historical-shape locks for the same audit-trail reason). As-shipped surface broken into B1/B2 split with file-disjoint inventory + the serial-in-practice dispatch note (the conductor's parallel-via-multitask path stayed open as fallback). As-shipped diff captures B1 (7+/7-) + B2 (133+/30-) + lockfile STOP-on-churn confirmation (only the two `name` lines diffed; npm ci clean against regen). Empirical CI metrics from post-merge run 26387909625 (Aggregate gate 10s, Lint 45s 128 baseline, forbidden-endpoints + forbidden-cors-headers 4s each, Playwright smoke 1m4s 3/3, Screenshot diff 1m5s continue-on-error swallow per Decision 4, vitest 21/21 in 35s with Risk 4 PRESERVE test green). Cross-validation finding: 4th convoy in a row where the same 3-test smoke spec defends the auth surface through a sweeping change (PR #15 Layout -> #19 CORS -> #20 rate-limit -> #21 brand-rename). Implementer-vs-architect calibration note on the B2 forecast paragraph vs § Full surface inventory — both correct, cross-reference between them could be tighter for the next architect. Operator-action-required-going-forward: run the new migration script before the next admin login attempt (idempotent + UNIQUE-collision-safe). What did NOT change audit trail (~25 lines of historical .convoys/ tcgvault references stay byte-identical per no-go-zones, plus seed-collections-*.js historical scripts with stale WHERE-clauses that now no-op harmlessly post-migration, plus repo-name references deferred to rename-repo-and-vercel-project, plus the docs/agent-context + add-api-route SKILL.md repo-name references that sweep with the same downstream convoy). .convoys/ship-readiness.md: - § Status summary: P1-quality-bar-work-in-progress line added below the existing 8/8 RESOLVED milestone footer, citing pick-a-name as the first such convoy. - § P0 launch sequence step 5 (pick-a-name): marked RESOLVED 2026-05-24 with the squash commit + brief shape + smoke metrics + operator-action callout inline, mirroring the format of step 4 (add-rate-limiting RESOLVED) introduced by the previous doc-writer pass. - § Queued convoys: four new entries surfaced by pick-a-name inserted as a logical group after purge-weak-creds-from-helpers (the natural neighbor — all brand-bound + credential-bound post-pick-a-name follow-ups): - rename-repo-and-vercel-project (P2 polish; GitHub repo + Vercel project rename; auto-redirects make low-urgency; lists the 8 architect-verified literal-repo references that sweep together; Risk 5 .agent-context-manifest.yml source-tag re-evaluation noted). - point-domain-at-deckhearth (P2 polish; blocked on domain acquisition; DNS pointer + Vercel domain claim + admin email TLD swap if non-`.com`; current placeholder-email rationale preserved — auth uses email as identity not as mail target). - regenerate-brand-assets (P2 polish; favicon, OG images, social cards; requires design pass; cross-references the original Role-design-system-auditor finding about unifying DH logo + AnimatedFireLogo). - convert-reset-db-to-esm (P2 hygiene; CJS-in-ESM bug at scripts/reset-db.js:142; fold-into-purge-weak-creds-from-helpers noted since both convoys touch the same file). - seed-visual-baselines-on-linux entry: extended with the AFTER-pick-a-name ordering note (now satisfied — 9abbab6 merged 2026-05-24); first Linux baseline will capture Deck Hearth strings not pre-rename TCG Vault strings. - purge-weak-creds-from-helpers entry: scope narrowed — the @tcgvault.com email-half is now satisfied by pick-a-name B2; remaining scope is the admin123/alice123/bob123 weak password literals only (which survived pick-a-name deliberately per Brief 2's scope boundary). Fold-into-convert-reset-db-to-esm suggested (overlapping file). AGENTS.md: - line 5 (top-of-file branding note): rewritten in full from the B1-installed transitional shape ("Deck Hearth is the canonical product brand (ratified 2026-05-24...)") to the post-merge documented shape with the squash commit + PR # citation + the Vercel-project parenthetical + the Risk 4 PRESERVE caveat citing test/lib/permission-middleware.test.js. **This is the showstopper change** — it's the line that's been flagged as inconsistent since project setup; the rewrite finally closes the brand-consistency loop end-to-end. - line 13 (§ 1 Project overview Auth bullet): stale `admin@tcgvault.com` reference updated to `admin@deckhearth.com` to match the post-B2 seed state. Per Brief 2's "Files explicitly out of scope" line: "AGENTS.md line 13 = doc-writer scope". - Gotcha #4 (Default admin credentials in the seed): extended with a new paragraph below the existing operator-caveat copy documenting the post-pick-a-name email rename + the migration script + the migration-FIRST-then-setup-db ordering rule + the UNIQUE-collision-safe failure mode. Existing drop-public-setup operator-caveat copy preserved verbatim above the new content. - Gotcha #12 (Rate-limit env vars): per-class limiter table extended with a Redis-prefix column showing the renamed `deckhearth:*` values (was implicit/missing); one-liner about the rename + accepted counter reset added immediately below the table. Original env-var contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN, fail-closed-in-prod / warn-and-noop-in-dev) preserved verbatim above the new table. The defensive THROW + gate-ordering-rule + sixth-class-shape paragraphs below stay byte-identical from the add-rate-limiting milestone doc-writer pass. .cursor/rules/api-routes.mdc: - § Rate limiting opening lineage line: extended to add the pick-a-name attribution (squash 9abbab6, PR #21, 2026-05-24) and to note the rename is namespace-only — call shape, return shape, and gate-ordering rules below are byte-identical post-rename. The convoy-attribution lineage now reads: fix-auth-bypass Brief 4 (auth class) -> add-rate-limiting (the other four classes + 7 routes) -> pick-a-name (Redis prefix rename). No example code blocks in this rule had `tcgvault:` literals to swap (verified via re-read); the per-class table earlier in the rule doesn't enumerate prefix values either, so the lineage line is the only edit needed. Files explicitly NOT changed: package.json, package-lock.json, lib/rate-limit.js, lib/**, pages/**, components/**, scripts/**, test/**, tests/**, .github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js, eslint.config.mjs, scripts/migrations/2026-05-24-rename-admin-email.js. Doc-writer pass touches docs only. The four queued downstream convoys (rename-repo-and-vercel-project, point-domain-at-deckhearth, regenerate-brand-assets, convert-reset-db-to-esm) own the remaining brand-bound + helper-bound source-code work; this commit only records them in the queued-convoys ledger. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 04:04:29 -04:00
**Shipped:** 2026-05-24
feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) Resolves the launch-blocking 'TCG Vault vs Deck Hearth' inconsistency called out in AGENTS.md line 5 since project setup. Operator gate-0 decision: Deck Hearth wins. Two briefs applied serially. B1 (mechanical): 7-file display + comment sweep. B2 (infrastructure): Redis prefix rename in lib/rate-limit.js (5 prefixes, accept one-time counter reset), package.json + lockfile regen (STOP-on-churn confirmed only name lines changed), admin/alice/bob email rename in seed scripts + login pre-fill + NEW idempotent migration script scripts/migrations/2026-05-24-rename-admin-email.js. Risk 4 PRESERVE applied: test/lib/permission-middleware.test.js retains admin@tcgvault.com literal with 7-line architect-authored why comment (documents pre-fix-auth-bypass bug shape; preserves historical truth per project's gotcha-documentation convention). All 5 D-decisions ratified at gate-1 (Deck Hearth / deck-hearth / deckhearth / admin@deckhearth.com / full deckhearth Redis prefix). Local: lint 128 baseline (B1 + B2), vitest 21/21 (B1 + B2). CI all green: Playwright smoke 3/3 against rebranded preview in 1m4s, forbidden-cors-headers pass, forbidden-endpoints pass, Screenshot diff pass, Vercel deployment complete. Cross-validation lineage: 4th convoy where the same 3-test smoke spec defends auth surface through sweeping change (after PR #15 Layout default-user, PR #19 CORS, PR #20 rate-limit, now this PR #21 brand rename). OPERATOR POST-MERGE ACTION REQUIRED: run 'node scripts/migrations/2026-05-24-rename-admin-email.js' against prod Neon DB before next admin login (ordering: migration FIRST, then any subsequent setup-db invocation). Migration is ESM, idempotent, UNIQUE-collision-safe. PR #21 architect-commit 50ce9ab, B1 ac8c998, B2 1c18d21.
2026-05-25 03:28:29 -04:00
**Operator decision (gate-0, already ratified):** **Deck Hearth wins.**
## Problem
`AGENTS.md` line 5 has called this out since project setup: the repo, README,
seed data, package name, infrastructure, and admin email all say **"TCG
Vault"** (and the kebab `tcg-vault` / camel `tcgvault` variants), while the
actual rendered header brand in `components/Layout.js` says **"Deck Hearth"**.
Two distinct names, two distinct visual brands (the `AnimatedFireLogo.js`
component is also affected). Pre-launch this MUST be one name end-to-end.
## Operator's brand decision (gate-0)
**Deck Hearth wins.** Rationale (operator-stated): more distinctive, more
ownable, and the brand the human-facing surface (Layout header) already
renders. The trade is real: this is the higher-cost path because the
already-shipped infrastructure ID is the *losing* name. The architect's job
is to scope and minimize that cost — NOT to re-litigate the brand decision.
## Initial surface inventory (architect to expand)
Pre-architect rough hit-count from `rg`, scoped to non-vendor files:
| Surface | Files (sample) | "TCG Vault" or `tcg(-)?vault` hits |
| --- | --- | --- |
| Marketing copy | `README.md`, `pages/index.js`, `pages/login.js`, `pages/signup.js` | 4 + 5 + 4 + 1 |
| Brand components | `components/AnimatedFireLogo.js`, `components/ShareModal.js` | 4 + 1 |
| Seed scripts (admin email) | `scripts/setup-neon-db.js`, `scripts/create-test-users.js`, `scripts/seed-collections-*.js` | 2 + 7 + 6 |
| Test guides | `TESTING_GUIDE.md` | 7 |
| **Infrastructure-bound (rename has external blast)** | `package.json`, `lib/rate-limit.js` (Redis prefix), `.github/workflows/*.yml` | 1 + 5 + 3 |
| **Already-correct (Deck Hearth)** | `components/Layout.js`, `styles/globals.css`, `components/AnimatedFireLogo.js` partial | n/a (these stay) |
## Hard scoping rules
- **Strictly NO re-litigating the brand decision.** Deck Hearth wins; the
architect ratifies *how* to rename, not *whether* to.
- **Keep the repo `tcg-vault` for now.** GitHub rename is a separate concern
(auto-redirects work, but Vercel-link / `gh` configs / local remotes /
bookmarks all need touching). Track as a queued follow-up convoy
`rename-repo-and-vercel-project`. This convoy renames the **product brand
in-app**; the repo/Vercel rename is a downstream convoy.
- **Redis prefix migration is in scope.** `lib/rate-limit.js` uses
`tcgvault:auth/search/upload/generate/import` as Redis key prefixes. These
must rename to `deckhearth:*` (or similar) atomically with the convoy.
Accept the one-time rate-limit-state reset (counters are per-15-min /
per-1-hour anyway; no user-perceptible impact).
- **Admin email migration is in scope.** `admin@tcgvault.com` rename to
`admin@deckhearth.com` (or similar — architect proposes the canonical
email, operator ratifies). Includes the in-DB UPDATE for any existing
`admin@tcgvault.com` row (write a one-off `scripts/migrations/YYYY-MM-DD-
rename-admin-email.js` per the no-go-zones rule for new migrations).
- **`package.json` name field:** rename to `deck-hearth` in this convoy
(npm package name; we don't publish, so blast is contained to lockfile +
any local script reference).
- **Lockfile regeneration:** the architect ratifies whether to regenerate
`package-lock.json` in this convoy or accept the rename mismatch
short-term. (Recommended: regenerate; cheap; matches package.json shape.)
- **DNS / domain:** out of scope — separate convoy
`point-domain-at-deckhearth` (you don't own a `deckhearth.*` domain yet
per operator's pre-convoy statement; if/when acquired, that triggers DNS).
- **Favicon / OG images / social cards:** in scope IF source files exist;
defer to follow-up convoy `regenerate-brand-assets` if they need new design.
## Architect deliverables
1. **Full surface inventory** — every occurrence of `TCG Vault`, `tcgvault`,
`tcg-vault`, plus every occurrence of `Deck Hearth` / `deck-hearth` /
`deckhearth` (the latter set tells you what's *already* correct and what
shape collisions need handling, e.g., if Layout.js has `Deck Hearth` but
AnimatedFireLogo.js has `DECKHEARTH` — pick canonical casing).
2. **Canonical-string ratification:** propose the canonical product name
("Deck Hearth" with internal-cap; or "Deckhearth" one word; or other);
the canonical kebab (`deck-hearth`); the canonical camel/lower
(`deckhearth`); the canonical admin email (`admin@deckhearth.com` or
variant); the canonical Redis prefix (`deckhearth` or `dh`). Mark each as
D1-D5 (operator gate-1 for all five — they're naming choices, not
architectural ones).
3. **Slice plan:** probably 2-3 briefs. Suggested split (architect can
change): Brief 1 = in-repo string sweep + AnimatedFireLogo brand update
(mechanical); Brief 2 = Redis prefix migration in `lib/rate-limit.js` +
admin-email DB migration script + package.json rename + lockfile
regen (operationally riskier — needs careful testing). Brief 3 (if
needed) = visual baseline reset (because the smoke + visual-diff specs
will all fail with the new brand).
4. **Boot-the-brief verification** for each: confirm specs compile, lint
passes, vitest passes, smoke can still run against a preview.
5. **Out-of-scope queued follow-ups:** explicit list (repo rename, Vercel
project rename, DNS / domain acquisition, brand asset regeneration,
social card refresh).
## Known constraints
- **Cross-cutting smoke/visual diff blast.** PR-#18's Playwright smoke spec
("sign-in page renders") + the visual-diff baseline are both predicated on
the *current* brand strings. Brief 2 (or Brief 3 if split) MUST update
the smoke spec expectations and either regenerate visual baselines or
accept temporary baseline drift (the `adopt-playwright-smoke` Decision 4
`continue-on-error` swallow is still in effect, so visual-diff drift
won't fail CI — but the queued `seed-visual-baselines-on-linux` convoy
becomes harder to ship until *after* this convoy lands).
- **Smoke spec lives at `tests/smoke/app.smoke.spec.ts`.** Likely needs
one-or-two-character edits to text matchers.
- **`AGENTS.md` line 5's branding note** is the canonical historical record
of the inconsistency; the doc-writer pass MUST update it from "Pick one
before launch" to "Deck Hearth (decided 2026-05-24 in `pick-a-name`
convoy; commit X)."
- **Operator-action-required pre-merge:** smoke-test the rebranded preview
manually before merge (every public page should render "Deck Hearth";
login + register should still work; admin import UI should still
function with the renamed admin user).
## Out of scope (queued follow-ups)
- `rename-repo-and-vercel-project` — GitHub repo rename + Vercel project
rename + local git remote update. P2 polish. Auto-redirects make this
low-risk-low-urgency.
- `point-domain-at-deckhearth` — DNS / domain. Blocked on domain
acquisition (operator pre-convoy: doesn't own `deckhearth.*` yet).
- `regenerate-brand-assets` — favicon, OG images, social cards. P2
polish; requires design pass.
- `migrate-existing-user-emails` — if any existing user accounts have
`@tcgvault.com` emails (besides admin), propose a migration. Architect
to surface count via a `rg`/DB query during inventory.
## Owns
`role-architect` (decisions D1-D5, slice plan) → `role-implementer(s)`
(per brief) → `role-doc-writer` (AGENTS.md line 5 + this file's
As-shipped + ship-readiness Status summary footer).
## Architecture (role-architect, 2026-05-24)
**Status:** architect-complete; awaiting operator gate-1 ratification on D1-D5
before implementer dispatch.
**No blocking findings.** No surfaced naming collision with existing npm
packages (we don't publish; the `name` field is a local identifier).
`deckhearth.com` domain is unowned per operator's pre-convoy statement —
queued in `point-domain-at-deckhearth`, downstream of this convoy. Redis
counter reset is an acknowledged one-time cost (per-15-min and per-1-hour
windows; no user-perceptible impact). All other costs are mechanical sweeps.
**Scope summary:** 16 source files modified across two parallel briefs +
1 new migration script. ~75-110 lines of net diff (excluding `package-lock.json`
regen, which is its own opaque artifact). Two briefs touch DISJOINT file
sets and can run in parallel via `/multitask`.
### Operator-ratified decisions (gate-0, captured)
- **Brand winner:** **Deck Hearth.** Operator's rationale: more distinctive,
more ownable, and the brand the human-facing surface (Layout header)
already renders. The architect's job is to scope and minimize the cost of
getting there — NOT to re-litigate the brand decision.
### Architect-proposed decisions (operator gate-1 required, ALL FIVE)
These five naming choices are NOT architect-self-ratifiable. They establish
the canonical strings the entire codebase will be swept to. **All five
require operator gate-1 ratification before Brief 1 OR Brief 2 dispatch.**
#### D1 — Canonical display string (operator gate-1)
**Recommendation:** **"Deck Hearth"** (two words, internal cap, space-separated).
Trade-off considered: "Deckhearth" (one word) is brand-simpler and gives a
cleaner email TLD (`deckhearth.com` reads as one syllable). But every
already-shipped user-facing surface in the repo renders the two-word form:
- `components/Layout.js` lines 623 + 716 → `Deck Hearth`
- `components/AnimatedFireLogo.js` line 13 → `Deck Hearth Logo`
- `pages/index.js` lines 64, 96, 266, 299, 315 → `Deck Hearth`
- `pages/login.js` lines 76, 158 → `Deck Hearth`
- `pages/signup.js` line 215 → `Deck Hearth`
- `components/ShareModal.js` line 116 → `Deck Hearth`
- `styles/globals.css` line 15 → `Deck Hearth Fire-Inspired`
Choosing "Deckhearth" would require RE-sweeping all seven currently-correct
surfaces — net negative-cost choice. Recommendation: **ratify "Deck Hearth"**
to make the in-progress strings the canonical strings.
#### D2 — Canonical kebab string (operator gate-1)
**Recommendation:** **`deck-hearth`** (matches existing CSS class
`deck-hearth-logo-container` in `components/AnimatedFireLogo.js` line 8 +
the conventional hyphenated lowercase used by package.json `name` fields and
file naming).
Trade-off: `deckhearth` (no hyphen) would match D3 below but conflicts with
both Node ecosystem convention (`@scope/kebab-name`) and the existing CSS
class. Recommendation: **ratify `deck-hearth`**.
#### D3 — Canonical camel/lower string for IDs (operator gate-1)
**Recommendation:** **`deckhearth`** (single token, no separators).
Used for: Redis key prefixes, env-var-style identifiers, internal symbol
names. Distinct from D2 because IDs lack convention-bound separators (the
Upstash `prefix:` option is a single string token; convention is colon-
separated namespace, e.g. `deckhearth:auth:1.2.3.4`). Recommendation: **ratify
`deckhearth`**.
#### D4 — Canonical admin email (operator gate-1)
**Recommendation:** **`admin@deckhearth.com`** (placeholder TLD pending the
queued `point-domain-at-deckhearth` convoy).
Trade-off: the operator does not yet own `deckhearth.com` (or any
`deckhearth.*` TLD) per the convoy seed § *"point-domain-at-deckhearth …
blocked on domain acquisition"*. The seed admin row's email is a STRING
literal in the database — it doesn't need to resolve to a real mailbox to
function (auth uses the email as a unique identifier, not as a mail target).
Picking `.com` now matches the most-likely future domain purchase; if the
operator instead buys `.app` or `.gg`, a future one-line migration can sweep
the TLD without touching this convoy's structure.
**Apply to all three current `@tcgvault.com` accounts:** admin, alice, bob
(the test users created by `scripts/create-test-users.js`). The migration
script in Brief 2 sweeps all three uniformly via a `REPLACE(email,
'@tcgvault.com', '@deckhearth.com')` UPDATE.
Recommendation: **ratify `admin@deckhearth.com` + sweep all `@tcgvault.com`
emails to `@deckhearth.com`** in the same migration. If the operator wants
a different TLD, swap `.com` for the chosen TLD in the migration script and
the seed scripts before Brief 2 implementer dispatch — that's a one-line
change confined to B2's file set.
#### D5 — Canonical Redis prefix (operator gate-1)
**Recommendation:** **full `deckhearth`** (NOT abbreviated `dh`).
Trade-off considered: `dh` saves ~8 bytes per key (vs. `deckhearth`). At peak
load with say 100k active rate-limit identifiers, total savings would be
~800KB of Redis memory — negligible against an Upstash plan that bills by
request count, not per-byte storage. The self-documenting `deckhearth:auth:…`
prefix is materially easier to debug when reading Redis console output. (And
the `cors-tighten` Decision-style precedent applies: short-and-cryptic IDs
tend to attract "what does this mean?" comments in code, longer-and-clear IDs
explain themselves.)
Recommendation: **ratify full `deckhearth`**.
### Full surface inventory
Architect-verified `rg` sweep results, grouped by classification. Files
under `.convoys/**` (append-only convoy history per `.cursor/rules/no-go-
zones.mdc`) are listed at the bottom for completeness but are NOT in any
brief's `files:` list — they stay byte-identical.
#### User-facing display strings (must rename)
| File | Line | Current | Brief |
| --- | --- | --- | --- |
| `README.md` | 1 | `# TCG Vault` | 2 (file already in B2 for email) |
| `TESTING_GUIDE.md` | 1 | `# 🎯 TCG Vault Collaboration Testing Guide` | 2 (file already in B2 for emails) |
| `scripts/README.md` | 1 | `# TCG Vault Bulk Import Scripts` | 1 |
#### Brand-bound infrastructure IDs (must rename; has external blast)
| File | Line | Current | Blast | Brief |
| --- | --- | --- | --- | --- |
| `lib/rate-limit.js` | 10-14 | `tcgvault:auth/search/upload/generate/import` | Orphans existing Redis counters at deploy time; new counters start at zero | 2 |
| `package.json` | 2 | `"name": "tcg-vault"` | Lockfile regen required | 2 |
| `package-lock.json` | 2, 8 | `"name": "tcg-vault"` (×2) | Generated; regen via `npm install` after `package.json` change | 2 |
| `scripts/setup-neon-db.js` | 148 | `VALUES (${'admin@tcgvault.com'}, …)` | Seeds NEW deploys' admin email; needs migration for existing deploys | 2 |
| `scripts/setup-neon-db.js` | 157 | `console.log(' Admin user ready (email: admin@tcgvault.com)')` | Stdout copy | 2 |
| `pages/api/cards/import-lorcana.js` | 14 | `'User-Agent': 'TCG-Vault/1.0'` | External HTTP identity sent to Lorcana API | 1 |
| `pages/api/cards/import-pokemon.js` | 14 | `'User-Agent': 'TCG-Vault/1.0'` | External HTTP identity sent to Pokémon TCG API | 1 |
| `scripts/import-lorcana.js` | 20 | `'User-Agent': 'TCG-Vault/1.0'` | External HTTP identity (CLI variant) | 1 |
#### Email-bound infrastructure (must rename; in-DB migration required)
| File | Lines | Current | Brief |
| --- | --- | --- | --- |
| `scripts/setup-neon-db.js` | 148, 157 | `admin@tcgvault.com` | 2 (also infra above) |
| `scripts/reset-db.js` | 147, 155 | `admin@tcgvault.com` | 2 |
| `scripts/create-test-users.js` | 18, 21, 27, 30, 34-36 | `alice@tcgvault.com`, `bob@tcgvault.com`, `admin@tcgvault.com` | 2 |
| `pages/login.js` | 172, 184 | `alice@tcgvault.com`, `bob@tcgvault.com` (quick-login fixtures) | 2 |
| `README.md` | 132 | `Admin email: the seed creates admin@tcgvault.com` | 2 |
| `TESTING_GUIDE.md` | 7-9, 24, 46, 53 | admin/alice/bob `@tcgvault.com` emails in account table + workflow | 2 |
| `test/lib/permission-middleware.test.js` | 87 | `email: 'admin@tcgvault.com'` (NEGATIVE regression assertion) | 2 (see B2 decision on whether to preserve or update — leaning preserve) |
| `scripts/migrations/2026-05-24-rename-admin-email.js` | NEW | (new file) | 2 |
#### Internal references in docs / rules / agent context (rename for consistency)
| File | Line | Current | Brief |
| --- | --- | --- | --- |
| `AGENTS.md` | 5 | `Branding note: the repo, README, and seed data say "TCG Vault" …` | 1 |
| `.cursor/rules/ui-and-theming.mdc` | 61 | `The repo says "TCG Vault" everywhere except …` | 1 |
| `.cursor/rules/auth-and-permissions.mdc` | 2 | `description: Auth model + permission model for tcg-vault (…)` | 1 |
#### Repo / Vercel project name (explicitly OUT OF SCOPE per convoy seed)
These references match the literal repo name `tcg-vault`, which stays for now
per the convoy seed § *"Keep the repo `tcg-vault` for now."* When the
follow-up convoy `rename-repo-and-vercel-project` ships, these update
together with the repo rename. **Do NOT touch in this convoy.**
| File | Line | Reference |
| --- | --- | --- |
| `README.md` | 30 | `cd tcg-vault` (git clone instructions) |
| `README.md` | 105 | `tcg-vault/` (directory tree diagram) |
| `AGENTS.md` | 1 | `# AGENTS.md — AI collaboration (tcg-vault)` |
| `.github/workflows/ci.yml` | 12 | `# NOTE: tcg-vault is JavaScript (not TypeScript). …` |
| `.github/workflows/ci.yml` | 123 | `# The tcg-vault frontend and API are served from the same Vercel …` |
| `.github/workflows/visual-diff.yml` | 5 | `# Paths are tcg-vault-specific (pages router, JS).` |
| `.agent-context-manifest.yml` | 84, 89, 99, 104, 109, 114 | `source: "tcg-vault-local"` (internal source tag, re-evaluated by `sync-agent-context` skill at repo rename time) |
#### Already-correct (Deck Hearth — stays byte-identical)
These render the canonical D1 string and have no `tcgvault` baggage. Do NOT
touch:
- `components/Layout.js` (lines 623, 716 — sidebar headers)
- `components/AnimatedFireLogo.js` (lines 8, 13, 25, 29 — CSS class +
alt text + style scope)
- `components/ShareModal.js` (line 116 — social share title)
- `pages/index.js` (lines 64, 96, 266, 299, 315 — landing page)
- `pages/login.js` (lines 76, 158 — welcome copy; emails at 172/184 are in B2)
- `pages/signup.js` (line 215 — signup heading)
- `styles/globals.css` (line 15 — theme comment)
#### Convoy artifacts + manifest (no-go per `.cursor/rules/no-go-zones.mdc`)
`.convoys/**/*.md` are append-only history — they describe the state at
write-time. Per the no-go zone for append-only artifacts and `scripts/seed-
*.js` historical jobs, these are NOT renamed in this convoy:
- `.convoys/fix-auth-bypass/brief-2-remove-admin-bypass.md` (4 hits — describes the BUG that returned the OLD synthetic-admin email)
- `.convoys/fix-auth-bypass/brief-5-vitest-and-auth-tests.md` (2 hits — same)
- `.convoys/drop-public-setup.md`, `.convoys/drop-public-setup/brief-*.md` (multiple hits — historical decision record)
- `.convoys/add-rate-limiting.md`, `.convoys/add-rate-limiting/brief-*.md` (8 hits — Redis prefix references reflect the *pre-rename* state at convoy-close; this convoy is what changes them; doc-writer pass at THIS convoy's close updates the gotcha / rule, NOT the historical convoy file)
- `.convoys/cors-tighten/brief-1-sweep-wildcard-cors.md` (2 hits — historical)
- `.convoys/fix-layout-default-user.md`, `.convoys/fix-layout-default-user/brief-*.md` (3 hits — historical)
- `.convoys/ship-readiness.md` (4 hits — historical milestone log; doc-writer pass appends a NEW "12. RESOLVED" entry rather than rewriting line 244)
- `.convoys/bump-next-js.md`, `.convoys/fix-vercel-deployment-protection-in-ci.md` (each 1 hit — historical)
- `scripts/seed-collections-with-cards.js`, `scripts/seed-collections-alice-bob.js` (3 hits each — historical seed jobs per no-go-zone rule "`scripts/seed-*.js` — historical migration / seed jobs already executed"; the email migration in B2 covers the in-DB rename, so these scripts' WHERE-clause literals would simply match zero rows post-migration if re-run, which is a harmless no-op)
- `components/Layout.js.backup` (1 hit — legacy snapshot per no-go-zone rule)
### API surface
**No new or modified API routes.** No request / response shape changes. The
Redis prefix rename in `lib/rate-limit.js` is internal to the lib module —
the five exported `check*RateLimit(req[, userId])` functions retain their
byte-identical Brief-4-and-`add-rate-limiting`-Brief-1 contracts: same name,
same args, same `{ allowed, remaining, reset }` return shape. The seven
gated handlers do NOT need editing.
### Schema diff
**No DDL changes.** The only schema-adjacent operation is the in-DB UPDATE
of `users.email` for at most 3 rows (admin + alice + bob), performed by
the new dated migration script `scripts/migrations/2026-05-24-rename-admin-
email.js`. No new tables, no new columns, no new indexes, no new
constraints. `docs/SCHEMA_MAP.md` requires no update (column shapes and
table inventory are unchanged).
The new `scripts/migrations/` folder is created with this convoy's first
migration file. Per `.cursor/rules/no-go-zones.mdc` § *"if the change
touches DDL strings or `INSERT` semantics, file a migration; if it only
touches Node-module behavior or pre-flight validation, edit in place and
document why in the convoy"*, the seed scripts' literal email INSERT values
in `scripts/setup-neon-db.js` + `scripts/reset-db.js` + `scripts/create-test-
users.js` ARE INSERT-semantics changes; the migration script is the
required artifact to accompany those edits. The architect ratifies this:
the in-place edits keep new-deploy seeding correct; the migration handles
the existing-deploy update.
### Test plan
**No new vitest unit tests** in this convoy (decoupled from the rebrand).
Per `add-rate-limiting` Decision 6 precedent — new handler / unit tests are
queued under `fill-vitest-handler-coverage`. The only test edit is the
update to `test/lib/permission-middleware.test.js` line 87's NEGATIVE
regression assertion (see B2 acceptance criteria).
**Vitest 21/21 must still pass** after Brief 1 and Brief 2 land. Brief 1
touches zero files referenced by any test. Brief 2 touches three test-
adjacent files (`pages/login.js` quick-login fixtures, `test/lib/permission-
middleware.test.js` line 87, and the test-user creation scripts) — none of
these break the unit-test suite. The negative regression assertion's
`.toBeNull()` check is the canonical assertion; the `.not.toEqual({...})`
soft check is incidentally about the historical literal email value and
will continue to pass against any email.
**Playwright smoke 3/3 must still pass** after both briefs. Verified at
architect time: `tests/smoke/app.smoke.spec.ts` text matchers are `'sign in'`
(case-insensitive regex), `/api/health`, and HTTP status < 500. NONE of
these text matchers reference `TCG Vault` or `Deck Hearth` brand strings;
the smoke spec is brand-agnostic and needs **zero edits**. (This is the
"audit" the prompt requested — confirmed via direct read of the spec file.)
**Playwright visual baseline**: verified at architect time that
`tests/visual/__screenshots__/` does NOT yet exist (no committed baselines).
Per the `adopt-playwright-smoke` Decision 4 `continue-on-error` swallow and
the queued `seed-visual-baselines-on-linux` convoy, the visual-diff
workflow's first-run failure is the documented end state. **No baseline
drift to manage in this convoy.** When `seed-visual-baselines-on-linux`
eventually lands, it MUST run AFTER `pick-a-name` so the seeded baseline
captures the Deck Hearth brand strings; the architect updates the
`seed-visual-baselines-on-linux` queued entry below to record this
ordering constraint.
**Manual verification** (operator pre-merge):
- Boot `npm run dev`. Visit `/`, `/login`, `/signup`, `/cards`. Every brand
string should render `Deck Hearth` (verify in browser DevTools "Find").
- Click the "Alice" / "Bob" quick-login buttons on `/login`. Expected
behavior post-migration: each button fills the email field with
`<name>@deckhearth.com` and the password `<name>123`. With the migration
run, login succeeds and redirects to dashboard. WITHOUT the migration
run (e.g., op forgot), login 401s — that's the expected failure mode and
the operator's signal to run the migration script.
- Run `node scripts/migrations/2026-05-24-rename-admin-email.js` against
a staging DB. Expected: prints the count of `@tcgvault.com` rows found
(0-3), UPDATEs them, prints the success count. Re-run: prints "Nothing
to migrate. Exiting." (idempotency check).
- Visit a Vercel preview. Verify all public surfaces render Deck Hearth.
Quick-login test (admin / alice / bob with the new emails) only works
if the preview's DB is the staging Neon with the migration run.
### Risk list
1. **Operator forgets to run the migration script post-deploy.** The
admin email and Alice/Bob test-user emails would remain `@tcgvault.com`
in the DB while the seed scripts and login.js fixtures reference
`@deckhearth.com`. Symptom: admin login attempts with the new email
401 because the DB still has the old email. **Mitigation:** the
`pick-a-name` PR description MUST include a "Post-merge operator
action" callout naming the migration script and the
`node scripts/migrations/...` command verbatim. (Same defense pattern
as `drop-public-setup`'s "operator caveat" for the weak-hash
rotation.) Doc-writer pass at convoy close adds this to `AGENTS.md`
Gotcha #4's "Operator caveat" paragraph as a sibling caveat.
2. **Redis counter reset surprises a heavy active user.** Rare in
pre-launch. The five sliding windows (15-min auth, 1-min search,
1-hour upload/generate/import) all expire within an hour anyway —
the worst-case "loss" is that an attacker who had burned 4/5 of the
auth budget at the moment of deploy gets reset to 0/5. This is
defensively neutral: the legitimate user's prior counter also resets
(they too get a fresh 5/15-min budget). **Mitigation:** none needed;
the convoy seed explicitly accepts this trade.
3. **Lockfile regen surfaces unrelated dependency churn.** Running
`npm install` after a `package.json` `name` change SHOULD only update
the `name` field at the top of `package-lock.json` plus the
`packages.""` entry inside it. But npm might opportunistically
refresh stale `integrity` hashes or pull in metadata updates.
**Mitigation:** B2 acceptance criteria require the implementer to
inspect the `package-lock.json` diff and confirm it touches only
`name`-related entries; if any unrelated `dependencies` / `version`
churn appears, the brief instructs the implementer to STOP and flag
for a separate `bump-deps-cleanup` convoy rather than landing
accidental dependency updates inside the rebrand PR.
4. **The historical synthetic-admin negative regression assertion in
`test/lib/permission-middleware.test.js` line 87.** The literal email
`admin@tcgvault.com` is the OLD bug's exact shape. Two options: (a)
preserve the historical literal so the test continues to encode "the
helper does NOT return THIS SPECIFIC pre-`fix-auth-bypass` shape"; or
(b) update to `admin@deckhearth.com` so the test's literal matches
the current admin email. Recommendation: **preserve (a)** and add a
comment explaining the historical preservation. The `.toBeNull()`
assertion two lines below is the strong contract; the
`.not.toEqual({...})` is the soft historical-shape check. B2's
acceptance criteria documents the preserve-(a) recommendation but
leaves the final call to the implementer.
5. **The `.agent-context-manifest.yml` `source: "tcg-vault-local"` tag.**
Architect-verified at brief time that 6 lines reference this string
as an internal source identifier for the `sync-agent-context` skill's
drift detection. Renaming to `deck-hearth-local` could break the
sync skill's behavior (it tracks "what shipped from this source").
**Mitigation:** leave as-is in this convoy; flag for re-evaluation
in `rename-repo-and-vercel-project` when the repo's literal name
actually changes.
6. **Email is the unique constraint on `users` — collision is impossible
but admin-row recreation is.** If the operator runs `setup-neon-db.js`
AFTER the migration runs, the seed's `INSERT … ON CONFLICT (email)
DO NOTHING` would NOT recreate the admin row (because the row exists
with the NEW email post-migration). That's the correct behavior. If
the operator runs `setup-neon-db.js` BEFORE the migration runs and
the existing admin row has the OLD email, the seed inserts a NEW row
with the NEW email, creating TWO admin rows (one with each TLD).
**Mitigation:** the migration script's UPDATE is idempotent and
handles both cases (it sweeps any remaining `@tcgvault.com` row to
`@deckhearth.com`, which would collide with the existing
`@deckhearth.com` row's UNIQUE constraint and fail loudly). The
architect recommends the operator run the migration FIRST, then
confirm via `SELECT email FROM users WHERE role = 'admin'` returns
exactly one row before running `setup-neon-db.js`. B2 acceptance
criteria document this ordering.
7. **B1 and B2 are parallelizable BUT both depend on operator gate-1
ratification of D1-D5 BEFORE dispatch.** This is a single gate at the
convoy level, not a per-brief gate. Once the operator says "approved,
run implementers" with the D1-D5 choices ratified, the conductor can
dispatch B1 + B2 in parallel via `/multitask` since their `files:`
sets are disjoint.
### Decomposition
| Brief # | Title | Files | Depends on | Estimated PR size |
| --- | --- | --- | --- | --- |
| 1 | In-repo display + comment sweep (User-Agent strings, branding notes, rule descriptions) | 7 files | none (gate-1 ratification of D1+D2+D3 required) | ~10-15 lines net |
| 2 | Infrastructure + email migration (Redis prefix rename, package.json + lockfile regen, admin/alice/bob email rename in seed scripts + login fixtures + migration script + regression test) | 10 files + 1 new migration script | none (gate-1 ratification of D1-D5 required); operator pre-merge action: run migration | ~65-95 lines net + opaque package-lock.json regen |
### Slice dependencies (multitask-ready)
```yaml
slice_dependencies:
- brief: 1
depends_on: []
files:
- AGENTS.md
- .cursor/rules/ui-and-theming.mdc
- .cursor/rules/auth-and-permissions.mdc
- scripts/README.md
- pages/api/cards/import-lorcana.js
- pages/api/cards/import-pokemon.js
- scripts/import-lorcana.js
- brief: 2
depends_on: []
files:
- lib/rate-limit.js
- package.json
- package-lock.json
- README.md
- TESTING_GUIDE.md
- scripts/setup-neon-db.js
- scripts/reset-db.js
- scripts/create-test-users.js
- pages/login.js
- test/lib/permission-middleware.test.js
- scripts/migrations/2026-05-24-rename-admin-email.js
```
**Parallelism:** B1's 7 files and B2's 11 files are DISJOINT. The
conductor can dispatch both in parallel via `/multitask` once the operator
ratifies D1-D5 at gate-1. Neither brief blocks the other.
### Out-of-scope queued follow-ups (reaffirmed + 2 new)
Reaffirms the convoy seed's four:
- **`rename-repo-and-vercel-project`** (P2 polish) — GitHub repo rename
(`tcg-vault` → `deck-hearth`) + Vercel project rename + local git
remote update + the 8 architect-verified `tcg-vault` references that
cite the literal repo (README cd/tree, AGENTS.md line 1, the three
workflow YAML comments, `.agent-context-manifest.yml` source tags).
Auto-redirects make this low-risk-low-urgency.
- **`point-domain-at-deckhearth`** — DNS / domain. Blocked on domain
acquisition (operator pre-convoy: doesn't own `deckhearth.*` yet).
If the operator buys a TLD other than `.com`, this convoy also
sweeps the email TLD via a one-line REPLACE migration.
- **`regenerate-brand-assets`** — favicon, OG images, social cards.
`public/favicon.ico` exists today (unverified what brand it is — out
of scope to inspect binary in this convoy). P2 polish; requires
design pass.
- **`migrate-existing-user-emails`** — for real users with
`@tcgvault.com` emails. Architect-verified at brief time: pre-launch,
the ONLY `@tcgvault.com` accounts are admin + alice + bob (3 fixtures
created by `create-test-users.js`). Brief 2's migration handles all
three. If the operator opened the app to friends-and-family pre-this-
convoy and any signed up with `@tcgvault.com` emails, the same
migration covers them too (it sweeps every row matching
`email LIKE '%@tcgvault.com'`).
Two NEW follow-ups surfaced by architect inventory:
- **`convert-reset-db-to-esm`** (P2 hygiene) — `scripts/reset-db.js` line
142 uses CommonJS `require('bcryptjs')` inside an ESM file. Same bug
pattern that hit `setup-neon-db.js` pre-`drop-public-setup` Brief 2.
Brief 2 of `pick-a-name` does NOT fix this in scope (it touches the
email literal and console-log copy only); a separate convoy converts
the require → import. May fold into `purge-weak-creds-from-helpers`.
- **`update-seed-visual-baselines-on-linux-ordering`** — the queued
`seed-visual-baselines-on-linux` convoy MUST run AFTER `pick-a-name`
so the first committed Linux baseline captures the Deck Hearth brand
strings (not TCG Vault). Architect recommends the operator (or the
conductor of that future convoy) add a `depends_on: pick-a-name`
ordering note to the `seed-visual-baselines-on-linux` queued entry
in `.convoys/ship-readiness.md`.
### As-shipped
docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy Reflects the merged pick-a-name convoy (PR #21, squash commit 9abbab6) in repo documentation. **This is the first post-P0 cleanup** — add-rate-limiting (squash 708ef45) closed the LAST P0 ship-blocker on 2026-05-24, and pick-a-name immediately follows as the first post-P0 P1 quality-bar convoy, closing P1 #12 (brand-consistency — the inconsistency `AGENTS.md` line 5 had flagged since project setup). Two file-disjoint briefs landed serially (B1 commit ac8c998 display + comment sweep, B2 commit 1c18d21 infrastructure + email migration); five canonical-string D-decisions ratified verbatim at gate-1 plus Risk 4 PRESERVE on the negative regression-lock literal. Architect commit 50ce9ab; no scope expansions and no implementer deviations from the verbatim spec beyond the parallel-vs-serial dispatch choice (parallelizable in theory; serial in practice for lockfile-regen + migration-script audit cleanliness). .convoys/pick-a-name.md: - frontmatter Status: in-progress -> shipped (added Shipped: 2026-05-24 line, matching the file's existing **Status:** / **Opened:** markdown-line shape rather than YAML frontmatter). - new ## As-shipped section replacing the placeholder "(To be appended post-merge by role-doc-writer.)" stub. Opens with the post-P0 transition language pointing at ship-readiness's "8 of 8 RESOLVED" baseline + this convoy's role as the first P1 quality-bar closure. Decisions section captures all five D-ratifications verbatim (D1 Deck Hearth two-word internal-cap; D2 deck-hearth kebab matching the existing CSS class + Node convention; D3 deckhearth single-token for IDs/Redis; D4 admin@deckhearth.com placeholder TLD pre-domain-acquisition, swept across admin/alice/bob uniformly; D5 full deckhearth Redis prefix not abbreviated dh, with the architect's self-documenting-prefix-vs-memory-savings rationale preserved) plus the Risk 4 PRESERVE on test/lib/permission-middleware.test.js line 87's negative regression-lock literal — including the project-pattern justification (Gotchas #2/#3/#4/#5/#8 in AGENTS.md all preserve historical-shape locks for the same audit-trail reason). As-shipped surface broken into B1/B2 split with file-disjoint inventory + the serial-in-practice dispatch note (the conductor's parallel-via-multitask path stayed open as fallback). As-shipped diff captures B1 (7+/7-) + B2 (133+/30-) + lockfile STOP-on-churn confirmation (only the two `name` lines diffed; npm ci clean against regen). Empirical CI metrics from post-merge run 26387909625 (Aggregate gate 10s, Lint 45s 128 baseline, forbidden-endpoints + forbidden-cors-headers 4s each, Playwright smoke 1m4s 3/3, Screenshot diff 1m5s continue-on-error swallow per Decision 4, vitest 21/21 in 35s with Risk 4 PRESERVE test green). Cross-validation finding: 4th convoy in a row where the same 3-test smoke spec defends the auth surface through a sweeping change (PR #15 Layout -> #19 CORS -> #20 rate-limit -> #21 brand-rename). Implementer-vs-architect calibration note on the B2 forecast paragraph vs § Full surface inventory — both correct, cross-reference between them could be tighter for the next architect. Operator-action-required-going-forward: run the new migration script before the next admin login attempt (idempotent + UNIQUE-collision-safe). What did NOT change audit trail (~25 lines of historical .convoys/ tcgvault references stay byte-identical per no-go-zones, plus seed-collections-*.js historical scripts with stale WHERE-clauses that now no-op harmlessly post-migration, plus repo-name references deferred to rename-repo-and-vercel-project, plus the docs/agent-context + add-api-route SKILL.md repo-name references that sweep with the same downstream convoy). .convoys/ship-readiness.md: - § Status summary: P1-quality-bar-work-in-progress line added below the existing 8/8 RESOLVED milestone footer, citing pick-a-name as the first such convoy. - § P0 launch sequence step 5 (pick-a-name): marked RESOLVED 2026-05-24 with the squash commit + brief shape + smoke metrics + operator-action callout inline, mirroring the format of step 4 (add-rate-limiting RESOLVED) introduced by the previous doc-writer pass. - § Queued convoys: four new entries surfaced by pick-a-name inserted as a logical group after purge-weak-creds-from-helpers (the natural neighbor — all brand-bound + credential-bound post-pick-a-name follow-ups): - rename-repo-and-vercel-project (P2 polish; GitHub repo + Vercel project rename; auto-redirects make low-urgency; lists the 8 architect-verified literal-repo references that sweep together; Risk 5 .agent-context-manifest.yml source-tag re-evaluation noted). - point-domain-at-deckhearth (P2 polish; blocked on domain acquisition; DNS pointer + Vercel domain claim + admin email TLD swap if non-`.com`; current placeholder-email rationale preserved — auth uses email as identity not as mail target). - regenerate-brand-assets (P2 polish; favicon, OG images, social cards; requires design pass; cross-references the original Role-design-system-auditor finding about unifying DH logo + AnimatedFireLogo). - convert-reset-db-to-esm (P2 hygiene; CJS-in-ESM bug at scripts/reset-db.js:142; fold-into-purge-weak-creds-from-helpers noted since both convoys touch the same file). - seed-visual-baselines-on-linux entry: extended with the AFTER-pick-a-name ordering note (now satisfied — 9abbab6 merged 2026-05-24); first Linux baseline will capture Deck Hearth strings not pre-rename TCG Vault strings. - purge-weak-creds-from-helpers entry: scope narrowed — the @tcgvault.com email-half is now satisfied by pick-a-name B2; remaining scope is the admin123/alice123/bob123 weak password literals only (which survived pick-a-name deliberately per Brief 2's scope boundary). Fold-into-convert-reset-db-to-esm suggested (overlapping file). AGENTS.md: - line 5 (top-of-file branding note): rewritten in full from the B1-installed transitional shape ("Deck Hearth is the canonical product brand (ratified 2026-05-24...)") to the post-merge documented shape with the squash commit + PR # citation + the Vercel-project parenthetical + the Risk 4 PRESERVE caveat citing test/lib/permission-middleware.test.js. **This is the showstopper change** — it's the line that's been flagged as inconsistent since project setup; the rewrite finally closes the brand-consistency loop end-to-end. - line 13 (§ 1 Project overview Auth bullet): stale `admin@tcgvault.com` reference updated to `admin@deckhearth.com` to match the post-B2 seed state. Per Brief 2's "Files explicitly out of scope" line: "AGENTS.md line 13 = doc-writer scope". - Gotcha #4 (Default admin credentials in the seed): extended with a new paragraph below the existing operator-caveat copy documenting the post-pick-a-name email rename + the migration script + the migration-FIRST-then-setup-db ordering rule + the UNIQUE-collision-safe failure mode. Existing drop-public-setup operator-caveat copy preserved verbatim above the new content. - Gotcha #12 (Rate-limit env vars): per-class limiter table extended with a Redis-prefix column showing the renamed `deckhearth:*` values (was implicit/missing); one-liner about the rename + accepted counter reset added immediately below the table. Original env-var contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN, fail-closed-in-prod / warn-and-noop-in-dev) preserved verbatim above the new table. The defensive THROW + gate-ordering-rule + sixth-class-shape paragraphs below stay byte-identical from the add-rate-limiting milestone doc-writer pass. .cursor/rules/api-routes.mdc: - § Rate limiting opening lineage line: extended to add the pick-a-name attribution (squash 9abbab6, PR #21, 2026-05-24) and to note the rename is namespace-only — call shape, return shape, and gate-ordering rules below are byte-identical post-rename. The convoy-attribution lineage now reads: fix-auth-bypass Brief 4 (auth class) -> add-rate-limiting (the other four classes + 7 routes) -> pick-a-name (Redis prefix rename). No example code blocks in this rule had `tcgvault:` literals to swap (verified via re-read); the per-class table earlier in the rule doesn't enumerate prefix values either, so the lineage line is the only edit needed. Files explicitly NOT changed: package.json, package-lock.json, lib/rate-limit.js, lib/**, pages/**, components/**, scripts/**, test/**, tests/**, .github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js, eslint.config.mjs, scripts/migrations/2026-05-24-rename-admin-email.js. Doc-writer pass touches docs only. The four queued downstream convoys (rename-repo-and-vercel-project, point-domain-at-deckhearth, regenerate-brand-assets, convert-reset-db-to-esm) own the remaining brand-bound + helper-bound source-code work; this commit only records them in the queued-convoys ledger. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 04:04:29 -04:00
Shipped 2026-05-24 as squash commit `9abbab6` (PR #21, architect-commit
`50ce9ab`, Brief 1 commit `ac8c998`, Brief 2 commit `1c18d21`). **This is
the first post-P0 convoy** — `add-rate-limiting` (squash `708ef45`, PR
#20, 2026-05-24) closed the LAST P0 ship-blocker, flipping
`.convoys/ship-readiness.md`'s § Status summary to "8 of 8 RESOLVED";
this convoy opens the post-P0 P1 quality-bar phase by closing the
brand-consistency P1 (#12 in the same file) that AGENTS.md line 5 has
flagged since project setup. Two briefs in the convoy: B1
(display + comment sweep) and B2 (infrastructure + email migration)
shipped without scope expansions and with all five D-decisions ratified
verbatim at gate-1 plus Risk 4 PRESERVE applied to the negative
regression-lock test.
#### Decisions ratified at gate-1
All five canonical-string choices required operator gate-1 ratification
(naming choices are not architect-self-ratifiable per the convoy file's
§ Architect-proposed decisions header). Operator ratified all five
verbatim:
- **D1 — Display string:** **"Deck Hearth"** (two words, internal cap,
space-separated). Avoids re-sweeping the seven user-facing surfaces
that already render the two-word form (`components/Layout.js`,
`components/AnimatedFireLogo.js`, `pages/index.js`, `pages/login.js`
welcome copy, `pages/signup.js`, `components/ShareModal.js`,
`styles/globals.css`).
- **D2 — Kebab string:** **`deck-hearth`**. Matches the existing
`deck-hearth-logo-container` CSS class + Node-ecosystem convention for
`package.json` `name` fields. Used in `package.json` rename + the
three `User-Agent` HTTP product tokens in `pages/api/cards/import-*.js`
+ `scripts/import-lorcana.js` (`Deck-Hearth/1.0`, hyphenated lowercase
per HTTP UA convention).
- **D3 — Camel/lower for IDs:** **`deckhearth`** (single token, no
separators). Used in the five Redis key prefixes
(`deckhearth:auth/search/upload/generate/import`) — full word, NOT the
abbreviated `dh` (the ~800KB Redis-memory savings at peak load is
negligible against an Upstash plan that bills by request count, and
the self-documenting prefix is materially easier to debug in Redis
console output).
- **D4 — Admin email:** **`admin@deckhearth.com`** (placeholder TLD
pending the queued `point-domain-at-deckhearth` convoy). Sweep
applied to all three `@tcgvault.com` accounts uniformly via
`REPLACE(email, '@tcgvault.com', '@deckhearth.com')`: admin + alice
+ bob (the test users created by `scripts/create-test-users.js`).
- **D5 — Redis prefix:** **full `deckhearth`**. Pattern:
`deckhearth:auth`, `deckhearth:search`, `deckhearth:upload`,
`deckhearth:generate`, `deckhearth:import`. The five-prefixes-must-be-
distinct invariant from `add-rate-limiting` Brief 1 is preserved.
**Risk 4 PRESERVE applied:** the architect's recommendation
(`.convoys/pick-a-name.md` § Risk list § 4) was to preserve the
historical `admin@tcgvault.com` literal in
`test/lib/permission-middleware.test.js` line 87's negative regression
assertion as the EXACT pre-`fix-auth-bypass` synthetic-admin shape this
test locks against. The implementer kept the literal and added a
7-line comment block above the `.not.toEqual({...})` call documenting
why future doc-writers / refactorers must NOT update the literal to the
new `@deckhearth.com` email — doing so would weaken the regression-lock
to a shape that never actually existed. **This matches the project's
established pattern** of treating historical-shape locks as audit-trail
artifacts rather than self-consistency markers (Gotchas #2, #3, #4, #5,
#8 in `AGENTS.md` all note "Entry kept (not renumbered) to preserve
cross-references" for the same reason). The post-merge state has
exactly **1** `@tcgvault.com` literal outside of `.convoys/**` and
`*.md` files — the preserved historical regression-lock.
#### Brief structure (file-disjoint; serial-in-practice)
The architect designed B1 (7 files: `AGENTS.md` line 5, two
`.cursor/rules/*.mdc` description / branding paragraphs, `scripts/README.md`
title, three `User-Agent` strings) and B2 (10 modified + 1 new
migration script: `lib/rate-limit.js` 5 prefix renames, `package.json`
`name` rename, `package-lock.json` regen, `README.md` + `TESTING_GUIDE.md`
title + email + operator caveat, three seed/reset/test-user scripts'
admin/alice/bob email rename, `pages/login.js` quick-login fixtures,
`test/lib/permission-middleware.test.js` Risk 4 PRESERVE comment block,
new `scripts/migrations/2026-05-24-rename-admin-email.js`) so that the
two `files:` sets are DISJOINT and the conductor could dispatch them in
parallel via `/multitask`. **In practice the two briefs landed
serially** (B1 commit `ac8c998` first, B2 commit `1c18d21` second on
the same branch) for the lockfile-regen + migration-script-author
caution recommended by Brief 2 § Risk 3 (lockfile STOP-on-churn
verification is easier to audit if no other diff is in flight). The
parallel-via-multitask path stayed open as the architect's intended
fallback if either brief had hit a blocker.
#### As-shipped diff
Per `git show --stat 9abbab6`:
- **B1 (`ac8c998`): 7+/7-** across 7 files (the 7 `files:` listed in
`.convoys/pick-a-name/brief-1-display-and-comment-sweep.md`). Pure
string-literal edits; no new code, no new imports, zero ESLint
surface added.
- **B2 (`1c18d21`): 133+/30-** across 10 modified files + 1 new
migration script. The +/- imbalance is dominated by the new
~85-line migration script (`scripts/migrations/2026-05-24-rename-
admin-email.js`) + the new operator-caveat blockquote in `README.md`
(~10 lines added) + the 7-line Risk 4 PRESERVE comment block in
`test/lib/permission-middleware.test.js`. Source-code edits in
`lib/rate-limit.js` (5 prefix lines) + `pages/login.js` (2 quick-
login fixtures) + `scripts/setup-neon-db.js` / `reset-db.js` /
`create-test-users.js` (admin/alice/bob email INSERTs + console
logs) are pure string-literal swaps.
- **Lockfile regen:** the `package-lock.json` diff was confirmed
before B2 commit to touch ONLY the two `name` lines (top-level +
`packages[""]` entry); the brief's STOP-on-churn rule (Risk 3) was
exercised by the implementer and confirmed clean — no opportunistic
dependency-metadata refresh, no integrity-hash churn, no `version`
bumps. `npm ci` succeeded against the regenerated lockfile.
#### As-shipped CI metrics
Post-merge run 26387909625 + subsequent runs on `main`:
- **`Playwright smoke` — PASS in 1m4s, 3/3 tests** against the rebranded
Vercel preview. Same three checks (`home redirects or renders without
5xx` ✓ / `sign-in page renders` ✓ / `public health endpoint responds`
✓) — all green. The new brand strings render correctly on `/` +
`/login`; the auth surface still works (smoke test 2 still asserts
the `<Link href="/login">Sign in</Link>` CTA wording, which is
brand-agnostic per architect-time verification of the smoke spec's
text matchers); no false 429s from the renamed Redis prefixes (the
post-rename `deckhearth:*` keys were brand-fresh in Upstash, but the
smoke spec's `/api/health` hit is anonymous and unrate-limited and
the home + sign-in routes don't touch any of the 6 newly-gated
rate-limit endpoints).
- **`Aggregate gate` — PASS in 10s.** All upstream gate dependencies
green.
- **`Lint` — PASS in 45s, 128 problems** (lint baseline preserved, no
regression from B1 or B2). Zero new lint problems; `|| true` wrapper
in `.github/workflows/ci.yml` was a no-op for this convoy.
- **`forbidden-endpoints` (from `fix-auth-bypass` Brief 3) — PASS in
4s.** No new `pages/api/test-*.js` shapes reintroduced.
- **`forbidden-cors-headers` (from `cors-tighten`) — PASS in 4s.** Pure
brand-rename diff; no `Access-Control-Allow-*` headers reintroduced.
- **`Unit tests (vitest)` — PASS, 21/21 in 35s.** The Risk 4 PRESERVE
comment-block addition kept the negative regression-lock test green
byte-identically — the assertion semantics are unchanged; only the
comment block above the `.not.toEqual({...})` call is new. Decision
6 (no new vitest specs in this convoy) honored per the
`add-rate-limiting` D6 + `cors-tighten` D4 precedent.
- **`Screenshot diff` — PASS in 1m5s** (`continue-on-error: true`
swallow per `adopt-playwright-smoke` Decision 4 still active; the
rebrand introduced no visual regression because no baseline is
committed yet — the queued `seed-visual-baselines-on-linux` convoy
generates the FIRST baseline and that baseline will capture the
Deck Hearth brand strings, per the architect's ordering note in
`.convoys/pick-a-name.md` § Test plan + the updated entry in
`.convoys/ship-readiness.md` § Queued convoys).
- **`Schema map up to date`** — skipping (only runs on Prisma changes;
this convoy made an INSERT-semantics change but no DDL change).
- **Vercel deployment** — completed cleanly. First production-tier
Vercel deploy serving the `deck-hearth` `package.json` `name`.
#### Cross-validation finding: smoke spec defends the brand-rename surface (organically)
This convoy is the **fourth in a row** where the same 3-test Playwright
smoke spec authored by `adopt-playwright-smoke` (PR #18, 2026-05-24)
defended the auth surface through a sweeping change without anyone
having to write a dedicated test. The lineage now reads:
- **PR #15** (`fix-layout-default-user`, `ca302a8`) — introduced the
`<Link href="/login">Sign in</Link>` CTA that smoke test 2 asserts on.
- **PR #19** (`cors-tighten`, `da50d78`) — removed wildcard CORS from
24 handlers; smoke test 2 still passed against the post-CORS preview.
- **PR #20** (`add-rate-limiting`, `708ef45`) — wired 6 new route gates
(search/upload/generate/import) and refactored `lib/rate-limit.js`
end-to-end; smoke test 2 still passed (the new 60/min search ceiling
doesn't 429 the smoke spec because home + `/login` don't touch any
gated route).
- **PR #21** (`pick-a-name`, `9abbab6`) — this convoy — renamed the
product brand end-to-end across 17 files + 1 new migration script,
including all 5 Redis key prefixes; smoke test 2 still passes.
The smoke spec's text matchers (`'sign in'` regex, `/api/health`,
HTTP status < 500) are brand-agnostic by architect-time design but
**organically**, smoke is doing real work: it has now defended the
auth surface against four sweeping changes spanning the full P0 #5
+ P0 #6 + P0 #7 closures + the P1 #12 brand-consistency closure.
That's a "trust the smoke spec" data point worth preserving. Future
sweeping changes (single-auth-provider, single-sql-client, the
god-component-split convoys) should expect the smoke spec to keep
catching auth-surface regressions without dedicated coverage —
unless and until a feature change deliberately rewords the
`<Link>Sign in</Link>` CTA, in which case the smoke spec needs a
matcher update in the same PR.
#### Implementer-vs-architect calibration note
The architect's B2 forecast paragraph (`.convoys/pick-a-name/brief-2-
infrastructure-and-email-migration.md` line 662, the post-B2
repo-wide grep cross-check) reads *"Expected: at most 1 match —
`test/lib/permission-middleware.test.js` line 87's preserved historical
regression literal"*. Architect-verified — the implementer's actual
post-B2 state showed exactly 1 non-`.convoys/`-non-`*.md` match. **But
the architect's authoritative § Full surface inventory § Convoy
artifacts + manifest list (line 344 of the convoy file)** had already
correctly enumerated `scripts/seed-collections-with-cards.js` and
`scripts/seed-collections-alice-bob.js` (3 hits each) as DO-NOT-TOUCH
historical seed jobs per `.cursor/rules/no-go-zones.mdc` § Append-only
/ historical, with the explicit reasoning: *"the email migration in B2
covers the in-DB rename, so these scripts' WHERE-clause literals would
simply match zero rows post-migration if re-run, which is a harmless
no-op."* So the forecast paragraph and the inventory are both correct
— the forecast counted "in-scope source files" while the inventory
documented why the seed-collections matches stay. Worth noting for the
next architect: when forecasting post-brief grep-cleanliness, cite the
inventory's exact carve-out rationale to avoid a reader thinking the
two numbers contradict each other. Not a process gap — both ends were
right; the cross-reference between them could be tighter.
#### Operator action required going forward
**Run `node scripts/migrations/2026-05-24-rename-admin-email.js`
against the prod Neon DB before any admin login attempt with the new
`admin@deckhearth.com` email.** The migration is ESM, idempotent,
UNIQUE-collision-safe — re-running on an already-migrated DB prints
"Nothing to migrate." and exits 0; running on a DB where the seed
script already created a duplicate `admin@deckhearth.com` row fails
loud on the `users.email` UNIQUE constraint (which is the correct
fail-loud behavior — manually delete one of the duplicate admin rows
before retrying). **Order: migration FIRST, then any subsequent
`npm run setup-db`** — the migration sweeps the existing
`@tcgvault.com` row to `@deckhearth.com`; a subsequent `setup-db`
sees the row exists with the new email and no-ops via `ON CONFLICT
(email) DO NOTHING`.
Documented in three places for redundancy:
- `AGENTS.md` Gotcha #4 (extended in this doc-writer pass with the
email-rename + migration-script + ordering paragraph).
- `README.md` operator caveat blockquote (added by B2 alongside the
existing `admin123`-rotation caveat from `drop-public-setup`).
- The migration script's own header comment + stdout messages.
Two downstream convoys are queued in `.convoys/ship-readiness.md`
§ Queued convoys after this one:
- **`rename-repo-and-vercel-project`** (P2 polish) — GitHub repo +
Vercel project rename to `deck-hearth`; auto-redirects make it
low-urgency. One-line update to local git remotes + Vercel link
+ the 8 architect-verified `tcg-vault` literal-repo references
(README cd / tree, AGENTS.md line 1, three workflow YAML comments,
`.agent-context-manifest.yml` source tags).
- **`point-domain-at-deckhearth`** (blocked on domain acquisition)
— DNS pointer + Vercel domain claim + admin email TLD swap if the
operator buys a TLD other than `.com`.
#### What did NOT change
Audit trail of files explicitly NOT touched by this convoy, despite
sitting near the brand-rename surface:
- **All `.convoys/**` historical artifacts.** ~25 lines across the
drop-public-setup / fix-auth-bypass / cors-tighten / add-rate-limiting
/ fix-layout-default-user / bump-next-js / fix-vercel-deployment-
protection convoy files reference `tcgvault` / `tcg-vault` /
`TCG Vault` as the historical state at write-time — per
`.cursor/rules/no-go-zones.mdc` § Append-only / historical, these
stay byte-identical. The doc-writer pass at THIS convoy's close
rewrites only `.convoys/pick-a-name.md` (this file) + Gotcha #4 +
Gotcha #12 of `AGENTS.md` + the launch-sequence step in
`.convoys/ship-readiness.md` + the lineage attribution in
`.cursor/rules/api-routes.mdc`. No retroactive historical rewrite.
- **`scripts/seed-collections-with-cards.js`,
`scripts/seed-collections-alice-bob.js`,
`scripts/add-*.js`, `scripts/fix-*.js`** — historical seed /
migration jobs per the no-go-zones rule. The 6 hits (3 each in the
two seed-collections scripts) of `@tcgvault.com` literals in their
WHERE-clauses are now stale — the migration sweeps the in-DB rows,
so any re-run of these scripts would match zero rows and no-op
harmlessly.
- **Repo-name references** — `README.md` lines 30 + 105, `AGENTS.md`
line 1, `.github/workflows/ci.yml` lines 12 + 123,
`.github/workflows/visual-diff.yml` line 5,
`.agent-context-manifest.yml` source tags. All literally describe
the GitHub repo + Vercel project, both of which still ARE named
`tcg-vault` until the queued `rename-repo-and-vercel-project` convoy
ships. Per the convoy seed § *"Keep the repo `tcg-vault` for now"*.
- **`docs/agent-context/README.md` line 78** (`"we'll measure
tcg-vault's number after a few convoys"`) and
**`.cursor/skills/add-api-route/SKILL.md` line 7**
(`"…for the tcg-vault stack"`) — both reference the literal repo
name, not the product brand. Stay byte-identical; they sweep
together with `rename-repo-and-vercel-project`.
- **`scripts/reset-db.js` line 142** (CJS-in-ESM bug) and **lines
143 + 156** (`admin123` weak password literals) — out of scope
per `convert-reset-db-to-esm` (now queued, surfaced 2026-05-24
during this convoy's architect inventory) + `purge-weak-creds-
from-helpers`. B2 only renamed the email literals on lines 147 +
155; the CJS bug + weak-password literals stay byte-identical for
the dedicated follow-up convoys to fix together.
- **`tests/smoke/**`, `tests/visual/**`, `playwright.config.js`**
— smoke spec is brand-agnostic (architect-verified at brief time);
visual baseline doesn't exist yet (per
`adopt-playwright-smoke` Decision 4). The queued
`seed-visual-baselines-on-linux` convoy generates the first
baseline AFTER `pick-a-name` lands so the captured strings are
Deck Hearth (now satisfied — `9abbab6` is on `main`).
- **`.agent-context-manifest.yml` `source: "tcg-vault-local"` tags**
(6 entries) — internal `sync-agent-context` skill source identifier
for drift tracking. Renaming could break the skill's behavior;
flagged for re-evaluation in `rename-repo-and-vercel-project` when
the actual repo name changes (Risk 5 of the convoy file).
- **`pages/api/cards/import-mtg.js`'s `User-Agent`** — the file does
NOT set a custom UA (Scryfall is fine with Node's default per
Brief 1 Finding 2). Only the Lorcana + Pokémon TCG import handlers
+ the standalone `scripts/import-lorcana.js` CLI variant set
`Deck-Hearth/1.0`.
- **No new vitest or playwright specs** — Decision 6 honored. The
Risk 4 PRESERVE comment-block addition is the only test-file edit;
assertion semantics are unchanged.