cli: refuse every global flag no verb can honor #151

Merged
natsukium merged 3 commits from docs/global-flag-matrix-136 into main 2026-09-06 02:43:43 +09:00
Owner

Fixes #136.

Three verb forms silently dropped a global flag: felis --version answers
before dispatch and ignored all four, while completions and __mangen
ignored the carrier flags (--host, --socket, --ssh-arg). They now
refuse what they cannot honor, exit 2, like every other verb that neither
reads nor forwards a global. The per-arm checks move into
verb_refusing_carrier, a twin of verb_reading_no_config; both are
exhaustive over Cmd, so a new verb cannot land without deciding its cells,
and a table-driven test walks every verb form against every global.

Doc cascade: docs/reference/cli.md gains a placement matrix stating every
verb/flag cell (forwarded, read, dials, refused) plus the frontend and
completions paragraphs that were only implicit; docs/reference/control-surfaces.md
points at the matrix; CHANGELOG.md records the three newly refused forms;
skills/felis/SKILL.md records that felis --version refuses the globals.

Verified: just check green (fmt, clippy, nextest, deny, proto-compat);
reviewed by pi luna + pi sol; docs proofread by Gemini gemini-3.8-flash-high

Deferred

  • A docs/explanation twin for the placement matrix — the plan named only the
    two reference pages; the matrix is normative lookup material with no new
    argument, so doc-cascade's default-to-no-record gate applies.
  • test: the new matrix test collapses three distinct global flags into one
    carrier bucket — the per-flag distinction for __complete-sessions
    (--socket dials vs --host/--ssh-arg give no candidates) is already
    pinned end-to-end by pre-existing, untouched tests in cli_sessions.rs;
    refactoring the new table-driven unit test's granularity goes beyond issue
    #136's criteria, since the real behavior is not unguarded.
  • test: the frontend row in the matrix table test never parses a global flag
    combined with the verb — the finding's own verification shows that parse +
    refuse combination is already pinned end-to-end in cli_sessions.rs,
    matching the round-1 precedent where a sibling asymmetry finding was
    deferred on the same grounds; fixing it would duplicate coverage rather
    than close a gap.
Fixes #136. Three verb forms silently dropped a global flag: `felis --version` answers before dispatch and ignored all four, while `completions` and `__mangen` ignored the carrier flags (`--host`, `--socket`, `--ssh-arg`). They now refuse what they cannot honor, exit `2`, like every other verb that neither reads nor forwards a global. The per-arm checks move into `verb_refusing_carrier`, a twin of `verb_reading_no_config`; both are exhaustive over `Cmd`, so a new verb cannot land without deciding its cells, and a table-driven test walks every verb form against every global. Doc cascade: `docs/reference/cli.md` gains a placement matrix stating every verb/flag cell (forwarded, read, dials, refused) plus the `frontend` and `completions` paragraphs that were only implicit; `docs/reference/control-surfaces.md` points at the matrix; `CHANGELOG.md` records the three newly refused forms; `skills/felis/SKILL.md` records that `felis --version` refuses the globals. Verified: just check green (fmt, clippy, nextest, deny, proto-compat); reviewed by pi luna + pi sol; docs proofread by Gemini gemini-3.8-flash-high ## Deferred - A docs/explanation twin for the placement matrix — the plan named only the two reference pages; the matrix is normative lookup material with no new argument, so doc-cascade's default-to-no-record gate applies. - test: the new matrix test collapses three distinct global flags into one carrier bucket — the per-flag distinction for `__complete-sessions` (`--socket` dials vs `--host`/`--ssh-arg` give no candidates) is already pinned end-to-end by pre-existing, untouched tests in `cli_sessions.rs`; refactoring the new table-driven unit test's granularity goes beyond issue #136's criteria, since the real behavior is not unguarded. - test: the frontend row in the matrix table test never parses a global flag combined with the verb — the finding's own verification shows that parse + refuse combination is already pinned end-to-end in `cli_sessions.rs`, matching the round-1 precedent where a sibling asymmetry finding was deferred on the same grounds; fixing it would duplicate coverage rather than close a gap.
Three forms dropped a global on the floor: `felis --version` answers
before any dispatch and ignored all four, while `completions` and
`__mangen` ignored the carrier. A silently dropped flag leaves a user
believing felis honored it, which is why `--config` and the carrier are
refused elsewhere already; these three now refuse for the same reason.

The per-arm carrier checks move into `verb_refusing_carrier`, a twin of
`verb_reading_no_config`. Both are exhaustive over `Cmd`, so a new verb
cannot be added without deciding its cells, and the matrix test walks
every verb form against every global.

`--version`'s own coverage test named its socket with `--socket`, which
is now refused beside the flag; it names it through `FELIS_SOCKET`
instead, since the test is about what the report touches.

Refs #136
"Global options" stated the four flags but not, per verb, whether one
is forwarded, read here, dialed, or refused, so a reader could only
infer the answer from the paragraphs that happened to mention it. The
matrix states every cell, which is what makes "no verb ignores a
global" checkable rather than a claim.

Refs #136
skills: record that felis --version refuses the globals
All checks were successful
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 51s
bench / Criterion regression gate (pull_request) Successful in 2m7s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m35s
pr / nix flake check (pull_request) Successful in 32s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m36s
windows / cargo nextest (Windows) (pull_request) Successful in 5m50s
pr / wire schema is compatible with the base (pull_request) Successful in 8s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 53s
windows / frontend smoke (Windows) (pull_request) Successful in 1m44s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (push) Has been skipped
darwin / build felis (aarch64-darwin) (push) Successful in 14s
pr / cargo build / clippy / test / deny (push) Successful in 1m38s
pr / wire schema is compatible with the base (push) Successful in 9s
pr / frontend smoke (x86_64-linux) (push) Successful in 6s
windows / cargo clippy (Windows cross) (push) Successful in 14s
pr / publish felis (x86_64-linux) (push) Successful in 10s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
fuzz / cargo fuzz smoke (per target) (push) Successful in 1m4s
pr / nix flake check (push) Successful in 6s
windows / cargo nextest (Windows) (push) Successful in 5m40s
windows / frontend smoke (Windows) (push) Successful in 1m32s
windows / package felis (x86_64-pc-windows-msvc) (push) Successful in 2m29s
3279fd8506
The product-shipped skill told agents the flag dials nothing but not
that a global beside it is now an exit 2, so a scripted
`felis --socket … --version` would look like a felis bug.

Refs #136
natsukium deleted branch docs/global-flag-matrix-136 2026-09-06 02:43:43 +09:00
Sign in to join this conversation.
No description provided.