Skip to content

Name commands by what you type, in examples as well as usage lines - #179

Merged
jasperf merged 4 commits into
mainfrom
fix/example-lines-name-the-command
Aug 5, 2026
Merged

Name commands by what you type, in examples as well as usage lines#179
jasperf merged 4 commits into
mainfrom
fix/example-lines-name-the-command

Conversation

@jasperf

@jasperf jasperf commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

wp-ops scanner-targeted --help printed a correct usage line and then, three
rows below it, wp-ops wp-cli/security/scanner-targeted.

5.1.0 fixed the usage line for every command and every executor — but it fixed
it by changing the code that generates it. Example lines are not generated;
they are prose copied verbatim out of each script's @example annotation, so
they were never touched. 42 of the 71 commands that carry examples printed
at least one path-style invocation, 47 lines in total, in both render paths
(exec.DetailBody and exec.writeManifestHelpBody each print the string
unchanged).

The path forms still resolved, so nothing was broken for anyone who pasted one.
They were wrong in that they taught the long form as the command's name.

One was genuinely broken: wp-cli-pattern-validate's example still named
bedrock/wp-cli-config/, a category 5.0.0 retired, so the line it told you to
type answered Unknown command or category. Same class of bug 5.1.1 fixed in
README.md, missed then because nobody thought to grep the scripts for the
retired category name.

Root cause

The catalog validates its own structure but not its prose. ShortName is
guaranteed to resolve, TestLoad_ShortNameUniqueBasenames pins that, and CI
regenerates the catalog and diffs it. Every one of those checks is about the
key. The Examples field travelled from a shell comment onto the user's
screen without a single assertion that the thing it told them to type exists.

Approach

A sweep alone would fix today's output and guard nothing — the next rename
reintroduces it exactly as 5.1.1's would have, had its two @example lines not
happened to be the subject of that PR. Normalizing in the generator was
rejected: it would bake ShortName into catalog.json, which
catalog.go:78-81 deliberately refuses to do, and it would leave the source
files lying, which matters because a script header is read directly at least as
often as through --help.

So: validate in the catalog package, then sweep once. The test works
post-Load, reading the real ShortName without persisting it.

Commits

  1. The test, red. Extracts the token after wp-ops — skipping VAR=value
    environment prefixes, which screenshot-patterns' example carries — and
    requires it to equal the entry's ShortName. It reproduced the 42/47 count
    independently, so it is both the audit and the guard, with no window where
    the guard exists but the sweep has not landed.

  2. The 47-line sweep, across 42 scripts, plus the regenerated catalog.
    Command token only; arguments, flags and environment prefixes untouched.
    One change applied 47 times, kept as one commit despite the atomic-commit
    rule.

  3. The four Go sites no sweep of the scripts could reach — the macOS
    access-log hint, the missing-argument error, the unannotated-playbook
    fallback — now read through CommandName(). The ambiguity message and
    search listing keep printing full keys, where the path is the point.

    The backup hints in serverside.go, db-backup.sh and site-backup.sh
    were stale twice over: path form and the -e site= syntax the playbooks
    stopped accepting when they moved to positional arguments. They now print
    wp-ops database-pull example.com production. serverside_test.go pinned
    the old strings, so its expectations move with them.

    check-deny-ips.sh:67 was not a hint but a real invocation piping into
    check-ips — the one place a rename would have broken a code path rather
    than a comment.

Plus docs/trellis-cli-comparison.md §5, which asserted in the present tense
something its own status header says 5.1.0 fixed, and two
trellis/security/README.md invocations. Documents that quote the path form
as the problem being analysed were left alone.

Verification

go test ./... passes. Both symptoms confirmed against the built binary:

$ wp-ops wp-cli-pattern-validate --help
Examples:
  wp-ops wp-cli-pattern-validate web/app/themes/theme-name/patterns/ --fix

go/internal/manifest/testdata/ keeps its path-style examples — the parser must
go on accepting the form — and was kept out of the sweep.

Not in scope

search prints full keys and list prints bare basenames, so the same command
appears under two names depending on how you found it. Neither is wrong on its
own terms; it is a UX call rather than a correctness one, and it stays open in
docs/example-line-drift.md §7.

jasperf and others added 4 commits August 5, 2026 15:00
The usage line is generated from ShortName and so cannot be wrong; an
@example is prose copied verbatim out of a shell comment, and prose goes
stale at every rename. Nothing asserted that the thing an example tells
you to type resolves at all.

The test extracts the token after "wp-ops" — skipping leading VAR=value
environment prefixes, which screenshot-patterns' example carries — and
requires it to equal the entry's ShortName. Working post-Load reads the
real ShortName without persisting it, which catalog.go:78-81 rules out.

Red at this commit: 47 lines across 42 entries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sweeps the 47 example lines the new test flagged, across 42 scripts, plus
the regenerated catalog. Each rewrites only the command token; arguments,
flags and the environment-variable prefix on screenshot-patterns' example
are untouched.

Most were merely long — the path form still resolves, so they worked while
teaching the wrong name three rows under a usage line that taught the right
one. wp-cli-pattern-validate's was genuinely broken: it still named
bedrock/wp-cli-config/, a category 5.0.0 retired, so the line it told you
to type errored out.

One change applied 47 times, kept as one commit despite the atomic-commit
rule. Turns TestExamplesNameTheCommand green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The example sweep only reached @example annotations. These four Go sites
format e.Key straight into user-facing text, so they kept naming the
internal key after 5.1.0 taught the usage line not to.

  serverside.go:171   access-log hint (macOS path)
  ansible.go:231      missing-argument error
  ansible.go:287      unannotated-playbook fallback

All three now read through CommandName(). Left alone: dispatch.go's
ambiguity message and search.go's listing, where the full key is the point.

serverside.go's backup hint and the two shell scripts that echo the same
advice were stale twice over — path form *and* `-e site=` , which the
playbooks stopped accepting when they moved to positional arguments. They
now print `wp-ops database-pull example.com production`, which is what
the playbook's own @example says. serverside_test.go pinned the old
strings, so its expectations move with them.

check-deny-ips.sh:67 is not a hint but a real invocation piping into
check-ips; it was the one place a rename would have broken a code path
rather than a comment.

Also past-tenses trellis-cli-comparison.md §5 rough edge #1, which asserted
in the present tense something its own status header says 5.1.0 fixed, and
the two trellis/security/README.md invocations.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Also lands the analysis this release implements, with its status header
updated: the document was written against 3a53555 and its prose is kept
as written, so it reads as a record of the pre-fix state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jasperf
jasperf merged commit 0991ca5 into main Aug 5, 2026
1 check passed
@jasperf
jasperf deleted the fix/example-lines-name-the-command branch August 5, 2026 08:08
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