Skip to content

fix(argv): reject duplicate flags - #945

Merged
jdx merged 2 commits into
agent/derive-clap-migrationfrom
agent/argv-duplicate-flags
Aug 17, 2026
Merged

fix(argv): reject duplicate flags#945
jdx merged 2 commits into
agent/derive-clap-migrationfrom
agent/argv-duplicate-flags

Conversation

@jdx

@jdx jdx commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • reject repeated non-repeatable scalar and boolean flags
  • preserve repeatability for counters, collections, variadic values, and var fields
  • add Clap-style diagnostics and conformance coverage
  • keep parsing allocation-free

This closes a behavior gap exposed by migrating the usage CLI itself: a scalar flag such as --format can no longer silently take its last repeated value.

Stack

Targets the command-alias migration PR. The missing-subcommand diagnostic PR follows this one, then #936.

Checks

  • cargo test -p usage-conformance --test post_binding
  • cargo test -p usage-argv --features diagnostics
  • cargo test -p usage-derive
  • full stacked cargo test --all --all-features
  • full stacked Clippy with warnings denied

This PR was generated by Codex.


Note

Medium Risk
Changes core CLI parsing semantics for duplicate flags across all derived commands; behavior is well-tested but could surface new errors for invocations that previously succeeded silently.

Overview
Non-repeatable flags (scalar booleans and value-taking flags that are not var, counters, or collections) now fail parse with a new Error::DuplicateFlag instead of silently keeping the last occurrence—matching clap-style behavior for cases like repeated --format.

The derive tracks __duplicated_* (and __negated_* for negate pairs) during binding; post-binding checks emit the error. Positive/negative spellings (--color / --no-color) override each other like overrides, but repeating the same spelling still errors. Overrides still win over earlier duplicates (e.g. --file twice then --stdin). Displaced flags clear duplicate state so override flows stay correct.

Diagnostics render: the argument '--jobs' cannot be used multiple times. Conformance tests cover duplicates, repeatability, and negate/override interactions.

Reviewed by Cursor Bugbot for commit 407362e. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 0836e217-5ec9-412e-b876-68cafdfefc9b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR rejects repeated non-repeatable scalar and boolean flags while preserving repetition for counters, collections, variadic values, and var fields.

  • Adds duplicate-state tracking and post-binding validation to generated parsers.
  • Clears duplicate state when an overriding flag displaces the duplicated flag.
  • Distinguishes repeated boolean spellings from positive/negative spellings that override each other.
  • Adds diagnostics and conformance coverage for duplicate and displacement behavior.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
derive/src/codegen.rs Generates duplicate and negation state, clears duplicate state during displacement, and reports duplicate flags during post-binding validation; the previously reported stale-state issue is fixed.
conformance/tests/post_binding.rs Adds regression coverage for duplicate scalar and boolean flags, repeatable flags, displaced duplicates, and positive/negative boolean spellings.
argv/src/lib.rs Adds the borrowed DuplicateFlag error variant used by generated post-binding checks.
argv/src/diagnostic.rs Renders the new duplicate-flag error with a Clap-style message and usage information.

Reviews (2): Last reviewed commit: "fix(derive): reset duplicate state for o..." | Re-trigger Greptile

Comment thread derive/src/codegen.rs

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7f8b07e. Configure here.

Comment thread derive/src/codegen.rs
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Instruction counts

benchmark trend instructions Δ wall (min) Δ
markdown ▁████████ 180,301,068 → 180,363,699 +0.03% 17.50 → 17.32ms -1.03%
startup ▁▆▂▂▅█▆██ 1,226,319 → 1,226,289 -0.00% 1.00 → 0.99ms -0.70%

No instruction-count regression above 1%.

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

Shadow comparison

Parsing mise use -g node@20 against a shadow of mise's committed spec.
Reported, not gated: the shadow grows as the derive learns to express more, so
what to watch is the ratio rather than either column.

usage clap ratio
instructions, cold parse 64525 5895248 91x
usage: argv -> struct                            1052 ns      1.05 µs
clap: build tree + parse -> struct             498957 ns    498.96 µs
clap: parse -> struct, tree reused              23234 ns     23.23 µs
clap: build tree only                          310826 ns    310.83 µs

407362e095c3 vs 02e459bc675b · measured on the runner, not pushed to the history.

@jdx
jdx merged commit 10b5f1f into main Aug 17, 2026
11 checks passed
@jdx
jdx deleted the agent/argv-duplicate-flags branch August 17, 2026 04:26
jdx added a commit that referenced this pull request Aug 17, 2026
…#965)

Stacked on #963, the `usage-rs` facade.

`usage` is now its own first adopter. Its command structs, root, and
command enums use the `usage-rs` facade instead of Clap, and
`--usage-spec` prints `Cli::to_kdl()` from the same tables that parsed
the command line.

## What goes away

- `clap`, `clap_usage`, and the `clap-sort` dev dependency
- `tests/clap_sort.rs`; declaration order is held by the spec
- the duplicated command-effect tables; effects now live on the commands
they describe
- the empty `Sponsors` struct; unit subcommands are supported directly
- runtime checks for relationships the spec can express

## Migration shape

The declarations remain close to their former Clap layout:

- inferred shorts use `#[usage(short, long)]`
- command aliases live on their command structs, e.g. `#[usage(alias =
"c", alias_hidden("complete", "completions"))]`
- positive dependencies use `requires`, including `--cache-key` →
`--usage-cmd` and `--out-dir` → `--multi`
- aliases written on enum variants still merge with struct aliases for
compatibility
- deliberately formatted docs keep `#[usage(verbatim_doc_comment)]`
- path hints keep `value_hint = usage_rs::ValueHint::FilePath` or
`DirPath` from usage’s own runtime type
- subcommand payloads stay unboxed as they were under Clap; `Box<T>`
remains an optional size optimization

The four shell commands are separate derived structs flattening a shared
group. A single destination struct cannot identify which enum variant
selected it, so a macro keeps their common declaration in one place.

## Parity gaps closed by the stack

- required subcommands survive KDL generation (#937)
- strict unknown-flag behavior is inherited by subcommands (#939)
- command-owned visible and hidden aliases are supported (#946)
- duplicate non-repeatable flags are rejected (#945)
- a missing required subcommand prints the available command choices
(#947)
- verbatim doc-comment layout is preserved on commands, fields, and
variants (#949)
- file and directory value hints reach native and emitted completions
(#951)

The emitted spec still intentionally gains metadata the Clap bridge
dropped, including `JDX_USAGE_BIN` on `--usage-bin`, preserved multiline
long help, the manpage description, and the correct binary name `usage`.

## Verification

- `mise run render`
- `cargo test --all --all-features`
- `cargo clippy --all --all-features --all-targets -- -D warnings`
- direct binary checks for no-argument help, duplicate flags, command
aliases, and positive requirements

_This PR was generated by Codex._

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how the primary `usage` CLI is parsed and documented
(including `--completions` UX), but behavior is covered by updated
integration tests and is an intentional migration to the shipped parser.
> 
> **Overview**
> The **`usage` binary dogfoods `usage-rs`**: command structs use
`#[derive(usage_rs::Cli)]` / `#[usage(...)]` instead of Clap, and
**`--usage-spec` emits `Cli::to_kdl()`** from the same parse tables that
handle argv. **Clap, `clap_usage`, and `clap-sort` are removed**, along
with the post-hoc **`command_effects` patch table**—`effect`,
`requires`, `overrides`, and similar metadata now live on the
declarations.
> 
> **Root CLI shape is corrected in spec and docs**: **`--completions
<SHELL>` is a long flag** (not a positional), with an **explicit
multi-line `usage` synopsis** for `usage <COMMAND>` / `--completions` /
`--usage-spec`. Shell runner commands (`bash`, `fish`, `zsh`,
`powershell`, `exec`) use **`unknown_flags = "value"`** so script
arguments can include unknown flags.
> 
> **Help and manpages** gain support for an optional root **`usage`
string** on `Spec` (derive + KDL emission); root help/man synopsis
prefer that over generated lines. Generated assets (`usage.usage.kdl`,
`usage.1`, Fig spec, CLI reference markdown, snapshots) are refreshed to
match.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3aba1aa. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added `--completions <SHELL>` for selecting a completion shell.
* Improved command validation, aliases, defaults, environment settings,
and file-output completion hints.
  * Shell commands now forward unrecognized arguments as script values.
  * Added clearer help text and descriptions for generation commands.

* **Documentation**
* Updated CLI references, manpages, usage specifications, and shell
documentation to reflect revised options and behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Replaces #936 after flipping the facade and CLI layers.

---------

Co-authored-by: Claude Opus 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.

1 participant