Context
#9773 (merged as #9785) typed parseOptions from CLI_FLAG_SPEC and introduced optionText(), which treats a flag given with no value as absent. The review of that PR caught one site the sweep missed (explainReviewRiskCli's contributorLogin). That was the visible end of a family — seven more sites read an option without narrowing it, each reachable by an ordinary typo:
| Invocation |
What happens today |
analyze-branch --login |
boolean true is sent to /v1/local/branch-analysis as the contributor login |
review-pr --login |
same |
login --github-token |
true is truthy, so the token branch is taken with true as the token instead of falling through to the device flow |
maintain selftune-audit --limit |
Number(true) is 1 — the audit is silently capped at one row instead of using the server default |
maintain precision --window-days |
same, a one-day window |
maintain audit-feed --since |
String(true) sends ?since=true, which the route reads as a cursor |
validate-config --file |
!options.file passes (true is truthy) and the manifest is read from the path "" |
Two other things in the same code
-
The chains are copied. optionText(options.login) ?? activeProfile.session?.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN appears at 14 call sites, readCliTextFile(optionText(options.bodyFile) ?? "", "Body") at three, and the --issue array/scalar/absent dance at three more. Each copy is its own chance to miss a step — which is exactly how the two --login sites above ended up different from the other twelve.
-
branchEligibilityFromOptions restates two route enums by hand (["eligible", "ineligible", "unknown"] and the four sources) and silently drops anything it does not recognise, so a status the route accepts can be discarded by the CLI with no error. branchEligibilitySchema already declares both, but it is a zod pipe (it transforms), so its members are not reachable through it.
-
optionalInteger and optionalNumber still take any — the annotation this whole issue exists to remove.
Requirements
- Every remaining raw read is narrowed, so a valueless flag is absent everywhere rather than at most sites.
- The three duplicated chains become single helpers, so there is one place to be correct and one place to test.
branchEligibilityFromOptions parses against the route's own schema. The contract exports the pre-transform object so its members are reachable.
optionalInteger/optionalNumber lose their any parameters.
- 100% patch coverage, branch-counted — including both arms of every fallback. The CLI suite runs the compiled
dist/ through a subprocess harness, which v8 cannot instrument, so these must be exercised in-process or they will read as uncovered.
Context
#9773 (merged as #9785) typed
parseOptionsfromCLI_FLAG_SPECand introducedoptionText(), which treats a flag given with no value as absent. The review of that PR caught one site the sweep missed (explainReviewRiskCli'scontributorLogin). That was the visible end of a family — seven more sites read an option without narrowing it, each reachable by an ordinary typo:analyze-branch --logintrueis sent to/v1/local/branch-analysisas the contributor loginreview-pr --loginlogin --github-tokentrueis truthy, so the token branch is taken withtrueas the token instead of falling through to the device flowmaintain selftune-audit --limitNumber(true)is 1 — the audit is silently capped at one row instead of using the server defaultmaintain precision --window-daysmaintain audit-feed --sinceString(true)sends?since=true, which the route reads as a cursorvalidate-config --file!options.filepasses (trueis truthy) and the manifest is read from the path""Two other things in the same code
The chains are copied.
optionText(options.login) ?? activeProfile.session?.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGINappears at 14 call sites,readCliTextFile(optionText(options.bodyFile) ?? "", "Body")at three, and the--issuearray/scalar/absent dance at three more. Each copy is its own chance to miss a step — which is exactly how the two--loginsites above ended up different from the other twelve.branchEligibilityFromOptionsrestates two route enums by hand (["eligible", "ineligible", "unknown"]and the four sources) and silently drops anything it does not recognise, so a status the route accepts can be discarded by the CLI with no error.branchEligibilitySchemaalready declares both, but it is a zod pipe (it transforms), so its members are not reachable through it.optionalIntegerandoptionalNumberstill takeany— the annotation this whole issue exists to remove.Requirements
branchEligibilityFromOptionsparses against the route's own schema. The contract exports the pre-transform object so its members are reachable.optionalInteger/optionalNumberlose theiranyparameters.dist/through a subprocess harness, which v8 cannot instrument, so these must be exercised in-process or they will read as uncovered.