Commit Graph
100 Commits
Author SHA1 Message Date
Charis 84e7316d8d disable coderabbit high-level summary (#51180)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * No user-facing changes are included in this update.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-02 11:10:43 -04:00
Charis 0daafca2ca feat(studio): status page banner (incident / maintenance / upcoming) (#51044)
## Summary

* Adds a new global status banner (`StatusBanner`) driven by the
[incident.io](<http://incident.io>) status page data, showing at most
one of: an active incident, in-progress maintenance, or upcoming
maintenance, in that priority order.
* All three types are independently dismissible (persisted to a new
localStorage key, `status-banner-dismissed-keys`); dismissal hides the
banner for items still active, and a new incident reappears even if a
related item was previously dismissed.
* Only shows to users who are actually affected (based on their
projects' regions) or when region data is incomplete (fails open), and
is bypassed entirely by the existing emergency incident override.
* Behind the existing `incidentIoStatusPage` ConfigCat flag —
`AppBannerWrapper` renders this new banner instead of the legacy
`StatusPageBanner` only when the flag is on; default behavior is
unchanged.
* This is PR 5b in a stacked series for Linear
[FE-4057](https://linear.app/supabase/issue/FE-4057) — see that issue
for full design context.

## Test plan

* New unit tests (`StatusBanner.utils.test.ts`) covering
banner-selection priority, dismissal-key handling, and copy generation
* New MSW component test (`StatusBanner.test.tsx`) covering loading
state, dismiss-and-persist, and the emergency-override path
* `pnpm --filter studio run typecheck`, `lint:ratchet`, `pnpm knip
--workspace apps/studio`, `pnpm test:prettier`, and relevant vitest
suites all pass

🤖 Generated with [Claude Code](<https://claude.com/claude-code>)

Co-Authored-By: Claude
[noreply@anthropic.com](<mailto:noreply@anthropic.com>)

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

* **New Features**
* Added status banners for relevant incidents and scheduled maintenance,
including incident details, maintenance timing, and links to the status
page.
* Banners can be dismissed, and dismissed items stay hidden while new
incidents or maintenance updates can still appear.
* Upcoming maintenance banners appear within the relevant lead time, and
maintenance timing is shown when available.
* Emergency overrides display a warning banner without a dismiss option.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-29 18:09:47 -04:00
Charis d22702e907 refactor(studio): extract user project regions + emergency override hooks (#51033)
## Summary
- Extracts `useUserProjectRegions` (org + project region aggregation,
fail-open on fetch errors) and `useEmergencyIncidentOverride` (the
`ongoingIncident` flag / env var check) out of
`useStatusPageBannerVisibility`, so the upcoming incident.io status-page
banner can reuse both without duplicating the org/project fan-out logic.
- No behavior change for the legacy banner except one intentional fix:
`StatusPageBanner.utils.ts` compared the incident's affected regions
(lowercased) against the user's regions (not normalized), so a
mixed-case region on either side could silently fail to match. Both
sides now go through the same `normalizeRegion` helper.
- Part of the Linear FE-4057 stack (PR 5a of 6). Base branch is PR 4
(`charis/fe-4057-pr4-project-creation-status-admonition`), not master.

Linear: FE-4057

## Test plan
- [x] `pnpm --filter studio run typecheck`
- [x] `pnpm --filter studio run lint:ratchet`
- [x] `pnpm knip --workspace apps/studio`
- [x] `pnpm test:prettier`
- [x] `pnpm --filter studio exec vitest run` on the touched test files
and the `hooks/misc/` and `components/layouts/AppLayout/` directories
(all passing, no regressions)

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved status banner targeting by matching incidents against
normalized regions across the user’s projects.
* Updated banner visibility checks to handle incomplete project or
region data, including when project information fails to load.
* Improved loading behavior so the banner can be evaluated based on
user-region data rather than waiting for separate organization and
project requests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-29 17:49:06 -04:00
Charis 53b20cad5e feat(studio): project creation status admonition (#51029)
## Summary

- Adds a new project-creation-form incident warning path behind the
`incidentIoStatusPage` ConfigCat flag (default off — no visible behavior
change until the flag is enabled)
- When the flag is on, reads the new `/api/status-page` endpoint (added
earlier in this stack) instead of the legacy `/api/incident-status`
endpoint, and only fetches the legacy endpoint when the flag is off
- Extracts region-matching logic into `RegionSelector.utils.ts`
(`regionMatches`, `getItemsAffectingProjectCreation`) with unit test
coverage

See Linear FE-4057 for full context and design.

## Test plan

- [x] `pnpm --filter studio run typecheck`
- [x] `pnpm --filter studio run lint:ratchet`
- [x] `pnpm knip --workspace apps/studio`
- [x] `pnpm test:prettier`
- [x] `pnpm --filter studio exec vitest run
components/interfaces/ProjectCreation/RegionSelector.utils.test.ts`
(35/35 passing)

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

* **New Features**
* Project creation displays a warning and links to the status page when
an incident or maintenance event may affect the selected region.
* Status notices match exact regions and broader smart-region groupings;
global project-creation notices are also shown.
* The AI assistant can report visible, active incidents and in-progress
maintenance, including affected components and update details. When
there are no active events, it reports that status.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-29 17:37:01 -04:00
Charis 13de8189c9 feat(studio): assistant get_active_incidents on status page endpoint (#50994)
## Summary

* Adds a second branch to the assistant's `get_active_incidents` tool
(`apps/studio/lib/ai/tools/incident-tools.ts`) that reads
`/api/status-page` when the global ConfigCat flag `incidentIoStatusPage`
is on, instead of the legacy `/api/incident-status` endpoint.
* Filters on `visible`, ignores `show_banner` (only the global banner
respects it), and concatenates ongoing incidents + in-progress
maintenances (not scheduled maintenances).
* Adds `isServerFlagEnabled` to `lib/server/configcat.ts` for reading
global, non-user-targeted ConfigCat flags server-side, and threads the
flag through `getTools` from `pages/api/ai/sql/generate-v4.ts`.

This is PR 3 of 6 in the
[FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured)
stack — stacked on `charis/fe-4057-pr2-support-form`. Behind the
`incidentIoStatusPage` flag (default off), so this ships no user-visible
change on its own. See Linear
[FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured)
for full context.

## Test plan

- [X] `pnpm --filter studio run typecheck`
- [X] `pnpm --filter studio run lint:ratchet`
- [X] `pnpm knip --workspace apps/studio`
- [X] `pnpm test:prettier`
- [X] `pnpm --filter studio exec vitest run
lib/ai/tools/incident-tools.test.ts` (19/19 passing, including 7 new
tests for the status-page branch)
2026-09-29 15:28:49 -04:00
Charis 5573d0dfbc fix(studio): disable network restrictions for v3 (AWS_K8S) projects (#50996)
## Summary
- Network restrictions aren't supported on v3 (AWS_K8S) projects, but
the Network Restrictions settings section previously only disabled
itself for High Availability projects, leaving a fully working (but
non-functional) UI for non-HA v3 projects.
- Adds `useIsAwsK8sCloudProvider()` to the section's disabled check,
alongside a v3-specific disabled notice mirroring the existing High
Availability disabled notice pattern.

Resolves
[FE-4482](https://linear.app/supabase/issue/FE-4482/disable-network-restrictions-for-v3-aws-k8s-projects).

## Test plan
- [x] `tsc --noEmit` passes for the changed file
- [x] `eslint` passes for the changed file
- [x] `prettier --check` passes for the changed file
- [x] Mirrors the existing, already-shipped High Availability
disabled-section pattern in the same component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

## Summary by CodeRabbit

* **Behavior Changes**
* Network restrictions are unavailable for AWS Kubernetes and High
Availability projects, and for users without update permission. AWS
Kubernetes projects display a dedicated explanation; restriction details
and management controls are hidden.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-29 08:16:10 -04:00
Charis 8b8569bd1a feat(studio): status page client data layer + support form (#50984)
## Summary

* Adds the client query layer for `/api/status-page`
(`statusPageQueryOptions` in `data/platform/status-page-query.ts`) and a
shared normalization module (`lib/status-page/status-page.utils.ts`)
that maps the endpoint response into region- and project-creation-aware
`StatusItem`s — later PRs in this stack (assistant tool,
project-creation admonition, global banner) build on this same module.
* Wires the support form's status pill and incident admonition to the
new data behind the `incidentIoStatusPage` ConfigCat flag.
`useSupportStatus` branches between the new endpoint and the existing
`useIncidentStatusQuery`/`processIncidentData` path, both mapped to the
same `SupportStatus` shape.
* `IncidentAdmonition` becomes purely presentational; the status link
now points at the `pageUrl` returned by the endpoint instead of a
hardcoded URL.

Part of
[FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured)
— see Linear for full design context.

## Test plan

- [X] `pnpm --filter studio run typecheck`
- [X] `pnpm --filter studio run lint:ratchet`
- [X] `pnpm knip --workspace apps/studio`
- [X] `pnpm test:prettier`
- [X] `pnpm --filter studio exec vitest run` (touched files) — 70 tests
passing, including a flag-on case for `SupportFormPage`

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

## Summary by CodeRabbit

* **New Features**
* Support pages now display current incident and maintenance
information, including relevant status descriptions and links to the
status page.
* Status details can reflect items affecting a user’s region or
project-creation services. Upcoming maintenance is excluded from active
alerts.
* **Bug Fixes**
* Status labels and alerts now account for loading, errors, incidents,
and maintenance consistently across support pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-29 07:53:28 -04:00
CharisandClaude Code b2cf3693dd feat(studio): /api/status-page endpoint backed by incident.io Widget API (#50931)
## Summary

* Adds `/api/status-page` (Next route + TanStack wrapper), backed by the
[incident.io](<http://incident.io>) Widget API, annotating each item
with `visible`, `show_banner`, and (for scheduled maintenances)
`banner_lead_days`.
* Deployment-mode visibility is driven by a new
`status_page:visibility_field_ids` custom-content key.
* Widget array parsing is fault-tolerant: a malformed item in one array
is dropped and logged rather than failing the whole response, so one bad
item can't hide a real ongoing incident.
* 429s from [incident.io](<http://incident.io>) are retried with
equal-jitter exponential backoff, respecting `Retry-After`, up to 2
retries.
* Nothing consumes this endpoint yet — it replaces no existing behavior
and changes nothing user-visible. Later PRs (this is PR 1 of a stack)
wire up consumers behind the `incidentIoStatusPage` ConfigCat flag.

Part of
[FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured)
— see Linear for full design context.

## Test plan

- [X] `pnpm --filter studio run typecheck`
- [X] `pnpm --filter studio run lint:ratchet`
- [X] `pnpm knip --workspace apps/studio`
- [X] `pnpm test:prettier`
- [X] `pnpm --filter studio exec vitest run status-page` — 44 tests
passing, including a regression test built from a real production
[incident.io](<http://incident.io>) payload that initially failed to
parse, and a compile-time type-safety regression test for the
array-parsing helper

Co-authored-by: Claude Code
[charis@supabase.io](<mailto:charis@supabase.io>)

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

## Summary by CodeRabbit

* **New Features**
* Added a status page that displays ongoing incidents and maintenance,
with visibility and banner settings based on linked incident details.
* Status page data is available through a new API endpoint, with caching
for successful responses and degraded results.
* **Bug Fixes**
* Status page data can still display when some linked incident details
are unavailable; affected results are marked as degraded.
* Improved handling of invalid widget entries so they don’t prevent
valid items from being processed.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Code <charis@supabase.io>
2026-09-28 11:36:29 -04:00
Charis 5b50701c11 fix(studio): surface link to Explorer start-page preference (#50670)
## Summary

* Added a persistent "Preferences" link (with Settings icon) pinned to
the bottom of the Explorer sidebar's root navigation
* Links to `/account/me#dashboard`, the existing Account settings anchor
that contains the Explorer startup preference dropdown
* Restructured sidebar layout to keep the footer link pinned while
content scrolls, addressing
[FE-4437](https://linear.app/supabase/issue/FE-4437/give-higher-visibility-to-changing-explorer-start-page-preference)
* The Home tab disappears when "SQL query" is selected as the start
page, so this visible link ensures users can always find and change
their preference

## Test plan

- [X] Typecheck passes on
`apps/studio/components/layouts/ExplorerLayout/ExplorerNavHome.tsx`
- [X] ESLint passes on the changed file
- [X] Manual verification of sidebar behavior and link functionality

Fixes
[FE-4437](https://linear.app/supabase/issue/FE-4437/give-higher-visibility-to-changing-explorer-start-page-preference):
"Give higher visibility to changing Explorer start page preference"

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

## Summary by CodeRabbit

- **New Features**
- Added a fixed **Preferences** link to the Explorer navigation footer.
- **UI Improvements**
- Updated Explorer navigation layout so resource links and recently
updated items scroll independently from the footer.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-21 16:01:00 -04:00
Charis ea203f70df fix(studio): use configured gp3 max-IOPS ceiling (#50269)
## Summary

* GP3 IOPS calculation was hardcoded to 16,000 instead of reading from
DISK_LIMITS config
* AWS updated the real ceiling to 80,000, making the hardcoded constant
stale
* Users with large multi-TB GP3 disks were incorrectly blocked from
provisioning above 16,000 IOPS

## Test plan

- [X] Updated unit tests for `calculateMaxIopsAllowedForDiskSizeWithGp3`
now assert correct behavior: scaling linearly (100 GB → 50,000 IOPS) and
capping at new 80,000 ceiling (1000 GB → 80,000 IOPS)
- [X] All 26 tests in DiskManagement.test.ts pass locally
- [X] Manually verify Infrastructure Settings > Disk IOPS field no
longer blocks GP3 disks above 16,000 IOPS up to 80,000

Closes
[FE-4379](https://linear.app/supabase/issue/FE-4379/update-iops-limits-for-new-aws-disk-capacity)

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

## Summary by CodeRabbit

- **Bug Fixes**
- Updated GP3 disk performance calculations to support up to 80,000
IOPS.
- Disk sizes below the minimum threshold continue to receive the correct
3,000 IOPS floor.
- Larger disks now scale linearly until reaching the updated maximum
IOPS limit.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-11 10:57:29 -04:00
CharisandJoshen Lim 82d7d347c4 fix(studio): eliminate per-row query duplication on org team page (#50238)
## Summary

* Fixes extreme slowness (browser-crashing on filter) on
`/org/[slug]/team` for orgs with 200+ members.
* Root cause: `MemberRow`/`MemberActions` each independently subscribed
to org-wide React Query data (roles, projects, permissions, feature
flags) and rendered a hidden `UpdateRolesPanel` per row. Filtering
caused hundreds of duplicate query observers to mount/unmount on every
keystroke, each scheduling its own stale-timeout bookkeeping and
blocking the main thread for multiple seconds.
* Hoisted all org-wide data fetching (`useOrganizationRolesV2Query`,
`useOrgProjectsInfiniteQuery`, `usePermissionsQuery`,
`useSelectedOrganizationQuery`, `useIsFeatureEnabled`) to `MembersView`
and passed the results down as props.
* Replaced the per-row `useAsyncCheckPermissions` hook calls in
`MemberActions` with the underlying pure `doPermissionsCheck` function
memoized locally, removing their internal query subscriptions.
* Simplified `useGetRolesManagementPermissions` to stop calling a
query-fetching fallback hook that was unreachable given all current call
sites already pass `permissions`/`orgSlug` directly.
* Replaced 200 hidden per-row `UpdateRolesPanel` instances with a single
shared instance owned by `MembersView`, opened via an `onManageAccess`
callback.
* Cached the regex built by `doPermissionsCheck`'s `toRegexpString`
instead of rebuilding it on every permission check.

Diagnosed from two Chrome performance traces of the team page while
typing in the filter box (multi-second main-thread blocking tasks traced
to React Query `QueryObserver` mount/unmount storms).

## Test plan

- [X] `tsc --noEmit` clean (only one pre-existing, unrelated error in
`packages/ui-patterns`)
- [X] `eslint` clean on all changed files (only pre-existing warnings)
- [X] `vitest run tests/components/Organization/TeamSettings` — 51 tests
pass
- [X] Manually verify filtering is smooth on an org with 200+ members

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

* **New Features**
* Team Settings provides centralized member access and role management.
  * Members can update roles through the access-management panel.

* **Improvements**
* Permission checks now more accurately handle organization and project
scopes, including wildcard patterns.
  * Member search is debounced for smoother filtering while typing.
* Access-management actions use current organization members, roles,
permissions, and feature settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-11 14:02:28 +08:00
Charis 0bebe50a76 fix(studio): serialize SQL folder deletion IDs (#50223)
## Summary

* serialize SQL folder IDs as the comma-delimited API query value
* add regression coverage for the folder deletion request

## Testing

* `pnpm --filter studio exec vitest run
data/content/sql-folders-delete-mutation.test.ts`
* `pnpm exec prettier --check
apps/studio/data/content/sql-folders-delete-mutation.ts
apps/studio/data/content/sql-folders-delete-mutation.test.ts`
* `pnpm --filter studio exec eslint
data/content/sql-folders-delete-mutation.ts
data/content/sql-folders-delete-mutation.test.ts`
* `pnpm --filter studio exec tsc --noEmit --pretty false`

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

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed SQL snippet folder deletion requests so multiple selected
folders are processed correctly.
* Improved request handling by formatting folder identifiers
consistently when submitting bulk deletions.

* **Tests**
* Added coverage to verify that deleting multiple SQL snippet folders
sends the expected folder identifiers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-10 09:16:42 -04:00
CharisandJoshen Lim 2ac052752a fix(pg-meta): fix invalid SQL when setting column default to NULL (#50097)
## Summary

- Fixes invalid SQL generation when setting a column's default value to
NULL through Studio's Table Editor
- The bug occurred because raw `null` values bypassed literal
serialization in the SQL builder
- Added special case handling in both `create()` and `update()`
functions to use `literal(null)` regardless of format

## Details

Setting a column's default to NULL via the "Set as NULL" suggestion in
Studio generated invalid SQL: `ALTER TABLE ... ALTER COLUMN ... SET
DEFAULT ;` (missing the NULL keyword). This was caused by
`default_value` being JS `null` and bypassing the literal()
serialization function when `default_value_format === 'expression'`.

The fix ensures that when `default_value === null`, we always use
`literal(null)` to produce the correct `SET DEFAULT NULL` clause.

## Test plan

- New regression test in `packages/pg-meta/test/columns.test.ts`
("update column default value to NULL via expression format") verifies
the fix by:
  - Creating a text column with a literal default
- Updating it using `default_value_format: 'expression', default_value:
null` (exact payload from Studio)
  - Asserting the column's default value is null
- All 58 tests in `packages/pg-meta/test/columns.test.ts` pass
- TypeScript check (`tsc --noEmit`) passes

Fixes FE-4343

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

- **Bug Fixes**
- Fixed column creation and updates so explicitly setting a default
value to `NULL` correctly preserves the `DEFAULT NULL` clause.
- Ensured setting a column default to `NULL` through expression-based
editing is reflected accurately when column details are retrieved.

- **Style**
- Improved the column editor’s suggestion menu layout and spacing for
clearer readability.

- **Tests**
  - Added coverage for updating a column default to `NULL`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-08 16:08:24 +08:00
Charis 1308a1d0d0 fix(studio): close popover menu when dragging block (FE-4301) (#49912)
## Summary

* Closes the block options popover (grip dropdown menu) when a drag
operation starts on that block
* Fixes the issue where the menu would remain visible during the drag if
it was already open

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

* **Bug Fixes**
* Section action menus now remain closed when dragging begins,
preventing delayed reopening and keeping the editing interface clear and
focused.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 09:39:42 -04:00
CharisandJoshen Lim 2ee0108928 studio: increase gp3 max size to new aws limit (#45611)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Increased GP3 disk limits to support up to 64 TB of storage, 80,000
IOPS, and 2,000 MB/s throughput.

* **Documentation**
* Updated the disk comparison table to reflect the expanded GP3 limits.

* **Bug Fixes**
* Improved disk-size validation and recommendation messaging for clearer
wording.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-09-04 09:39:27 -04:00
Charis a4d925c230 fix(studio): write debug prompt into current chat when assistant is already open (#49911)
## Summary

* Query blocks embedded inside an active assistant conversation
(`AssistantQueryCell`) reused the same "Debug with Assistant" handler as
standalone query blocks (Explorer Query tab, notebook cells), which
always opens a brand-new chat and navigates away.
* Clicking Debug on a block that's already part of the open conversation
silently abandoned it for an unrelated new chat, which read as the
button doing nothing.
* Added an optional `onDebug` override threaded through `QueryEditor` →
`QueryResultRenderer` → `QueryResultError`; `AssistantQueryCell` now
uses it to write the debug prompt into the currently active chat's
composer (`ai-assistant-state`'s new `setInitialInput`) instead of
creating a new chat. Standalone query blocks keep the existing "open a
new chat" behavior since no `onDebug` override is passed there.
* `ExplorerChatTab` now wires `composerContext` into `AssistantChat` (it
wasn't before), so the pre-filled prompt actually reaches the visible
textarea on the Explorer chat route.

Fixes
[FE-4319](https://linear.app/supabase/issue/FE-4319/debug-with-ai-assistant-does-seemingly-nothing-if-query-is-already).

## Test plan

- [X] `pnpm vitest run` on `QueryResultError.test.tsx` /
`QueryResultError.selfhosted.test.tsx` / `ExplorerChatTab.test.tsx` /
`AssistantQueryCell.utils.test.ts` — all pass, including new test
asserting `onDebug` is called instead of `createChat`.
- [X] `pnpm exec eslint` on touched files — clean (only pre-existing
unrelated warnings).
- [X] Manual check: run a query inside an assistant chat that errors,
click "Debug with Assistant" on that block, confirm the debug prompt
appears in the current chat's composer rather than opening a new chat.

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

## Summary by CodeRabbit

* **New Features**
* Added a “Debug with Assistant” workflow that sends SQL error details
to the AI Assistant as its initial input.
* Preserved the existing behavior of opening a new debug chat when the
Assistant panel is unavailable.

* **Tests**
* Added coverage confirming that debugging invokes the Assistant
callback without creating an additional chat.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 09:26:18 -04:00
Charis 357d6cd3eb add github discussion link to explorer preview (#49918)
Adds the GitHub discussion link for Explorer feature preview.

Resolves FE-4256

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

## Summary by CodeRabbit

* **Bug Fixes**
* Updated the Explorer & Notebooks feature preview with a working link
to its GitHub discussion.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-03 07:51:09 -04:00
Charis e7d91dbd06 fix(studio): display diff for view-only notebook cell edits (#49901)
## Summary

Fixed a bug in the AI Assistant notebook-update proposal preview where a
`replace_cell` operation that only changed a cell's view (table ↔ chart)
or chart parameters (type, x/y columns, cumulative, scale, labels) would
show as a "Replaced" row but the expanded diff would appear empty.

**Root cause:** The diff editor only compared the cell's SQL text; view
and chart configuration were never considered, so changes to those
aspects showed no diff.

**Solution:**

* Refactored `getCellMetadata` to return structured `NotebookCellFields`
with separate `source` (database/time range) and `view` (table/chart)
fields instead of a single concatenated string
* Added `formatChartConfig` and `formatCellView` helpers to describe
chart cells
* Updated `getEntryMetadata` to diff source and view independently,
showing only the fields that actually changed (e.g., "Table → Chart
(bar, ...)" when only the view changed, with the unchanged database
omitted)
* If neither field changed, metadata is hidden entirely

## Test plan

* Added test cases for: chart-view cells reporting a `view` field,
view-only changes surfacing without the unchanged database,
chart-parameter-only changes surfacing without the unchanged database,
database-only changes surfacing without the unchanged view, and
fully-unchanged replacements hiding metadata entirely
* All 43 tests in the touched test file pass
* `tsc --noEmit` on apps/studio shows no new type errors

## Summary by CodeRabbit

* **Enhancements**
* Improved AI Assistant notebook previews with clearer cell details,
including source content and table or chart views.
* Chart previews now show key configuration details, such as chart type
and selected dimensions
* Replacement previews highlight only the fields that changed and hide
entries with no visible changes.

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

## Summary by CodeRabbit

* **New Features**
* Notebook previews now distinguish cell content from its view,
including table and chart details.
* Chart previews display relevant configuration, such as chart type and
axes.
  * Log previews include their formatted time range.
  * Replacement previews now show only the fields that changed.

* **Bug Fixes**
* Unchanged replacements are now hidden instead of displaying misleading
content.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-02 09:30:42 -04:00
Charis 9aaa753306 feat(studio): add telemetry for explorer/sql editor temporary switch buttons (#49898)
## Summary

Add PostHog event tracking for the two new buttons introduced in PR
supabase/supabase#49698 that allow users to temporarily switch between
the Explorer and SQL Editor:

* **Explorer button**: "Back to SQL Editor" button in the Explorer
sidebar title bar now fires `explorer_temp_access_sql_editor_clicked`
event
* **SQL Editor button**: "Back to Explorer" button in the SQL Editor
title bar (shown during temporary visits) now fires
`sql_editor_back_explorer_clicked` event

Both event interfaces follow the repo's telemetry-standards conventions,
carrying only `groups: TelemetryGroups` property with no additional
custom properties.

## Test plan

- [X] Verify `explorer_temp_access_sql_editor_clicked` event fires in
PostHog when clicking "Back to SQL Editor" button in Explorer
- [X] Verify `sql_editor_back_explorer_clicked` event fires in PostHog
when clicking "Back to Explorer" button in SQL Editor
- [X] Run typecheck: `pnpm typecheck` passes without errors
- [X] Run lint: `pnpm lint --filter=studio` passes

## Issue

Resolves
[FE-4213](https://linear.app/supabase/issue/FE-4213/explorer-set-up-telemetry-for-metrics-where-appropriate)

## Summary by CodeRabbit

* **Analytics**
  * Added tracking for navigation from the Explorer to the SQL Editor.
  * Added tracking for returning from the SQL Editor to the Explorer.
2026-09-02 09:07:06 -04:00
Charis 17d15ee1e2 fix(studio): confirm destructive notebook queries (#49658)
## What

- add a second notebook-level confirmation for destructive SQL before
forced batch execution
- reuse the shared SQL safety detector for stored and live cell SQL
- cover destructive confirmation, cancellation, non-destructive
mutations, and live SQL

## Testing

- pnpm --filter studio exec vitest run
components/interfaces/Explorer/ExplorerNotebookTab.utils.test.ts
components/interfaces/Explorer/__tests__/ExplorerNotebookTab.test.tsx
--coverage.enabled=false
- pnpm --filter studio exec eslint
components/interfaces/Explorer/ExplorerNotebookTab.tsx
components/interfaces/Explorer/ExplorerNotebookTab.utils.ts
apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.utils.test.ts
apps/studio/components/interfaces/Explorer/__tests__/ExplorerNotebookTab.test.tsx

Closes FE-4284

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

* **Improvements**
* Simplified notebook execution into a single confirmation step for
mutating queries.
* Destructive queries, including operations such as `DROP` or
`TRUNCATE`, are clearly marked with a **Destructive** badge.
* The confirmation dialog lists affected queries and lets you proceed or
cancel.
* Detection uses the latest SQL from the editor and ignores destructive
keywords in comments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-01 09:53:47 -04:00
Charis 8db7368ccf feat(studio): clarify quick query as temporary space (#49695)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature

## What is the current behavior?

Standalone queries in the Explorer "Quick Query" surface have editable
titles (defaulting to "Untitled query") without clear indication that
they are temporary and not saved, similar to the old SQL Editor snippet
model.

## What is the new behavior?

The Quick Query tab now clearly indicates that standalone queries are
temporary. The title is no longer editable and displays "Temporary space
for one-off queries" as static muted italic text. The default query name
has been changed from "Untitled query" to "Quick query" for clarity.

## Additional context

Resolves FE-4297. Notebook cells (variant === 'embedded') remain
unchanged with editable titles.

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

* **UI Improvements**
* Renamed new one-off queries from **“Quick query”** to **“Run SQL”**
when no title is provided.
* Updated viewport query editor toolbars to display the fixed **“Run
SQL”** label.
  * Preserved editable titles for embedded query editors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-01 09:13:33 -04:00
Charis 4b1f93bb99 feat(explorer): add path back to SQL Editor for snippet access (#49698)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature

## What is the current behavior?

Users who have opted into the Explorer feature preview have no way back
to the SQL Editor from within Explorer, so they can't easily check their
old snippets.

## What is the new behavior?

- The Explorer sidebar title bar now has a button (using the same icon
as the SQL Editor/Explorer nav entry) that links to the SQL Editor, with
a tooltip explaining it's a temporary switch to access snippets.
- Clicking it marks the visit as temporary in localStorage, which
surfaces a matching "Back to Explorer" button in the SQL Editor title
bar. Clicking that button clears the temporary flag and returns to
Explorer.
- Fixed the product menu title bar badge slot to sit flush right instead
of directly next to the title text.

## Additional context

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

* **New Features**
  * Added a quick switch from the Explorer to the project’s SQL Editor.
* Added a “Back to Explorer” option in the SQL Editor when opened from
Explorer.
  * Added tooltips to clarify these navigation actions.
* Navigation state is preserved per project for a smoother return
experience.

* **UI Improvements**
* Improved product menu spacing and title truncation for better layout
handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-01 09:07:54 -04:00
Charis b278b1ec8a fix(studio): Debug with Assistant and Copy prompt work (#49690)
## Summary

* Resolved hanging buttons in Explorer's QueryResultError panel that
were wired to stub no-ops (`buildPrompt={() => ''}`,
`onOpenAssistant={() => {}}`).
* "Debug with Assistant" now opens a new chat seeded with a real prompt
combining the SQL query and error context using existing
`buildDebugPromptText` util and `useCreateChat` hook.
* "Copy prompt" now copies the same real debug prompt text to clipboard.
* Threaded `sql` and query `source` props down through `QueryEditor` →
`QueryResultRenderer` → `QueryResultError` while keeping them optional
for backward compatibility with other callers like
`AssistantNotebookPreviewCell`.

## Test plan

- [X] Run `pnpm typecheck` — passes
- [X] Run `pnpm lint --filter=studio` — passes
- [X] Run `pnpm test:studio --run
apps/studio/components/interfaces/Explorer/QueryEditor` — Explorer
vitest suite (99 tests across 13 files) passes with no regressions
- [X] Manually verify in Explorer: trigger an ad-hoc SQL query that
fails, confirm "Debug with Assistant" opens a new chat with the error
prompt seeded, and "Copy prompt" copies the prompt to clipboard

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

- **New Features**
- Added “Debug with Assistant” to query errors using the submitted SQL
and error details.
- Added an option to copy the debugging prompt for easier
troubleshooting.
- Assistant actions are hidden when query details are unavailable or
restricted.

- **Bug Fixes**
- Ensured query errors reference the SQL that produced them, rather than
later editor changes.

- **Tests**
- Added coverage for assistant debugging, prompt copying, conditional
visibility, and self-hosted behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-01 08:27:38 -04:00
Charis 86c813ec03 fix(notebooks): reset insert offset when anchor cell moves (#49694)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

When a cell gets moved via `move_cell` operation in
`deriveNotebookDiff`, the `insertedAfter` offset map is not cleared for
that anchor cell. This causes later `insert_cell` operations anchored on
the same (now-moved) cell to apply the stale offset on top of the
correct current-position lookup, resulting in the new cell landing after
the wrong position.

## What is the new behavior?

The offset for an anchor cell is now cleared from `insertedAfter` when
it gets moved, since cells previously inserted after it stay behind at
its old location and should not affect subsequent inserts at its new
position.

A regression test has been added that reproduces the exact ticket
scenario (insert after cell-1, move cell-1 after cell-3, insert after
cell-1 again) and verifies the correct final cell order.

## Additional context

Fixes:
https://linear.app/supabase/issue/FE-4308/insert-anchored-to-a-previously-moved-cell-lands-after-the-wrong-cell

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

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed notebook cell insertions after moving an anchor cell, ensuring
new inserts appear relative to the anchor’s updated position.
* Preserved the placement of inserts made before the anchor cell was
moved.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-28 12:12:04 -04:00
Charis b366496c8c [FE-4246] fix(studio): resolve notebook database labels (#49554)
## I have read the
[CONTRIBUTING.md](<https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md>)
file.

YES

## What kind of change does this PR introduce?

Bug fix for Studio notebook previews.

## What is the current behavior?

Notebook diffs display the raw database_identifier value. This exposes
opaque database IDs and does not communicate whether the cell targets
the primary database or a read replica.

Related issue:
[https://linear.app/supabase/issue/FE-4246](<https://linear.app/supabase/issue/FE-4246>)

## What is the new behavior?

Notebook database metadata is resolved independently from existing
selector formatting:

* an omitted database identifier is Primary
* an identifier matching the project ref is Primary
* other identifiers show a loading state while databases load
* a loaded non-primary match is Replica
* an unmatched identifier is Unknown
* database lookup failures use a neutral unavailable state

Labels are compact: Database: Primary, Database: Replica, and Database:
Unknown.

The databases query is enabled only when a preview contains an explicit
non-primary identifier.

## Additional context

Validation:

* 39 focused notebook preview tests
* Studio typecheck
* targeted ESLint
* Prettier check
* git diff --check

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

## Summary by CodeRabbit

* **New Features**
* Notebook previews now identify database targets as primary, read
replica, or unknown.
  * Database metadata is resolved automatically when needed.
  * Loading states display a clear “Loading database…” indicator.
* Database metadata now handles unavailable, hidden, and error states
more clearly.
* Notebook entries reflect updated database information before and after
replacement.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-26 17:58:35 +08:00
Charis 4dee589735 fix(studio): stop assistant fabricating database_identifier for notebooks (#49558)
## Summary

* Fixes
[FE-4275](https://linear.app/supabase/issue/FE-4275/assistant-always-creates-notebooks-with-wrong-identifier-first-try):
the assistant always created database notebook cells with a fabricated
`database_identifier` (`"primary"`, later observed as `""` /
`"_primary"` under different prompt wording) instead of omitting the key
for the project's primary database, which tripped the tool's
reject-and-retry validation on the very first attempt.
* Prompt wording alone wasn't reliable — live eval runs against the real
model kept substituting a new placeholder every time the prompt was
tightened further.
* Normalizes an empty-string `database_identifier` to absent at the
schema level (`databaseIdentifierSchema` in `notebook-schema.ts`), which
is inherited by every schema built from it — the AI SDK's `inputSchema`
for `create_notebook`/`update_notebook`, and the write-boundary
`writableNotebookSchema` used right before the PUT to the backend.
* Adds an eval case (`evals/dataset.ts`) reproducing the original bug,
plus unit tests covering schema-level and write-boundary normalization.

## Test plan

- [X] `pnpm --filter studio exec tsc --noEmit` passes
- [X] `pnpm exec prettier --check` passes on touched files
- [X] Unit tests pass: `notebook-schema.test.ts`,
`notebook-upsert-mutation.test.ts`, `notebook-tools.test.ts` (104 tests)
- [X] Ran the new eval case against the real model 3x before the code
fix (0% correctness, fabricated `""`/`"_primary"`) and 3x after (100%
correctness)

## Summary by CodeRabbit

* **Bug Fixes**
* Improved notebook handling of empty database identifiers by treating
them as absent.
  * Ensured notebook requests omit unused database identifier fields.
  * Added validation guidance for read-replica database identifiers.
2026-08-25 15:08:28 -04:00
Charis 8f124cd2e5 chore: gitignore personal skills in agents (#49544)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated ignore rules to exclude local agent skill files from version
control.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 12:05:02 -04:00
Charis 632e099487 feat(studio): track assistant notebook divergence (#49539)
## Summary
- record server divergence when an assistant changes a notebook with
local unsaved edits
- clear the session-only marker after a successful save or notebook
removal
- cover update, delete, saved eviction, and lifecycle behavior

## Verification
- pnpm --dir apps/studio exec vitest run
state/notebooks/notebooks-state.test.ts
lib/ai/notebook-cache-invalidation.test.ts
- pnpm --dir apps/studio exec eslint state/notebooks/notebooks-state.ts
state/notebooks/notebooks-state.test.ts
lib/ai/notebook-cache-invalidation.ts
lib/ai/notebook-cache-invalidation.test.ts
- pnpm --dir apps/studio typecheck

Stacked on the approval-warning PR for FE-4255.

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved handling of server changes to notebooks with unsaved local
edits.
* Server updates and deletions are now tracked as divergences instead of
being silently skipped.
* Divergence indicators are cleared when changes are saved or notebooks
are removed.
  * Unrelated notebook changes no longer create false conflicts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 11:42:46 -04:00
Charis b686a72d89 feat(studio): warn on dirty notebook assistant proposals (#49536)
## Summary

* Warn before approving assistant update/delete proposals when the
notebook has unsaved local changes.
* Keep approval available; this is an informed-choice warning.
* Add coverage for dirty, saved, and absent local notebook state.

Towards
[FE-4255](https://linear.app/supabase/issue/FE-4255/assistant-update-notebook-can-silently-overwrite-unsaved-local)

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

## Summary by CodeRabbit

* **New Features**
* Added warnings when approving notebook update or deletion proposals
that could discard unsaved local changes.
  * Warning messages now distinguish between update and delete actions.
* Update proposal approval remains available after the warning is
displayed.

* **Bug Fixes**
* Prevented unnecessary warnings when notebooks have no saved local
changes or are unavailable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 10:45:47 -04:00
CharisandJoshen Lim 40d9a2e929 fix(studio): invalidate notebook cache after assistant delete (#49496)
Stacked on supabase/supabase#49415.

## Summary

- collect completed assistant delete_notebook tool calls as deleted
cache effects
- evict deleted notebooks from the React Query cache and notebook state
- cover collector, cache eviction, and the open-tab Notebook not found
state

## Verification

- pnpm test:studio -- notebook-cache-invalidation
ExplorerNotebookTab.assistant-cache-invalidation
- pnpm --filter studio typecheck
- touched-file ESLint and Prettier checks

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved notebook deletion handling so removed notebooks no longer
remain visible after deletion.
* Navigation and cached notebook data now update promptly when a
notebook is deleted.
* Attempting to access a deleted notebook now displays a clear “Notebook
not found” message.

* **Tests**
* Added coverage for notebook deletion, cache invalidation, navigation
updates, and the resulting not-found state.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-25 14:41:56 +08:00
Charis de3a8799d6 fix(studio): invalidate notebook caches after assistant create/update (#49415)
## Summary
- The assistant's `create_notebook`/`update_notebook` tools run entirely
server-side, so an open notebook tab's React Query cache and Valtio
store never learn a write happened — the tab keeps showing stale content
until a manual reload.
- Adds `collectNotebookCacheEffects`/`applyNotebookCacheEffects`
(`apps/studio/lib/ai/notebook-cache-invalidation.ts`), which scan
finished assistant messages for completed
`create_notebook`/`update_notebook` tool calls and evict the affected
notebook via `evictNotebookFromCaches`
(`apps/studio/data/content/notebooks/notebook-cache.ts`), plus
invalidate the nav list.
- Wired into `createChatInstance`'s `onFinish` in
`state/ai-assistant-state.tsx`, with per-chat dedupe so replayed history
isn't reprocessed.
- Removes the cache entry outright rather than invalidating it, since a
remounting `useNotebookQuery` would otherwise read the stale cached
value synchronously before its refetch lands.
- Explicitly skips eviction when the open tab has unsaved local edits,
so an assistant write can't silently discard them.

Related: [FE-4235](https://linear.app/supabase/issue/FE-4235)

**Out of scope:** this only protects the client-side cache/store from
being clobbered after the fact. Preventing the assistant's
`update_notebook` tool call itself from overwriting a user's unsaved
edits (a data-layer conflict, not a cache-freshness one) is tracked
separately in [FE-4255](https://linear.app/supabase/issue/FE-4255).

## Test plan
- [x] `pnpm test:studio -- notebook-cache notebook-cache-invalidation
ai-assistant-state.notebook-cache-invalidation
ExplorerNotebookTab.assistant-cache-invalidation
ExplorerNotebookTabCoordinator` — all passing
- [x] Reproduction-first component test
(`ExplorerNotebookTab.assistant-cache-invalidation.test.tsx`) — verified
it fails without the fix (stale content persists) and passes with it
- [x] Regression test for the dirty-notebook guard (an edited, unsaved
notebook is left untouched by an assistant write)
- [x] `pnpm typecheck --filter=studio` / `pnpm lint --filter=studio`
clean


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

* **Bug Fixes**
* Notebook changes made through the AI assistant now appear correctly in
open notebook tabs and after reopening them.
* Saved notebook caches are refreshed after completed create or update
actions, preventing stale content from being displayed.
  * Unsaved notebook changes are preserved during cache cleanup.
  * Closing a notebook tab now consistently removes its cached content.

* **Tests**
* Added coverage for assistant-driven updates, remounts, duplicate
actions, project context changes, and cache behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 14:15:27 +08:00
CharisandJoshen Lim 89b4f1aca4 feat(studio): add delete_notebook tool to AI assistant (#49413)
## Summary

* Adds a `delete_notebook` AI assistant tool (`needsApproval: true`)
that lets the assistant delete a notebook with explicit user approval,
mirroring the existing `create_notebook`/`update_notebook` tools.
* Wires up a destructive-styled approval card in the AI Assistant Panel
(fetches the notebook to show its name, warns the deletion is permanent)
using the same `Confirm`/tool-approval plumbing as the other notebook
tools.
* Updates `tool-filter.ts` opt-in gating, the assistant system prompt,
the eval-harness mock tools, and the eval dataset with `delete_notebook`
coverage.
* Adds test coverage in `notebook-tools.test.ts`, `mock-tools.test.ts`,
and `NotebookProposalRenderer.test.tsx`.

Closes
[FE-4242](https://linear.app/supabase/issue/FE-4242/assistant-delete-notebook-tool).

## Test plan

- [X] `pnpm typecheck --filter=studio` passes
- [X] `pnpm --filter studio exec vitest run` for the touched files
(notebook-tools, mock-tools, NotebookProposalRenderer,
[Message.Parts](<http://Message.Parts>), and existing consumers of
`content-delete-mutation`) — all passing
- [X] `eslint` and `prettier --check` clean on all touched files
- [X] Manual verification of the approval UI in a running Studio
instance (not done in this session)

## Summary by CodeRabbit

* **New Features**
* Added AI-assisted notebook deletion with explicit confirmation and
irreversible-action warnings.
* Added safeguards to distinguish deleting an entire notebook from
removing individual panels.
* Completed deletions now display the deleted notebook’s name without an
option to reopen it.
* **Bug Fixes**
* Improved handling of missing notebooks and invalid deletion requests.
* **Tests**
* Added coverage for deletion approval, denial, errors, and successful
completion.

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

* **New Features**
* Added AI-assisted notebook deletion with explicit approval and
irreversible-action warnings.
* Added confirmation, loading, error, and completion states for notebook
deletion.
* Prevented accidental full-notebook deletion when only a panel or
section should be removed.
* Improved notebook update results by showing applied changes when
available.

* **Bug Fixes**
  * Notebook deletion now uses the required API version.
* Improved handling and validation of missing notebooks during deletion.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-24 13:18:12 -04:00
CharisandJoshen Lim 48cc37f0d2 refactor(studio): extract notebook cache eviction helper (#49414)
## Summary

Part 1 of the FE-4247 stack
([FE-4247](https://linear.app/supabase/issue/FE-4247/assistant-invalidate-cache-after-notebook-editdeletion)).
Pure refactor, no behavior change — extracts the notebook cache eviction
logic that `ExplorerNotebookTabCoordinator` had open-coded into a shared
helper, so the upcoming assistant create/update/delete cache
invalidation (PR 2/3 in the stack) can reuse it instead of duplicating
the two-cache-layer eviction dance.

- New `evictNotebookFromCaches({ queryClient, projectRef, id, mode })`
in `apps/studio/data/content/notebooks/notebook-cache.ts`. `mode:
'refresh' | 'remove'` selects `invalidateQueries` vs `removeQueries` on
`contentKeys.resource`. Drops the notebook from `notebooksState` only
when its status is `'saved'`, matching the original open-coded guard
exactly. Returns whether it evicted, so callers can branch.
- `ExplorerNotebookTabCoordinator` now calls the helper with `mode:
'remove'` instead of inlining the logic.

## Test plan

- [x] `pnpm test:studio -- notebook-cache
ExplorerNotebookTabCoordinator` — new helper tests
(refresh/remove/dirty-guard/unknown-id) and existing coordinator tests
all pass
- [x] `pnpm typecheck --filter=studio`
- [x] `pnpm lint --filter=studio` — 0 errors, no new warnings

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

- **Bug Fixes**
- Improved detection of unsaved notebook changes for tab indicators and
close confirmations.
- Empty, never-saved notebooks are no longer included in discard
prompts.
- Improved cache cleanup when closing saved notebooks while preserving
unsaved work.
  - Added safeguards for missing notebook records.
- **Tests**
- Added coverage for notebook cache refresh, removal, preservation, and
no-op scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-24 15:59:41 +08:00
Charis 233cbdc8e5 Restore notebook diff preview for completed updates (#49402)
## Summary

This is **PR 3 of 3** in the FE-4243 stack fixing "Notebook update
proposal shows 'unapplyable' error for already-completed updates."

- Consumes the `previous_content` field added by PR 2 (#49401) to
reconstruct diffs for already-applied notebook updates
- Restores the diff preview that PR 1 initially dropped — completed
updates now show the full before/after instead of a generic "Notebook
updated" message
- Uses the same diff derivation function called pre-approval,
guaranteeing the rendered diff matches what was shown during
confirmation
- Includes defensive fallback handling for older persisted chats (before
`previous_content` existed) and edge cases

**Depends on**: PR 2 (#49401) merging first — this PR consumes the
`previous_content` field from that server change.

Resolves FE-4243 

## Test plan

- ✅ 19/19 tests pass in NotebookProposalRenderer.test.tsx (2 confirmed
as real regressions)
- ✅ 118/118 tests pass in full AIAssistantPanel suite
- ✅ Typecheck: clean on modified files
- ✅ ESLint: zero errors/warnings on changed files  
- ✅ Lint ratchet: passes (some rules improved)
- ✅ New regression tests cover: delete_cell, insert_cell, missing
previous_content, and operations that no longer reconcile
- ✅ No notebook fetch in completed update tests (proves no redundant
re-fetching)

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

* **New Features**
* Added visual previews showing notebook changes, including inserted and
deleted cells, when prior content is available.
  * Prevented duplicate cells from appearing in update previews.
* Retained a compact completion message when change details are
unavailable or inconsistent.
  * Ensured previews are shown only for the relevant notebook.

* **Tests**
* Added coverage for notebook update previews, deletion and insertion
diffs, duplicate prevention, notebook matching, and fallback behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 15:09:44 -04:00
Charis 8920439569 Expose previous notebook content in update_notebook (#49401)
## Summary
- Plumb pre-update notebook snapshot through `update_notebook` tool
response as `previous_content`
- Add sanitizers in `tool-sanitizer.ts` to strip snapshot before model
sees it
- Add client-side stripping in `prepareMessagesForAPI` to avoid
re-uploading snapshot on subsequent turns
- This is PR 2 of 3 fixing Linear issue FE-4243 (notebook update
proposal shows 'unapplyable' error for already-completed updates)
- Ships no visible behavior change on its own; enables PR 3 to restore
diff preview for completed updates

## Test plan
- [x] Unit tests: 80/80 passing across notebook-tools.test.ts,
tool-sanitizer.test.ts, generate-assistant-response.utils.test.ts,
message-utils.test.ts, and mock-tools.test.ts
- [x] Typecheck: clean for all changed files
- [x] ESLint: zero errors, lint:ratchet passes (exit 0)
- [x] Integration: previous_content is correctly populated with
pre-update notebook, stripped before model context, and stripped on
client-side re-upload

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

- **Bug Fixes**
- Notebook updates now retain previous content for recovery and history.
- AI responses expose only the notebook’s ID and name, keeping previous
content out of model-visible data.

- **Tests**
- Added coverage for notebook update results, content sanitization, and
message preparation, including cases where previous content is absent or
preserved.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 14:58:32 -04:00
Charis c172195269 test(studio): add eval cases for list_databases-driven notebook creation (#49398)
## Summary
- Adds three eval cases exercising the behavior this stack wires up: a
happy path where the model calls `list_databases` before targeting a
named read replica, a guard against fabricating an identifier when the
user names a region/replica `list_databases` doesn't actually return,
and a guard against targeting a non-primary database when the user never
asked for one.

Part 6/6 (final) of the stack for FE-4225 (expose valid database
identifiers to the notebook AI agent). Stacked on #49334.

## Test plan
- [x] Ran all three cases against the real model; inspected transcripts
directly
- [x] Re-verified reworded `correctAnswer` text against real outputs via
the correctness evaluator
- [x] `pnpm --filter studio exec tsc --noEmit` passes
- [x] `prettier --check` passes

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

## Summary by CodeRabbit

* **Tests**
* Added evaluation coverage for selecting the correct database replica
when creating notebooks.
  * Verified primary-database defaults when no database is specified.
* Added checks to prevent fabricated database identifiers when a
requested replica is unavailable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 12:12:55 -04:00
Charis 27ff49c145 Stop re-deriving notebook update diffs after completion (#49399)
## Summary

- Fixes false-negative "This update can't be applied" warning for
completed notebook updates (FE-4243)
- When `state='output-available'` (tool completed), skips notebook fetch
and diff derivation
- Renders compact "Notebook updated: {name}" instead of a phantom/failed
diff
- `UnapplyableNotebookUpdateNotice` now accepts and forwards
`footerAction` prop, preserving "Open notebook" link
- Different warning copy for terminal confirm states
(success/error/denied): "Preview unavailable / notebook has changed"
instead of "can't be applied"
- Preserves diff derivation for non-completed states
(output-denied/error)

This is PR 1 of a 3-PR stack; PRs 2-3 restore the full diff preview for
completed updates (requires server snapshot).

Towards FE-4243

## Test plan

- [x] All 15 tests in NotebookProposalRenderer.test.tsx pass
- [x] Typecheck clean
- [x] ESLint clean
- [x] Regression tests added: completed updates with missing target
cells (auto & manual approval)
- [x] Confirms denied/error states still derive against live content

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

## Summary by CodeRabbit

- **New Features**
  - Added clearer status handling for AI-generated notebook updates.
- Completed updates now show a confirmation with the notebook name when
available.
- Update actions and footer controls now adapt to the proposal’s current
state.

- **Bug Fixes**
- Improved messaging when notebook changes prevent an update preview
from being reconstructed.
- Preserved accurate previews for denied or failed updates using the
notebook’s latest content.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 12:05:52 -04:00
Charis dd534d229b docs(studio): instruct the notebook agent to use list_databases (#49334)
## Summary
- Adds a bullet to `NOTEBOOKS_PROMPT` instructing the assistant to call
`list_databases` before setting a `database_cell`'s
`database_identifier`, mirroring the existing `list_tables`
schema-validation instruction immediately above it.

Part 5/6 of the stack for FE-4225 (expose valid database identifiers to
the notebook AI agent). Stacked on #49333. This is the last piece that
makes the assistant actually *use* the tool and schema field wired up
earlier in the stack, rather than just having them available.

## Test plan
- [x] `pnpm --filter studio exec tsc --noEmit` passes
- [x] `prettier --check` passes

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved notebook database configuration by ensuring database
identifiers are selected from available databases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 11:57:13 -04:00
Charis ee59d28240 fix(studio): reject unknown database_identifier before writing a notebook (#49333)
## Summary
- `create_notebook`/`update_notebook` now validate every
`database_cell`'s `database_identifier` against the project's real
database list (`getReadReplicas`) before writing, throwing an
assistant-actionable `NotebookToolError` (same pattern as the existing
`expected_updated_at` mismatch check) when it doesn't match.
- Only fetches the database list when a cell actually sets
`database_identifier` — no added cost for the common case.
- `update_notebook` validates only the cells its own operations
introduce (`insert_cell`/`replace_cell`), not the whole resulting
notebook — otherwise an unrelated, untouched pre-existing cell whose
replica was removed after the fact would block updates that never touch
it.

Part 4/6 of the stack for FE-4225 (expose valid database identifiers to
the notebook AI agent). Stacked on #49332. This closes the gap that PR 3
reopened: a model can no longer invent an identifier that silently
breaks a cell — it now gets a retryable error naming `list_databases`
(added in #49328) as the way to find a real one.

## Test plan
- [x] `create_notebook`/`update_notebook` reject an unknown
`database_identifier` with `NotebookToolError` + `exposeToAssistant:
true`
- [x] Both succeed when the identifier matches a real database
- [x] `create_notebook` never calls the databases endpoint when no cell
sets `database_identifier`
- [x] `update_notebook` succeeds without validating or fetching
databases when no operation introduces a database_cell, even if an
untouched existing cell carries a now-invalid identifier
- [x] `pnpm --filter studio exec tsc --noEmit` passes

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

* **New Features**
* Added a database-listing tool that provides database identifiers and
metadata, including primary-database status.
  * Database results now return only the relevant fields.

* **Bug Fixes**
* Added validation to prevent notebooks from referencing unknown
databases.
* Create and update actions now provide clear errors for invalid
database identifiers.
* Updates validate newly added or replaced cells while preserving
existing, untouched cells.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 11:05:31 -04:00
Charis 7b04fc7d09 revert(studio): reinstate database_identifier on the agent notebook schema (#49332)
## Summary
- Reverts #49326's temporary mitigation, which stripped
`database_identifier` from the agent-facing notebook cell schema
(`agentCellSchema`) because the assistant had no legitimate source of
truth for valid read-replica identifiers.
- The previous PR in this stack (#49328) added the `list_databases`
tool, so that source of truth now exists — `database_cell`s can carry
`database_identifier` again.

Part 3/6 of the stack for FE-4225 (expose valid database identifiers to
the notebook AI agent). Stacked on #49328.

## Test plan
- [x] Existing schema/preview tests (reverted alongside the mitigation)
pass
- [x] `pnpm --filter studio exec tsc --noEmit` passes

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

* **New Features**
* AI-generated notebook database cells now support database identifiers.
* Notebook previews display the associated database source, including
transitions between primary and replica databases.

* **Bug Fixes**
* Improved database metadata handling to preserve identifiers when
updating notebook cells.
* Database information is now shown only when available, preventing
inaccurate or missing metadata displays.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 10:36:20 -04:00
Charis 144c2eadb6 fix(studio): expose notebook diff validation errors for auto-retry (#49331)
## Summary

- Automatically deny notebook tool proposals with specific error reasons
when client-side diff validation fails (e.g., unknown cell ID)
- Enables the AI Assistant to see the actual failure reason and retry
automatically instead of requiring manual user intervention
- Exposes the same `describeNotebookOperationError` helper used
server-side for consistent error messaging
- Adds `denyWithReason()` to manual tool approval handlers for flexible
denial messaging

## Test plan

- Run `pnpm --filter studio exec vitest run
apps/studio/components/ui/AIAssistantPanel/Confirm.utils.test.ts` to
verify denyWithReason tests
- Run `pnpm --filter studio exec vitest run
apps/studio/components/ui/AIAssistantPanel/NotebookProposalRenderer.test.tsx`
to verify auto-deny behavior, Skip fallback, and no re-fire after
approval is already handled
- Confirm no regressions in existing notebook tool approval flows

## Manual testing

- Get assistant to create a notebook.
- Open the notebook and manually delete a cell yourself.
- Ask the assistant to delete the cell you just deleted.
- Assistant should automatically recover from the error.

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

## Summary by CodeRabbit

* **New Features**
* Notebook proposals now display clear success, error, and denial
outcomes.
* Tool errors for SQL, Edge Functions, and notebooks now appear in their
respective result views.
  * Output links are supported in notebook proposal results.
* Approval panels remain visible after completed actions with
standardized status messages.

* **Bug Fixes**
* Specific denial reasons are preserved instead of showing a generic
skipped message.
* Unapplyable notebook updates are automatically denied with an
explanation.
  * Prevented duplicate denial responses after approval decisions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 10:35:56 -04:00
Charis 0f730de9e0 feat(studio): add list_databases tool for the AI assistant (#49328)
## Summary
- Adds a `list_databases` tool to the notebook AI tools
(`getNotebookTools`), returning `{ identifier, is_primary, region,
status }` for the project's primary and any read replicas, via
`getReadReplicas`.
- Registers `list_databases` in `tool-filter.ts` (opt-in validation
schema + SCHEMA category), and adds a mock fixture in `mock-tools.ts`
for evals.

Part 2/6 of the stack for FE-4225 (expose valid database identifiers to
the notebook AI agent). Stacked on #49327. A later PR in the stack
reinstates `database_identifier` on the agent-facing notebook schema and
requires the agent to call this tool first.

## Test plan
- [x] New test coverage in `notebook-tools.test.ts` for
`list_databases`, including `is_primary` computation
- [x] Existing exact-tool-set assertion updated
- [x] `pnpm --filter studio exec tsc --noEmit` passes

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

## Summary by CodeRabbit

* **New Features**
  * Added an AI notebook tool for listing a project’s databases.
* Results include each database’s identifier, region, status, and
whether it is the primary database.
  * Supports projects with read replicas.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 09:28:56 -04:00
Charis 7a2e237892 Add update_notebook evals; fix prompt gaps they surfaced (#49324)
## Summary
- Add `update_notebook` eval cases (insert/replace/delete/move, a
combined delete+insert, and guard/safety cases) mirroring the existing
`create_notebook` cases, targeting the notebooks already seeded in the
mock tool harness.
- Fix two behavior gaps in `NOTEBOOKS_PROMPT`/`LIMITATIONS_PROMPT` that
these cases surfaced when run live: the assistant asking the user for a
notebook id instead of resolving it via `list_notebooks`, and the
destructive-operations warning rule not being connected to SQL written
into notebook cells.
- Soften the destructive-SQL case's `correctAnswer` to match
`update_notebook`'s real approval-gated behavior — a warning
accompanying the reported change is acceptable, not only one strictly
preceding the tool call.

## Test plan
- [x] `pnpm run typecheck` (apps/studio) — clean
- [x] `pnpm exec prettier --check` on both changed files — clean
- [x] `evals/scorer.test.ts`, `evals/transcript.test.ts`,
`evals/trace-utils.test.ts` — 21/21 pass
- [x] Ran the new eval cases live against OpenAI (bypassing the
Braintrust proxy) via Braintrust MCP; confirmed via trace inspection
that the prompt fix resolved the id-resolution gap (Tool Usage 0% → 100%
across 3 trials) and that the assistant now includes an explicit
irreversibility warning when destructive SQL is written into a notebook
cell

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

- **New Features**
- Improved notebook creation and editing support across SQL, query,
chart, and time-range cells.
- Added clearer handling for saved notebooks, recurring requests, and
one-time SQL execution.
  - Enhanced validation for database cells and notebook configuration.

- **Bug Fixes**
- Improved safeguards and warnings for destructive queries, including
saved notebook queries.
  - Better handling of missing tables and notebooks.
  - More precise notebook cell updates and tool usage validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 08:39:46 -04:00
Charis 717927f4f2 fix(studio): AI assistant notebooks no longer set an invalid database_identifier (#49326)
## Summary

- The AI assistant's `create_notebook`/`update_notebook` tools could set
a `database_cell`'s `database_identifier` to a value that doesn't
correspond to any real database, because no tool exposes a project's
actual read-replica identifiers to the model.
- An unresolvable `database_identifier` silently breaks the cell:
`QueryEditor`'s connection-string lookup fails to find a match, and
running the cell fails with `Unable to run query: Connection string is
missing` — even though the exact same SQL runs fine when pasted into a
manually-created cell (which never sets this field).
- Fix: strip `database_identifier` from the agent-facing schema
(`agentCellSchema` in `notebook-schema.ts`) entirely, so the model can
no longer emit it at all. **This is a temporary fix** until we wire in
real read-replica support for the AI assistant (e.g. a tool exposing a
project's valid replica identifiers) — the field can be reintroduced
once the model has a legitimate source of truth to pull a valid
identifier from.
- Updated tests that relied on agent cells carrying
`database_identifier` to reflect the new behavior, and added a
regression test asserting `agentNotebookSchema` rejects a
`database_cell` with that field set.

Resolves FE-4224

## Test plan

- [x] `notebook-schema.test.ts`, `notebook-operations.test.ts`,
`notebook-tools.test.ts`, `AssistantNotebookPreview.test.tsx`,
`AssistantNotebookPreview.utils.test.ts` all pass
- [x] `tsc --noEmit` clean
- [x] Prettier clean on touched files

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved handling of database notebook cells when database metadata is
unavailable.
* Cells without database identifiers now display “No metadata” instead
of an incorrect replica identifier.
* Prevented invalid database identifiers from being accepted in
agent-generated notebook content.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-20 16:26:12 -04:00
Charis ebd616fa90 fix(studio): auto-retry notebook updates on stale/invalid conflicts (#49323)
## Summary

- **Removed dead client-side refresh UI** in
`NotebookProposalRenderer.tsx` and its test — the diff preview is always
computed from live data, so the check was redundant with the tool's
server-side re-validation
- **Added typed `NotebookToolError`** in `notebook-tools.ts` with
structured metadata (`{ exposeToAssistant: boolean }`) validated by a
zod schema with a literal discriminant tag (`tag:
'notebook_tool_error'`) — tracks the two retryable failures: staleness
conflict and invalid operations (unknown cell id)
- **Encoded errors in `generate-v4.ts` onError** — the one place in the
pipeline that holds the live `Error` before it becomes a string in the
persisted message
- **Extracted and fixed message history filter** into new
`generate-assistant-response.utils.ts` — any tool-error whose
`errorText` decodes against the `NotebookToolError` schema is let
through (with `errorText` rewritten to plain prose so the model sees the
message, not JSON), while other errors stay filtered as before

Net effect: the assistant detects the specific, actionable rejection
reason and retries on its own with no dead button or human intervention
needed.

## Test plan

- Existing unit tests in `NotebookProposalRenderer.test.tsx` pass (dead
button test removed)
- New unit tests in `notebook-tools.test.ts` cover encode/decode
round-trips and error discrimination
- New unit tests in `generate-assistant-response.utils.test.ts` cover
message history filtering with all error states
- `pnpm typecheck` is clean
- `pnpm --filter studio run lint:ratchet` passes (no new ESLint
warnings)

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

## Summary by CodeRabbit

* **New Features**
* Notebook update errors now provide clearer, structured explanations to
the AI assistant.
* Assistant responses preserve relevant notebook error details while
filtering invalid or temporary tool states.
* **Bug Fixes**
* Improved handling of stale notebook revisions and invalid notebook
update operations.
* Notebook proposal rendering proceeds without an unnecessary refresh
step.
* **Tests**
* Expanded coverage for notebook errors, message filtering,
serialization, and error handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-20 16:08:19 -04:00
Charis e7c3cad8de feat(evals): add notebook eval cases and forbiddenTools scorer capability (#49104)
## Summary

- Added ~11 new eval cases for Notebooks AI assistant evals, including:
basic notebook cell creation, multi-cell composition
(markdown+database+log), log cell time ranges, row_limit defaults, chart
config, destructive SQL safety warnings, and hallucination guards for
nonexistent tables
- Extended `toolUsageScorer` with deterministic `forbiddenTools` field
to score both required and forbidden tool usage, enabling eval cases to
assert tool choice (e.g., `execute_sql` vs `create_notebook`) without
LLM-as-judge
- Extended SQL validators
(`sqlSyntaxScorer`/`sqlIdentifierQuotingScorer`) to validate SQL inside
`create_notebook` database cells (log cells deliberately excluded as
they use ClickHouse dialect)
- Fixed two real assistant issues in `NOTEBOOKS_PROMPT`: (a) reuse
`CLICKHOUSE_LOGS_COMPLETION_INSTRUCTIONS` and schema section to prevent
incorrect BigQuery-style SQL in logs queries, (b) require schema
verification before writing `database_cell` to prevent queries against
nonexistent tables

Resolves FE-4087

## Test plan

- All 11 new eval cases run live against OpenAI via Braintrust; traces
inspected and validated
- Existing unit tests, lint, and typecheck pass

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

* **New Features**
* Added support for creating notebooks with database, Markdown, chart,
and log cells.
* Improved handling of reusable notebook requests versus one-off SQL
queries.
* Added guidance for modern ClickHouse SQL and absolute log time ranges.

* **Bug Fixes**
* Improved SQL validation, row-limit enforcement, and destructive-query
safety.
  * Prevented invalid or nonexistent-table queries from being accepted.
  * Improved validation of notebook cell types and tool usage.
  * Improved handling of ClickHouse log queries and database-cell SQL.
* Improved evaluation reliability by limiting concurrent test execution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-20 15:58:58 +00:00
Charis 474bf5da4a fix(studio): reset rename form between same-named SQL snippets (#49275)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

In the SQL Editor, when two snippets are both named "Untitled query" and
one is renamed, the rename modal's state is not reset afterwards.
Opening the rename modal for the second snippet prefills the input with
the first snippet's new name, and the second snippet can't be renamed at
all because the "Rename query" button stays disabled.

`RenameQueryModal` fed the snippet to react-hook-form through the
`values` option, which only re-runs its reset when the values object
deep-changes. Two snippets with the same name (and no description)
produce a deep-equal object, so switching between them never resets the
form — it keeps the previously renamed name and stays non-dirty.

## What is the new behavior?

The form is mounted per snippet (`key={snippet.id}`) with plain
`defaultValues`, so no form state can carry over between snippets
regardless of name collisions. `SQLEditorNav` derives modal visibility
from the selected snippet and clears it on cancel/complete, matching
`SearchList`.

Covered by a new component test in `RenameQueryModal.test.tsx` that
renames one "Untitled query", reopens the modal for a second one, and
asserts the field resets and the second rename submits.

## Additional context

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

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed the rename dialog retaining input from a previously renamed
snippet.
* Ensured the rename form resets correctly after successful submission
and when switching between snippets.
* **Tests**
* Added regression coverage for renaming multiple untitled snippets with
the same original name.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-20 09:09:45 -04:00
CharisandJoshen Lim 8bdfe03fe7 refactor(studio): drop notebook type widening now that the API supports it (#49272)
## Summary

- Regenerates `packages/api-types` for the content endpoints now that
the Platform API's `notebook` content type has landed (list/get/upsert
`type` enums, plus `UpsertContentBody`'s notebook cell shape with
`_id`/`y_series`). Unrelated schema drift from the same regen
(Warehouse, SSO, notification exceptions, etc.) is excluded — only the
content-endpoint hunks are applied.
- Removes every local widening cast added while the API support was
pending (`content-query.ts`, `content-infinite-query.ts`,
`notebook-query.ts`, `notebook-upsert-mutation.ts`,
`sql-folders-query.ts`).
- What remains is scoped and renamed to match: draft ids
(`generateDraftId`/`isDraftId`), used only for cells created client-side
in the editor before their first save, dropped before they'd ever reach
the backend as a fake `_id`.

## Test plan

- [x] `pnpm typecheck` — clean
- [x] `pnpm --filter studio test` — full suite passes (518 files / 5471
tests)
- [x] `pnpm --filter studio run lint:ratchet` — no new warnings
- [x] `pnpm format` / prettier — clean

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

* **Bug Fixes**
* Improved notebook cell tracking during editing, reordering, insertion,
and deletion.
* Preserved existing cell identifiers while removing temporary draft
identifiers before saving.
* Improved chart configuration for selecting and displaying multiple
Y-axis series.
  * Strengthened notebook validation and content persistence behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-20 13:06:41 +08:00
Charis 4343e21da0 feat(studio): tighten the notebook diff preview (#49218)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

UI refactor of the notebook create/update preview in the AI Assistant
panel, plus a small additive prop on the shared `CodeBlock`.

## What is the current behavior?

The assistant's notebook diff renders each cell as its own bordered box
with a gap between them, under a `6 cells` line that is easy to miss.
Cells can't be collapsed, each one carries a repeated `ADDED` badge and
a nested "Show more" toggle, and long markdown scrolls sideways instead
of wrapping.

## What is the new behavior?

<img width="796" height="1076" alt="CleanShot 2026-08-18 at 14 41 30@2x"
src="https://github.com/user-attachments/assets/45e58c6c-48f2-404b-8699-757ee96a4a8d"
/>

- The whole diff is one card: a distinct header row (notebook name,
summary, expand/collapse all) over cells glued together by dividers.
- Every cell is a `Collapsible`. Added and replaced cells open by
default; unchanged, moved, and removed cells stay as single rows but are
now inspectable instead of being content-free.
- The per-row badge is replaced by a colored gutter glyph (`+` `−` `~`
`↕`) with a tooltip naming the change type. The change type reaches the
accessible name via `aria-label` on the row.
- The nested "Show more" toggle inside each cell is gone — the row
itself is the only control.
- `CodeBlock` gains a `wrapLongLines` prop (default `false`, no change
for existing callers), used here so markdown and SQL soft-wrap. The
highlighter sets `white-space` inline on the `<code>` element, so a
class on the `<pre>` can't do this.

## Additional context

Towards FE-4143

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

* **New Features**
* Notebook previews now display titles, notebook icons, and clearer
bordered layouts.
* Added per-cell expand/collapse controls, including “Expand all” and
“Collapse all.”
  * Long code lines can now wrap for improved readability.

* **Improvements**
* Added mode-based fallback labels when notebook titles are unavailable.
* Newly added and replaced cells expand by default, while unchanged
cells remain collapsed.
* Improved change markers, tooltips, removed-cell styling, and notebook
proposal preview spacing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-19 12:11:10 -04:00
Charis 8c409e2df5 Fix eval scorer truncation via local transcript capture (#49151)
## Problem

Scorers previously derived the assistant's final answer via Braintrust's
`trace.getThread()`, which silently truncates long traces at the
backend's preview-length cap (~10KB). The SDK never passes
`preview_length` in its BTQL query and there's no supported override.
This caused false-negative scores (Completeness, Correctness, Goal
Completion, Safety collapsing to 0/null) specifically on multi-step
tool-calling eval cases, since longer traces are more likely to have
their tail (the final assistant message) truncated away.

## Solution

Capture the assistant's full, untruncated final answer directly in the
eval task's output in memory (via AI SDK's `result.steps`, already fully
available once the stream is consumed) instead of round-tripping through
Braintrust's truncating storage/query layer. Scorers now read
`output.transcript` instead of calling `trace.getThread()`.

## Changes

- **New**: `apps/studio/evals/transcript.ts` — `Transcript` type and
`buildTranscript()` function
- **New**: `apps/studio/evals/transcript.test.ts` — unit tests (5
passing)
- **Modified**: `apps/studio/evals/assistant.eval.ts` — captures
`result.steps` and returns transcript
- **Modified**: `apps/studio/evals/scorer.ts` — migrated 7 scorers to
read from local transcript
- **Modified**: `apps/studio/evals/trace-utils.ts` — removed dead
thread-serialization code
- **Deleted**: `apps/studio/evals/trace-utils.test.ts` — superseded by
transcript tests

## Test Plan

- [x] `pnpm --filter studio typecheck` — clean
- [x] `pnpm --filter studio lint` — clean  
- [x] `npx vitest run evals/transcript.test.ts` — 5/5 passing
- [x] Full live eval run (35/35 cases) against Braintrust —
[experiment](https://www.braintrust.dev/app/supabase.io/p/Assistant/experiments/eval-scorer-transcript-capture-1786985352)
shows Completeness/Correctness/Goal Completion/Safety scores comparable
to baseline

## Known Residual Risk

Other scorers that derive data from `trace.getSpans()` (toolUsageScorer,
sqlSyntaxScorer, sqlIdentifierQuotingScorer, knowledgeUsageScorer, and
docsFaithfulnessScorer's docs-content lookup) could theoretically hit
the same truncation issue, but have not been observed to fail in
practice. This is not addressed in this PR.

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

* **New Features**
* Added transcript generation from assistant interaction steps,
including text and tool-call inputs.
* Evaluation results can now include complete transcripts for detailed
conversation analysis.
* Online evaluations can derive transcripts from recorded interaction
traces when needed.

* **Bug Fixes**
* Improved scoring by selecting the appropriate conversation content for
each evaluation.
* Ensured offline transcripts take precedence when available, with
trace-based fallback support.

* **Tests**
* Added coverage for multi-step interactions, tool calls, filtering,
empty steps, and URL validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-18 12:22:50 -04:00
Charis 79fbe467ba feat(studio): wire notebook create/update proposals into assistant panel (#49159)
## Summary

PR 4 of the notebook-approval-preview stack.

- Adds `NotebookProposalRenderer`, wiring
`create_notebook`/`update_notebook` into `MessagePartSwitcher` and
rendering `NotebookPreview` across all 6 tool states (drafting,
approval-requested, approval-responded, output-available, output-denied,
output-error).
- `update_notebook` fetches the live notebook via `useNotebookQuery`,
checks `expected_updated_at` against the fetched `updated_at`, and gates
the confirm action behind a refresh when stale.
- A tool-input parse failure renders a raw-input admonition instead of
returning `null`, so `ConfirmFooter` — and the ability to Skip/deny —
stays available rather than leaving the chat stuck.

Towards FE-4143

## Test plan

- [x] `tsc --noEmit` clean
- [x] `eslint` clean on touched files
- [x] `prettier --check` clean
- [x] New `NotebookProposalRenderer.test.tsx` (create/update previews +
approve, version-mismatch warning, parse-failure fallback with working
Skip, output-available/output-denied summaries)
- [x] Existing notebook test suites (`notebook-tools.test.ts`,
`notebook-operations`, `NotebookPreview`) still pass

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

## Summary by CodeRabbit

* **New Features**
* Added AI-assisted notebook creation and updating with previews,
approval controls, and operation summaries.
* Added clear handling for loading, errors, denied actions, stale
notebook versions, and invalid proposals.
  * Added links to open notebooks after successful creation or updates.
  * Preserved notebook SQL content when displaying proposed changes.

* **Bug Fixes**
* Improved notebook proposal handling for conflicts and incomplete tool
responses.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-18 11:56:50 -04:00
Charis 071a07118a feat(studio): add NotebookPreview diff component (#49112)
## Summary

Stacked on #49109 (PR 1 — `deriveNotebookDiff`). This is PR 3 of the
notebook approval-preview stack: a pure presentational component that
renders the cell-level diff for a proposed notebook create/update, for
use in the assistant approval UI (wired in a later PR).

- `NotebookPreview` — header summary (`"6 cells"` for create, `"+2 −1 ~1
↕1"` for update) + entry list + "Show N more cells" for long notebooks.
- `NotebookPreviewCell` — dispatches per entry tag:
`unchanged`/`removed`/`moved` collapse to a muted badge row; `added`
renders source via `CodeBlock` (with a max-height/expand toggle);
`replaced` renders a `DiffEditor` diff, plus a before → after metadata
line when only `database_identifier`/`time_range` changed (SQL/text
identical).
- `NotebookPreview.utils` — pure helpers (labels, source/metadata
extraction, language mapping, summary formatting), unit tested.
- **Safety property**: cell content only ever renders through
`CodeBlock`/`DiffEditor` (literal source), never through a markdown
renderer — agent-authored text can't trigger image loads or link
navigation before the user approves. Covered by an adversarial test
(`![x](evil)`, `[y](evil)`, `<img onerror>` → zero
`img`/`[href]`/`[src]` DOM nodes).
- Adds `'markdown'` as a supported `CodeBlock` language (small, additive
change to `packages/ui-patterns`).

Towards FE-4143

## Test plan

- [x] `pnpm --filter studio test` — NotebookPreview suite (21 tests)
passes
- [x] `pnpm --filter studio exec eslint
components/interfaces/Explorer/NotebookPreview` — clean
- [x] `pnpm --filter studio exec tsc --noEmit` — no new errors
- [x] `pnpm exec prettier --check` — clean

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

## Summary by CodeRabbit

* **New Features**
  * Added notebook previews showing create and update summaries.
* Displayed added, removed, moved, unchanged, and replaced cells with
metadata and source diffs.
* Added expandable previews with truncation and a “Show more cells”
option.
  * Added Markdown syntax highlighting to code blocks.

* **Bug Fixes**
* Safely render adversarial agent-authored Markdown as literal content.

* **Tests**
* Added comprehensive coverage for notebook previews, summaries,
metadata, formatting, and truncation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-18 11:44:23 -04:00
Charis 2e68f2bf6e refactor(studio): derive notebook diff entries (#49109)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Refactor, plus one bug fix.

Groundwork for showing the user a preview of what they are approving
when the AI Assistant creates or edits a notebook. No UI in this PR.

Towards FE-4143

## What is the current behavior?

`applyNotebookOperations` resolves an ordered list of notebook
operations into the resulting cells and nothing else. Rendering a diff
for the approval gate needs to know *what happened* to each cell
position, not just where things landed, so there is no way to build the
preview on top of it.

Separately, replacing a cell dropped its id, so `[replace cell-2, insert
after cell-2]` failed with a spurious `unknown_cell_id`.

## What is the new behavior?

`deriveNotebookDiff` resolves operations into one annotated entry per
cell position (`unchanged`, `added`, `removed`, `replaced`, `moved`).
`applyNotebookOperations` becomes a thin projection over its result, so
there is a single interpreter of notebook operations and the diff a user
approves cannot disagree with the cells that get written. The
pre-existing tests pass untouched, which is the evidence that the
projection is faithful.

Notes on the annotations:

- `removed` entries stay in the position the cell used to hold so the
list reads as a diff. This does not perturb insert-anchor arithmetic:
prior inserts still sit contiguously after their anchor.
- Moves that cancel out are downgraded to `unchanged`, since two moves
can anchor on each other and leave every cell where it started. Badging
those as moved would make the preview lie.
- `fromIndex` is the cell's position in the original notebook rather
than in the shifted working order, so `was #3` means what a reader
expects.

A replaced cell now stays addressable as an anchor. Anchoring and
targeting are separate lookups: a replaced cell can be anchored on, but
is never a legitimate target.

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

* **New Features**
* Notebook changes now provide a structured view of added, removed,
replaced, moved, and unchanged cells.
* Replaced cells can be used as insertion anchors, while invalid or
duplicate targets are rejected.
  * No-op moves are handled as unchanged cells.
* Notebook edits preserve operation ordering and original cell positions
for more predictable results.

* **Bug Fixes**
* Improved notebook operation handling and error reporting for complex
cell edits.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-17 13:11:40 -04:00
Charis 9be60cab63 refactor(studio): add optimistic locking to update_notebook (#49111)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Refactor / hardening

## What is the current behavior?

The `update_notebook` AI tool re-fetches the notebook right before
applying operations, but concurrent edits are last-write-wins: the model
has no way to detect that the notebook changed since it planned the
edit, so a stale diff can silently overwrite someone else's changes.

## What is the new behavior?

- `get_notebook` now returns the notebook's `updated_at` timestamp.
- `update_notebook` requires a new `expected_updated_at` input field
(the `updated_at` the model got from `get_notebook`).
- At execute time, after the existing re-fetch and before applying
operations, `update_notebook` compares the fetched `updated_at` against
`expected_updated_at` and throws a descriptive error if they don't
match, telling the model to re-read the notebook and reissue the update.
- The notebook system prompt and mock tools (used by the eval harness)
are updated to match.

## Additional context

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

## Summary by CodeRabbit

* **New Features**
  * Notebook retrieval now includes the latest update timestamp.
* Notebook edits require confirmation that the content is current before
saving.

* **Bug Fixes**
  * Prevented stale edits from overwriting newer notebook changes.
* Conflicting updates are rejected, allowing the latest content to be
fetched before retrying.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-14 13:20:27 -04:00
Charis 628473b3eb refactor(studio): extract notebook query-cell logic and give log cells display settings (#49075)
Final PR of the stack. #49069, #49070, #49072 and #49074 have merged, so
this now targets `master` directly.

**Rebased onto latest `master`**, which includes the centralized
result-rendering work (#49096). See "Conflict resolution" below.

## What's left after master's own fixes

`QueryCell` was written for database cells and adapted to log cells
afterwards. Master has since fixed most of it directly:
`handleUpdateCell` no longer bails on a non-database cell, the cell's
own binding is read via `getQuerySourceBinding`, and
`database_identifier` / `time_range` propagate across a source change.

What remains:

- **`display` was only passed for database cells**, so the `view` field
on `log_cell` stayed unreachable and a logs query could never be
charted. That is the one behavioral fix left in this PR.
- The per-backend branching is inline and untested.

## What changed

Per-backend logic moves into `QueryCell.utils.ts`, where it is
unit-tested: `changeCellSource`, `setCellSql`, `cloneQueryCell`,
`getCellDisplay`, `toQueryModel`. Each narrows on the cell tag exactly
once, so the SQL brand and the backend's parameters stay correlated
rather than being re-derived at each call site. `cloneQueryCell` also
rebuilds the chart's series array, which valtio hands over as `readonly
string[]`.

`NotebookEditor` renders through `isQueryCell` (#49069) rather than a
tag switch, so a new backend gets picked up by classifying it in
`CELL_KINDS` instead of by remembering to add a `case`.

## Conflict resolution

Two rounds of master's work landed in this file set.

**`QueryCell/index.tsx` (master's own rework).** `changeCellSource`
**subsumes the four source-change branches** master had inline, each
covered by a test:

| Master's branch | Test |
|---|---|
| database → database (replica change) | `keeps the query when only the
database changes` |
| logs → logs (time-range change) | `keeps the query when only the log
time range changes` |
| database → logs | `carries the query text over when moving from the
database to logs` |
| logs → database | `carries the query text over and restores a default
row limit …` |

Two improvements fall out of consolidating them:

- A **logs → database** move now keeps the selected replica; pinned by
`applies the selected database when moving from logs to the database`.
- The row-limit default is **named** rather than a hard-coded `100`.
`Explorer/utils.ts` now shares `DEFAULT_CELL_ROW_LIMIT` with
`createQueryCellSkeleton`, so cell creation and backend conversion can't
drift.

Untouched from master: `snap.updateCell`, `AddCellDropdown`,
`MoveCellDropdownContent`, the `SortableSection` grip props, and
`NotebookEditor`'s add-cell buttons, skeletons, `reorderCells` and
`insertCellAfter`.

**Centralized result rendering (#49096).** That PR moved
`QueryCell/QueryResultChart.tsx` up to `Explorer/`, split
`QueryResultTable` into `QueryResultError`, and added
`QueryResultRenderer`. Since this PR removes `QueryChartConfig`, the
type swap had to follow the move and also reach `QueryResultRenderer`,
which is new and referenced the removed type. `QueryResultRenderer`,
`QueryResultError` and `DataGridResults` are otherwise untouched — the
empty/error-state centralization is fully preserved, and `QueryEditor`
still renders through it.

## Behavior worth a second opinion

`changeCellSource` **carries the query text across a backend change**
and rebrands it. This is probably not what a user wants — Postgres SQL
and logs SQL are separate dialects over separate schemas, so a
carried-over query will usually fail to run, and the rebrand asserts a
dialect the text was never written in.

Keeping it for now because it destroys nothing and needs no confirmation
prompt. The tradeoff is written up at the function. Worth revisiting
once we know whether people switch source to port an existing query or
to start a fresh one — if it's the latter, clearing the body behind a
confirmation is the better answer.

Results *are* dropped on a backend change, since another engine returns
unrelated columns.

## Incidental

`Explorer/types.ts` drops `QueryChartConfig`, which duplicated the wire
schema's `ChartConfig` field for field. `chart` stays persisted
alongside `view`, so switching to the table and back returns the user's
chart settings rather than rebuilding them.

## Verification

Typecheck, Prettier, and the lint ratchet clean. 1013 tests pass across
`state/`, the Explorer surfaces, notebooks, query sources, `data/sql`,
the SQL editor, and `components/ui`; 13 of them are new coverage for the
extracted helpers.

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

## Summary by CodeRabbit

- **New Features**
- Improved notebook cell rendering with more consistent handling of
query and markdown cells.
- Query cells now preserve SQL, source settings, display preferences,
chart configuration, and query results when edited or switched between
sources.
  - Added a default limit of 100 rows for applicable database queries.

- **Bug Fixes**
- Prevented stale query results from carrying over when changing query
sources.
- Improved chart configuration consistency across query results and
display settings.

- **Tests**
- Added comprehensive coverage for query-cell updates, source
transitions, SQL changes, display state, and chart data.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-14 09:42:18 -04:00
Charis b5bfc70c06 fix(studio): correlate a query's SQL brand with the backend it runs against (#49074)
Fourth of the stack; PRs 1–3 (#49069, #49070, #49072) have merged, so
this now targets `master` directly.

**Rebased onto latest `master`.** See "Conflict resolution" at the
bottom for what was reconciled.

## The bug

`QueryEditor` took `sql: string`, so a query's dialect brand died at the
prop boundary and the component re-branded whatever it was handed based
on a separately-passed `source`. Nothing tied the two together, which
meant nothing stopped Postgres SQL from reaching the analytics endpoint.

Explorer query drafts made it concrete. `explorer-query.ts` branded
**every** draft with `untrustedSql` regardless of source:

```ts
uncheckedSql: untrustedSql(sql)   // even for a logs draft
```

and the editor then re-branded that same text with `untrustedLogSql` at
run time for a logs draft — laundering a Postgres-branded value straight
through the boundary that `safe-analytics-sql.ts` exists to defend. The
brands are deliberately disjoint precisely so this can't happen; passing
plain strings around defeated it.

## The fix

Both carriers are now tagged by backend, so one `_tag` check narrows the
SQL brand and that backend's parameters together.

- **`ExplorerQueryDraft`** becomes `DatabaseQueryDraft |
LogsQueryDraft`, and `toDraft` is the single place a persisted string
re-enters the type system — branded for the backend its binding names.
The draft is rebuilt rather than mutated in place, since a backend
change changes which brand its SQL carries.
- **`QueryEditor`** takes one discriminated `query` prop instead of
`sql` + `source` + `rowLimit`. The tag picks both the brander at the
editor boundary and the execution endpoint, so the mismatch is no longer
expressible.
- The two `acceptUntrusted*` promotions stay **inlined** in the run
handler rather than factored into a shared helper, so each stays visible
next to the user gesture that authorizes it, per the safe-SQL model.
- **`rowLimit` moves onto the database member.** Logs execution has no
use for it — `applyAutoLimit` is Postgres-specific — so it no longer
sits on a shared type where it reads as meaningful for both.

## Local storage

Existing query drafts shape-mismatch and fall back to a database binding
via the existing `safeParse` guard — harmless, and notebooks are still
behind the `explorer` flag so there is no saved server content in play.

## Conflict resolution

`master` moved inside every file this PR touches. The type change is
applied on top of that work; nothing was reverted.

| Preserved from `master` | Where |
|---|---|
| zod parsing of persisted drafts (`persistedDraftsSchema`,
`persistedDraftSchema`) | `explorer-query.ts` |
| `MAX_PERSISTED_EXPLORER_QUERY_DRAFTS` cap, retaining
most-recently-updated | `explorer-query.ts` |
| debounced SQL persistence + `flushPendingPersistence`, immediate
write-through for rename/source | `explorer-query.ts` |
| `removeDraft` clearing pending timers | `explorer-query.ts` |
| `getQuerySourceBinding(cell)` and the four source-change branches,
incl. `database_identifier` / `time_range` propagation |
`QueryCell/index.tsx` |
| `restoredQueryKey` per `ref:id` and the `role="status"` loader |
`QueryTab.tsx` |
| `applyAutoLimit` relocated to `@/data/sql/utils` | `QueryEditor.tsx` |

Two adaptations were needed:

- `updateDraft` rebuilds the draft through `toDraft` instead of mutating
it in place — required, because the object's shape depends on its tag.
The debounced `persist` closure still re-reads `state.drafts[id]` at
fire time, so behavior is unchanged.
- Master's new test `falls back to the database source when persisted
source data is invalid` asserted `draft.source`, which the tagged union
replaces. Rewritten to assert the same intent against `_tag`.

**Dropped from this PR's original description:** it previously claimed
to fix a log cell always running against a synthesized default time
range. Master fixed that itself by adopting `getQuerySourceBinding`
(from #49072), so the claim no longer applies.

## Verification

Typecheck, Prettier, and the lint ratchet clean. 736 tests pass across
`state/`, the Explorer surfaces, notebooks, query sources, `data/sql`,
and the SQL editor — including master's new `QueryTab.test.tsx`,
`ExplorerQuerySourceMenu.test.tsx`,
`ExplorerQueryTabCoordinator.test.tsx`, and the five draft-store tests
added since this branch was cut.

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

## Summary by CodeRabbit

- **Bug Fixes**
  - Improved Explorer query handling across database and logs backends.
- Preserved query text when switching backends while clearing
incompatible results.
  - Retained results when changing parameters within the same backend.
- Improved restoration of saved drafts, including fallback handling for
legacy or invalid sources.
- Added validation before executing edited SQL to help prevent invalid
requests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-14 08:49:54 -04:00
Charis 794e45378c test(studio): add get_notebook eval cases (FE-4088) (#49067)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature (test coverage) — second PR of the notebooks-evals plan,
covering FE-4088 (Evals: Assistant can read notebook). Follows #49010
(FE-4086, list_notebooks).

## What is the current behavior?

`dataset.ts` has no coverage for `get_notebook`. Separately,
`NOTEBOOKS_PROMPT` only covers *choosing* between `create_notebook`,
`update_notebook`, and `execute_sql` — it says nothing about reading or
describing an existing notebook.

## What is the new behavior?

**Eval cases** (`evals/dataset.ts`), three of them:

- Resolve a notebook by name via `list_notebooks`, then `get_notebook`,
and report the queries it actually contains.
- Summarize a smaller, single-log-cell notebook as a baseline.
- Report a nonexistent notebook id as not found instead of hallucinating
contents. The mock's `execute` throws, which the AI SDK surfaces to the
model as a `tool-error` part rather than failing the eval task.

No new scorers or tool changes — `toolUsageScorer` and
`correctnessScorer` already cover these, and `get_notebook` has unit
coverage in `notebook-tools.test.ts`.

**Prompt change** (`lib/ai/prompts.ts`) — please review this one
separately, it's the only production behavior change here:

Running the first case surfaced a real gap. The assistant transcribed
each cell's SQL correctly but was inconsistent about the configuration
that changes what a cell returns — dropping the log cell's time range in
some runs, miscounting markdown cells as queries in others. Adds one
bullet to `NOTEBOOKS_PROMPT` telling it to report a query cell's
configuration and not count markdown cells as queries. Gated behind the
Explorer flag, same as the rest of that prompt.

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

* **New Features**
* Added notebook evaluation scenarios for database and log queries,
including concise summaries and nonexistent notebook handling.
* Improved notebook descriptions by reporting query configurations that
affect returned results.
  * Markdown cells are now excluded from query counts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-13 15:09:36 -04:00
Charis 8baaa517d0 test(studio): add list_notebooks eval cases (FE-4086) (#49010)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature (test coverage) — first PR of the notebooks-evals plan, covering
FE-4086 (Evals: Assistant can list notebooks).

## What is the current behavior?

`dataset.ts` has zero notebook eval cases. Separately, two small gaps
block writing them: `assistant.eval.ts` never sets `isExplorerEnabled`,
so `NOTEBOOKS_PROMPT` never loads into the eval task's system prompt;
and `list_notebooks`' `inputSchema` has no sort parameter, even though
`getContent` already supports one, so "most recent" isn't answerable.

## What is the new behavior?

- `assistant.eval.ts` passes `isExplorerEnabled: true` so
`NOTEBOOKS_PROMPT` loads during evals.
- `list_notebooks` gains a `sort_by: 'name' | 'inserted_at'` param,
forwarded to `getContent`'s `sort`. Only creation order is exposed,
since the underlying API has no `updated_at` sort key.
- 3 new dataset cases: basic notebook enumeration, sorting by creation
time (`sort_by`/`limit` args), and a nonexistent-notebook case guarding
against hallucinated results.
- A unit test covering `sort_by` forwarding to the content API's query
param.

## Additional context

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

* **New Features**
* Added notebook sorting options when listing notebooks, including by
name or insertion date.
* Added evaluation coverage for listing notebooks, finding the newest
notebook, and avoiding fabricated results for nonexistent notebooks.

* **Bug Fixes**
* Ensured selected notebook sorting preferences are correctly applied
when retrieving content.
  * Improved assistant evaluation coverage with Explorer mode enabled.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-12 16:50:08 -04:00
Charis b9835e419c test(studio): mock notebook tools (#48952)
## Summary
- Adds deterministic `list_notebooks`/`get_notebook` fixtures (two
seeded notebooks, each with markdown/database/log cells) and stateful
in-memory `create_notebook`/`update_notebook` mocks to
`apps/studio/lib/ai/tools/mock-tools.ts`, so Braintrust evals can
exercise notebook tool calls without a real project.
- Both write-tool mocks force `needsApproval: false`, matching the
existing `execute_sql`/`deploy_edge_function` mock pattern — the eval
harness filters out tool-parts in `'approval-requested'` state and can
never answer an approval gate.
- All four notebook tools are wrapped from the real `getNotebookTools()`
definitions (only `execute`/`needsApproval` overridden), so evals
validate the model's arguments against the exact production schemas.
- Dedupes `describeOperationError` (previously duplicated between this
new mock and `notebook-tools.ts`) into a single exported
`describeNotebookOperationError` in `notebook-operations.ts`.

**Stacked on #48949** (`feature/notebooks-update-tool`) — this PR's base
branch is that PR, not `master`, because it reuses `update_notebook` and
the shared error helper that only exist there. Merge #48949 first, then
retarget/merge this one.

## Test plan
- [x] \`pnpm --filter studio typecheck\` passes
- [x] \`pnpm --filter studio test\` — all notebook-related suites pass
(\`mock-tools.test.ts\`, \`notebook-tools.test.ts\`,
\`data/content/notebooks/*\`)
- [x] \`eslint\` / \`prettier --check\` clean on all touched files
2026-08-12 10:34:37 -04:00
Charis 4da8bfe469 feat(studio): wire update_notebook tool (#48949)
## Summary
- Adds `update_notebook`, an AI tool (`needsApproval: true`) that
re-fetches a notebook, applies an ordered list of cell operations
(insert/replace/delete/move, from `notebook-operations.ts`) via
`applyNotebookOperations`, and PUTs the resolved content. Concurrent
edits are last-write-wins, since re-fetching at execute time is the only
mitigation per the RFC.
- Registers `update_notebook` in `toolSetValidationSchema` /
`TOOL_CATEGORY_MAP`, and documents it in the notebooks system prompt.
- The untrusted→safe SQL promotion
(`acceptUntrustedSql`/`acceptUntrustedLogsSql`) is inlined directly
inside each `needsApproval: true` tool's own `execute` (both
`create_notebook` and `update_notebook`), rather than behind a shared
helper — kept auditable right next to the approval gate that authorizes
it, and not reusable by an unrelated caller unaware it needs
re-promotion.

Resolves FE-4083

## Test plan
- [x] `notebook-tools.test.ts`: re-fetch → apply operations → PUT with
promoted SQL; descriptive error (no PUT) when an operation targets an
unknown cell id; `needsApproval` asserted for both create and update
- [x] `pnpm vitest run` on `notebook-tools.test.ts`,
`tools/index.test.ts`, `tool-filter.test.ts` — all pass
- [x] `tsc --noEmit`, `eslint`, `prettier --check` — clean on all
touched files

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

## Summary by CodeRabbit

* **New Features**
* AI-assisted notebook editing now supports updating existing notebooks.
* Notebook changes are refreshed before being applied to help preserve
the latest content.
* Updates report operation errors and return a summary of the modified
notebook.
  * Notebook edits require user approval before being saved.

* **Bug Fixes**
* Invalid or unknown cell references are reported without applying a
partial update.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-12 09:31:41 -04:00
Charis 4ca17a23ce fix(studio): keep the new SQL editor tab from being pruned on first keystroke (#48956)
## What kind of change does this PR introduce?

Bug fix.

## What is the current behavior?

With a snippet already open in the SQL Editor, clicking the `+` button
and typing in the new tab made it look like the previously open tab was
being taken over: the tab bar showed the *old* snippet's name while the
editor showed the newly typed content. With several tabs open, the
rightmost one appeared to be the one taken over.

`useSqlEditorTabsCleanup` prunes any `sql-*` tab whose snippet is absent
from the server-fetched snippet list, so tabs for snippets deleted in
another session don't linger. But a snippet created by typing in a new
tab exists only in the local store until its first save lands, so it is
legitimately absent from that list — and the same keystroke that creates
it calls `setSql({ shouldInvalidate: true })`, invalidating the snippet
lists and triggering a refetch that pruned the tab that had just been
opened.

`removeTab` then reassigns `activeTab` to a neighbor, so the tab bar
fell back to whichever tab was open before, while the URL, sidebar, and
editor content all stayed correctly on the new snippet — none of them
read from the tabs store.

Confirmed from a user's persisted tab state, which showed a single tab
in `openTabs` pointing at the previous snippet while the URL pointed at
the new one.

No snippet content was ever lost — this was tab state only.

## What is the new behavior?

Tabs (and recent items) whose snippet is present in the local store with
a never-persisted status (`new`, `new_saving`, `new_save_failed`) are
preserved by the cleanup pass. Genuine stale-tab pruning for snippets
removed outside the session is unaffected.

## Additional context

The regression test in `Tabs.utils.test.tsx` fails without the fix
(`expected undefined to be defined`) and passes with it. The
pre-existing pruning tests still pass, confirming legitimate cleanup
still works.

Also adds two tests covering adjacent invariants that were verified
while narrowing this down: snippet content isolation between tabs, and
unique snippet id generation across `/sql/new` navigations.

Verified: 343 tests pass across `components/layouts/Tabs` and
`components/interfaces/SQLEditor`; typecheck clean; ESLint warning count
unchanged (ratchet safe); Prettier clean.

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

## Summary by CodeRabbit

- **Bug Fixes**
- Preserved newly created SQL editor tabs and recent items before their
first save.
  - Prevented unsaved snippets from being removed during tab cleanup.
- Ensured editing a new tab does not overwrite content in existing tabs.
  - Ensured successive new SQL tabs receive distinct identities.

- **Tests**
- Added regression coverage for snippet editing, route changes, and tab
cleanup behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-12 09:30:59 -04:00
Charis 065468f8ac fix(studio): reset rename form after renaming a SQL snippet (#48951)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

The SQL snippet rename modal is mounted once per nav and reused for
every snippet, so a single form instance is shared across renames. On a
successful rename the form was never re-baselined, leaving it dirty, and
the effect that synced the form to the selected snippet bailed out
whenever the form was dirty.

Renaming a second snippet therefore opened the modal pre-filled with the
previous snippet's name, with the submit button enabled — one careless
confirm renamed the wrong query.

## What is the new behavior?

The form is reset after a successful rename, and the hand-rolled sync
effect is replaced with react-hook-form's `values` option so the form
follows whichever snippet is selected.

`keepDirtyValues` keeps a background refetch from clobbering in-progress
input, which is what the old dirty guard was protecting against. It has
to be disabled explicitly on the resets that discard input, since
`resetOptions` on `useForm` applies to every `reset` call — not just the
`values`-driven one.

Adds component tests covering the submit path, the rename-then-rename
regression, and discarding an abandoned edit on cancel.

## Additional context

Fixes FE-4114

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

## Summary by CodeRabbit

- **Bug Fixes**
- Improved the rename query experience by ensuring the selected snippet
name is displayed correctly when reopening the rename dialog.
- Cancelled edits are now discarded reliably, preventing unsaved changes
from persisting.
- After a successful rename, the form reflects the updated query name
and maintains consistent input and button behavior.
- **Tests**
- Added coverage for successful renaming, cancellation, reopening with a
newly selected snippet, and submitted values.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-11 20:05:46 +00:00
Charis 810d292121 feat(studio): notebook cell operations (#48940)
## Summary
- Pure module (`data/content/notebooks/notebook-operations.ts`) for
applying `update_notebook` cell edits client-side: `insert_cell`
(`after_cell_id` incl. `'start'`), `replace_cell`, `delete_cell`,
`move_cell`.
- Never touches the safe-sql brands — SQL promotion still happens at the
tool-execute boundary, matching `create_notebook`.
- Stacked on #48938. No wiring yet — `update_notebook` tool wiring is
next.

Towards FE-4083

## Test plan
- [x] `pnpm vitest run
data/content/notebooks/notebook-operations.test.ts` — 13 unit tests
covering every op, combinations, and all three error cases.
- [x] `pnpm exec tsc --noEmit` clean
- [x] `pnpm exec eslint` clean on new files

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

* **New Features**
* Added support for applying notebook cell operations, including
insertion, replacement, deletion, and movement.
* Operations are applied in a predictable order, with support for
anchoring new cells at the beginning or near existing cells.
* Added validation for invalid references, conflicting operations, and
self-referential moves.
* Added clear handling when operations produce an empty notebook result.

* **Tests**
* Added comprehensive coverage for individual, combined, ordered,
conflicting, invalid, and empty-result notebook operations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-11 12:35:27 -04:00
Charis ddb3e2c442 feat(studio): create_notebook AI tool (#48938)
## Summary
- Adds a `create_notebook` AI assistant tool (`needsApproval: true`)
that lets the assistant create a new notebook after explicit user
approval.
- Cell SQL is promoted from untrusted to safe via
`acceptUntrustedSql`/`acceptUntrustedLogsSql` inside `execute`, using
the approval gate as the confirming user gesture (same pattern as
`execute_sql`).
- Input is validated against the existing agent-writable notebook
schema, which rejects any agent-supplied cell `id` at the schema level.
- Threads an optional auth-headers param through
`upsertContent`/`createNotebook`/`updateNotebook` so the tool can pass
its own bearer token server-side.
- Registers the tool in the tool-filter (`SCHEMA` category, alongside
`list_notebooks`/`get_notebook`) and adds a `## Notebooks` prompt
section guiding the assistant on when to use `create_notebook` vs.
one-off `execute_sql`.

Resolves FE-4082

## Test plan
- [x] `notebook-tools.test.ts` covers: tool registration,
`needsApproval`, cell-id rejection, valid input, PUT body shape, and the
returned id — all passing
- [x] Typecheck clean
- [x] Lint clean (no new warnings)

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

* **New Features**
* Added AI-assisted notebook creation for saving multi-step
investigations.
* Added support for database and log SQL cells in newly created
notebooks.
* Notebook creation requires approval before saving and returns the
notebook’s name and identifier.
* Added support for custom request headers during notebook and content
operations.
* Added guidance for choosing between one-time SQL execution and
reusable notebooks when Explorer is enabled.

* **Improvements**
* Improved validation and normalization of notebook content before
saving.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-11 11:54:49 -04:00
Charis 4587d177c3 Add optional title field to notebook cells (#48937)
## Summary

- Adds optional `title` field to `databaseCellSchema` and
`logCellSchema` in notebook schema
- Allows database and logs notebook cells to carry descriptive titles
- Field automatically propagates through derived schemas (wire,
writable, agent, domain) via Zod inheritance

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

## Summary by CodeRabbit

* **New Features**
  * Added optional titles to database and log notebook cells.
* Cell titles are now preserved across notebook editing, viewing, and
agent workflows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-11 09:21:33 -04:00
Charis 7798e42435 feat(studio): notebook read tools (#48908)
## Summary
- Adds `list_notebooks` (cursor-paginated) and `get_notebook` AI tools
in `lib/ai/tools/notebook-tools.ts`, modeled directly on
`report-tools.ts`: server-side `getContent`/`getNotebook` with the
`authorization` header forwarded, zod-validated input.
- `get_notebook` resolves every cell and exposes `unchecked_sql` as a
plain `sql` field for the agent to read — display only, per the
`safe-sql-execution` skill; nothing here executes SQL.
- Registers both tools in `lib/ai/tools/index.ts` (same platform branch
as reports) and in `lib/ai/tool-filter.ts`'s `toolSetValidationSchema` +
`TOOL_CATEGORY_MAP` (`SCHEMA` tier).
- Adds an optional `headers` param to `content-infinite-query.ts`'s
`getContent`, mirroring the sibling `content-query.ts`, so the
cursor-paginated fetch can carry the `Authorization` header from a
server context.
- New tools are behind the Explorer feature flag.

Stacked on #48907 (1.4 — notebook query and mutation hooks), per the
Notebooks implementation plan (stack 2.1).

Resolves FE-4081
Resolves FE-4080

## Test plan
- [x] `pnpm exec tsc --noEmit` — no new errors
- [x] `pnpm exec vitest run lib/ai/tools/notebook-tools.test.ts
lib/ai/tools/index.test.ts lib/ai/tools/report-tools.test.ts
data/content/notebooks` — 36/36 passing
- [x] `pnpm --filter studio run lint` — no new warnings
- [x] `pnpm exec prettier --check` on changed files — clean

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

* **New Features**
  * Added AI tools to list project notebooks with pagination.
* Added AI support for retrieving notebook markdown and resolved SQL
cell content.
  * Notebook tools now respect project and authorization context.
* Notebook features are available only when Explorer access is enabled.
  * Content requests can forward custom request headers.

* **Tests**
* Added coverage for notebook tools, Explorer access, feature flags,
authorization, pagination, and error handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-11 08:40:51 -04:00
Charis 1296a1c745 feat(studio): notebook query and mutation hooks (#48907)
## Summary

Implements the "notebook query and mutation hooks" step of the notebooks
data layer:

- `data/content/notebooks/notebook-query.ts` —
`getNotebook`/`useNotebookQuery`, wrapping the existing `getContentById`
and narrowing to `type: 'notebook'`.
- `data/content/notebooks/notebooks-infinite-query.ts` —
`useNotebooksInfiniteQuery`, a typed wrapper over
`useContentInfiniteQuery` narrowing pages to notebook rows.
- `data/content/notebooks/notebook-upsert-mutation.ts` —
`createNotebook`/`updateNotebook` + their mutation hooks, PUTting
through the existing `upsertContent`.

Write-path correctness, worked out while building the mutation hooks:

- Cell `id`s are always backend-generated, never client-supplied — a
brand-new cell has no `id` at all; an existing cell being kept/edited in
an update keeps its real id so the backend can diff it against the
previous version. `notebook-schema.ts` gains
`writableCellSchema`/`writableNotebookSchema` (ids optional per cell)
and `WritableCell`/`WritableNotebook` types, derived from `z.infer` of
those schemas rather than hand-duplicated, with only the `sql` field
re-branded per cell type via a small distributive conditional type.
- Cell SQL at this write boundary must already be
`SafeSqlFragment`/`SafeLogSqlFragment` (proven user-authored at a
save/run event handler), not `unchecked_sql` — matching the
`safe-sql-execution` skill's provenance model.
- `content-remap.ts`'s notebook `unmapSqlContentField` branch is
simplified to a passthrough: notebook writes only ever arrive already
wire-shaped via `createNotebook`/`updateNotebook`, so there's nothing
left to unmap.

Note: this was originally stacked on
`feature/notebooks-types-convergence`, but that branch merged into
`master` (#48905) while this PR was in progress, so it's rebased
directly onto `master` now.

## Test plan

- [x] `pnpm --filter studio run typecheck` passes
- [x] `pnpm --filter studio exec vitest run data/content/notebooks
data/content/content-remap.test.ts` — 38/38 passing
- [x] `pnpm --filter studio exec eslint` clean on all touched files

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

## Summary by CodeRabbit

* **New Features**
* Added notebook listing with pagination, filtering, sorting, and
project-specific queries.
  * Added notebook retrieval for viewing individual notebooks.
  * Added notebook creation and editing with automatic content refresh.
* Added support for preserving cell IDs and safely handling SQL content.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-10 15:32:46 -04:00
Charis 0111aa371b ref(studio): converge notebook UI types with canonical content schema (#48905)
## Summary
- Joshen's `state/notebooks/types.ts` (Explorer/notebook editor UI)
redefined its own `TimeRange`, cell union, and `NotebookContent` shapes,
duplicating the canonical schema from
`data/content/notebooks/notebook-schema.ts` (#48813, #48815).
- Points `Notebook.content` and `notebooksState.updateCells` at the
canonical `Notebooks.Content` / `Notebooks.Cell` types (via `@/types`)
instead, and fixes the handful of call sites that constructed notebook
content by hand to match the real wire shape: `schema_version: 1` (not
`'1.0'`) and `_tag`-discriminated cells (e.g. `{ _tag: 'markdown_cell',
id, text }` instead of `{ type: 'markdown', content }`).
- No behavioral changes — Joshen's state management, editor component,
and hooks are untouched aside from the type-level fixes needed to
compile against the canonical schema.

## Test plan
- [x] `pnpm exec tsc --noEmit` — no new errors
- [x] `pnpm exec vitest run state/notebooks/notebooks-state.test.ts
components/interfaces/Explorer/__tests__/NotebookEditor.test.tsx` — 8/8
passing
- [x] `pnpm exec eslint` on changed files — clean
- [x] `pnpm exec prettier --check` on changed files — clean

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

## Summary by CodeRabbit

* **Bug Fixes**
* Updated notebook content handling to use the current schema version
format.
* Improved compatibility for markdown cells, including their identifiers
and text.
* Standardized notebook content and cell updates for more consistent
behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-10 13:39:02 -04:00
CharisandJoshen Lim 957e9fec67 feat(studio): notebook content at the API boundary (#48815)
## Summary

Stacked on #48813 (1.2: notebook content schema). Part of
[FE-4109](https://linear.app/supabase/issue/FE-4109/notebooks-data-model)
— see that issue for the rest of the notebooks data-model stack.

- Teach `content-remap.ts`'s wire↔domain dispatcher about the `notebook`
content type, branding each cell's `sql` per `_tag` via the notebook
schemas added in 1.2 (parses through `notebookDomainSchema` on the way
in, unbrands per cell on the way out).
- Add `{ type: 'notebook'; content: Notebooks.Content }` to the
`Content` union in `content-query.ts`, plus a `ContentOfType<T>` helper
for narrowing it.
- Fix the resulting narrowing fallout at call sites that assumed
`Content` only ever meant `sql`/`report`/`log_sql`: two
generated-query-param casts, and four report/logs call sites now
narrowed via `ContentOfType<'report'>` / `ContentOfType<'log_sql'>`.

## Test plan

- [x] `pnpm --filter studio vitest run data/content/` — 35 tests pass,
including new notebook coverage in `content-remap.test.ts` (per-cell
brand separation, missing-field throw, remap↔unmap round-trip)
- [x] `pnpm typecheck` — clean (pre-existing unrelated `ui-patterns`
error aside)
- [x] `pnpm --filter studio lint` — no new warnings/errors on changed
files
- [x] `pnpm format` — clean

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-10 10:15:24 -04:00
CharisandJoshen Lim 0b97e37ccf feat: notebook content schema (#48813)
Related to FE-4109.

## Summary

- **API codegen workaround**: Platform API's `notebook` content type
hasn't shipped to the OpenAPI spec yet, so `pnpm api:codegen` can't be
run. Locally widened `ContentBase.type` to include `'notebook'` (marked
with TODO for removal once spec publishes).
- **Notebook schema & type system**: Introduced Zod schemas mirroring
RFC-defined notebook shape (`schema_version: 1, cells: Cell[]`).
Maintains wire/domain boundary (cell `sql` → `unchecked_sql` branded for
security). Agent-writable schema for `create_notebook` tool omits cell
IDs (backend-generated); future update operations will require them. All
TypeScript types are `z.infer`'d from schemas (no hand-written parallel
interfaces).
- **IsoDateTimeString moved**: Extracted ISO datetime validator from
`querySource.ts` to `lib/iso-datetime.ts` (data layer shouldn't import
from components layer). Needed by notebook `time_range` fields.

## Test plan

- [x] Unit tests: `notebook-schema.test.ts` (9 tests),
`iso-datetime.test.ts` (3 tests), `querySource.test.ts` updated and
passing (26 tests)
- [x] Typecheck: no new errors
- [x] Prettier: formatting clean

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

## Summary by CodeRabbit

- **New Features**
- Added support for validating and processing notebook content,
including markdown, database, log cells, time ranges, and chart
configurations.
  - Added compatibility for notebook content types in content handling.
- Added reliable ISO date-time validation for notebook data and related
features.

- **Tests**
- Expanded coverage for valid and invalid notebook structures, cell
requirements, time ranges, chart settings, and date-time values.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-08-07 13:40:18 +07:00
Charis 8831e15fc3 Revert "refactor(studio): static tab kind registry and tab id codec (PR 1/9)" (#48762)
Reverts supabase/supabase#48718
2026-08-05 16:03:16 +00:00
Charis ffd6d6636d refactor(studio): static tab kind registry and tab id codec (PR 1/9) (#48718)
## Summary

First PR in the tab/snippet decoupling stack. Purely additive: no
existing call sites change behavior except the `EntityTypeIcon` rewire,
which preserves its API exactly.

- **`state/tabs/kinds.ts`** — static `TAB_KINDS` descriptor table
(`TabKind`, `TabSurface`, `surfaceOf`, `kindsOnSurface`, `isTabKind`).
Leaf-safe: its only reference to the domain `Tab` type is a type-only
import, so it stays importable at module scope (needed later for the
valtio store and the persistence migration reader) without creating a
runtime cycle.
- **`state/tabs/kinds.icons.tsx`** — per-kind icon leaf module (`ui` +
`lucide-react` + `TabKind` type only), plus the preserved
`LogsSnippetIcon`.
- **`state/tabs/tab-id.ts`** — `createTabId` / `parseTabId` /
`toUrlSegment` / `parseUrlSegment(segment, surface)` codec.
`parseUrlSegment` is surface-scoped: a bare segment only resolves to a
kind when the surface has exactly one bare kind (true for `sql`). The
table surface has five bare kinds (`r`/`v`/`m`/`f`/`p`) with no URL
disambiguator between them, matching `/editor/[id]`, which learns kind
from the fetched entity rather than the URL — so a bare table segment
correctly resolves to nothing.
- **`components/ui/EntityTypeIcon.tsx`** — rewired to a thin wrapper
delegating to `kinds.icons.tsx`, preserving its exact prop API (`type`,
`size`, `strokeWidth`, `isActive`, `sqlSource`) for all existing
consumers.
- **`state/tabs/tab-id.test.ts`** — codec round-trips per kind,
bare-vs-prefixed URL segments, the `templates`/`examples`/`new`
sentinels, and surface scoping (including the table-surface ambiguity
above).

Full plan: `apps/studio/TABS_DECOUPLING_PLAN.md` (not included in this
PR).

## Test plan

- [x] `pnpm typecheck`
- [x] `pnpm lint --filter=studio` (0 errors)
- [x] `pnpm --filter studio run lint:ratchet` (warning counts did not
increase)
- [x] `pnpm test:studio` (full suite green, including 50 new/updated
tests in `state/tabs/`)
- [x] `pnpm format` (no-op)

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

## Summary by CodeRabbit

* **New Features**
* Added consistent icons and metadata for SQL, notebook, chat, table,
view, and related tab types.
* Added support for creating, parsing, and converting tab identifiers to
URL segments.
  * Improved handling of tab types across SQL and table surfaces.

* **Bug Fixes**
* Invalid, empty, or ambiguous tab identifiers and URL segments are now
rejected.

* **Tests**
* Added coverage for tab identifiers, URL conversion, supported tab
types, and edge cases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-05 10:39:58 -04:00
Charis 2165746784 fix(studio): keep SQL editor source menu open when switching sources (#48715)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

Selecting Database/Logs in the SQL Editor's query-source dropdown closes
the menu (Radix's default select behavior), so switching to Logs gives
no visible indication that a Time range control just became available
until the dropdown is reopened.

## What is the new behavior?

Selecting a source keeps the dropdown open, so the newly-available
source-specific controls (e.g. Time range for Logs) are immediately
visible.

## Additional context

Fixes FE-4036

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved source switching in the SQL editor so the selection menu
remains open while changing between database and logs sources.
  * Ensured source-specific controls update correctly after switching.

* **Tests**
* Added coverage for source selection, menu behavior, and
source-specific control updates.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 20:23:12 +00:00
Charis 5986fecc89 fix(studio): use table + card component in log retention upgrade prompt (#48713)
## Summary
- Refactor the SQL Editor / Logs "Log retention" upgrade-prompt dialog
to use the design system's `Card` + `Table` components instead of a
custom flexbox-div table, matching the pattern in
`apps/design-system/registry/default/example/table-demo.tsx`
- No behavior or data change

Fixes FE-4034

Non-blocking review nit from #48452 (comment:
https://github.com/supabase/supabase/pull/48452#issuecomment-5128918096)

## Test plan
- [x] `pnpm --filter studio typecheck` passes
- [x] `pnpm --filter studio run lint:ratchet` passes (no new warnings)
- [x] Prettier check passes

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

## Summary by CodeRabbit

* **Style**
* Updated the upgrade prompt with a more consistent card and table
layout.
  * Preserved existing plan names and log retention details.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 16:01:14 -04:00
Charis b9053794a0 fix(studio): add tooltip explaining why Prettify SQL is disabled (#48712)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

In the SQL Editor, the Prettify SQL action (both the "More actions"
dropdown menu item and the toolbar button) is disabled for logs
snippets, but gives no indication why.

## What is the new behavior?

The disabled Prettify menu item now uses `DropdownMenuItemTooltip` and
the disabled Prettify toolbar button uses `ButtonTooltip`, both showing
"Can only prettify SQL queries" while disabled. Addresses review
feedback from #48452 (Linear FE-4038).

## Additional context

Resolves FE-4038

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

## Summary by CodeRabbit

* **Improvements**
  * Updated SQL editor tooltips with clearer, consistent messaging.
* Log-source users now see an explanation when SQL formatting is
unavailable.
  * Regular users continue to see the SQL prettify keyboard shortcut.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 18:07:08 +00:00
Charis 89010c1649 fix(studio): show selected custom time range in SQL editor time range menu (#48709)
## What

Display the actual selected custom date range in the Time Range
dropdown's secondary text, instead of the static "Custom range" label.
The range is formatted as `DD MMM, HH:mm - DD MMM, HH:mm` to match the
existing format in the Logs Explorer's date-picker trigger button,
ensuring visual consistency across the Logs UI.

Fixes FE-4035

## Test plan

- [x] Typecheck passes: `pnpm typecheck`
- [x] Manually verify: Open SQL Editor with `sqlEditorLogsSource` flag
enabled, open the Time Range dropdown menu, select a Custom range, and
confirm the dropdown's secondary text now displays the selected date
range instead of "Custom range"

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

## Summary by CodeRabbit

* **New Features**
* Custom time ranges now display formatted start and end timestamps for
clearer time selection.
  * Preset time ranges continue to show their existing helper text.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 13:26:02 -04:00
Charis 0791b04eb8 feat(sql-editor): roll out manual saving by default (#48706)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature — progresses the SQL Editor manual saving rollout, and removes a
fully rolled out feature flag.

## What is the current behavior?

Manual saving (snippets save only on an explicit Save / Cmd+S rather
than autosaving every edit) is opt-in. It requires both the
`sqlEditorManualSave` ConfigCat flag and the user turning on the
"Disable snippet auto-saving" feature preview themselves. That flag is
now fully rolled out.

## What is the new behavior?

- `sqlEditorManualSave` is removed, so the feature preview is listed for
everyone. (Delete the flag in ConfigCat after a few months.)
- New `sqlEditorManualSaveForced` flag progresses the rollout. It forces
manual saving on regardless of what the user stored previously,
including an explicit opt-out, via a new `isForced` field on
`FeaturePreview` that `initializeFlags` resolves ahead of the
localStorage lookup — so the feature preview modal reflects the forced
state too, not just the save behavior. Turning the flag off reverts
everyone who never opted in, so it remains a working kill switch.
- Users the rollout switches over get a one-time dialog on their first
SQL Editor route, explaining that snippets no longer autosave. Dismissal
persists in `sql-editor-manual-save-notice-dismissed` (allowlisted, so
it survives sign-out).
- Users who opted into the preview themselves never see the dialog — it
records their dismissal up front instead, since the notice needs to
outlive the feature preview and once the preview is retired there's no
stored opt-in left to recognize them by.
- The preview keeps its switch so users who lose their local storage can
opt in early, but once the rollout reaches them the "Disable feature"
button is disabled with a tooltip explaining why.

### To test

Turn on `sqlEditorManualSaveForced` on via the dev toolbar.

- No `supabase-ui-sql-editor-manual-save` and no dismissal key → dialog
appears on `/project/<ref>/sql`, toolbar shows the Save button. Dismiss,
reload → no dialog.
- `supabase-ui-sql-editor-manual-save` set to `false` (previously opted
out) → still forced onto manual saving, and still gets the dialog.
- `supabase-ui-sql-editor-manual-save` set to `true` → no dialog, and
the dismissal key is written automatically.
- Flag off, no opt-in → autosave, no dialog, and the "Disable autosave"
power-off button still opens the preview modal.
- Preview modal while forced → "Disable feature" is disabled with a
tooltip; another preview (e.g. Column-level privileges) still disables
normally.

## Additional context
2026-08-04 12:16:18 -04:00
Charis 21511042a3 feat(studio): assistant logs context and reports guard (#48514)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature — final PR (9/9) of the SQL editor logs-source stack.

**Base branch:** `charislam/sql-editor-inline-ai-clickhouse-dialect` (PR
8). Nothing here is user-visible: entry points stay behind
`sqlEditorLogsSource` + `otelLegacyLogs`, and flag rollout happens after
the whole stack merges.

## What is the current behavior?

- The Assistant has no idea a SQL editor snippet targets the logs
backend. Ask it about a logs snippet and it answers in Postgres, because
the attached query is fenced as ` ```sql ` and nothing tells the model
otherwise.
- Because the `sql` fence is what `MessageMarkdown` treats as runnable
Postgres, an attached ClickHouse query is rendered with a
Run-against-Postgres affordance and branded with `untrustedSql`.
- "Debug with Assistant" on a failed logs query produces a dialect-less
prompt, so both the in-app assistant and the copyable version get
debugged as Postgres.
- A report referencing a `log_sql` snippet runs its ClickHouse SQL
against the user's Postgres database and surfaces the resulting error.

## What is the new behavior?

**Assistant panel.** The "Current Query" chip records which backend the
attached query targets. That reaches the model two ways: each attachment
is fenced with its own dialect (` ```clickhouse ` vs ` ```sql `), and a
`containsLogsSnippets` flag rides on the user message as AI SDK
`metadata`. The server reads the flag off the conversation and prepends
the ClickHouse dialect rules plus the logs schema reference as a
non-cached context message.

Two design points worth calling out in review:

- The flag lives on the **message**, not the request body, so Retry and
the tool-approval continuation reproduce the context a message was
originally asked in — neither of those passes a per-call body.
- It's derived from **what's actually attached**, so detaching the chip
drops the claim rather than leaving the two able to disagree.

The `clickhouse` fence also keeps a logs query out of
`MessageMarkdown`'s `sql` branch, so it's no longer offered as runnable
Postgres or branded with `untrustedSql` — a boundary this stack's
distinct brands exist to prevent crossing.

**Debug flow.** `buildDebugChatArgs` attaches its query with a source
for the same reason, and names the dialect in the prompt text so the
copyable version stands on its own outside the app.

**Reports.** A report only stores a snippet id, so whether it queries
the logs backend is only knowable once the content loads. `ReportBlock`
guards on the fetched type and renders a `LogsSnippetReportBlock`
placeholder instead of executing. Double-guarded: no `sql` for a logs
snippet (so it's out of the query key and `queryFn` short-circuits even
on an explicit `refetch`) and `enabled` excludes it.

**Incidental cleanups.** `buildAssistantContextMessages` extracted out
of `generate-assistant-response`; a schema-access sentinel that was
duplicated as a string literal across two files (and compared against)
replaced with one exported constant; `SqlSnippet` deduplicated to a
single declaration; `resolveSnippetSource` / `isLogsSource` shared
instead of re-implemented per surface.

**Tests.** 4 new/extended suites. Notable cases pinned: a message with
no metadata must validate (`safeValidateUIMessages` applies
`metadataSchema` to *every* message, so a required schema would 400
every existing conversation); only *user* messages count, so a model
reply can't talk the server into a different dialect; a mixed-attachment
message is flagged without overclaiming a single source; and
`ReportBlock` registers no pg-meta mock for the logs cases, so an
unhandled request failing the test *is* the assertion that logs SQL
never reaches Postgres.

Verified: `pnpm typecheck`, `lint:ratchet` (no regression), Prettier,
and the full Studio suite (459 files / 4969 tests).

## Additional context

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

## Summary by CodeRabbit

- **New Features**
- Added support for recognizing log snippets in reports, with clear
guidance to open them in the SQL editor or remove them.
- AI Assistant now understands log snippets and provides
ClickHouse-specific context, formatting, and troubleshooting guidance.
- Snippets retain their source information when shared with the AI
Assistant.

- **Bug Fixes**
- Prevented unsupported log snippets from being executed as regular
database queries.
  - Improved source detection when opening snippets directly from links.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 09:02:40 -04:00
Charis 8b38e0d1ed feat(studio): ClickHouse dialect for logs snippet AI + rewrite to ClickHouse (#48501)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature, plus a refactor of the shared logs-rewrite flow.

PR 8 of the SQL editor query-source series. Stacked on #48457 — review
that one first, and merge this after it.

## What is the current behavior?

A `log_sql` snippet runs against the ClickHouse-backed analytics
endpoint, but the SQL editor's AI still writes Postgres: inline edits
get Postgres system prompts, and the result is run through
`sql-formatter`, which mangles ClickHouse backticks and `log_attributes`
map lookups.

Legacy Logs Explorer saved queries open in the editor as `log_sql`
snippets. Those are BigQuery dialect and error against the ClickHouse
endpoint the editor runs them on, with no in-editor way out — only the
Logs Explorer offered a rewrite.

The completion route was also asymmetric. It assembled a
schema/code/instruction message for Postgres but forwarded `prompt`
verbatim for ClickHouse, so a client wanting ClickHouse had to
hand-build the equivalent string.

## What is the new behavior?

**Inline AI speaks ClickHouse for logs snippets.** `sqlSourceToDialect`
maps a snippet's source to `postgres`/`clickhouse` and
`buildCompletionRequestBody` threads it through. For ClickHouse,
`useSqlEditorAi` strips code fences from the response and skips
`formatSql`. Execution and dialect both follow the snippet type, so a
snippet's valid dialect never flips.

**Rewrite to ClickHouse in the editor.** A banner offers the rewrite for
a logs snippet whose text trips `looksLikeLegacyLogsQuery`, and proposes
the result through the editor's existing AI diff view rather than
replacing the snippet, so it's accepted or discarded like any other AI
edit. Gated on `otelLegacyLogs`: on a non-migrated org the BigQuery text
is still correct, so rewriting it would break a working query.

The offer is a state machine (`offered` / `rewriting` / `failed` /
`noRewriteNeeded` / `dismissed`) with a declarative table of valid
transitions, so the states are mutually exclusive by construction and
dismissal is terminal. A failure keeps its message and offers a retry; a
response identical to the input is reported rather than opening an empty
diff.

**One place assembles completion prompts.** The route now uses a single
template for both dialects, branching only the schema section and — for
`intent: 'rewrite'` — the instruction. `lib/ai/clickhouse-logs.ts` is
the single home for ClickHouse-logs prompt content, replacing two
independently maintained descriptions of the same table. Clients carry
no prompt text.

**The rewrite flow is shared with the Logs Explorer.** Both surfaces
previously hand-rolled the same sequence and had drifted: only one
detected a no-op rewrite, they sourced `log_attributes` keys
differently, and the Explorer formatted errors with an `as Error` cast.
Both now use `useLegacyLogsRewrite` and the same state-driven banner, so
the Explorer picks up no-op detection and typed error extraction.

**Attribute keys are fetched on submit, not while typing.** The detected
source would otherwise feed a reactive query key, making every edit that
changed it cost another network call. `useLogsAttributeKeys` is
imperative and goes through `queryClient.fetchQuery`, so a source
already cached — including by the Explorer header and query panel, which
subscribe reactively — is reused. This also closes a gap where inline
edits never received keys at all, unlike full rewrites.

`getErrorMessage` gains an optional typed fallback and no longer
stringifies a bare object into `'[object Object]'`; every existing
caller already hand-rolled a fallback, except `QueueSettings`, which
interpolated the raw result and now passes one.

Nothing here is user-visible until the `sqlEditorLogsSource` flag is
enabled.

Tests: dialect selection and request-body shape, the ClickHouse prompt
content (including that the schema section does not restate the dialect
rules), the reducer's valid and invalid transitions,
`shouldOfferLegacyLogsRewrite`, on-submit key discovery with cache
reuse, and `getErrorMessage`.

## Additional context

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

## Summary by CodeRabbit

* **New Features**
* Added an Assistant banner to help rewrite legacy BigQuery-style logs
queries into ClickHouse SQL.
* SQL assistance now adapts to the selected query type, including
relevant log attribute context.
* Rewrite suggestions can be reviewed as editor diffs before being
applied.

* **Bug Fixes**
* Improved rewrite failure handling, retry options, dismissal behavior,
and “no rewrite needed” messaging.
* Error notifications now provide a clearer fallback message when
details are unavailable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 09:02:40 -04:00
Charis b3c5c9fc04 feat(studio): logs snippets in SQL editor nav, search, and tabs (#48457)
## What

PR 7 of the SQL-editor query-source (Database vs Logs) stack. Surfaces
`log_sql` snippets as a distinct query source across the SQL editor
sidebar. Stacked on **`charislam/toolbar-ui-creation-flow`** (PR 6 —
toolbar UI + creation flow); review/merge that first.

Nothing is user-visible until the flags roll out — every entry point
requires **both** `sqlEditorLogsSource` **and** `otelLegacyLogs`.

## Changes

- **Nav** — a flag-gated **Logs** section (`LogsSnippetsSection`) backed
by its own single-type `log_sql` query. The active snippet is injected
only into the section it belongs to, via a shared
`withActiveSnippet(snippets, active, belongsPredicate)` helper (also
DRYs the private/favorites/shared injections).
- **Search** (`SearchList`) — a **Logs** result group with a shared,
extracted `SqlSnippetTree`; the "N results found" count now sums
database + logs, with loading/empty states covering both queries.
- **Tabs** — an immutable `sqlSource` field on tab/recent-item metadata
(set at tab creation, lazily backfilled once the snippet loads via
`useEffectEvent`), and a distinct `ScrollText` icon via a shared
`LogsSnippetIcon`. Tab cleanup treats `log_sql` tabs as live and only
prunes them when logs data is authoritative (`canPruneLogsTabs`), so a
disabled/erroring logs query never wrongly deletes logs tabs or blocks
database-tab cleanup.
- **Data layer** — `useSqlSnippetsQuery` gains an optional `type` param
so logs reuse the same `SnippetWithContent` shape as the other sections
(no casts).

## Tests

- `state/tabs.test.ts` — `sqlSource` backfill + creation-time
carry-through.
- `components/layouts/Tabs/Tabs.utils.test.tsx` — cleanup prunes stale
database/logs snippets, keeps live ones, and preserves logs tabs when
logs data isn't authoritative.

## Verification

- `pnpm --filter studio typecheck` ✓
- `pnpm --filter studio run lint:ratchet` ✓
- `pnpm test:studio` (affected suites) ✓

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

* **New Features**
* Added a collapsible Logs section to the SQL editor sidebar for
browsing, sorting, selecting, renaming, and deleting log queries.
* Expanded SQL search with separate, paginated results for database and
log queries.
* Added dedicated log-query icons across navigation, tabs, previews, and
recent items.
* **Bug Fixes**
* Improved tab and recent-item cleanup while preserving active log
queries and accurate source metadata.
* **Tests**
* Added coverage for log tab cleanup and SQL source metadata
synchronization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-04 09:02:39 -04:00
Charis ec64135f9d perf(pg-meta): scope column privileges query to a single table (#48553)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Performance improvement

## What is the current behavior?

The column privileges page in Studio only ever renders one table, but
the underlying query still `aclexplode`s every column in the whole
schema and filters the result client-side.

## What is the new behavior?

Adds a scoped SQL path that prunes `pg_class`/`pg_namespace` to the
requested schema+table before exploding ACLs, gated behind the
`pgMetaScopedIntrospection` flag, with a plan-guard test asserting
`pg_class`/`pg_attribute` stay index-driven. Studio's query hook and
cache keys now thread the selected table through so column-privilege
invalidation and cold-load races are scoped correctly, and the page
fetches per-table instead of per-schema.

## Additional context

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

## Summary by CodeRabbit

* **Improvements**
* Column privileges are now scoped to the selected schema and table for
more accurate results.
* Changing schemas automatically updates the table selection and
refreshes the displayed privileges.
* Privilege updates now refresh only the relevant schema, table, and
column data.
* Loading states are handled more accurately when no table is selected.

* **Bug Fixes**
* Improved consistency between scoped and unscoped column privilege
results, including table-, column-, and grant-option privileges.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 15:26:37 -04:00
Charis 50e1eb7436 chore(eslint): bump eslint-config-next to v16 for useEffectEvent (#48458)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Chore / build (ESLint config upgrade + lint cleanup).

## What is the current behavior?

`eslint-plugin-react-hooks` v5 (pulled in transitively by
`eslint-config-next` v15) doesn't recognize stable `useEffectEvent`, so
every effect that calls an effect-event handler needs an `eslint-disable
react-hooks/exhaustive-deps` to silence a false positive. There are 30
such dead disables across Studio.

## What is the new behavior?

Bumps `eslint-config-next` to v16, which pulls in
`eslint-plugin-react-hooks` v7 whose `exhaustive-deps` understands
`useEffectEvent`, and removes the 30 now-dead disable directives (and
their orphaned explanatory comments).

Supporting changes:

- **Flat-config migration**: v16 is a native flat-config array (v15 was
eslintrc), so `eslint-config-supabase` now spreads it directly instead
of bridging through `FlatCompat`.
- **React Compiler rules off**: v16 enables react-hooks v7's
`recommended`, which layers the React Compiler lint rules on top of the
two classic rules. These are switched off (derived dynamically from what
next enables) to keep this change scoped to the `exhaustive-deps`
improvement.
- **Plugin-registration fallout** (v16 scopes plugin registration to a
file glob rather than registering globally like FlatCompat did): stop
re-registering `@typescript-eslint` (shared) and `jsx-a11y` (studio);
scope our react / react-hooks / jsx-a11y rule overrides (studio, www) to
v16's plugin glob so they don't error on files outside it (e.g. `.cjs`).
- **Lint surface preserved**: v16's glob newly includes `.mts`/`.cts`
(v15 didn't lint them), which surfaced pre-existing errors in tooling
scripts. The shared config keeps the prior surface by leaving
`.mts`/`.cts` unlinted; linting them is left as a separate change.
- **Ratchet**: rebaselines `@tanstack/query/exhaustive-deps` 9 → 89. v15
forced next's `@babel/eslint-parser` onto `.ts` files, hiding these
deps; v16 parses `.ts` with `@typescript-eslint/parser` and correctly
surfaces the intentional `connectionString`-excluded-from-`queryKey`
pattern. Worth a follow-up to review whether any are real
cache-correctness bugs.
- Drops three now-dead devDeps from `eslint-config-supabase`:
`@eslint/eslintrc`, `@eslint/js`, `@typescript-eslint/eslint-plugin`.

Verified locally: `turbo run lint` → 7/7 packages pass with 0 errors;
Studio `lint:ratchet` passes; Prettier clean on changed files; typecheck
unaffected.

## Additional context

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

## Summary by CodeRabbit

* **Chores**
* Refined linting configuration and removed outdated lint suppressions
across Studio.
* Updated Next.js linting support and refreshed related development
configuration.
  * Expanded lint baseline coverage for query-related code.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 09:01:05 -04:00
CharisandJoshen Lim c16c7e94cc feat(studio): SQL editor logs source — toolbar UI + creation flow (#48452)
## What

PR 6 of the SQL-editor "query source (Database vs Logs)" stack (builds
on the merged PR 5, #48414). Adds the user-facing toolbar surface for
the logs query source and consolidates the SQL-editor toolbar into a
single **source menu**.

Everything stays behind `sqlEditorLogsSource` + `otelLegacyLogs`
(dual-flag gated); with the flags off the toolbar is unchanged.

## Changes

- **Consolidated source menu** (`QuerySourceMenu`) — one `Database ▾` /
`Logs ▾` dropdown that both labels the snippet's source and hosts the
source-specific controls as flyout submenus:
- Database: database selector (`Primary` / read replicas), `Run as`
(role impersonation), and `Row limit`.
- Logs: `Time range` — the same relative presets as the Logs Explorer
plus a `Custom range…` calendar dialog.
- **Source is immutable** — the Database/Logs rows aren't a toggle. An
existing (materialized) snippet opens a *fresh* tab of the target source
(never reinterpreting a query against the wrong backend); a blank new
tab re-flavors in place. Extracted as the pure, unit-tested
`resolveSourceSwitch`.
- **New-snippet-with-source** threaded through `/sql/new?source=`, the
nav "Create a new logs query" entry, and the duplicate flow. Logs
snippets hide the (db-dialect) Export action.
- **Run-affordance guard** — the Run button is disabled + annotated for
a logs snippet on a non-ClickHouse org (sits above PR 5's execution
short-circuit).
- **Retention entitlement gating** — both preset and custom logs ranges
past `log.retention_days` surface the upgrade prompt instead of applying
silently. Prettify is disabled for logs (sql-formatter mangles
ClickHouse).

## Tests

- `querySource.test.ts` — `logDateRangesEqual` (structural
relative/absolute matching, incl. the "Last hour" vs "Last 1 hour" label
case).
- `QuerySourceMenu.utils.test.ts` — `resolveSourceSwitch`
push-vs-replace / no-op behavior.

`pnpm --filter studio typecheck` · `lint:ratchet` · Prettier · SQL
editor suite (307 tests) all green.

## For reviewers

To test manually, enable the `sqlEditorLogsSource` feature flag for
yourself on local/staging. There is no nav for Log SQL snippets
currently (that is by design, this PR is big enough as-is), so to check
an existing logs snippet, you can create one using the existing Logs
Explorer, copy its UUID, and force navigate to that snippet in the SQL
editor via URL.

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

## Summary by CodeRabbit

* **New Features**
* Added support for creating and switching between database and logs
queries.
  * Added log time-range presets and custom date-range selection.
  * Added database, run-as role, and row-limit controls.
* Added read-replica selection, including options to create a new
replica when available.
* **Improvements**
  * Added clearer explanations when query execution is unavailable.
* Disabled SQL formatting and query export where unsupported for logs
queries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-07-30 08:38:11 -04:00
Charis 4c8ed105d2 feat(studio): logs SQL execution wiring + source-aware run gestures (#48414)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature (SQL editor: execution wiring for logs-source snippets). Part of
the stacked SQL-editor "Database vs Logs" query-source series.

## What is the current behavior?

The SQL editor only ever runs queries against the user's Postgres
database. There is no execution path for a logs (`log_sql`) snippet, and
the run-button telemetry event carries no backend discriminator.

## What is the new behavior?

- `useRunSource(id)` derives the run backend from the snippet type; a
`log_sql` snippet resolves to `{ type: 'logs', dateRange }`, pairing the
run with its session time range (default: last hour).
- `useLogsSqlExecution` runs a promoted `SafeLogSqlFragment` against the
analytics OTEL (ClickHouse) endpoint with the resolved time range as
`iso_timestamp_start`/`iso_timestamp_end` request params. The endpoint
is **pinned to OTEL** — a snippet's dialect must not flip with org
migration.
- The run gestures (toolbar button and Cmd+Enter) branch on the source
and promote with the matching `acceptUntrusted*` right at the user
action, preserving the auditable promotion-at-gesture boundary. pg
intellisense is gated off for logs snippets.
- The `sql_editor_query_run_button_clicked` telemetry event gains a
required `{ source: 'database' | 'logs' }` property, fired from both
execution paths.
- Capability guard: a `log_sql` snippet is reachable by direct URL
regardless of the (later) entry-point flag gating, so `executeLogsQuery`
short-circuits when `otelLegacyLogs` is off — recording a clear "not
available yet" result message instead of firing a request that would
only return an opaque backend error on a non-ClickHouse project. This is
a guard on the gesture, not endpoint selection.
- Tests: `useRunSource` routing, `useLogsSqlExecution`
endpoint/range/structured-error/capability-guard, and a reusable `flags`
option on `renderSqlEditorHook`.

No UI entry points are added — the feature runs dark until the
flag-gated creation/nav PRs later in the stack.

## Additional context

Stacked on the query-source series; base branch is `master` now that PR
4 (log date range domain + session state, #48401) is merged. Follow-ups
in the stack add the toolbar/creation UI (with a run-affordance gate on
`otelLegacyLogs`), nav section, AI dialect support, and reports guard.

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

## Summary by CodeRabbit

* **New Features**
  * Added support for running log queries directly from the SQL editor.
* Log query results, errors, and time ranges are now handled within the
editor session.
* Added automatic selection between database and log query execution,
including support for custom date ranges.
* SQL assistance is disabled while editing log queries where database
definitions do not apply.

* **Tests**
* Added coverage for log query execution, date ranges, feature
availability, and execution source selection.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 10:43:48 -04:00
Charis d5436ae826 feat(studio): log date range domain + session logRange state (#48401)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature (+ a small refactor and a docs/convention note). PR 4 of the
stacked SQL-editor query-source series (Database vs Logs).

## What is the current behavior?

The SQL editor has no representation of a logs query's time range:
`querySource.ts` only knows how to map a snippet type to a source
(`getSnippetSource`), and session state (`sql-editor-session-state.ts`)
tracks results and the row limit but not a per-snippet time range. The
Logs date picker's pure range helpers (`parseCustomInput`,
`generateDynamicHelper`, the `Unit` type) are trapped inside the
`Logs.DatePickers.tsx` React component.

## What is the new behavior?

- **Logs time-range domain** in `querySource.ts`: branded
`IsoDateTimeString` + `isoDateTimeString()`, `RelativeTimeUnit`, a
`LogDateRange` discriminated union (relative/absolute),
`DEFAULT_LOG_DATE_RANGE`, a single date-picker parser
(`datePickerValueToLogDateRange` / `logDateRangeToDatePickerValue` —
handles the five presets *and* dynamic `2h`/`30m` helpers; `calcTo ===
''` means "now"; unparseable helpers degrade to absolute), and
`resolveLogRunRange` which re-resolves relative ranges against `now` at
run time (reusing the existing `ResolvedLogDateRange` shape).
- **Session state**: per-snippet `logRange` + `setLogRange` —
session-only, never written to snippet content, so it works on read-only
shared snippets and is cleaned up in `clearForSnippet`.
- **Refactor**: extracted the picker's framework-free helpers into a new
pure `Logs.datePickerHelpers.ts`; the logs domain now shares the `Unit`
type and reuses `generateDynamicHelper` instead of duplicating them.
Importers point at the new module directly (no re-export shim). Hardened
the amount parse against `NaN`.
- **Full unit coverage** in `querySource.test.ts`. Recorded the no-shim
refactoring convention in the `studio-best-practices` skill.

Verification: `pnpm typecheck` clean, lint ratchet improved, 43 tests
pass (querySource + Logs.Datepickers), Prettier clean.

## Additional context

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

- **New Features**
- Added robust Logs date-range modeling with support for relative (e.g.,
last N units) and absolute time periods.
  - SQL Editor sessions now remember log date ranges per snippet.
- **Bug Fixes**
- Safer handling of invalid or missing date inputs, with sensible
fallback to default/current time.
- **Tests**
- Added/expanded automated coverage for date-range conversion, helper
parsing, and resolution behavior.
- **Refactor**
- Centralized date-picker helper utilities for reuse across the Logs and
SQL query experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 13:49:11 -04:00
Charis fa5eb17277 feat(studio): discriminated snippet union + source-aware writes (#48313)
Stacked on #48305.

## What

PR 3 of the stacked SQL-editor query-source series (Database vs Logs).
Stacked on the PR 2 branch `charislam/log-sql-content-shape`.

Turns `SnippetWithContent` into a discriminated union on `type` and
makes all snippet writes source-aware:

- `data/content/sql-folders-query.ts`: `SnippetWithContent` is now `{
type: 'sql'; content?: SqlSnippets.Content } | { type: 'log_sql';
content?: LogSqlSnippets.Content } | { type: 'report'; content?: never
}`. `report` is kept (the content endpoints' wire type carries it) but
has no SQL content — its body is `Dashboards.Content`, loaded through
the separate `Content` union.
- `setSql` brands per type (`untrustedLogSql` vs `untrustedSql`).
- `buildUpsertPayload` persists `snippet.type` (no longer hardcoded
`'sql'`).
- `createSqlSnippetSkeletonV2({ source })` emits the matching type +
content shape with the `as any` cast removed.
- New `components/interfaces/SQLEditor/querySource.ts`:
`SqlSnippetSource` + `getSnippetSource`.
- `seedSnippet` test helper gains a `source` arg.
- New `remapWireSnippet` boundary helper in `content-remap.ts`
concentrates the single wire->domain assertion, so `content-id-query` /
`content-upsert-mutation` call sites are cast-free (no `as unknown as`).
- Collateral: query result types aligned to the union; `updateSnippet`
no longer accepts `type` (source is immutable); db-only editor read
paths narrow away `log_sql`.

## Why

Impossible-states-impossible typing: a snippet's brand follows its
content type, so logs SQL and database SQL can never cross execution
paths. No behavior change for existing database snippets.

## Testing

- \`pnpm typecheck\` — clean
- \`pnpm --filter studio run lint:ratchet\` — no new warnings
- \`pnpm test:studio\` (data/content, SQLEditor, state/sql-editor) —
passing, including new tests for \`getSnippetSource\`, source-aware
\`setSql\`, type-aware \`buildUpsertPayload\`, and both skeleton shapes.

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

* **New Features**
* Added source-aware creation for SQL editor snippets, including
log-based SQL snippets.
* Introduced backend source mapping so log snippets are treated as
log_sql.
* **Bug Fixes**
* Improved SQL retrieval/prettification so log snippets no longer use
the wrong fallback content.
* Ensured log snippets are sanitized and preserve correct type, content,
identifiers, and statuses during save/upsert flows.
* **Tests**
* Expanded unit and integration coverage for log snippet creation,
source mapping, editing, prettification, and upsert payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 12:28:36 -04:00
Charis 7743fee3ab feat(studio): log_sql content shape + remap content.sql to unchecked_sql (#48305)
## What

PR **2 of 9** in the SQL-editor query-source (Database vs Logs) stack.

**Base:** `charislam/snippet-source-typing` (#48301) — this is a stacked
PR; review/merge that one first.

Client-side rename only — **the wire format is unchanged** (the platform
API still stores and returns `content.sql`). This moves the frontend
`LogSqlSnippets.Content` field to the branded `unchecked_sql`, matching
`SqlSnippets.Content`, and hardens the remap boundary so the rename
can't silently drop saved query text.

## Changes

- **`types/userContent.ts`** — `LogSqlSnippets.Content`'s plain `sql:
string` becomes `unchecked_sql: UntrustedLogSqlFragment` (the brand
added in PR 1). Shape kept minimal: `{ content_id, unchecked_sql,
schema_version }`.
- **`data/content/content-remap.ts`** — extend
`remapSqlContentField`/`unmapSqlContentField` to `log_sql`, branding
**per type** (`untrustedLogSql` for logs, `untrustedSql` for database)
and never mixing brands. **Defensive unmap**: content missing
`unchecked_sql` is never clobbered with `sql: undefined`; a residual raw
`sql` field (a missed save-path rename) throws in development to surface
the bug loudly, while production no-ops safely.
- **Legacy Logs Explorer consumers** updated to the branded field: the
explorer save/update paths, `SavedQueriesItem`, `RecentQueriesItem`, and
the recent-queries page.
- **Two db-only write sites** that leaned on
`LogSqlSnippets.Content.sql`: `EditorPanel` now saves `unchecked_sql`,
and `MoveQueryModal` switches to the SQL-editor-specific
`getSqlSnippetById` so its content is typed as `SqlSnippets.Content` —
no narrowing or casting.

## Tests

- **content-remap**: `log_sql` remap/unmap round-trip with the logs
brand; the defensive-unmap no-op (prod) and dev throw.
- **content-upsert-mutation**: a `log_sql` payload reaches the wire as a
plain `content.sql` and the response remaps back to `unchecked_sql` (the
data-loss-critical round-trip shared by both explorer save-new and
`SavedQueriesItem` update).

## Verification

- `pnpm --filter studio run typecheck` ✓
- `pnpm --filter studio run lint:ratchet` ✓ (no new warnings)
- `pnpm test:studio` for `data/content` + `Settings/Logs` — 139 passing
✓
- Prettier ✓

Nothing is user-visible yet — logs snippet entry points arrive later in
the stack behind the `sqlEditorLogsSource` flag.

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

- **Bug Fixes**
- Improved handling of saved and recent log queries across the SQL
editor and Logs Explorer.
- Log SQL now uses `unchecked_sql` (branded as untrusted) consistently
when creating, editing, moving, and reopening queries, with correct
remapping to/from the API boundary.
- Fixed saved-query update payloads to preserve the right query content
and omit legacy fields.

- **Tests**
- Added/expanded Vitest coverage for saved log query editing, recent-log
normalization, and `log_sql` remap/upsert request/response behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 10:47:43 -04:00
Charis ec1c889349 feat(studio): logs SQL brands + execution data layer (#48301)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature (data layer only — PR 1 of the SQL-editor query-source stack;
nothing user-visible yet, no consumers).

## What is the current behavior?

The Studio SQL editor only runs queries against Postgres. There is no
type-safe brand for user-authored logs SQL and no
execution/normalization layer for running SQL against the logs/analytics
(ClickHouse) backend.

## What is the new behavior?

Pure additions, no behavior change:

- `data/logs/safe-analytics-sql.ts` — adds distinct untrusted/safe
brands for user-authored logs SQL (`UntrustedLogSqlFragment`,
`untrustedLogSql`, `acceptUntrustedLogsSql`), mirroring pg-meta's
`UntrustedSqlFragment` but kept intentionally disjoint so Postgres and
logs SQL can never cross boundaries.
- `data/logs/execute-logs-sql-mutation.ts` (new) — `executeLogsSql`
wraps `executeAnalyticsSql`, attaches the resolved time range as request
params (`iso_timestamp_start/end`, never spliced into SQL), and
normalizes to `{ rows, error? }`; `mapLogsError` normalizes the
analytics backend's structured 200-body error into the `{ message }`
shape the result pane reads; `useExecuteLogsSqlMutation` collapses
transport and 200-body errors into React Query's single `onError` path.
- Unit tests for `mapLogsError`, the brands (including compile-time
disjointness vs pg-meta brands), and safe composition.

Verification: `pnpm test:studio` (new suites, 26 passed), `pnpm
typecheck`, `lint:ratchet` (no new warnings), and Prettier all pass.

## Additional context

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

* **New Features**
* Added the ability to run user-authored logs SQL with resolved
start/end timestamps.
* Normalized query error handling so failures surface a clear message
(including sensible fallbacks) and integrates with mutation error flows
(with a default error toast when not customized).
* Introduced safety branding for logs SQL fragments, including promotion
to runnable safe SQL.
* **Tests**
* Added tests covering error normalization across multiple
malformed/empty error shapes.
* Added tests ensuring logs SQL branding preserves/accepts only the
intended types and rejects unsafe inputs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 10:26:30 -04:00
Charis 0bef8e7d90 test(sql-editor): e2e coverage + delete jsdom test + merge Results.utils tests (Steps 5-6) (#48217)
## Summary

Steps 5 and 6 of the SQL editor test refactor plan (the final two
steps).

**Step 5** — extends `e2e/studio/features/sql-editor.spec.ts` (real
browser, zero mocks) with cases that need the real Monaco editor / full
app render:
- destructive-query warning modal: confirm actually re-runs the forced
query (previously only `Cancel` was exercised)
- debug button opens the AI Assistant with the query error pre-filled

Deletes `apps/studio/tests/components/SQLEditor/SQLEditor.test.tsx` —
its logic-level cases are now covered mock-free by the Step 4 hook
tests, and its integration cases by e2e. Deleting rather than narrowing
is the honest consequence of "no mocking": every remaining assertion it
could make in jsdom requires a Monaco mock.

**Step 6** — merges
`apps/studio/tests/components/SQLEditor/Results.utils.test.ts`
(`formatClipboardValue`/`formatCellValue`) into the colocated
`apps/studio/components/interfaces/SQLEditor/UtilityPanel/Results.utils.test.ts`
(`formatResults`/`convertResultsToMarkdown`/`convertResultsToJSON`/`getResultsHeaders`/`isLargeValue`/`convertResultsToCSV`)
— both tested disjoint exports of the same source file. Deletes the
`tests/` copy.

This is the last step in the plan.

## Test plan

- [x] `pnpm --filter studio typecheck` — no new errors in changed files
- [x] `npx prettier --check` on all changed files
- [x] Ran the new/changed e2e cases locally end-to-end against a live
local stack — both pass
- [x] `cd apps/studio && npx vitest run
components/interfaces/SQLEditor/UtilityPanel/Results.utils.test.ts` —
42/42 passing after the merge

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

* **Tests**
* Added end-to-end coverage for destructive SQL query warning modal flow
before forced execution.
* Added end-to-end coverage for the AI Assistant debug flow when SQL
execution fails.
* Expanded unit test coverage for SQL editor results formatting
utilities (clipboard and cell value formatting).
* Removed the prior SQLEditor unit test suite and the older
results-formatting unit tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 16:55:17 -04:00
Charis 08c4f64c42 test(sql-editor): add mock-free hook tests (Step 4) (#48214)
## What

Step 4 of the SQL editor testability plan: **mock-free hook tests** for
the extracted SQL editor hooks, built on the Step 3 renderHook harness
(`tests/lib/sql-editor-test-utils.tsx`) — in-memory editor port + real
valtio stores + MSW. **Zero `vi.mock`.**

| File | Tests | Covers |
|------|-------|--------|
| `useSqlEditorExecution.test.tsx` | 8 | destructive-query gating
(`potentialIssues` vs. forced run), auto-limit suffixing,
connection-string → `x-connection-encrypted` header,
`onSuccess`/`onError` session-store writes, error-line highlight,
diff-open short-circuit |
| `useSqlEditorAi.test.tsx` | 7 | one-shot diff-request drain (empty vs.
non-empty editor), drain-exactly-once across remounts, accept/discard
diff, `onDebug` opening the assistant chat + debug prompt |
| `usePrettifyQuery.test.tsx` | 2 | in-place format + write-back,
diff-open no-op |
| `useSnippetIdentity.test.tsx` | 2 | generated identity + store-driven
loading state |
| `useSnippetTitleGenerator.test.tsx` | 2 | untitled-snippet naming via
the title endpoint |

Every test exercises real dependencies at the seam where they're real:
network via MSW, stores used real and reset per test, Monaco via the
in-memory editor port.

## Test plan

- [x] `pnpm test:studio -- SQLEditor` → **286/286 passing** (21 new
tests included)
- [x] `pnpm --filter studio typecheck` clean
- [x] Confirmed zero `vi.mock` in the new files


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

## Summary by CodeRabbit

* **Tests**
* Added comprehensive automated coverage for SQL query formatting,
snippet identity, and AI-generated titles.
* Added coverage for AI-assisted SQL editing, including diff acceptance,
rejection, debugging, and request handling.
* Added coverage for query execution, result persistence, safety checks,
replica selection, error highlighting, and diff-state behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 14:53:46 -04:00
Charis 4d793a708e test(sql-editor): shared renderHook harness with in-memory editor port (#48209)
## Summary

- Add `renderSqlEditorHook()` test harness that eliminates mocking
Monaco by injecting a real, deterministic in-memory editor port
(EditorController/DiffController backed by plain JS state)
- Include `createInMemoryEditor()`, `resetSqlEditorStores()`, and
`setupSqlEditorMocks()` utilities to provide isolation and mock-free
network testing via MSW handlers
- Export `CustomWrapper` from custom-render and add optional
`editor`/`diff` injection points to SQLEditorProvider (production
unaffected via null-coalesce fallback)

This is **Step 3** of an in-progress SQL editor testability refactor
(Step 2 finished EditorController/DiffController port; this harness has
no consumers yet — hook tests land in a follow-up step).

## Test plan

- [x] `pnpm --filter studio typecheck` passes (already verified)

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

## Summary by CodeRabbit

* **Tests**
* Added reusable SQL Editor test utilities for in-memory editing,
selections, error highlighting, snippets, and diff content.
* Added helpers for resetting editor state, configuring API mocks, and
rendering SQL Editor hooks in a complete test environment.
* Enabled SQL Editor providers to accept optional controller overrides
for isolated testing.
  * Exported the shared test wrapper for reuse across test suites.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 13:32:55 -04:00
Charis 3d83e026f9 refactor(sql-editor): finish EditorController/DiffController port (Step 2) (#48166)
## Summary
Step 2 of the SQL Editor testability plan.

`SQLEditorContext` already wrapped the Monaco refs and exposed a few
semantic imperative helpers (`getEditorSql`, `clearHighlights`,
`applyErrorHighlight`, `refocusEditor`, …). This finishes that
abstraction so no hook or controller touches
`editorRef.current`/`diffEditorRef.current` directly anymore — they only
call the port. The port is what will let Step 3's test harness inject a
real in-memory editor adapter instead of mocking Monaco; production
wires it to the real Monaco refs, unchanged.

- Extends the context value with two semantic controllers, backed by the
existing refs:
- `editor: EditorController` — `isReady`, `getValue`,
`getSelectionStartLine`, `getSql` (today's `getEditorSql`), `replaceAll`
(wraps the repeated `executeEdits(...)` pattern), `focus`,
`revealLineInCenter`, `highlightErrorLine` (today's
`applyErrorHighlight`), `clearHighlights`.
- `diff: DiffController` — `isMounted`, `getModifiedValue`, `setDiff`
(the diff-sync effect body), `attach` (today's `handleDiffEditorMount`).
- Migrates every touch point off raw refs onto the port:
`useSqlEditorExecution`, `usePrettifyQuery`, `useSqlEditorShortcuts`,
`SQLEditorControllers`' `readEditorSql`, and `useSqlEditorAi`'s
`acceptAiHandler`/`drainDiffRequest`/`handleDiffEditorMount`/diff-sync
effect.
- `SQLEditorEditorPanel.tsx` is intentionally left untouched — it wires
the raw refs into the real Monaco/DiffEditor React components for
rendering, which isn't decision logic to abstract.

Behavior-preserving.

## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor` (265 tests passing)
- [x] `pnpm --filter studio run lint:ratchet`
2026-07-21 16:22:47 -04:00
Charis cdc843dadd refactor(sql-editor): extract deriveSnippetIdentity, debug/completion/diff-key helpers (#48014)
## Summary
Pure-fn extraction pass across the SQL editor hooks.

- Extracts `deriveSnippetIdentity` out of `useSnippetIdentity`'s inline
id + `isLoading` derivation into `SQLEditor.utils.ts`.
- Extracts `extractDebugContext` (shared snippet/result/error
extraction) and `buildDebugChatArgs` (the `aiSnap.newChat(...)` payload
builder) out of `useSqlEditorAi`'s `buildDebugPrompt`/`onDebug` into
`SQLEditor.utils.ts`.
- Extracts `buildCompletionRequestBody` (the AI completion endpoint's
request body builder) and `planDiffRequestApplication` (the
pending-diff-request application decision: replace vs. open a diff,
depending on whether the editor is currently empty) out of
`useSqlEditorAi` into `SQLEditor.utils.ts`. The `drainDiffRequest`
effect now just applies the plan instead of branching inline.
- Extracts `resolveDiffKeyAction` out of `useSqlEditorShortcuts`'s
window-keydown Enter/Escape branch into `SQLEditor.utils.ts`.

## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor` (265 tests passing)
- [x] `pnpm --filter studio run lint:ratchet`
2026-07-21 13:47:40 -04:00
Charis 1c827c5cbb refactor(sql-editor): extract title-gen/execute-params + merge auto-limit functions (#48013)
## Summary
Part 2/6 of the SQL Editor testability follow-up, stacked on #47980 (the
analyzeQueryIssues/resolveConnectionString PR).

- Extracts `shouldAutoGenerateTitle` and `buildExecuteParams` out of
`useSqlEditorExecution`'s inline logic into `SQLEditor.utils.ts`.
- Merges `checkIfAppendLimitRequired` and `suffixWithLimit` into a
single `applyAutoLimit` function — the two were only ever called
together and re-parsed the same query twice at every call site.
`applyAutoLimit` only accepts `SafeSqlFragment` (never a plain string)
and composes the `LIMIT` suffix through `safeSql`/`literal` rather than
raw template concatenation, so the only place in the file that reasserts
the `SafeSqlFragment` brand on a derived string is the small, dedicated
`trimTrailingSemicolons` helper — removing existing terminators can't
introduce unsafe content, unlike gluing new text onto the fragment.
- Updates the two other `checkIfAppendLimitRequired`/`suffixWithLimit`
call sites (`EditorPanel.tsx`, `ReportBlock.tsx`) accordingly;
`ReportBlock` now promotes its report SQL once and reuses the result for
both its display-only auto-limit hint and its execution, instead of
promoting twice.

## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor ReportBlock EditorPanel` (239 tests
passing)
2026-07-16 17:08:26 -04:00
Charis 11fb715149 refactor(sql-editor): extract query-issue analysis + connection string resolution (#47980)
## Summary
Part 1/6 of the SQL Editor testability follow-up (extracting pure
decision logic out of the SQL editor hooks so it can be exhaustively
unit-tested without mocking).

- Extracts `analyzeQueryIssues` and `hasBlockingIssues` out of
`useSqlEditorExecution`'s inline destructive-query /
warning-modal-gating logic into `SQLEditor.utils.ts`.
- Extracts `resolveConnectionString`, deduping the
`databases?.find(...)` lookup that was duplicated verbatim in both
`useSqlEditorExecution` and `useSqlEditorExplain`.
- Behavior-preserving — same runtime logic, now unit-testable in
isolation.

## Test plan
- [x] `pnpm --filter studio typecheck`
- [x] `pnpm test:studio -- SQLEditor.utils` (159 tests passing, includes
new exhaustive-permutation cases for the three extracted functions)

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

* **Bug Fixes**
* Improved SQL safety checks before execution, including destructive
queries, unsafe updates, database-altering commands, and tables missing
row-level security.
  * Correctly recognizes tables protected by active security triggers.
* Improved database connection selection for primary and read-replica
databases.
* Preserved the ability to run queries with force enabled when safety
warnings are present.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-16 15:30:44 -04:00
Charis 4df3c86e93 feat: add admin studio link (#47976)
Add link to Admin Studio in top bar for internal use only.

Resolves FE-3864

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

## Summary by CodeRabbit

* **New Features**
* Added a conditional Admin Studio shortcut to the desktop project
header.
* The shortcut opens the relevant project in Admin Studio and is
available only when configured and enabled.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:11:46 -04:00
Charis 9e80a159b5 refactor(sql-editor): decompose into controller contexts + presentational components (decompose 6/6) (#47938)
## What

Final step (**6 of 6**) of the `SQLEditor.tsx` decomposition. Splits the
two `ResizablePanel` bodies out of the `SQLEditorContent` monolith into
presentational sub-components:

- **`SQLEditorPane`** — the editor panel: loading state, `DiffEditor` +
diff ask-AI widget, `MonacoEditor` + ask-AI widget. Reads the Monaco
refs (`editorRef`/`monacoRef`/`diffEditorRef`) from `SQLEditorContext`,
and receives the `diff`/`prompt`/`ai` controllers plus reactive state as
props.
- **`SQLEditorResults`** — the results panel: loading state +
`UtilityPanel`.

`SQLEditorContent` is now a composition root: shared-ref provider, hook
composition, and the run-query warning modal.

## Result

`SQLEditor.tsx` goes from the original **1056-line** monolith down to
**259 lines** (≈75% reduction). The remaining size over a bare ~140-line
root is the run-query warning modal, kept inline **deliberately**: its
handlers hold the `acceptUntrustedSql` promotion, which must stay at the
explicit user-action boundary in the root rather than moving into a
presentational pane.

## Behavior-preserving

The moved JSX is byte-identical aside from prop threading. No logic,
effects, dependency arrays, or `eslint-disable`s changed. The two
**render-time ref reads** — the editor placeholder (`!promptState.isOpen
&& !editorRef.current?.getValue()`) and the ask-AI widget gate
(`editorRef.current && promptState.isOpen && !isDiffOpen`) — are
preserved verbatim in `SQLEditorPane`, which re-renders whenever the
`prompt`/`diff` props change, keeping those reads fresh (the guardrail
from the plan).

Verification (all green):
- `SQLEditor.test.tsx` characterization suite — 11/11 pass
- `tsc --noEmit` — no new errors
- eslint — clean (no new ratchet entries)
- prettier — clean

## Stack

Builds on decompose 5 (#47935). This is the last PR in the series — the
decomposition is complete after this merges.


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

* **New Features**
* Added a resizable SQL editor layout with run warnings, query results,
and an Explain view.
* Introduced centralized SQL editor controllers and expanded AI-assisted
prompt/diff workflows.
  * Added keyboard support for running and Explain analysis.
* **Bug Fixes**
  * Restored editor focus after accepting or discarding AI changes.
  * Improved handling and validation of SQL Explain actions.
  * Preserved editor scroll position when switching snippets.
* **Refactor**
* Streamlined the SQLEditor into a composed layout and improved
memoization to reduce unnecessary re-renders.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:10:09 -04:00
Charis 52a25c2ebb refactor(sql-editor): extract AI/diff + shortcuts hooks (decompose 5/6) (#47935)
## What

Decompose step **5 of 6** for `SQLEditor.tsx`. Extracts the Assistant /
diff cluster and the keyboard-shortcut wiring out of the
`SQLEditorContent` monolith into two focused, individually-testable
hooks:

- **`useSqlEditorAi`** — SQL completion (`complete`), the ask-AI prompt
flow (`handlePrompt`), accept/discard diff handlers, `onDebug` /
`buildDebugPrompt` helpers, `handleDiffEditorMount`, and the fragile
diff lifecycle effects (one-shot diff-request drain, diff-editor value
sync, ask-AI widget visibility).
- **`useSqlEditorShortcuts`** — the registered shortcuts (focus editor,
new snippet, format, explain) plus the accept/discard/escape keydown
handling.

`SQLEditorContent` now composes these hooks alongside the
execution/explain hooks landed in decompose 4.

## Behavior-preserving

This is a pure extraction. The moved function bodies, effect logic,
dependency arrays, and JSX are unchanged from the previous monolith
(verified via `git diff` against the pre-decomposition source). In
particular:

- `useEffectEvent` is preserved for `drainDiffRequest` / `resetDiff`.
- `editorMountCount` remains single-owner (passed into the AI hook to
drive the one-shot drain).
- The untrusted→safe SQL promotion (`acceptUntrustedSql`) continues to
happen in the run/explain gesture and warning-modal handlers in
`SQLEditorContent`, as close to the explicit user action as possible.

The Phase-1 characterization suite (`SQLEditor.test.tsx`, 11 tests)
remains green.

## Stack

Part of the SQLEditor decomposition stack (1/6 … 6/6). Builds on
decompose 4 (execution + explain hooks, #47923). Next: PR6 splits the
JSX into panes + final cleanup.


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

## Summary by CodeRabbit

* **New Features**
* Improved SQL editor AI assistance, including completion prompts,
debugging support, and diff review controls.
* Added keyboard shortcuts for accepting or discarding AI-generated SQL
changes.
* Added shortcuts for focusing the editor, creating snippets, formatting
queries, and explaining SQL.

* **Bug Fixes**
  * Prevented SQL execution while reviewing AI-generated differences.
* Improved handling of AI diff state during editor loading and
interaction.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 15:14:12 -04:00
Charis 7832d9f716 chore: dependabot autobump gha major versions (#47924)
We were ignoring major version updates for Dependabot updates of GitHub
Actions, so we are now several major versions behind on several actions.
Turning major version updates back on so we can update everything.

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

## Summary by CodeRabbit

* **Chores**
  * Dependency update configuration now includes major version updates.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 10:52:11 -04:00
Charis 3fa7f086df refactor(sql-editor): extract execution + explain hooks (decompose 4/6) (#47923)
## Summary

PR **4 of 6** in the SQLEditor decomposition stack. Extracts the two
query-run concerns out of the composition root into cohesive hooks.
Behavior-preserving — the characterization suite stays green.

## New hooks

- `useSqlEditorExecution` — the execute mutation, the `executeQuery`
pipeline (destructive-query gating, lazy title generation, connection
resolution), and the `potentialIssues` warning-modal state.
- `useSqlEditorExplain` — the execute-explain mutation and the
`executeExplainQuery` pipeline.

Both pipelines accept an **already-promoted `SafeSqlFragment`**.

## Safe-SQL boundary

Per review guidance, `acceptUntrustedSql` promotion stays in the
**composition root's** run/explain gesture handlers and warning-modal
confirm handlers — as close to the explicit user action as possible, so
it's auditable. The hooks never call `acceptUntrustedSql`; they only
accept safe SQL. `executeQuery` lists its (now all-stable) dependencies
directly rather than suppressing `react-hooks/exhaustive-deps`.

## Verification

- `vitest` — 11 characterization tests pass
- `pnpm --filter studio typecheck` — clean for SQL editor files
- `eslint` / `lint:ratchet` — pass (0 new violations)
- `prettier --check` — clean

`SQLEditorContent` is down to ~530 lines (from 1056 at the start of the
stack). Remaining: PR5 (AI/diff + shortcuts), PR6 (JSX pane split +
cleanup).

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved SQL query execution safeguards for potentially destructive
statements, unsafe updates, and missing row-level security coverage.
* Improved EXPLAIN handling, including clearer validation for
unsupported multi-statement queries.
* Preserved query results, errors, and EXPLAIN output more reliably in
the SQL editor.

* **Improvements**
* Added clearer execution status handling and more consistent editor
focus, highlighting, and utility-tab behavior.
* Improved connection selection and SQL execution reliability across
supported database configurations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 10:37:22 -04:00