Skip to content

chore: production deploy - #7004

Merged
supabase-cli-releaser[bot] merged 60 commits into
mainfrom
develop
Oct 6, 2026
Merged

supabase-cli-releaser[bot] merged 60 commits into
mainfrom
develop

Conversation

@supabase-cli-releaser

Copy link
Copy Markdown
Contributor

supabase-cli-releaser Bot and others added 30 commits October 1, 2026 07:19
This bumps realtime from v2.140.3-r0 to v2.140.6-r0.


https://github.com/supabase/slim-services/releases/tag/realtime-v2.140.6-r0

Planned after supabase/slim-services published v2.140.5-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…hotfixes (#6926)

Follows up the non-blocking review notes on #6883.

- **Required fetchers.** `RevisionIo.fetchManifest` and
`fetchProvenance` are now required, and `refreshCatalogPin` always
resolves `upstreamImage`. The serializer only accepts a pin type where
that field is required, so a rewritten `Artifacts.ts` always typechecks.
- **One update per release line.** When a line upgrades,
`planSlimUpdates` skips that line's hotfix. It emits a warning that
`--service <svc> --release <U>-r<N>` can pin the hotfix alone. Otherwise
the `slim-hotfix/<svc>[-<line>]` PR would conflict once the upgrade
merges. Separate lines, such as postgres 17 and 15, stay independent.
- **Docs over comments.** The sync script's mode docs and the
`slim-release-published.yml` header move into ADR 0026:
  - a new "Invocations" section;
- "Hotfix and upgrade pickup" now covers payload trust, the per-line
rule, line assignment and app-token handling.

Provenance, narration and over-long comments are trimmed across the
files #6883 changed.
- **Listing tests.** `services.shared.unit.test.ts` covers two `supabase
services` cases against the real catalog again:
  - the default slim listing;
  - a catalog-pinned postgres 15 override.

  The auth-only fixture stays the default for the other tests.
## Current Behavior

The Live E2E notifier works out the previous suite result by walking the
workflow run history when it runs. That lookup misfires at event time:
some failures are silently deduplicated against a failure that was never
reported, and "✅ Live E2E recovered" gets posted after runs whose
predecessor had passed.
## Expected Behavior

The notifier records the newest suite result it has applied for each
branch in a small state artifact, and decides from that record instead
of the run history:

- A failure is posted once. Repeated failures and reruns stay quiet
until a pass re-arms the alert.
- A pass posts "✅ Live E2E recovered" only when the recorded state is a
reported failure, so each alert gets at most one recovery. Other passes
only update the recorded state.
- Each record carries its source run's creation time, and results older
than the recorded one are ignored, so late or rerun events can't
overwrite newer state.
- If the recorded state can't be read, failures are still posted.
- The state is keyed by a digest of the full branch name, and only
artifacts from same-repository runs are trusted, so forks can't spoof
it. The lookup pages through the artifact listing until it finds a
trusted record.
- Suite classification runs in its own job, so only real suite results
queue on the per-branch lock that serializes state updates. The lock
group can replace a pending run; if any notifier run was cancelled since
the recorded failure, the next failure is posted instead of
deduplicated.

Each run logs the recorded state, the current result and the action
taken.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This bumps edge-runtime from v1.77.1-r0 to v1.77.2-r0.


https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.2-r0

Planned after supabase/slim-services published v1.77.2-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## TL;DR

fixes two flaky tests in the slim images e2e and the stack functions
integration suite

- `start.slim-images` served the scaffolded `functions new` template,
which pulls `jsr:` and `npm:` packages when the worker boots, so a fresh
`@supabase/server` release that was still propagating failed the boot.
the function is now dependency free and `new.stack.e2e` keeps covering
the template imports

- `serves a standalone Functions bootstrap over HTTP` ran on the 30s
default even though it can pull the edge-runtime image first. it now
gets the same 120s every other real image service test uses

## ref

- spotted in: [slim
images](https://github.com/supabase/cli/actions/runs/36699192281/job/109834573341)
[functions
bootstrap](https://github.com/supabase/cli/actions/runs/36713073189/job/109879274052)
## Summary

**TL;DR:** `cli_command_executed` now records which local backend a
command ran on (`stack_backend`: `legacy` or `stack`), which runtime its
stack uses (`stack_runtime`: `docker`, `podman`, or `native`), and
whether the project selects an OrioleDB image (`orioledb`). With these,
error rates can be compared per command between the legacy and new stack
backends (and per container or native runtime), and between OrioleDB and
standard Postgres projects, before the new stack becomes the default.

### Before

```mermaid
flowchart LR
  A["supabase &lt;command&gt;"] --> B["resolve backend<br/>(legacy for unrouted)"]
  B --> C["handler runs"]
  C --> D["cli_command_executed<br/>no backend, no engine"]
```

### After

```mermaid
flowchart LR
  A["supabase &lt;command&gt;"] --> B{"routed by<br/>experimental.stack?"}
  B -->|yes| C["StackBackendContext<br/>legacy or stack"]
  B -->|no| D["no backend"]
  C --> E["handler runs"]
  D --> E
  E --> F["config read records<br/>orioledb true/false"]
  E --> R["stack lookup records<br/>stack_runtime"]
  F --> G["cli_command_executed<br/>+ stack_backend<br/>+ orioledb<br/>+ stack_runtime"]
  R --> G
```

## Why

Command telemetry had no way to tell whether a command ran on the new
stack runtime or the legacy one, so we couldn't measure the new
runtime's adoption or failure rate. It also couldn't separate OrioleDB
projects from standard Postgres ones.

## What changed

- `resolveStackBackend` returns no backend for commands that
`experimental.stack` does not route (and for `--version`). The root
command provides `StackBackendContext` only when a backend was selected.
Handlers still default to legacy, so routing behavior is unchanged.
- `withCommandTelemetry` adds `stack_backend` when a backend was
selected. It also provides a command-scoped attribute sink, so code
below the handler can add properties to the command event.
- `orioledb` is recorded wherever local database config is resolved: the
legacy config reader, Postgres image resolution, and stack config
loading. The stack records it before rejecting OrioleDB, so failed stack
starts on OrioleDB projects are attributed. The value describes local
config only; it doesn't describe remote targets such as `--linked` or
`--db-url`.
- `stack_runtime` is recorded where a stack-backend command learns its
stack's runtime: runtime selection for new and shadow stacks, the
project stack lookup used by local database commands, and the stack
target resolver used by `stack` commands. Commands without a single
stack (stack-mode `services`, `stack stop --all`) omit it.
- Removed the unused shared `withCommandInstrumentation` wrapper and the
unused `flags_used`/`flag_values` context fields. `docs/analytics.md`
now describes the current wrapper, event shape, flag redaction and base
properties.

## Linked issue

Closes CLI-2589
Closes CLI-2590

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ss-throughs (#6953)

## Summary

A shallow-module audit of `apps/cli` found two kinds of cleanup. Each
candidate was checked with the deletion test: remove the module and
paste its body into every caller. If the callers end up no longer, the
module was only passing work through.

**Dead code.** Knip did not flag these because each file's only importer
was its own colocated unit test.

- The superseded auth layer under `apps/cli/src/shared/auth/` (api,
crypto, credentials, platform-api, token, errors). Live replacements are
`src/auth/command-*.ts` and `src/command-internal/login-*.layer.ts`.
- `shared/config/cli-project-local-service-versions.{service,layer}.ts`,
plus the two matching die-placeholders in `run.ts` and the mock in
`tests/helpers/mocks.ts`.
- `shared/output/table.ts` and `shared/output/time.ts`.

**Pass-throughs inlined or merged.**

- `db-connection.layer.ts` was a two-line alias; the sql-pg layer now
exports `dbConnectionLayer` directly.
- `OutputFormatFlag` moves into `command-internal/global-flags.ts` with
the other globals.
- The region display table is hoisted once to
`command-internal/region.format.ts` and shared by backups and projects.
- `changedLinkedLocalFlags` is called directly by seed and storage
instead of through per-command aliases; its coverage moves to the
db-target-flags unit test.
- `tracing.service.ts`, `db-bootstrap/messages.ts`,
`standalone-analytics-config.layer.ts`, `supabaseHome()`,
`output/bold.ts`, `go-string.ts`, `report.layers.ts`, and
`prepare.options.ts` are inlined at their single caller.
- `packages/cli-test-helpers` exports `harness.ts` directly instead of
through a barrel.

**Follow-up cleanups in later commits.**

- The error-tag stability snapshot
(`src/shared/telemetry/__fixtures__/error-tags.txt`) drops the four tags
that belonged to the deleted modules: `ApiError`,
`InvalidLocalServiceVersionsStateError`, `InvalidTokenError`, and
`PlatformAuthRequiredError`. None of them reached production telemetry.
- Docs that referenced the removed code are updated: the
`apps/cli/README.md` architecture list now points at `src/auth/` and
describes what remains in `src/shared/auth/`;
`apps/cli/docs/tracing-monitoring.md` no longer names a `Tracing`
service; `apps/cli/docs/code-structure.md` uses live symbols in its
naming examples; and the `db start` side-effects note points at
`start-database.ts` for the startup messages.
- `docs/self-documenting-cli.md` was an orphaned copy still describing
the long-removed `--usage` and `--skill` flags and guide templates. It
now covers only the `generate-docs.ts` script and the Fumadocs site that
exist today.

No behaviour change. Span names and telemetry command paths are
unchanged.

Linear:
[CLI-2606](https://linear.app/supabase/issue/CLI-2606/delete-dead-sharedauth-layer-and-inline-single-caller-pass-throughs-in)

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This bumps realtime from v2.140.6-r0 to v2.140.7-r0.


https://github.com/supabase/slim-services/releases/tag/realtime-v2.140.7-r0

Planned after supabase/slim-services published v2.140.7-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#6923)

Docker-backed stack tests left a named `supabase-db-*` volume behind on
every run.

Docker database storage keeps one shared volume per state root and
daemon. Stack destroy deliberately clears only the stack's namespace
inside it, because the volume is shared with other stacks and holds the
snapshot cache. Tests that run a Docker database under a temporary state
root destroyed the stack and then deleted the root. After that, nothing
could find the volume again.

This PR cleans up those volumes in one place per test run instead of in
each test:

- When `SUPABASE_STACK_TEST_RUN` is set, `DockerDatabaseStorage` adds a
`com.supabase.stack-test-run=<id>` label to the volumes it creates, and
the container runtime adds it to the containers it creates. Nothing else
changes, and nothing changes when the variable is unset.
- `packages/stack/tests/docker-volume-run.ts` is a vitest `globalSetup`
for the stack and CLI integration and E2E projects. It generates a
random id unless an outer invocation already owns one. Its teardown
removes exactly the stack-managed volumes carrying that id, without
force, and fails the run if a removal fails. It skips when Docker is
unavailable.
- Vitest workers, CLI subprocesses and detached stack owners inherit the
id. `processEnvLayer` keeps it when it replaces the environment.
- Docker test state roots must stay private to a run. The
`Commands.integration` Docker variants passed a bare temporary directory
as the database root, which made them share one never-removed volume
keyed to the parent of `$TMPDIR`. They now use `makeDockerDatabaseRoot`
like the other database service tests.
- The stack-cache E2E's own marker-based volume cleanup is removed.

- Under Bun, vitest exits on SIGINT and SIGTERM without running
globalSetup teardown, so an interrupted run used to leave its volumes
behind and its containers running. The owning setup now records a
per-user, host-local marker with its pid, and deletes it at teardown.
Each new run's setup finds markers whose process is dead and removes
that run's labelled containers, then its labelled volumes, then the
marker. Live runs, other machines sharing the daemon, and unlabelled
resources are never touched.

Volumes created before this change carry no run label and are not
cleaned up automatically.
This bumps edge-runtime from v1.77.2-r0 to v1.77.4-r0.


https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.4-r0

Planned after supabase/slim-services published v1.77.4-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary

**TL;DR:** Every CLI run can now produce one OpenTelemetry trace that
shows where time goes, down to individual migrations, containers, health
waits, API operations, and SQL statements. Export is opt-in per run
(`SUPABASE_TRACE_FILE` for a local OTLP/JSON file, or
`SUPABASE_OTLP_ENDPOINT` for Tempo/Jaeger/Grafana), costs nothing when
unset, and never exports free-form text or secrets. Two new scripts turn
traces into answers: `trace-report` (critical path, self time, repeated
spans) and `bench-cli` (fair A/B timing between two binaries, optionally
per span).

### Before

```mermaid
flowchart LR
  A["CLI run"] --> B["ad-hoc spans<br/>(uneven coverage)"]
  B --> C{"telemetry consent?"}
  C -->|yes| D["~/.supabase/traces/*.ndjson<br/>no parent ids, never read"]
  C -->|no| E["dropped"]
  B -->|--debug| F["one stderr line per span"]
```

### After

```mermaid
flowchart LR
  A["CLI run"] --> R{"SUPABASE_TRACE_FILE<br/>or SUPABASE_OTLP_ENDPOINT?"}
  R -->|neither| N["tracer disabled<br/>no-op spans, no I/O"]
  R -->|set| T["cli.run span tree<br/>commands, phases, I/O, processes"]
  T --> S["sanitizer<br/>no free text, no secrets"]
  S --> F["OTLP/JSON file"] --> TR["trace-report / bench-cli"]
  S --> O["OTLP collector<br/>Tempo, Jaeger, Grafana"]
```

### Why

Benchmarking or debugging the CLI meant adding temporary instrumentation
for each investigation. The existing tracer wrote consent-gated NDJSON
without parent span ids, so no span tree could be rebuilt, and nothing
read those files. Agents and maintainers need one consistent trace per
run that standard tools can open.

### What changed

- **Export pipeline** (`apps/cli/src/shared/telemetry/`): one sink per
run (file xor OTLP/HTTP, both is a config error); CLI-namespaced
variables so a global `OTEL_*` setup never receives CLI internals;
`TRACEPARENT` is adopted as the parent and passed to child processes;
the trace scope flushes within a bounded window before
`processControl.exit`.
- **Tracing off by default**: without a sink the root runs with
`TracerEnabled = false` and the exporter module is never loaded. Startup
and command wall time match `develop` within noise.
- **Sanitizer**: exported batches keep only structured data — exception
events keep `exception.type` (plus a structured SQLSTATE), status and
log messages are dropped, SQL text becomes `db.operation.name` and a
per-run salted hash, URLs lose query strings and storage object keys,
headers are allowlisted, resource attributes are allowlisted, trace
files are `0600`.
- **Instrumentation sweep** across commands, `command-internal`,
`shared`, `packages/stack`, and `packages/api`, following the new
conventions ADR: named spans at I/O and service boundaries, handler
phase spans, outcome annotations (counts, cache hit/miss, attempts, exit
codes), process spans with the docker verb, API spans named by operation
id, no spans per poll attempt or loop item.
- **`SUPABASE_DEBUG=1` console** keeps its behavior (spans up to depth 2
plus failures on stderr) with sanitized, length-capped attributes;
`--debug` alone still prints no spans.
- **Scripts**: `apps/cli/scripts/trace-report.ts` and
`apps/cli/scripts/bench-cli.ts` (alternating launches, isolated
environment, warm-up, untimed traced/profiled launches, `--cpu-prof`).
- **Removed** the NDJSON sink and the dead `command-instrumentation.ts`
copy of the command span.
- **Docs**: [ADR 0027](docs/adr/0027-cli-tracing-conventions.md) for
conventions and
[tracing-monitoring.md](apps/cli/docs/tracing-monitoring.md) for usage;
telemetry docs updated for the removed NDJSON files (old
`~/.supabase/traces` files are no longer written and are safe to
delete).

### Terminal captures

Recorded from a fresh corpus project against a local `grafana/otel-lgtm`
and with `vhs`; both builds are local binaries of `develop` and this
branch.

**Tempo: CLI runs, `supabase start` waterfall, cold `db diff` breakdown,
a sanitized process span, two commands grouped by `TRACEPARENT`**


![tempo-search](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/tempo-search.gif)

![tempo-start-waterfall](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/tempo-start-waterfall.gif)

![tempo-db-diff](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/tempo-db-diff.png)

![tempo-span-attributes](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/tempo-span-attributes.png)

![tempo-grouped-trace](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/tempo-grouped-trace.png)

**Terminal: debug console, traced command, trace-report, A/B bench,
per-span bench**


![term-debug-console](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/term-debug-console.gif)

![term-trace-file](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/term-trace-file.gif)

![term-trace-report](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/term-trace-report.png)

![term-bench](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/term-bench.gif)

![term-bench-trace](https://gist.githubusercontent.com/avallete/064209e6f89ba05e2e7afe473301ddbc/raw/term-bench-trace.png)

## Linked issue

None.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t mode (#6890)

## Problem

Nothing limits test growth, and agents write a test for nearly every
change. The rules against low-value tests live as prose in the "Test
quality" section of `AGENTS.md`, which agents skim past when they add or
review tests.

## Change

- Adds `.agents/skills/test-audit/SKILL.md`, which loads whenever a test
is written, changed or reviewed. It includes:
- **Authoring gate:** four questions a new test must answer before it's
added.
- **Level rule:** one primary test per contract, at the strongest
boundary that can reach it.
- **Junk-pattern checklist:** fitted to our Effect layers, handler
integration tests and `runSupabase()` e2e, including real-time waits on
production timeouts.
  - **Retention bar and regression-test rule.**
- **Read-only audit mode:** a retain / fix / consolidate / delete ledger
with one evidence line per mark.
- "Test quality" in `AGENTS.md` points to the skill and drops the
redundant-coverage bullet, which the skill now covers.
- `.claude/skills/` holds one symlink per repo skill (`effect`,
`test-audit`) into `.agents/skills/`, so Claude Code discovers them
automatically and Codex keeps reading `.agents/skills`. `.gitignore` now
ignores `.claude/*` except `.claude/skills`, and local state such as
worktrees and settings stays ignored. A new skill in `.agents/skills`
needs a matching symlink.
`types.typegen-host.integration.test.ts` › "survives a tool that exits
before reading a large stdin" intermittently hit its 5s timeout when the
machine was busy.

The typegen host writes the request to the tool's stdin and waits for
that write to finish. When the write hits backpressure, it waits for a
`drain` that never comes once the reader has exited. The only way out is
the pipe's `error` (EPIPE) event. Under load, that event can be
delivered before the listener for it is attached, so the feed hangs even
though the tool's exit code, stdout and stderr have all resolved.

The stdin feed is now raced against the child's exit, which is always
observable once the tool is gone, so the wait no longer depends on
winning that scheduling race. Under the same CPU load, the test failed 2
times in 50 before the change and 0 times in 50 after.

The stack's `CommandRunner` stdin path already races its feed against
process exit, and it stays settled when several MB of stdin are fed to a
psql that exits immediately.
`InstanceRoot.integration.test.ts` › "lets concurrent first claims of a
fresh root all succeed" failed intermittently.

`ensureOwnedInstanceRoot` claimed a fresh root by writing the owner
marker with `writeFile(…, { flag: "wx" })`. Node implements that as an
exclusive `open` followed by a separate `write`. In that window a
concurrent claimer could see the marker file already created but still
empty, compare it against its own marker, and fail with "belongs to
another instance".

The marker is now written in full to a unique temporary file beside the
root and hard-linked into place. `link` is atomic and fails with
`EEXIST` when the marker already exists, so it keeps the exclusivity of
the old exclusive create without ever exposing a partial file. Every
claimer then validates the marker that landed: concurrent claims for the
same instance succeed, and conflicting claims still fail. The temporary
file is always removed.

A new integration test covers conflicting concurrent first claims, where
exactly one may win.
…topped (#6937)

When a native service ignores its graceful stop signal past
`gracefulStopTimeout`, `NativeProcess.killProcess` fell back to
SIGKILLing the launcher and its process group. It returned without
confirming that either had exited, so `stop` could report a service
stopped while its workload still held its port or files, and a
replacement started right after could collide with it.

On macOS and Linux the fallback now kills the workload's process group
first, while the launcher is still alive to reap its direct child, then
waits for the launcher to exit. If the launcher hasn't exited within the
graceful-stop timeout, it is force-killed and its exit is awaited, so
stop always settles. Windows behaviour is unchanged.

The tests drive the timeout with `TestClock`. They prove `kill` stays
pending until the launcher's exit is confirmed, and that a launcher that
never reaps is force-killed.

Waiting on deeper detached descendants of the workload would need a
launcher protocol change and is out of scope.
…orkloads (#6952)

The Docker whole-stack E2E "eager lifecycle and reopen" failed
intermittently on its identity check
(`identity.endsWith(databaseMarker)` in
`tests/whole-stack/scenarios.ts`).

`tests/whole-stack/workloads.ts` listed Docker workloads by the
`com.supabase.stack` label alone. The container runtime's host-gateway
probe (`readProbeHosts` in `Container.ts`) is a short-lived `docker run
--rm` container that carries the stack labels but deliberately no
`com.supabase.instance` label, since it isn't an instance. When the
listing ran while the probe was still exiting, the probe's bare
container ID was taken as a workload identity. Running the probe command
in a loop while polling the same `docker ps` filter catches it
mid-flight on most iterations.

The listing now only counts containers carrying `com.supabase.instance`
or `com.supabase.stack-managed=true`, which are the labels that identify
stack workloads. The probe still removes itself, so the check that every
workload is gone at stop keeps its coverage. Product code is unchanged.
…6939)

## Summary

**TL;DR:** The upgrade notice no longer calls the GitHub releases API on
every run outside a project or on every `supabase --version`. Outside a
project the latest tag is now cached in `<SUPABASE_HOME>/cli-latest` for
ten hours, and root `--version` uses the cache like every other command.
The notice text, the `SUPABASE_NO_UPDATE_NOTIFIER` opt-out, and
project-dotenv precedence are unchanged.

### Before

```mermaid
flowchart LR
  A["Command succeeds"] --> B{"real supabase/ dir?"}
  B -- yes --> C{"--version?"}
  C -- no --> D{"cache fresh?"}
  D -- yes --> E["read supabase/.temp/cli-latest"]
  D -- no --> F["fetch GitHub, write cache"]
  C -- yes --> F
  B -- no --> G["fetch GitHub every run, no cache"]
```

### After

```mermaid
flowchart LR
  A["Command succeeds"] --> B{"real supabase/ dir?"}
  B -- yes --> C["supabase/.temp/cli-latest"]
  B -- no --> D["SUPABASE_HOME/cli-latest"]
  C --> E{"fresh and no symlink?"}
  D --> E
  E -- yes --> F["read cached tag"]
  E -- no --> G["fetch GitHub, write tag or empty backoff"]
```

### Why

The cache lived only in `supabase/.temp/cli-latest` and was written only
when `supabase/` existed, so any command run outside a project fetched
`api.github.com/repos/supabase/cli/releases/latest` before exiting. Root
`--version` always bypassed the cache. Each fetch blocked exit for up to
a 3s timeout on slow or offline networks, and unauthenticated calls
count against GitHub's 60 requests per hour per IP; repeated runs
exhaust that quota quickly.

### Performance

Median wall time over alternating `develop` / PR launches on one
machine, each build with its own `SUPABASE_HOME` (10–15 runs per row,
5–6 for the hung-network and first-run rows, one warmup). "Hung network"
is a local proxy that accepts the connection and never answers. Outside
a project, `develop` never caches, so it calls GitHub on every run; this
PR reads `<SUPABASE_HOME>/cli-latest` after the first run.

**Improved:** runs where `develop` calls GitHub and this PR reads a
cache or backs off.

| Scenario | `develop` | This PR | Change |
|---|---|---|---|
| Outside a project, `--version` | 209 ms | 133 ms | −37% |
| Outside a project, `completion zsh` | 221 ms | 142 ms | −36% |
| Inside a project, `--version` | 206 ms | 130 ms | −37% |
| Outside a project, hung network, `--version` | 3174 ms | 154 ms | −95%
|
| Outside a project, hung network, `completion zsh` | 3168 ms | 159 ms |
−95% |
| First run, hung network, `--version` | 3367 ms | 1107 ms | −67% (800ms
timeout) |

**Controls (expected unchanged):** both builds do the same work.

| Scenario | `develop` | This PR | Why |
|---|---|---|---|
| First run with an empty cache, `--version` | 241 ms | 245 ms | both
fetch once |
| Inside a project, `completion zsh` | 137 ms | 140 ms | both read the
project cache |

#### Trace

Median `UpgradeNotice.check` span duration from `SUPABASE_TRACE_FILE`
traces of alternating launches (10 runs, 5 for the first run). `develop`
has no child fetch span, so its check duration is its GitHub request.

| Scenario | `develop` check | This PR check | This PR
`UpgradeNotice.fetch` | This PR attributes |
|---|---|---|---|---|
| Improved: outside a project, `completion zsh` | 79.9 ms | 0.6 ms |
none (0/10) | `cache.location=user`, `cache.hit=true` |
| Improved: inside a project, `--version` | 76.3 ms | 0.5 ms | none
(0/10) | `cache.location=project`, `cache.hit=true` |
| Control: first run with an empty cache, `--version` | 77.4 ms | 78.4
ms | 77.1 ms (5/5) | `cache.location=user`, `cache.hit=false` |
| Control: inside a project, `completion zsh` | 1.1 ms | 1.7 ms | none
(0/10) | `cache.location=project`, `cache.hit=true` |

### What changed

- A real `<project>/supabase/` directory keeps using
`supabase/.temp/cli-latest` exactly as before.
- Otherwise the tag is cached in `<SUPABASE_HOME>/cli-latest`
(`SUPABASE_HOME` from the shell env, default `~/.supabase`), with the
same ten-hour TTL and empty-file offline backoff.
- The symlink protections apply to the new location: a symlinked
`SUPABASE_HOME` (including spellings with a trailing `/` or `/.`) or
cache file disables the cache, and the write still uses `O_NOFOLLOW`.
- Root `--version` no longer forces a fetch, so a release published
within the last ten hours may surface on a later run instead of
immediately.
- The release fetch times out after 800ms instead of 3s. A round trip to
`api.github.com` normally takes well under that; a slower fetch falls
through to the empty-file backoff, so on such networks the notice is
skipped until the next check.
- The `UpgradeNotice.check` span records `cache.location` (`project`,
`user`, or `disabled`) and `cache.hit`, and a cache miss runs the GitHub
request as an `UpgradeNotice.fetch` child span, so traces and `bench-cli
--trace` show when the check pays for a network round trip.
- `resolveSupabaseHome` accepts any `{ join }` implementation so the
hook can reuse it with `node:path`.
- The new state file is documented in `apps/cli/docs/supabase-home.md`.

## Linked issue

None.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…6957)

`slim-release-published.yml` force-pushes one branch per service release
line. When that branch's PR sits in the merge queue, GitHub rejects the
push (GH006) and the job used to fail, dropping the planned pin until
the next upstream release. On 2026-10-01 this lost edge-runtime
v1.77.4-r0: the v1.77.3-r0 and v1.77.4-r0 dispatches both hit
`slim-bump/edge-runtime` while #6930 was queued (restored by #6955).

When a push is rejected because the branch is queued for merging, the
Apply step now:

- waits for the queue to merge or drop that PR, polling `isInMergeQueue`
for up to 30 minutes and within the app token's lifetime;
- stops processing the current plan, so no remaining item is built from
the pre-merge base;
- re-sends the same dispatch so a fresh run re-plans from the updated
default branch, at most three times in a row (tracked by a validated
`client_payload.replay` counter).

The concurrency group now keeps every pending run (`queue: max`) instead
of replacing the pending one, so a replay can never cancel a newer
dispatch and its release-visibility wait. A failed queue lookup or
dispatch, a queue that holds the branch past the deadline, an exhausted
replay budget, and any other push rejection still fail the run with the
manual recovery command. ADR 0026 documents the behavior.
…ects a config change (#6944)

When `supabase start` (with `[experimental] stack`) refuses a change to
a saved stack, the error now says which setting changed and what to do
about it. It used to report internal paths such as `endpoints.http` with
a generic suggestion.

- Each rejected change is reported as the `config.toml` key, or the
`SUPABASE_*` environment variable that set it, with the saved and
requested values, after a lead-in saying the saved stack cannot adopt
them. For example: `[db] major_version: saved 17, requested 15`, or
`[api] port: saved automatic, requested 54999`.
- Changes across all affected services are reported together in one
error. A shared `[api] port` appears once.
- The suggestion offers two ways out: revert the setting to keep the
stack and its data, or run `supabase stack destroy --stack-id <id>` to
recreate it. It warns that recreating deletes the local database data.
The ID is used so the command targets this stack regardless of the
current directory. When a change can't be reverted, such as a
catalog-pinned artifact or Postgres build from a CLI upgrade, the
suggestion offers only the destroy command.
- JSON and stream-json errors carry the same details in structured form
(`stack_changes` with service, path, key, saved and requested values,
plus `recreate_command`), so agents don't have to parse prose.
`stack_changes` keeps one entry per affected service, and
`recreate_command` omits `--yes`, which a JSON-mode destroy requires.

Port settings are now defined once in `stack-config.ts`, shared by
config translation and error attribution, so a new port can't be added
to one and missed in the other. The tests use the real composition
planner.
…6941)

On the native runtime, a reopened stack intermittently failed to start
Logflare with `port 46477 already in use` / `eaddrinuse`. This showed up
in the native "eager lifecycle and reopen" stack E2E on Linux CI.

`ProcessRecipe` reserved each native backend port by binding port 0 and
releasing it before spawning the service. That port comes from the OS
ephemeral range (Linux: 32768–60999). In the gap, an unrelated outgoing
connection can take it as its source port and leave it in TIME_WAIT, and
the service's own bind then fails.

Backend ports are now reserved by `Ports.ts`, with the same
below-ephemeral scan that public auto allocation uses (ADR 0017):
- Ports claimed by any saved stack are skipped, so a backend can't take
a sticky public port that another stack will reclaim on its next start.
Backend ports are never saved as claims.
- Occupancy uses the same `accepts`-based check as public ports. A
loopback-only probe bind can coexist with a wildcard listener on macOS,
BSD and Windows, which hid ports already published by Docker.
- When an attempt loses its bind to another listener, that port is
excluded from the retry, so retries try a different port.

The window between releasing the probe and the child binding, and the
retry count, are unchanged.
…6986)

## TL;DR

fixes `status` and the `start` summary dropping `API_URL`,
`FUNCTIONS_URL` and `MCP_URL` when `[api] enabled = false`.

## what's missing?

`start` runs Kong whatever `[api] enabled` says, since that setting only
gates PostgREST.

`status` still read it as Kong's switch, so with `[api] enabled = false`
or `SUPABASE_API_ENABLED=false` it hid `API_URL`, `FUNCTIONS_URL` and
`MCP_URL` even though Kong was serving auth there.
- same class as #5332, reached
through the api setting instead of `-x postgrest`.

## now fixed by:

gating the gateway urls on Kong's own exclusion, the same way `start`
does
only `REST_URL` and `GRAPHQL_URL` still follow `api.enabled`.

## ref:

- closes: #6976
## TL;DR

fixes `stop --no-backup` leaving the
`supabase_edge_runtime_<project_id>` volume behind after a migra `db
diff` or `db pull`

## what got missed?

since the native `db diff` and `db pull` port in v2.108.0, `docker run
-v` created the migra Deno cache volume with no labels

`stop --no-backup` prunes volumes by project label, so it skipped this
one and still exited 0

## sorted by:

creating the volume w the `com.supabase.cli.project` and
`com.docker.compose.project` labels before the migra run, like `start`
and the old Go CLI did, so `stop --no-backup` removes it

it is skipped under Bitbucket Pipelines, which drops the mount

> [!NOTE]
> tho volumes already left unlabeled by older versions still stay that
way and need a one time `docker volume rm` to clear them

## ref:

- closes: #6973
- broken in: #5620
)

## Summary

**TL;DR:** `supabase stack list` prints an 8-character stack ID, but
every `--stack-id` flag rejected it and required the full 64-character
id. Every stack command now accepts the short ID from the list, or any
unique prefix of at least four characters, the same convention git and
docker use. The list table keeps its compact column.

### Before

```mermaid
flowchart LR
  L["stack list"] --> S["ID column: 8 chars"]
  S --> D["stack destroy --stack-id abcd1234"]
  D --> E["error: must be a lowercase SHA-256 stack id"]
```

### After

```mermaid
flowchart LR
  A["--stack-id value"] --> B{"4-64 lowercase hex?"}
  B -->|no| E["flags error + hint"]
  B -->|64 chars| F["exact lookup"]
  B -->|4-63 chars| D["match saved stacks by prefix"]
  D -->|none| N["not found: run stack list"]
  D -->|several| G["ambiguous: lists full ids"]
  D -->|one unreadable| I["read error surfaced"]
  D -->|one| OK["full id used"]
  F --> OK
```

### Why

Copying the ID column from `stack list` into `stack destroy --stack-id`
(or `stop`, `status`, ...) failed with `--stack-id must be a lowercase
SHA-256 stack id`, so users had to rerun the list with `--output-format
json` to find the real id.

### What changed

- The shared stack target resolver accepts a full id (unchanged exact
lookup) or a unique lowercase-hex prefix of 4 or more characters. This
covers `destroy`, `stop`, `start`, `restart`, `status`, `logs`, and
`prepare`.
- A prefix matching several stacks fails with a flags error listing
every matching full id. A prefix matching a stack whose saved state is
unreadable surfaces the read error instead of reporting it missing.
- "Not found" and malformed-id errors now point to `supabase stack
list`.
- The `status --env` pointer printed by `stack start` repeats the
resolved full id rather than the typed prefix.
- Flag help, command `SIDE_EFFECTS.md` files, and
`docs/stack-commands.md` describe prefix selection.

- The stack package's `discover` takes an `idPrefix`, so a prefix lookup
only probes the owners of matching stacks. A full id keeps the direct
lookup.

### Terminal captures

Recorded with `vhs` from source on the base commit and on this branch,
against two native demo stacks (`default`, `feature-a`) in a throwaway
`SUPABASE_HOME`. Each capture runs `stack list`, destroys `feature-a`
with the 8-character ID from the list, then lists again.

**Before:** the listed ID is rejected and both stacks remain.


![before-destroy-short-id](https://gist.githubusercontent.com/avallete/e9becd6013177475950a54a532b2bff3/raw/before-destroy-short-id.gif)

**After:** the listed ID resolves to the full id, `feature-a` is
destroyed, and `default` remains.


![after-destroy-short-id](https://gist.githubusercontent.com/avallete/e9becd6013177475950a54a532b2bff3/raw/after-destroy-short-id.gif)

## Linked issue

Closes
[CLI-2609](https://linear.app/supabase/issue/CLI-2609/stack-text-stack-list-shows-an-8-character-id-that-stack-id-rejects)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…6949)

## TL;DR

fixes disabled data api detection in the shared mapping used by `config
diff`, `config push` and `config pull`

## what's hurting the users?

the v2 project config returns `pg_pgrst_no_exposed_schemas` when the
data api is disabled, but the mapping treated any nonempty value as
enabled

`config push` started hitting this in stable v2.117.0 when it switched
from the v1 `/postgrest` read to the shared v2 mapping

## fixed now by:

`remoteDataApiDisabled` recognizes both `""` and the exact marker. 
`api.enabled` now uses that same check as `schemas`, `extra_search_path`
and `max_rows`

<details>
<summary>before/after:</summary>

with local `api.enabled = false`, the remote marker, and no unrelated
drift:

| invocation | before | now |
| --- | --- | --- |
| `config diff --exit-code` | reports false api drift; exits 2 | no
differences; exits 0 |
| `config push --yes` | sends `{"db_schema":""}` again | api config is
up to date |
| `config pull --yes` | proposes enabling the api and importing the
marker as a schema | proposes no api changes |

</details>

explicit `api.enabled = true` now re-enables the api with default
schemas
custom schemas still work. schema-only changes with enablement
undeclared require an explicit enable request...
<details>
<summary>rep:</summary>

```mermaid
flowchart TD
    off["data api disabled"]
    off --> v1["v1: empty db_schema"]
    off --> v2["v2: platform marker"]

    v1 --> legacy["v2.116.0 push"]
    legacy --> disabled["reads disabled"]

    v2 --> shared["push from stable v2.117.0"]
    shared --> before["before: nonempty means enabled"]
    before --> wrong["reads enabled"]

    shared --> after["now: empty or exact marker means disabled"]
    after --> disabled
```

</details> 

## ref:

- closes #6932
- regression introduced in #6454
Slim pickup PRs (`chore(stack): bump …` / `chore(stack): pin …`) are
opened by the release app, which cannot approve its own PRs, and they
touch files owned by `@supabase/cli`, so each one currently waits for a
manual review.

- `slim-release-published.yml` now approves each opened or rewritten PR
with `AUTO_APPROVE_PR_PAT` (skipped when the PR is already approved, so
rewrites don't stack reviews) and enables auto-merge, so a green PR
enters the merge queue on its own. The new token is unset for the sync,
format and render steps, like the app token.
- `CODEOWNERS` adds `@supabase-oss` as a co-owner of the generated
`packages/stack/src/Artifacts.ts`,
`apps/cli-go/pkg/config/templates/Dockerfile`, and
`apps/cli-go/api/v1-openapi.yaml`, so its approval satisfies code-owner
review on the slim and API-types sync PRs. Human PRs touching these
files still need a `@supabase/cli` review.
- Both workflows that approve with `AUTO_APPROVE_PR_PAT` now run in the
`auto-approve` environment, which only deploys from `develop`, so a
workflow pushed on another branch cannot approve with it.
- ADR 0026 documents the auto-approval.
…h effect lint (CLI-2593) (#6947)

## TL;DR

brings the `cli` area and `stack-constants.ts` under the effect lint

## whats introduced?

effect lint applied to `shared/cli`, `shared/stack-constants.ts` and
their tests:

- `shared/cli/**` and `shared/stack-constants.ts` allow list entries,
with the plain node npm launcher `cli/bin.ts` kept out
- `runCli` is an effect that `main.ts` runs once, instead of an async
function chaining three `Effect.runPromise` calls
- the three `Effect.provide` chains in `run.ts` are named
`Layer.provideMerge` chains in the same order, so layers build, share
and release exactly as before
- the analytics layer option lists the services it needs instead of
`any`, and `beforeParse` keeps its own error type instead of `unknown`
- `seed` applies its `--linked`/`--local` global flags after its
subcommands so they no longer leak into the root command's requirements,
and `root.ts` checks the root with `satisfies CliRootCommand` so it
keeps its real error type
- tests use `it.effect`, `runSupabaseEffect` with scoped temp dirs,
`FileSystem`, `Path` and `ChildProcess` instead of async helpers, spies
and `node:fs`, with every `as Effect.Effect` cast gone
- the hidden `db test` alias test now proves dispatch through the
handler's typed mutual exclusivity failure in a pinned env, instead of a
missing service crash

## ref:

- closes: CLI-2593
…-2592) (#6960)

## TL;DR

brings the `functions` download, delete, config and shared helpers under
the effect lint

## whats introduced?

- the `download`, `delete`, `functions-api`, `functions.shared` and
`functions-config` modules join the allow list file by file, so the rest
of `shared/functions` waits for the later PRs in this stack
- `download` runs on the `FileSystem` and `Path` services instead of
`node:fs` and `node:path`, with a posix `Path` for container and API
paths
- the temp file still opens with `wx`, renames into place and is removed
when the rename fails
- host failures keep their native error text, and the four plain `Error`
failures on the eszip path become an untagged
`FunctionEszipDownloadError` with the same telemetry
- the functions list and metadata responses decode through `Schema`, so
a malformed response now reads `Expected a valid JSON string` instead of
the parser message
- `downloadFunctions` and `deleteFunction` become `Effect.fn` with the
same span names
- integration tests cover an empty file, a malformed list or metadata
response, a mistyped list, a failed rename with temp cleanup, a failed
temp dir and the native host errors on the eszip path

## ref

- related: CLI-2592
…6968)

## TL;DR

brings `functions deploy` under the effect lint

## whats introduced?

- `deploy.ts`, `deploy.errors.ts` and the new errors unit test join the
allow list, and the `deploy` unit tests follow on the next branch
- the import map walk, source collection and docker binds run as effects
on the `FileSystem` and `Path` services, shared with `functions serve`
and `supabase start`, and `onWarning` is now an effect that `serve.ts`
passes
- source collection and the binds walk run in detached fibers that are
joined, so an interrupt still lets them finish in the background
- host failures keep their develop shape, plain `Error` failures become
an untagged `FunctionDeployError` and the import map parse error keeps
the `SyntaxError` name, so telemetry and traces stay the same
- the user's `deno.json` or import map and the list, deploy and function
responses decode through `Schema`, so a malformed document now reads
`Expected a valid JSON string`
- `deployFunctions` becomes `Effect.fn` with the same span name, `DEBUG`
reads through `Config` and dated rate limit headers read the time
through `Clock`
- tests cover the decode text and error identity, an invalid static
glob, a failed progress write, a NUL byte in an import, an uncreatable
bundle dir, both detached walks and `DEBUG` driving `--verbose`

## ref

- related: CLI-2592
…2592) (#6970)

## TL;DR

brings the `deploy` unit tests under the effect lint

## whats introduced?

- `deploy.unit.test.ts` joins the allow list
- the tests keep every existing case and run on `@effect/vitest` with
`BunServices`, building fixtures through `FileSystem` and `Path` in
scoped temp dirs and encoding JSON through `Schema`
- failure assertions read the exit cause instead of catching a
rejection, and pin both import walk failures as defects, the
`FunctionImportNotDirectoryError` and the native `ENOENT` host error
- a new test pins which entries `discoverFunctionSlugs` counts as
functions, keeping symlinked directories and skipping files, links to
files, broken links and directories without an entrypoint

## ref

- related: CLI-2592
…LI-2592) (#6971)

## TL;DR

brings the serve main bundler and its tests under the effect lint

## whats introduced?

- `serve-main-bundler.ts` and its unit and integration tests join the
allow list
- `bundleServeMainTemplate` now returns an effect, which `serve.ts`
yields and the build scripts and offline e2e run with
`Effect.runPromise`
- the template bundles in a detached fiber that `serve.ts` joins, so an
interrupted startup still fills the template cache, and an esbuild
failure stays a defect
- the bundler integration tests keep all eighteen cases and coordinate
through `Deferred` and scoped abort signals instead of manual promises,
with the fake `Deno.errors` classes as `Data.Error`

## ref

- related: CLI-2592
Comment thread .github/workflows/slim-release-published.yml Outdated
supabase-cli-releaser Bot and others added 3 commits October 6, 2026 12:55
This bumps storage from v1.79.33-r0 to v1.79.34-r0.


https://github.com/supabase/slim-services/releases/tag/storage-v1.79.34-r0

Planned after supabase/slim-services published v1.79.34-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps pgmeta from v0.99.0-r0 to v0.100.0-r0.


https://github.com/supabase/slim-services/releases/tag/pgmeta-v0.100.0-r0

Planned after supabase/slim-services published v0.100.0-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was automatically created to sync the generated `@supabase/api`
package with the latest Management API OpenAPI document.

Changes were detected in the upstream OpenAPI documents exposed by
`https://api.supabase.com/api/v1-json` and
`https://api.supabase.com/api/v2-json`.

Co-authored-by: jgoux <1443499+jgoux@users.noreply.github.com>
This bumps storage from v1.79.34-r0 to v1.79.35-r0.


https://github.com/supabase/slim-services/releases/tag/storage-v1.79.35-r0

Planned after supabase/slim-services published v1.79.35-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
supabase-cli-releaser Bot and others added 3 commits October 6, 2026 14:51
This bumps storage from v1.79.35-r0 to v1.79.36-r0.


https://github.com/supabase/slim-services/releases/tag/storage-v1.79.36-r0

Planned after supabase/slim-services published v1.79.36-r0. This branch
is rewritten from develop whenever a newer relevant release arrives.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary

**TL;DR:** The CLI now bundles `@supabase/pg-delta@1.0.0-alpha.57` and
`@supabase/pg-topo@1.0.0-alpha.8`. An unchanged declarative export no
longer makes `db` declarative sync revoke column grants.

```mermaid
flowchart LR
  exp["declarative export (unchanged)"] --> old["alpha.56: column grants dropped from grouped export"] --> rev["sync plans REVOKE"]
  exp --> new["alpha.57: column grants kept"] --> ok["sync is a no-op"]
```

## Why

alpha.57 keeps column grants in the grouped declarative export. Without
it, re-running declarative sync against an export that did not change
emits `REVOKE` statements for column-level grants. The release also
relicenses both packages under the PostgreSQL License.

## What changed

- Bump `@supabase/pg-delta` from `1.0.0-alpha.56` to `1.0.0-alpha.57`.
- Bump peer `@supabase/pg-topo` from `1.0.0-alpha.7` to `1.0.0-alpha.8`,
which alpha.57 requires.
- Point `minimumReleaseAgeExclude` at those two versions.

## Linked issue

Supabase maintainer change; no public issue to close.

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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Every PR a bot opens against `develop` (Dependabot, the release app's
sync and stack pickup PRs) is now assigned to and approved by
`supabase-oss` and set to auto-merge by a single `bot-pr-auto-merge.yml`
workflow, so green required checks and the merge queue are the only
gate.

- `bot-pr-auto-merge.yml` runs on `pull_request_target` without checking
out PR code, and acts only when both the PR author and the event sender
are Dependabot or the release app (other apps that open PRs here, such
as coding agents, still need a maintainer), the head branch is in this
repository, the PR is not a draft, and it is not a `release-notes/` PR
(those are published by a maintainer approval and must never merge). The
`supabase-oss` token stays in the develop-only `auto-approve`
environment.
- It replaces the Dependabot-only `automerge.yml` and the
approve/auto-merge steps in `api-package-sync.yml`,
`cli-go-api-sync.yml`, and `slim-release-published.yml`, none of which
need the approval token anymore.
- CODEOWNERS lists `supabase-oss` as a co-owner of only the files bots
update (workflow files, dependency manifests and lockfiles, Go modules,
service Dockerfiles, the artifact catalog, and the generated API clients
and their OpenAPI snapshots), replacing the ownerless rules there. Human
PRs touching those paths need CLI team review again, and human commits
pushed onto a bot PR that touch anything else still need it too. The AI
review and infra workflows stay maintainer-only.
@supabase-cli-releaser
supabase-cli-releaser Bot merged commit 753520f into main Oct 6, 2026
80 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do not merge Approve to apply; do not merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants