Skip to content

Rust CLI: complete usage-error exit-code coverage (exit 2, matching Node) - #332

Merged
poiley merged 10 commits into
rust-cli-portfrom
rust-cli-usage-exit-2-r2
Jul 22, 2026
Merged

Rust CLI: complete usage-error exit-code coverage (exit 2, matching Node)#332
poiley merged 10 commits into
rust-cli-portfrom
rust-cli-usage-exit-2-r2

Conversation

@poiley

@poiley poiley commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces #331 (its base was force-rebased, making it unmergeable). The rebased port added a CliError usage-error mechanism with exit 2; this PR finishes the job.

What changed

  • ~100 remaining sites converted to crate::usage_error. The CliError mechanism covered generated commands and some friendly families, but unknown flags, unknown boolean/short flags, missing flag values, unexpected arguments, and unknown subcommands still exited 1 across agent, auth, domains, doctor, inbox, memories, org-secrets, payloads, search, emails, mail, wake, routes, functions, and payments.
  • Auth post-parse invocation-shape checks now exit 2: missing/extra positionals, --signup-code without an email, missing --accept-terms, conflicting or absent verification-code sources, --code-from-stdin on a TTY. Also the unknown functions template name.
  • Chain-walk in main_entry: exit-code selection now searches the whole error chain for CliError instead of downcasting only the outermost error, so a usage error wrapped by context() still exits 2. Regression test added.
  • Deliberately left at exit 1 (Node CLI also exits 1 per the parity fixtures): describe unknown operation, config value validation, doctor failures, pending-auth state mismatches, unreadable --code-from-file, unset --code-from-env, browser login denial.
  • tests/payments.rs: payments charge --bogus expectation updated from 1 to 2, consistent with its own missing-value assertion three lines down and with the Node CLI.
  • tests/payloads.rs: added the same root-level usage_error stub the functions test already uses.

Validation

  • make rust-cli-check (fmt, clippy -D warnings, doc, all tests)
  • make cli-parity: 453/453 cases green, command-surface parity, operation coverage, help sweep with --compare-flags

🤖 Generated with Claude Code

etbyrd and others added 2 commits July 20, 2026 09:04
The port's CliError usage mechanism (exit 2 for invalid invocations,
matching the Node CLI's oclif convention) covered the generated-command
path and a few friendly families but left ~100 parse/validation sites
returning plain anyhow errors that exited 1: unknown flags, unknown
boolean/short flags, missing flag values, unexpected arguments, and
unknown subcommands across agent, auth, domains, doctor, inbox, memories,
org-secrets, payloads, search, emails, mail, wake, routes, functions, and
payments; plus auth post-parse invocation-shape checks (missing or extra
positionals, --signup-code without an email, missing --accept-terms,
conflicting or absent verification-code sources, --code-from-stdin on a
TTY) and the unknown functions template. All now route through
crate::usage_error.

main_entry now walks the full error chain to find CliError instead of
downcasting only the outermost error, so a usage error wrapped by
context() still exits 2. Added a regression test.

State-dependent failures stay at exit 1, matching the Node CLI per the
parity fixtures: describe unknown operation, config value validation,
doctor failures, pending-auth mismatches, unreadable --code-from-file,
unset --code-from-env, browser login denial.

payments integration test updated: `payments charge --bogus` now exits 2
like its missing-value sibling assertion and the Node CLI.

Validation: make rust-cli-check, cli-parity (453 cases green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
cli-rust/src/auth_commands.rs Updates auth parsing and verification-code paths so usage errors keep the expected exit code.
cli-rust/src/lib.rs Walks the error chain when choosing the process exit code for CLI usage errors.
cli-rust/src/doctor_commands.rs Converts doctor argument errors to usage errors and removes the unused macro import.
test-fixtures/cli-parity/cases.json Updates parity expectations for usage-error exit code behavior.

Reviews (8): Last reviewed commit: "Merge rust-cli-port (5 fixes already lan..." | Re-trigger Greptile

Comment thread cli-rust/src/auth_commands.rs
poiley and others added 2 commits July 20, 2026 10:04
Greptile: the --code-from-stdin TTY usage error was flattened by
map_err(anyhow!) before reaching exit-code selection, so it exited 1.
The closure now re-marks usage errors after adding the flag prefix while
leaving genuine read failures at exit 1.

Also convert the sibling body-flag validation sites the same sweep missed:
the --raw-body/--body-file conflict and invalid-JSON values for both flags
(file-read failures stay runtime).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The parity fixtures pin invalid --raw-body JSON to exit 1 on both CLIs
(the Node CLI treats body construction as runtime, not parse), so the
--raw-body/--body-file conflict and invalid-JSON conversions from the
previous commit contradicted Node. Reverted; the --code-from-stdin marker
preservation stands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@etbyrd
etbyrd force-pushed the rust-cli-port branch 2 times, most recently from 64ae18d to 8303e82 Compare July 21, 2026 00:53
The base snapshot was force-rebased again (f1b5005 -> 411c26c), rewriting
auth_commands.rs. Took the new upstream auth code and reapplied the usage
conversions on top (24 parse/validation sites plus the --code-from-stdin
marker preservation); upstream's new auth error sites are state/runtime
failures and correctly stay at exit 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@etbyrd
etbyrd force-pushed the rust-cli-port branch 5 times, most recently from 322c5b5 to 0765fa5 Compare July 21, 2026 17:25
# Conflicts:
#	.github/workflows/rust-cli-release.yml
#	.github/workflows/sdk-checks.yml
#	Makefile
#	cli-rust/src/agent_commands.rs
#	cli-rust/src/auth_commands.rs
#	cli-rust/src/completion_commands.rs
#	cli-rust/src/config.rs
#	cli-rust/src/doctor_commands.rs
#	cli-rust/src/domains_commands.rs
#	cli-rust/src/emails_commands.rs
#	cli-rust/src/friendly.rs
#	cli-rust/src/functions_commands.rs
#	cli-rust/src/help_snapshots.generated.rs
#	cli-rust/src/inbox_commands.rs
#	cli-rust/src/lib.rs
#	cli-rust/src/mail_commands.rs
#	cli-rust/src/memories_commands.rs
#	cli-rust/src/org_secrets.rs
#	cli-rust/src/payloads.rs
#	cli-rust/src/payments.rs
#	cli-rust/src/routes_commands.rs
#	cli-rust/src/search_commands.rs
#	cli-rust/src/wake_commands.rs
#	cli-rust/tests/completion_commands.rs
#	cli-rust/tests/config.rs
#	cli-rust/tests/payloads.rs
#	cli-rust/tests/payments.rs
#	scripts/generate-rust-cli-help-snapshots.mjs
#	scripts/run-cli-help-sweep.mjs
#	scripts/run-cli-parity.mjs
#	test-fixtures/cli-parity/cases.json
@poiley poiley mentioned this pull request Jul 21, 2026
poiley and others added 3 commits July 21, 2026 15:08
A deterministic differential flag fuzzer (walks the operation manifest,
runs seeded flag combos through both CLIs against a mock, diffs
exit/stdout/stderr/request) surfaced a class of required-argument
validations the earlier exit-code pass missed: they returned plain errors
(exit 1) where the Node CLI exits 2.

Converted, each verified Node=2/Rust=2 against a live differential:
- describe with no command id
- memories set with no key
- chat with no recipient
- payloads push with no file path
- payloads pull with no merkle root
- search with no query
- routes reorder with no --set
- send/chat when no --from is given and no active verified outbound domain
  resolves (pick_default_from_address), plus the send shortcut's
  --from-required guard

Regression-guarded: reply with no --from stays exit 0 (it defaults from the
replied-to email and never hits the domain lookup), memories set with a key
but no value stays exit 1, and describe of an unknown operation stays exit
1 (distinct from the missing-argument path). The shared resolve_outbound_from
helper used by reply is deliberately left at exit 1.

Eight lock-in fixtures assert exit 2 + empty stdout + request behavior on
both CLIs (stderr wording is intentionally divergent so it isn't asserted).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A live end-to-end differential against a staging org (every command run
through both CLIs) found more missing-required-flag validations exiting 1
where the Node CLI exits 2: `send --to`, `org secrets set --key`, and
`reply --id`. The shared `flag_one(...).ok_or_else(|| anyhow!("Missing
required --{name}"))` helper was already usage_error in functions_commands
and the generated path, but still plain anyhow in org_secrets, wake, and
mail. Convert those three to crate::usage_error.

Verified each Node=2/Rust=2 against the live org; endpoints test (already
correct) stays 2 as a regression guard. Three lock-in fixtures assert exit
2 + empty stdout on both CLIs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	cli-rust/tests/config.rs
#	test-fixtures/cli-parity/cases.json
@poiley
poiley merged commit 7c7a621 into rust-cli-port Jul 22, 2026
17 checks passed
@poiley
poiley deleted the rust-cli-usage-exit-2-r2 branch July 22, 2026 22:20
poiley added a commit that referenced this pull request Jul 22, 2026
…339)

PR #335 removed compare:false and added stdout/stderr assertions to the 48
request-only "generated ... alias request" fixtures, but the conflict
resolution when landing #332 reverted them via an ours-wins union. Re-apply
the transformation: assert stdout "null\n" + empty stderr + full Node<->Rust
cross-compare on each (the mocks already return {success:true,data:null}).

make cli-parity: 485 cases green.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants