- Add an Admin nav item (key 'admin', href /settings/users) with
visibleTo=['admin'] so signed-in users with role='user' don't see it.
- nav-config exposes navItemsForRole(role) helper that filters NAV_ITEMS
by visibleTo.
- Root layout fetches getCurrentUser() and forwards role into AppShell.
AppShell narrows the role gate to the rendered nav (sidebar + drawer);
/login still short-circuits to the bare header. Unknown role falls
back to 'user' visibility (defense-in-depth).
- Settings page renders an admin-only card linking to Users so admins
have a discoverable in-app entry point too.
Tests:
- nav-config: navItemsForRole admin/user matrix + admin entry shape.
- app-shell: admin link visible for admin, hidden for user, hidden for
null/unauthenticated, /login bare header strips nav entirely.
- actions/auth: cookie payload encodes role=user, unknown role rejected,
AUTH_SECRET-unset path, whitespace-only username rejected, rate-limit
key contains client IP, unknown-user path still hits DB+bcrypt.
440 tests now (was 423).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>