deckhearth/.convoys/fix-layout-default-user.md

133 lines
5.7 KiB
Markdown
Raw Normal View History

---
name: fix-layout-default-user
classification: convoy
success_metric: |
components/Layout.js's user prop defaults to null. Every page in pages/**
that renders Layout either passes a user prop explicitly OR relies on the
new logged-out rendering branch. No real email address remains in any
component default-prop. Manual smoke: load an unauthenticated session on
a page that previously impersonated the maintainer; confirm the navbar /
profile dropdown reads "Sign in" rather than "me@randallstillwell.com".
skip:
- role-design-system-auditor # no design-token changes
- role-ia-architect # no URL / IA changes
- browser-smoke # local smoke is fine for this scope
status: open
created: 2026-05-23
parent: ship-readiness
addresses: P0 #7
depends_on:
- bump-next-js (shipped)
- fix-auth-bypass (shipped)
- drop-public-setup (shipped)
---
# Fix Layout default user
Close P0 #7 from `.convoys/ship-readiness.md` (the **last** remaining P0
ship-blocker). `components/Layout.js` line 562 defaults the `user` prop to
`{ email: 'me@randallstillwell.com', role: 'user' }` — any page that renders
`Layout` without passing `user` explicitly displays the maintainer's real
email and impersonates them as the logged-in user.
## Scope (verbatim from ship-readiness P0 #7)
- **`components/Layout.js`** — change the `Layout({ children, user = {...} })`
default to `user = null`. Add a logged-out rendering branch (navbar /
profile dropdown / mobile menu) that handles `user === null` cleanly —
typically "Sign in" CTA replacing the user avatar + dropdown.
- **17 pages in `pages/**`** that import Layout (confirmed via grep):
```
pages/scanner.js
pages/collection/[identifier].js
pages/card/[id].js
pages/my-cards.js
pages/cards.js
pages/deck-builder.js
pages/deck/[id].js
pages/decks.js
pages/dashboard.js
pages/community/collections.js
pages/collections.js
pages/settings.js
pages/profile.js
pages/invite/decline.js
pages/invite/accept.js
pages/admin/card-import.js
pages/admin/card-editor.js
```
For each: confirm it passes `user` explicitly OR triage that it should
use the new logged-out branch (e.g. public pages like card/[id].js,
community/collections.js may legitimately render Layout for anonymous
visitors).
## Out of scope
- **Branding** — Layout still renders "Deck Hearth" / "DH" while the rest of
the repo says "TCG Vault". That mismatch is the queued `pick-a-name`
convoy (P1 #12). Don't fix branding here.
- **Three parallel auth providers** — Layout reads from one of
`lib/auth-context.js` / `lib/admin-auth.js` / `lib/use-auth.js`. Collapsing
them is the queued `single-auth-provider` convoy (P1 #9).
- **God-component split** — `components/Layout.js` is 700+ lines. Splitting
is the queued `god-component-split` convoy (P2 #13). Touch only the user-
prop default and the logged-out rendering branch in THIS convoy.
- **`MobileNavigation`** — receives `user` from Layout. May need a similar
default-prop fix if it has the same anti-pattern. Audit during architect
pass and decide whether to fold in or queue separately.
- **`AGENTS.md` Gotcha #8** — will be marked RESOLVED in the post-convoy
doc-writer pass; do not pre-emptively edit AGENTS.md here.
## Architect's questions
1. **Logged-out rendering branch design.** When `user === null`, what should
Layout render?
- **Q1a:** Navbar / profile dropdown — replace the user avatar + email
with a "Sign in" link to `/login`?
- **Q1b:** Mobile bottom-nav — same treatment, or hide the user-only
items entirely?
- **Q1c:** Authenticated-only nav items (admin, settings, profile) —
hide them, or show but link to `/login`?
2. **Page audit triage.** For each of the 17 pages, three buckets:
- **Always-authenticated** (dashboard, my-cards, profile, settings,
scanner, admin/*) — must pass `user` explicitly; pages without it
should add it via `useAuth()`.
- **Public-or-authenticated** (cards, card/[id], collection/[identifier],
community/collections, deck/[id], collections, decks) — currently
show different views based on auth; the Layout user prop should
come from `useAuth()` either way.
- **Anonymous-allowed** (invite/decline, invite/accept) — may
legitimately render Layout without a user; rely on the new
logged-out branch.
The architect should produce the exact bucket assignment per page and
the brief should give the implementer the per-page instruction.
3. **Brief decomposition.** Three options:
- **Single brief, one PR.** All 18 files (Layout + 17 pages) in one diff.
Reviewable but big.
- **Two briefs, one PR.** Brief 1: Layout change + logged-out rendering.
Brief 2: page audit (depends on Brief 1). Both ship together.
- **Fan-out by page bucket.** Brief 1: Layout change. Brief 2: always-auth
pages. Brief 3: public-or-auth pages. Brief 4: anonymous-allowed pages.
Multitask-friendly via worktrees.
**Recommend two briefs in one PR** for size + reviewability balance,
unless the page audit reveals >10 files needing real changes (in which
case fan-out makes sense).
4. **Test coverage.** Should this convoy add vitest tests that exercise
Layout's logged-out branch? The fix-auth-bypass convoy added 16 auth
tests (`test/lib/permission-middleware.test.js`); a similar lock-in for
the user-prop default could prevent regression.
**Recommend yes** — a single test that asserts `Layout` renders the
logged-out shape when `user === undefined` and `user === null` would
catch any future regression that reintroduces the maintainer-email
default. Trivial to write; high value.
## Expected size
1-2 briefs, ~18 files total (1 component, 17 pages). Single PR likely.