diff --git a/artifacts/requirements.yaml b/artifacts/requirements.yaml index f336ef3..69a93d4 100644 --- a/artifacts/requirements.yaml +++ b/artifacts/requirements.yaml @@ -6497,3 +6497,35 @@ artifacts: created-by: ai-assisted model: claude-opus-4-8 timestamp: 2026-06-06T01:49:27Z + - id: REQ-209 + type: requirement + title: "Top-level path args (`--project`/`--schemas`) must be position-independent (clap `global`)" + status: implemented + description: | + Finding (issue #500, hit while implementing #488). `--project`/`-p` and + `--schemas` were declared on the top-level Cli without `global = true`, so + they only parsed BEFORE the subcommand: `rivet -p X validate` worked but + `rivet validate -p X` errored — and with `--format json` produced empty + stdout (error on stderr), a confusing failure for anything shelling out to + rivet (it cost a debug cycle in the #488 `--new-since` subprocess). + + Fix: mark both args `global = true` so they work in any position. Additive + and low-risk — the pre-subcommand form still parses. + + Acceptance: + - `cargo test -p rivet-cli --bin rivet cli_global_args_tests` passes + (`--project`/`--schemas` parse after the subcommand; `-p` before still parses). + - `rivet validate --project .` and `rivet validate -p . --format json` + both succeed (previously errored). + - `rivet validate` on the rivet repo still PASS. + tags: [cli, ergonomics, clap, args, dogfooding] + fields: + priority: should + category: functional + links: + - type: traces-to + target: REQ-007 + provenance: + created-by: ai-assisted + model: claude-opus-4-8 + timestamp: 2026-06-06T02:18:14Z diff --git a/rivet-cli/src/main.rs b/rivet-cli/src/main.rs index 71fb6f7..891f702 100644 --- a/rivet-cli/src/main.rs +++ b/rivet-cli/src/main.rs @@ -193,12 +193,16 @@ fn check_for_updates() { #[derive(Parser)] #[command(name = "rivet", about = "SDLC artifact traceability and validation", version = build_version())] struct Cli { - /// Path to the project directory (containing rivet.yaml) - #[arg(short, long, default_value = ".")] + /// Path to the project directory (containing rivet.yaml). + /// `global` so it works in any position (`rivet validate -p X` as well as + /// `rivet -p X validate`) — a non-global path arg silently errors when + /// placed after the subcommand, which is surprising for tools (#500). + #[arg(short, long, default_value = ".", global = true)] project: PathBuf, - /// Path to schemas directory - #[arg(long)] + /// Path to schemas directory. `global` for the same position-independence + /// as `--project` (#500). + #[arg(long, global = true)] schemas: Option, /// Increase verbosity @@ -16990,6 +16994,32 @@ fn print_diagnostics_with_remediation( // ── LSP unit tests ───────────────────────────────────────────────────── +#[cfg(test)] +mod cli_global_args_tests { + use super::Cli; + use clap::Parser; + + // rivet: verifies REQ-209 + #[test] + fn project_and_schemas_parse_after_the_subcommand() { + // The #500 regression: a non-global `--project` errored when placed + // after the subcommand. With `global = true` both positions parse. + let after = + Cli::try_parse_from(["rivet", "validate", "--project", "/x", "--schemas", "/s"]) + .expect("`--project`/`--schemas` must parse AFTER the subcommand"); + assert_eq!(after.project.to_str(), Some("/x")); + assert_eq!( + after.schemas.as_deref().and_then(|p| p.to_str()), + Some("/s") + ); + + // The pre-subcommand form still works (no regression). + let before = Cli::try_parse_from(["rivet", "-p", "/y", "validate"]) + .expect("`-p` before the subcommand must still parse"); + assert_eq!(before.project.to_str(), Some("/y")); + } +} + #[cfg(test)] mod new_since_diff_tests { use super::diff_new_diagnostics;