feat(cli): add stack command options and destruction - #6576
Conversation
There was a problem hiding this comment.
Superseded by a newer AI review
🤖 AI Review
Adjudicated 10 Claude findings and 1 Codex finding against the checked-out code. The major Codex finding is confirmed: excluding REST or Analytics while Studio remains enabled produces a configuration rejected by the stack compiler. Six lower-severity findings are also confirmed, chiefly around corrupt-registry recovery, duplicated routing knowledge, validation precedence, and error construction. Four Claude findings are refuted by concrete code or trusted documentation. No critical issues were found.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | apps/cli/src/commands/experimental/stack/start/start.handler.ts:104 |
correctness |
codex | Excluding REST or Analytics while Studio remains enabled creates an invalid capability graph, causing otherwise documented --exclude invocations to fail during start. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/stop/stop.handler.ts:104 |
error-handling |
claude | Bulk stop permanently exits nonzero for each unreadable registry entry, while the CLI provides no command capable of removing such an entry. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/start/start.handler.ts:89 |
maintainability |
claude | The CLI duplicates package-internal knowledge of which capabilities use the API listener even though the supervisor already disables listeners with no usable routes. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/start/start.options.ts:2 |
maintainability |
claude | The CLI manually duplicates the package capability-name list, so newly added optional capabilities will remain unavailable to --exclude until this separate list is updated. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/start/start.handler.ts:141 |
validation-order |
claude | Exclusion validation now masks the established legacy output-flag rejection when both inputs are invalid. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/stop/stop.handler.ts:105 |
error-handling |
claude | Bulk stop concatenates every failure into one unbounded line and stores an untyped aggregate object as the error cause. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/destroy/destroy.handler.ts:19 |
consistency |
claude | Destroy materializes suggestion: undefined instead of conditionally omitting the optional property like the sibling handlers. |
Refuted findings (kept for transparency, not posted as review comments)
apps/cli/src/commands/experimental/stack/start/start.handler.ts:66(compatibility): The stack-backed top-level start alias incompatibly rejects legacy service names accepted by legacy start.
Refuted: Trusted apps/cli/docs/stack-commands.md:25-27 explicitly states that the selected backend determines accepted flags and that selecting the stack backend makes top-level start an alias of the unstable stack command. The trusted document also states at lines 3-5 that this interface is outside the compatibility promise. Different service vocabulary is therefore documented backend behavior, not a compatibility regression.apps/cli/src/commands/experimental/stack/destroy/destroy.command.ts:22(service-wiring): Destroy can reach promptYesNo's Stdin-dependent branch without providing stdinLayer, making piped confirmation fail with a missing service.
Refuted: destroy.handler.ts:101-106 rejects non-TTY invocations unless yes is already true. With yes=true, prompt-yes-no.ts:39-41 returns before requesting Tty or Stdin; otherwise the guard guarantees text output on a TTY, which uses promptConfirm at lines 64-66. The Stdin branch is unreachable in the implemented command.apps/cli/src/command-internal/db-bootstrap/shadow-cache.unit.test.ts:64(test-configuration): A 30-second timeout was unnecessarily added to a pure SHA-256 test.
Refuted: shadowCacheKey is not a simple SHA-256 operation: shadow-cache.ts:246 calls synchronous scryptSync. The test computes a base key and then recomputes it for 21 mutations, so the explicit timeout covers deliberately expensive password-hardening work.apps/cli/src/commands/experimental/stack/destroy/destroy.integration.test.ts:78(test-coverage): The destroy tests inherit ambient SUPABASE_YES, allowing that environment variable to invert confirmation assertions.
Refuted: The cited helper does the opposite of the claim: mocks.ts:651-663 deletes every current environment variable before applying the supplied map. processEnvLayer({}) therefore clears SUPABASE_YES for each test and restores the environment afterward. Command-specific environment cases are absent, but the asserted ambient-environment flakiness does not exist.
Stats
Claude findings: 10 · Codex findings: 1 · Confirmed: 7 · Refuted: 4 · 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.
|
/ai-review |
There was a problem hiding this comment.
🤖 AI Review
Both reviews completed. All nine deduplicated findings were verified: five minor correctness concerns and four nits; no major or critical defects were found.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/destroy/destroy.errors.ts:17 |
telemetry |
claude | Declining the destroy confirmation is classified as actionable invalid input instead of user cancellation. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/destroy/destroy.handler.ts:34 |
error-classification |
codex | Container-engine and runtime-mismatch errors handled by the destroy command fall through to the unknown classification. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/stop/stop.handler.ts:94 |
error-classification |
claude+codex | Bulk stop replaces every per-stack failure classification and suggestion with reason lifecycle, producing misleading invalid-config telemetry. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/stop/stop.command.ts:8 |
cli-compatibility |
claude | An explicit --all=false loses presence information, allowing it alongside --stack or --stack-id unlike the legacy top-level stop command. |
| 🟡 MINOR | apps/cli/src/commands/experimental/stack/destroy/destroy.handler.ts:101 |
user-experience |
claude | Destroy may open a hidden interactive prompt when stdin is a TTY but stdout is redirected. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/stop/stop.handler.ts:87 |
user-experience |
claude | Bulk-stop skip messages repeat the stack ID. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/destroy/destroy.integration.test.ts:127 |
test-coverage |
claude | Destroy tests omit the interactive acceptance path and rejection of the legacy -o/--output flag. |
| ⚪ NIT | apps/cli/src/commands/experimental/stack/stop/SIDE_EFFECTS.md:25 |
documentation |
claude | The compatibility contract does not document bulk stop's machine-readable success payload. |
| ⚪ NIT | apps/cli/src/command-internal/db-bootstrap/shadow-cache.unit.test.ts:64 |
maintainability |
claude | An unrelated pure hashing test receives a 30-second timeout without any rationale. |
Stats
Claude findings: 8 · Codex findings: 2 · Confirmed: 9 · Refuted: 0 · 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.
…-develop # Conflicts: # packages/stack/src/public/EffectStack.ts # packages/stack/src/runtime/NativeProcess.ts # packages/stack/src/runtime/native-runtime.integration.test.ts # packages/stack/src/supervisor/handles.integration.test.ts
Add service selection, bulk stopping, and explicit destruction to experimental local stacks directly on
develop. The existing config-selected top-levelstartandstopaliases inherit the new options.start --exclude/-xaccepts comma-separated or repeated service names, preserves the project file, and records the effective configuration in stack state. Dependent services are also disabled: excluding REST or Analytics disables Studio.stop --allpreserves data and attempts every readable registered stack. It reports per-stack error details and stopped/failed/skipped counts after processing every readable stack, returning nonzero when any stack could not be verifiably stopped.stack destroypermanently removes one selected stack after confirmation, with--yesfor unattended execution.This extracts the non-status work from the earlier PR. Status and credential-export changes are excluded because the new status command has not landed on
develop.Supersedes #6517