feat(usage): declare what each command does to the world - #1122
Conversation
usage 4 added `effect=`, which lets a spec say whether a command only inspects state, changes it, or destroys something (jdx/usage#739, #742). Declare it for all 114 aube commands: 60 read, 37 write, 6 destructive. clap cannot express this, so the classification lives in one table in crates/aube/src/command_effects.rs and is applied to the derived spec. Matches what mise, hk, pitchfork and communique now do. Two rules are worth stating, and both are in the module docs: Fetching or extracting a package is not an effect. Nearly every command here may populate the store or the metadata cache on the way to its real job. Counting that would make every command `write` and leave the field with no signal. The same goes for node_modules, which is reproducible from the lockfile — `clean` and `ci` delete it and are still only `write`. Registry commands act on everyone. `publish`, `deprecate`, `dist-tag` and `access` change what every consumer of a package sees. They follow the same rules as everything else — `unpublish` removes, so it is destructive; `publish` creates, so it is write — but none are `read`, and each carries a comment about the blast radius, because the tier alone understates it. A published version cannot be replaced and `unpublish` takes it away from everyone. 11 commands are deliberately unclassified: `run`, `exec`, `dlx`, `node`, `create` and the script shortcuts execute code that is not aube's, so their effect is whatever that code does. Four tests keep the table honest: every command classified, no stale entries, no duplicates, and `apply` actually transferring them onto the spec. Coverage includes hidden commands, since those still run. Also switches to clap_usage::spec() — clap_usage 4.0.0 exposes the Spec before rendering, which is what makes annotating it possible — emits min_usage_version "4.0" since older CLIs reject the field, and bumps usage to 4.0.0 in mise.lock so the pinned CLI can read what aube emits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 6 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (98)
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 |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Greptile SummaryAdds command-effect metadata throughout the generated usage specification.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; both previously reported effect-classification defects are corrected in the source and generated specification. Important Files Changed
Reviews (2): Last reviewed commit: "fix: activate writes shims, logout destr..." | Re-trigger Greptile |
Three review findings, all correct: `activate` was `read` by analogy with other CLIs, where it only prints shell code. aube's also calls `ensure_shims`, which creates the shim directory and writes the tool shims into it. Now `write`. `logout` was `write` on the grounds that logging back in restores the token. But getting it back means authenticating again, which is exactly the "redoing work" that makes `config delete` destructive — and this is `config delete` for the auth key. Now destructive. `publish` is `#[cfg(feature = "publish")]`, so `--no-default-features` drops the subcommand while the table still claimed it, failing the stale-entry test there. Moved to a FEATURE_EFFECTS table gated the same way. `config tui` looked like the same case and is not: a `cfg(not(feature = "config-tui"))` stub keeps the subcommand present either way, it just errors when invoked. Verified by running the tests under `--no-default-features` — which is how the difference showed up, since gating it there made the completeness test fail. Both feature configurations now pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d2892fe. Configure here.
| ("set-script", Read), | ||
| ("stage", Read), | ||
| ("token", Read), | ||
| ("whoami", Read), |
There was a problem hiding this comment.
npm fallback mislabeled read
Medium Severity
The npm-compat stubs (owner, pkg, search, set-script, stage, token, whoami) are annotated effect=read based on the error-only path, but when npmPath is configured they delegate to the real npm binary and can perform registry writes or other side effects. That contradicts the module’s rule that mislabeling read is dangerous and that unknown behavior should stay unclassified.
Reviewed by Cursor Bugbot for commit d2892fe. Configure here.
|
All three findings were correct; fixed.
That one had a twist worth recording. I assumed Verified under both configurations: This comment was generated by an AI coding assistant. |


usage 4 shipped
effect=(jdx/usage#739, #742), which lets a spec say whether a command only inspects state, changes it, or destroys something. This declares it for all 114 aube commands.60 read · 37 write · 6 destructive · 11 deliberately unclassified
Completes the set alongside jdx/mise#11306, jdx/hk#1121, jdx/pitchfork#666 and jdx/communique#240.
Two rules, both in the module docs
Fetching or extracting a package is not an effect. Nearly every command here may populate the store or the metadata cache on the way to its real job. Counting that would make every command
writeand leave the field with no signal. Same fornode_modules, which is reproducible from the lockfile —cleanandcidelete it and are still onlywrite.Registry commands act on everyone. This is what makes aube different from the other four:
publish,deprecate,dist-tagandaccesschange what every consumer of a package sees, not just this machine.They follow the same rules as everything else —
unpublishremoves so it is destructive,publishcreates so it is write — but none areread, and each carries a comment about blast radius, because the tier alone understates it:unpublishdist-tag rmaccess revokepublishdist-tag addlatestincludeddeprecateundeprecateundoes itWorth saying plainly: the three-value vocabulary doesn't distinguish "destroys my stuff" from "destroys everyone's stuff". I kept to the letter of the definitions rather than inflating tiers per-command, since consistency across five CLIs is worth more than a bespoke judgement here — but
publishsitting atwriteis the place where that shows, and it's flagged in the source.Unclassified is a real answer
11 commands —
run,exec,dlx,node,create,recursive,install-testand the script shortcuts — execute code that isn't aube's: a package's lifecycle scripts, a binary fetched from the registry, a script from package.json. Their effect is whatever that code does, andreadwould be actively dangerous.Tests
Four: every command classified, no stale entries, no duplicates, and
applyactually transferring the table onto the spec. That last one matters — the other three check the table against the CLI, so without it the table could be perfect and silently never applied. Coverage walks hidden commands too, since__node-gyp-bootstrapand the npm-compat aliases still run.Also
clap_usage::spec()— clap_usage 4.0.0 exposes theSpecbefore rendering, which is what makes annotating it possible at all;generate()writes straight to a writermin_usage_version "4.0", since olderusageCLIs reject the spec withunsupported cmd prop effectmise.lockso the pinned CLI can read what aube emitsVerified
cargo build -p aube,cargo clippy -p aube --all-targets,cargo fmt --checkclean. All four classification tests pass.mise run renderregeneratedaube.usage.kdland the CLI docs.This PR was generated by an AI coding assistant.
Note
Low Risk
Mostly declarative usage metadata and dependency lock updates; no change to default command behavior unless external tools start acting on
effect=.Overview
Adds usage 4
effect=metadata for every aube command so tooling can tell inspect-only work from local mutations and irreversible/registry-wide actions.A new
command_effectsmodule holds the classification table (with documented rules: store/cache fetches are not effects; registry commands are neverread; script runners stay unclassified).print_usage_specnow usesclap_usage::spec(), callscommand_effects::apply, and emitsmin_usage_version "4.0"in generatedaube.usage.kdl. Tests enforce full CLI coverage and that effects land on the spec.clap_usage/usage-libare bumped to 4.0.0 (reflected inCargo.lock: drops the old tera 1.x template stack, consolidateswindows-sys, updatesroff). The pinnedusageCLI inmise.lockis updated so render stays compatible.Reviewed by Cursor Bugbot for commit d2892fe. Bugbot is set up for automated code reviews on this repo. Configure here.