Repository navigation
chore(repo): sync develop into next - #7082
supabase-cli-releaser[bot] wants to merge 21 commits into
Conversation
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:<v>-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 <v>-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/><v>-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>
Resolution for
|
|
/ai-review |
There was a problem hiding this comment.
🤖 AI Review
Confirmed all five findings from both independent reviews: two major runtime bugs, two minor concerns, and one nit. No duplicates were found. Verification used code inspection and a static scanner check; missing dependencies and blocked local sockets prevented full runtime testing.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | apps/cli/src/command-internal/db-connection.pool-client.ts:205 |
cancellation |
codex | SQL cancellation queues behind the statement it must cancel because it acquires a connection from the same saturated pool. After cancellation times out, the active client is released without discarding it, allowing the interrupted statement to continue. |
| 🟠 MAJOR | packages/stack/src/HttpProxy.ts:372 |
resource-lifecycle |
codex | An early upstream response can leave its incomplete HTTP request open after the downstream response completes, retaining an occupied upstream connection. |
| 🟡 MINOR | apps/cli/src/command-internal/db-target-flags.unit.test.ts:200 |
test-coverage |
codex | Both flag-completeness tests pass vacuously because their scanner recognizes lowercase constructors while command declarations now use capitalized constructors. |
| 🟡 MINOR | docs/adr/README.md:74 |
documentation |
claude | Two different ADRs share number 0030, making references by ADR number ambiguous. |
| ⚪ NIT | apps/cli/src/command-internal/db-config.toml-read.ts:1062 |
dead-code |
claude | The removed SUPABASE_EXPERIMENTAL_PG_DELTA toggle remains in ENV_OVERRIDE_NAMES despite no longer controlling any configuration setting. |
Stats
Claude findings: 2 · Codex findings: 3 · Confirmed: 5 · Refuted: 0 · Uncertain: 0
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: manual · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
| pool.query(`SELECT pg_cancel_backend(${processId})`, () => { | ||
| resume(Effect.void); | ||
| }); | ||
| }).pipe(Effect.interruptible, Effect.timeoutOption(5000), Effect.asVoid) |
There was a problem hiding this comment.
🟠 MAJOR · cancellation · source: codex
SQL cancellation queues behind the statement it must cancel because it acquires a connection from the same saturated pool. After cancellation times out, the active client is released without discarding it, allowing the interrupted statement to continue.
Evidence: db-connection.pool-client.ts:205-208 submits pg_cancel_backend through pool.query with a five-second Effect timeout and no queue-removal cleanup. Lines 65-66 release the client normally, and line 110 waits for cancellation before releasing it. db-connection.sql-pg.layer.ts:547 configures max: 1, and line 872 passes that pool to makePoolSqlClient.
Suggested fix: Use a separate cancellation connection or PostgreSQL CancelRequest transport. Discard the active client when cancellation cannot be confirmed, and ensure timed-out cancellation requests cannot remain queued.
| const onUpstreamEnd = () => { | ||
| if (outgoing !== undefined) request.unpipe(outgoing); | ||
| if (request.readableEnded) endResponse(); | ||
| else { | ||
| request.once("end", endResponse); | ||
| Deferred.doneUnsafe(draining, Effect.void); | ||
| request.resume(); | ||
| } |
There was a problem hiding this comment.
🟠 MAJOR · resource-lifecycle · source: codex
An early upstream response can leave its incomplete HTTP request open after the downstream response completes, retaining an occupied upstream connection.
Evidence: HttpProxy.ts:373 unpipes the upstream request, while lines 376-378 drain subsequent client bytes without forwarding them. Normal response completion calls finish at line 366; its cleanup at lines 342-354 neither ends nor destroys outgoing. Destruction occurs on abandonment or interruption at lines 358-362 and 429-433.
Suggested fix: Destroy the incomplete upstream request after its response finishes while preserving the valid downstream response. Extend early-rejection tests to assert upstream connection cleanup.
| function extractDeclaredFlags(filePath: string, source: string): Array<DeclaredFlag> { | ||
| const callRegex = /Flag\.(string|integer|choice|choiceWithValue|float|boolean)\(/g; | ||
| const calls = Array.from(source.matchAll(callRegex), (match) => ({ | ||
| index: match.index, |
There was a problem hiding this comment.
🟡 MINOR · test-coverage · source: codex
Both flag-completeness tests pass vacuously because their scanner recognizes lowercase constructors while command declarations now use capitalized constructors.
Evidence: db-target-flags.unit.test.ts:183 retains lowercase VALUE_FLAG_KINDS, and line 201 matches lowercase Flag constructors. Lines 240-247 and 262-271 only assert empty missing lists. dump.command.ts:63 uses Flag.String("file"). A static scan across 185 included command files found zero matches for the existing scanner.
Suggested fix: Prefer parser-level coverage of value-consuming flags. If retaining the scanner, update constructor names and kinds, and assert that declarations were actually extracted.
| | 0030 | [Go CLI Removal](0030-go-cli-removal.md) | accepted | | ||
| | 0030 | [Hotfix Releases](0030-hotfix-releases.md) | accepted | |
There was a problem hiding this comment.
🟡 MINOR · documentation · source: claude
Two different ADRs share number 0030, making references by ADR number ambiguous.
Evidence: docs/adr/README.md:74-75 lists both Go CLI Removal and Hotfix Releases as 0030. Both files exist and their headings use 0030. ADR 0016:3 references the Go removal decision, while apps/cli/docs/release-process.md:293,568,576 labels the hotfix decision ADR 0030.
Suggested fix: Renumber the Go CLI Removal ADR to 0031 and update its filename, heading, index entry, supersession link, and remaining references.
| "SUPABASE_DB_MIGRATIONS_SCHEMA_PATHS", | ||
| "SUPABASE_API_AUTO_EXPOSE_NEW_TABLES", | ||
| "SUPABASE_API_SCHEMAS", | ||
| "SUPABASE_EXPERIMENTAL_PG_DELTA", |
There was a problem hiding this comment.
⚪ NIT · dead-code · source: claude
The removed SUPABASE_EXPERIMENTAL_PG_DELTA toggle remains in ENV_OVERRIDE_NAMES despite no longer controlling any configuration setting.
Evidence: db-config.toml-read.ts:1062 retains the name. Lines 1132-1145 resolve and scan all registered override values, but pg-delta configuration at lines 1402-1433 uses SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED instead. diff-engine.ts:12-16 computes activation solely from configEnabled and usePgDeltaFlag.
Suggested fix: Remove SUPABASE_EXPERIMENTAL_PG_DELTA from ENV_OVERRIDE_NAMES.
|
Superseded by a fresh sync from the reworked workflow (#7083) |
Merging
developintonextconflicted, so Claude resolved it onsync/develop-into-next. Every later push todevelopupdates this pull request instead of opening another one.3 decisions need review.
Approve to fast-forward
next; do not use the merge button. To change a resolution, push tosync/develop-into-nextand explain the decision in a comment: later syncs read this pull request and reuse the decision. Converting this pull request to a draft pauses updates until it is marked ready again.Earlier resolution comments on this pull request still apply. Latest:
Resolution for
5f82350Claude (
claude-opus-5-5) merged intosync/develop-into-next. Workflow rundevelop(f825003)I resolved all 8 files in group 1.
nextdeletes the Go CLI, anddevelop's edits to the three Go-only files (two workflows and the Go Dockerfile) are covered elsewhere or no longer matter, so those files are deleted. In the remaining files,develop's content is ported ontonext's Go-free wording. Nothing has been run, so CI is the first check. No earlier sync settled any of these files, so I followed no precedent. I resolved all eight files in group 2 and removed every conflict marker. No checks have been run, since I can't run commands here; CI will run them on the PR.nextrenamed tests and messages to drop Go wording and made pg-delta on by default.developmoved code to Effect 4.0.2 and the Effect lint style (scoped temp dirs,withEnvconfig providers,Cause.pretty,Effect.callback). I keptdevelop's new structure and appliednext's names, messages and pg-delta default on top.nextdeletedenvLookupfrom the config reader's result, whiledevelophad only narrowed its type. I couldn't search the codebase from this session, so I haven't confirmed that nodevelop-side code still reads that field. If any does, the type-check will flag it.nextremoved the supabase-go proxy layer and its test;developonly changed import paths in them.No earlier sync PR covered these files, so none of the resolutions cites a precedent. I resolved all eight files in group 3. I couldn't run any checks, so CI has to confirm the result type-checks. The main change from
developis the effect 4.0.2 upgrade: imports move fromeffect/unstable/clitoeffect/cli, and constructors are renamed fromFlag.string/Argument.stringtoFlag.String/Argument.String. I applied that ontonext's version of each file.nextdeleted the fourdb branchleaf command files anddeveloponly changed their imports, so they are deleted. No earlier sync pull request covered these files. I resolved all 8 files in group 4. All conflict markers are gone, and nothing was run because I can't execute commands here; CI will check the result. Every resolution keepsnext's breaking changes and addsdevelop's Effect 4.0.2 upgrade or its OrioleDB slim-image doc wording. No recorded precedent applied: the only earlier sync, #7059, was closed without landing. I resolved all 8 files in group 5. One needs a maintainer's call:nextanddevelopeach added a different ADR as 0030, and both entries are now in the ADR index under that number. No earlier sync settled any of these files; the only past sync (#7059) was closed without landing. I couldn't run any checks, so type-checking and tests are left to CI. In most files the merge combinesdevelop's Effect 4.0.2 API renames (effect/cliimports,Flag.Literals,Flag.String,Flag.Int) withnext's removal of Go wording. I resolved the conflict inmise.toml. The Go and golangci-lint entries stay removed, asnextintended when it dropped the supabase-go binary. The jactionlint and shellcheck entries fromdevelopare kept because the workflow lint checks need them.Needs a decision
nextadded ADR 0030 'Go CLI Removal' anddevelopadded ADR 0030 'Hotfix Releases'. Which one keeps 0030? (docs/adr/README.md)next's Go CLI Removal ADR moves to 0031, sincedevelop's ADR has already shipped. That means renaming its file and updating references to it.develop's Hotfix Releases ADR to 0031 instead. That breaks links that already point to it ondevelop..github/workflows/cli-go-api-sync.yml).github/workflows/cli-go-mirror.yml)Resolved files
.github/workflows/cli-go-api-sync.yml: Deleted, asnextremoved the Go CLI and its workflows.developonly quoted$GITHUB_OUTPUTfor the new jactionlint check, which doesn't apply to a removed workflow. Flagged for review because it is under.github/..github/workflows/cli-go-mirror.yml: Deleted, asnextremoved the Go CLI and its workflows.developonly quoted$GITHUB_OUTPUTfor the new jactionlint check, which doesn't apply to a removed workflow. Flagged for review because it is under.github/.CONTRIBUTING.md: Keptnext's removal of the Go and golangci-lint tool entries and the Go version files. Addeddevelop's jactionlint and shellcheck rows to the tool table, plus its note thatpnpm check:allneeds shellcheck onPATH.apps/cli-go/pkg/config/templates/Dockerfile: Deleted along with the rest of the Go tree.develop's realtime v2.143.3, storage v1.81.0 and logflare 1.53.0 bumps are already innext's generatedapps/cli/src/shared/services/Dockerfile.apps/cli/docs/release-process.md: In See Also, keptdevelop's new link to ADR 0030 (hotfix releases) andnext's single-binary description ofbinary-distribution.md.develop's hotfix, rollback and sync text merged without conflict.apps/cli/src/cli/complete.ts: In the comment above the duration-flag set, combinednext's Go-free wording (isValidDuration) withdevelop's Effect 4.0.2 nameFlag.String. The rest of the Effect upgrade (imports,Int/Finitetags) merged cleanly.apps/cli/src/command-internal/db-config.integration.test.ts: Useddevelop's Effect rewrite of the linked-config-ordering test (scoped temp dir viaFileSystem,Cause.pretty, BunServices) undernext's title without the GoParseDatabaseConfigreference.apps/cli/src/command-internal/db-config.parse.unit.test.ts: Tookdevelop's full Effect rewrite (layeredit.effecttests,withConfigEnv, scoped temp files instead ofprocess.envandnode:fs). Then applied each ofnext's renames that drop pgconn and Go references from test and describe titles.apps/cli/src/command-internal/db-config.toml-read.ts: Keptnext's removal of theenvLookupfield from the config reader's result.develop's other changes merged without conflict and are kept: the shell-env lookup that goes through Effect's config service, the list ofSUPABASE_*override variable names, and failing with the error directly.apps/cli/src/command-internal/db-config.toml-read.unit.test.ts: Keptdevelop's Effect-based setup (scoped temp dirs,withEnvproviders,Cause.pretty) withnext's test names and its new 'expected a string, got …' error text. The pg-delta tests follownext's on-by-default behaviour: the remote-block test now sets the env override to "false", and the absolute-path test still checks that pg-delta is enabled.apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts: Usednext's test name for the auth-failure rendering test, withdevelop's directyield* fakePostgresServer(...)instead of theEffect.promisewrapper.apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts: Rewrote the failing step-down test asdevelop'sit.effect/Effect.callbackversion and dropped the Go-parity suffix from its name, asnextdid.apps/cli/src/command-internal/db-image.unit.test.ts: Useddevelop's scopedwithTempeffect withnext's test name, which drops the Go reference.apps/cli/src/command-internal/global-flags.ts: Keptnext's hidden 'Removed'--create-ticketflag (description and comment) and switched it todevelop's Effect 4.0.2GlobalFlag.Setting/Flag.Booleanconstructors.apps/cli/src/command-internal/string-slice-flag.ts: Keptnext'sflagLabelrename and the wording without Go references, and switched the flag todevelop'sFlag.Stringconstructor.apps/cli/src/commands/db/branch/branch.command.ts: Keptnext's removed-command placeholders fordb branch, which are defined inline. They now import fromeffect/cliand useArgument.String, matching the effect upgrade. The imports of the deleted leaf files are dropped.apps/cli/src/commands/db/branch/create/create.command.ts: Deleted, as onnext.developonly updated its imports, and the replacement placeholder now lives in branch.command.ts.apps/cli/src/commands/db/branch/delete/delete.command.ts: Deleted, as onnext.developonly updated its imports, and the replacement placeholder now lives in branch.command.ts.apps/cli/src/commands/db/branch/list/list.command.ts: Deleted, as onnext.developonly updated its imports, and the replacement placeholder now lives in branch.command.ts.apps/cli/src/commands/db/branch/switch/switch.command.ts: Deleted, as onnext.developonly updated its imports, and the replacement placeholder now lives in branch.command.ts.apps/cli/src/commands/db/diff/diff.command.ts: Keptnext's engine-flag comment and its hidden--use-pg-schemaflag that reports the removal, and switched both flags todevelop'sFlag.Booleanconstructor.apps/cli/src/commands/db/dump/SIDE_EFFECTS.md: Keptnext's environment-variable table, which drops theDB_PASSWORDviper key. Addeddevelop's OrioleDB wording ("OrioleDB tags the catalog does not pin") and realigned the table columns.apps/cli/src/commands/db/pull/SIDE_EFFECTS.md: Keptdevelop's wider env-var table, including its new OrioleDB wording ("OrioleDB tags the catalog does not pin"). Dropped theSUPABASE_EXPERIMENTAL_PG_DELTArow becausenextno longer reads that variable.apps/cli/src/commands/db/remote/changes/changes.command.ts: Keptnext's stub that tells users the command was removed, with its runtime and telemetry imports. Switched the CLI import todevelop'seffect/clipath and dropped the unused handler and type imports.apps/cli/src/commands/db/test/SIDE_EFFECTS.md: Tooknext's rewritten Notes. They no longer mentionFlag.boolean, sodevelop's rename toFlag.Booleanhas nothing left to update.apps/cli/src/commands/functions/download/download.command.ts: Keptnext's--legacy-bundleflag, which is optional and now only produces a removal error, along with its Effect/Layer/Option imports. Useddevelop'seffect/cliimport paths and itsFlag.Booleanconstructor.apps/cli/src/commands/gen/keys/keys.command.ts: Keptnext's stub that tells users the command was removed, with its imports. Switched the CLI import toeffect/cliand dropped the unused handler and type imports.apps/cli/src/commands/gen/types/types.command.ts: Keptnext's updated comment ("the handler prints the deprecation line") and useddevelop'sFlag.Booleanconstructor for--postgrest-v9-compat.apps/cli/src/commands/gen/types/types.handler.ts: Keptnext's localloadCliConfigimport fromcommand-internal/cli-config-load.tsand useddevelop'seffect/processimport path.develop's change to the connection-string parser in the preview-branch pooler fallback had already merged cleanly.apps/cli/src/commands/start/SIDE_EFFECTS.md: Tookdevelop's env-var table with the new OrioleDB slim-image wording. Re-appliednext's change of theSUPABASE_*row text to "Generic env override", which removes the Viper wording.apps/cli/src/commands/storage/cp/cp.command.ts: Keptnext's comment without the pflag/Go wording and applieddevelop's rename ofFlag.integertoFlag.Int.apps/cli/src/shared/cli/run.unit.test.ts: Keptnext'sData/Runtimeimports, which its custom-exit-code test error needs, and switched todevelop'seffect/cliimport path.apps/cli/src/shared/init/project-init.templates.unit.test.ts: Keptdevelop's change that reads the OrioleDB version from the rendered template instead of a hard-coded value, plus its new pinned-version test. Keptnext's renamed helpers and its comments without Go wording.apps/cli/src/shared/telemetry/error-actionability.ts: Merged the error-fingerprint lists alphabetically:develop'srelease_mismatch(from the stack-failure classification) next tonext'sremoved_commandandremoved_flag. Both sides' other changes merged without conflict.apps/cli/src/telemetry/command-telemetry.unit.test.ts: Useddevelop'sFlag.LiteralsAPI withnext's test names (no 'Go parity' wording) and its single-line test layout.docs/adr/README.md: Kept both new ADR index entries:next's 'Go CLI Removal' anddevelop's 'Hotfix Releases'. Both currently use number 0030.knip.jsonc: DroppedgofromignoreBinaries, sincenextremoved the Go toolchain, and keptdevelop's newjactionlintentry.mise.lock: Removed thegoandgolangci-lintlock entries asnextdid, and keptdevelop's newjactionlintandshellcheckentries.mise.toml: Keptnext's removal of the go and golangci-lint tool pins. Kept the[tools]table with only the jactionlint and shellcheck pins thatdevelopadded for GitHub Actions workflow linting..github/workflows/cli-go-api-sync.yml: deleted.github/workflows/cli-go-mirror.yml: deletedapps/cli-go/pkg/config/templates/Dockerfile: deletedapps/cli/src/command-internal/go-proxy.layer.ts: deletedapps/cli/src/command-internal/go-proxy.layer.unit.test.ts: deletedapps/cli/src/commands/db/branch/create/create.command.ts: deletedapps/cli/src/commands/db/branch/delete/delete.command.ts: deletedapps/cli/src/commands/db/branch/list/list.command.ts: deletedapps/cli/src/commands/db/branch/switch/switch.command.ts: deletedFormat and type check
Passed after these changes to the merged tree:
apps/cli/src/commands/functions/download/download.removed-flag.integration.test.ts: Changed the CliOutput/Command import from 'effect/unstable/cli' to 'effect/cli'.apps/cli/src/commands/services/services.integration.test.ts: Renamed the setup option goOutput to outputFlag in the OrioleDB test.@supabase/cli: 3 choices need a decision. Approve to keep them. To reverse one, push the change to
sync/develop-into-nextand reply here with the decision; later syncs follow it.