open-notebook/frontend/AGENTS.md
Luis Novo 23b51faf9f
docs: development docs match the codebase; add-a-provider playbook (#1458)
* docs: development docs match the codebase; add-a-provider playbook

- architecture.md: rewritten as a ~110-line map verified against the code
  (processes, layout, data model from the migrations, graphs, model calls,
  background jobs); links the accurate topic pages
- testing.md, api-reference.md, code-standards.md: rewritten from the real
  suite, the /api prefix and Bearer auth, and the actual error-handling
  pattern (no longer contradicts AGENTS.md)
- change-playbooks.md: new "Add an AI provider" playbook (registry + the four
  hand-maintained copies + tests + docs, checked against #1443/#1444); fixes
  async nodes, command registration, polling paths, locale steps
  (LanguageToggle, satisfies TranslationShape), colocated frontend tests
- development-setup.md is the single setup page (deps, .env with localhost
  SURREAL_URL, DB -> API -> worker -> frontend, real log lines, /health);
  quick-start.md and README.dev.md point to it
- contributing.md: Conventional Commits, CHANGELOG rule, pre-PR checks that
  match CI; PR template gains ruff format and frontend checks
- AGENTS files agree with the docs (provider edits, routers vs services,
  async nodes, checkpoint path, ADR-013, stop_on); api/AGENTS.md is a plain
  link instead of a Claude-only @ import
- ADR index explains ADR-010 (reserved for #1332) and number-at-merge;
  GitHub-compatible encryption anchor; small credentials/security fixes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: address review (CI commands, async source creation, paths, ADR-010, language toggle)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:06:36 -03:00

4.8 KiB

Frontend Rules (frontend/)

Normative rules for working on the Next.js frontend. Architecture and flow walkthroughs live in docs/7-DEVELOPMENT/frontend.md — this file is only what you must know before changing code. Project-wide rules are in the root AGENTS.md.

Commands (run inside frontend/)

  • Dev server: npm run dev (port 3000; API must be up at 5055 first)
  • Lint: npm run lint (eslint src/)
  • Tests: npm run test (vitest run) · coverage: npm run test:coverage
  • Build: npm run build

Hard rules

  • i18n is mandatory: every UI string goes through t('section.key') and the key must exist in all locales under src/lib/locales/ (currently 14; en-US is the reference). Missing keys fall back to en-US silently — keep locales in sync. Each non-en-US locale ends with satisfies TranslationShape (type derived from en-US), so a missing/extra key fails tsc; the parity test (src/lib/locales/index.test.ts) checks the same at runtime. Adding a whole language also touches src/lib/utils/date-locale.ts; src/components/common/LanguageToggle.tsx renders the languages list, so it needs no edit (see the i18n playbook).
  • Styling goes through design tokens (ADR-011): use semantic utilities (text-destructive, bg-warn-tint, text-teal, text-type-video, …) defined in src/app/globals.css — never raw Tailwind palette classes (text-red-600, bg-amber-50) and never hand-written dark: color twins (tokens re-resolve per theme). The laws: fern acts, teal is the AI voice, red only destroys, warn is clay, color never washes a reading surface. /dev/design (dev-only route) renders every token and primitive in both themes — it's the visual reference, and screen-level PRs must not change it.
  • All requests go through apiClient (src/lib/api/client.ts); never create a second axios instance. Auth token is auto-added from localStorage key auth-storage.
  • Data fetching uses TanStack Query hooks in src/lib/hooks/ with QUERY_KEYS; mutations invalidate caches and show toasts (sonner). Follow the existing hook shape.
  • FormData requests: nested objects/arrays must be JSON.stringify-ed before appending; the interceptor strips Content-Type so the browser sets the multipart boundary — don't re-add it.
  • No automatic request retry — handle failures in the consuming code (podcast retry is an explicit endpoint/hook, not a client retry).

Gotchas

  • Zustand stores with persist: check hasHydrated before rendering persisted state (SSR hydration mismatch), and keep each store's name key unique (localStorage collision).
  • NEXT_PUBLIC_API_TIMEOUT_MS: default 600000 (10 min) for slow LLM calls; 0 disables the timeout.
  • SSE (useAsk): incomplete lines stay in the buffer between reads; incomplete JSON is silently skipped — don't "simplify" the buffer handling.
  • useSourceStatus polls every 2s while status is running/queued/new.
  • Cache invalidation is deliberately broad (e.g. ['sources'] hits all source queries) — a simplicity/perf trade-off, be precise only when it matters.
  • Dark mode requires the dark class on the document root, not just prefers-color-scheme. It's hand-rolled: the zustand theme-store (persisted as theme-storage) sets the class via ThemeProvider — no next-themes.
  • Dialogs don't auto-reset form state (parent clears it) and auto-focus the first input (layout shift risk with conditional inputs). Fixed overlays use z-50.
  • Provider nesting order in app/layout.tsx matters: ErrorBoundary → ThemeProvider → QueryProvider → I18nProvider → ConnectionGuard → Toaster. ErrorBoundary is a class component and uses the raw enUS locale object (no hooks).
  • i18n runs with useSuspense: false (no SSR for translations).

Deep dives

frontend architecture · credentials system · change playbooks (i18n / frontend-only changes) · code standards

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/ (resolved from this file's directory; in monorepos the next package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by next dev — verify at node_modules/next/dist/server/lib/generate-agent-files.js. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.