Repository navigation
fix(cli): bring top-level help lines in line with their parsers and pin the drift - #198
Conversation
The enable/disable and activate/deactivate pairs shared one string, so disable advertised --scope and @<version>, and activate omitted --agent. Each verb now has its own, matching its top-level help line and parser. Refs #195
…h their parsers enable gains --override-stale-lock. The adopt conversion line gains <pack>[@<ver>], --scope and --allow-version-change. The migrate line gains --commit-consent and --override-stale-lock. The pin test now joins wrapped help entries, and the stale-lock pin from #194 reads the new conversion line. Closes #195
arcavenai
left a comment
There was a problem hiding this comment.
Requesting changes on one point. The parser check misses a real regression, so sideshow activate --agent can break with every test green. The help-line half of the change is right, and I would approve it as it stands; details below.
The blocker: check 2 is wider open than its known limit says. Two one-line mutants of cmd/sideshow/activate.go pass go test -count=1 ./cmd/sideshow:
runActivatecallsparseActivateArgs("activate", args, false)instead oftrue(line 17).- The
--agentcase refuses unconditionally (if true {forif !allowAgent {).
Either way, sideshow activate demo --agent x fails with --agent applies to activate only, although the usage string and the help line both name --agent.
TestUsageDrift_EveryParserAcceptsEveryFlagItsUsageNames misses this for two reasons:
- It calls
parseActivateArgswith its ownallowAgent, not with the argument the verb passes, so the runner's call is never exercised. - It counts any error that lacks the text "unknown flag" as acceptance, so a parser can reject a named flag in other words and pass.
The comment and body name only the other direction: a flag the parser accepts but no usage names. This direction, a named flag rejected with a different message, is not admitted.
A fix that stays inside this PR:
- For the four parse-only verbs, require
err == nil. I checked that all 8 of their flag runs return nil today with the test's dummy values, so this is safe now. - Reach
parseActivateArgsthrough the same argumentrunActivateandrunDeactivatepass. A shared table or a func value would do. - For the full
runAdoptandrunCoexistCheckruns, where other errors are expected, also fail when the error names the flag.
Criterion check, for the record.
-
Gaps from #194 are closed. The conversion entry now shows
<pack>[@<ver>],--scopeand--allow-version-change. The migrate entry shows--commit-consentand--override-stale-lock. The top-level enable line shows--override-stale-lock. -
Two top-level lines still omit flags their own usage strings name. The
coexistline omits--sideshow-active(coexist.go:23). Theproject initline omits--user-nameand--dry-run(main.go:787). The body lists these as outside the approved verbs. They are two one-line help edits if you want them here. (install's flags appear in its Install options block.) -
905a4f4 is behavior-neutral apart from two messages. I diffed every
--help,<verb> --help, bare<verb>,<verb> --bogusand<verb> demo --bogusoutput for 16 verbs, main against 905a4f4. Only these two pairs differ, each printed by a bare call and by--bogus:disable:usage: sideshow disable <pack>[@<version>] [--repo <path>] [--scope local|project] [--override-stale-lock]becomesusage: sideshow disable <pack> [--repo <path>] [--override-stale-lock]activate:usage: sideshow activate <pack> [--repo <path>]becomesusage: sideshow activate <pack> [--repo <path>] [--agent <name>]
The parser code changes only in the branch that prints usage.
-
The four split verbs, run through their parsers.
- enable:
--repo,--scopeand--override-stale-lockeach parse with no error. - disable:
--repoand--override-stale-lockeach parse with no error. - activate:
--repoand--agenteach parse with no error. - deactivate:
--repoparses with no error, and--agentis refused with "--agent applies to activate only".
- enable:
-
Red and stability. At 6e2c4fa the pin fails on exactly the drifted entries:
- enable's
--override-stale-lock; - the conversion entry's
--scope,--allow-version-changeand[@<ver>]; - the migrate entry's
--override-stale-lockand--commit-consent.
With head's tests and 905a4f4's
main.go, 2 tests fail, so the regex change in the green commit does not mask the drift. Head passesgo test -count=1 ./.... - enable's
-
Mutants that are killed. The 9 listed are killed. So are four I added:
- a fake
[--fake]inenableUsage, which fails both checks; [--repo <path>]dropped from the deactivate help line;[--repo <path>]dropped from the adopt conversion entry;--scopeadded todisableUsage.
- a fake
-
The by-design survivor (
activateUsagedrops--agent) is what the stated limit describes. The limit is broader than stated only in the direction above. -
Other verbs. Help for verbs the PR does not touch is unchanged apart from the three edited entries, which every verb's fallback help shows. CI on 1a4b2d0 passes.
On the split you asked about. Giving disable and deactivate their own usage strings is right. parseVerbArgs still accepts --scope and @<version> for disable, but Disable ignores both: the scope comes from the ledger row (settings := settingsFile(opts.RepoDir, bindings.RepoScope(row.SettingsScope))), and opts.Scope is only validated. So the old usage advertised two flags that do nothing. The new error wording is more accurate, and I found nothing that parses it. Rejecting those no-op flags in the parser would be a behavior change, and the known limit already covers it.
Seat: reviewer / reviewer
activate and deactivate decide --agent from the verb, not from an argument a caller supplies. enable, disable, activate and deactivate each have a parser function their runner calls. coexist and project init gain named usage strings. Refs #195
…-level help coexist gains --sideshow-active; project init gains --user-name and --dry-run, each named by its own usage string. Refs #195
arcavenai
left a comment
There was a problem hiding this comment.
Approving at 46fa325. My round-1 blocker is closed. The parser check now reaches each verb through the function its runner calls, and it fails on any error from a parse-only verb, so the activate --agent regressions I posted fail a test. The two help lines left over from round 1 now name their flags.
Round-1 mutants at head.
allowAgentforced false: killed byTestUsageDrift_EveryParserAcceptsEveryFlagItsUsageNames.- The
--agentcase refusing unconditionally (if true || !allowAgent {): killed by the same test. parseActivatepassing"deactivate": killed by the same test.
Call sites. Each runner calls a named parser, and the test calls that same function:
runEnablecallsparseEnableArgsandrunDisablecallsparseDisableArgs(enable.go:17,25).runActivatecallsparseActivateandrunDeactivatecallsparseDeactivate(activate.go:17,25).parseActivateArgsdecides--agentfrom the verb, and nothing else calls it.coexist,coexist-check,project initandadoptrun their whole runner.
6788895 is a pure refactor. I diffed 71 outputs between 1a4b2d0 and 6788895: --help, a bare sideshow, and for 16 verbs <verb> --help, a bare <verb>, <verb> --bogus and <verb> demo --bogus, plus activate/deactivate demo --agent x and three project init forms. They are byte-identical. Between 6788895 and 46fa325, the only change is the two edited entries, the coexist line and the project init line, in the 16 outputs that print the top-level help.
The new flags, run.
coexist ck --sideshow-activeexits 0, whilecoexist ck --bogusexits 1 withunknown flag: --bogus.project init ck --dry-run,--user-name Ada --dry-runand--user-name=Ada --dry-runeach print the dry-run plan and leave the repo holding only.git.
The stated survivors, re-derived.
activateUsagedrops--agent: check 2 reads its flag list from that string, so the dropped flag is never tried. This is the accepted-but-unnamed limit, as stated.- The project init parser: its
switchhas nodefaultcase, so an unknown flag is ignored. That is true at 46fa325, on main, and at 6dc5d2c (#152), so this PR did not introduce it. Check 2 cannot see a flag dropped from it. Other tests in the package do catch two of the drops: dropping the space form--user-namefails 6TestProjectInit_*tests, and dropping--dry-runfails 2. Dropping only the--user-name=form passes everything, and the usage string does not name that form. So in practice the survivor is narrower than the comment, not broader.
The named residual. A runner calling the wrong parser survives as stated: runActivate calling parseDeactivate, or runDisable calling parseEnableArgs. It is in the body. The body says both limits are named in the test comment, but the comment names only the project init gap.
Red and mutants.
-
Red: at 036acd5 the help pin fails on exactly
coexist --sideshow-active,project init --user-nameandproject init --dry-run. -
Head:
go test -count=1 ./...passes. -
Listed mutants: each is killed by its named test. That includes the enable parser returning an error that does not say "unknown flag", and adopt refusing
--yeswith an error that names it. -
My extra mutants:
- killed: the coexist parser dropping
--sideshow-active,[--sideshow-active]dropped from the coexist help line,[--dry-run]dropped from the project init entry; - survives: the coexist parser refusing
--sideshow-activewith an error that does not name the flag.
That survivor follows from the rule the test comment states: for a full-run verb, only an error naming the flag counts. It is the rule I suggested in round 1, and every full-run parser here names the flag in its rejections today. Non-blocking.
- killed: the coexist parser dropping
CI on 46fa325 passes.
Seat: reviewer / reviewer
Several top-level
sideshow --helplines omitted flags their verbs accept, so a user could not find a flag from help, including one that an error hint tells them to pass. This brings those lines in line with their parsers and adds tests that fail when a help line or a parser drifts from the verb's usage string, so the drift stops coming back.Closes #195. Help text and tests only; the parsers' behavior is unchanged. Two usage strings change in wording (listed below).
What changed:
enablegains--override-stale-lock; the adopt conversion line gains<pack>[@<ver>],--scopeand--allow-version-change; the migrate line gains--commit-consentand--override-stale-lock. Review of this PR added two more:coexistgains--sideshow-active, andproject initgains--user-nameand--dry-run. The long entries wrap, as the doctor entry does.enableUsage,disableUsage,activateUsage,deactivateUsage,coexistCheckUsage,coexistUsage,projectInitUsage;adoptUsagealready was). Enable and disable shared one string, so disable advertised--scopeand@<version>, which its help line and per-verb help do not; activate and deactivate shared one that omitted--agent. Each verb now has its own, so two error messages change: disable's drops[@<version>]and[--scope ...], and activate's gains[--agent <name>].parseEnableArgs,parseDisableArgs,parseActivate,parseDeactivate), and--agentis allowed by the verb insideparseActivateArgs, not by an argument a caller passes. The test reaches each parser through the same code its runner runs.The pin (
cmd/sideshow/usage_drift_test.go), read from the usage strings, withadoptUsagesplit by mode at its--migrate-user-scopegroup:--helpentry. Covers enable, disable, activate, deactivate, coexist-check, coexist, project init, and the adopt conversion, migrate and finish entries. The conversion entry must also show[@<ver>].--migrate-user-scope, because--yesalone is refused on purpose.Known limits. The first two are named in the test comment; the third is only here:
project initparser ignores flags it does not know (this predates this PR), so check 2 cannot see a flag dropped from it. Check 1 still holds its help entry.Red/green: the first pin was red on the three drifted entries (
6e2c4fa) and green after the help edit (1a4b2d0). Review found that the first parser check let two mutants of the activate path through, because it called the activate parser with its ownallowAgentand treated any error other than "unknown flag" as acceptance. The follow-up (6788895refactor,036acd5test,46fa325fix) pins the help lines for coexist and project init red-first; the stricter parser check passes on arrival and is held by the mutants below.Mutants, each compiled and killed by a named test: enable help line drops
--override-stale-lock; migrate line drops--commit-consentand--yes; conversion line drops@<ver>; coexist line drops--sideshow-active; project init entry drops--dry-run; disable usage names a flag its help line lacks; the adopt, enable, activate and coexist-check parsers each stop accepting one named flag; the coexist parser drops--sideshow-active; activate treats--agentas refused becauseallowAgentis false; the--agentcase refuses unconditionally; the enable parser returns an error that does not say "unknown flag"; adopt refuses--yeswith an error that names it. Two survive by design:activateUsagedropping--agent, and the project init parser dropping--user-name; both are the limits above.Gates, each run on its own: build, vet,
go test -count=1 ./..., golangci-lint (0 issues), gofumpt -l (empty).Opportunities, not touched:
install,useandproject unregistercarry inline usage strings outside the approved verb list.Closes #195