Skip to content

fix(cli): exit 2 for remaining flag validation errors - #6639

Merged
jrusso1020 merged 2 commits into
mainfrom
fix/cli-usage-exit-codes
Oct 28, 2026
Merged

jrusso1020 merged 2 commits into
mainfrom
fix/cli-usage-exit-codes

Conversation

@jrusso1020

@jrusso1020 jrusso1020 commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

What

Flag and argument checks that commands make themselves now exit 2 and, under --json, answer with a usage_error envelope. Before this they exited 1 and reported runtime_error, even though the docs say a usage error exits 2. This uses the existing failUsage() / CliUsageError path from earlier in the stack.

Also, an unattended spinner now writes its start message as one plain line on stderr, next to its outcome. Before, only the final outcome was written, so a tool watching init output never saw the "Transcoding" start line. Progress updates stay silent, so a long remove-background run or a Chrome download does not print a line per update.

Why

Agents and scripts branch on the exit code. A bad flag should read as "fix your command" (2), not "the run failed" (1).

Related work

Follow-up to #6622 (stacked on fix/cli-agent-mode-json).

How

Converted to exit 2 / usage_error:

  • lambda render and render-batch: missing --width/--height, invalid --fps, positive-integer flags (--width, --height, --chunk-size, --max-parallel-chunks, --target-chunk-frames, --max-concurrent, --wait-interval-ms, --memory), --format/--codec/--quality/--chrome-source/--video-arch, --output-resolution, lambda deploy --concurrency, lambda sites <bad verb>, unknown lambda subcommand
  • cloudrun render and render-batch: the same checks, plus --max-instances/--timeout on deploy, cloudrun sites <bad verb>, and unknown cloudrun subcommand. Render flags are now checked before the stack state is read and before any upload. Before this, in render-batch a bad flag only showed up as N failed renders.
  • capture: --capture-budget/--max-screenshots, a URL together with --local-file, and an invalid URL
  • feedback: out-of-range --rating, no rating and no comment, --file-issue without a rating, bad --source
  • benchmark --runs (now checked before the project lookup)
  • tts: no text given, --speed out of range
  • init: --template (renamed), -V, --video together with --audio
  • snapshot: conflicting --at/--range/--composition, --review clashes, --range/--at parse, empty --composition, --keep name collision, and an invalid --frames (abc, 0, -3, 2.5). Before this, a bad --frames was silently replaced with 5 frames.
  • compare (fewer than 2 paths, --labels, --at, --cols) and grade-compare (--grades vs --luts, empty --luts), via failCaught, which now keeps a CliUsageError as a usage error
  • check: --range, --frame-check, --layout, --caption-zone (options are now parsed before the project lookup)
  • normalize-audio: --tolerance, --lufs, the same element for reference and target
  • media-treatment: --selector-index, --grading/--apply/--clear combinations, --capabilities combinations, --file outside the project, missing --selector, --analyze combinations
  • play and present: --port, --user-data-dir without --browser-path, --remote-debugging-port and its dependencies
  • unknown models, docs, browser and telemetry subcommands or topics
  • cloud render with conflicting inputs, and cloud delete without --no-confirm when it cannot prompt (same as publish/upgrade without --yes)
  • --variables given twice or as bad inline JSON (an unreadable or bad --variables-file stays exit 1)

Still exit 1: missing or unreadable files, bad batch-file contents, missing stack state, network and render failures. Some flag checks stay at exit 1 on purpose, because they depend on the project or the machine and retyping the command will not fix them. Two examples:

  • render --resolution whose aspect ratio does not match the composition. The flag is valid; it conflicts with what the project contains.
  • render --gpu --docker on an arm64 machine. The same command works on x86_64; it fails because of the host.

Test plan

  • Unit tests added/updated

  • Manual testing performed

  • Documentation updated (if applicable)

  • Comments follow CONTRIBUTING.md "Comments"

  • cli.agentMode.test.ts: a new table runs about 50 of the converted checks as an agent would. Each must exit 2. Commands with --json must also answer with a usage_error envelope that names the problem.

  • Updated unit tests for capture, feedback, check, init, media-treatment and play/present --port. The spinner test in ui/prompts.test.ts now checks that start and outcome are each written as one plain line and progress writes nothing. snapshotSampling.test.ts covers the --frames parse, and the agent-mode table includes snapshot --frames abc and --frames -3.

  • In packages/cli: typecheck, oxlint, oxfmt check and fallow audit pass. In the full CLI suite, 6688 tests pass and 11 fail. The 11 are Chrome-launch tests (*.process.test.ts, snapshot.lateImages.browser) that time out on this machine. They fail the same way on the base branch.

🤖 Generated with Claude Code

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits

The exit-code conversions hold up. I ran 15 of them as an agent in a scaffolded project, and each exited 2 with a usage_error envelope naming the problem. The touched suites pass locally (2,287 tests). Keeping project- and machine-dependent checks at exit 1 (--aspect-ratio vs the composition, --gpu --docker on arm64) is right; an agent can't fix those by retyping the command.

  • blocker — Comments is red: the comment ratchet rejects raised comment shares in commands/check.ts (+1 line), commands/cloudrun.ts (+2), utils/jsonError.ts (+1) and utils/variables.ts (+2). Cut those lines, or move the explanation into a name.
Scope and notes

This PR is stacked, so CI doesn't run; I tested locally. Not from this PR, but in the stack (#6622): render --json writes its human header and progress bar to stdout. A failure after that point, such as render --variables '{bad' --json, exits 2 with no envelope, because the boundary sees that stdout was already written. Also not from this PR: snapshot --frames abc and --frames -3 are accepted silently (exit 0, with the default frames).

— Genesis

Comment thread packages/cli/src/ui/prompts.ts Resolved
@jrusso1020
jrusso1020 force-pushed the fix/cli-usage-exit-codes branch from 25132f4 to b4f433c Compare October 28, 2026 08:10

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 2 resolved, 0 open, 0 new · 25132f4 → b4f433c. Comments is green at this head, and snapshot --frames abc|0|-3 exits 2 with a usage_error. Stacked, so CI has not run; the touched suites, tsc, oxlint and oxfmt pass locally.

— Genesis

@jrusso1020
jrusso1020 force-pushed the fix/cli-agent-mode-json branch from 280f7dc to dab4b83 Compare October 28, 2026 10:04
@jrusso1020
jrusso1020 force-pushed the fix/cli-usage-exit-codes branch from b4f433c to cf67718 Compare October 28, 2026 10:04
heygengenesis[bot]
heygengenesis Bot previously approved these changes Oct 28, 2026

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 0 resolved, 0 open, 0 new · b4f433c → cf67718. Rebase only: both commits match the approved ones (range-diff =).

— Genesis

Base automatically changed from fix/cli-agent-mode-json to main October 28, 2026 11:05
@jrusso1020
jrusso1020 dismissed heygengenesis[bot]’s stale review October 28, 2026 11:05

The base branch was changed.

@mintlify

mintlify Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 28, 2026, 11:17 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

jrusso1020 and others added 2 commits October 28, 2026 11:07
Flag and argument checks that commands make themselves still exited 1 and
reported runtime_error under --json. They now take the existing usage-error
path (failUsage / CliUsageError): exit 2 and a usage_error envelope. This
covers lambda and cloudrun render/render-batch (--width/--height, --fps,
positive-number, enum and --output-resolution flags, unknown subcommands),
capture, feedback, benchmark --runs, tts, init, snapshot, compare,
grade-compare, check, normalize-audio, media-treatment, play/present --port,
models, docs, browser and telemetry unknown subcommands, cloud render
conflicting inputs, cloud delete without --no-confirm, and inline --variables.
A few checks now run before project or stack lookup so a bad flag is reported
first. Runtime failures (missing files, network, render errors) still exit 1.

An unattended spinner now writes its start and progress text as plain stderr
lines, not only the outcome, so tools watching init can see "Transcoding".

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…p added comments

- `snapshot --frames abc` (or 0, -3, 2.5) is now a usage error: exit 2, and a
  usage_error envelope under --json. It used to fall back to 5 frames silently.
- An unattended spinner prints its start line once and its outcome, but no
  longer one line per progress update, which flooded stderr during
  remove-background and the Chrome download.
- Removes the comment lines this branch added, so the comment ratchet passes.
- The render --json test now expects exit 2 and usage_error for bad inline
  --variables.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@github-actions

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 182 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 0 resolved, 0 open, 0 new · cf67718 → ed900a5. Rebase onto main only; both commits match the approved ones (range-diff =). This is the first full CI run on this PR, and every workflow passed, CI included.

— Genesis

@jrusso1020
jrusso1020 added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 2d3033c Oct 28, 2026
72 checks passed
@jrusso1020
jrusso1020 deleted the fix/cli-usage-exit-codes branch October 28, 2026 11:45
@heygengenesis heygengenesis Bot mentioned this pull request Oct 28, 2026

This branch was successfully deployed

1 active deployment
staging - docs — ed900a55 Deployed Oct 28, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant