Files
supabase/apps/studio/CLAUDE.md
Alaister YoungandAlaister Young 2d5ec97df8 chore: split CLAUDE.md into root and studio-specific files (#48202)
Splits agent guidance into a lean monorepo-wide root file and a
studio-specific file that Claude Code lazy-loads when working under
`apps/studio/`. This keeps every session's baseline context small while
giving studio work much richer, enforceable guidance.

**Changed:**
- `.claude/CLAUDE.md` — now monorepo-wide only: corrected pnpm version
(10 → 11), expanded workspace table (design-system, ui-library,
lite-studio, ui-patterns, api-types, pg-meta, shared-data), commands
(`format`, `generate:types`, `api:codegen`), CI gates + never-hand-edit
generated files, monorepo-wide conventions (incl. the named-exports
rule, which lives in the shared eslint preset and applies to all six
apps), and monorepo-wide skill triggers. Studio detail is replaced by a
pointer to the nested file. Also corrects a long-standing error
inherited from the old file: the `_Shadcn_` convention was inverted —
`Button_Shadcn_` is the only suffixed export left and is rarely the
right choice; primitives are unsuffixed.
- `.claude/skills/studio-ui-patterns/SKILL.md` — removed the same stale
`_Shadcn_` claim from the forms section (this skill also feeds
CodeRabbit reviews).
- `apps/studio/components/README.md` — component template now uses a
named export, matching the lint-enforced convention (was the one doc
still showing `export default`).
- `apps/studio/TANSTACK_MIGRATION.md` — cleanup checklist gains an item
to remove the migration section from `apps/studio/CLAUDE.md` when the
migration finishes.
- `.gitignore` — removed the blanket `CLAUDE.md` ignore rule (added in
#40231 for personal local files, no longer used that way). Nested
`CLAUDE.md` files are now tracked by default, so shared guidance can't
silently fail to land. For *personal* notes, use `CLAUDE.local.md`
(Claude Code loads it automatically alongside `CLAUDE.md`, and it's now
gitignored here) — or `.git/info/exclude` if you prefer a different
filename.

**Added:**
- `apps/studio/CLAUDE.md` — studio guidance, loaded on demand: mandatory
skill routing (always load `studio-best-practices`, plus a task → skill
table), TanStack Start migration rules (pages/routes mirroring, when a
manual mirror is needed, never delete `pages/**` files),
data-layer/state orientation, a default-to-shipping-tests-with-changes
policy, and a "defaults that differ here" list (ESLint warning ratchet +
local `lint:ratchet` command, `copyToClipboard` await rule, `useParams`
from `common`, dayjs/sonner, `ui` vs `ui-patterns` import split,
`@tanstack/react-table` over `react-data-grid`, etc.).

## Accuracy

Every factual claim in both files (62 total) was verified against the
code by parallel review agents instructed to refute each one. Results:
54 correct as written, 2 wrong (the inherited `_Shadcn_` inversion, and
a fabricated `useExecuteSqlQuery` hook name — the real export is
`useExecuteSqlMutation`), 6 imprecise (e.g. dayjs plugins load in both
runtime entries, the ratchet counts occurrences regardless of severity).
All fixed in this PR.

## Context cost

| File | Size | When it loads | % of a 200k window |
|---|---|---|---|
| `.claude/CLAUDE.md` | 70 lines, ~1.2k est. tokens | every session |
~0.6% |
| `apps/studio/CLAUDE.md` | 53 lines, ~1.6k est. tokens | only when
touching studio files | ~0.8% |

The always-loaded footprint grew only ~0.2k est. tokens vs the old
45-line file — everything studio-heavy sits behind the lazy load, so
docs/www sessions pay nothing for it. Both files are well under Claude
Code's large-file warning threshold (~40k chars) and the <200-line
adherence guidance, with room to roughly double before it's worth
worrying about.

## To test

- Open a fresh Claude Code session from the repo root and read any file
under `apps/studio/` — `apps/studio/CLAUDE.md` should get pulled into
context automatically.
- `git check-ignore apps/studio/CLAUDE.md` exits 1 (not ignored); `git
check-ignore CLAUDE.local.md` exits 0 (ignored).
- Skim both files — every claim has been code-verified (see Accuracy
above), but a human sanity pass on the *judgment* calls (what's
included/omitted) is welcome.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Refreshed monorepo onboarding conventions with updated tooling
requirements, expanded inventory, standardized common scripts, and
clearer CI gating and checks.
* Added/updated Studio contributor guidance, including the TanStack
Start migration rules and Studio development/testing/UI conventions.
  * Updated Studio component documentation to use named exports.
* Refreshed the “Forms” UI pattern guidance and adjusted the referenced
UI primitives.
* **Chores**
* Updated ignore rules so the primary top-level onboarding document is
tracked.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-07-23 01:49:22 +08:00

6.4 KiB

Supabase Studio

Next.js pages router + TanStack Start (mid-migration, see below), React 19. Dev server: pnpm dev:studio → http://localhost:8082.

Skills — load before working

Always load the studio-best-practices skill before writing or modifying any Studio code. Then stack the area-specific skills:

Task Additional skills
Query/mutation hooks, query keys (data/**) studio-queries
UI: pages, forms, tables, charts, sheets, empty states studio-ui-patterns
Displaying API errors studio-error-handling
Tests (deciding, writing, reviewing) studio-testing, then studio-mock-api-tests (component/MSW) or studio-e2e-tests (Playwright)
PostHog event tracking telemetry-standards
SQL against user databases safe-sql-execution
Logs Explorer SQL, data/logs clickhouse-logs-queries
Component API design, boolean-prop refactors vercel-composition-patterns
User-facing copy copywriting

TanStack Start migration

Studio is migrating from the Next.js pages router (pages/**) to TanStack Start (routes/**). Both runtimes ship side-by-side; the STUDIO_FRAMEWORK env var selects which one pnpm dev/build runs (default: next, resolved in scripts/dispatch.js). Full route map and strategy: TANSTACK_MIGRATION.md.

  • Never delete a page file. Most routes/** files are thin wrappers re-exporting the default export of their pages/** counterpart, so the Next file is load-bearing for both runtimes until the final cleanup pass.
  • Pure page-body edits propagate to the route automatically. Mirror a change by hand into the corresponding routes/** file only when it touches what the route duplicates: getLayout/layout wrapping, page titles or other staticData (incl. skip*Layout flags), withAuth, or redirect paths.
  • A new page under pages/** needs a matching route under routes/** plus a checklist entry in TANSTACK_MIGRATION.md.
  • New code uses native TanStack APIs — no next/router or next/link. The compat/next/ shims exist only for legacy re-exported pages.
  • routeTree.gen.ts is generated by the Vite plugin — never hand-edit.

Orientation

  • Data layer — all platform API calls go through data/fetchers.ts (openapi-fetch, typed by the generated api-types package) with handleError; never raw fetch. One folder per resource in data/, most with a keys.ts query-key factory.
  • State — valtio for global state (state/), nuqs for URL state, react-hook-form + zod for forms.
  • Platform vs self-hosted — IS_PLATFORM gates platform-only behavior; withAuth is a no-op when self-hosted.
  • Telemetry — useTrack() from lib/telemetry/track; event types live in packages/common/telemetry-constants.ts.
  • Tests — default to including relevant tests with any change: a couple of unit tests for extracted logic, component tests for UI behavior, E2E only when the scope demands it (studio-testing has the decision tree). Not every PR needs them, but "no tests" should be a considered choice, not the default. Tooling: vitest + MSW; component tests use customRender + addAPIMock from tests/lib/; unhandled network requests fail tests. Don't vi.mock('@/data/...').
  • Shortcuts — use the registry in state/shortcuts/ and components/ui/Shortcut*.tsx; keep G then … chords for navigation; no one-off keyboard listeners.
  • Reuse first — before writing a new hook or helper, search for an existing one (hooks/, lib/, packages/common, packages/ui-patterns). If you do need a new one, make it as reusable as possible: general naming, no page-specific coupling, placed where other callers can find it.
  • Co-locate sub-components with their parent; avoid barrel re-export files.

Defaults that differ here

  • ESLint warnings are ratcheted in CI: the per-rule occurrence count must not increase, so a new any, unresolved exhaustive-deps warning, or default export fails the build even though it's "only a warning". Check locally with pnpm --filter studio run lint:ratchet.
  • Clipboard: copyToClipboard from 'ui', and never await anything before calling it (Safari requires the write inside the user gesture; lint-enforced) — pass a Promise as the argument instead.
  • useParams() comes from 'common', not next/navigation — it camelCases keys and returns string | undefined.
  • Permissions: useAsyncCheckPermissions from hooks/misc/useCheckPermissions (returns can: true when self-hosted).
  • Gating: useIsFeatureEnabled for product features, useFlag from 'common' for feature flags — two different systems.
  • Dates: dayjs (plugins pre-loaded at both entries, pages/_app.tsx and routes/__root.tsx), not date-fns. Toasts: toast from 'sonner'.
  • Import split: 'ui' = primitives, 'ui-patterns' = composed patterns (ConfirmationModal, …), @ui/* = alias into packages/ui/src. Icons come from lucide-react.
  • New tables use @tanstack/react-table; react-data-grid is banned for new code.
  • Ad-hoc SQL against the user's database goes through executeSql / useExecuteSqlMutation (data/sql/execute-sql-mutation).
  • Confirmations: ConfirmationModal / TextConfirmModal from ui-patterns, never window.confirm. Disabled buttons needing an explanation use ButtonTooltip; inline warnings use Admonition.