Skip to content

Guard: assignment-prefixed interpreters and positional pwsh are silent; check 11 brittleness; no cross-rule fixture matrix #2

Description

@BradleyDB

Re-homed from the private predecessor repo's feedback bus (entry F-253, deferred past 0.30.0 on 2026-08-09 with the reopen trigger "the first round after 0.30.0 ships", which never fired). The bus was not carried into this repo (see CONTRIBUTING, "Reading the citations in code comments"), so the debt is recorded here. Re-checked against the seeded tree on 2026-09-09: items 1 to 3 and 8 are still open as described; items 4 to 7 were not re-measured.

None of these is a regression. Every item was measured as silent on the pre-0.30.0 hook too, and the guard is documented as advisory (asks, never blocks) with its residual list in the plugin README.

Guard (plugins/gs-superadmin/hooks/gs-admin-guard.mjs)

  1. pwsh positional branch breaks on the first non-option token, so the -Command path is unreachable behind a positional: powershell foo.ps1 -Command "gs-admin jo p save" is silent on both lanes. Proved by mutation at the time (deleting the branch made the same command ask). Fix: try the flag path first; fall back to the positional only when no command-string flag is found in the segment.
  2. Assignment strip is in isGsAdminWord but not normalizeProg, which is what resolves interpreter names. Every interpreter behind an assignment is invisible: $x=bash -c '...', $x=eval '...', $x=iex '...', $x=powershell "..." are all silent, while the direct $x=gs-admin ... correctly asks. Still true on 2026-09-09: normalizeProg is stripLauncherSuffix(name.split(/[\/]/).pop()) with no ASSIGNMENT_PREFIX strip.
  3. Structural cause of 2: two independent word-normalization paths (isGsAdminWord, normalizeProg); a rule added to one does not reach the other. The same class was fixed once before by sharing stripLauncherSuffix and then reintroduced. Fix 3 first (one shared normalizer) and 1 and 2 close with it.

build/check-doc-drift.mjs check 11 (LOW, from reading rather than probing; deserve more skepticism)

  1. The universal-pin-claim test matches one literal phrase ("pins each one"); a reworded claim lands in the "no claim at all" branch with the wrong message.
  2. The anchor is located twice with different patterns (a multiline-capable exec, then a per-line findIndex); a multi-line anchor passes the first and fails the second, giving a residual block text of the literal string "undefined".
  3. The residual block ends at the first line not matching /^\s*>/, so a bare blank line inside the callout truncates it and drops the pin claim out of scope: a FALSE failure a maintainer would most plausibly "fix" by loosening the check.
  4. The block starts AT the anchor, so a marker whose prose belongs in the earlier bullet can never be found.

Test coverage

  1. Every guard rule is fixtured in isolation (braces, assignment, eval/iex, positional pwsh, option-after-flag) and nothing crosses two of them. Items 1 and 2 were found in minutes by combining already-fixtured rules. Add a combination matrix so the next interaction gap is caught by the suite, not by a review. (The 0.37.0 guard oracle in test/guard-oracle.mjs may already be the right home; not assessed.)

Recommended order

3, then 8, then 4 to 7 as one pass over check 11. Items 1 and 2 are each small enough to fix sooner on their own.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions