Skip to content

fix(help): render the page for the mount the words reached - #928

Merged
jdx merged 2 commits into
agent/unit-variantsfrom
agent/help-route
Aug 17, 2026
Merged

fix(help): render the page for the mount the words reached#928
jdx merged 2 commits into
agent/unit-variantsfrom
agent/help-route

Conversation

@jdx

@jdx jdx commented Aug 16, 2026

Copy link
Copy Markdown
Owner

A Subcommands type mounted under two parents is one Command at one
address, so help::find — which located a command by core::ptr::eq over
the whole tree — returned whichever mount came first. ex beta shared --help printed Usage: ex alpha shared, with alpha's globals, for as long
as this crate has had help. Diagnostics fixed their half of this by
resolving the route rather than the command; help kept the pointer.

A &Command cannot distinguish the mounts, so it cannot be the input.
route_to builds the route from the argv the derive already has: it takes
the path the parser walked, and for the help beta shared spelling — where
the parse stops at the command that saw the word, short of the one being
asked about — walks the remaining words by name among the children of
wherever it has got to. By name, because the address is the thing that
cannot be trusted here. render_at then resolves the metadata chain the
same way, matching each step among that command's own children.

parse() prefers the route and falls back to render, so a caller that
reaches help by some other path still gets a page.

Both spellings are tested, and so is the first mount — resolving by route
must not make every page beta's.


Stack created with GitHub Stacks CLIGive Feedback 💬


Note

Medium Risk
Touches help rendering and generated parse() exit paths; behavior change is intentional for shared mounts, with fallback preserving old render when routing fails.

Overview
Fixes wrong help for shared subcommands mounted under multiple parents. When the same Subcommands type is wired under two parents, both mounts share one Command pointer, so help::render / find picked the first mount—e.g. ex beta shared --help showed Usage: ex alpha shared and alpha’s globals.

The parser records help_span: the argv slice where the help word walked subcommand names (not flag values). help::route_to re-parses argv, builds the command path from command_path + that span, and help::render_at builds usage/metadata from that route by matching children per parent. Generated parse() uses route + render_at on Error::Help, falling back to render when the route can’t be rebuilt.

Conformance tests cover --help, help beta shared, both mounts, and --config <name> so a global flag value isn’t mistaken for a route step.

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

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

@coderabbitai

coderabbitai Bot commented Aug 16, 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: 4a2cc13f-c94e-444e-9fa7-b3e3ec8b7168

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 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR makes help rendering route-aware so shared subcommands mounted beneath multiple parents use the requested parent’s path and globals.

  • Records the exact post-help subcommand span during parsing.
  • Reconstructs and renders help from the parsed command route, retaining the address-based renderer as a fallback.
  • Adds regression coverage for both mounts, both help spellings, and detached flag values matching subcommand names.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
argv/src/help.rs Adds route reconstruction and route-aware help rendering that disambiguate shared command objects by their parent path.
argv/src/lib.rs Records the exact argv span resolved as the target of the help word, excluding previously consumed flag values.
derive/src/codegen.rs Updates generated help handling to prefer route-aware rendering while preserving the existing fallback.
conformance/tests/shared_subcommand.rs Covers both shared-command mounts, both help syntaxes, and the previously reported detached flag-value collision.

Reviews (4): Last reviewed commit: "fix(help): ask the parser which words na..." | Re-trigger Greptile

Comment thread argv/src/help.rs Outdated

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

Comment thread argv/src/help.rs Outdated
@jdx
jdx force-pushed the agent/help-route branch from b789fdf to 64eaa5c Compare August 16, 2026 23:53
jdx added a commit that referenced this pull request Aug 17, 2026
`route_to` extended the route for a `help` word by scanning every token from
where the parse stopped and skipping what did not match. A flag's detached
value is just a word, so `ex --config alpha help beta shared` descended into
`alpha` on the way past and printed `Usage: ex alpha shared` with alpha's
globals — the mount bug this function exists to fix, reintroduced through a
different door. It passed the arrival check because both mounts are one
address, which is the thing that makes this hard to notice.

Only the parser knows which tokens were in command position. It already
resolves the words after `help` itself, one subcommand at a time, breaking at
the first that names none — and then discarded the walk. It now records the
`argv` range it covered, and `route_to` extends over exactly that, matching
each word among the children of where it has got to.

Two indices rather than the commands, so the parser goes on allocating
nothing; empty for `--help`, which asks about wherever the parse reached.

Tested in both directions, so a fix that merely preferred the later match
would not pass, and mutation-checked: scanning raw argv again fails the new
test with `Usage: ex alpha shared`.

Reported by Greptile and Bugbot on #928.

jdx commented Aug 17, 2026

Copy link
Copy Markdown
Owner Author

Both of you were right, and I reproduced it before fixing:

["--config", "alpha", "help", "beta", "shared"]   Usage: ex alpha shared   ["--alphaglobal"]

The arrival check did not catch it for the reason Bugbot gives: both mounts are one address, so ptr::eq succeeded on the wrong one. That is the same property this PR exists to work around, arriving through a different door.

Fixed in d67156e. The scan was the wrong source: only the parser knows which tokens were in command position. It already resolves the words after help itself — one subcommand at a time, breaking at the first that names none — and was throwing that walk away. It now records the argv range it covered, and route_to extends over exactly that range instead of over raw argv. Two indices rather than the commands themselves, so the parser goes on allocating nothing; the span is empty for --help, which asks about wherever the parse reached.

Tested in both directions — --config alpha help beta shared and --config beta help alpha shared — so a fix that merely preferred the later match would not pass. Mutation-checked by restoring the scan, which fails the new test with Usage: ex alpha shared.

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

jdx added 2 commits August 17, 2026 00:25
A `Subcommands` type mounted under two parents is one `Command` at one
address, so `help::find` — which located a command by `core::ptr::eq` over
the whole tree — returned whichever mount came first. `ex beta shared
--help` printed `Usage: ex alpha shared`, with alpha's globals, for as long
as this crate has had help. Diagnostics fixed their half of this by
resolving the route rather than the command; help kept the pointer.

A `&Command` cannot distinguish the mounts, so it cannot be the input.
`route_to` builds the route from the argv the derive already has: it takes
the path the parser walked, and for the `help beta shared` spelling — where
the parse stops at the command that *saw* the word, short of the one being
asked about — walks the remaining words by name among the children of
wherever it has got to. By name, because the address is the thing that
cannot be trusted here. `render_at` then resolves the metadata chain the
same way, matching each step among that command's own children.

`parse()` prefers the route and falls back to `render`, so a caller that
reaches help by some other path still gets a page.

Both spellings are tested, and so is the first mount — resolving by route
must not make every page beta's.
`route_to` extended the route for a `help` word by scanning every token from
where the parse stopped and skipping what did not match. A flag's detached
value is just a word, so `ex --config alpha help beta shared` descended into
`alpha` on the way past and printed `Usage: ex alpha shared` with alpha's
globals — the mount bug this function exists to fix, reintroduced through a
different door. It passed the arrival check because both mounts are one
address, which is the thing that makes this hard to notice.

Only the parser knows which tokens were in command position. It already
resolves the words after `help` itself, one subcommand at a time, breaking at
the first that names none — and then discarded the walk. It now records the
`argv` range it covered, and `route_to` extends over exactly that, matching
each word among the children of where it has got to.

Two indices rather than the commands, so the parser goes on allocating
nothing; empty for `--help`, which asks about wherever the parse reached.

Tested in both directions, so a fix that merely preferred the later match
would not pass, and mutation-checked: scanning raw argv again fails the new
test with `Usage: ex alpha shared`.

Reported by Greptile and Bugbot on #928.
@jdx
jdx force-pushed the agent/help-route branch from d67156e to e00424a Compare August 17, 2026 00:26
@github-actions

Copy link
Copy Markdown
Contributor

Instruction counts

benchmark trend instructions Δ wall (min) Δ
markdown ▁▁▁▃▄▄▃▃███ 180,287,329 → 180,311,783 +0.01% 16.15 → 16.26ms +0.68%
startup ▁▁▁▁▁▁▁▁▆▃█ 1,222,732 → 1,226,019 +0.27% 0.95 → 0.98ms +3.08%

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 72207 5895162 81x
usage: argv -> struct                            1239 ns      1.24 µs
clap: build tree + parse -> struct             498295 ns    498.30 µs
clap: parse -> struct, tree reused              23680 ns     23.68 µs
clap: build tree only                          305220 ns    305.22 µs

e00424ac9e2d vs 81b1ee788d23 · measured on the runner, not pushed to the history.

@jdx
jdx merged commit 1c1a755 into main Aug 17, 2026
11 checks passed
@jdx
jdx deleted the agent/help-route branch August 17, 2026 01:19
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