fix(spec): apply default_subcommand only at the root - #850
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
8384add to
85f943d
Compare
85f943d to
363dbd7
Compare
Greptile SummaryThe PR restricts
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (3): Last reviewed commit: "fix(spec): apply default_subcommand only..." | Re-trigger Greptile |
Instruction counts
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 comparisonParsing
|
363dbd7 to
85f943d
Compare
A spec declares one `default_subcommand`, once, at the top — and the parser looked that name up at whichever command it happened to be standing on. So a command with an unrelated subcommand of the same name acquired a default of its own: with `default_subcommand "ls"` declared for the program, `ex config zzz` descended into `config ls` and bound `zzz` there. Nothing declared that, and there is no way to declare it, which is what makes it a bug rather than a feature. Gated on still being at the root, which `out.cmds` already records — it holds just the root until something descends. Found while teaching usage-argv to route on the property: the two disagreed, and the corpus recorded it as a divergence pending a decision. jdx's call was to fix it here. The vector that recorded the difference is now an ordinary agreeing one, and deleting its label was not optional — the reference test checks labels in both directions, so it failed with an instruction to remove it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
85f943d to
6bd131f
Compare
A spec declares one
default_subcommand, once, at the top — and the parser looked that nameup at whichever command it happened to be standing on. So a command with an unrelated
subcommand of the same name acquired a default of its own: with
default_subcommand "ls"declared for the program,
ex config zzzdescended intoconfig lsand boundzzzthere.Nothing declared that, and there is no way to declare it, which is what makes it a bug
rather than a feature.
Gated on still being at the root, which
out.cmdsalready records — it holds just the rootuntil something descends.
Found while teaching usage-argv to route on the property: the two disagreed, and the corpus
recorded it as a divergence pending a decision. jdx's call was to fix it here. The vector
that recorded the difference is now an ordinary agreeing one, and deleting its label was not
optional — the reference test checks labels in both directions, so it failed with an
instruction to remove it.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Stack created with GitHub Stacks CLI • Give Feedback 💬
Note
Medium Risk
Changes core argv routing in
lib/src/parse.rs, which affects how nested commands parse; scope is narrow and covered by corpus and unit tests.Overview
Fixes incorrect
default_subcommandrouting in usage-lib so the single spec-level default is honored only at the root, not on nested commands that happen to share a subcommand name.The parser now gates default routing on
out.cmds.len() == 1in addition to the existing one-shot flag. Before this change,ex config zzzwithdefault_subcommand "ls"at the top could incorrectly descend intoconfig lsand bindzzzthere; it now correctly yieldsunexpected_argwhenconfighas no argument.The corpus vector
default-is-declared-for-the-rootis updated to expect that error and its usage-lib divergence label is removed. PLAN.md documents the fix instead of an open divergence, and a unit test covers both the nested non-routing and root routing cases.Reviewed by Cursor Bugbot for commit 6bd131f. Bugbot is set up for automated code reviews on this repo. Configure here.
What was happening
default_subcommandis a property of theSpec, declared once at the top — there is nosyntax for a per-command one. But the parser looked that single name up against
out.cmd, whichever command it was standing on. So a command with an unrelated subcommandof the same name silently acquired a default:
Nothing declared that behavior and nothing could have, which is what makes it a bug rather
than a feature. Gated on still being at the root, which
out.cmdsalready records — it holdsjust the root until something descends.
Verification
still does. Mutation-checked — with the guard removed it fails.
default_subcommand(explicit selection, same-named child, nested subcommands via mount,and discovery precedence).
Deleting its label was not optional:
reference_labels_are_accuratechecks labels inboth directions, so it failed with
usage-lib now agrees — delete the label. That is themechanism working as intended, and it is also the proof the fix took effect.
Provenance
Found while teaching usage-argv to route on this property (#848): the two implementations
disagreed, and rather than guess I recorded it as a divergence pending your decision. You
said fix it, so this is that fix.
AI-assisted — Tool: Claude Code; model: anthropic/claude-opus-5; version: unavailable.