test: add ShouldProcess and comment-based-help coverage sweeps, and fix the gaps they find - #146
Merged
juemerson-at-purestorage merged 7 commits intoAug 25, 2026
Conversation
Two CI tripwires over the whole Public/ population, in the style of Tests/PfbEmptyPipelineGuardCoverage.Tests.ps1: AST assertions, not behaviour tests, that catch a 544-cmdlet generated-and-hand-edited population decaying silently as new cmdlets arrive without the convention. PfbShouldProcessCoverage asserts that state-changing verbs declare SupportsShouldProcess, that a declaration is actually called, that the call is not in an inner scope, that read-only verbs never declare it, and -- the load-bearing one -- that every Remove-* declares ConfirmImpact = 'High'. ConfirmImpact prompts only when it meets or exceeds $ConfirmPreference, whose default is High, so a Remove-* at Medium deletes without ever prompting. PfbHelpCoverage asserts a non-empty .SYNOPSIS per cmdlet, a .PARAMETER entry per declared parameter matched BY NAME, no .PARAMETER naming a parameter that does not exist, and a non-empty body on each. Name matching rather than counting is what catches a rename, where the counts stay equal. Both files assert a population floor first. Every other assertion is "the offender set is empty", so an empty Public/ glob or a regressed AST walk would pass all of them while checking nothing. Both also carry a mutation-proof It that runs the record builders against synthetic fixtures with known answers in each direction, so a parser that flags nothing cannot read as green. Test-PfbNestedInInnerScope is duplicated from PfbEmptyPipelineGuardCoverage.Tests.ps1 rather than shared. Extracting one copy to tools/lib/ is the right end state and is a deliberate follow-up; refactoring a CI-critical gate was out of scope here. Remove-PfbWorkloadTag, the only Remove-* at ConfirmImpact = 'Medium', is parked in a clearly-marked PENDING-DECISION list of its own. Whether it is a defect or a deliberate choice is the maintainer's call and must be settled before merge. Pure parsing throughout -- neither file imports the module, so no module state is created or leaked. Both pass under pwsh 7 and Windows PowerShell 5.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Documentation only -- no executable line changes, so nothing here can alter a request the module sends or a response it parses. Six cmdlets had undocumented parameters: Update-PfbSupport no help block at all; 0 of 2 parameters Set-PfbContext 0 of 5 Invoke-PfbInContext 0 of 5 Clear-PfbContext 0 of 1 Get-PfbLog 4 of 6 (StartTime, EndTime) Get-PfbRemoteArray 6 of 7 (CurrentFleetOnly) A seventh, not in the original list, is the opposite drift and is the reason the sweep matches by name rather than by count: Remove-PfbArrayErasure carried a .PARAMETER Attributes entry for a parameter its param() block does not declare. Removed rather than added -- the cmdlet issues a DELETE with no body, so the entry is stale rather than a missing feature. tools/Update-PfbContextHelp.ps1 was checked first for the three Context cmdlets, per the spec's note. It generates a .NOTES context-requirement block only, keyed on endpoint contextScope, and emits no .PARAMETER entries at all -- so these are hand-written and are not at risk of being overwritten by a regeneration. Public/ now reads 544/544 cmdlets with a .SYNOPSIS and 2860/2860 declared parameters documented, with no orphaned, duplicated, nameless or empty entries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eight code findings from the whole-branch review, plus the ConfirmImpact ruling
the previous commit left open.
Help-block lookup -- the one that could false-green:
PfbHelpCoverage located a cmdlet's help by taking the nearest .SYNOPSIS block
INSIDE the function, falling back to the nearest one ABOVE it. Two shapes then
report as fully documented while Get-Help shows nothing: a file-header block a
dozen lines up, and a block belonging to a nested helper when the cmdlet itself
has none. The second is why dropping the fallback alone would not have been
enough -- a nested function's extent is a subset of the cmdlet's, so those
blocks passed the "inside" test too. A block must now sit inside the function
body and outside any nested function within it. All 544 blocks in Public/
already satisfy that, so this is strictly stronger rather than a restriction
anyone has to work around. The fallback branch had no fixture at all; four new
fixtures now pin placement in both directions.
ShouldContinue no longer satisfies SupportsShouldProcess:
ShouldContinue does not participate in -WhatIf -- it is an extra confirmation
prompt that runs regardless -- so a cmdlet declaring SupportsShouldProcess whose
only guard is ShouldContinue still issues its request under -WhatIf, which is
verbatim the harm that assertion exists to catch. The record now counts the two
separately. ShouldContinue stays in the inner-scope scan, where the hazard is
identical either way. sweep-tests-spec.md specified the original wording, so the
implementation was faithful and the spec is what needed correcting.
Remove-PfbWorkloadTag -- MAINTAINER RULING, keep it at Medium:
The previous commit parked this as pending and said it must be settled before
merge. Settled: a workload tag is metadata, DELETE /workloads/tags carries an
empty body and destroys no array data, and the tag is cheaply recreated, so the
prompt its 111 siblings carry would be friction protecting nothing. It is now a
settled entry in $confirmImpactExempt carrying that reason. Accepted cost,
recorded rather than glossed: someone who has learned that Remove-Pfb* stops and
asks will not be asked here. Revisit if the endpoint ever gains the ability to
delete something other than labels.
The separate pending-decision list goes away with the ruling and its entry moves
into $confirmImpactExempt, which keeps the same per-entry staleness guard: each
name must resolve to exactly one real cmdlet, and that cmdlet must still be
non-High. Neither list ever had a bound on its LENGTH -- a growth cap was
requested in review and declined, so nothing mechanically stops N Remove-*
cmdlets being parked at Medium with the sweep still green, and nothing enforces
that an entry carries a written reason. That gap stays open on purpose; it is
held as its own decision rather than folded into this ruling.
Four fail-open paths closed:
- A verb in none of the three lists was checked by nothing: not required to
declare SupportsShouldProcess, not forbidden from declaring it. A census now
reds once, names the offender, and turns classification into a one-line
decision. Connect and Disconnect were sitting in that gap.
- $shouldProcessExempt gained the staleness guard its neighbour already had.
- The two copies of Test-PfbNestedInInnerScope were held in sync only by a
comment, on a function both files call CI-critical and whose regression is
caught by exactly one It in each. An It now asserts they are -ceq identical.
- A comment claimed "first .SYNOPSIS wins" while the guard only prevented
un-setting a flag that is never un-set, so a populated first .SYNOPSIS
followed by an empty second flagged the cmdlet. Now actually enforced.
coverage-baseline.psd1 gains both new Describes in both edition blocks.
Assert-PfbTestCoverage.ps1 walks $Result.Containers, so a file that drops out of
DISCOVERY entirely appears nowhere and the run stays green with the tripwire
silently gone; RequiredDescribes is the only rail that sees that. The entries
match the declared Describe names exactly, verified by comparison -- but they
are first exercised end-to-end by CI, since this repo reserves the full-suite
run for CI rather than running it locally.
Also folds in a held docstring fix in scripts/Assert-PfbSpecCache.ps1, which
cited .gitignore by line number (:36 and :46) and justified its own location
with a blanket tools/ ignore that no longer exists. Comment text only.
Verification: 17 tests pass under pwsh 7 and Windows PowerShell 5.1, 0 skipped,
containers ok on both; 48/48 with the exemplar sweep and CiCoverageGate run
alongside. Each of the eight new or changed assertions was mutation-tested and
all eight were killed. The limit of that evidence is worth stating: neutering an
offender FILTER survives by construction for any "the offender set is empty"
assertion, since the set is empty either way on a clean tree. What protects
those is the fixture pair and the population floors, not the mutation run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit claimed to close the help-lookup false-green, and it did not.
Its rule was "the block is inside the function extent and outside any nested
function", and a residual fail-open of exactly the class it set out to fix
survived: a block one line BELOW param(), or stranded mid-body after an early
return or a validation stanza, satisfies both conditions and Get-Help renders
nothing for it. The sweep reported such a cmdlet as fully documented. It also
took the FIRST surviving candidate on the stated grounds that tokens arrive in
source order, which decides nothing about which block Get-Help reads.
So this replaces the placement rule with a measured one. 34 probe shapes were
written to disk, dot-sourced and read back through Get-Help -Full under both
Windows PowerShell 5.1 and PowerShell 7.6.5 -- every shape agreed across the two
editions, so there is one rule and not one per edition:
- honoured positions are immediately ABOVE the function keyword (at most one
blank line, nothing else in between), at the START of the body before the
param block, and at the END of the body after the last statement.
[CmdletBinding()] belongs to the param block, so a block between the
attribute and param( is not honoured either.
- ABOVE beats everything inside the body; START of body beats END of body;
blocks on consecutive lines form one run and are concatenated, so a later
.SYNOPSIS overrides an earlier one; a blank line ends the run and the first
run carrying help keywords is the help.
- a run whose keywords do not include .SYNOPSIS still claims the help and
suppresses every later block -- .DESCRIPTION alone renders an EMPTY synopsis
rather than falling through. That is why the keyword pattern matches any
.KEYWORD: matching only .SYNOPSIS would credit a block Get-Help never reads.
A block ABOVE the function is honoured by Get-Help -- the previous comment had
that backwards, and the file contradicted itself about it -- but it stays a
finding here, for two reasons now stated instead of asserted. It renders IN
PLACE OF a block inside the body, so a cmdlet carrying both shows the outer one
and the inner one is text no reader ever sees. And crediting it would mean this
sweep has to agree with Get-Help about adjacency to the byte, where the failure
direction is a file-header block being credited as a cmdlet's help. The record
now carries HelpAboveFunction and HelpBlockMisplaced so the failure message can
name the remedy -- move the block -- rather than telling a developer with
perfectly good help to write some.
One place where a first attempt at this was wrong in the other direction, kept
here because it is the reason the rule is shaped as it is: a run that mixes a #
line comment with the help block is not symmetric. A line comment ABOVE the
block makes Get-Help render nothing; one BELOW it is honoured, with the line's
text appended to the last section. Public/FileSystem/Get-PfbOpenFile.ps1 is the
second shape -- it carries a drift-report note between the block and
[CmdletBinding()] -- so rejecting both would have red-built a cmdlet whose help
renders correctly, which the real file confirmed as a control.
Also: the keyword regexes end in [ \t\r]*$ rather than [ \t]*$. In multiline
mode $ matches before the \n but not before the \r, so a tail without \r matches
nothing at all in a CRLF file, which every file here is. The symptom was the
entire 544-cmdlet population reading as undocumented while the same pattern
worked on an LF fixture.
Nine fixtures added, each pinning a measured shape rather than a reading of the
docs: help-below-param, help-mid-body, help-body-end, two-inside-start-and-end,
two-blocks-one-run, two-runs-blank-separated, linecomment-then-help,
help-then-linecomment, above-plus-inside and above-description-only. The nested
function walk stays, demoted to diagnosis: the position test already rejects a
nested helper's block, and what the walk now decides is whether a cmdlet is told
"no help" or "help in the wrong place", which are different fixes.
Two comment corrections in PfbShouldProcessCoverage and one message reword:
- the ConfirmImpact exemption comment retired a rail that never existed. It
said a one-entry cap on the pending-decision list had been removed because
it had no remaining job. Verified against 564f7a0: that list carried only a
per-entry staleness loop and no length assertion anywhere. A growth cap was
requested in review and declined. The comment now says so, and says plainly
that the gap is open -- N Remove-* cmdlets could be parked at Medium and
this file would stay green, and nothing mechanically enforces the "written
reason" the paragraph leans on.
- assertion 1's -Because said only that a cmdlet needs comment-based help with
a .SYNOPSIS. Under the new placement rule a cmdlet can fail it while holding
a perfectly good .SYNOPSIS, so the message now names the placement
requirement and each offender says which of the three cases it is.
Verification: 48 passed / 0 failed / 0 skipped under both pwsh 7.6.5 and Windows
PowerShell 5.1, across PfbHelpCoverage, PfbShouldProcessCoverage,
PfbEmptyPipelineGuardCoverage and CiCoverageGate. Five mutations applied, each
anchor confirmed to match exactly once before running so a no-op is not read as
a survivor, and all five killed by the fixture assertion intended: neutering the
position filter, taking the first rather than the last .SYNOPSIS in a run,
searching the trailing region before the leading one, dropping the
above-the-function flag, and ignoring line-comment order within a run.
No version bump and no CHANGELOG entry -- the maintainer's separate decision.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
be32412 measured where Get-Help reads help from and encoded it, and the review of it found three fail-open shapes still standing. All three are the same root cause: it modelled where a block SITS and never asked what else shared its comment run. Adjacent comments concatenate into one help block and a blank line ends the run, so a block at a perfectly honoured position renders nothing when the line above it is ordinary commentary -- and the sweep scored it as documented. The rule was re-derived from scratch rather than patched, because three separate written accounts of Get-Help placement during this work were each wrong and each corrected only by measurement, including statements in be32412's own message and comments. 125 probe shapes were written to disk, dot-sourced and read back through Get-Help -Full under both Windows PowerShell 5.1 (5.1.26100.8875) and PowerShell 7.6.5. Every shape gave the same placement answer on both editions, so there is one rule and not one per edition. - The unit is the RUN: comment tokens on consecutive lines, `#` line comments and `<#...#>` blocks alike, concatenated with each delimiter stripped IN PLACE and each line comment's leading `#` removed. Content sharing a delimiter's line survives, measured, so the delimiter LINE must not be dropped. - A run is help when its first non-blank line is a recognised directive line AND every directive-SHAPED line in it is recognised. Prose above the first directive voids it; below one it is that section's body. One unrecognised directive anywhere voids the whole block, .SYNOPSIS included. - Regions are searched above the function (last run only, at most one blank line away), then the start of the body, then the end. A region holding no help run suppresses nothing and the search moves on -- so a #region marker or a file header above a function costs nothing. A run that IS help claims it absolutely, even with no .SYNOPSIS in it. The three findings then fall out of one predicate rather than three patches: - above-function help followed by a `#` line comment. The comment JOINS the run and the outer block still renders, in place of any block inside the body. The old code rejected any non-newline token between block and keyword, so it ignored the outer block and credited the inner one -- rendered help and scored help came from different blocks. - an ordinary `<#...#>` block on the line above a help block. Non-directive text first voids the run, help block and all. - the same with an unknown-keyword block. Its blank-line-separated twin is now CREDITED, reversing the documented conservative case: an unrecognised keyword claims nothing, so a help block below it renders normally, and treating it as claiming red-built a shape that works. Measurement contradicted the file in three more places, and the measurement wins in each: - the LAST .SYNOPSIS in a run wins, not the first, within one block as much as across blocks. A trailing EMPTY .SYNOPSIS overrides a populated one and Get-Help renders a blank synopsis. Taking the first was a false green over exactly the defect the empty-synopsis assertion exists to catch. The second-synopsis fixture asserted the opposite and now asserts this. - a run is ONE help block, so every .PARAMETER in it renders no matter which comment carried it. Scoring the single token that held the winning .SYNOPSIS hid the other blocks' entries from the orphan and duplicate assertions; two-blocks-one-run's .PARAMETER Other is a genuine orphan and is now asserted as one. - a bare `.PARAMETER` does not merely fail to document its parameter, it voids the entire block. The NamelessParameterSections field and its assertion are therefore gone: once the run test is correct that counter can never be non-zero, and an assertion over a structurally-zero field cannot fail, which is the failure mode the anti-vacuous floor at the top of the file exists to prevent. The same source defect now fails the first assertion instead, earlier and harder, and HelpRunDefect names the offending line -- the one case a developer cannot diagnose by reading a block that looks correct. The LOW from the review is fixed in the same message. It told developers that help above the function is not rendered. It IS rendered; the finding is a convention one, because an outer block renders in place of an inner one and a cmdlet carrying both has inner help no reader ever sees. The message now says that, and says why crediting it would force this sweep to match Get-Help on adjacency to the byte. Sixteen fixtures added or changed, each measured on both editions first: above-linecomment-then-inside, ordinary-then-help-one-run, ordinary-blank-then-help, unknown-keyword-then-help-one-run, unknown-keyword-blank-then-help, prose-before-keyword, synopsis-with-inline-argument, linecomment-help-claims-run, linecomment-continues-run, invalid-above-falls-through, ordinary-start-help-at-end, delimiter-line-content, unknown-keyword-later-in-block, dotword-prose-voids-block, above-blank-line-still-adjacent and distant-block-falls-through, plus reversed expectations on second-synopsis, two-blocks-one-run and nameless. The last two new ones exist because a mutation of the above-the-function adjacency bound SURVIVED: distant-header looks like it covers that bound and does not, since its last run before the keyword is a line comment and the block above never gets an adjacency test at all. dotword-prose-voids-block is worth naming on its own. `.NET Core is mentioned here` in an .EXAMPLE body is prose to a reader and a malformed directive to Get-Help, because the directive pattern is `\w` and not `[A-Za-z]` -- so is `.5 is a fraction`. Its harmless twin is `.\tools\Update-PfbContextHelp.ps1 -WhatIf` in the dotted-prose fixture, where a backslash is not a word character and the block renders. The pair is the point. Verification: 48 passed / 0 failed / 0 skipped on both editions across PfbHelpCoverage, PfbShouldProcessCoverage, PfbEmptyPipelineGuardCoverage and CiCoverageGate, unchanged from be32412. The 544-cmdlet population is unchanged too -- 544 cmdlets, 542 with parameters, 2860 declared, 2860 documented, and zero findings of any kind under the stricter rule. Seventeen mutations of the detector were applied on both editions, each anchor confirmed to match exactly once first so a no-op is not read as a survivor, and every one was killed. Two of the seventeen survived their first run and are the reason four of the fixtures above exist. Separately, all 41 fixtures were put through real Get-Help on both editions and compared against the detector: 37 agree exactly and 4 diverge, all four being the deliberate above-the-function convention. No version bump and no CHANGELOG entry -- the maintainer's separate decision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rule A scoped review of the help-detector fix found two behaviours the fixture set asserts but does not discriminate: mutating either survives every assertion in the file, so a regression in either would land green. The character class. The existing 'dotword-prose-voids-block' fixture is documented as pinning `\w` against `[A-Za-z]`, and does not -- `.NET` is pure ASCII, so both patterns match it identically and what that fixture actually pins is the argument group. Add 'digit-dotword-voids-block', whose `.EXAMPLE` body opens with `.5 is a fraction ...`: directive-shaped to `\w` alone. Measured on pwsh 7.6.5 and WinPS 5.1, Get-Help renders auto-generated syntax help and no parameter text for it, so narrowing the class would credit a cmdlet whose help does not render at all. Correct the attribution in the two comments that made the wrong claim. Which run above the function counts. Every above-function fixture holds exactly one run up there, so reading the first instead of the last is indistinguishable in all of them -- and in 'distant-header' the first falls through anyway, on adjacency. Add 'last-run-above-function-wins': a file header first, real help last, and a body block as well. Measured on both editions, Get-Help renders the last run, so reading the first would fall through and credit a body block no reader ever sees. Both fixtures were mutation-checked: narrowing the class to `[A-Za-z]` at both sites, and taking `$aboveRuns[0]`, each now fail the suite and passed before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adding the missing help block to Update-PfbSupport.ps1 pushed its param() block from line 5 to line 24, and the drift report records that line as the target of each finding. Six `paramBlockLine` values, one per finding against that file; no endpoint, parameter or enum verdict moves. Regenerated through scripts/Assert-PfbDerivedArtifacts.ps1 -UpdateCommitted rather than by running the generator bare, so the output is the one the CI gate compares against: the gate stages exactly the 29 pinned spec versions, while Build-PfbApiDriftReport.ps1 run on its own records whatever the local tools/specs/ cache happens to hold. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
juemerson-at-purestorage
force-pushed
the
tests/sweep-shouldprocess-help
branch
from
August 25, 2026 22:20
de4018a to
0a81619
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Two CI tripwires over the whole
Public/population, in the style ofTests/PfbEmptyPipelineGuardCoverage.Tests.ps1— AST assertions rather than behaviour tests, aimed at a 544-cmdlet generated-and-hand-edited population decaying silently as new cmdlets arrive without the convention. Plus the seven comment-based-help gaps the new sweep finds.Tests/PfbShouldProcessCoverage.Tests.ps1asserts that state-changing verbs declareSupportsShouldProcess, that a declaration is actually called, that the call is not in an inner scope, that read-only verbs never declare it, and — the load-bearing one — that everyRemove-*declaresConfirmImpact = 'High'.ConfirmImpactprompts only when it meets or exceeds$ConfirmPreference, whose default isHigh, so aRemove-*left atMediumdeletes without ever prompting.Tests/PfbHelpCoverage.Tests.ps1asserts a non-empty.SYNOPSISper cmdlet, a.PARAMETERentry per declared parameter matched by name, no.PARAMETERnaming a parameter that does not exist, and a non-empty body on each. Name matching rather than counting is what catches a rename, where the counts stay equal.Both files assert a population floor first. Every other assertion is "the offender set is empty", so an empty
Public/glob or a regressed AST walk would pass all of them while checking nothing. Both also carry a mutation-proofItrunning the record builders against synthetic fixtures with known answers in each direction, so a parser that flags nothing cannot read as green.The seven documentation fixes
Six cmdlets had undocumented parameters —
Update-PfbSupport(no help block at all),Set-PfbContext,Invoke-PfbInContext,Clear-PfbContext,Get-PfbLog(StartTime,EndTime) andGet-PfbRemoteArray(CurrentFleetOnly).The seventh is the opposite drift, and the reason the sweep matches by name rather than by count:
Remove-PfbArrayErasurecarried a.PARAMETER Attributesentry for a parameter itsparam()block does not declare. Removed rather than added — the cmdlet issues aDELETEwith no body, so the entry is stale rather than a missing feature.tools/Update-PfbContextHelp.ps1was checked first for the three Context cmdlets, per the spec's note.Where most of the work went: what Get-Help actually reads
The help sweep is only worth having if it agrees with
Get-Helpabout which block a reader sees, and three successive attempts at that rule were wrong. Each was corrected by measurement rather than by reasoning, so the rule now in the file was re-derived from scratch:param(), or is stranded mid-body after an early return. The sweep reported such a cmdlet as fully documented.#line comments and<# #>blocks alike — concatenate into one help block, with each delimiter stripped in place; a blank line ends the run. So a block at a perfectly honoured position renders nothing when the line above it is ordinary commentary, and content sharing a delimiter's line survives into the help.Get-Helpcannot accept —.WIBBLE, a bare.PARAMETER,.DESCRIPTION with text on the same line— voids the entire block,.SYNOPSISincluded.Get-Helpand is still reported here rather than credited, because it renders in place of a body block, and because crediting it would mean agreeing withGet-Helpabout adjacency to the byte — where the failure direction is a file header being scored as a cmdlet's help. The record carriesHelpAboveFunctionandHelpBlockMisplacedso the message can name the remedy (move the block) rather than telling a developer with perfectly good help to write some.125 probe shapes were written to disk, dot-sourced and read back through
Get-Help -Fullunder Windows PowerShell 5.1 (5.1.26100.8875) and PowerShell 7.6.5. Every shape gave the same placement answer on both editions, so there is one rule and not one per edition.Verification
PfbHelpCoverage,PfbShouldProcessCoverage,CiCoverageGate,Update-PfbContextHelp. The full suite is CI's job.Get-Helpon both editions and compared against the detector: 37 agree exactly, and the 4 that diverge are all the deliberate above-the-function convention.Get-Help, zero unexplained divergences on either edition, and byte-identical SHA-256 digests across editions over 19,800 directive-line verdicts.That review found two behaviours the fixture set asserted but did not discriminate; the last commit closes both.
dotword-prose-voids-blockwas documented as pinning\wagainst[A-Za-z]and does not —.NETis pure ASCII, so both patterns match it identically, and what that fixture actually pins is the argument group.digit-dotword-voids-blockopens its.EXAMPLEbody with.5 is a fraction …, which is directive-shaped to\walone. Measured on both editions,Get-Helprenders auto-generated syntax help and no parameter text for it, so narrowing the class would credit a cmdlet whose help does not render at all.last-run-above-function-winsputs a file header first, real help last and a body block as well;Get-Helprenders the last run, so reading the first would fall through and credit a body block no reader ever sees.Both new fixtures were mutation-checked: narrowing the class at both sites, and taking
$aboveRuns[0], each now fail the suite and passed before.Live verification
Exempt. Every change under
Public/is inside a comment block — no executable line changed in the module source or the manifest, confirmed by comparing the parsed token stream on both sides of the diff.Derived artifacts
Reports/PfbApiDriftReport.jsonis regenerated in the last commit. Adding the missing help block toUpdate-PfbSupport.ps1pushed itsparam()block from line 5 to line 24, and the report records that line as each finding's target — sixparamBlockLinevalues, no endpoint, parameter or enum verdict moved. Regenerated throughscripts/Assert-PfbDerivedArtifacts.ps1 -UpdateCommitted; the full gate now reports all 11 artifacts up to date.Not included
No version bump and no
CHANGELOG.mdentry — the maintainer's separate decision.