chore(components): remove dead user prop from MobileNavigation #28

Merged
varutasu merged 1 commit from convoy/cleanup-mobile-nav-dead-props into main 2026-05-26 23:53:29 -04:00
3 changed files with 156 additions and 2 deletions
Showing only changes of commit 6b70cca58c - Show all commits

View file

@ -0,0 +1,155 @@
# cleanup-mobile-nav-dead-props (P3 polish — single-prop hygiene)
**Status:** SHIPPED 2026-05-26 (PR TBD)
**Classification:** hygiene
**Priority:** P3 polish (not a bug, not a security issue; dead-prop
removal is purely a clarity-of-surface cleanup)
**Convoy owner:** parent (no architect — single-line prop removal in
one component + one caller; surfaced and pre-decided in a sibling
convoy)
**Opened:** 2026-05-26
## Background
`components/MobileNavigation.js` accepts `{ user, onMenuOpen }` but
never reads `user.*` — the bottom-bar items (Cards, Decks, Dashboard,
Community, More) are static and don't depend on auth state or role.
This was originally surfaced as **R8** in the `fix-layout-default-user`
convoy (see `.convoys/fix-layout-default-user.md` § R8 and § "Anything
flagged but not acted on") and deferred there with explicit
instructions: *"If the implementer is tempted to delete the prop, they
MUST stop — that's god-component-split / single-auth-provider
territory."* The deferral was correct for that convoy's scope; it is
no longer needed because the prop is genuinely dead at the current
static-bar reality, and removing it does not require a wider auth
refactor.
The follow-up was queued as `cleanup-mobile-nav-dead-props` in
`.convoys/ship-readiness.md` § Queued convoys, with a note that it
may fold into `god-component-split` (P2 #13) if that lands first.
God-component-split has not landed; this small hygiene convoy ships
first.
## Audit results
Pre-edit audit (the spec's "don't blindly trust the queue entry"
clause):
1. **Reading `components/MobileNavigation.js`** — the file is 171
lines. Line 5 destructures `{ user, onMenuOpen }`. Lines 6170 use
`onMenuOpen` exactly once (line 39, as the `onClick` for the "More"
button). `user` does not appear elsewhere — no `user.email`,
`user.role`, `user.id`, no conditional render gated on `user`, no
pass-through to a child component. The bottom-bar `navigationItems`
array is hardcoded and does not branch on auth state.
2. **`rg '\buser\b' components/MobileNavigation.js`** before edit: 1
hit (the destructure on line 5). After edit: 0 hits.
3. **`rg "MobileNavigation" components/ pages/ --type js`**: two
import + JSX-callsite pairs in the codebase:
- `components/Layout.js` (active) — line 5 import, line 598-601 JSX
call passing `user={user}` and `onMenuOpen={...}`.
- `components/Layout.js.backup` (no-go-zone per
`.cursor/rules/no-go-zones.mdc` § "Append-only / historical" —
"legacy snapshot; delete with a real PR, never edit") — line 5
import, line 264 JSX call. Left untouched per the no-go-zone
rule; if/when the `.backup` file is eventually deleted, this dead
call disappears with it.
Audit verdict: `user` is genuinely dead. Cleanup is safe.
## The fix
Two-file, three-line diff:
1. **`components/MobileNavigation.js` line 5**: remove `user` from the
destructured props.
- Before: `export default function MobileNavigation({ user, onMenuOpen }) {`
- After: `export default function MobileNavigation({ onMenuOpen }) {`
2. **`components/Layout.js` lines 598-601**: remove the `user={user}`
JSX attribute from the only active call site.
- Before:
```
<MobileNavigation
user={user}
onMenuOpen={() => setIsMobileMenuOpen(true)}
/>
```
- After:
```
<MobileNavigation
onMenuOpen={() => setIsMobileMenuOpen(true)}
/>
```
No new code. No refactors. No tests added (the component has no
direct test coverage; `test/components/Layout.test.js` tests Layout's
logged-out branch and does not assert on `MobileNavigation`'s prop
shape).
## Out of scope
- The unused `import { useState } from 'react'` on
`components/MobileNavigation.js` line 3. The hook is imported but
not called. This is a pre-existing dead import unrelated to the
`user` prop; the convoy spec explicitly forbids "refactor anything
else in MobileNavigation.js (this is a single-prop removal)". A
future hygiene pass can sweep it (or it'll get caught by an
eventual lint-no-unused-imports rule).
- `components/Layout.js.backup` — no-go-zone, untouched.
## Verification plan
1. `rg '\buser\b' components/MobileNavigation.js` → 0 hits (post-edit
confirmation that the prop is truly gone, not just renamed).
2. `rg "MobileNavigation" components/ pages/ --type js` → confirm
each active call site passes only `onMenuOpen`.
3. `npm run lint` → 128 problems baseline preserved (no regression
introduced; no new dead-code/unused-var warnings created by the
change).
4. `npm run test:run` → 21/21 pass. Specifically,
`test/components/Layout.test.js` continues to pass — its
regression-lock assertions for the logged-out Layout branch
(Gotcha #8) do not depend on `MobileNavigation`'s prop shape, so
the dead-prop removal is invisible to that suite.
5. `npm run build` skipped — relying on Vercel preview CI. Trade-off:
single-prop removal in a leaf component is extremely low risk of
build-time regression, and the Playwright smoke + visual-diff
workflows on the PR will catch any Layout-rendering issue before
merge.
## Risks
- **R1 — A future feature that wants per-user bottom-bar items would
need to re-add the prop.** Hypothetical examples: showing an
unread-count badge on a "Notifications" tab gated on `user.id`, or
hiding the "Community" tab for unauthenticated visitors. **Accepted.**
Re-adding a prop is a one-line change when the feature actually
lands; carrying a dead prop "just in case" obscures the current
surface and adds nothing. The cleanup is correct for the
current static-bar reality; future features pay their own
add-the-prop cost.
- **R2 — `components/Layout.js.backup` still references the old prop
shape.** **Accepted.** The backup is a no-go-zone (per
`.cursor/rules/no-go-zones.mdc`) and is dead code by definition.
Touching it would violate the rule; leaving it as a stale snapshot
is the convention. When the backup is eventually deleted in a
separate convoy, this stale call disappears with it.
## Acceptance criteria
- 2 files modified, 3 lines net change (1 line edit + 1 attribute
removal from a multi-line JSX block).
- `npm run lint` exit 1 with 128 problems (baseline preserved).
- `npm run test:run` 21/21 pass.
- `rg '\buser\b' components/MobileNavigation.js` → 0 hits post-edit.
## Owns
Parent (single-prop removal in a leaf component; no architect or
implementer subagent required; audit confirms the queue entry's
premise).
## As-shipped
_To be filled in post-merge._

View file

@ -596,7 +596,6 @@ export default function Layout({ children, user = null, showSearch = false }) {
<div className="flex h-screen" style={{ backgroundColor: 'var(--bg-primary)' }}>
{/* Mobile Navigation - Bottom bar for mobile */}
<MobileNavigation
user={user}
onMenuOpen={() => setIsMobileMenuOpen(true)}
/>

View file

@ -2,7 +2,7 @@ import { useRouter } from 'next/router';
import Link from 'next/link';
import { useState } from 'react';
export default function MobileNavigation({ user, onMenuOpen }) {
export default function MobileNavigation({ onMenuOpen }) {
const router = useRouter();
// Navigation items for the bottom bar