| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 7 | 6 | 0% |
| Commands | 1 | 12 | 2 | 7% |
| Section tags | 3 | 2 | 2 | 43% |
What each file covers
Sections
0 shared · 7 only in A · 6 only in B- − Supabase Monorepo
- − Structure
- − Common Commands
- − CI
- − Conventions
- − Skills
- − Studio
- + Supabase Studio
- + Skills — load before working
- + TanStack Start migration
- + Orientation
- + Code style
- + Defaults that differ here
Commands
1 shared · 12 only in A · 2 only in B- − pnpm dev:docs
- − pnpm dev:www
- − pnpm test:studio
- − pnpm e2e
- − pnpm build --filter=studio
- − pnpm lint --filter=studio
- − pnpm typecheck
- − pnpm format
- − pnpm generate:types
- − pnpm api:codegen
- − eslint-config-supabase
- − vitest
- + pnpm dev
- + pnpm --filter studio run lint:ratchet
- pnpm dev:studio
Section tags
3 shared · 2 only in A · 2 only in B- − architecture
- − monorepo
- + testing-strategy
- + database
- test
- lint-format
- code-style
Line diff
supabase/supabase · .claude/CLAUDE.md
@@ −1 @@
1# Supabase Monorepo
2
3pnpm 11 + Turborepo monorepo. Requires Node >= 22.13.
4
5## Structure
6
7| Directory | Purpose |
8| ------------------------ | --------------------------------------------------------------------------- |
9| `apps/studio` | Supabase Studio/Dashboard — has its own `apps/studio/CLAUDE.md` (see below) |
10| `apps/docs` | Documentation site — Next.js app router, MDX (port 3001) |
11| `apps/www` | Marketing website — Next.js, app + pages (port 3000) |
12| `apps/design-system` | Component demos — source of truth for Studio UI patterns (port 3003) |
13| `apps/ui-library` | shadcn-style registry site for Supabase UI blocks (port 3004) |
14| `apps/lite-studio` | Lightweight Studio — different stack: React Router 7 + Vite + Tailwind v4 |
15| `packages/ui` | Shared UI components (shadcn/ui based) — `import { Button } from 'ui'` |
16| `packages/ui-patterns` | Composite components — subpath imports, e.g. `ui-patterns/AssistantChat` |
17| `packages/common` | Shared utils, telemetry constants, feature flags |
18| `packages/api-types` | Generated platform Management API types |
19| `packages/pg-meta` | SQL builders for Postgres introspection (`SafeSqlFragment`) |
20| `packages/shared-data` | Static data: pricing, plans, regions, error codes |
21| `e2e/studio`, `e2e/docs` | Playwright E2E tests |
22| `supabase/` | Local Supabase project: edge functions, migrations, config.toml |
23
24## Common Commands
25
26```bash
27pnpm dev:studio # run Studio dev server → http://localhost:8082
28pnpm dev:docs # run docs dev server
29pnpm dev:www # run www dev server
30pnpm test:studio # Studio unit tests (vitest)
31pnpm e2e # Studio E2E tests (playwright)
32pnpm build --filter=studio # build Studio
33pnpm lint --filter=studio # lint Studio
34pnpm typecheck # typecheck all packages
35pnpm format # Prettier write (check: pnpm test:prettier)
36pnpm generate:types # local DB types → supabase/functions/common/database-types.ts
37pnpm api:codegen # platform Management API types → packages/api-types
38```
39
40## CI
41
42Every PR must pass typecheck + lint (one workflow), Prettier, and a typos check. Other checks are path-filtered: Studio unit tests/build and the lint ratchet (ESLint warning count must not increase) run on `apps/studio/**` changes; app-specific test suites run on their own paths.
43
44Never hand-edit generated files: `packages/api-types/types/**`, `**/routeTree.gen.ts`, `**/__generated__/**`, `apps/docs/features/docs/generated/**`, `apps/www/.generated/**`, `supabase/functions/common/database-types.ts`.
45
46## Conventions
47
48**UI** — import from `'ui'`; primitives are shadcn/ui-based and exported unsuffixed (`Input`, `Select`, `Form`, …). Use `Button` — the in-house component and the standard everywhere (a raw shadcn `Button_Shadcn_` also exists but is rarely the right choice). Check `packages/ui/index.tsx` before creating new primitives. Higher-level patterns live in `packages/ui-patterns`.
49
50**Styling** — Tailwind only, semantic tokens (`bg-muted`, `text-foreground-light`), no hardcoded colors.
51
52**Exports** — named exports only; default exports are allowed only where a framework requires them (`pages/**`, `app/**`, config files — the eslint preset has the exact carve-out list). Lint-enforced across all apps via `eslint-config-supabase` (severity `warn` everywhere; hard-enforced in Studio by the lint ratchet).
53
54**Language** — Use U.S. English everywhere.
55
56## Skills
57
58The skills in `.claude/skills/` are the source of truth for conventions — load the relevant ones before working, don't guess:
59
60- `copywriting` — any user-facing text, anywhere in the monorepo
61- `docs-content` — anything under `apps/docs`
62- `telemetry-standards` — PostHog events, `packages/common/telemetry-constants.ts`
63- `dev-toolbar-review` — `packages/dev-tools`, `packages/common/posthog-client.ts`, `packages/common/feature-flags.tsx`
64- `safe-sql-execution` — any code that builds or executes SQL against user databases
65- `react-hook-form` — writing or modifying any form code, anywhere in the monorepo
66- `vitest` / `vercel-composition-patterns` — generic unit-testing and React composition references
67
68## Studio
69
70Before working on anything in `apps/studio`, read `apps/studio/CLAUDE.md` if it isn't already in context — it maps Studio tasks to required skills and covers the TanStack Start migration rules.
71
supabase/supabase · apps/studio/CLAUDE.md
@@ +1 @@
1# Supabase Studio
2
3Next.js pages router + TanStack Start (mid-migration, see below), React 19. Dev server: `pnpm dev:studio` → http://localhost:8082.
4
5## Skills — load before working
6
7Load the skills matching the task; stack them when a task spans areas:
8
9| Task | Additional skills |
10| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
11| Query/mutation hooks, query keys (`data/**`) | `studio-queries` |
12| UI: pages, forms, tables, charts, sheets, empty states | `studio-ui-patterns` |
13| Form logic: react-hook-form fields, watch/formState, reset, number inputs | `react-hook-form` |
14| Displaying API errors | `studio-error-handling` |
15| Tests (deciding, writing, reviewing) | `studio-testing`, then `studio-mock-api-tests` (component/MSW) or `studio-e2e-tests` (Playwright) |
16| PostHog event tracking | `telemetry-standards` |
17| SQL against user databases | `safe-sql-execution` |
18| Logs Explorer SQL, `data/logs` | `clickhouse-logs-queries` |
19| Component API design, boolean-prop refactors | `vercel-composition-patterns` |
20| User-facing copy | `copywriting` |
21
22## TanStack Start migration
23
24Studio 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`.
25
26- **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.
27- 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.
28- A new page under `pages/**` needs a matching route under `routes/**` plus a checklist entry in `TANSTACK_MIGRATION.md`.
29- New code uses native TanStack APIs — no `next/router` or `next/link`. The `compat/next/` shims exist only for legacy re-exported pages.
30- `routeTree.gen.ts` is generated by the Vite plugin — never hand-edit.
31
32## Orientation
33
34- **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.
35- **State** — valtio for global state (`state/`), nuqs for URL state, react-hook-form + zod for forms.
36- **Platform vs self-hosted** — `IS_PLATFORM` gates platform-only behavior; `withAuth` is a no-op when self-hosted.
37- **Telemetry** — `useTrack()` from `lib/telemetry/track`; event types live in `packages/common/telemetry-constants.ts`.
38- **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/...')`.
39- **Shortcuts** — use the registry in `state/shortcuts/` and `components/ui/Shortcut*.tsx`; keep `G then …` chords for navigation; no one-off keyboard listeners.
40- **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.
41- Co-locate sub-components with their parent; avoid barrel re-export files.
42
43## Code style
44
45Older Studio code predates some of these conventions. For new or modified code, follow them rather than mirroring nearby legacy patterns:
46
47- **Booleans** read as `is`/`has`/`can`/`should`. Derive them from existing state (`const isFormValid = name.length > 0 && email.includes('@')`) — mirroring a derivable value into `useState` synced by `useEffect` is a bug pattern. Give multi-condition logic a name (`const canShowAddButton = !isSchemaLocked && canUpdateColumns && …`) instead of inlining the chain in JSX.
48- **Ternaries**: one is fine for a binary choice; never nest them. Anything bigger flattens — early returns in statement position, sibling `&&` blocks in JSX.
49- **Fetch states** render with early returns at the top level, or a flat `&&` chain with mutually exclusive guards inline — never a nested ternary:
50
51 ```tsx
52 // Top level: early return per state
53 if (isLoading) return <GenericSkeletonLoader />
54 if (isError) return <AlertError error={error} subject="Failed to retrieve data" />
55 if (isSuccess && data.length === 0) return <EmptyState />
56 return <DataDisplay data={data} />
57
58 // Inline: flat `&&` blocks, mutually exclusive guards
59 <div>
60 {isLoading && <ShimmeringLoader />}
61 {isError && <AlertError error={error} />}
62 {isSuccess && data.length === 0 && <EmptyState />}
63 {isSuccess && data.length > 0 && <DataDisplay data={data} />}
64 </div>
65 ```
66
67- **`useEffect` is for synchronizing with external systems** (subscriptions, DOM, timers) — not for deriving data (compute it in render), reacting to user actions (do it in the handler), or fetching (React Query). Older code uses effects for all of these; don't copy it.
68- **State** stays as local as possible — lift it only when it's actually shared. Related form fields belong in a single react-hook-form + zod form, not parallel `useState` calls.
69- **Component size**: split at ~200–300 lines — or sooner when a component grows multiple distinct UI sections, tangled conditional rendering, or clusters of unrelated `useState`. Extract repeated JSX into small components, non-trivial pure logic into `.utils.ts` functions (which get unit tests), and reusable stateful logic into custom hooks.
70- **Memoization is not the default**: `useMemo`/`useCallback` only for measured expense or referential stability a memoized child depends on.
71- **TypeScript**: avoid `as` casts — where external data enters, parse it with zod (`schema.parse`/`safeParse`) instead. Model multi-state values as discriminated unions (`{ status: 'success'; data: T } | { status: 'error'; error: Error }`) rather than independent boolean flags.
72- **Naming**: prop callbacks are `onX`, internal handlers are `handleX`. Custom hooks return objects, not tuples.
73- **Refactoring**: when you move or extract code into a new module, update every importer to point at the new location directly — do **not** leave a re-export shim in the old file "for backward compatibility." It's a one-line import change per consumer, and keeping shims around makes the codebase messy and the true source of a symbol ambiguous.
74
75## Defaults that differ here
76
77- **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`.
78- **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.
79- **`useParams()` comes from `'common'`**, not `next/navigation` — it camelCases keys and returns `string | undefined`.
80- **Permissions**: `useAsyncCheckPermissions` from `hooks/misc/useCheckPermissions` (returns `can: true` when self-hosted).
81- **Gating**: `useIsFeatureEnabled` for product features, `useFlag` from `'common'` for feature flags — two different systems.
82- **Dates**: `dayjs` (plugins pre-loaded at both entries, `pages/_app.tsx` and `routes/__root.tsx`), not `date-fns`. **Toasts**: `toast` from `'sonner'`.
83- **Import split**: `'ui'` = primitives, `'ui-patterns'` = composed patterns (`ConfirmationModal`, …), `@ui/*` = alias into `packages/ui/src`. Icons come from `lucide-react`.
84- **New tables** use `@tanstack/react-table`; `react-data-grid` is banned for new code.
85- **Ad-hoc SQL** against the user's database goes through `executeSql` / `useExecuteSqlMutation` (`data/sql/execute-sql-mutation`).
86- **Confirmations**: `ConfirmationModal` / `TextConfirmModal` from `ui-patterns`, never `window.confirm`. Disabled buttons needing an explanation use `ButtonTooltip`; inline warnings use `Admonition`.
87
@@ −1 +1 @@
1−# Supabase Monorepo
1+# Supabase Studio
22
3−pnpm 11 + Turborepo monorepo. Requires Node >= 22.13.
3+Next.js pages router + TanStack Start (mid-migration, see below), React 19. Dev server: `pnpm dev:studio` → http://localhost:8082.
44
5−## Structure
5+## Skills — load before working
66
7−| Directory | Purpose |
8−| ------------------------ | --------------------------------------------------------------------------- |
9−| `apps/studio` | Supabase Studio/Dashboard — has its own `apps/studio/CLAUDE.md` (see below) |
10−| `apps/docs` | Documentation site — Next.js app router, MDX (port 3001) |
11−| `apps/www` | Marketing website — Next.js, app + pages (port 3000) |
12−| `apps/design-system` | Component demos — source of truth for Studio UI patterns (port 3003) |
13−| `apps/ui-library` | shadcn-style registry site for Supabase UI blocks (port 3004) |
14−| `apps/lite-studio` | Lightweight Studio — different stack: React Router 7 + Vite + Tailwind v4 |
15−| `packages/ui` | Shared UI components (shadcn/ui based) — `import { Button } from 'ui'` |
16−| `packages/ui-patterns` | Composite components — subpath imports, e.g. `ui-patterns/AssistantChat` |
17−| `packages/common` | Shared utils, telemetry constants, feature flags |
18−| `packages/api-types` | Generated platform Management API types |
19−| `packages/pg-meta` | SQL builders for Postgres introspection (`SafeSqlFragment`) |
20−| `packages/shared-data` | Static data: pricing, plans, regions, error codes |
21−| `e2e/studio`, `e2e/docs` | Playwright E2E tests |
22−| `supabase/` | Local Supabase project: edge functions, migrations, config.toml |
7+Load the skills matching the task; stack them when a task spans areas:
238
24−## Common Commands
9+| Task | Additional skills |
10+| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
11+| Query/mutation hooks, query keys (`data/**`) | `studio-queries` |
12+| UI: pages, forms, tables, charts, sheets, empty states | `studio-ui-patterns` |
13+| Form logic: react-hook-form fields, watch/formState, reset, number inputs | `react-hook-form` |
14+| Displaying API errors | `studio-error-handling` |
15+| Tests (deciding, writing, reviewing) | `studio-testing`, then `studio-mock-api-tests` (component/MSW) or `studio-e2e-tests` (Playwright) |
16+| PostHog event tracking | `telemetry-standards` |
17+| SQL against user databases | `safe-sql-execution` |
18+| Logs Explorer SQL, `data/logs` | `clickhouse-logs-queries` |
19+| Component API design, boolean-prop refactors | `vercel-composition-patterns` |
20+| User-facing copy | `copywriting` |
2521
26−```bash
27−pnpm dev:studio # run Studio dev server → http://localhost:8082
28−pnpm dev:docs # run docs dev server
29−pnpm dev:www # run www dev server
30−pnpm test:studio # Studio unit tests (vitest)
31−pnpm e2e # Studio E2E tests (playwright)
32−pnpm build --filter=studio # build Studio
33−pnpm lint --filter=studio # lint Studio
34−pnpm typecheck # typecheck all packages
35−pnpm format # Prettier write (check: pnpm test:prettier)
36−pnpm generate:types # local DB types → supabase/functions/common/database-types.ts
37−pnpm api:codegen # platform Management API types → packages/api-types
38−```
22+## TanStack Start migration
3923
40−## CI
24+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`.
4125
42−Every PR must pass typecheck + lint (one workflow), Prettier, and a typos check. Other checks are path-filtered: Studio unit tests/build and the lint ratchet (ESLint warning count must not increase) run on `apps/studio/**` changes; app-specific test suites run on their own paths.
26+- **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.
27+- 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.
28+- A new page under `pages/**` needs a matching route under `routes/**` plus a checklist entry in `TANSTACK_MIGRATION.md`.
29+- New code uses native TanStack APIs — no `next/router` or `next/link`. The `compat/next/` shims exist only for legacy re-exported pages.
30+- `routeTree.gen.ts` is generated by the Vite plugin — never hand-edit.
4331
44−Never hand-edit generated files: `packages/api-types/types/**`, `**/routeTree.gen.ts`, `**/__generated__/**`, `apps/docs/features/docs/generated/**`, `apps/www/.generated/**`, `supabase/functions/common/database-types.ts`.
32+## Orientation
4533
46−## Conventions
34+- **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.
35+- **State** — valtio for global state (`state/`), nuqs for URL state, react-hook-form + zod for forms.
36+- **Platform vs self-hosted** — `IS_PLATFORM` gates platform-only behavior; `withAuth` is a no-op when self-hosted.
37+- **Telemetry** — `useTrack()` from `lib/telemetry/track`; event types live in `packages/common/telemetry-constants.ts`.
38+- **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/...')`.
39+- **Shortcuts** — use the registry in `state/shortcuts/` and `components/ui/Shortcut*.tsx`; keep `G then …` chords for navigation; no one-off keyboard listeners.
40+- **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.
41+- Co-locate sub-components with their parent; avoid barrel re-export files.
4742
48−**UI** — import from `'ui'`; primitives are shadcn/ui-based and exported unsuffixed (`Input`, `Select`, `Form`, …). Use `Button` — the in-house component and the standard everywhere (a raw shadcn `Button_Shadcn_` also exists but is rarely the right choice). Check `packages/ui/index.tsx` before creating new primitives. Higher-level patterns live in `packages/ui-patterns`.
43+## Code style
4944
50−**Styling** — Tailwind only, semantic tokens (`bg-muted`, `text-foreground-light`), no hardcoded colors.
45+Older Studio code predates some of these conventions. For new or modified code, follow them rather than mirroring nearby legacy patterns:
5146
52−**Exports** — named exports only; default exports are allowed only where a framework requires them (`pages/**`, `app/**`, config files — the eslint preset has the exact carve-out list). Lint-enforced across all apps via `eslint-config-supabase` (severity `warn` everywhere; hard-enforced in Studio by the lint ratchet).
47+- **Booleans** read as `is`/`has`/`can`/`should`. Derive them from existing state (`const isFormValid = name.length > 0 && email.includes('@')`) — mirroring a derivable value into `useState` synced by `useEffect` is a bug pattern. Give multi-condition logic a name (`const canShowAddButton = !isSchemaLocked && canUpdateColumns && …`) instead of inlining the chain in JSX.
48+- **Ternaries**: one is fine for a binary choice; never nest them. Anything bigger flattens — early returns in statement position, sibling `&&` blocks in JSX.
49+- **Fetch states** render with early returns at the top level, or a flat `&&` chain with mutually exclusive guards inline — never a nested ternary:
5350
54−**Language** — Use U.S. English everywhere.
51+ ```tsx
52+ // Top level: early return per state
53+ if (isLoading) return <GenericSkeletonLoader />
54+ if (isError) return <AlertError error={error} subject="Failed to retrieve data" />
55+ if (isSuccess && data.length === 0) return <EmptyState />
56+ return <DataDisplay data={data} />
5557
56−## Skills
58+ // Inline: flat `&&` blocks, mutually exclusive guards
59+ <div>
60+ {isLoading && <ShimmeringLoader />}
61+ {isError && <AlertError error={error} />}
62+ {isSuccess && data.length === 0 && <EmptyState />}
63+ {isSuccess && data.length > 0 && <DataDisplay data={data} />}
64+ </div>
65+ ```
5766
58−The skills in `.claude/skills/` are the source of truth for conventions — load the relevant ones before working, don't guess:
67+- **`useEffect` is for synchronizing with external systems** (subscriptions, DOM, timers) — not for deriving data (compute it in render), reacting to user actions (do it in the handler), or fetching (React Query). Older code uses effects for all of these; don't copy it.
68+- **State** stays as local as possible — lift it only when it's actually shared. Related form fields belong in a single react-hook-form + zod form, not parallel `useState` calls.
69+- **Component size**: split at ~200–300 lines — or sooner when a component grows multiple distinct UI sections, tangled conditional rendering, or clusters of unrelated `useState`. Extract repeated JSX into small components, non-trivial pure logic into `.utils.ts` functions (which get unit tests), and reusable stateful logic into custom hooks.
70+- **Memoization is not the default**: `useMemo`/`useCallback` only for measured expense or referential stability a memoized child depends on.
71+- **TypeScript**: avoid `as` casts — where external data enters, parse it with zod (`schema.parse`/`safeParse`) instead. Model multi-state values as discriminated unions (`{ status: 'success'; data: T } | { status: 'error'; error: Error }`) rather than independent boolean flags.
72+- **Naming**: prop callbacks are `onX`, internal handlers are `handleX`. Custom hooks return objects, not tuples.
73+- **Refactoring**: when you move or extract code into a new module, update every importer to point at the new location directly — do **not** leave a re-export shim in the old file "for backward compatibility." It's a one-line import change per consumer, and keeping shims around makes the codebase messy and the true source of a symbol ambiguous.
5974
60−- `copywriting` — any user-facing text, anywhere in the monorepo
61−- `docs-content` — anything under `apps/docs`
62−- `telemetry-standards` — PostHog events, `packages/common/telemetry-constants.ts`
63−- `dev-toolbar-review` — `packages/dev-tools`, `packages/common/posthog-client.ts`, `packages/common/feature-flags.tsx`
64−- `safe-sql-execution` — any code that builds or executes SQL against user databases
65−- `react-hook-form` — writing or modifying any form code, anywhere in the monorepo
66−- `vitest` / `vercel-composition-patterns` — generic unit-testing and React composition references
75+## Defaults that differ here
6776
68−## Studio
69−
70−Before working on anything in `apps/studio`, read `apps/studio/CLAUDE.md` if it isn't already in context — it maps Studio tasks to required skills and covers the TanStack Start migration rules.
77+- **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`.
78+- **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.
79+- **`useParams()` comes from `'common'`**, not `next/navigation` — it camelCases keys and returns `string | undefined`.
80+- **Permissions**: `useAsyncCheckPermissions` from `hooks/misc/useCheckPermissions` (returns `can: true` when self-hosted).
81+- **Gating**: `useIsFeatureEnabled` for product features, `useFlag` from `'common'` for feature flags — two different systems.
82+- **Dates**: `dayjs` (plugins pre-loaded at both entries, `pages/_app.tsx` and `routes/__root.tsx`), not `date-fns`. **Toasts**: `toast` from `'sonner'`.
83+- **Import split**: `'ui'` = primitives, `'ui-patterns'` = composed patterns (`ConfirmationModal`, …), `@ui/*` = alias into `packages/ui/src`. Icons come from `lucide-react`.
84+- **New tables** use `@tanstack/react-table`; `react-data-grid` is banned for new code.
85+- **Ad-hoc SQL** against the user's database goes through `executeSql` / `useExecuteSqlMutation` (`data/sql/execute-sql-mutation`).
86+- **Confirmations**: `ConfirmationModal` / `TextConfirmModal` from `ui-patterns`, never `window.confirm`. Disabled buttons needing an explanation use `ButtonTooltip`; inline warnings use `Admonition`.
7187
