[pull] master from supabase:master - #1205
Merged
Merged
Conversation
<!-- ccr-slack-attribution --> _Requested by **Pam Chia** · [Slack thread](https://supabase.slack.com/archives/C076KTY11DF/p1788139328573799?thread_ts=1788139328.573799&cid=C076KTY11DF)_ ## 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 (telemetry). Adds one PostHog event. Linear issue: [GROWTH-1154](https://linear.app/supabase/issue/GROWTH-1154/telemetry-explorer-feature-preview-banner-has-no-exposure-event-so) ## What is the current behavior? The Explorer feature preview banner emits `explorer_banner_dismiss_button_clicked` and `explorer_banner_cta_button_clicked` (both from `apps/studio/components/ui/BannerStack/Banners/BannerExplorer.tsx`, shipped in #49606), and nothing else. With no impression event there is no denominator, so no click-through or dismiss rate can be reported. ## What is the new behavior? `explorer_banner_exposed` fires when the banner content is rendered, at most once per page load. The event is declared in `packages/common/telemetry-constants.ts` next to the two existing Explorer banner events and added to the `TelemetryEvent` union, following the existing `*_exposed` family. It carries no custom properties; `project` and `organization` groups are attached by `apps/studio/lib/telemetry/track.ts`. **Verification:** - `prettier --check` on both changed files: passing - `tsc --noEmit` in `packages/common`, which covers the new event interface and the `TelemetryEvent` union: passing - Studio-scoped lint, typecheck, and tests: green on CI - Browser-tested on the studio-staging preview (Playwright): the exposure event fires exactly once per page load (201 on the wire), does not re-fire on client-side navigation or banner hover within the same page load, fires again after a full reload, and does not fire after dismissal; the CTA and dismiss click events are unchanged and carry the `project`/`organization` groups **Out of scope:** - Pre-consent drops: every telemetry event waits for consent, so this event degrades the same way the rest of the `*_exposed` family does (transient, recovers on the next page load). A family-wide fix is a separate issue. - Mirroring the `explorer` flag state into event properties: redundant once exposure exists. - The CTA handler not dismissing the banner: raised separately, both click handlers untouched. - [GROWTH-1153](https://linear.app/supabase/issue/GROWTH-1153/telemetry-advisorcategory-omitted-for-health-lints-on-two-of-five) and its draft PR #49746: separate issue, no overlap. --- _Generated by [Claude Code](https://claude.ai/code/session_01Xwj2SotnaHByjbTfqF4Kdm); reworked per Pam's review._ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Pamela Chia <pamelachiamayyee@gmail.com>
…etry surfaces (#49746) <!-- ccr-slack-attribution --> _Requested by **Pam Chia** · [Slack thread](https://supabase.slack.com/archives/C076KTY11DF/p1788139328573799?thread_ts=1788139328.573799&cid=C076KTY11DF)_ ## 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 (telemetry correctness). No user-visible change. ## What is the current behavior? Linear: [GROWTH-1153](https://linear.app/supabase/issue/GROWTH-1153/telemetry-advisorcategory-omitted-for-health-lints-on-two-of-five) **Before:** five surfaces emit the optional `advisorCategory` property on `advisor_detail_opened` and `advisor_assistant_button_clicked`, and they disagree about how to derive it. Three pass the lint's category straight through as `categories[0]`. Two compute it with a hardcoded ladder — `categories.includes('SECURITY') ? 'SECURITY' : categories.includes('PERFORMANCE') ? 'PERFORMANCE' : undefined` — which predates the `HEALTH` category and falls through to `undefined` for anything it does not name. Because the property is optional, those two surfaces ship the event with `advisorCategory` silently absent: no type error, no runtime error, just a hole in the data. A reader querying a category breakdown of either event gets numbers that depend on which surface the user happened to click, and `HEALTH` is under-counted. The split is clearest in `AdvisorSection.tsx`, where a single advisor card emits both events — the card click through the ladder (L83) and the Assistant button through the pass-through (L206) — so one card can report two different categories for the same lint. The cause is that `AdvisorCategory` in `packages/common/telemetry-constants.ts` is schema-derived: ```ts type AdvisorCategory = components['schemas']['GetProjectLintsResponse'][number]['categories'][number] ``` The API-types regeneration in supabase/supabase #49646 (merged 2026-08-27, `26e89b36c349893540f8efbd45613921be0a4d18`) widened `categories` from `('PERFORMANCE' | 'SECURITY')[]` to `('PERFORMANCE' | 'SECURITY' | 'HEALTH')[]`. `AdvisorCategory` picked up the third value incidentally and the two ladders were never updated — a union widening is invisible to a hardcoded ladder, so nothing broke loudly. | Event | Surface | HEALTH behavior before | | --- | --- | --- | | `advisor_detail_opened` | `apps/studio/components/ui/AdvisorPanel/AdvisorPanel.tsx` (L203) | ladder → property absent | | `advisor_detail_opened` | `apps/studio/components/interfaces/ProjectHome/AdvisorSection.tsx` (L83) | ladder → property absent | | `advisor_detail_opened` | `apps/studio/components/interfaces/Linter/LinterDataGrid.tsx` (L163) | pass-through → `'HEALTH'` | | `advisor_assistant_button_clicked` | `apps/studio/components/interfaces/Linter/LintDetail.tsx` (L38) | pass-through → `'HEALTH'` | | `advisor_assistant_button_clicked` | `apps/studio/components/interfaces/ProjectHome/AdvisorSection.tsx` (L206) | pass-through → `'HEALTH'` | The two `advisorCategory` property doc comments in `telemetry-constants.ts` (L2949, L2980) also still read "Category of the advisor (SECURITY or PERFORMANCE)", which the widening made false. ## What is the new behavior? **After:** all five surfaces derive `advisorCategory` the same way, so a category breakdown of these two events is consistent regardless of which surface produced the event, and `HEALTH` is reported wherever it can occur. The two ladder sites now read `item.original.categories[0]`, matching the three sites that already did. The `signal` branch (which reports `'SECURITY'`) and the `notification` branch (`undefined`) of those two expressions are unchanged, so nothing about non-lint advisor items moves. The stale parenthetical is cut from both doc comments. Net diff is 3 files, -12/+4 lines. No behavior change outside the value of one optional telemetry property. ## Additional context **How.** The fix is the pass-through, not an extended ladder. Per the two options considered: 1. **No lint carries more than one category in practice.** Every lint fixture in `apps/studio` uses a single-element array (`['SECURITY']`, `['PERFORMANCE']`). The API type permits a multi-element array, but nothing in the repo produces one, so the ladder's SECURITY-over-PERFORMANCE priority is not load-bearing. 2. **The advisors UI already treats the first element as canonical** — `LinterDataGrid.tsx` L196 renders `<LintCategoryBadge category={selectedLint.categories[0]} />`. 3. **Extending the ladder would not actually produce agreement.** In the one reachable multi-category case, a ladder with a `HEALTH` branch appended still reports the higher-priority category while the three pass-through sites report `categories[0]`. Only `categories[0]` makes all five agree, which is the point of the change. **Reviewers should look at this first — how much data is actually affected.** Narrower than the headline suggests, and worth stating precisely. Every surface feeding these events filters lints upstream by category, and all three filters still admit only `SECURITY` or `PERFORMANCE`: - `AdvisorPanel.utils.ts` `createAdvisorLintItems` drops any lint that resolves to no tab (`if (!tab) return null`), and it is the item source for **both** ladder surfaces - `pages/project/[ref]/advisors/security.tsx` filters `categories.includes('SECURITY')` - `pages/project/[ref]/advisors/performance.tsx` filters `categories.includes('PERFORMANCE')` So a HEALTH-**only** lint is not surfaced anywhere in Studio today and cannot currently reach any of the five emit sites. The divergence reachable today is a lint carrying `HEALTH` alongside another category: it passes the filters, and then the ladder sites and the pass-through sites disagree. The HEALTH-only omission is latent, and becomes live data loss the moment HEALTH lints are surfaced — presumably the point of the API adding the category. Practical consequence: **no backfill or historical-data caveat is needed**, because no HEALTH-only event was ever emitted. This is a correctness fix that gets the emit surfaces right ahead of the category being shown, not a response to an active data incident. **How it was tested.** Honest caveat up front: `pnpm install` cannot complete in this sandbox, so the Studio-scoped checks could not be run here. `apps/studio` depends on `@std/path` → `npm:@jsr/std__path`, and the JSR registry is network-blocked in this environment (`GET https://npm.jsr.io/~/11/@jsr/std__path/1.0.8.tgz` → `403`, both direct and proxied; `registry.npmjs.org` returns `200`, so it is JSR specifically). CI on this PR is the real signal for Studio lint, typecheck, and tests. What did run clean: - `prettier --config prettier.config.mjs --check` on all three changed files — clean - `tsc --noEmit` in `packages/common` (installed via `pnpm install --filter=common...`) — clean, and `--listFiles` confirms it genuinely covers both `telemetry-constants.ts` and the widened `packages/api-types/types/platform.d.ts` - the changed expression typechecked in a standalone harness against the real generated `components['schemas']['GetProjectLintsResponse']`, confirming `categories[0]` is assignable to `AdvisorCategory | undefined` — with a negative control that correctly errored (`Type '"HEALTH"' is not assignable to type '"PERFORMANCE" | "SECURITY" | undefined'`) to prove the harness had teeth No tests are added. There is no existing test coverage of `handleItemClick` / `handleCardClick` in either ladder component, and the change is a narrowing of one expression to match three existing call sites rather than new logic. Asserting an emitted property value would require standing up component tests for two components that have none, which is a larger piece of work than this fix and better done as its own change. **Suggested follow-up, deliberately not in this PR.** `createAdvisorLintItems` and the two advisors pages filter HEALTH lints out entirely, so the category the API now returns is invisible in Studio. Whether to surface it is a product decision about a new advisor category, not a telemetry fix. Also out of scope by request: `Linter.utils.tsx` badge styling (HEALTH falling back to PERFORMANCE's badge is harmless). --- _Generated by [Claude Code](https://claude.ai/code/session_01Xwj2SotnaHByjbTfqF4Kdm)_ Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Pamela Chia <pamelachiamayyee@gmail.com>
## 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? Repo maintenance: one bullet added to the committed agent instructions in `.claude/CLAUDE.md`. ## What is the new behavior? Agent sessions working in this repo (including Slack-triggered ones) get an explicit rule that PR descriptions, issues, and code comments are world-readable, so internal content stays out of them: absolute production metrics (percentages, ratios, or relative change instead), internal decision detail (vendor, legal, pricing, or strategy discussions), and competitor names (protocol identifiers such as user-agent strings are fine). That context goes in the linked Linear issue. I added this after an agent-authored PR quoted absolute internal event volumes in its description. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added guidance to keep public PRs, issues, and code comments free of sensitive internal details. * Clarified that production metrics, internal decisions, and competitor information should be documented privately instead. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Changelog entry pages advertised a `.md` alternate tag unconditionally while the page is ISR, so an entry published in the changelog repo between www deploys pointed agents at a `.md` sibling that 404s until the next build (the static file and `CHANGELOG_PAGES` are both build-time artifacts). PR #49357 made bare-URL negotiation fail closed for those entries; I gate the advertising side here the same way. **Changed:** - **No more dead `.md` links on freshly published entries**: `getStaticProps` passes a `hasMarkdownVariant` flag computed from `CHANGELOG_PAGES` membership and the page renders the alternate tag only when true. An entry published between deploys carries no tag until the build that ships its `.md` file; the set reference stays inside `getStaticProps`, so the generated module stays out of the client bundle. - **Drift coverage**: `md-alternates.test.ts` gains the changelog direction, source-level like the existing `_app.tsx` drift test; the assertion pins the full `CHANGELOG_PAGES.has(` + backtick-`changelog/${entry.slug}`-backtick + `)` expression so a dropped key prefix fails the suite, and removing the gate fails it too. **Note:** without changelog sync secrets `CHANGELOG_PAGES` is empty, so the tag never renders in local dev. Preview and prod are the verification surface. ## To test Tested on Vercel preview: - [x] Open a published changelog entry page and view source: expect `<link rel="alternate" type="text/markdown" href="/changelog/<slug>.md">` in the head — observed exact href `/changelog/19669-supavisor-1-0.md` - [x] Fetch that href: expect 200 with `content-type: text/markdown` — observed 200, `text/markdown; charset=utf-8` - [x] (added) Client-side nav from `/changelog` into an entry: alternate tag appears with that entry's slug; hopping to a second entry updates the href (no stale tag) - [x] (added) Navigating back to `/changelog`: entry tag gone; the index shows its own pre-existing `/changelog.md` alternate (hardcoded in `pages/changelog.tsx`, outside this diff), and `/changelog.md` returns 200 `text/markdown` - [x] (added) Console: zero new errors across all scenarios vs page-load baseline ## Linear - fixes GROWTH-1120 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Changelog pages now advertise a Markdown alternate link only when a Markdown version is available. * Prevented links to unavailable Markdown content from appearing on changelog entries. * **Tests** * Added coverage to verify correct Markdown alternate detection and rendering. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem `<FormItemLayout>` does not apply item id correctly. This can be seen on https://supabase.com/design-system/docs/ui-patterns/forms: open the devtool and check the form items labels. They have no `for` attribute. This makes it harder to correctly test and is an accessibility issue. Axe devtool actually report it ## Solution When inside React Hook Form, `<FormItemLayout>` actually generate an `id` (via `<FormItem>`). However, this `id` is overridden in `<FormLayout>` and read from context by `<FormLabel>`. Ensure we use the generated id unless one was provided. Also updated the paths filters for the CI check so that any changes in either `ui` or `ui-patterns` triggers the studio unit and e2e tests. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved form accessibility by ensuring labels consistently connect to their corresponding input fields. - React-based forms now correctly preserve field-specific identifiers when associating labels with inputs. - Added support for explicitly specifying a label’s input target, improving compatibility with customized form layouts. - Updated Studio forms to use consistent control identifiers and labeling behavior. - **Quality Improvements** - Automated validation now also runs when shared UI components and patterns are updated. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary - Unify Explorer toolbar actions: 16px / 2px Lucide icons, `text-tertiary-foreground` that becomes `text-foreground` on hover (Analyze icon goes brand on hover). - Soften chat scroll edges with top/bottom fades, and align the composer width with the conversation content (`px-7` + `max-w-3xl`). - Put **Run SQL** first on Explorer home, and rename the tab-bar new-tab item from “New query” to **Run SQL** so it matches. ## Test plan - [ ] Open Explorer and check query, notebook, and chat toolbars: icons are 16px, muted by default, and go to foreground on hover. Analyze on a notebook with cells: icon goes brand on hover; empty notebook still disables Analyze. - [ ] Open a query tab: source menu, result settings, save, and more-options all look like the other toolbar actions (including while the dropdown is open). - [ ] Open Explorer chat: scroll a long thread and confirm top/bottom fades sit on the chat surface. Composer lines up with message width (not inset extra). - [ ] On Explorer home, **Run SQL** is the first card; clicking it still opens a SQL tab. - [ ] From the tab bar **+** menu, the first item is **Run SQL** (not “New query”); it still creates a SQL tab. **New notebook** and **New chat** still work. Made with [Cursor](https://cursor.com) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **UI Improvements** * Standardized Explorer toolbar icons for consistent sizing and visual weight. * Updated toolbar action colors, hover states, and keyboard-focus visibility. * Reordered Explorer home actions so “Run SQL” appears first. * Renamed “New query” to “Run SQL” in the new-tab menu. * Improved query source and settings toolbar controls. * Refined AI assistant chat layout with centered content, decorative gradients, and improved focus styling. * Added hover styling for the notebook Analyze action. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…49752) ## Summary - Match Explorer/SQL query results table typography to Table Editor: sans cells at `text-grid`, headers at `text-xs` / `text-foreground` - Reuse Table Editor `NullValue` for null cells so empty results read the same way - When the Explorer feature preview is on, the inline editor "expand" action opens a new Explorer query tab (same pattern as the assistant) instead of the SQL Editor ## Test plan - [ ] Run a query in Explorer and confirm header/cell font, size, and color match Table Editor - [ ] Confirm `NULL` cells use the same faded treatment as Table Editor - [ ] Check the SQL Editor results pane (shared `DataGridResults`) still looks correct - [ ] With Explorer feature preview on, expand the inline editor and confirm it creates an Explorer query tab with the current SQL, then closes the panel - [ ] With Explorer feature preview off, expand the inline editor and confirm it still opens a SQL Editor snippet <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## New Features - Added the ability to open SQL directly in Explorer from the editor panel. - Run SQL actions now create a query draft and navigate to the query tab. ## Style - Improved data grid readability with clearer text, left-aligned headers, truncated labels, and selectable content. - Added dedicated styling for null values. - Updated empty-results messaging with standard sans-serif text. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
## Problem Workers with an omitted runtime are displayed as Unknown, even though an omitted runtime represents a custom worker image. ## Fix Display Custom when the runtime is omitted. Preserve friendly labels for known runtimes and raw values for explicit unrecognized runtimes. ## How to test - Run `node_modules/.bin/vitest --run components/interfaces/Workers/Workers.utils.test.ts` from `apps/studio`. - Open a worker whose API response omits `spec.runtime`. - Expected result: the runtime badge displays Custom. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Worker runtimes without available metadata are now labeled **“Custom”** instead of **“Unknown.”** - Updated the related behavior validation to reflect the corrected runtime label. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What
A/B test for the plan-change side panel
(`/org/_/billing?panel=subscriptionPlan`) — gated behind PostHog flag
`pricingPanelPlanPresentation` (multivariate, 3 arms).
The current panel drops `description`, `preface`, and `footer` from
`shared-data/plans.ts` and uses a much smaller type scale than the www
pricing page, so the two surfaces look unrelated and plan differences
are hard to reason about. This experiment tests whether matching the www
style and surfacing plan gaps improves upgrade conversion.
| Variant | Surface |
| --- | --- |
| `control` | Current panel — no change (baseline cohort, still tracked)
|
| `parity` | www pricing page style: mono uppercase heading,
description, CTA above price, large mono price, preface ("Everything in
the Free Plan, plus:"), 13px features |
| `gaps` | `parity` + gap rows at the bottom showing what the plan is
missing (✗ Daily backups, ✗ Email support, dimmed ✓ 1-day log retention)
|
## Variants
Control
<img width="3520" height="2394" alt="Arc 2026-08-26 16 14 09"
src="https://github.com/user-attachments/assets/95464e83-b377-4754-85ee-c65dce0206c7"
/>
Parity
<img width="3520" height="2394" alt="Arc 2026-08-26 16 14 01"
src="https://github.com/user-attachments/assets/f50e66e2-7cbe-4e65-b1fb-083efd79ff00"
/>
Gaps
<img width="3520" height="2394" alt="Arc 2026-08-26 16 29 48"
src="https://github.com/user-attachments/assets/fbb08a12-1b76-4ce5-9247-20a6e8399be3"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a refreshed subscription plan selector with pricing,
descriptions, features, exclusions, and plan-specific messaging.
* Added upgrade and downgrade actions with loading states and
eligibility-based controls.
* Added plan comparison views highlighting missing and lower-tier
features.
* Added tailored handling for enterprise plans and supported billing
arrangements.
* Improved accessibility by respecting reduced-motion preferences during
plan highlights.
* **Tests**
* Expanded coverage for plan eligibility, feature comparisons, and
presentation variants.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: kemal <hello@kemal.earth>
## 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? Incorporating the parent PR. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restores `hooks/misc/useTrackExperimentExposure.ts`, fixing the typecheck failure on master that broke the latest Studio production deploy. The hook was deleted as dead code in #49719 (it was genuinely unused on master at the time), but #49534 was in flight and reintroduced a usage in `plan-presentation.ts`. The two PRs merged cleanly with no textual conflict, so nothing typechecked the combination until the deploy off master failed with: ``` plan-presentation.ts(5,44): error TS2307: Cannot find module '@/hooks/misc/useTrackExperimentExposure' ``` **Added:** - `apps/studio/hooks/misc/useTrackExperimentExposure.ts` — restored verbatim from before #49719; no longer dead code since `plan-presentation.ts` imports it ## To test - `pnpm --filter studio typecheck` passes (verified locally) - `pnpm knip --workspace apps/studio` no longer flags the hook (verified locally) - Studio production deploy succeeds once merged https://claude.ai/code/session_01XZGr2n1dBzJ7m3DYsGu852 Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )