Concrete, well-scoped tasks chosen to build understanding while making a real change. Ordered from easiest to hardest.
app/styles/tailwind.css (line 218) sets the global border-color to hsl(var(--border)), but --border holds an oklch() value, so the declaration is invalid and every element with a bare border class falls back to currentColor. TooltipContent and both dropdown menu panels use border with no colour utility, so their outlines take the text colour. Change it to var(--border), then compare a StatusButton tooltip and the user menu before and after, in light and dark mode.
Why it's a good first task: A one-line change with a visible before and after that teaches the token pipeline — :root variables, the @theme inline mapping and how utilities consume them — without touching server code.
shouldRequestTwoFA in app/routes/_auth/login.server.ts says users are asked for 2FA again after two hours, but const twoHours = 1000 * 60 * 2 is two minutes. requireRecentVerification in verify.server.ts relies on it to guard disabling 2FA and changing email. Decide which is intended (a short window is defensible for sensitive settings), make the constant and the comment agree under a clearly named value, and add a colocated Vitest test covering a session verified inside and outside the window.
Why it's a good first task: It walks a newcomer through the session and verification cookies behind every sensitive settings page, and the colocated test pattern in app/utils/auth.server.test.ts is already there to copy.
server/index.ts applies its strongest rate limit only to paths listed in strongPaths, matched exactly with includes. /forgot-password and the /webauthn passkey endpoints are missing, while /resources/login and /resources/verify no longer exist as routes. Rebuild the list from the routes that accept credentials or send email, consider prefix matching so nested paths are covered, record the change in docs/decisions/025-rate-limiting.md, and check it with npm run build and npm run start:mocks.
Why it's a good first task: Small in code, but it forces reading the Express middleware order and the full route list, which is the map a newcomer needs before touching the server.
The init migration grants admins update:note:any, and the note page shows the Edit link to anyone allowed to delete the note, but the loader in $noteId_.edit.tsx and the action in +shared/note-editor.server.tsx both filter by ownerId: userId, so an admin who follows Edit gets a 404. Replace the ownership filters with requireUserWithPermission using update:note:own or update:note:any, make sure saving keeps the note's original owner, show the Edit link through userHasPermission the way canDelete already works, and add a Playwright test that signs in as an admin and edits another user's note.
Why it's a good first task: It crosses every layer a real feature does — seeded RBAC data, server-side permission guards, the UI permission check and an end-to-end test — on a small, well-contained flow.