Skip to content

fix(parse): stop losing a flag that is missing its value - #807

Merged
jdx merged 2 commits into
mainfrom
agent/parse-strict-flags
Aug 11, 2026
Merged

fix(parse): stop losing a flag that is missing its value#807
jdx merged 2 commits into
mainfrom
agent/parse-strict-flags

Conversation

@jdx

@jdx jdx commented Aug 11, 2026

Copy link
Copy Markdown
Owner

First of the parser fixes, taking you at your word that these are bugs that went unnoticed because the parser is mostly used for completions.

Three divergences the corpus recorded, all of them the parser quietly discarding something:

ex --jobs parsed fine and bound nothing. A forgotten value looked like a working command, and ex --jobs --force bound force while dropping jobs silently. Now an error.

Worth noting where the check went: parse, not parse_partial. A half-typed --jobs is exactly what a completion is asked about, so the pending flag stays in the partial output and only a full parse — which has nothing left to wait for — treats it as a failure. That distinction is why ParseOutput::flag_awaiting_value exists, so the fix is two lines and a comment rather than a new code path.

-j=8 bound =8. The value was taken as everything after the letter. One = between letter and value is a separator, matching the long form; -j==8 still means =8.

--jobs= bound nothing, collapsing "given empty" into "not given". The two are now told apart by whether an = was written rather than by whether anything followed it.

The corpus did its job

Fixing these made the suite report five labels as stale — "you claimed usage-lib diverges here, it now agrees, delete the label" — which is the workflow the corpus was built for. Deleting them takes the recorded divergences from 16 to 11.

One test needed rethinking rather than updating: it asserted usage-argv resolves "at least a dozen" divergences, which was a floor that shrinks every time usage-lib gets fixed. An empty set would mean the reference caught up entirely, which is the goal, so the floor is gone and the per-vector assertion carries the weight.

Two I did not touch, because they need your call

Unrecognized flags fall through to positionals (ex --wat binds --wat to an argument). This is the root of most remaining divergences, but mise parses task arguments with this parser at run time — usage::Parser::new(&spec).parse(&args) in src/task/mod.rs — so rejecting an undeclared flag changes what a task accepts, not just what a completion offers. That could break tasks that pass arbitrary flags through today.

A repeated -- is eaten. The grammar says only the first is a separator. But test_double_dashes_without_preserve asserts the current behavior explicitly, and double_dash="preserve" exists precisely to keep separators — which makes "the default consumes them all" look like a deliberate design rather than an oversight. I reverted my fix for this one when that test failed. If your reading is that the spec's design wins, the corpus vector changes instead of the parser.

Both are marked Needs a decision in PLAN.md.

AI-assisted — Tool: Claude Code; model: anthropic/claude-fable-5; version: unavailable.


Note

Medium Risk
Changes real argv binding and errors for full parses (including mise task argument parsing), though completions stay on parse_partial; behavior is intentional and corpus-backed.

Overview
Aligns usage-lib parsing with the argv grammar on three flag-binding bugs that previously showed up only in the conformance corpus.

Missing flag values now fail in full parse when a value-taking flag ends without a value (e.g. ex --jobs, ex --jobs --force), using flag_awaiting_value after partial parsing. parse_partial is unchanged so half-typed flags still work for completions.

Attached = semantics are fixed: long --jobs= binds an empty string (distinct from omitting the flag); boolean --force= no longer leaves a stray empty positional; short -j=8 binds 8 by treating a single leading = as a separator (long-form parity).

The corpus drops stale usage-lib divergence labels on the affected vectors, adds coverage for --force=, bumps in-scope vector count, and relaxes the conformance test that required resolving “at least a dozen” divergences. PLAN.md and docs/spec/argv.md record remaining open decisions (unknown flags → positionals, repeated --).

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

Three of the divergences the conformance corpus recorded, all of them the
parser quietly discarding something.

`ex --jobs` parsed successfully with nothing bound, so a forgotten value
looked like a working command — and `ex --jobs --force` bound `force` while
dropping `jobs` without a word. A full parse now reports it. `parse_partial`
deliberately does not: a half-typed `--jobs ` is exactly what a completion
is asked about, which is why the pending flag is left in the output rather
than raised there.

`-j=8` bound `=8`, because the value was taken as everything after the
letter. One `=` between the letter and the value is a separator, matching
the long form; `-j==8` still means `=8`.

`--jobs=` bound nothing at all, collapsing "given empty" into "not given" —
the two were told apart by whether an `=` was written, rather than by
whether anything followed it.

The corpus is how each of these was verified: fixing them made it report
five labels as stale, and deleting those labels is the whole workflow.
Eleven divergences remain, two of which need a decision rather than a
patch and are marked as such in PLAN.md.

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

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jdx, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 8a5e7996-f34d-464a-9ec8-d9059bf32809

📥 Commits

Reviewing files that changed from the base of the PR and between 5b917ad and 22703a2.

📒 Files selected for processing (6)
  • PLAN.md
  • conformance/tests/argv.rs
  • corpus/01-long-flags.json
  • corpus/02-short-flags.json
  • docs/spec/argv.md
  • lib/src/parse.rs

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.

@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 550c2b9. Configure here.

Comment thread lib/src/parse.rs Outdated
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR corrects three argv parsing divergences while preserving partial-parse behavior for completions.

  • Full parsing now rejects value-taking flags that end without a value.
  • Attached empty long values and short-form = separators are handled distinctly.
  • Conformance labels, parser documentation, and planning notes are updated to reflect the fixes.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported empty-suffix positional leak is prevented by the new argument-aware queue condition and covered by a focused corpus vector.

Important Files Changed

Filename Overview
lib/src/parse.rs Implements missing-value validation and attached-value handling; the prior empty-suffix positional leak is fixed by restricting empty queued values to flags with arguments.
corpus/01-long-flags.json Updates long-flag expectations and adds coverage ensuring --force= does not synthesize an empty positional.
corpus/02-short-flags.json Removes stale divergence labels for corrected short attached values and missing values.
conformance/tests/argv.rs Updates corpus scope and removes a divergence-count floor that would become stale as the reference parser catches up.
docs/spec/argv.md Updates documented divergence counts and remaining parser differences.
PLAN.md Marks the three addressed parser divergences complete and records rationale for unresolved behavior.

Reviews (2): Last reviewed commit: "fix(parse): do not queue an empty value ..." | Re-trigger Greptile

Comment thread lib/src/parse.rs Outdated
Telling `--jobs=` apart from `--jobs` by whether an `=` was written also
started queueing the empty text after `--force=`, where the flag holds no
value and has nothing to do with it — leaving a stray empty word to be read
as a positional.

An empty value now reaches only a flag that takes one. A non-empty value on
a flag that takes none is left as it was, which is a separate question the
corpus does not answer yet.

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

jdx commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Fixed. Telling --jobs= apart from --jobs by whether an = was written also started queueing the empty text after --force= — a flag that holds no value and has nothing to do with it — which left a stray empty word to be read as a positional.

An empty value now reaches only a flag that takes one, with a corpus vector pinning --force= on a boolean. A non-empty value on such a flag (--force=8) behaves as it did before, which is a separate question the corpus does not answer yet — arguably it should be an error, but that is a behavior change beyond this PR.

AI-assisted — Tool: Claude Code; model: anthropic/claude-fable-5; version: unavailable.

@github-actions

Copy link
Copy Markdown
Contributor

Instruction counts

Nothing was compared, and so nothing was gated. No series appears on both sides: either the base has no measurements recorded, or the two were measured on different runner classes, which are deliberately not comparable — counts shift between machine types by more than a real regression does.

New, nothing to compare against: markdown on bamboo-v2-ubuntu24.04-x64-30vcpu-24gb-rust1.97.1, startup on bamboo-v2-ubuntu24.04-x64-30vcpu-24gb-rust1.97.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.

22703a2971b2 vs 5b917adc5f27 · measured on the runner, not pushed to the history.

jdx commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Both remaining threads here are re-posts of the empty-suffix finding, which is fixed in 0f2e0ff: an empty value now reaches only a flag that takes one, so --force= no longer queues a stray empty word for the positionals. A corpus vector pins it.

Green and ready.

AI-assisted — Tool: Claude Code; model: anthropic/claude-fable-5; version: unavailable.

@jdx
jdx merged commit 4873950 into main Aug 11, 2026
9 checks passed
@jdx
jdx deleted the agent/parse-strict-flags branch August 11, 2026 19:00
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