Skip to content

feat(argv,derive): add compiled clause support - #1320

Open
jdx wants to merge 2 commits into
feat/spec-clause-interpreterfrom
feat/compiled-clauses
Open

feat(argv,derive): add compiled clause support#1320
jdx wants to merge 2 commits into
feat/spec-clause-interpreterfrom
feat/compiled-clauses

Conversation

@jdx

@jdx jdx commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • add clause tables and boundary events to the zero-allocation argv parser
  • support typed Vec clause fields through usage derive
  • add equivalent Go parser, spec-lowering, and generated-table support
  • extend the shared corpus so interpreted Rust, compiled Rust, and Go agree
  • report clause addition, removal, and separator changes as breaking in usage diff

Stack

Test plan

  • mise run test
  • mise run lint
  • mise run render
  • mise run gen-shadow
  • cd go && go test ./...

AI-assisted — Tool: Codex; model: OpenAI/GPT-5; version: unavailable.


Note

Medium Risk
Touches core argv binding across Rust, Go, derive, and conformance; incorrect separator or reset logic could mis-parse multi-instance CLIs, but behavior is heavily corpus- and test-covered.

Overview
Adds repeatable clause parsing: commands can declare a separator-delimited positional group whose instances are preserved instead of overwriting earlier values.

The Rust argv parser gains Command::clause, Event::ClauseSeparator, and routing through clause inner args (including separator handling after automatic double_dash and literal separators after --). Help, completions, spec emission, and usage lines now read positionals from the clause when present (usage shows repeatable pattern like {inner} [{separator} {inner}]…).

usage derive supports #[usage(clause, separator = "…")] on Vec<T> (T: Args) with compile-time limits (one clause, no mixing with top-level args/subcommands). Conformance and the new corpus vectors expect a clauses map; the reference and Go parser/spec lowering mirror the same behavior.

usage diff treats adding, removing, or changing a clause separator as breaking.

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

@coderabbitai

coderabbitai Bot commented Aug 25, 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: dcac1f8b-f528-4429-a9d0-1bdd3164f4c2

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.

Comment thread argv/src/lib.rs
Comment thread derive/src/codegen.rs
Comment thread cli/src/cli/diff.rs
Comment thread derive/src/model.rs
Comment thread argv/src/complete.rs
@jdx
jdx force-pushed the feat/compiled-clauses branch from 24d040a to 911cc4c Compare August 25, 2026 17:41
@jdx
jdx force-pushed the feat/compiled-clauses branch from 911cc4c to 20207e5 Compare August 25, 2026 17:54
Comment thread go/argv/parser.go
@jdx
jdx force-pushed the feat/compiled-clauses branch from 20207e5 to e3f365e Compare August 25, 2026 18:00
@jdx
jdx force-pushed the feat/compiled-clauses branch from e3f365e to b79b9c2 Compare August 25, 2026 18:15
@jdx
jdx force-pushed the feat/compiled-clauses branch from b79b9c2 to eb1cdf8 Compare August 25, 2026 18:17
@jdx
jdx force-pushed the feat/compiled-clauses branch from eb1cdf8 to a86bd3f Compare August 25, 2026 18:27
Comment thread derive/src/model.rs
@jdx
jdx force-pushed the feat/compiled-clauses branch from a86bd3f to ca197ee Compare August 25, 2026 18:32
Comment thread argv/src/complete.rs
@jdx
jdx force-pushed the feat/compiled-clauses branch from ca197ee to f2ed8d7 Compare August 25, 2026 18:45
@jdx
jdx force-pushed the feat/compiled-clauses branch 2 times, most recently from 1304064 to 64a99e6 Compare August 25, 2026 19:00
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Instruction counts

benchmark trend instructions Δ wall (min) Δ
markdown █▁ 341,704,094 → 339,362,202 -0.69% 28.24 → 28.62ms +1.33%
startup █▁ 910,757 → 910,458 -0.03% 0.85 → 0.90ms +5.71%

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.

framework stripped binary, bytes
usage 1340672
bpaf 2493280
clap 3101832
framework instructions, cold parse vs usage
usage 8487
clap 6315228 744x
bpaf 21909141 2581x
                                              min       p01       p10    median
usage-rs: argv -> struct                      429       433       438       448  ns
clap: build tree + parse -> struct         514843    516263    519692    534298  ns
bpaf: build parser + parse -> struct      1596632   1596632   1606200   1655062  ns

usage: argv -> struct                             460 ns      0.46 µs
clap: build tree + parse -> struct             527188 ns    527.19 µs
clap: parse -> struct, tree reused              23838 ns     23.84 µs
clap: build tree only                          319487 ns    319.49 µs

bf7076a80f06 vs 685848aaacbc · measured on the runner, not pushed to the history.

@jdx
jdx force-pushed the feat/compiled-clauses branch from 64a99e6 to e173603 Compare August 25, 2026 20:21
@jdx
jdx force-pushed the feat/compiled-clauses branch from e173603 to 22683bd Compare August 25, 2026 20:41
@jdx
jdx force-pushed the feat/compiled-clauses branch from 22683bd to 1e0e644 Compare August 25, 2026 20:48
@jdx
jdx force-pushed the feat/compiled-clauses branch from 1e0e644 to 80ff8a9 Compare August 25, 2026 20:59
@jdx
jdx force-pushed the feat/compiled-clauses branch from 80ff8a9 to b3c69b6 Compare August 25, 2026 21:12
@jdx
jdx force-pushed the feat/compiled-clauses branch from b3c69b6 to bf7076a Compare August 25, 2026 21:26

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

Comment thread lib/src/go/mod.rs
block.push("\t},".to_string());
block.push("},".to_string());
lines.push(Line::Block(block));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Go tables omit clause argument keys

High Severity

Clause arguments now receive sequential keys, but metadata and help_table still size and fill their slices as 1 + flags + args. Those keys therefore land past the table or in empty slots, so Lookup returns nil for clause args and for every later flag, argument, or subcommand. Required checks, choices, and help text silently disappear for generated Go CLIs that declare a clause.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bf7076a. Configure here.

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