Skip to content

fix(tests): restore hidden-subcommand completion coverage#3492

Merged
max-sixty merged 3 commits into
mainfrom
fix/issue-3491
Jul 16, 2026
Merged

fix(tests): restore hidden-subcommand completion coverage#3492
max-sixty merged 3 commits into
mainfrom
fix/issue-3491

Conversation

@worktrunk-bot

Copy link
Copy Markdown
Collaborator

Problem

test_completion_validation in tests/integration_tests/completion_validation.rs had stopped guarding its invariant — that internal/deprecated hide = true items stay out of generated shell completions. The test still ran and passed, but could essentially never fail on a leak (an invisible-failure-mode gap). Two independent causes:

  1. Wrong command. It read wt config shell init <shell>, which back when the test was written (Add black-box completion tests for zsh, bash, and fish #117) emitted static completion definitions (complete -c wt … -l internal). Completions later moved to a dynamic engine (COMPLETE=<shell> wt, assembled in src/completion.rs), so config shell init now emits only the shell wrapper function — no flag or subcommand lines at all. extract_flags therefore returned empty sets for all three shells.
  2. Stale assertion + dead code. The only Severity::Error path checked a hardcoded hidden_flags = ["internal"], and --internal was removed in Replace --internal flag with WORKTRUNK_DIRECTIVE_FILE environment variable #269. The three per-shell validators (validate_fish/validate_bash/validate_zsh) were no-op stubs returning Vec::new().

So the surviving assertion was just "these three commands exit 0."

Root-cause verification

I built wt and drove the dynamic engine directly to settle the question the issue flagged (does re-pointing the test at real hide = true flags restore coverage, or surface a leak?):

  • Hidden subcommands are correctly excludedwt <Tab> omits select; wt hook <Tab> omits run-pipeline and approvals; wt config <Tab> omits completions, previous-branch, hints, ci-status.
  • Hidden flags are intentionally shown. wt merge --<Tab> offers --squash, --commit, --rebase, --remove, --ff, --verify, --no-verify alongside the visible flags. That's the documented behavior of hide_non_positional_options_for_completion ("all-hidden = all shown") — every long flag is surfaced when the user explicitly types --. So "hidden flags never appear in completions" is not an invariant the current design holds; re-pointing the old assertion at the hide = true flag list would have been wrong.

The living, meaningful invariant is therefore about subcommands, not flags.

Solution

Rewrite the test to assert that hide = true subcommands never appear in completions, driving the actual dynamic engine (COMPLETE=fish wt -- <words…>). Each context also asserts a visible sibling is present — that guard is load-bearing: without it, a broken invocation returning no candidates would satisfy every "hidden item absent" check vacuously, which is exactly how the old test rotted. Removes the obsolete config shell init flag-extraction machinery and the three no-op validators (dead code per CLAUDE.md). A doc comment records why hidden flags are deliberately not checked.

Testing

$ cargo test --test integration test_hidden_subcommands_excluded_from_completions
test integration_tests::completion_validation::test_hidden_subcommands_excluded_from_completions ... ok

Verified empirically that the asserted candidate sets match the built binary's dynamic completion output for each context, and that the visible-sentinel assertions keep the test non-vacuous.

Note for review

I chose to restore coverage (assert the subcommand invariant) rather than delete the now-obsolete test. If you'd rather drop it entirely — the subcommand exclusion leans partly on clap_complete's own hidden-item filtering — that's a reasonable alternative call.


Closes #3491 — automated triage

test_completion_validation had rotted into a vacuous test: it read
`wt config shell init <shell>` (which under dynamic completions emits
only the shell wrapper, no flag lines) and checked a hardcoded
`hidden_flags = ["internal"]` set for a flag removed in #269. The three
per-shell validators were no-op stubs returning `Vec::new()`, so the
test could never fail on a leak.

Rewrite it to assert the real, current invariant against the dynamic
completion engine: `hide = true` *subcommands* (`select`,
`hook run-pipeline`, `hook approvals`, `config completions`/
`previous-branch`/`hints`/`ci-status`) never appear in completions.
Each context also asserts a visible sibling is present, so a broken
invocation returning no candidates can't pass vacuously.

Hidden flags are intentionally not checked: the completion assembly
(hide_non_positional_options_for_completion) deliberately surfaces every
long flag, hidden ones included, when the user types `--`.

Closes #3491
The config-context assertions checked completions/previous-branch/hints/
ci-status for absence under `wt config`, but those aren't direct children
of `wt config`: `completions` lives under `wt config shell` and the other
three under `wt config state`. clap never surfaces grandchildren at a parent
level, so the checks passed regardless of `hide = true` — the same vacuous
shape this test was rewritten to eliminate. Probe `wt config shell` and
`wt config state` directly, where the items are real direct children and
their absence is actually caused by hide filtering.

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Self-review — the rewrite is sound. I drove the source independently: select, hook run-pipeline/approvals, config shell completions, and config state previous-branch/hints/ci-status are all #[command(hide = true)], the visible siblings (switch, pre-merge via inject_hook_subcommands, config shell install, config state cache) exist, and affected tests (linux, advisory) passed on this head. Keying on COMPLETE=fish is enough because hide-filtering happens in the clap_complete engine before shell formatting.

One doc nit inline — the guard comment says "hidden flag absent" while describing subcommand checks, which slightly undercuts the flag-vs-subcommand distinction the PR is drawing.

///
/// Each context also asserts a visible sibling is present. That guard is
/// load-bearing: without it, a broken invocation returning *no* candidates would
/// satisfy every "hidden flag absent" check vacuously — the exact failure mode

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Suggested change
/// satisfy every "hidden flag absent" check vacuously — the exact failure mode
/// satisfy every "hidden subcommand absent" check vacuously — the exact failure mode

Minor: this guard covers subcommand checks, and the whole point of the surrounding comment is that flags and subcommands are different invariants — so "flag" here reads as a slip.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 849ab95 — reworded to "hidden subcommand absent" so the comment matches the check it describes and keeps the flag-vs-subcommand distinction clean.

@max-sixty max-sixty merged commit 7735d11 into main Jul 16, 2026
39 checks passed
@max-sixty max-sixty deleted the fix/issue-3491 branch July 16, 2026 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated-fix Automated CI fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test_completion_validation no longer catches hidden flags leaking into completions

2 participants