Skip to content

fix(e2e): refuse a bad flag at the runner's entry instead of running everything, through node:util parseArgs - #567

Merged
Vivswan merged 2 commits into
mainfrom
fix/run-parse-args
Oct 27, 2026
Merged

Vivswan merged 2 commits into
mainfrom
fix/run-parse-args

Conversation

@Vivswan

@Vivswan Vivswan commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

Before

argv -> hand-rolled walk: a recognized flag is read, anything else is skipped -> the whole corpus runs
$ bun test/e2e/run.ts --section labels
... all 373 scenarios run, minutes later: exit 0

After

argv -> node:util parseArgs (tokens) -> an unknown flag, stray word, missing or option-looking value, or a value on a boolean? -> yes: exit 2, the flag and the fix named
                                                                                                                               -> no:  the filtered run
$ bun test/e2e/run.ts --section labels
e2e: unknown flag --section; the flags are --sections <key,key|all>, --scenario <name>, --print-stdout

How

  • Five answers change, each an input a developer can type and none the repository passes: --sections=labels and --scenario=name are honored; a later --sections all clears the list; a -- after a flag is refused; a string flag followed by -- or another flag is refused as a missing value; an unknown flag, stray word, or --print-stdout=yes exits 2.
  • Every invocation the repository makes parses identically: the bare bun run test:e2e, the replay lines the runner writes, and the header's eight flag pairings.
  • Library: node:util parseArgs, already used by test/e2e/fuzz.ts.

Proof

  • Tests: a new spawn-based run.test.ts, 11 cases (9 refusals with exact stderr and exit 2, 2 honored shapes), every row red against the parser it replaced; e2e 373/373.
  • Gates: typecheck, fleet biome 0, member-shape 0, knip, line cap.
Technical details
  • Differential over 6000 generated argv shapes: 149 identical, 5770 differ by a refusal where the old parser accepted silently, 81 by an inline value or a later all now honored; no other class.
  • parseArgs takes the next word as a string option's value even when it starts with - (the Open Group convention), so a one-predicate guard refuses such a non-inline value; an inline --scenario=-name keeps its dash.
  • bun drops a -- that directly follows the script path (node keeps it), so bun test/e2e/run.ts -- --sections labels filters on labels in both old and new; the test puts a flag ahead of the --.
  • Recorded, not built: strict: true would reject the same shapes by throwing, at the cost of translating its messages to keep the runner's wording.
  • Out of scope: validating section keys against SECTION_KEYS, a --help flag.
  • Files: test/e2e/run.ts, test/e2e/run.test.ts (new).

…arseArgs does

The curated runner's parseFlags walked argv by hand and ignored whatever it did not recognize: a typo'd flag, a stray word, an inline `--sections=labels` or `--scenario=name`, a flag with no value, or an empty `--sections ""` ran the whole corpus for minutes and exited green, and `--` was skipped so the flags after it were read. node:util's parseArgs, which test/e2e/fuzz.ts already uses, reads the three flags in every form it defines, and the runner now refuses the rest at the entry with exit 2, naming the argument and the usage line, as the fuzzer does.

Answers that change, each an input a developer can type and none the repository itself passes:
- `--sections=labels` and `--scenario=name` are read as the flag's value; the old parser ignored both and ran everything.
- `--sections labels --sections all` ends with no filter: `all` clears the list, so the last flag wins whichever word it carries; the old parser kept `labels` because `all` only ever meant "do not set".
- A `--` and the arguments after it are refused as unexpected; the old parser skipped the `--` and read the flags after it. bun itself drops a `--` that directly follows the script path, so `bun test/e2e/run.ts -- --sections labels` reaches the script without it and filters as before, while a `--` after a flag reaches the script and is refused.
- An unknown flag, a stray word, a flag with no value, `--print-stdout=yes`, and an empty or all-commas `--sections` are refused; the old parser ran everything, or filtered on an empty key and found nothing.

Differential check, the old parser against the new: every invocation the repository makes (`bun run test:e2e` from package.json, checks.yml, and CONTRIBUTING; the `--scenario <name>` replay lines the runner writes; the header's `--sections a,b`, `--sections all`, `--print-stdout`, and their pairings) parses identically. Of 6000 generated argv shapes over the flags, their inline forms, `all`, `--`, stray words, misspellings, and empty values, 149 parse identically and 5851 differ only as listed above: 5770 by a refusal where the old parser accepted silently, 81 by an inline value or a later `all` now honored. The repeated `--sections` forms the old parser got right (`all` first, or two lists) still parse the same.

The new test/e2e/run.test.ts spawns the runner on each refused shape and on the two honored shapes a filter nothing matches exposes through its message, so neither can regress to a silent full run.
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:46
@github-actions

Copy link
Copy Markdown
Contributor

File size check

0 over a hard cap (fails), 6 warning(s).

File Size Tier Cap
src/engine/diff.ts:56 11 comment lines warn 10
src/github/api-error.ts:9 11 comment lines warn 10
src/problem.ts:73 18 comment lines warn 10
src/sections/contract/errors.ts:13 11 comment lines warn 10
src/sections/contract/module.ts:55 12 comment lines warn 10
src/sections/shared/list/decl.ts:265 14 comment lines warn 10

Split the file, wrap the line, shorten or exempt the comment, or list the path in .file-size-allow.local with a # reason.

5 managed file(s) skipped; repo-platform owns them.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

String options can still swallow subsequent flags or --, allowing malformed invocations to run the entire corpus.

1 open issue
What changed in this PR

This PR adds argument validation to the end-to-end runner to prevent malformed invocations from silently running the full scenario corpus.

Changes:

  • Replaces manual argument parsing with node:util token parsing and explicit exit-2 errors.
  • Supports inline string values and lets a later --sections all clear earlier filters.
  • Adds nine spawn-based cases covering refusals and filter parsing.
File Description
test/​e2e/​run.ts Adds flag validation, diagnostics, and updated filter handling.
test/​e2e/​run.test.ts Adds process-level assertions for invalid arguments and honored filters.

Review effort: Balanced

Comment thread test/e2e/run.ts Outdated
…belongs

parseArgs hands a string flag the next word whatever it is, so `--sections -- --sections all` took `--` as the sections value, the later `all` cleared the filter, and the whole corpus ran instead of exit 2. A non-inline value that starts with `-` is now refused as the value missing (`--sections needs a value; ...`), before `--sections` or `--scenario` applies; an inline `--scenario=-name` still passes.

One answer changes with it: `--sections --print-stdout` is refused at the entry, where the hand-rolled loop and the first commit both took the flag as the value and the run then reported that no scenario matched it. Two regression rows pin both shapes.
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused parser changes address the previous flag-swallowing defect with regression coverage and no unresolved blocking findings.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@Vivswan
Vivswan marked this pull request as ready for review October 26, 2026 21:01
@Vivswan
Vivswan merged commit 9c734b8 into main Oct 27, 2026
29 checks passed
@Vivswan
Vivswan deleted the fix/run-parse-args branch October 27, 2026 07:18
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.

2 participants