Same operation, different verb and flag name depending on the noun #35
Links
-
🔒 private issue
Cross-cutting naming inconsistencies found while auditing the whole command surface. Individually small; together they mean users cannot guess any command.
edit vs update
edit: pr edit, issue edit, label edit, milestone edit, board edit-column
update: release update, requirement update, runner update
anvil release update <TAG> --title X and anvil pr edit <N> --title X are the same operation. Note the top-level anvil update means “replace this binary”, so update is overloaded.
delete vs remove vs revoke
delete: label delete, milestone delete, release delete, board delete-column, ci delete-secret, requirement delete
remove: ssh-key remove, runner remove, label remove, epic remove-child
ssh-key remove and label delete are both “delete a thing you own” with different verbs. Sharpest case: registry token delete is named delete, its help says “Revoke a registry token by id” (registry.rs:29-30), and its success message says “Revoked registry token” (registry.rs:164) — three words for one operation in one command.
–state vs –status
--state (default open): pr list, issue list, epic list
--status (no default): ci list, requirement list
Both are free-form String with no ValueEnum, so typos are only caught server-side — even though issue link --kind and release list --format do validate client-side.
–org vs –organization
--org: repo create, repo list, runner list, runner token
--organization: all 8 requirement subcommands
–repo means three different things
- An
org/repopair (most commands) - A repo slug within an org —
requirement.rs:214-216, where passingorg/repobuilds a malformed URL - A scope target mutually exclusive with
--org—registry.rs:47-49,runner.rs:172-176
–limit: four defaults, and absent from 16 list commands
repo/pr/issue list = 30, epic list = 50, ci/commit list = 20, runner logs --lines = 100. No pagination at all on branch list, label list, board list, milestone list, agent list, release list, ssh-key list, registry token list, deploy list, issue comments, issue links, pr reviews, ci secrets, release assets, runner list, requirement list.
Four encodings of a boolean
release create --draft(bool) vsrelease update --draft <BOOL>(takes a value) — same field, same nounpr edit --draft/--ready(paired exclusive flags)epic auto-close --on/--off
list vs bare plural noun
Nested list: registry token list, requirement applicability list, runner service list
Bare plural: issue comments, issue links, issue milestones, pr reviews, agent sessions, release assets, ci secrets, epic children
runner service svc-* prefix
runner service has install, uninstall, list unprefixed but svc-start, svc-stop, svc-restart, svc-status prefixed (runner.rs:263-289). The prefix only exists to avoid a Rust enum-variant clash with RunnerCommand::Start/Stop/... — an implementation detail in the user-facing surface. It also leaves anvil runner stop and anvil runner service svc-stop as two similar names for different things.
Suggested approach
Pick one verb per operation, alias the losers for compatibility, and add a test that asserts the chosen vocabulary across the command tree so it cannot drift again.
Found during a CLI-wide consistency audit.
#54 addresses the runner service svc-* prefix item only — the four verbs
are now start|stop|restart|status with the svc- spellings kept as hidden
clap aliases. Docs and the server-generated install script follow in
fangorn/anvil#235 (tracked by fangorn/anvil#382).
Leaving this open: #54 does not add the vocabulary test across the command
tree that the “Suggested approach” section asks for, and every other item here
(edit/update, delete/remove/revoke, --state/--status,
--org/--organization, the three meanings of --repo, the four --limit
defaults, the four boolean encodings, list vs bare plural) is untouched. The
near-collision this issue flags also survives the rename: anvil runner stop
vs anvil runner service stop is arguably more confusable now that the
distinguishing token has left the leaf verb.
One correction to the description while it’s open: the svc- prefix did not
exist “to avoid a Rust enum-variant clash with RunnerCommand::Start/Stop/...”.
ServiceCommand is a separate enum, and its variants were already plainly named
Start, Stop, and Restart on main (src/commands/runner.rs:264,271,278
before #54) — only the clap #[command(name = "svc-…")] attribute carried the
prefix. SvcStatus was the sole variant genuinely prefixed at the Rust level.
There was no clash to avoid, so that rationale shouldn’t be used to defend
keeping a prefix anywhere else.