Skip to content

chore(repo): sync develop into next - #7097

Closed
supabase-cli-releaser[bot] wants to merge 33 commits into
nextfrom
sync/develop-into-next
Closed

supabase-cli-releaser[bot] wants to merge 33 commits into
nextfrom
sync/develop-into-next

Conversation

@supabase-cli-releaser

Copy link
Copy Markdown
Contributor

Merging develop into next conflicts. This branch starts at the tip of develop (a1fa3b2); next is at 2ac84c2.

Conflicting files:

  • .github/workflows/cli-go-api-sync.yml
  • .github/workflows/cli-go-mirror.yml
  • CONTRIBUTING.md
  • apps/cli-go/pkg/config/templates/Dockerfile
  • apps/cli/docs/release-process.md
  • apps/cli/src/cli/complete.ts
  • apps/cli/src/command-internal/db-bootstrap/local-container-inputs.ts
  • apps/cli/src/command-internal/db-bootstrap/shadow-cache.ts
  • apps/cli/src/command-internal/db-config.integration.test.ts
  • apps/cli/src/command-internal/db-config.parse.unit.test.ts
  • apps/cli/src/command-internal/db-config.toml-read.ts
  • apps/cli/src/command-internal/db-config.toml-read.unit.test.ts
  • apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts
  • apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts
  • apps/cli/src/command-internal/db-image.unit.test.ts
  • apps/cli/src/command-internal/debug-logger.layer.ts
  • apps/cli/src/command-internal/debug-logger.layer.unit.test.ts
  • apps/cli/src/command-internal/docker-ids.unit.test.ts
  • apps/cli/src/command-internal/experimental-gate.unit.test.ts
  • apps/cli/src/command-internal/global-flags.ts
  • apps/cli/src/command-internal/go-proxy.layer.ts
  • apps/cli/src/command-internal/go-proxy.layer.unit.test.ts
  • apps/cli/src/command-internal/local-project-context.ts
  • apps/cli/src/command-internal/local-project-context.unit.test.ts
  • apps/cli/src/command-internal/pg-dump.run.ts
  • apps/cli/src/command-internal/profile-load.ts
  • apps/cli/src/command-internal/profile-load.unit.test.ts
  • apps/cli/src/command-internal/project-environment.unit.test.ts
  • apps/cli/src/command-internal/stack-shadow-cache.ts
  • apps/cli/src/command-internal/string-slice-flag.ts
  • apps/cli/src/command-internal/supabase-env.ts
  • apps/cli/src/command-internal/viper-env.unit.test.ts
  • apps/cli/src/commands/db/branch/branch.command.ts
  • apps/cli/src/commands/db/branch/create/create.command.ts
  • apps/cli/src/commands/db/branch/delete/delete.command.ts
  • apps/cli/src/commands/db/branch/list/list.command.ts
  • apps/cli/src/commands/db/branch/switch/switch.command.ts
  • apps/cli/src/commands/db/diff/diff.command.ts
  • apps/cli/src/commands/db/dump/SIDE_EFFECTS.md
  • apps/cli/src/commands/db/dump/dump.handler.ts
  • apps/cli/src/commands/db/pull/SIDE_EFFECTS.md
  • apps/cli/src/commands/db/pull/pull.integration.test.ts
  • apps/cli/src/commands/db/remote/changes/changes.command.ts
  • apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts
  • apps/cli/src/commands/db/schema/declarative/sync/sync.integration.test.ts
  • apps/cli/src/commands/db/test/SIDE_EFFECTS.md
  • apps/cli/src/commands/functions/download/download.command.ts
  • apps/cli/src/commands/gen/keys/keys.command.ts
  • apps/cli/src/commands/gen/types/types.command.ts
  • apps/cli/src/commands/gen/types/types.handler.ts
  • apps/cli/src/commands/start/SIDE_EFFECTS.md
  • apps/cli/src/commands/start/start.handler.ts
  • apps/cli/src/commands/storage/cp/cp.command.ts
  • apps/cli/src/shared/cli/run.unit.test.ts
  • apps/cli/src/shared/functions/deploy.ts
  • apps/cli/src/shared/functions/download.ts
  • apps/cli/src/shared/functions/serve.ts
  • apps/cli/src/shared/init/project-init.templates.unit.test.ts
  • apps/cli/src/shared/telemetry/error-actionability.ts
  • apps/cli/src/telemetry/command-telemetry.unit.test.ts
  • docs/adr/README.md
  • knip.jsonc
  • mise.lock
  • mise.toml

To resolve:

git fetch origin
git switch sync/develop-into-next
git merge origin/next
# resolve the conflicts, then commit
git push origin sync/develop-into-next

Approve to fast-forward next; do not use the merge button.

supabase-cli-releaser Bot and others added 30 commits October 8, 2026 10:51
This bumps storage from v1.80.0-r0 to v1.80.2-r0.


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

Planned after supabase/slim-services published v1.80.1-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>
…nt (CLI-2653) (#7045)

## TL;DR

brings the `db` connection string parser, `pgpass` and `pgservicefile`
under the effect lint

## whats introduced?

- `db-config.parse`, `pgpass` and `pgservicefile` join the allow list
file by file, so the rest of the `db` module waits for the later PRs in
this stack
- the parser reads `pgpass` and the service file through the
`FileSystem` and `Path` services, and its libpq env reads go through
`Config` once up front
- `parseConnectionString` and `poolerConfigFromConnectionString` become
effects, and every caller across `db`, `stack`, `test-db`, `migration
squash`, `gen types` and the pg-delta engine yields them
- an empty shell var still wins over the project `.env`, a missing
`pgpass` or service file still reads as absent, and every parse error
keeps its text
- unit tests run on temp dirs and a pinned `Config` env instead of
`process.env` writes, with a new case for the empty shell var

## ref

- related: CLI-2653
) (#7046)

## TL;DR

brings the `db` pg connection layer and few resolvers under the effect
lint

## whats introduced?

- `db-connection` and `db-dns` join the allow list
- the `sslrootcert`, `sslcert` and `sslkey` reads go through
`FileSystem`, keeping the BOM and the errno messages, and
`dbConnectionLayer` captures it at build so `DbConnection.connect` keeps
its type
- a cert path with a NUL byte, only reachable as `%00` in a DSN, now
reads `FileSystem.readFile: ...` instead of `TypeError
[ERR_INVALID_ARG_VALUE]: ...`, since the platform keeps only the message
for that throw
- `--dns-resolver https` sends the same request through `HttpClient`
with automatic HTTP tracing disabled, so no trace headers reach
Cloudflare, and keeps every failure text and the 10s timeout; a
`Db.resolveHostsOverHttps` span records the response status and answer
count
- tests pin the full cert read errors and cover the DoH resolver:
non-200, rejected request, no valid IP, invalid and empty JSON and the
timeout

## ref

- related: CLI-2653
…I-2653) (#7048)

## TL;DR

brings the `db` config toml reader under the effect lint

## whats introduced?

- `db-config.toml-read` joins the allow list
- shell env reads go through `Config` once, for the override names and
every `env(...)` ref in the toml, and the sync `envLookup` reads from
that snapshot
- `config push` drops its copy of the env ref helpers and uses the
reader's
- the toml parse runs before the env snapshot with the same failure
order and messages
- invalid signing keys files retain native JSON parser details in both
`config.toml` loading and `local-config-values`, with the same narrow
lint exception used by the SSO readers
- unit tests pin env through `Config` and temp dirs instead of
`process.env` writes, and the push, reset and start integration tests
pin the three values the reader now takes from `Config`

## ref

- related: CLI-2653
…I-2653) (#7049)

## TL;DR

brings the rest of the `db` module under the effect lint

## whats introduced?

- the per file `db-config.parse`, `db-config.toml-read`, `db-connection`
and `db-dns` entries collapse into `db-*.ts`, and `branch-target`,
`connect-errors`, `pooler-fallback` and `schema-flags` join, which
covers the whole `db` module
- `db-config.layer`, `db-pull-run` and `db-push-core` yield their errors
directly with the same classes and messages, and `branch-target`
recovers through `Effect.option` and `orElseSucceed`
- `db-config` integration tests feed env through `Config` instead of
`process.env` hooks, and `db-image` and `db-target-flags` move to
`it.effect`

## ref

- closes: CLI-2653
This bumps realtime from v2.142.0-r0 to v2.143.0-r0.


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

Planned after supabase/slim-services published v2.143.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>
Co-authored-by: Andrew Valleteau <avallete@users.noreply.github.com>
Co-authored-by: Julien Goux <hi@jgoux.dev>
Linear: [CLI-2442](https://linear.app/supabase/issue/CLI-2442)

Hotfix PR gating, the first of three PRs that complete the hotfix flow
on top of the branch and release work from #7038. A hotfix is a
`hotfix/*` PR into `main` with a `fix`, `perf`, or `revert` title;
squash-merging publishes the next stable patch and the existing sync
carries it back to `develop`.

- **Check Deploy** always runs and reports. On the scheduled `develop`
promotion it succeeds. On `hotfix/*` heads it requires the title to
start with `fix`, `perf`, or `revert`, with an optional scope and no
`!`, and the failure message says what to change. Any other head fails,
and so does a fork head on either path: hotfixes come from this
repository. The job is named so it can become a required status check on
`main` later; because `main` has no App bypass, the promotion's head SHA
must carry a real success for the deploy fast-forward push to pass.
- **Release Smoke Test** triggers on hotfix PRs into `main` and runs the
shared release pipeline with `dry_run: true`, restoring the
build-and-smoke rehearsal that beta normally provides. Dispatch inputs
keep their defaults. Runs are per-PR concurrent with cancel-in-progress,
and fork pull requests are skipped since their token cannot hold the
permissions the shared workflow declares.
- **Release dispatch guard**: a non-dry-run stable release can only be
dispatched from `main`. The `hotfix/*` exemption existed for the manual
rehearsal, which this PR makes automatic. @Coly010, was a real stable
publish from an unmerged hotfix branch intended? If so, say so and I
will restore it.

Verified on a scratch repository: with a ruleset requiring only this
check on `main`, a direct push of a deploy PR head and of a hotfix PR
head both passed once the job had reported on the head SHA, and a push
with no check run was rejected (rule suites 4418391424 and 4418634505).
The runbook's hotfix section and the rollback policy change in a
separate PR.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
…#7052)

Linear: [CLI-2442](https://linear.app/supabase/issue/CLI-2442)

Third of three PRs for the hotfix flow on top of #7038. Documentation
only.

- **Runbook, hotfix section** (`apps/cli/docs/release-process.md`): when
a hotfix is warranted, the patch-type title rule and why not to edit the
squash subject, the checks that run automatically including the dry-run
release rehearsal, the sync back into `develop` and what to do when it
conflicts, the interaction with an open deploy PR, and how a hotfix
reaches `@supabase/config` consumers. The manual dry-run dispatch step
is gone.
- **Runbook, rollback section**: replaced by the forward-only policy. A
`revert:` hotfix is the response to a bad release.
- **ADR 0030**: hotfix criteria, forward-only, the required `Check
deploy`, the automatic rehearsal, and the notifications, referencing ADR
0028 for the sync and fast-forward mechanics.
- **Maintainers guide and root instructions**: a short hotfix paragraph
and one sentence respectively, pointing at the runbook.

Stacked on #7050, so the workflows this runbook describes are in the
checkout: review this diff against that branch. It merges after #7050
and #7051 (Slack and the fast-forward comment); once #7050 lands, GitHub
retargets this PR to `develop` and I bring `develop` in before it goes
through the queue.

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

Co-authored-by: Claude <noreply@anthropic.com>
Linear: [CLI-2442](https://linear.app/supabase/issue/CLI-2442)

Second of three PRs for the hotfix flow on top of #7038. After a hotfix
publishes from `main`, the sync merges `main` back into `develop`. When
that conflicts, the bot opens a sync PR that nobody is told about, and a
Tuesday deploy approved in the meantime is refused with a comment that
only says `main` moved. This PR closes both gaps without changing how
the sync or the fast-forward work.

- **Sync branches** exposes the conflict PR number as a job output. For
the `main-into-develop` pair a new job posts a `sync-conflict` message
to the release Slack channel with the PR link and the resolution steps,
through the shared Slack workflow. The `develop-into-next` pair is
unchanged. CODEOWNERS already requests the CLI team on these PRs, so no
review request is added.
- **Fast-forward on approval**: when a deploy PR is refused because
`main` has commits `develop` lacks, the comment names the open
`sync/main-into-develop` PR, or tells the approver to dispatch the sync
if none is open. A failed lookup falls back to the generic comment
rather than skipping it.
- **Slack notify** gains the `sync-conflict` status and a `pull_request`
input; `version` becomes optional for messages that are not about a
release.

Draft alongside #7050; both are independent of each other.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
## Summary

**TL;DR** Failures from the new local stack (`supabase stack *`, and
stack-backed `db reset`, `db dump`, `db diff`, `test db` and `functions
serve`) now reach telemetry with a cause. Before, most of them were
reported as one fingerprint per error tag with category `unknown`, so
start and destroy failures couldn't be told apart. A port conflict now
counts as user-actionable only when the user pinned the port.

### Before

```mermaid
flowchart LR
  A["typed failure<br/>port, pull, timeout"] --> B["StackError<br/>message only"]
  B --> C["…StartError<br/>reason unknown"]
  C --> D["tag:ExperimentalStackStartError<br/>category unknown"]
```

### After

```mermaid
flowchart LR
  A["typed failure<br/>port, pull, timeout"] -->|failureKind| B["StackError<br/>kind + reason"]
  B --> C["…StartError<br/>reason stack"]
  C -->|StackError adapter| D["tag:ExperimentalStackStartError:port_conflict<br/>invalid_config"]
```

### Why

Every `@supabase/stack` failure crossed the owner RPC boundary as
message text. The CLI wrapped each one as `reason: "unknown"`, and none
of the `ExperimentalStack*Error` reasons set a fingerprint suffix. As a
result:

- the top new-stack start and destroy errors showed as `unknown`;
- `stack status` "no stack" and real flag errors shared one fingerprint;
- a raw `StackError` that escaped a command was reported as
`tag:unknown`, losing even its tag.

### What changed

- **`@supabase/stack` carries a failure kind.** `failureKind()` derives
a closed `StackFailureKind` from the typed cause chain. It is carried on
`StackError`, each failed composition outcome, the `POST /shutdown`
failure body and the owner readiness line.
- Failure sites that know the cause set the kind explicitly: port
claims, engine command failures, health and startup timeouts, process
exit and stop, artifact preparation, state, owner startup, connection
and exit, and lease conflicts.
- Docker stderr is classified only where the container error is built,
as with the existing retry classifiers.
- **The wire field is an open string.** An older client that decodes a
newer owner's error ignores kinds it doesn't know instead of rejecting
the response.
- **Port provenance.** `port-conflict` (user-actionable) is reserved for
a port requested by number, i.e. set in `config.toml` or a
`SUPABASE_*_PORT` override. The stack's own allocation reports
`port-allocation` (internal bug):
- auto-assigned ports, including a saved auto assignment that is now
taken;
  - native backend port collisions;
  - Docker publish refusals, since Docker chooses the host port.
- **A typed `StackError` adapter in the CLI.** It lives in
`error-actionability.ts` and checks, in order:
  1. the client `reason`;
  2. the `kind`;
  3. the first failed outcome's kind;
  4. otherwise `:unclassified`.
- **Per-reason fingerprints.** Every new-stack command error reason,
plus `FunctionsServeStackError`, has its own fingerprint suffix (for
example `:not_found`, `:flags`, `:confirmation`). A `stack` reason
classifies through its wrapped cause, so wrapped CLI errors such as
`DbSetupError` keep their own classification.
- **Stack-backed db and functions wrappers** keep the stack cause, so
the real failure shows through: `LocalDbRunningError`, `db reset`, `db
dump`, `db diff`, declarative sync, `test db` and `functions serve`.
- **Engine command failures** are classified as `runtime_crash` /
`engine_command` rather than `invalid_config`.
- **Unused internal CLI reasons are removed:** `registry`, `port`,
`artifact` and `impossible-state`.

### Reviewer notes

- **Dashboards:** fingerprint values change for these errors. A query
matching `tag:ExperimentalStackStartError` exactly needs a prefix match
now.
- **`functions serve` edge case:** on a stack with no API service it
still binds the CLI's default API port, so a conflict there reports as
user-actionable. That number also forms the served `SUPABASE_URL` before
the instance exists, so it is left as is.
- **Not covered here** (both pre-existing, for every command): parse
errors (an unknown flag or unexpected argument) and Ctrl+C send no
`cli_command_executed` event.

## Linked issue

None.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This bumps analytics from v1.52.0-r0 to v1.53.0-r0.


https://github.com/supabase/slim-services/releases/tag/analytics-v1.53.0-r0

Planned after supabase/slim-services published v1.53.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 bumps realtime from v2.143.0-r0 to v2.143.1-r0.


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

Planned after supabase/slim-services published v2.143.1-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 realtime from v2.143.1-r0 to v2.143.2-r0.


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

Planned after supabase/slim-services published v2.143.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>
This bumps realtime from v2.143.2-r0 to v2.143.3-r0.


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

Planned after supabase/slim-services published v2.143.3-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>
Adds [jactionlint](https://github.com/jdx/jactionlint) as a core lint
for our GitHub Actions workflows and runs it as part of `check:all`.

- Replaces `.github/actionlint.yaml` with `.github/jactionlint.yaml` and
enables `require-commit-hash`, so actions, reusable workflows, and
`docker://` images must be pinned to a full SHA or digest.
- Pins `jactionlint` and `shellcheck` through mise. jactionlint only
checks `run:` scripts when shellcheck is on `PATH`, so pinning it keeps
local and CI results the same.
- Adds a root `lint:actions:check` script, registers it as an uncached
Turbo task, and includes it in `check:all`.

Fixes for existing findings:

- The `publish` jobs in `cli-go-pg-prove.yml` and
`cli-go-publish-migra.yml` now grant `packages: write` and `id-token:
write`. The reusable `cli-go-mirror-image.yml` workflow needs both, and
these callers only had the workflow-level `contents: read`.
- `$GITHUB_OUTPUT` redirects in workflow scripts are now quoted.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Julien Goux <hi@jgoux.dev>
This bumps storage from v1.80.2-r0 to v1.81.0-r0.


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

Planned after supabase/slim-services published v1.81.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>
…7068)

## TL;DR

stops a database helper from being left behind as a `Created` container
when readiness times out mid create

## whats introduced?

the helper is now created by name with `docker create --rm -i` and 
then attached with `docker start --attach --interactive`, so the name
exists before any readiness timeout and `rm -f` always finds it. `create
--rm -i` keeps the `AutoRemove` and `StdinOnce` of `run`, so a dead
owner still ends the helper...

the create is bounded to 30 seconds and its stderr stays in readiness
errors. a create the daemon finishes after that timeout is removed again
by name on owner shutdown, and an owner killed between `create` and
`start` leaves a helper the stack sweep on stop and destroy removes.

## ref:

- closes: #7063
- left/spotted in: #7014

---------

Co-authored-by: 7ttp <117663341+7ttp@users.noreply.github.com>
…7075)

## TL;DR

stops the local stack gateway from opening a new upstream connection for
every write.

## whats broken?

every `.rpc()`, `auth.admin.createUser`, delete or upload went upstream
on its own connection and left it in `TIME_WAIT`, so a busy test run
used up the macOS port range.

## now fixed by:

writes reuse the gateway's keep alive pool like reads do. a write is
still never resent.
Studio and its `/mcp` route keep a fresh connection per write, as before

chunked GET and DELETE bodies are forwarded as chunked, so they reach
the upstream and stay off the next request
PS: Auth's own Postgres churn is tracked in #6981

## ref:

- closes: #6987
- extends: #6925
Bumps `effect`, `@effect/platform-bun`, `@effect/platform-node`,
`@effect/platform-node-shared`, `@effect/sql-pg`, and `@effect/vitest`
from `4.0.0-rc.112` to the stable `4.0.2` release. `@effect/tsgo` is
unchanged.

The goal is zero change to CLI behaviour. Every edit is either required
by a removed or renamed API, or restores a behaviour whose upstream
default moved.

## Commit layout

The first three commits are the bump plus two scripted rewrites and can
be skipped by reviewers:

- `effect/unstable/*` import paths moved to `effect/*` (removed upstream
in rc.118,
[effect#8354](Effect-TS/effect#8354)).
- CLI and Config constructors renamed to PascalCase
([effect#7453](Effect-TS/effect#7453),
[effect#8121](Effect-TS/effect#8121)).

The remaining commits are hand migrations, one topic each.

## Hand migrations

- **node-postgres pool bridge** — `@effect/sql-pg` 4.0 is a native
client and dropped `PgClient.fromPool`
([effect#7426](Effect-TS/effect#7426)). The
remote database session keeps its own node-postgres pool for the zero
idle timeout, the per-connection role step-down hook, and raw `COPY`
connections, so `db-connection.pool-client.ts` is a small
`SqlClient.make` adapter over that pool with the same statement
execution, cancellation, and error classification the old bridge had.
- **Stack RPC strictness** — parse-option annotations no longer affect
decoding ([effect#8131](Effect-TS/effect#8131))
and the RPC transport passes no parse options, so the initialization
command and `runCommand` payload schemas now reject unknown keys through
a `Record` filter ahead of the struct decode, with the same "Expected no
excess property" wording. Encoding drops `undefined` entries first, so
optional fields passed as explicit `undefined` (for example
`pgProve.workingDir`) still cross the RPC JSON codec as they did with
the plain struct.
- **One statement per query** — the native `@effect/sql-pg` client sends
everything through the extended protocol, which rejects multi-command
strings. The realtime schema bootstrap runs as two statements, and the
generated `ALTER ROLE` / `ALTER DATABASE` batches come back from
Postgres as an array and run one at a time inside the same transaction.
- **Socket addresses, file sizes, encoding, sockets** — server addresses
are `NetAddress.SocketAddress` values, `File.seek` and `File.Info.size`
use `bigint` and `ByteSize`, `Encoding` moved to
`effect/encoding/Base64Url`, and the whole-stack WebSocket test helper
uses the reader and writer pair.
- **`Effect.partition`** returns `[passes, fails]`; the `stack destroy`
handler destructures in that order.
- **Management API contracts** — `packages/api` contracts are
regenerated; the 4.0.2 schema codegen emits code-point length checks and
Unicode-flagged patterns.
- **Config package** — `SchemaAST.Union.mode` moved under `options`,
`ToJsonSchemaOptions.additionalProperties` became `onExcessProperty`,
and regex patterns need the Unicode flag to be exported into JSON Schema
([effect#8482](Effect-TS/effect#8482)). Peer
dependency ranges are untouched and already satisfy 4.0.2.

## Preserved defaults

- The stack's `PgClient.layer` calls pin `idleTimeout: "10 seconds"`;
the default since 4.0.1 is 60 seconds
([effect#8679](Effect-TS/effect#8679)).
- The published JSON Schema files (`@supabase/config` `schema.json` and
`project-schema.json`, mirrored at `apps/docs/public/cli/*.schema.json`)
are byte-identical to the develop build. `toCliConfigJsonSchema` passes
`onExcessProperty: "error"` so structs keep `additionalProperties:
false` ([effect#8147](Effect-TS/effect#8147));
the shared document assembler leaves pattern-keyed records open, as the
generator has no per-schema setting for that; and bucket tables are
modelled so they render as the published object-or-array union.

## Behaviour changes reviewed and accepted

These upstream changes have no configuration knob. Each was checked
against the repo's call sites and judged not user-visible:

- Child-process `kill` and scoped release now wait for the process
group, bounded to one second without `forceKillAfter`
([effect#8018](Effect-TS/effect#8018)). The
stack host, bundled Postgres clients, and containers already set
`forceKillAfter`; other spawn sites can only see up to one extra second
of teardown when a descendant lingers.
- `Effect.cached` treats interruption as abandonment
([effect#8719](Effect-TS/effect#8719)). Every
cached effect in the repo is awaited by a single fiber or forked into an
owning scope, so the shared-interruption semantics do not apply.
- Empty bucket tables under `storage.analytics.buckets` and
`storage.vector.buckets` are modelled as an object-or-array union
instead of `Schema.Struct({})`. Runtime decoding now rejects a primitive
bucket value (`foo = "x"`), which the old schema accepted and ignored;
objects and arrays decode as before.
- HTTP client spans are named after the bare method (`GET` instead of
`http.client GET`), following the OpenTelemetry convention. Nothing in
the repo keys on the old name.
- Generated Management API string length checks count code points
instead of UTF-16 code units, which only differs for characters outside
the Basic Multilingual Plane.

## Patches

- Rebased: the `@effect/platform-node-shared` stdin EPIPE listener, now
against 4.0.2. 4.0.2 still removes the stdin sink's error listener
before the final `end()` flush, so a child that exits before reading its
input raises an uncaught EPIPE on Linux;
[effect#7712](Effect-TS/effect#7712) and
[effect#7714](Effect-TS/effect#7714) do not
cover that path.
- Rewritten: the `@effect/vitest` `runTest` patch. Upstream's finalizer
ordering fix
([effect#8154](Effect-TS/effect#8154)) covers
the abort hook, so the patch now only layers the two behaviours the
stack's `effect-timeout` tests assert on top of it: rejecting with the
pretty errors and reporting a finalizer failure after a timeout.

## Test-only changes

- Stack integration and CLI `db reset` e2e fixtures that seeded data
through `PgClient.layer` with semicolon-joined statements now issue one
statement per call; fixtures that go through `psql` are unchanged.
- The `db reset` e2e fixture reads `count(*)::int`, since the native
client decodes `int8` as `bigint`.

## Follow-ups

- Migrate the remote database session to the native `@effect/sql-pg`
client and delete the pool adapter. That changes result decoding (int8,
timestamps, bytea) across the SQL consumers and needs its own review.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Julien Goux <hi@jgoux.dev>
## Summary

**TL;DR:** The experimental local stack can now run OrioleDB through
`db.orioledb_version`, on both the Docker and native runtimes. It uses
the new slim-services OrioleDB release line (`<upstream>-orioledb`). A
version without a published artifact fails closed, listing the supported
ones.

### Before

```mermaid
flowchart LR
  A["db.orioledb_version set"] --> B{"experimental stack?"}
  B -- yes --> C["error: unsupported by the experimental stack"]
  B -- no --> D["Compose: docker.io supabase/postgres:&lt;v&gt;-orioledb"]
```

### After

```mermaid
flowchart LR
  A["db.orioledb_version set"] --> B{"OrioleDB pin in catalog?"}
  B -- no --> C["fail closed: lists published versions"]
  B -- yes --> D["stack: slim &lt;v&gt;-orioledb-rN<br/>docker or native"]
  D --> E["db diff / declarative shadow<br/>use the same pin"]
  D --> F["data reuse only within<br/>the OrioleDB line"]
  G["slim-services publishes<br/>&lt;v&gt;-orioledb-r0"] --> H["catalog sync opens<br/>add postgres PR"]
  H --> B
```

### Why

OrioleDB is leaving experimental. The new stack rejected
`db.orioledb_version` with `db.orioledb_version is unsupported by the
experimental stack`. supabase/slim-services#315 now builds and publishes
OrioleDB as its own Postgres release line, next to stock 15 and 17, so
the stack can pin it like any other artifact.

### What changed

- **Catalog sync.** `-orioledb` releases form their own catalog line
(`17-orioledb`).
  - The first publish opens an `add` PR.
  - Later releases upgrade or hotfix that line on their own.
  - An OrioleDB release never touches the stock 17 pin, and the reverse.
  - Legacy tags without `-rN` are ignored.
- **One version resolver.** `db.orioledb_version` (and
`SUPABASE_DB_ORIOLEDB_VERSION`) resolves to `<v>-orioledb` through one
shared resolver. `stack start`, the `db diff` shadow and its cache key,
and the versions output all use it.
  - The setting requires `db.major_version = 17`.
  - An unpinned version fails with the list of published versions.
  - `postgresVersion("17")` still resolves to stock.
- **Data safety.** The ready-marker, `PG_VERSION` and snapshot checks
compare the engine line, not only the major. Stock and OrioleDB data,
snapshots and shadow caches never mix.
- Unmarked data, including data left behind by an interrupted first
start, is refused for an OrioleDB request.
  - The error suggests `supabase stack destroy`.
- Switching `orioledb_version` on a saved stack requires `stack
destroy`, because the stack keeps its database version.
- **`test db`.** Maps OrioleDB to its major for pg_prove.
- **Declarative shadow.** No CLI-side handling: pg-delta's Supabase
profile treats `orioledb` as a platform extension
([supabase/pg-toolbelt#513](supabase/pg-toolbelt#513)),
so declarative export never emits it and the diff never creates or drops
it. Shipped in pg-delta `1.0.0-alpha.58`, which this PR pins.
- **Compose.** With `SUPABASE_USE_SLIM_IMAGES`, Compose maps
`supabase/postgres:<v>-orioledb` to the pinned slim OrioleDB image.
Unpinned versions stay on docker.io.
- **Docs.** `stack-commands.md`, `SIDE_EFFECTS.md`,
`packages/stack/ARCHITECTURE.md` and ADR 0026.

It also pins the first published OrioleDB release,
`17.11.0.002-orioledb-r0`, added with the catalog sync's `--release`
mode. Later OrioleDB releases arrive through the existing
`slim-release-published` sync.

## Linked issue

Part of CLI-2387. Depends on supabase/slim-services#315.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ts (#6950)

With `[experimental] stack` enabled, changing a port in `config.toml`
(including removing the fixed ports a legacy `supabase init` writes)
used to make the next `supabase start` fail with "The saved stack cannot
adopt these changes". The only way out was to destroy the stack and lose
its data. Now, when a stopped stack starts, it applies endpoint port
changes and prints each one, for example `api: 54321 → 26199`. JSON and
stream-json output add an `endpoint_changes` array to the success
payload; each entry names the endpoint, the `endpoints` keys it moved
(`keys`), and its `from`/`to` port.

- `config.toml` is the source of truth. The re-plan runs inside the
stack owner's startup, while the owner holds the stack lease and before
it binds any endpoints. It saves the requested endpoint intents,
releases the changed endpoints' rows in the port registry (for example
an old sticky automatic port), and claims the new ports through the
owner's normal port acquisition, with the usual conflict and occupancy
checks. Concurrent starts attach to the winning owner instead of racing
it.
- Nothing is rolled back. A requested fixed port that is already taken
fails the start with the usual port conflict, naming the config key and
noting that the new port is saved and the previous automatic port
released, so starting again succeeds once the port is free.
- Changing which services share the API port is handled too: when a
different sharing service requests a fixed API port, the shared
listener's old automatic claim is released.
- A start request is the complete composition, so excluded or absent
services no longer pin the shared API port, and removing `[api] port`
moves the stack to an automatic port instead of being silently ignored.
A stopped service excluded during a re-plan has its endpoints rewritten
when it is requested again, so the start reuses it and its data instead
of creating a second instance.
- Endpoints whose configuration didn't change keep their saved ports. A
running stack still has to be stopped first, and so does an idle owner
kept alive by `stack prepare`: its error now suggests `stack stop` then
`stack start` instead of `stack destroy`. Artifact-version and
PostgreSQL major-version changes are still rejected.
- The stack definition sent on first registration and the requested
configuration for a re-plan reach the spawned owner through a private
(`0600`), short-lived file the owner deletes after reading, instead of
its command line, so secrets don't appear in process listings.

ADR 0017 is amended: saved ports stay stable unless the configuration
deliberately changes them on a stopped stack.
…efore start (#7081)

## TL;DR

stops a container service from failing to start when its published host
port is taken between create and start.

## whats broken?

containers publish with an engine-chosen host port
(`127.0.0.1::<port>`). Podman picks that port at create by opening and
closing a socket, and pasta only binds it at start. Anything that grabs
the port in between, such as an outbound connection from another stack,
fails the start with `Listen failed for HOST TCP port …: Address in
use`.

## now fixed by:

- pasta's "Address in use" is classified as a port-allocation failure,
like Docker's "port is already allocated".
- a launch that fails with a port-allocation failure removes its
container and creates a new one, which gets a new port, up to 3
attempts. The last failure is returned as before.

## ref:

- seen on: #7080
…7089)

## TL;DR

fixes `supabase start` failing on podman when the project has no
`supabase/snippets` folder

## whats broken?

since the ts port, start creates containers through `podman create -v`
which rejects a missing bind source while docker just creates it. studio
binds `supabase/snippets`

so a fresh project failed with `statfs <project>/supabase/snippets: no
such file or directory`

## now fixed by:

start creates `supabase/snippets` when studio is enabled, before any
container is created
 matching what the stack already does...

## ref:

- closes: #7086
- broken/missed in: #5847
… lint (CLI-2649) (#7084)

## TL;DR

brings the `api`, `http` and `auth` modules under the effect lint,
 along with `profile`, `debug` and the rest of `project`

## whats introduced?

- the files of these modules join the allow list
- `dohFetch` runs as one effect, and the request signal still only
cancels the DoH lookup, so responses and fetch errors come back as
before
- `identity-stitch` writes `telemetry.json` through a schema codec and
`DateTime` with the same bytes, a raw int64 `schema_version` included
- the `--debug` HTTP timestamp and the default login token name read the
clock through Effect, and the branch resolvers move to `Effect.fn` with
the same span names
- tests move to scoped temp dirs, `TestClock` and `it.effect`, with new
cases for the abort handling, the int64 token, the debug timestamp and
the token name

## ref

- closes: CLI-2649
## TL;DR

patches a couple of flakes:

- **slim images** timed out without naming the stalled image. i've added
a timeout per probe and bounded cleanup; the original hang's cause is
still unknown.
- **streamed HTTP** reset the body because the fixture could end its
response before the upload finished. i've fixed it by waiting for
completion.
- **Functions cleanup** gets a minor change: reuse the artifact cache to
avoid a cold download.
- **pinned ports** in the Network tests came from Linux's ephemeral
range, so an outbound socket could already hold one. i've moved them
below every range the stack and the OS hand out.
- **log halving** could split its 256 lines when the forwarder read
mid-append. i've seeded them before Analytics wakes.

## ref:

- spotted in: [slim
images](https://github.com/supabase/cli/actions/runs/37597900104/job/112714886007)
· [streamed
HTTP](https://github.com/supabase/cli/actions/runs/37640970591/job/112859517856)
· [pinned
ports](https://github.com/supabase/cli/actions/runs/37935786356/job/113837363870)
· [log
halving](https://github.com/supabase/cli/actions/runs/37754787871/job/113236924626)
This bumps studio from 2026.10.05-sha-94b8b06-r0 to
2026.10.12-sha-3699278-r0.


https://github.com/supabase/slim-services/releases/tag/studio-2026.10.12-sha-3699278-r0

Planned after supabase/slim-services published
2026.10.12-sha-3699278-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>
Co-authored-by: Julien Goux <hi@jgoux.dev>
…` with effect lint (CLI-2650) (#7077)

## TL;DR

brings the project `.env` loader and the local project context under the
effect lint

## whats introduced?

- `project-environment`, `local-project-context`, `dotenv`, `vault` and
`vault-decrypt` join the allow list, and `viper-env` follows in the next
PR of this stack
- the project `.env` files are read through the `FileSystem` and `Path`
services and fail in the error channel, so both callers keep the same
`failed to read config: Error: ...` text
- the read order, shell-wins precedence, a kept leading BOM and the
missing, unreadable or directory `.env` cases behave as before, and a
`.env` FIFO no longer blocks Ctrl+C
- `SUPABASE_ENV` and `SUPABASE_PROJECT_ID` are read through `Config`
with the same empty and unset handling
- tests pin env through `Config`, stay hermetic against an exported
`SUPABASE_ENV` or `SUPABASE_PROJECT_ID`, and cover the BOM, directory,
symlink loop and `.env.local` skip cases

## ref

- related: CLI-2650
## TL;DR

brings `viper-env` and the experimental gate tests under the effect lint

## whats introduced?

- `viper-env` and `experimental-gate` join the allow list
- the `SUPABASE_*` readers read through `Config` and become effects, so
every caller keeps its `flag || env` short-circuit and the same true,
false, empty and unset handling
- `dumpNetworkMode` in `pg-dump.run` becomes an effect for the same
reason
- tests that set these vars through `process.env` would pass without
testing anything once `Config` snapshots the env, so they pin them
through `withConfigEnv`, and the unset guards also hide them from
`Config` through a new `withEmptyConfigEnv` helper
- the shadow cache guard becomes a `shadowCacheDisabledLayer` merged
into each file's setup

## ref

- closes: CLI-2650
… event log (#7091)

## TL;DR

stops the Podman sweep integration test from timing out with `The next
owner did not sweep orphans`.

## whats broken?

the test waits for each swept container's `remove` event from `podman
events --since …`. On CI, Podman logs events to a shared file that it
rotates when it reaches its size limit. A reader that hasn't polled an
event yet when the log rotates skips it, so the wait never completes and
the test hits its 1 minute timeout. Parallel Podman test files fill that
log quickly, so this happens occasionally in the Podman integration job.

## now fixed by:

- on Podman, the helper waits on the container itself with `podman wait
--condition removing --ignore`, which also returns for a container that
is already gone. Docker keeps its event stream, which is not affected.
- the database stop test's helper reads Podman's exit code with `podman
wait <container>` instead of its `died` event, which the same rotation
can drop.
- the timeout message names the waits that are still pending, so any
remaining failure points at its cause.

## ref:

- seen on: #7081 (merge queue) and
https://github.com/supabase/cli/actions/runs/37703640927
## Summary

Scoped personal access tokens let a user pick exactly which permissions
a token gets. For that to be useful, a user needs to know what a given
CLI command actually needs before creating a token — today the CLI can't
answer that at all. This work builds the mapping from every command (and
its flags) to the Management API permissions it needs, plus tests that
keep that mapping from drifting as the code changes. See
[CLI-2474](https://linear.app/supabase/issue/CLI-2474).

This is the first of several PRs. This one is pure foundation — no
command is mapped yet.

## What's in this PR

- The data model for a command's permission mapping (plain, serializable
data), and a `withPermissions` combinator that attaches a command's
declaration to the `Command` itself. The command tree is the single
registry, so a declaration sits next to the command it describes and any
future docs or help feature reads it straight off the command.
- A spec-backed operation matcher (FGA permissions and OAuth scope) and
a drift-assertion test helper that later PRs will use to check a
command's declared mapping against its real API calls.
- A completeness test that walks the real CLI command tree for both
stack backends and fails if any command or flag isn't accounted for.
- A central list of commands still pending a declaration, so the
completeness test passes until each batch lands. Follow-up PRs shrink
it, and the last one deletes it.

Nothing in the shipped CLI imports these modules yet, so this PR doesn't
change the bundle.

Follow-up PRs will map the shared logic several commands reuse, then the
remaining commands in batches.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
avallete and others added 3 commits October 12, 2026 12:41
## Summary

**TL;DR** An unhandled crash (an Effect defect that matches no
classification) is now fingerprinted as `error:Defect:<identity>`
instead of a bare `error:Defect`. Unrelated crashes no longer share one
fingerprint, so the most frequent one can be identified from telemetry.

### Before

```mermaid
flowchart LR
  A["Namespace.NamespaceError"] --> C["error:Defect"]
  B["Error with code ENOENT"] --> C
```

### After

```mermaid
flowchart LR
  A["Namespace.NamespaceError"] --> C["error:Defect:Namespace.NamespaceError"]
  B["Error with code ENOENT"] --> D["error:Defect:Error:ENOENT"]
```

### Why

`stack status`, `status` and `stack list` crash on Linux with
`error:Defect`. That fingerprint collapses every defect the classifier
doesn't recognize, so the crash can't be told apart from any other
unhandled failure. Native JS exceptions already name themselves
(`error:TypeError`); everything else did not.

### What changed

- `classifyCliCauseActionability` appends the first unclassified
defect's identity:
- for an `Error`, the tag its class defines on the prototype (dotted
tags such as `Namespace.NamespaceError` included), else `Error`,
followed by its `code` when it is one of a fixed list of system and DNS
error codes (for example `error:Defect:Error:ENOENT`);
  - for anything else, the value's type (`object`, `string`, …).
- The category is unchanged: these crashes still count as `internal_bug`
/ `panic`.
- Only source-owned values are used: instance `_tag`, `name`, and
message are never read, the code is read only as a plain data property,
and a code outside the list is dropped. If inspecting the defect throws,
the identity is `Unreadable` and the command's own failure is kept.
- Dashboards that match `error:Defect` exactly need a prefix match.

## Linked issue

None.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## TL;DR

stops local Auth and the stack host from opening a new connection for
every request.

## whats broken?

Auth keeps no idle Postgres connections unless an idle pool size is set,
so every Auth request opened and closed its own database connection and
left it in `TIME_WAIT`. A busy test suite could use up the macOS port
range, the same way gateway writes did before #7075.

The stack host's HTTP client (readiness probes every 250 ms, log
forwarding to Analytics) also used a non-keep-alive agent, so each call
opened a new connection.

## now fixed by:

- local Auth keeps its pooled connections open when idle: 2 in the stack
(the cap of its image), 10 in `supabase start`. No new `config.toml`
keys.
- the stack host's HTTP client reuses connections and closes them after
4 s idle, before the shortest upstream keep-alive of 5 s.

## known limitation:

Auth's database driver doesn't detect a pooled connection that Postgres
closed while Auth kept running (`pg_terminate_backend`, or a Postgres
restart without an Auth restart), so each such connection fails one Auth
request before it is replaced. `db reset` restarts Auth and is
unaffected.

## ref:

- closes: #6981
- extends: #7075
## TL;DR

syncs the shared effect skill with the latest upstream, which was
refreshed for stable effect v4.

## whats introduced?

- adds `references/V4_APIS.md` with the current v4 imports and common
migration traps
- updates `SKILL.md` and the config, schema, caching, http and testing
references for the current v4 apis

## ref

- extends: #6688
@supabase-cli-releaser
supabase-cli-releaser Bot requested review from a team and supabase-oss as code owners October 12, 2026 14:12
@supabase-cli-releaser supabase-cli-releaser Bot added the do not merge Approve to apply; do not merge. label Oct 12, 2026
@avallete

Copy link
Copy Markdown
Member

Superseded by a fresh sync from the reworked workflow (#7083).

@avallete avallete closed this Oct 12, 2026
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.

6 participants