133 lines
5.7 KiB
Markdown
133 lines
5.7 KiB
Markdown
|
|
---
|
||
|
|
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.
|