Skip to content

fix: reject --key=value form in unknown-flag guard#373

Merged
danieljohnmorris merged 2 commits into
mainfrom
fix/flag-equals-form
May 18, 2026
Merged

fix: reject --key=value form in unknown-flag guard#373
danieljohnmorris merged 2 commits into
mainfrom
fix/flag-equals-form

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

ab-tester rerun9 noticed the unknown-flag guard catches --bogus and --engine tree but lets --engine=tree and --foo=bar through. The equals form gets silently consumed as a positional and surfaces as a misleading ILO-R012 no functions defined or ILO-R004 main: expected N args, got N+1 downstream. Same retry-tax trap as the bare-flag variant six personas burned minutes on in rerun8, just on a shape the guard didn't cover.

Manifesto framing: zero-token-cost diagnostics on the highest-frequency CLI mistake. The fix is one split_once('=') away from symmetric coverage with the space-separated form, and it turns a confused arity error into a clear "unrecognised flag" with the -- escape hint already in place.

Repro

Before (on main, 0.11.8):

$ ilo m.ilo main --engine=tree 5
{"code":"ILO-R004","message":"main: expected 1 args, got 2",...}

After:

$ ilo m.ilo main --engine=tree 5
error: unrecognised flag '--engine=tree'. Use 'ilo --help' for valid flags. To pass it as a literal arg, separate with '--' first.

Symmetric with the already-working ilo m.ilo main --engine tree 5 error.

What's in the diff

cf23109 reject --key=value form in unknown-flag guard

  • reject_unknown_flags_with_allowlist now split_once('=') on each arg and runs looks_like_clean_long_flag against the --key head.
  • Allowlist matches against both the head and the original token, so any future caller pre-approving a known flag accepts --bench=on as well as --bench.
  • The pre-existing unit test that intentionally accepted --foo=bar was flipped to assert rejection. The doc comment justifying the old "err on the side of accepting it" behaviour was itself the bug.
  • 6 new unit tests: --engine=tree, --foo=bar rejection, allowlist-head acceptance, -- separator escape on equals form, --foo= empty-value rejection, key=value (no leading --) data passthrough.

a0997e9 add cross-CLI regression tests for --key=value rejection

  • 5 new cases in tests/regression_unknown_flag_guard.rs covering bare-positional path, run subcommand path, -- separator escape on the equals form, and key=value data passthrough.
  • File-level contract comment updated to spell out the --word=value shape is now caught.
  • examples/unknown-flag-equals-form.ilo exercises the guard's no-op behaviour on plain positionals across every engine via the existing tests/examples_engines.rs harness.

Test plan

  • cargo test --release --features cranelift green locally (full suite)
  • Targeted: cargo test --release --features cranelift -- unknown_flag all green, including the four new equals-form cases
  • cargo test --release --features cranelift --test examples_engines green (new example file picked up automatically)
  • cargo fmt --check clean
  • cargo clippy --release --features cranelift --all-targets -- -D warnings clean
  • Manual repro: --engine=tree, --foo=bar both now error cleanly with exit 1; --engine tree / --bogus still error; -- --foo=bar still passes through as data
  • CI green on all four checks

Follow-ups

None. Behaviour parity restored with the space-separated form; nothing else in the CLI path expects the equals form to slip through.

split each arg on '=' before the shape check so --engine=tree and
--foo=bar are caught the same way as --engine tree. before this the
shape predicate excluded any token containing '=', so the equals form
slipped past the guard, got silently consumed as a positional, and
surfaced as a misleading ILO-R012/R004 downstream.

allowlist also matches against the --key head, so callers that
pre-approve a known flag accept both --bench and --bench=on.

the pre-existing unit test that intentionally accepted --foo=bar was
flipped to assert rejection; the doc comment justifying the old
behaviour was itself the bug. new tests cover --engine=tree, --foo=
(empty value), allowlist-head acceptance, and --foo=bar after the --
separator.
cover the bare-positional path, the run subcommand path, the --
separator escape still working on the equals form, and key=value
(no leading --) staying as data. update the file-level contract
comment to spell out that the --word=value shape is now caught.

examples/unknown-flag-equals-form.ilo gives the engine harness a
happy-path file so the guard's no-op behaviour on plain positional
args stays under cross-engine regression coverage.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris merged commit 5fd5ca9 into main May 18, 2026
5 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/flag-equals-form branch May 18, 2026 10:44
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