Teach the wire-name resolver three assignment shapes; measure 32 more keys and classify dead keys - #151
Merged
juemerson-at-purestorage merged 29 commits intoAug 28, 2026
Conversation
Recognize only AST-exact Boolean casts and zero-argument ToString/ToLower chains while preserving the resolver's never-guess boundary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Exercise method arity and Boolean type aliases while keeping wire-key fixtures independent of parameter names. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Extract Get-PfbHelperArgumentSourceVariable so the Add-PfbCommonQueryParams -Names/-Ids readers accept a bare variable or an exact zero-argument $var.ToArray() call, and nothing else. Real shape: Get-PfbUserGroupQuotaPolicy hands its [List[string]] accumulators to the helper's [string[]]-typed parameters as $allNames.ToArray(). Guards, one per early-return line: not static, member literally ToArray, zero arguments (via Test-PfbInvokeHasNoArguments), bare VariableExpressionAst target. The static guard is separate because $var::ToArray() satisfies all three others -- it parses as an InvokeMemberExpressionAst with member ToArray, no arguments, and a bare variable target -- so only .Static rejects it. Extracted rather than inlined for testability: the guards are now reachable from a unit test against a parsed AST, independent of the fixture-file path, giving each guard a second kill route. A guard with one route is one outer-check bug away from being silently uncovered. The fixture builder now throws on parse errors. `-Names [SomeType]::ToArray()` does not parse in command-argument mode -- PowerShell emits ExpectedExpression and splits it into a bareword plus a ParenExpressionAst -- so a fixture written that way never reaches any guard while the suite stays green. Inventory: 2071 -> 2073 resolved of 2168, the two rows being Get-PfbUserGroupQuotaPolicy -Name -> names and -Id -> ids, Query/GET on user-group-quota-policies. Nothing else moves. Tests 119 -> 136, both editions, 0 failed 0 skipped.
Two reviewers (opus5-medium, kimi-k3-max) both APPROVED 4da55f0. Three live Minor findings, all addressed here; no behaviour change. - Document the dynamic-member guard as StrictMode-defensive rather than behavioural. It has no mutation kill route because deleting it leaves the next line refusing the same shape while this file runs without Set-StrictMode -- an equivalent mutant, not an uncovered guard. Measured by re-running the shape corpus against the mutated build. - Assert the shared-accumulator fixture parses. It was the one new fixture parsing inline source without the assertion the other two builders make. - Relabel two direct negatives that are refused by the member-name guard, not the STATIC guard. Measured under mutation: `$allNames::ToArray()` is the only shape in that block that reaches Static. Scoped Pester: 136 passed / 0 failed / 0 skipped, both editions, unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wire-name resolver decided a variable's request role by its NAME: a
`switch ($TargetVariable) { 'body' { 'Body' } 'queryParams' { 'Query' } }`
trust gate that no request argument ever had to confirm. That was wrong in
both directions. A variable called $q, $payload or $destroyQuery got no role
at all even when it was handed straight to Invoke-PfbApiRequest, and a
variable called $body would have been reported as a Body landing even if the
only place it went was -QueryParams.
Get-PfbRequestRoleForVariable replaces the gate. It scans the function's
Invoke-PfbApiRequest calls, derives the surface from the command PARAMETER
the variable is passed to, and derives the operation from literal -Method and
-Endpoint arguments. Both argument forms are read (`-Body $x` and `-Body:$x`),
a nonliteral operation argument leaves the operation unread rather than
guessed, and an unread landing still counts as a landing -- a readable call
never speaks for one it could not read.
Arbitration is now over the complete tuple. Every landing an idiom finds is
collected, identical tuples collapse to one, and a complete answer is returned
only when exactly one distinct tuple survives; otherwise only the components
every candidate agrees on are kept and the rest are nulled. This is what keeps
Remove-PfbFileSystem -DeleteLinkOnEradication honest: it writes the same key
into $destroyQuery (reaching a PATCH) and into $queryParams (reaching a
DELETE), and retiring the name gate makes $destroyQuery the earlier AST match,
so a first-match resolver would report PATCH and hide the DELETE outright. It
keeps its key and its Query surface and names no operation.
Get-PfbEndpointForVariable stays as a compatibility wrapper and delegates; it
keeps no copy of the name switch.
Over the real Public/ tree this resolves 30 (cmdlet, parameter) pairs that
were previously unresolved, all of them payload variables the old gate could
not see by name. It also withdraws one previously-resolved row --
Update-PfbBucketAuditFilter -BucketName, which is written to two different
query keys ('bucket_names' and, in the -Name default branch, 'names') and so
has no single wire name to report. The old answer was true but partial, and
picking it depended on source order.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…are components ordinally Review round 1 on the issue dmann000#141 Task 3 resolver. Finding 1 (major). Get-PfbWireNameForParameter documented that a tier which finds candidate landings and then abstains ends the resolution, but only the index tier implemented it. The literal, nested-reference and helper tiers gated on the truthiness of the arbitrated answer, which cannot distinguish "found nothing" from "found landings that disagreed", so an abstaining tier fell through and let a weaker idiom publish the arbitrary pick the abstention exists to refuse. Split the two landing producers out of their arbitrating wrappers (Get-PfbHashtableLiteralWireLanding, Get-PfbNestedReferenceWireLanding) and gate every tier on landings.Count instead. The same invariant now holds for the two sub-forms inside the nested-reference tier. Finding 2 (major). Resolve-PfbWireLandingArbitration merged candidate components with -ne, which is case-insensitive for strings: two landings differing only in the case of a wire key or an endpoint were judged to agree and the first one the parser reached was published, with no clash reported. Compare with [string]::Equals(..., Ordinal), matching the ordinal List[string].Contains distinctness tests in Get-PfbRequestRoleForVariable. Dropped the matching ToUpperInvariant on the method so the tracer reports what the source says rather than a literal that appears nowhere in it. Minor. Corrected the comment on the -isnot [CommandParameterAst] argument-binding guard, which claimed to prevent a misread that every downstream branch already re-validates away; it is kept for correctness at the point of binding, not because a test covers it. Added the parse assertion test-bed rule 1 asks for to the two on-disk fixtures in the issue dmann000#99 Describe. Tests. One tier-boundary test per boundary, each carrying a control assertion that the later tier really would have answered; four case-sensitivity tests; and a real-tree enumeration of the whole one-variable-many-keys population, masked instances included, so a future unmasking is a named event rather than a regression from nowhere. No behaviour change on the real tree: all 2168 inventory rows are byte-identical to the previous commit.
The Task 3 re-review found one surviving mutant: returning $literalLandings[0] instead of arbitrating over the whole array leaves the suite green at 192/0/0. Not an equivalent mutant -- on two disagreeing outer keys the resolver correctly abstains and the mutant publishes the first one, which is first-match arbitration alive in the code path fix round 1 restructured. The existing sibling test cannot reach it: the nested tier consults its INDEX sub-form first, so with any index assignment present the literal sub-form never answers. The new fixture has no index assignment at all, making the literal sub-form the only producer. Carries a control assertion -- one outer key alone resolves to 'gamma' -- so the refusal is the disagreement talking rather than an inert fixture. Scoped Pester: 193 passed / 0 failed / 0 skipped, both editions. Mutant re-applied and confirmed KILLED in both editions, by this test alone (192/1). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The re-review noted the .DESCRIPTION asserted the sticky-abstention invariant as a general property. It holds within Get-PfbWireNameForParameter and stops at its return: an abstention is signalled as $null, which is also how silence is signalled, and Get-PfbCmdletParameterInventory retries through Find-PfbAccumulatorVariable on any falsy result. Measured end-to-end: a parameter written to both $q[alpha] and $q[beta] AND fed to an accumulator keyed at $q[names] abstains in the resolver and still emits a Typed row naming names. Latent -- no cmdlet in Public/ has that shape, as neither known multi-key parameter has an accumulator -- and it predates the tier work rather than being introduced by it. Comment only. An over-broad doc claim about this invariant is the same defect the tier fix addressed, one level up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Integrate issue dmann000#141 Task 3's role tracing into the cmdlet parameter inventory and retire the remaining name gates. Resolve-PfbParameterWireLanding now returns { Landings; Resolution } so the inventory can tell an ABSTENTION from SILENCE. Both used to surface as $null, and the Find-PfbAccumulatorVariable retry fired on either -- so a parameter whose own landings disagreed had a fifth source consulted on its behalf and was republished with a confident name. The retry now fires only on Landings.Count -eq 0. Add two non-applicable Surface values, neither of which is a failure to resolve: OutsideStandardRequest (the declaring function issues no Invoke-PfbApiRequest at all) and NotWireParameter (an audited request control that steers the call rather than appearing in it). The latter is an enumerated Cmdlet|Parameter allowlist, never a name pattern -- the suite re-validates every entry against the real AST so a stale one fails loudly, and a fixture pair proves two identically shaped -Eradicate switches classify differently purely by identity. Add tools/Compare-PfbInventoryTuple.ps1 and Compare-PfbInventoryTupleSet: a row-level regression gate over Surface|WireName|WireSurface|Method| Endpoint. Task 3 withdrew Update-PfbBucketAuditFilter -BucketName while the Typed total went UP, and only a hand diff inside a code review caught it. A change passes only when a declaration names its exact before and after; a declaration that matched nothing fails too, so the gate cannot rot into a rubber stamp. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Get-PfbParameterCoverageGaps decided doubt with `Surface -ne 'Typed'`. That is a denylist: it reads "anything I cannot resolve is doubt about this endpoint", so the two non-applicable surfaces would have been swept into confidence.unresolvedParameters and demoted 34 real endpoints from high to partial on the strength of parameters that are not wire fields at all and could never have covered a gap. Replace it with an exhaustive `switch ($row.Surface)` whose `default` throws. A new Surface value must now be assigned a meaning here, by hand; until it is, the report fails rather than silently taking whichever side the negation happened to fall on. The second `-ne 'Typed'`, in Get-PfbWireNameCmdletCounts, is left as it is with a comment saying why: its `-or -not $row.WireName` companion already excludes every row a non-applicable surface can produce, and that guard is load-bearing for Typed rows too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Build-PfbFieldCmdletMap.ps1 had three buckets and no reconciliation, so a Surface it had not been taught was filtered out by all three Where-Objects and vanished from the report without a word -- the quietest possible failure, and the one issue dmann000#141 exists to stop. Emit a fourth bucket, notApplicable, carrying `surface` per row. It is deliberately NOT folded into typedUnresolved: that list is read as "the tool could not find this field wire name", and these rows are not fields. The Markdown gets its own heading for the same reason -- a reader triaging work should not be handed six -Eradicate switches as work. Assert the partition instead of assuming it: every inventory row must land in exactly one of five buckets, the fifth being typed-with-ValidateSet, which is emitted nowhere because this report recommends ADDING one. A row that lands in none throws and names the counts. coverage-baseline.psd1: Build-PfbFieldCmdletMap.Tests.ps1 15 -> 25. The two new Describes exercise the script itself, which carries `#Requires -Version 7.0`, so they take the file existing PS7 gate. Measured on Windows PowerShell 5.1, not inferred. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Markdown heading for the non-applicable bucket read "Not a wire field (nothing to inspect)". That is a confident false statement about six real cmdlets. Connect-PfbArray -Username/-Password are OutsideStandardRequest and are POSTed to /api/login by hand (Public/Connection/Connect-PfbArray.ps1:331 builds the body, :351 sends it via Invoke-WebRequest); -ClientId/-Issuer/-KeyId reach the OAuth2 token request the same way. Plan Correction 1 requires these rows be classified as outside the standard request-payload resolver, NOT as having no wire effect, and Completion Condition 7 spells out the same limit. Publishing the stronger claim in prose is the same class of error as writing a wrong row into JSON -- the never-guess contract does not stop at the artifact boundary. The heading now names the resolver's reach rather than the parameter's behaviour, and a disclaimer under it says what OutsideStandardRequest does and does not mean, with the Connect-PfbArray case as the worked example. The new test asserts BOTH halves: the retired wording must not come back, and the disclaimer must be present. Asserting only the first could be satisfied by deleting the section entirely, which would lose the rows instead of describing them correctly. Coverage pin: Build-PfbFieldCmdletMap.Tests.ps1 25 -> 26 on Windows PowerShell 5.1 for that test, measured not inferred. The header note's arithmetic was also wrong independently of this change -- it said one entry had moved since the 297 seed and summed to 307, but PfbApiDriftTools.Tests.ps1 had already moved 8 -> 12 for issue dmann000#113. Recomputed from the map: 297 + 4 + 11 = 312.
Compare-PfbInventoryTuple.ps1 defaulted -BaselineRef to origin/main while the declaration file recorded nothing about the ref its tuples were measured at, so the documented invocation only worked by coincidence. On a stacked branch, origin/main is not an ancestor of the measured ref, and every improvement made by the branches in between is then reported as an undeclared change. The next maintainer either concludes the tool is broken or pastes those rows in to silence it -- pre-authorising movement nobody reviewed, which is the anti-rubber-stamp rail running backwards. The file is now an object with a required baselineRef, which wins over the parameter default when -BaselineRef is not explicitly passed. Explicit -BaselineRef still overrides, so comparing declarations against some other ref stays possible. The resolved ref and its provenance are printed on every run. A bare array is refused with its own diagnostic rather than quietly accepted. That refusal needs ConvertFrom-Json -NoEnumerate: without it the pipeline unrolls a ONE-element JSON array into a bare PSCustomObject, the -is [array] test never fires, and a single-declaration array falls through to the less specific "no baselineRef" error. Both refusal paths were probed and each now emits its own message. Retirement is documented rather than made a softer rail. Once a declared change lands, every entry matches nothing and the run goes red with one STALE-DECL per entry; the script now prints a NOTE naming that case and pointing at tools/inventory-tuple-baselines/landed/, which it never reads. Teaching the unused-declaration rail to tolerate the expected case would also teach it to tolerate the typo'd key it was built to find. Also resolves tar by absolute path on Windows: a bare `tar` under a pwsh launched from Git Bash gets GNU tar, which reads the leading C: of the archive path as a remote host spec and aborts with "Cannot connect to C: resolve failed".
…EADME Compare-PfbInventoryTuple.ps1 was the only script in tools/ with no entry in tools/README.md, so the one place a contributor looks to find out what is in this directory did not mention the gate that a resolver change is supposed to pass. An undiscoverable gate is not a gate. Adds a "Resolver regression gate" section covering why totals cannot catch a withdrawn resolution (the real Task 3 case: Typed +61 while Update-PfbBucketAuditFilter -BucketName silently went from bucket_names to unresolved), the working invocation, the declaration-file shape, and the retirement step. Also adds it to the numbered list, flagged as the one entry there that generates nothing and runs on demand rather than as part of a normal pipeline run. The issue-dmann000#141 Task 4 paragraph in the field-to-cmdlet section is corrected the same way the report heading was: OutsideStandardRequest is a statement about this resolver's reach, not about whether the parameter reaches the wire.
Get-PfbCommonQueryParamHelperWireName collapses two different answers into one $null: "no Add-PfbCommonQueryParams call names this parameter" (silence, keep looking) and "two calls disagree about it" (abstention, stop looking). Resolve-PfbParameterWireLanding is safe because it retries on an empty landing set rather than on a $null from any single resolver, but that safety lives at the caller and was documented only there. A future caller that treated this $null as "not my parameter" and fell through to a looser resolver would turn a deliberate abstention into a confident wrong wire name. Recording the hazard where the ambiguity is actually created. No behaviour change. Nothing in the tree reaches the disagreement branch today, and that is measured: only Get-PfbQuotaUser has two helper call sites, both target $queryParams, and the ByParameterName rule has no Name entry, so the distinct (WireName, TargetVariable) count is 1 tree-wide.
…mp leak
Three defects from the round-1 fix diff, all in what the fix itself added.
The RESIDUAL ABSTENTION HAZARD block on Get-PfbCommonQueryParamHelperWireName
described the hazard as mitigated, which is backwards in three ways and
contradicted the correct account 250 lines away on
Resolve-PfbParameterWireLanding ("One abstention remains invisible here").
Resolution is strict tier precedence, not collection from every resolver; the
accumulator retry lives in Get-PfbCmdletParameterInventory, not in
Resolve-PfbParameterWireLanding; and retrying on an empty landing set is the
mechanism by which this abstention ESCAPES, not a guard against it -- an
abstaining helper leaves the tier empty, which is precisely the retry trigger.
Two comments giving opposite accounts is bad in any file; in the one whose
whole thesis is never-guess, the wrong one reads as permission. Rewritten as
the recorded limit it is. The measurement that nothing reaches it today, and
the warning to future callers, are unchanged.
The recompute command added to coverage-baseline.psd1 did not work: ExpectedSkips
is nested under winps51 and pwsh7, not at the root. Verbatim it throws on pwsh 7
and silently yields nothing on 5.1 without StrictMode -- the worse direction,
since a mitigation for "a running total drifts silently" that returns nothing
would confirm any total put to it. Now names the edition, and says why that is
not optional.
Compare-PfbInventoryTuple.ps1 leaked an empty temp directory on either
declaration-validation refusal: moving the declaration read ahead of the archive
(correct in itself) left the two new throws outside the try whose finally is the
only cleanup. The scratch directory is now created immediately before that try,
so nothing thrown during validation has a directory to leak. Verified against a
control that the counter can see such a directory: 0 before, 0 after both
refusal probes.
Add a `classification` and a `declaredElsewhere` field to every dead-key record in Reports/PfbDeadKeyReport.json (issue dmann000#141 Task 5). A dead key is now labelled WRONG-SURFACE when the endpoint declares the key as a request body property under some verb, WRONG-VERB when another verb declares it as a query key, and UNDECLARED when no operation on the endpoint declares it on either surface. `declaredElsewhere` carries the provenance -- the (method, surface) pairs the classification is derived from -- deduplicated and ordered by method then surface ordinally. The declaration index reuses Get-PfbSpecCapabilities for body properties, so the $ref/allOf walk that reaches Alert.flagged is the repo's existing walker rather than a second implementation, and reuses Get-PfbDeclaredQueryKey for query keys, so the index can never disagree with the gate that decided the key was dead. Capability records' Parameters field is deliberately not used: it is not filtered by `in:`, and 630 header-parameter occurrences across 629 of 632 operations would otherwise be read as query declarations. Deadness itself is unchanged: the pre-existing deadKeys and noSurvivingSelector populations are byte-identical on their pre-existing fields, and the 66 parameters previously bucketed as "wire name unresolved" now split 32/28/6 across unresolved, outside-standard-request and not-wire-parameter with no leakage. Reports/PfbDeadKeyReport.json is deliberately NOT regenerated here; the regeneration gate's staleness assertion stays red until Task 6 refreshes the artifact and re-baselines Tests/coverage-baseline.psd1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review round 1. The endpoint key of the declaration index was an uncovered guard, not an equivalent mutant as previously reported: the deadness gate reaches the spec through PSObject property access on a ConvertFrom-Json object, which is case-insensitive, so a cmdlet whose -Endpoint literal differs in case from the spec path key is gated normally and reaches classification. An ordinal-exact index would then miss it and publish UNDECLARED -- a positive assertion of absence about a key the same generator can see. Add one It driving a mixed-case endpoint fixture (spec /api/9.9/widgets, cmdlet -Endpoint 'Widgets') against a lower-case control (/api/9.9/gadgets, 'gadgets') that is identical in every other respect. Measured: shipped code classifies both WRONG-SURFACE with [PATCH/Body]; with the index dictionary patched to StringComparer::Ordinal the mixed-case record becomes UNDECLARED with an empty provenance while the control is unchanged, so the discrimination is provably the case divergence and not the fixture. Not a live defect -- all 85 real dead-key endpoint literals match a normalized spec path exactly -- so this guards a future ordinal hardening. Also, comments only, no behaviour change: - soften the dedup comment from "cannot be produced" to "is not produced by any spec we pin", and say why the dedup is load-bearing anyway (the unstable introsort makes a duplicate pair reorder the committed artifact with no input change); - record that MaxDepth 32 is inherited from Get-PfbSpecCapabilities' own default rather than pinned by this report; - record the residual of the array-items hop: a Body site inside an array element reads identically to a top-level one; - note that the explicit -MaxDepth 32 in the real-spec control does not exercise the issue dmann000#71 truncation, since depth 8 also returns all 18 properties of PATCH /alerts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…uards Review round 2, comments and tests only -- no classifier behaviour changes. The dedup comment imported the introsort-stability argument from the top-level sorts, where it holds, to a place where it does not: a declaredElsewhere site carries only Method and Surface and the projection emits only those two, so a tie on both sort keys is a tie on the entire serialised record and an unstable sort cannot reorder byte-identical elements. Measured from a planted duplicate with the dedup removed: the two entries serialise to one distinct string. The real consequence is a WRONG ROW -- declaredElsewhere: [DELETE/Query, DELETE/Query] published in a committed artifact -- so the comment now says that, points at the assertion that catches it, and warns off the "add a tie-break property" remedy, which makes the order total and still publishes both rows. Should -Be is case-insensitive for strings, so the endpoint-literal assertion could not fail for the reason it gave: lowercasing the emitted record left the whole file green. It is now Should -BeExactly, and that mutant is KILLED. The anti-leak exclusion list was a hand-maintained literal, whose mechanical response to a red is to append the offending endpoint -- a one-token edit that disables the gate and looks like every legitimate edit around it. It is now derived from the fixture spec (normalized paths whose operations declare a requestBody), so an unjustified widening cannot be written and a fixture path that loses its body cannot leave a stale exclusion behind. Re-planting a document-keyed Body leak reds the assertion with the offender named, and the derivation also proved the old hand-list was wrong in the other direction: it omitted synthetic/undeclared. Finally, the header claimed this file contributes six 5.1 skips. That has been false since the first Task 5 commit; it is sixteen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Round-3 comment-only corrections. No executable line changes: all 42 changed lines are comments, both files parse clean, and the scoped run is unchanged at pwsh 7 15/1/0 (inherited staleness only) and WinPS 5.1 0/0/16, container ok. - tools/Build-PfbDeadKeyReport.ps1: fix the cross-reference to the assertion that catches a duplicated declaredElsewhere entry -- the quoted string is at Tests/Build-PfbDeadKeyReport.Tests.ps1:251 and the assertion block is :249-252, not :247-249, which held only comments and an assignment. - tools/Build-PfbDeadKeyReport.ps1: put the anti-tie-break caution at the head of Sort-PfbDeadKeyRecords, where the remedy it contradicts actually lives. A site object carries only Method and Surface, so there is no third property to break a tie with, and a total order would still publish both rows. - Tests/Build-PfbDeadKeyReport.Tests.ps1: retract the falsified introsort claim in the two further places it had been copied to -- the deduplication assertion and the ordered-provenance assertion. Neither ordering depends on sort stability: the provenance sites have distinct sort keys so the comparison never returns 0, and a tie on (method, surface) is a tie on the entire serialised record. Both assertions are correct; only their stated reasons were wrong. The ordered form is justified by the comparer contract it pins instead. - Tests/Build-PfbDeadKeyReport.Tests.ps1: record that the derived exclusion set is strictly larger than the literal it replaced, newly excluding synthetic/undeclared, so a Body leak there is caught only per-record. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add two synthetic-fixture declarations of `policy_names` -- a body property on SyntheticEndpointPatch and a query key on DELETE synthetic/allow -- neither of which any fixture record needs declared. They exist so that widening Get-PfbDeadKeyDeclarationSite's Body lookup from the record's own endpoint to a union over the index stops being an equivalent mutant: either widening now hands Remove-PfbSyntheticDeadKey's dead PolicyName a Body provenance on synthetic/dead, which the anti-leak control rejects. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment-only. An earlier draft of this paragraph, written before the
measurement existed, read as though the anti-leak assertion were broadly blind
to a scope error. It is not.
Measured: with the 'policy_names' body property now in the fixture, widening
the Body lookup to a document-wide union over the whole declaration index DOES
red the anti-leak assertion, on
'Remove-PfbSyntheticDeadKey|PolicyName on synthetic/dead' -- that endpoint
declares no request body, so it is not excluded and the leak surfaces there.
Before that fixture property existed the same widening produced zero offenders
and the assertion passed while the index was document-keyed.
So the residual is exactly one exempt endpoint ('synthetic/undeclared', newly
excluded because the derived set is strictly larger than the literal it
replaced), not a general weakness. The paragraph now says so.
Scoped run unchanged: pwsh 7 15/1/0 with the inherited staleness failure as the
only red, WinPS 5.1 0/0/16, container ok both. 12 changed lines, all comments,
zero executable; file parses clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes a blocking review finding against my own prior commit, plus two over-broad claims in comments I wrote. Comment-only: 31 changed lines, zero executable, both files parse clean, scoped run unchanged at pwsh 7 15/1/0 (inherited staleness only) and WinPS 5.1 0/0/16, container ok. BLOCKING. b4dbeba set out to fix a stale cross-reference and committed one that was stale on arrival: it cited Tests/Build-PfbDeadKeyReport.Tests.ps1:249-252, derived from the file BEFORE that same commit inserted seven lines of retraction prose above the target. At HEAD those four lines are pure comment; the $keys assignment is :256, the assertion :257-259, the quoted string :258. That is worse than the range it replaced, which at least included the assignment. Rather than re-point the range -- the second stale citation of this same assertion in two rounds -- the pointer is now the quoted string "has a duplicated declaredElsewhere entry", verified unique in that file. A range is correct only if re-derived after the edit that writes it, and a pointer that silently rots is worse than none when the comment bills itself as the thing to follow on a red. Two minors, both mine, both universals generalised from one measurement: - The residual-scope paragraph said the exemption "is the ONLY blind spot". True of what the derivation added; false of the assertion in general. A union narrowed to 'flagged' leaks Body provenance onto 'synthetic/surface', excluded for reasons predating the derivation and caught by the ordered provenance assertion instead. Scoped to "the only blind spot the derivation added", with the counterexample recorded. - The ordered-provenance justification claimed the assertion pins "method first, then surface, both ordinal". The ordering halves are measured -- dropping Surface and swapping the keys each red it -- but ordinality is not pinned: DELETE before PATCH and Body before Query sort identically under ordinal and culture-aware comparison, so a -Culture '' mutant survives. Claim narrowed and the real location of the ordinality argument named. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…inter Round-5 fix, both findings against my own commit 30138d0 and both the same class the commit before it was written to remove: a claim asserted without being executed. Comment-only -- 55 changed lines, zero executable, executable token skeleton unchanged at 2981, file parses clean, scoped run unchanged at pwsh 7 15/1/0 (inherited staleness only) and WinPS 5.1 0/0/16, containers ok. MAJOR 1. The residual-scope paragraph justified its narrowing with a second counterexample -- "a union narrowed to 'flagged' leaks Body provenance onto synthetic/surface" -- that is FALSE. 'Flagged' is a body property of SyntheticSurfaceBase only, reached only by synthetic/surface PATCH, so a union narrowed to it returns the record's OWN endpoint's sites: declaredElsewhere is unchanged, nothing leaks, every assertion stays green. It is an equivalent mutant, which is exactly the hazard the 'policy_names' comment defuses for the *_names case and the reason that property had to be planted. Replaced with the structural statement the measurement supports: the assertion is blind to a Body leak landing on any of the four endpoints the exclusion covers (synthetic/surface, synthetic/undeclared, widgets, gadgets -- verified against the fixture), and the derivation added exactly one of them. The other three were exempt under the hand-written literal too. The only measured positive control is the 'policy_names' one, and the paragraph now says so and says not to restore an unexecuted counterexample. MAJOR 2. The ordinality retraction pointed the reader at Tests/CommittedDeadKeyReport.Tests.ps1 for coverage that does not exist there for this data: that file does not mention declaredElsewhere at all. What it asserts (:507-508, :514-515) is ordinal order for the two TOP-LEVEL sorts only -- deadKeys on (cmdlet, parameter) and noSurvivingSelector on (cmdlet, method, endpoint). The comment now separates ARGUED from ASSERTED, names the top-level scope, and states plainly that no fixture pins ordinality for the declaredElsewhere (method, surface) sort -- nor can one while every real method and both surfaces are same-case ASCII. A gap closed by argument, not assertion. The other half was accurate and is kept: the ordinal-vs -Culture '' argument is at the head of tools/Build-PfbDeadKeyReport.ps1 (:58-59, above Sort-PfbDeadKeyRecords at :84) and governs the shared comparer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Last Minor from the round-5 verification, and it is the same class one more time: a direction word carried over from deleted text and re-asserted without being checked, inside the very paragraph whose subject is not restating unchecked things. The 'policy_names' comment is ABOVE, at :337-344 on the SyntheticEndpointPatch schema, not below. Names the schema so the pointer does not depend on relative position at all. Comment-only: 3 changed lines, zero executable, executable token skeleton unchanged at 2981, parses clean. Scoped run unchanged -- pwsh 7 15/1/0 (inherited staleness only), WinPS 5.1 0/0/16, containers ok. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 6 of issue dmann000#141. Regenerates the seven artifacts the resolver work moved, re-baselines three tripwires and adds two skip-reason keys, retires Task 4's tuple declaration, and pins the measured 5.1 skip count. REGENERATION. Through scripts/Assert-PfbDerivedArtifacts.ps1, never a bare generator. Seven of eleven checked artifacts were stale; PfbValueEnumMap.json and PfbValueEnumReconciliation.md held, as expected. All eleven now report up to date. PfbDeadKeyReport.json goes 31,597 -> 40,242 normalised characters, which clears the inherited staleness failure Tasks 4 and 5 were forbidden from fixing: Build-PfbDeadKeyReport.Tests.ps1 is now 16/0/0 on pwsh 7. CONTROL FOR THE STALENESS. main (76f0c95) regenerates 11/11 up to date, so all seven movements are attributable to this branch and none is pre-existing debt. SEMANTIC MOVEMENT, verified by regenerating rather than by checking that the arithmetic closes -- the plan previously carried a paired off-by-one that reconciled just as correctly: keysEvaluated 1779, parametersInventoried 2168, dead keys 85, noSurvivingSelector 7, skip reasons 32 / 28 / 6 / 309 / 14 / 0. Classification census UNDECLARED 71, WRONG-VERB 13, WRONG-SURFACE 1. Diff against main's committed report is +2 and -0. No previously-reported record was lost. THREE GATE MOVEMENTS, each justified beside its pin: - baselineDeadKeyCount 83 -> 85. Raising a monotone gate is against the file's own stated discipline, so the justification is written in: dmann000#141 changes no cmdlet, it teaches the resolver three assignment shapes it had been skipping, and two of the newly-evaluated parameters were dead all along -- Get-PfbAlert|Flagged|flagged|GET|alerts and New-PfbCertificateSigningRequest|Name|names|POST|certificates/certificate-signing-requests. Pre-existing module defects made visible, not introduced. - baselineNoSurvivingSelectorCount 6 -> 7, with the CSR identity added to the allowlist. CSR has one selector-shaped query key and the operation declares zero. - 'wire name unresolved' 127 -> 32. Lowering, and the failure mode here is leaving it high: 34 of the 66 null-WireName rows are now separately accounted for, so 127 would carry 95 rows of slack. TWO NEW VOCABULARY KEYS, 'outside standard request' = 28 and 'not wire parameter' = 6. Required rather than optional: the scan treats an unknown reason as an offender and separately asserts it visited every reason. Both are ceilinged rather than unceilinged because, unlike 'body property', each names a population the resolver positively classified rather than failed to read. PIPELINE SELECTOR -- the brief said this artifact must not move and it does. Diagnosed rather than copied: probe pairs are 1247 on both sides while candidates move 629 -> 647. The generator's code and probe population are unchanged; its candidate set depends on how many parameters resolve to a wire name, which is what this issue improved. Two consequences, both real: - Build-PfbPipelineSelectorMap headline pin 264 -> 266 findings, 101 -> 102 pairs, with probePairs left pinned at 1247 as the discriminator. - Rail A gains one waiver: Get-PfbUserGroupQuotaPolicy|Name, Family scope, 2 producers. This is a LIVE WIRE-CORRECTNESS DEFECT that dmann000#141 reveals -- the parameter stringifies a nested join item to names=@{context=; member=; policy=} on GET /user-group-quota-policies/ file-systems and /members. Same root cause as the existing Get-PfbTlsPolicy and Get-PfbWormPolicy entries. Waived against dmann000#141 following the register's own convention of naming the revealing issue; a fix issue is owed. STEP 6b. tools/inventory-tuple-baselines/issue-141-task4.json git mv'd to landed/ (R100, byte-identical). The brief's rationale for this was wrong in both directions and is corrected in the task report: the file is NOT currently stale (it validates CLEAN, exit 0, 34/34 declared), and a run with no -DeclarationPath is NOT clean (97 undeclared changes against origin/main, which is the gate working as designed on a branch that moves 97 tuples). The move is still right, on the authority of the file's own retirement note and the script's documented trigger: staleness fires once these commits ARE the baseline, i.e. at merge, and this is the PR that merges them. Retirement is safe because the script has zero auto-discovery -- -DeclarationPath is explicit-only -- so landed/ can never be read implicitly. COVERAGE BASELINE. Build-PfbDeadKeyReport.Tests.ps1 6 -> 16, measured on Windows PowerShell 5.1 for that file alone and read out of the runner's child winps51.json rather than its Write-Host summary: 0/0/16, container ok. No headroom added; these entries are exact. The tree-wide total is only measurable by a full-suite run, which is CI's. TESTS. Plan Step 7 list, both editions: pwsh 7 581/0/0, WinPS 5.1 418/0/163, containers ok on both. CONSTRAINTS. No change under Public/, Private/, PureStorageFlashBladePowerShell.psd1 or .psm1 -- checked by explicit filename, because a *.psd1 pathspec also matches Tests/coverage-baseline.psd1 and reads as a false breach. No version bump, no CHANGELOG edit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review fixes for 8472011. Three Major, two Minor, all of them bookkeeping that the previous commit either edited or invalidated and did not carry through. Comment- and documentation-only: executable token skeletons byte-identical for all three .ps1 files (1304/1015/609), every file parses clean, and no module file is touched. MAJOR 1. Tests/coverage-baseline.psd1 reconciliation note said "TWO entries have moved ... 297 + 4 + 11 = 312". 8472011 moved a third (Build-PfbDeadKeyReport 6 -> 16) and left the note alone. Recomputed from the map with the check the note itself prescribes rather than incremented by hand: 19 entries, sum 322. The paragraph immediately below exists because an earlier revision made this exact error; it has now been made twice by the same mechanism, and the note says so. MAJOR 2. The Step 6b git mv left four documented invocations pointing at a path that no longer exists -- Compare-PfbInventoryTuple.ps1 .DESCRIPTION and .EXAMPLE, and two runnable blocks in tools/README.md. The .EXAMPLE is the documented way to run the gate, so anyone copying it got a file-not-found and would reasonably conclude the gate was broken. The three runnable sites now use <your-declaration>.json; the prose site points at landed/ and says the file is a shape to read, not a file to pass. MAJOR 3. The rationale given for retiring the declaration file pre-merge is withdrawn. It claimed the authority of the file's own retirement note, and that note -- with Compare-PfbInventoryTuple.ps1:56 -- says retire it WHEN THE CHANGE MERGES, which is the opposite. The move is kept on narrower and honest grounds: the plan mandates it in this task, this is the PR that merges those commits, and the script has zero auto-discovery so landed/ can never be read implicitly. What is given up is stated rather than glossed -- the file was a live, passing gate at the moment of retirement (CLEAN, 34/34 declared, exit 0) and a reviewer who wants it must now run it explicitly against landed/. That sentence belongs in the PR body. MINOR 4. CommittedDeadKeyReport.Tests.ps1 attributed the whole keysEvaluated delta to dmann000#141. Measured against main's committed artifact: churn took 2174/1757 -> 2168/1747, and dmann000#141 then raised keysEvaluated 1747 -> 1779. The old text credited dmann000#141 with +22 where it earned +32 against a base that had fallen. MINOR 5. PfbPipelineSelectorRail.Tests.ps1 still carried 264/101 in three places while the identical prose in the waiver fixture had been updated to 266/102, so the two disagreed. Now 266 / 102 / "101 of the 102". NOT FIXED, deliberately: CommittedDeadKeyReport.Tests.ps1's "this collection has only 18 entries" is wrong (noSurvivingSelector has 7) but is pre-existing -- the identical line is at origin/main:289 -- so it is flagged for the whole-branch review rather than pulled into this task's scope. Scoped run over the six affected files, both editions: pwsh 7 83/0/0, WinPS 5.1 23/0/60, containers ok. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Whole-branch final review of 21d7068: APPROVED, no Blocking, no defect in code, artifacts, gates or the exemption. Every load-bearing numeric claim held on independent measurement. What it did find was prose arguing from figures a later task changed -- the failure mode its cross-task item exists to catch. Comment- and doc-only; executable skeletons byte-identical for both .ps1 files. MAJOR. tools/Compare-PfbInventoryTuple.ps1 .DESCRIPTION credited Task 3 with raising the Typed count by 61. Task 3's own delta is +29 (2073 -> 2102); 61 is the cumulative branch figure, which the tuple gate confirms independently -- 62 rows entered Typed, 1 left, 2102 - 62 + 1 = 2041 on main. The narrative was right and only the number was misattributed, but this docstring is the authoritative explanation of why the gate exists. The plan's Completion Condition 8 carried the same class of error and is fixed in the plan file (not in this repo): three of its nine values were the superseded 65 / 31 / 1,780 triple that Task 6 Step 4 had already retracted 175 lines earlier, so the branch failed its own definition of done while being correct. Acceptance criteria are what a reader checks the artifact against, so that one could have driven someone to "fix" the artifact toward the wrong numbers. MINOR. Tests/CommittedDeadKeyReport.Tests.ps1: "this collection has only 18 entries" describes noSurvivingSelector, which holds 7. Pre-existing -- byte-identical at origin/main:289 -- and I deferred it once as out of scope. Fixing it now, because the reviewer's argument is better than mine was: this commit raises baselineNoSurvivingSelectorCount 6 -> 7 four lines above it in the same BeforeAll, and Task 6 already swept this exact file for prose arguing from contradicted figures. Not unsafe either way; the assertion beneath it is an equality against input size, not against 18. MINOR. The Compare-PfbInventoryTuple docstring and the plan gave opposite instructions for the retired declaration file -- "no longer a file to pass to -DeclarationPath" against a plan sentence telling a reviewer to pass exactly that path. Softened to "not a file any ROUTINE run should pass; pass it explicitly only to reproduce Task 4's evidence", and it now records the sharper reason retiring it pre-merge was right, which the review supplied: its baselineRef is an intermediate feature-branch commit, and this repo squash-merges, so after merge the ref may be unreachable and a future run would fail on ref RESOLUTION rather than the 34 STALE-DECL Step 6b predicted. Verification, re-run after these edits -- scoped 10-file list, both editions: pwsh 7 581/0/0, WinPS 5.1 418/0/163, containers ok, TotalCount 581 on both legs, so the 5.1 leg discovered the identical set rather than being a smaller run wearing a green label. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A cmdlet may write the same wire key from a later arm of one if/elseif chain as a convenience default derived from a different parameter. Update-PfbBucketAuditFilter does this: -Name sets names, and an elseif sets names from -BucketName so a -BucketName-only caller need not restate the value. The resolver counted that arm as a landing of -BucketName, so -BucketName appeared to land both bucket_names and names, the arbitration abstained, and PATCH /buckets/audit-filters lost parser traceability and dropped to partial confidence -- tripping the issue dmann000#31 guard in CI. Test-PfbIsDefaultingAliasAssignment flags an index assignment whose key and target variable are already written by an earlier sibling clause of the same if/elseif chain from an expression that does not mention this parameter. Flagged landings are dropped only when the parameter still has an unflagged landing of its own. That proviso is load-bearing: New-PfbFleetMember writes members from a FleetKey arm and again from a -Members elseif, but neither defaults the other and -Members has no other landing, so an unconditional drop deletes its only evidence and relocates the same regression onto POST /fleets/members. Measured against the previous commit: keysEvaluated 1779 -> 1780, resolved ok 1694 -> 1695, wire-name-unresolved 32 -> 31, dead keys unchanged at 85, field map 2067 -> 2068 entries (the single added entry being Update-PfbBucketAuditFilter|BucketName|bucket_names). Selector findings are unchanged at 266 rows over 102 pairs with probePairs still 1247 and control leakage still 0; SelectorUnresolved falls 14 -> 6. A whole-module sweep now reports zero wire-landing abstentions, where the same sweep on the previous commit reports exactly one. Also re-points the 102 selector waivers off closed dmann000#90: 64 to dmann000#152 (the join-item class), 37 to dmann000#153 (items with no name for unrelated reasons), and 1 to dmann000#123, which already tracks that pair against a different, upstream blocker. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Closes #141.
What this changes
The wire-name resolver decides, for each cmdlet parameter, which key that parameter becomes on
the wire. Three assignment shapes in the module were written in forms the resolver did not
recognise, so it did not resolve them wrongly -- it declined to resolve them at all and recorded
them as skipped. A skipped parameter is invisible to every downstream safety gate: the dead-key
detector, the drift report and the field map all reason only over parameters that were measured.
This branch teaches the resolver those three shapes, traces which payload role each resolved
value lands in, and then updates the committed artifacts and the tripwires so they describe the
larger measured population instead of the smaller one.
No cmdlet behaviour changes. This is entirely tooling, tests, and regenerated reports.
Measured effect
Both columns are read from
Reports/PfbDeadKeyReport.jsonat the respective commit.main(76f0c95)wire name unresolvedoutside standard requestnot wire parameterbody propertyendpoint/method ambiguousThe parameter population is unchanged at 2168, which is the control: nothing was added to the
inventory. What moved is how many of those parameters the resolver could actually measure.
wire name unresolvedfalling 127 -> 31 is the headline. That bucket was doing two jobs: holdingparameters the resolver genuinely could not read, and holding parameters that were never wire
parameters in the first place. Splitting out
outside standard request(28) andnot wire parameter(6) means the residual 31 is now a real backlog rather than a mixed bag, and it issmall enough to enumerate.
Dead-key classification
Dead-key records now carry a
classificationand adeclaredElsewhereprovenance list, whichdid not exist on
main(every record there classified as empty):UNDECLARED-- 71WRONG-VERB-- 13WRONG-SURFACE-- 1WRONG-VERB= 13 is not anticipated anywhere in the plan and is not a regression. Those keys aredeclared in the spec on the same endpoint under a different HTTP method; previously they were
indistinguishable from keys the spec does not know at all. Separating them is the point of the
classification -- a key that exists under the wrong verb is a different kind of problem from one
that does not exist.
The dead-key count rising 83 -> 85 is the direct consequence of evaluating 33 more keys. Both new
identities are named in the justification beside the re-baselined gate in
Tests/CommittedDeadKeyReport.Tests.ps1.Abstention, and the defaulting-alias rule that removed its last real-tree case
The resolver now withdraws a row when two role-agreeing landings disagree on the name, rather
than publishing a guess. An abstention is not a failure to resolve, and it does not fall through
to a weaker idiom.
No row in the module is withdrawn. A whole-module sweep of all 2861 parameters reports zero
abstentions; the same sweep against the previous commit reports exactly one. The machinery is
retained as a guard and is unit-tested against synthetic ambiguity, not left unexercised.
That one case was a genuine defect in the rule, caught by CI rather than locally.
Update-PfbBucketAuditFiltersetsnamesfrom-Name, and anelseifsetsnamesfrom-BucketNameso a-BucketName-only caller need not restate the value. The resolver countedthat arm as a landing of
-BucketName, so-BucketNameappeared to land bothbucket_namesand
names, the arbitration abstained,PATCH /buckets/audit-filterslost parser traceabilityand dropped to
partialconfidence, andTests/Issue31.DriftConfidence.Tests.ps1failed. Itfailed only on Linux/macOS
pwsh: that Describe is-Skipped below PowerShell 7, so WinPS 5.1was green while the same assertion was red beside it.
Test-PfbIsDefaultingAliasAssignmentflags an index assignment whose key and targetvariable are already written by an earlier sibling clause of the same
if/elseifchainfrom an expression that does not mention this parameter. Two independent
ifs are not a chainand are left abstaining, because that is real ambiguity.
The proviso on top of it is load-bearing, and the first version of this rule did not have it:
a flagged landing is dropped only when the parameter still has an unflagged landing of its
own.
New-PfbFleetMemberwritesmembersfrom aFleetKeyarm and again from a-Memberselseif; neither arm defaults the other, and-Membershas no other landing. An unconditionaldrop deleted its only evidence and relocated the identical regression onto
POST /fleets/members.Three synthetic controls all missed that, because each happened to give the parameter a second
landing; only regeneration against the real tree caught it. It is now pinned by a test.
Pipeline selector map moved, and why that is expected
Reports/PfbPipelineSelectorMap.jsonmoved: findings 264 -> 266, finding pairs 101 -> 102.The discriminator is that
probePairsis unchanged at 1247 on both sides whilecandidatePairsmoves 629 -> 655. The generator's code and its probe population are identical;what changed is how many parameters now resolve well enough to be probed at all. The gate
breakdown accounts for every one of those pairs on both sides, with 28 rows leaving
SelectorUnresolved(34 -> 6) intoCandidate(+26) andMatched(+2), and 655 + 586 + 6 =1247 on this side against 629 + 584 + 34 = 1247 on
main.controlLeakageremains 0.probePairsis deliberately left un-rebaselined inTests/Build-PfbPipelineSelectorMap.Tests.ps1so it keeps working as that discriminator: if a future change moves the probe population itself,
that assertion should fail.
New selector waiver -- a live wire defect this branch reveals
Get-PfbUserGroupQuotaPolicy | Nameis waived in Rail A against this issue. It is a real defectin the module that this branch reveals without introducing: the primary producer binds
-Namecorrectly, but two family endpoints (
GET /user-group-quota-policies/file-systemsand/members) return join items whose members are objects (context,member,policy) carryingno
name, so a name-shaped selector cannot bind by property name and the whole object isstringified onto the wire --
BoundValue@{context=; member=; policy=}, carried under thenameskey (Evidencerecords thenames=prefix;BoundValueitself is the bare object).Get-PfbTlsPolicyandGet-PfbWormPolicyshare the same root cause, though not an identicalBoundValue--Get-PfbTlsPolicystringifies@{member=; policy=}, with nocontextmember.The class is wider than these three: measured against the regenerated map, 150 rows across 65
cmdlet/parameter pairs coerce a join item this way, in three shape variants
(
@{context=; member=; policy=}x124,@{member=; policy=}x21,@{context=; link=; member=; policy=}x5), and the broader object-stringification class is all266 findings across 102 pairs. Every one of them is
Coercedrather than silently bound, and noprimary producer is affected.
Two issues now track this and the waivers have been re-pointed off closed #90 in this branch:
#152 takes the 65-pair join-item class, #153 takes a further 37 pairs whose items lack a
namefor unrelated reasons (alert/hardware records keyed oncomponent_name,@{group=; member=}membership items, realm/object-store associations). They are tracked apart on purpose:#152 is one coherent cause a single fix plausibly resolves, while #153 has no shared structure to
key a fix on. 65 + 37 = 102, every
Coercedpair, with no silent remainder.In the waiver file the split lands as 64 / 37 / 1:
Get-PfbUserGroupQuotaPolicyRule | PolicyNamebelongs to the #152 class by root cause but points at #123, which already tracks that pair
against a different, upstream blocker (the array honours a
policy_nameskey the publishedOpenAPI omits). Both numbers are correct; they answer different questions, and the file's header
says so.
Fixing the cmdlets is deliberately not done here -- see the exemption note below.
Live-test exemption
Verified mechanically with
wire-exemption\Test-PfbWireExemption.ps1 -RepoPath <worktree>, baseorigin/main @ 76f0c952, headc566c78: VERDICT: EXEMPT (exit 0), "No in-scope filechanged."
Every changed file is under
Reports/(7),Tests/(9) ortools/(7) -- 23 in total.Public/,Private/,PureStorageFlashBladePowerShell.psd1and.psm1are untouched -- not merely free of executablechanges, but absent from the diff entirely. This is why the three selector-coercion cmdlet fixes
are a follow-up rather than part of this PR: one executable line in any of those four locations
disqualifies the whole PR from exemption, and there is no partial exemption.
Verification
container status read per edition rather than inferred from a summary line: 484 passed / 0
failed under pwsh 7, 418 passed / 0 failed / 66 skipped under WinPS 5.1.
non-vacuity control: 2 of the 7 go red there. The other 5 are regression pins that must hold on
both sides, including the
New-PfbFleetMembershape, which pins against the over-broad firstversion of the rule rather than against
main../scripts/Assert-PfbDerivedArtifacts.ps1-- all eleven checks up to date. As a control, thesame script was run on
main(76f0c95) and reported 11/11 clean there too, which establishesthat the artifacts regenerated here were stale because of this branch and not pre-existing
debt.
PATCH /buckets/audit-filtersregression described under Abstention above, and it is fixed inc566c78. TheTests/coverage-baseline.psd1skip ceilings remain measurable only by a fullsuite (
Build-PfbDeadKeyReport.Tests.ps1is pinned at 16, with entries summing to 322), sothey are settled by CI rather than locally.
Deliberately not included
Tests/CommittedDeadKeyReport.Tests.ps1says a collection "has only 18 entries" wherenoSurvivingSelectorhas 7. The identical line is atorigin/main:289, so it predates thiswork and is out of scope here.