next-role / frontend
AGENTS.md67 starsChanged 13 days agoMITView on GitHub
Next.js 16 (App Router, Turbopack) + React 19 + TypeScript + Tailwind. Talks to the backend agents via @langchain/react (the useStream v2 runtime + scoped selector hooks) with @langchain/langgraph-sdk for the raw Client (threads/store APIs) and shared types. DESIGN.md (in this folder) is the human-facing spec for the UI: color tokens (warm paper + espresso, user-selectable accent — emerald default), typography (Newsreader / Schibsted Grotesk / JetBrains Mono), spacing/radius/shadow scales, and per-component specs with do's & don'ts. Read it before…
- Installs packages
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- analytics/AGENTS.mdAGENTS.md
- backend/AGENTS.mdAGENTS.md
- agent-browserSkill
- archify-reviewSkill
- archifySkill
- commitSkill
- drawio-skillSkill
- frontend-designSkill
- qualitySkill
- upgrade-backend-depsSkill
- upgrade-frontend-depsSkill
- write-prdSkill
- agent-browserSkill
- archify-reviewSkill
- archifySkill
- commitSkill
- drawio-skillSkill
- frontend-designSkill
- qualitySkill
- upgrade-backend-depsSkill
- upgrade-frontend-depsSkill
- write-prdSkill
The file
We split this AGENTS.md into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 81
All three direct deps are required: app code imports Client/Assistant/Thread from the SDK (not re-exported by @langchain/react) and message …
- Very specific: line 90
NOT warn at install time — pnpm silently installs two copies.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
frontend/AGENTS.md
Next.js 16 (App Router, Turbopack) + React 19 + TypeScript + Tailwind. Talks to the backend agents via @langchain/react (the useStream v2 runtime + scoped selector hooks) with @langchain/langgraph-sdk for the raw Client (threads/store APIs) and shared types.
Design system
DESIGN.md (in this folder) is the human-facing spec for the UI: color tokens (warm paper + espresso, user-selectable accent — emerald default), typography (Newsreader / Schibsted Grotesk / JetBrains Mono), spacing/radius/shadow scales, and per-component specs with do's & don'ts. Read it before changing styling, colors, theming, or adding/altering components. Keep it in sync when the design system changes.
Tokens live in src/app/globals.css (CSS variables: app --color-*/--brand-accent* layer + Radix HSL layer) and tailwind.config.mjs. Gotcha: Tailwind bg-primary/text-primary map to the paper tokens, not the brand — use bg-brand-accent/text-brand-accent/text-on-accent or the Button primary variant for brand-colored elements.
Tooling
- Package manager:
pnpmonly (lockfile ispnpm-lock.yaml, version pinned viapackageManagerinpackage.json). Don't runnpm installoryarn. - Dev:
pnpm dev(Turbopack). - Build:
pnpm build(Turbopack). - Lint:
pnpm lint:fix. - Format:
pnpm format(Prettier). - Type check:
pnpm type-check(tsc --noEmit). - All-in-one:
pnpm qualityruns lint + format + type-check. - Adding a dependency:
pnpm --dir frontend add <pkg>on the host, thendocker compose restart frontend. The container's startup runspnpm install --frozen-lockfile(seedocker-compose.yml), so the lockfile change syncs into the container'snode_modulesvolume on next boot — no image rebuild, no volume nuking.
Testing
Vitest 4 + React Testing Library. Tests are colocated (<module>.test.ts(x) next to its
source) and run in CI's required frontend-tests check — deliberately not in pre-commit.
- Run:
pnpm test(full suite),pnpm test:watch,pnpm test:coverage(v8),pnpm exec vitest run <path>(one file). - Environment by extension (
vitest.config.tsprojects):.test.ts→ node (pure modules, API route handlers, fetch wrappers);.test.tsx→ jsdom (anything that renders or toucheswindow/storage — a.tsxtest without JSX is fine when jsdom is needed). - Globals are on (
describe/it/expect/vineed no imports; tsconfig hasvitest/globals); RTL auto-cleanup is active. vitest.setup.tsowns the jsdom polyfills (ResizeObserver, matchMedia, pointer capture, scrollIntoView/scrollTo, PointerEvent) plus jest-dom matchers. Extend it rather than shimming per-test.- Mocking conventions — never hit the network or the real backend:
@langchain/react:vi.mockwith theimportOriginalspread, stubbing onlyuseStream/ the scoped selector hooks the component uses.- SDK
Client: mock the class where it's constructed (ClientProvider,useThreads).app/lib/agentFiles.tsreceives the client as a parameter (type-only import) — pass a plain stub object, no module mock. nuqs:NuqsTestingAdapterfor components,vi.mock("nuqs")for hooks.swr: never mock — wrap in a fresh-cache<SWRConfig value={{ provider: () => new Map() }}>.fetch:vi.stubGlobal("fetch", ...)+vi.unstubAllGlobals()in afterEach.sonner: mocktoast.
- Import-time state:
src/lib/config.tsreads env vars at module load — stub first (vi.stubEnv),vi.resetModules(), then dynamicallyawait import(...). Never enable globalrestoreMocks(it silently killsbeforeAllspies). - File bytes never pass through Next.js: artifact upload/read/list/write/delete go to the
backend files API (
${deploymentUrl}/files/*, seeapp/lib/agentFiles.tsfilesApiUrl). There are no Next API routes for files — test the client libs by stubbingfetchagainst/files/*URLs (seeagentFiles.test.ts,uploadFiles.test.ts); server-side validation lives inbackend/agents/files_api.pyand is tested inbackend/tests/test_files_api.py. - Upgrade rule:
vitest+@vitest/coverage-v8are an exact-version lockstep pair — bump together (see theupgrade-frontend-depsskill).
Style
- Line length: 100 (Prettier
printWidth). Matches the backend; keep them in sync. - Double quotes (Prettier
singleQuote: false), 2-space indent,trailingComma: "es5". prettier-plugin-tailwindcssreorders class names — don't fight it manually.- ESLint allows
@typescript-eslint/no-explicit-any; underscore-prefixed unused vars are fine.
Authentication (multi-user, opt-in)
Off by default (NEXT_PUBLIC_AUTH_ENABLED unset → today's zero-login single-user app). When on, Better Auth runs inside this app (Google OAuth + email/password + JWT plugin), tables in the shared Postgres.
- Gate on
isAuthEnabled()(src/lib/auth/enabled.ts) everywhere auth-specific code runs — every auth component/module must render/behave as a no-op when it returns false, so the zero-login path is byte-for-byte unchanged. Same forisGoogleAuthEnabled()(the Google button). - Files:
src/lib/auth/server.ts(server config, server-only — lazily imported by the route so zero-login never needs a DB pool/secret),client.ts(authClient),token.ts(bearer plumbing),src/app/api/auth/[...all]/route.ts(handler, 404s when disabled),src/app/login/page.tsx,src/app/components/auth/{SessionGate,UserMenu}.tsx. - Backend calls carry the JWT via
authOnRequest(SDKonRequesthook on bothClientsites) andauthedFetch(the 5 raw/files/*calls). Both are no-ops without a token —authedFetchpasses the request through untouched, so tests that assert exactfetch(...)arity still pass. The token is cached and refreshed on expiry/401 (getBearerToken/clearBearerToken). - Contract for the backend:
GET /api/auth/tokenmints an EdDSA JWT (sub= user id, 15-min TTL);/api/auth/jwkspublishes keys. The backend (backend/agents/auth.py) verifies against JWKS. - Config override is pinned in auth mode:
getConfig()(src/lib/config.ts) ignores a storeddeploymentUrl/assistantIdwhen auth is on (XSS bearer-exfil guard) — model/UI prefs stay local. WithNEXT_PUBLIC_CONNECTION_SETTINGS_ENABLED=false(production build) it also ignores a stored LangSmith key, and Settings hides the Connection fields. - Store namespaces need no per-user handling here: the FE keeps sending logical
["career_agent", …]; the backend's@auth.on.storerewrite prepends identity. Don't add the user segment on the FE. - Testing auth code:
vi.stubEnv("NEXT_PUBLIC_AUTH_ENABLED", ...)per test; mock@/lib/auth/client.token.tsholds module-level cache state, so re-import it fresh (vi.resetModules) after stubbing env (seetoken.test.ts).
Stack notes
- UI primitives are Radix (
@radix-ui/*) + Tailwind, not a single component library. - State:
swrfor data fetching,nuqsfor URL state,zodfor runtime validation. - LangGraph integration uses
@langchain/react+@langchain/langgraph-sdk+@langchain/core. All three direct deps are required: app code importsClient/Assistant/Threadfrom the SDK (not re-exported by@langchain/react) and message classes from core (a peer dep of@langchain/react— peers are never bundled; the app owns the singleBaseMessageidentity). Upgrade rule:@langchain/react+@langchain/langgraph-sdkare a lockstep pair (same monorepo, likereact/react-dom) — bump@langchain/reactfirst, then pin the SDK exactly (no^) to the version the new@langchain/reactdepends on:pnpm --dir frontend add @langchain/langgraph-sdk@<ver> --save-exact. The invariant is one installed copy of the SDK, shared with@langchain/react(Clientinstances cross the package boundary). The SDK is a regular dep of@langchain/react, so a version conflict does NOT warn at install time — pnpm silently installs two copies; only peer conflicts (@langchain/core) warn. The exact pin prevents self-inflicted drift (pnpm updatecan't float it), andscripts/check-langchain-sdk-sync.mjs(pre-commit hookfrontend-langchain-sdk-sync+pnpm quality) catches the forgotten-sync case on@langchain/reactbumps, printing the exact fix command. Prefer@langchain/reactre-exports for stream types (SubagentDiscoverySnapshot,AssembledToolCall); onlyClient+ schema types come from the SDK. Keep the SDK in sync with the backend'slanggraphmajor.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

