feat(cli): add experimental stack list - #6509
Conversation
There was a problem hiding this comment.
Superseded by a newer AI review
🤖 AI Review
Only Claude's independent review was available; Codex's review did not complete. All eight Claude findings were verified and confirmed. No major or critical defects were identified.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/list/list.handler.ts:61 |
error-handling |
claude | One unreadable registry entry aborts the entire listing, and the resulting error does not identify the affected stack directory. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/list/list.handler.ts:56 |
consistency |
claude | The legacy output-flag rejection omits an actionable suggestion, causing text output to show the generic --debug troubleshooting hint instead. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/list/list.handler.ts:43 |
maintainability |
claude | renderRuntime handles StackRuntime with a non-exhaustive fallback, so a new runtime variant would silently render its kind rather than cause a compile-time failure. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/list/list.handler.ts:21 |
naming |
claude | compareCodepoint is misnamed because JavaScript string relational comparison orders UTF-16 code units rather than Unicode code points. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/list/list.command.ts:11 |
telemetry |
claude | Passing empty flags and config records to withLegacyCommandInstrumentation has no effect. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/list/list.handler.ts:46 |
readability |
claude | The complete multi-line stack display is embedded in one long template expression, making user-visible output harder to review and modify. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/list/list.integration.test.ts:161 |
test-quality |
claude | A single test combines two independent scenarios: legacy output-flag rejection and registry-read failure. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/list/list.integration.test.ts:42 |
test-quality |
claude | mutationCalls also counts read-only findStack and inspectStack calls, so its name misstates what the assertion measures. |
Stats
Claude findings: 8 · Codex findings: 0 · Confirmed: 8 · Refuted: 0 · Uncertain: 0
Models: claude-opus-5 + gpt-5.6-sol · Trigger: auto · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
92e35d8 to
a3e8e9b
Compare
a3e8e9b to
a0e49b1
Compare
a0e49b1 to
edeccb4
Compare
Supabase CLI previewnpx --yes https://pkg.pr.new/supabase/cli/supabase@edeccb4273a62bdd4e9316aaf6cf45353ec4e4cdPreview package for commit |
|
/ai-review |
There was a problem hiding this comment.
🤖 AI Review
Only Claude's independent review was available; the Codex pass did not complete. Three findings are confirmed: one minor telemetry classification issue and two test-quality issues. Two findings are refuted because fail-closed discovery is deliberate existing behavior and the empty JSON case adds no distinct branch beyond existing coverage.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/list/list.handler.ts:34 |
error-handling |
claude | All stack-discovery failures are classified as invalid configuration, including registry filesystem and permission failures. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/list/list.integration.test.ts:172 |
test-coverage |
claude | The two error tests can pass with an unexpected error type because their substantive assertions are conditional on instanceof LegacyExperimentalStackListError without asserting that type. |
| ⚪ NIT | packages/stack/src/public/effect-stack.integration.test.ts:1015 |
test-quality |
claude | The new failure-inspection block duplicates the immediately preceding StackStateInvalidError assertion. |
Refuted findings (kept for transparency, not posted as review comments)
packages/stack/src/public/EffectStack.ts:1101(reliability): A single unreadable or unsupported registry entry aborts the entire list operation and prevents healthy stacks from being returned.
Refuted: The propagation is deliberate fail-closed behavior, not introduced by this change. trusted/packages/stack/src/public/EffectStack.ts:1089-1091 already propagates every read failure except the explicitly recoverable remnant case, and trusted/packages/stack/src/public/effect-stack.integration.test.ts:967-987 explicitly verifies that durable missing-state remnants remain fail-closed. The PR preserves that contract while adding stack-id context.apps/cli/src/commands/experimental/stack/list/list.integration.test.ts:152(test-coverage): There is no dedicated test for an empty registry using JSON output.
Refuted: There is no distinct empty-JSON implementation branch to leave untested. apps/cli/src/commands/experimental/stack/list/list.handler.ts:77-78 sends every non-text result through the same output.success call, already exercised by the structured-output test at :116-136, while the empty-array result is independently exercised at :152-159.
Stats
Claude findings: 5 · Codex findings: 0 · Confirmed: 3 · Refuted: 2 · Uncertain: 0
Models: claude-opus-5 + gpt-5.6-sol · Trigger: manual · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
| const mapStackError = (error: StackDiscoveryError) => | ||
| new LegacyExperimentalStackListError({ | ||
| reason: "invalid-config", | ||
| message: error.message, | ||
| suggestion: | ||
| "Inspect the managed stack registry under $SUPABASE_HOME/managed/stacks or ~/.supabase/managed/stacks.", | ||
| cause: error, | ||
| }); |
There was a problem hiding this comment.
🟡 MINOR · error-handling · source: claude
All stack-discovery failures are classified as invalid configuration, including registry filesystem and permission failures.
Evidence: apps/cli/src/commands/experimental/stack/list/list.handler.ts:34-41 always assigns reason "invalid-config". packages/stack/src/public/EffectStack.ts:1089-1097 converts failures from exists/readDirectory into StackStateInvalidError, so platform I/O failures reach that classification.
Suggested fix: Preserve enough platform-error information in the stack API to distinguish registry I/O failures, then give those failures a non-configuration actionability and targeted filesystem guidance.
| if (Option.isSome(error) && error.value instanceof LegacyExperimentalStackListError) { | ||
| expect(error.value.message).toContain("legacy -o/--output flag"); | ||
| expect(error.value.suggestion).toContain("--output-format"); | ||
| expect(error.value[ErrorActionabilityId]).toEqual(actionability.provideFlags); | ||
| } |
There was a problem hiding this comment.
🟡 MINOR · test-coverage · source: claude
The two error tests can pass with an unexpected error type because their substantive assertions are conditional on instanceof LegacyExperimentalStackListError without asserting that type.
Evidence: apps/cli/src/commands/experimental/stack/list/list.integration.test.ts:169-177 and :199-208 assert only that some error exists before conditionally entering the expected-type branch.
Suggested fix: Assert toBeInstanceOf(LegacyExperimentalStackListError) before narrowing in both tests.
| if (Exit.isFailure(listed)) { | ||
| const error = Cause.findErrorOption(listed.cause); | ||
| expect(Option.isSome(error)).toBe(true); | ||
| if (Option.isSome(error)) { | ||
| expect(error.value).toBeInstanceOf(StackStateInvalidError); | ||
| if (error.value instanceof StackStateInvalidError) { | ||
| expect(error.value.stackId).toBe(orphanId); | ||
| expect(error.value.message).toContain(orphanId); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
⚪ NIT · test-quality · source: claude
The new failure-inspection block duplicates the immediately preceding StackStateInvalidError assertion.
Evidence: packages/stack/src/public/effect-stack.integration.test.ts:1009-1014 already extracts the failure and asserts StackStateInvalidError; :1015-1025 repeats both operations before checking stackId and message.
Suggested fix: Remove the older assertion block and retain the new narrowed block containing the additional checks.
supabase experimental stack listdiscovers managed stacks across branches and worktrees, including stopped and unconfigured stacks. It reports persisted identity, runtime, and desired lifecycle in a stable order, with text and structured output.This PR builds on experimental stack status and reads the managed registry without loading project configuration or contacting stack owners.