Skip to content

E: one word→program-name normalizer, pwsh positional + -Command, oracle matrix + PowerShell lanes, check 11 (F-460, closes #2) - #23

Merged
BradleyDB merged 5 commits into
devfrom
round-e-guard-normalizer
Sep 16, 2026
Merged

BradleyDB merged 5 commits into
devfrom
round-e-guard-normalizer

Conversation

@BradleyDB

Copy link
Copy Markdown
Owner

Base branch must be dev, not main. (It is.)

Regenerated output committed? The plugin bundle was rebuilt (build/build-plugin-gs-superadmin.mjs) and produced no drift; no generator or embedded doc changed.

What

Session E of the September round plan (GD-1, GD-2, GD-3 — issue #2 items 1–3 and 8, and check 11's items 4–7). Bus F-460 logged and FIXED on this branch (Class: second-scanner). Plugin 0.41.2.

  • GD-1 · one word→program-name normalizer (hooks/gs-admin-guard.mjs). isGsAdminWord and normalizeProg were two normalizers; the assignment strip lived only in the first, so every interpreter behind a PowerShell assignment — $x=iex '…', $x=bash -c '…', $x=powershell "…", $x=cmd /c '…', and the ${x}= / $env:X= spellings — ran the mutation with no ask and no journal row (PowerShell assigns the interpreter's output; measured on 5.1 through the oracle). normalizeProg is now the one normalizer (assignment strip → last path segment → case-fold + launcher suffix) and isGsAdminWord derives from it; the pipe-safety lint's consumer lookup and the nested-interpreter table go through the same function.
    • Sibling found by the sweep and fixed in the same change: the assignment grammar's variable arm spelled the name's alphabet by hand ([A-Za-z_][\w:]*), while the computed-name branch carried a second, broader assignment regex. PowerShell runs $1=gs-admin jo p save, ${1}=…, $x.y=… and $a[0]=… (even when the property assignment then errors — measured), and all four were silent on dev. One grammar now ($-led word up to its first =), a superset of the old at the same position, so it can only add asks; the computed-name branch derives from it.
    • Item 1, with a deviation from the issue's recommended fix: the pwsh positional branch ended the payload search at the first non-option word, so powershell foo.ps1 -Command "…" never reached its flag. The issue recommended "flag path first, positional only as a fallback". Measured on 5.1 before choosing: the FIRST positional is the command text — powershell "gs-admin jo p save" -Command "echo hi" runs the mutation with -Command echo hi as arguments — so that order would have dropped a real ask (tenet 3). Both the positional and the -Command payload are re-scanned now. foo.ps1 -Command "…" runs nothing on 5.1 (and -File s.ps1 -Command "…" hands -Command to the script), so those asks are over-asks, the safe direction; the oracle's item-1 rows record it.
  • GD-2 · the combination matrix lives in the oracle's generator (test/guard-oracle.mjs; ruling: Bradley, 2026-09-16, after a decision-tree walk-through). Assignment prefix ($x=, ${x}=, $env:X=, $1=, $x.y=, $a[0]=, the spaced control) × interpreter (bash/sh -c, option-after-flag, iex, iex -Command, Invoke-Expression, {ps} -Command, option-before-flag, positional, option-then-positional, positional-then--Command, cmd /c, eval) plus the item-1 rows, on the PowerShell lane; the $x= × interpreter crosses and the name-in-a-variable × payload crosses on the bash lane. Count: 51 PowerShell matrix rows per lane + 8 bash matrix rows (the PowerShell lane goes 47 → 98 rows, bash 603 → 611) (the full product is pruned where a prefix exercises only the prefix arm — the review's efficiency rule).
    • Every PowerShell on the host is a lane (ruling A): the oracle detects powershell.exe (5.1) and pwsh (7), runs the PowerShell rows under each, spells nested-PowerShell rows with the lane's own executable ({ps}), and prints per-lane version + counts. Effect: the ubuntu leg a dev PR runs now judges the PowerShell rows under pwsh 7 (every GitHub-hosted image ships it) — before, they were skipped on every leg but Windows, so no PowerShell row was ever judged on a dev PR. Windows judges both. A row one version does not execute (7 reads a bare positional as a file path) is unjudged there and counted, not assumed. cmdExe rows claim mustExecute on win32 only (the bash4 pattern).
  • GD-3 · check 11 (build/check-doc-drift.mjs), each item reproduced by a fixture in build/test-check-doc-drift.mjs against the pre-change checker before the change (all five went red; log quoted in the review notes):
    • item 4 (reproduced, fixed): the pin claim's recognised spellings are a PIN_CLAIMS table (universal / split); a block that talks about pinning in an unknown wording is reported as such, pointing at the table, never as "no claim at all".
    • item 5 (reproduced, fixed — with a measurement that sharpened it): the anchor is located ONCE, from the id match, and its grammar accepts an id list wrapped across > quote lines (the old [^>] class refused a wrapped anchor outright as "no anchor"). The literal item-5 shape — a continuation line without > — did pass the first pattern and fail the second (block from index −1, every marker "missing"); that line also ends the callout in CommonMark, so it is red truthfully now, naming the block's line range.
    • item 6 (reproduced the mechanism; not a defect, closed with the measurement): a blank line inside the callout ends the block — CommonMark ends the blockquote there too, so the pin claim past it really has left the callout a user reads. The messages now name the block's line range so the truncation is seen rather than the check loosened; pinned as the decision.
    • item 7 (reproduced, fixed): the block is the whole callout (the walk runs backward to its first line as well as forward), so a marker whose sentence sits in the bullet above the anchor is found; a control pins that a marker outside the callout is still missing.
  • Fixture pins for message text in test/guard-fixtures.mjs (20 new; 17 red against the pre-fix hook); README parsing-boundary bullet extended (no residual closed — the JSON set is unchanged); CHANGELOG 0.41.2.
  • Review round (/code-review medium: 8 finders, 3 file-batched verifiers; 9 confirmed, 1 plausible, 7 cleanups applied, 8 dismissed). Two confirmed defects fixed here: (a) a live bypass on every shipped version — a value-taking PowerShell option before the payload (powershell -ExecutionPolicy Bypass "gs-admin jo p save", -InputFormat Text …, and behind $x=) had its VALUE read as the positional payload, so the real one was never re-scanned; the value-option table gains a pwsh predicate (value-taking parameters by prefix; valueless ones excluded so no payload can be skipped) shared by the flag loop and rescanFrom; (b) a regression this branch had introduced — with the break gone, powershell "gs-admin jo p save" -Command "gs-admin jo p save" journaled two rows for one execution; a positional that carried an invocation now ends the search. Plus: the oracle's on: lane gate (positive claims measured only on 5.1 are asserted on that lane; executed ⇒ guarded holds everywhere), matrix breadth as a data column, one duplicate row removed, isAssignmentWord named once, normalizeProg read once per word, stale "two scanners" comments corrected, workflow comment updated, check 11 on lib.mjs's QUOTE_RE with unterminated-anchor and anchor-outside-callout refusals. Dismissed with reasons: pruning the ${x}= full cross (the braces axis is the issue's own), the double PowerShell lane on Windows (ruling A), Linux-unrunnable rows counted as over-ask (that is what they are), the nested powershell spelling on pwsh-only hosts (a name the host lacks cannot be judged), a preamble marker satisfying the whole-callout block (item 7 by design), deriving the split claim from data (F-252's deliberate false-direction-only rule), merging green drift mutants, and the PowerShell start-up double spawn.

Why

Issue #2 (re-homed from the private bus's F-253): silent misses on the safety surface, measured by mutation, plus check 11's brittleness. The class is second-scanner (F-443): two readers re-deriving one grammar disagree exactly where it is subtle — here, which words are assignments and what a word's program name is.

Review notes

Oracle counts (the judge of record — test/guard-oracle.mjs, this Windows host: bash 5.x + Windows PowerShell 5.1; no pwsh on this machine, so the 7 lane's first measurement is this PR's ubuntu suite):

  • Pre-fix hook (dev at PR D: --deep and Phase 6 boundaries (F-457), guard repeat-ask remedy (F-453), work-ordering bullet (#1) — plugin 0.41.1 #22's merge, run through GUARD_ORACLE_HOOK against a git archive of the dev plugin tree — a lone hook file fails open without its bundled catalog, which the first attempt proved): 709 generated lines run (611 bash 5.x; 98 PowerShell rows × 1 lane(s) — powershell 5.1.26100.9444: 98 rows, 44 bypass, 3 over-ask); 44 bypass, 52 over-ask, 44 over-qualified, 9 documented residuals held80 failures; every bypass is a matrix row (14 under $x= and 14 under ${x}= — the 12 assignment × interpreter crosses each plus the two value-option crosses — 3 under $env:X=, 3+1 under $1=, 3+1 under $x.y=, 3+1 under $a[0]=, 1 under ${1}=).
  • Fixed hook: 709 generated lines run (611 bash 5.x; 98 PowerShell rows × 1 lane(s) — powershell 5.1.26100.9444: 98 rows, 0 bypass, 7 over-ask); 0 bypass, 62 over-ask, 44 over-qualified, 9 documented residuals heldall checks passed.
  • The over-ask delta is exactly the non-executing shapes the generator added (bash $x= forms, $x=eval, the two item-1 scripts).

Pre-change drift-test run (build/test-check-doc-drift.mjs with build/check-doc-drift.mjs from dev): 11b red "makes no claim about pinning at all" (item 4); 11c wrapped anchor red "carries no anchor"; 11c literal shape red with every marker missing; 11d red (same as after, the message differs); 11e red "readmeMarker no longer appears … zzz-above-anchor" (item 7). After: 129 checks green (11f, the unterminated-anchor refusal, added by the review round).

Guard-fixtures against the pre-fix hook: 17 red (10 interpreter/positional pins, 4 assignment-sibling pins, 3 value-option pins; the single-row pin is green there because the old break kept one row), controls green; after: 697 green.

Where to look first: the one normalizer and ASSIGNMENT_PREFIX (hook ~lines 240–300); the pwsh positional loop (~1450); the oracle's lane detection and matrix (~110, ~545); check 11 (~1286–1400).

Battery run locally, verbatim: guard-fixtures, guard-oracle, doc-lib-fixtures, contract-conformance, test-check-doc-drift, test-check-stale-facts, test-defect-classes, check-stale-facts, check-doc-drift, check-imports, check-instance-data, npm run typecheck, plugin rebuild + git diff (no drift), claude plugin validate ×2 (strict).

Issue #2 disposition: items 1, 2, 3, 8 closed by this PR; items 4, 5, 7 fixed with fixtures; item 6 measured not a defect (pinned as the decision). All eight addressed → Closes #2 — the E-V verdict comments the oracle's counts on the issue.

  • Plugin version bumped in .claude-plugin/plugin.json with a CHANGELOG entry (0.41.2 — the hook's decisions change).
  • Safety boundary: this PR touches hooks/gs-admin-guard.mjs. No "ask" became a "deny" or disappeared: the normalizer change is a superset match (every word that matched before matches now with the same remainder), the assignment grammar is a superset at the same position, and the positional branch re-scans MORE payloads, never fewer. test/guard-fixtures.mjs extended (16 pins) and the oracle's generator extended (the matrix + lanes).
  • Skill edits: none (no SKILL.md changed; README prose only).
  • No test or dev step touched real gs-admin state.
  • No tenant/instance/org-specific data in commit messages or this PR body.

Closes #2

…oracle's combination matrix and PowerShell lanes, check 11 items 4–7 (F-460, issue #2)

GD-1 (hooks/gs-admin-guard.mjs): normalizeProg is the ONE normalizer (assignment strip →
last path segment → case-fold + launcher suffix); isGsAdminWord derives from it, so every
interpreter behind a PowerShell assignment ($x=iex '…', $x=bash -c '…', $x=powershell "…",
$x=cmd /c '…') now asks. Sibling from the sweep: the assignment grammar admits every
spelling PowerShell runs ($1=, ${1}=, $x.y=, $a[0]=) as ONE grammar shared with the
computed-name branch (isAssignmentWord). The pwsh positional loop re-scans a positional
that carried nothing AND a later -Command (issue #2 item 1; measured: the first positional
is 5.1's command text, so the issue's flag-first order would have dropped a real ask), and
skips a value-taking option's value (review round: -ExecutionPolicy Bypass "…" was a live
bypass on every shipped version) through a pwsh entry in the value-option table; a
positional that carried an invocation ends the search (one execution, one journal row).

GD-2 (test/guard-oracle.mjs; ruling: the oracle's generator): assignment prefix ×
interpreter × option/positional matrix (51 PowerShell rows per lane, 8 bash), every
PowerShell on the host as a lane (powershell.exe 5.1 + pwsh 7, {ps}-spelled nested rows,
per-lane counts), an `on:` gate for claims measured on 5.1 only. This host: pre-fix hook
709 lines / 44 bypass / 80 failures; fixed 709 / 0 bypass / 62 over-ask.

GD-3 (build/check-doc-drift.mjs check 11): PIN_CLAIMS table with an unrecognised-wording
message (item 4); one wrap-tolerant anchor grammar located once, refusing an unterminated
anchor (item 5); a blank line ends the callout truthfully, the message names the block
range (item 6 — measured, not a defect); the block is the whole callout on lib.mjs's
QUOTE_RE, refusing an anchor outside it (item 7). Fixtures 11b–11f in
test-check-doc-drift.mjs, each red on the pre-change checker.

Plugin 0.41.2 + CHANGELOG; README parsing-boundary bullet; guard-fixtures 20 new pins
(17 red on the pre-fix hook); bus F-460 logged and FIXED (Class second-scanner).
@BradleyDB

Copy link
Copy Markdown
Owner Author

Dispatch evidence on the handoff tip 58b1330 (hb-20260916-02), quoted after completion: validate-plugin 35147717941 (PR) changes/validate (ubuntu)/manifests all success; docs-drift 35147717956 (PR) drift (full) success; dispatches 35147785798 (validate-plugin, all jobs success) and 35147788662 (docs-drift, full + stripped success). The pwsh 7 lane's first measurement, from the ubuntu job log: 709 generated lines run (611 bash 5.x; 98 PowerShell rows × 1 lane(s) — pwsh 7.6.5: 98 rows, 0 bypass, 20 over-ask); 0 bypass, 78 over-ask, 44 over-qualified, 9 documented residual(s) held — All guard-oracle checks passed. The two PowerShell residual rows executed under 7 too; the 13 extra over-asks are the positional shapes 7 reads as a file path.

BradleyDB added a commit that referenced this pull request Sep 16, 2026
…0 logged + FIXED, hb-20260916-02, dispatches quoted green [skip ci]
…ed; oracle 709/0/62, 5.1 lane 98/0/7, pwsh 7.6.5 lane 98/0/20 in CI) [skip ci]
…916-02 (the [skip ci] verdict commit leaves dev's required drift (full) check unrun)
…02; the previous two tips carried the skip marker)
BradleyDB added a commit that referenced this pull request Sep 16, 2026
…-V), suites green on the verdict tip 963897d, unmerged [skip ci]
@BradleyDB
BradleyDB merged commit 9549d7b into dev Sep 16, 2026
5 checks passed
@BradleyDB
BradleyDB deleted the round-e-guard-normalizer branch September 16, 2026 21:37
BradleyDB added a commit that referenced this pull request Sep 16, 2026
…nch deleted, #2 closed, post-merge dispatches quoted green on 6308661 [skip ci]
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