Skip to content

Latest commit

 

History

History
45 lines (31 loc) · 4.22 KB

AGENTS.md

File metadata and controls

45 lines (31 loc) · 4.22 KB

This is NOT the Next.js you know

This version has breaking changes. APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.

Follow the Next.js App Architecture skill for all architectural decisions.

Repo conventions

  • Next.js 16.3 canary, cacheComponents: true, React Compiler enabled
  • Tailwind CSS v4 with CSS custom properties as design tokens
  • React 19 with ViewTransition for streaming reveals; toggled via experimental.viewTransition in next.config.ts
  • Feature folders: features/profile/, features/leaderboard/
  • Feature roots should stay readable: keep <feature>-queries.ts / <feature>-actions.ts at the root, and use named subfolders like data/ when a feature has static data.
  • Theme plumbing lives in components/theme/
  • Shared UI primitives, button states, UI icons, crossfade, and error boundaries live in components/ui/; Cronotype-specific shared components stay in components/
  • Domain verb "reveal" is the app's term for classifying a handle; don't reintroduce "diagnose"
  • Public GitHub data queries use 'use cache: remote' where durable cross-request caching protects rate limits.
  • GitHub 403/429 is an expected operational state. Foreground profile renders should still reach the route error UI, but cached/background work must not leak GitHub rate-limit rejections as unhandled promises. If a cached helper can revalidate outside a request boundary, catch only GitHub rate-limit errors at that boundary and return an explicit fallback value; rethrow not-found and unknown errors.
  • Theme: light/dark via next-themes; client components use useSyncExternalStore (not useEffect + setMounted) to read "mounted" state without lint errors

GitHub data sources

  • Search Commits API for the 90-day hourly distribution. 30 requests/minute limit - the dominant cost. Capped to 1 page (100 commits) per profile in features/profile/profile-queries.ts to keep budget reasonable.
  • Search Commits API for yearly archetypes. Every active history year is classified; normal years sample 100 commits, while years above 1000 contributions sample fewer commits so high-volume profiles keep their timeline shape without burning the rate-limit budget.
  • GraphQL contributions calendar for the multi-year history. 5000/hr pool. One call per year, fanned out serially in features/profile/profile-queries.ts.
  • GitHub OAuth private flow in features/profile/profile-private-queries.ts requests the classic repo scope because GitHub requires it for private repo commit search. The token is used for read requests during the callback and is not stored; only the derived result is kept in the browser.
  • The top revealed list only displays featured handles. Reveal state lives in lib/reveals.ts, backed by Upstash when configured, and the featured reveal lookups use cacheLife('cronotype').

Cache failure handling

  • Do not rely on instrumentation.ts or process.on('unhandledRejection') to keep Vercel functions alive. It may log escaped rejections, but the platform can still treat them as fatal.
  • Use isGitHubRateLimitError for 403/429 fallbacks. Keep the fallback small and explicit, and keep other errors loud.
  • Image routes do not have the profile page error boundary. They should return the generic/fallback image for GitHub rate limits, notFound() for missing users, and rethrow unknown errors.

Error boundaries

Use unstable_catchError from next/error. The reusable boundary primitive lives in components/ui/error-boundary.tsx; pass the profile or timeline-shaped fallback inline at the call site.

Mocking GitHub for local dev

Set MOCK_PROFILE=1 in .env.local. The mock path in features/profile/profile-queries.ts returns synthetic stats based on the login string, so every page renders without burning rate-limit budget. Use it for UI work.