deckhearth/.convoys/pick-a-name.md
Randall Stillwell 50ce9ab43a architect: pick-a-name (queued → in-progress; 2 briefs, D1-D5 routed back for operator gate-1)
Five decisions routed back for operator gate-1 ratification — none
architect-self-ratifiable, since all five are naming choices rather than
architectural ones. Gate-0 brand winner (Deck Hearth) is captured;
architect's job was to scope and minimize the cost of the rename, not to
re-litigate the brand. No blocking findings surfaced: no npm-package
collision (we don't publish), domain ownership is already a known queued
follow-up, Redis counter reset is the explicitly-accepted trade.

Architecture: 2 file-disjoint briefs that can run in parallel via
/multitask once gate-1 lands. ~75-110 lines net diff across 16 source
files + 1 new migration script (excluding the opaque package-lock.json
regen). Brief 1 is the mechanical display/comment sweep (7 files, ~7
lines) — branding notes, rule descriptions, three User-Agent product
tokens. Brief 2 owns the infrastructure + email-rename blast (10 files
+ 1 new migration script, ~30 edits) — Redis prefix rename in
lib/rate-limit.js (5 lines), package.json + lockfile regen, admin/alice/
bob email rename across seed/reset/test-user scripts + login.js
fixtures + README + TESTING_GUIDE + the test-file regression-lock, plus
the new scripts/migrations/2026-05-24-rename-admin-email.js (idempotent
REPLACE() UPDATE with UNIQUE-constraint fail-loud semantics).

D1-D5 recommendations all biased toward existing-string consistency:
D1 "Deck Hearth" (matches all 7 already-correct user-facing surfaces;
choosing "Deckhearth" would re-sweep them — net-negative cost), D2
`deck-hearth` (matches the existing `deck-hearth-logo-container` CSS
class), D3 `deckhearth` (single token for ID use), D4
`admin@deckhearth.com` (placeholder .com pending point-domain convoy),
D5 full `deckhearth` Redis prefix (the 8-byte/key savings of `dh` are
negligible vs. self-documenting debuggability).

Boot-the-brief findings preempted: lockfile regen is architect-verified
to touch only the 2 `name` field lines (lines 2 + 8 of package-lock.json);
the test-file negative regression assertion's email literal recommendation
is PRESERVE the historical `admin@tcgvault.com` (the literal is a
documented pre-fix-auth-bypass bug shape, not an arbitrary email value);
scripts/reset-db.js line 142's CJS-in-ESM bug is OUT OF SCOPE and queued
as convert-reset-db-to-esm; the in-DB migration's UNIQUE-constraint fail-
loud is the intentional safety behavior. AGENTS.md Gotcha #4 / #12
updates are reserved for the doc-writer pass at convoy close (not
preempted by Brief 1).

Two NEW out-of-scope follow-ups surfaced beyond the convoy seed's four:
convert-reset-db-to-esm (CJS-in-ESM bug in reset-db.js, may fold into
purge-weak-creds-from-helpers) and update-seed-visual-baselines-on-linux-
ordering (the queued seed-visual-baselines convoy MUST run AFTER
pick-a-name so the first Linux baseline captures Deck Hearth strings,
not TCG Vault).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:04:07 -05:00

33 KiB
Raw Blame History

pick-a-name (P1 — pre-launch brand consistency)

Status: in-progress 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 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)

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-vaultdeck-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

(To be appended post-merge by role-doc-writer.)