fix(cli): name the changed setting and the fix when a saved stack rejects a config change - #6944
Conversation
…ects a config change Maps each incompatible plan path (endpoint port, db major version) to the config.toml key or SUPABASE_*_PORT env var that set it, shows the saved and requested values, and suggests reverting it or running the stack's exact destroy command with a data-loss warning.
…mplete Always suggest the exact --stack-id destroy command instead of an unquoted --stack <name> that re-resolves against the caller's current workdir, carry structured per-change records (service, path, key, saved, requested) plus the recreate command on the JSON/stream-json error envelope via MachineErrorContext, and collect every incompatible member into one error instead of reporting only the first, deduplicating shared API port lines. Use the production planSupabaseComposition planner in the start handler's integration tests instead of a hand-rolled approximation, and derive the port-setting table and createCreations' env-override calls from one shared definition per port to prevent drift.
There was a problem hiding this comment.
🤖 AI Review
Verified all 10 supplied findings against the checked-out code and trusted conventions. Three overlapping pairs merge into seven confirmed findings: two minor error-guidance issues and five nits. Full PostgreSQL pins can produce identical displayed major versions despite an incompatibility, and artifact-version mismatches receive advice to revert a setting the CLI does not expose. No critical or major issue was established. Locations below use the actual new-side line numbers.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/start/start.handler.ts:229 |
error-messages |
claude | When saved and requested PostgreSQL pins differ within the same major version, the error displays identical major versions and suggests reverting a major_version setting that already matches. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/start/start.handler.ts:328 |
error-handling |
claude+codex | Artifact-version mismatches receive instructions to revert a setting that has no config.toml key or environment override in the CLI. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/start/start.handler.ts:328 |
wording |
claude+codex | Multiple changed settings produce the grammatically incorrect suggestion 'Revert the settings above to its saved value', although the changes are joined inline. |
| ⚪ NIT | apps/cli/src/command-internal/stack-config.ts:173 |
documentation |
claude+codex | The PortSetting comment promises automatic consistency between creation wiring and endpoint-setting lookup that the implementation does not enforce. |
| ⚪ NIT | packages/stack/src/effect.ts:71 |
api-surface |
claude | The PR adds a planner re-export to the main Effect entrypoint for a new consumer that exists only in a CLI integration-test fake. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/start/start.handler.ts:299 |
code-quality |
claude | destroyCommandFor accepts an unused name property, and the same rejection constructs the destroy command separately for prose and structured output. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/start/start.handler.ts:177 |
comment-style |
codex | The new internal-helper comments narrate implementation mechanics prohibited by the trusted repository comment policy. |
Stats
Claude findings: 6 · Codex findings: 4 · Confirmed: 7 · Refuted: 0 · Uncertain: 0
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: auto · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
…name-the-changed-setting-and-the-fix-when-a-saved-stack
…in the stack error Same-major Postgres version differences and catalog-pinned artifact versions have no config.toml key or env var, so the incompatible-stack error no longer suggests reverting major_version or an editable setting for them: it names the full saved and requested builds, drops the revert sentence when nothing is editable, and explains the fix in plain language. Mixed rejections keep the revert advice for the editable keys only, with singular/plural grammar matching their count. destroyCommandFor now takes only the stack id (it never read the name) and is computed once per rejection, reused for both the error suggestion and the JSON/stream-json envelope's recreate_command. Move planSupabaseComposition's export from the main @supabase/stack/effect entrypoint to @supabase/stack/testing, since the CLI only needs it to build a realistic test double for its own composition plan.
avallete
left a comment
There was a problem hiding this comment.
Dogfooded from source on 0247b903b (Docker and native runtimes, usebasejump__basejump sample) and reviewed. Nice improvement — the messages read well and the structured envelope works. One correctness issue in the advice text is worth fixing before merge; the rest is non-blocking.
Dogfood results
| Scenario | Result |
|---|---|
[db] port changed in config.toml |
✅ [db] port: saved A, requested B, singular revert advice, --stack-id matches stack status |
SUPABASE_DB_PORT from shell env and from supabase/.env |
✅ both labelled SUPABASE_DB_PORT |
[api] port changed (5 services share it) |
✅ exactly one [api] port line |
automatic → fixed [api] port |
✅ saved automatic, requested N |
[db] major_version 17 → 15, via toml and SUPABASE_DB_MAJOR_VERSION |
✅ saved 17, requested 15, env label when env-driven |
| db + api + studio ports changed together | ✅ one error, plural advice |
--output-format json / stream-json |
✅ stack_changes + recreate_command present, non-zero exit |
Named stack (--stack dogfood-a) |
✅ (stack dogfood-a) in the suggestion |
| Revert the setting → start | ✅ same stack id, data preserved (rejected attempt did not modify the stack) |
Run recreate_command verbatim from another cwd |
✅ destroys the right stack; next start succeeds |
Unchanged restart / only --exclude set changed |
✅ no false rejection |
Native runtime ([db] port change) |
✅ same message shape |
Top-level supabase start with stack = true |
✅ same handler and message |
Fixed → automatic [api] port (key removed) |
ℹ️ start succeeds and keeps the saved port (planner sticky-port behaviour, locked by the "accepts a shared API port going from fixed back to automatic" test). The PR description's [api] port: saved 54321, requested automatic example doesn't occur for this direction — worth updating the example. |
Not reachable without a catalog change, so not dogfooded: same-major Postgres build mismatch, artifact-version mismatch. Integration file passes locally (39/39); types:check clean in apps/cli and packages/stack.
Review
- Should fix: mixed editable + non-editable rejection advises a revert that cannot unblock start (inline).
- Non-blocking: text message has no headline; several test assertions are guarded by
instanceofand can pass vacuously; one duplicated test;stack_changesvs text dedupe contract (inline). - Minor nit:
recreate_commandomits--yes, andstack destroyin JSON mode requires it, so an agent running it verbatim hits the confirmation error. Leaving--yesout is the safer default for a data-deleting command; maybe just mention it in the suggestion or docs.
…tart A rejected config change that mixed an editable key (e.g. a port) with a non-editable one (a catalog-pinned artifact or Postgres build) suggested reverting the editable key and dropped the non-editable change, but reverting it alone can't unblock start. Use the destroy-only wording whenever any non-editable change is present, and lead the change-list message with a sentence so it reads standalone in text output. Also document the `stack_changes`/`recreate_command` JSON contract and the intentional `--yes` omission, and make several integration-test assertions that lived inside `if (error instanceof StackCommandStartError)` fail loudly instead of passing vacuously for a different error type.
|
Thanks for the thorough dogfooding. Addressed in cdaef16; the inline threads have the details. On the summary notes:
|
When
supabase start(with[experimental] stack) refuses a change to a saved stack, the error now says which setting changed and what to do about it. It used to report internal paths such asendpoints.httpwith a generic suggestion.config.tomlkey, or theSUPABASE_*environment variable that set it, with the saved and requested values, after a lead-in saying the saved stack cannot adopt them. For example:[db] major_version: saved 17, requested 15, or[api] port: saved automatic, requested 54999.[api] portappears once.supabase stack destroy --stack-id <id>to recreate it. It warns that recreating deletes the local database data. The ID is used so the command targets this stack regardless of the current directory. When a change can't be reverted, such as a catalog-pinned artifact or Postgres build from a CLI upgrade, the suggestion offers only the destroy command.stack_changeswith service, path, key, saved and requested values, plusrecreate_command), so agents don't have to parse prose.stack_changeskeeps one entry per affected service, andrecreate_commandomits--yes, which a JSON-mode destroy requires.Port settings are now defined once in
stack-config.ts, shared by config translation and error attribution, so a new port can't be added to one and missed in the other. The tests use the real composition planner.