Integration: Fusion Phase 0 prerequisites + capability-map schema-walk fixes (#96, #97) - #98
Merged
juemerson-at-purestorage merged 23 commits intoAug 13, 2026
Conversation
Splits the four prerequisites out of the design doc's Phase 1 into their own phase, each independently justifiable and shipping no user-visible context surface: 1. Move context_names component resolution into Private/ (#74) 2. contextScope per-endpoint map field, schemaVersion 1 -> 2 3. Connect-time capability-map staleness warning 4. Get-PfbFleetMember top-level MemberName/FleetName Derived from docs/design/fusion-context-injection.md rev 4. Extension counts (5 override / 28 incomplete-gre / 266 block) and the four-entry curated contextScope table verified against tools/specs/fb2.28.json. Refs #25, #74 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The spec treated contextScope + schemaVersion 2 as standalone. It is not: issue #83 also needs schemaVersion 2, and tracking issue #84 plans both as a single bump in a single PR (PR 2, behind PR 1 = #71 + #82). Adds the coordination section with three options and a recommendation, and flags the decision at the top of the document. Also corrects two claims about test enforcement: - schemaVersion is per-artifact, not global. Five generators each write their own independent value, all currently 1, and nothing in Private/ or Public/ reads any of them -- it is a label, not a migration gate. The earlier justification (Get-PfbCapabilityMap as the single gate) overstated it. - There is no byte-identical regeneration gate on the committed map. The drift test byte-identical assertions check determinism across two runs on synthetic fixtures; the real-artifact checks skip on gitignored tools/specs/. A green result is not proof they ran. Notes that #85 (phantom-diff blocker) is closed, and that origin/automated/update-api-capability-map holds regenerations main never received because the auto-PR step has failed since 2026-07-24. Refs #25, #83, #84 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decision: Phase 0 ships contextScope and takes schemaVersion 2. #84s generator work (#71, #82, #83) moves to its own PR, with #83 incrementing from whatever it finds rather than being pre-assigned 3. The test was whether Phase 0 could absorb all of #84, which would have preserved the one-bump intent. It cannot, structurally: #71, #82 and #83 all land in Add-PfbSchemaPropertyNodes (tools/lib/PfbSpecTools.ps1:191), a recursive walker whose MaxDepth threads through five call sites and which feeds both Data/PfbCapabilityMap.json and Data/PfbResponseShapeMap.json. It also has a non-Fusion downstream consumer -- #44 is blocked on PR 1, whose four endpoints are the ones #82 makes visible. contextScope reads operation-level x-pure-* extensions and never touches the body-schema walk, so the two changes are independent in code. The cost is two sequential regenerations of the map plus the lost bookkeeping tidiness, both acceptable because nothing reads schemaVersion. Follow-up owed: update #84 so its PR 2 no longer claims contextScope. Refs #25, #83, #84 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…omponent resolution The three-step resolution contract had no runtime home: it was inlined in tools/lib/PfbContextRuleTools.ps1, which the module never loads. Extracting it pure and parameterised lets both the module and tools/ execute one copy. Key-present-but-null vs key-absent are distinguished, with an explicit regression test -- both return $null, so the distinction is invisible in the return value and trivially reimplemented wrong. Refs #74 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ustomObject Review of 74ada89 found the key-presence test only worked for the shape ConvertFrom-Json produces. For a Hashtable or OrderedDictionary, .PSObject.Properties.Name returns the adapted CLR surface (Keys, Values, Count, IsReadOnly, ...) rather than the keys, so the test was ALWAYS FALSE and every lookup fell through to the defaults -- reproducing the exact wrong-component defect this function exists to prevent. It was also a false positive for an intrinsic name: -ParameterName 'Count' returned the item count, an [int]. That shape is not hypothetical. tools/Build-PfbCapabilityMap.ps1 builds parameterComponentDefaults and parameterComponentOverrides as [ordered]@{}, so any tools/ caller resolving against the map it just built, rather than the JSON round-trip, was affected. Also closes four mutation gaps the original fixtures could not see. All four wrong implementations below passed the original 7 tests; each is now killed: - regex -match instead of -contains: 'ids' matches the key 'ids_or_names' - reversed -contains operands: works only because every original fixture had a single-key overrides object. The real map has 40 multi-key overrides objects, 18 containing context_names -- the original bug class on real endpoints - guarding on the overrides PROPERTY existing rather than being non-null - PSObject-only resolution (the defect above) -like was also examined and is NOT a defect: with no wildcard characters it is exact match, so it is equivalent to -contains for real parameter names. Behaviour on the shipped map is unchanged: the Get-PfbContextParameterFact fact-set hash over Data/PfbCapabilityMap.json is byte-identical before and after, per edition (376 facts, 376 with a component, 135 rule-multi-value). Refs #74 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nlining
Get-PfbContextParameterFact now calls Resolve-PfbParameterComponent rather than
reimplementing the three-step contract, and the context/allow_errors parameter
names move to Private/ so the module can reach them in Phase 1.
tools/ now depends on Private/ for the resolver, the constants and the
cardinality rule. That direction is the fix -- relocating without inverting the
dependency would not resolve the issue.
Behaviour is unchanged, verified three ways:
- Tests/PfbContextRuleTools.Tests.ps1 is 54/0/0 on both editions, matching the
pre-change baseline exactly
- the full fact-set hash over Data/PfbCapabilityMap.json is byte-identical
before and after the refactor, per edition
- tools/ loads standalone in a fresh process with NO module imported, under
both pwsh 7 and Windows PowerShell 5.1, yielding 376 facts / 376 with a
component. A session with the module already imported would have masked a
broken path computation, so this is checked without one.
Closes #74
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Get-PfbSpecContextScope reads x-pure-remote-execution-context-domains-override, x-pure-incomplete-gre and x-pure-block-remote-execution per operation. It is a sibling of Get-PfbSpecResponseShapes rather than an extension of Get-PfbSpecCapabilities: the latter's record feeds drift gap accounting, and issue #84's PR 1 edits its body walk, so extending it would collide for no benefit. It never reaches Add-PfbSchemaPropertyNodes, so neither generated artifact can move. DomainsOverride is an empty array when the extension is absent, never $null, so callers can test .Count without a null guard. Includes a canary on the 5/28/266 extension counts at fb2.28, so the curated table gets revisited when upstream's annotation pass advances. Verified against the real spec: the 5 overrides are all /presets/workload (GET ARRAY+FLEET, every write verb FLEET only), and all four endpoints the generator curates in the next commit are in fact flagged incomplete. Refs #25 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…sion 2
Every endpoint now carries { scope, provenance }. Declared domains overrides are
trusted (5 preset operations); four live-tested endpoints are curated; the
remaining 19 flagged x-pure-incomplete-gre record 'unknown' rather than being
defaulted, since a flagged endpoint's absent override proves nothing; everything
else defaults to 'array', the fail-safe direction.
Measured distribution over 632 endpoints, matching the spec's acceptance
criteria exactly:
provenance declared 5, live-tested 4, unknown 19, default 604
scope array 606, fleet 7, unknown 19
The regeneration was verified structurally rather than by reading the diff,
since there is no CI gate on this artifact (cross-platform-tests.yml never
fetches tools/specs/, so every real-artifact assertion skips). Comparing the
committed map against the regenerated one, key by key: schemaVersion 1 -> 2,
one contextScope object added per endpoint, and ZERO endpoints differing in any
other key. Endpoint key set and order identical; parameterComponentDefaults
identical.
contextScope does not reach drift accounting: the drift report generated from
the old map and from the new one is byte-identical (SHA-256), and the string
'contextScope' appears nowhere in either output. Reports/ therefore needs no
regeneration here and is deliberately left untouched -- its one stale entry
predates this branch and belongs to issue #84's PR 1.
DomainsOverride is now coerced to [string[]] so the declared .OUTPUTS type is
literally true; a bare @() yielded Object[].
Nothing reads contextScope at runtime in this phase -- Phase 1's kind-vs-scope
validation and scope-aware error messages consume it.
schemaVersion is per-artifact and read by nothing in Private/ or Public/, so the
bump is a maintainer label. Issue #83 increments from whatever it finds.
Refs #25, #84
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fails when a curated entry gains an upstream domains override or stops being flagged incomplete, so the table shrinks on its own instead of silently shadowing better data. Also pins the 5/4/19 provenance distribution and the topology-group write verbs' array default. Verified the guard bites: adding a bogus fifth curated entry pointing at GET /presets/workload -- which does carry an override -- fails exactly the "gained an upstream override" and "exactly the expected curated endpoints" assertions, then passes again once reverted. The topology-group family is enumerated by regex rather than by a hardcoded verb list, since issue #38 is still settling which verbs exist. The curated table is mirrored here deliberately rather than imported: a test that reads the table it is checking cannot detect the table going stale. Refs #25 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lity map Assert-PfbApiCapability stays permissive beyond the map's scanned range, because a cmdlet caller never chose the negotiated version and should not be blocked by a packaging lag they cannot see. This warning is the other half of that trade, not decoration. Fires once per connection, caches ExceedsCapabilityMapCoverage on the connection object, and makes no Gallery lookup -- this module runs against air-gapped arrays. Major/minor compared as integers, since a string compare places 2.9 above 2.28. The comparison is DELEGATED, not reimplemented: Test-PfbVersionAtLeast is the same comparator Assert-PfbApiCapability uses, so the warning and the capability check cannot disagree about what is in range, and ConvertTo-PfbVersionObject finds the highest scanned version. A third copy of major/minor parsing in the phase whose purpose is removing a duplicated rule would reintroduce #74's failure mode. Mutation-verified: swapping the delegation for a string compare fails the '2.9 does not read as newer than 2.28' test and nothing else. Two failure modes are deliberately silent rather than loud, because the map is diagnostic and the connection is the user's actual goal: an unparseable generatedFrom entry (both version helpers cast with [int] and do not guard), and a capability map that THROWS. The latter is a real regression risk this guards against -- Get-PfbCapabilityMap returns $null for a missing file but does not guard ConvertFrom-Json, so a corrupt shipped map would otherwise have become a connect-time failure, leaving no way to connect and diagnose it. ExceedsCapabilityMapCoverage is kept out of $defaultProps: it is diagnostic state, and adding it would change the default Format-List view for every connection. Refs #25 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…Member
ValueFromPipelineByPropertyName matches only top-level names, and fleets/members
nests the array name at .member.name -- so piping into a context-scoping cmdlet
would bind nothing and silently no-op. Additive: the raw nested objects survive.
Decoration runs per emitted item rather than over the first page, so
-AutoPaginate's later pages are decorated too.
No IsLocal: is_local is relative to the call's context rather than the
connection, so the obvious Where-Object { -not $_.IsLocal } idiom would silently
select a different array once a context is active. Determining the local array
belongs to the connection, not a per-call response field.
Adds the cmdlet's first test file.
Refs #25
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review found that two of the four scope-ladder decisions survived mutation with
every test green. Since there is no CI gate on Data/PfbCapabilityMap.json, the
fixture is the only rail, and it modelled a spec that does not exist.
1. contextScope had NO last-seen-wins coverage. The fixture used one spec
version, and the comment claimed that was "enough" because the field is not
version-gated -- backwards, since one version is exactly what cannot
demonstrate non-version-gating. Wrapping the assignment in the same
first-sight guard its neighbours use (minVersion, parameters,
bodyProperties, three lines away) passed 6/6 while erasing all 5 declared and
all 19 unknown records from the real artifact, because the annotations exist
only in fb2.28 while every endpoint is first sighted in 2.0-2.27. The fixture
is now two versions: 2.27 declares the endpoints unannotated, 2.28 annotates
them, and the newer value must win. minVersion is asserted as still 2.27, so
the two policies are shown to coexist rather than one having replaced the
other. That mutation now fails 4 tests.
2. The fixture inverted reality on the only endpoints where ladder steps 1 and 3
compete. All 5 override-bearing operations in the real fb2.28 are ALSO flagged
x-pure-incomplete-gre; the fixture's carried the override without the flag, so
making the flag beat the declaration passed 6/6 (declared 5 -> 0,
unknown 19 -> 24). The flag is now on both fixture operations, with a
dedicated test for the precedence. That mutation now fails 3 tests.
3. An override declaring an unrecognised domain became 'fleet' with
provenance 'declared' -- asserting a scope on no evidence while telling
Phase 1 upstream had declared it. It now falls to unknown/unknown, the same
honest failure mode the flagged-but-uncurated case uses. Unreachable today:
only ARRAY and FLEET occur across all 29 cached specs, and the regenerated
artifact is byte-identical (SHA-256 2E59D07D...), so this is inert and covered
by its own synthetic fixture.
4. Get-PfbFleetMember decorated Invoke-PfbApiRequest's { total_item_count = N }
sentinel -- returned when the API reports a count but no items, which is what
a filter matching nothing produces. That emitted an object looking like a
fleet member whose name is $null, so piping it onward would bind $null: the
exact silent-wrong-binding failure the decoration exists to prevent. The
sentinel now passes through undecorated so its absence fails loudly.
Also: the drift guard never read the generator's curated table, only the
committed map, so editing $curatedContextScope without regenerating was
invisible. It now scrapes the generator's literal too. And the multi-page test
was renamed to what it actually proves -- it mocks Invoke-PfbApiRequest, so it
exercises one multi-item return, not pagination; what makes paging safe is that
-AutoPaginate accumulates every page into one array, a property of that function
and tested there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ion gaps
Review swept 20 hostile generatedFrom shapes and could NOT break connecting: the
warning branch's parse was a byte-for-byte replay of the helper's own expression,
so a $true return was itself proof the expression had already succeeded. Safe --
but only by an invariant living in a different file, with nothing at the call site
saying so. Proven fragile: changing the helper's catch to return $true made the
call site throw outside any try and return no connection at all.
So the helper now hands back the maximum it already computed, via a [ref]
out-param (the idiom Assert-PfbConnection already uses here). The warning branch
performs no parse, no index and no cast -- it is one Write-Warning -- so it cannot
break the connect path structurally rather than by cross-file contract. The same
mutation now degrades to a spurious warning instead of a failed connection, and
is still caught by a test.
Three surviving mutations closed, all test-only:
- Swapping the two versions in the warning text passed all 11 tests. The
assertions checked only that '2.29' and '2.28' each appeared SOMEWHERE, so the
backwards and actively misleading "running REST 2.28 ... covers through REST
2.29" was green. Each version is now pinned to its role.
- Adding ExceedsCapabilityMapCoverage to $defaultProps passed all 11 tests,
despite the exclusion being presented as a deliberate decision. Now asserted
against DefaultDisplayPropertySet, together with the property still being
reachable programmatically.
- The no-Gallery-lookup check was a raw text grep, which fails on a harmless
COMMENT mentioning Find-Module or a doc link to the Gallery -- this test's own
comment would have tripped it. It now walks the AST's invoked command names,
so comments and strings cannot produce a false positive, and it covers all
five files in the warning's call graph rather than two. The earlier claim that
grepping "proves absence" was overstated on both counts.
Also corrected an inaccurate docstring: the .Count guard only short-circuits a
literal empty array. An absent or $null generatedFrom becomes @($null), whose
Count is 1, and is caught by the try instead. Both routes return $false, so the
guards are defence in depth and neither is individually load-bearing.
Refs #25
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… values The synthetic Describe carried -Skip:($PSVersionTable.PSVersion.Major -lt 7), withholding 9 assertions from Windows PowerShell 5.1 for no reason of its own: Get-PfbSpecContextScope is a pure PSObject walk with no pwsh-7 dependency. The only 5.1 blocker was the fixtures' own ConvertFrom-Json -Depth 32, and these fixtures are four levels deep -- 5.1's ConvertFrom-Json parses them unaided. Dropping -Depth lets the block run on both editions, matching the convention in PfbSpecTools.Tests.ps1, which skips only the Describe that genuinely needs it. The second Describe keeps its guard and now says why: the real fb2.28 spec does need -Depth 64, which does not exist before PowerShell 6.2. Also closes a surviving mutation. IsIncompleteGre/BlocksRemoteExec test both key presence AND the key's value, but every fixture here -- and all 304 occurrences across the 29 cached specs -- emits these extensions only as literal true, so a presence-only implementation passed every assertion. Verified: reducing both to [bool]($opKeys -contains $key) now fails on both editions. An endpoint upstream deliberately un-flagged would otherwise have been forced to unknown/unknown with nothing to catch it. pwsh 7 14/0/0, WinPS 5.1 10/0/4, Container ok on both.
…EADME tools/README.md is the tracked contract doc for the capability map's shape. It enumerates what each endpoint record carries, and contextScope -- now emitted on all 632 endpoints -- appeared nowhere in it, nor did the schemaVersion 1 -> 2 bump. Note contextScope is unconditional, unlike the "where non-empty" keys above it, and record that it is last-seen-wins for the same non-monotonicity reason as readOnlyBodyProperties. Also point the parameter-component resolution prose at Private/Resolve-PfbParameterComponent.ps1. That file now declares itself the single runtime home of the contract and Build-PfbCapabilityMap.ps1 points here, which left this README as the one copy of the contract not pointing at its implementation -- the prose form of exactly the drift #74 exists to close.
…y-map-2026-08 feat(fusion): Phase 0 prerequisites - contextScope in the capability map, centralised component resolution, staleness warning
…tems Two defects in Get-PfbSpecCapabilities' request-body walk, both changing Data/PfbCapabilityMap.json and both runtime-visible through Private/Assert-PfbApiCapability.ps1, which reads bodyProperties. #71 -- the two body-schema helper calls passed no -MaxDepth, so both took the helpers' own default of 8. The fb2.12-2.16 schemas compose through allOf chains deeper than that, so five PATCH /password-policies fields recorded introducedVersion 2.17 instead of 2.16 and would have been refused against a 2.16 array. Get-PfbSpecCapabilities now takes -MaxDepth, defaulting to 32 -- the value Get-PfbSpecResponseShapes already uses, chosen there by measuring this same truncation (184 false removals at 8 versus 7 true ones at 32). #82 -- a request body that is itself `type: array` carries its element schema on the `items` sibling keyword, not as a property, so the walk terminated immediately and four batch endpoints recorded an empty bodyProperties, hiding 23 fields from both the runtime gate and the drift report. The items hop is done at the CALL SITE, not by teaching Add-PfbSchemaPropertyNodes to descend `items`. That walker is shared with Get-PfbSpecResponseShapes, whose contract is that an envelope's properties and its items element's properties stay two separate levels; collapsing them would silently change Data/PfbResponseShapeMap.json and make its cross-version removal detection compare incomparable sets. This mirrors the hop Get-PfbSpecResponseShapes already performs at its own call site. Verified: Data/PfbResponseShapeMap.json regenerates byte-identical. Both regressions are covered by fixtures confirmed to fail before the fix. Artifact regeneration is deliberately NOT in this commit -- it is sequenced behind Fusion Phase 0 per issue #84. Refs #71, #82. Unblocks #44. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rule has never had any effect. It was added in 69fe478 -- the same commit that first committed tools/ -- as a hedge while it was undecided whether the toolchain should be tracked ("Not yet decided whether this should be tracked"). Ignore rules do not apply to already-tracked files, so all 14 files under tools/ have been tracked from birth and the rule has been inert ever since. Removing it is a no-op for the working tree and a small improvement going forward: a NEW file added under tools/ now shows up in git status instead of being silently invisible, which is how tooling work here has previously gone missing. tools/specs/ keeps its own separate rule (.gitignore:36) and stays ignored -- verified that all 29 cached spec files remain ignored after this change, and that git surfaces no newly-untracked files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The comment explaining why array bodies go unchecked is invalidated by the issue #82 map change in this branch, in both of its halves. It stated that every array-bodied endpoint carries "bodyProperties": {}, so a per-element check "could never fire". That is no longer true -- the four batch endpoints now carry real per-element fields. It then predicted that "this loop picks it up for free if a future map representation ever lands." That was wrong even when written: the loop is guarded on $Body being an IDictionary, and an array body arrives as [hashtable[]] (Set-PfbWorkloadTag passes -Tags straight through), so the loop is skipped before the map is consulted. A richer map alone changes nothing. Replaced with what is actually true now: the map records the fields, the type guard is the remaining blocker, and relaxing it is a real behaviour change that can refuse calls which succeed today -- deliberately not smuggled in with a generator fix. Also records that the blast radius is nil today (only Set-PfbWorkloadTag reaches such an endpoint, all its fields are 2.23) and that this stops holding once #44 adds cmdlets for the other three. Comment-only. No behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pre-existing correction, isolated deliberately. This commit reverts nothing and fixes nothing in the generator -- it regenerates Reports/ with tools/lib/PfbSpecTools.ps1 held at the Phase-0 base, so the entire diff here is staleness that already existed on main before this branch. Reports/PfbApiDriftReport.json on main was one gap behind the committed capability map, which is why the "nothing vanishes" invariant in Tests/Build-PfbApiDriftReport.Tests.ps1 has been red locally. Root cause is New-PfbFileSystemReplicaLink's [Nullable[bool]]$RemoteDefaultExports being sent through a conditional assignment the drift tracer cannot follow, so confidence degrades high -> partial, enrichment is disabled, and systemicGaps/conventionStrength move 252 -> 246. Not spec staleness: analysedVersions is identical at 2.28. The diff here (271/19/14/5 lines across four files) is numerically identical to what origin/automated/update-api-capability-map already holds -- that workflow has failed at its "Open pull request" step on every run since 2026-07-24, so main never received this regeneration. Splitting it out means the next commit's diff contains only what this PR's code change actually causes.
#82 Everything in this diff is caused by the walker fix in the previous commits. Verified by construction: reverting tools/lib/PfbSpecTools.ps1 to the base and regenerating reproduces Data/PfbCapabilityMap.json byte-identical to the committed Phase-0 artifact, so nothing here is drift or nondeterminism. Capability map -- exactly 7 endpoint records change, in four groups: #71 5 x introducedVersion 2.17 -> 2.16 on PATCH /password-policies (name, id, enabled, is_local, location) #82 23 bodyProperties recovered across the 4 array-bodied endpoints: POST /nodes/batch +12, POST /resource-accesses/batch +2, PUT /workloads/tags/batch +5, POST /fleets/members/batch +4 #82 readOnlyBodyProperties on POST /nodes/batch resolves from empty to 8: capacity, chassis_serial_number, data_addresses, details, id, raw_capacity, status, unique --- 2 order-only records, no value changes: PATCH /ssh-certificate-authority-policies (bodyProperties key order) PATCH /file-systems (top-level key order; contextScope and readOnlyBodyProperties swap position) Note the four batch endpoints did not have an EMPTY bodyProperties before -- each had exactly one entry keyed by the empty string, the nameless artifact of a walk that reached the array node and could not descend it. That is what the 23 real fields replace. The map is deterministic: three consecutive regenerations produced identical SHA-256. Data/PfbResponseShapeMap.json is byte-identical (423de668a78356bad13b6d345bf9e834eca3438e7badf96577e78a5bbf93fef6). This is the guard that the fix stayed at the Get-PfbSpecCapabilities call site and did not leak into the shared Add-PfbSchemaPropertyNodes walker, whose contract is that an envelope and its items element are two separate levels. Phase 0 is undisturbed: schemaVersion still 2, 632 endpoints, and the full contextScope scope-x-provenance cross-tab is unchanged at array/default 604, array/declared 1, array/live-tested 1, fleet/declared 4, fleet/live-tested 3, unknown/unknown 19. Drift report: PUT /workloads/tags/batch gains 5 missingBodyProperties (copyable, key, namespace, resource, value); addable body properties 422 -> 427. Only this one of the four batch endpoints appears, because Set-PfbWorkloadTag is the only existing cmdlet calling any of them -- the other three are #44's scope. Reports/PfbFieldCmdletMap.json, Reports/PfbValueEnumMap.json and their .md siblings do not move under this change.
This was referenced Aug 5, 2026
Resolve the four generated report artifacts by regeneration rather than by hand. Both sides had regenerated them from different inputs: main after the P0 wire-fix batch (2978f19), this branch after the Phase 0 and #84 PR 1 capability-map work. Three conflicted textually; Reports/PfbFieldCmdletMap.json auto-merged into a splice that matched no generator run on either side, which is the more dangerous case because nothing flags it. Regenerated in dependency order: Build-PfbCapabilityMap, Build-PfbFieldCmdletMap, Build-PfbResponseShapeMap, Build-PfbApiDriftReport. Data/PfbCapabilityMap.json and Data/PfbResponseShapeMap.json both reproduce byte-identically across the merge (6B9F0865...F156 and 423DE668...FEF6), so main's cmdlet changes did not leak into either map. Against main the only substantive report change is Set-PfbWorkloadTag's missingBodyProperties moving from [] to the five fields copyable, key, namespace, resource, value -- #82's array items[] walk, working as intended.
juemerson-at-purestorage
added a commit
to juemerson-at-purestorage/fb-powershell
that referenced
this pull request
Aug 13, 2026
…landed on main The 5.1 leg went red on the skip ceiling with 0 failed tests: 1818 passed, 190 skipped, ceiling 185. Nothing in this branch caused it. PR dmann000#98 (Fusion context Phase 0) merged to main between this branch's previous CI run and its latest one. A pull_request run tests the MERGE commit, so the ceiling -- measured against a main that predated dmann000#98 -- went stale without any change here. dmann000#98 added 21 PS7-gated It blocks (8 in Build-PfbCapabilityMap.ContextScopeDrift.Tests.ps1, 9 in Build-PfbCapabilityMap.Tests.ps1, 4 in PfbSpecTools.ContextScope.Tests.ps1), and the observed 5.1 skip count moved 169 -> 190. Exactly +21. They run on 7 -- the pwsh7 leg is unchanged at 2 skipped over 2006 passed, against its ceiling of 8 -- so this is the PS7 gate doing its job, not lost coverage. All three required Describes ran on 5.1 with 0 skips. Raised to 206, keeping the same +16 headroom over measured that 185 had over 169, with the measurement, the run id and the attribution recorded inline as this file asks for. Worth noting the gate behaved correctly here: it is designed to red on a stale ceiling, and a merge to main is one of the ways a ceiling goes stale. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 13, 2026
juemerson-at-purestorage
added a commit
that referenced
this pull request
Aug 14, 2026
Rebasing this lane onto a main carrying PR #98 (regenerated capability map) and PR #107 changes the drift denominator, so the measured figures quoted as the justification for the 0.55 -> 0.60 top-10 aggregation ceiling were stale. Record the re-measured values -- main alone 54.81% (570/1040), this lane on top of it 55.79% (564/1011) -- alongside the original pre-#98 pair. The conclusion is unchanged: 0.55 is genuinely exceeded and 0.60 still holds. Co-Authored-By: Claude <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.
Brings
integration/capability-map-2026-08tomain. This is two already-reviewed PRsstacked and tested together, not new work:
contextScopeto the capability map,schemaVersion1 → 2itemsFixes #71. Closes #74. Closes #82 — see the scope note under "Deliberately not in scope".
(Those keywords live here rather than on #96/#97 because closing keywords only fire for merges
into the default branch, and both of those targeted the integration branch. #83 and #95 stay
open deliberately.)
Both were reviewed and CI-green on their own. They are stacked rather than merged
independently because both regenerate
Data/PfbCapabilityMap.json, and merging them inparallel would have made each one's artifact diff unreadable — you could not tell which
change caused which line to move. Sequencing preserves that evidence; the integration branch
then proves the combined state works before anything reaches
main.Sequencing was agreed in #84.
Once this merges, Phase 1 of the Fusion context work is unblocked
This branch is the complete prerequisite set — merging it is what allows Phase 1 to start.
The design doc originally folded all four prerequisites into Phase 1; they were split out
because each is independently justifiable and two are load-bearing in a way that is easy to
get wrong if built alongside the feature that consumes them. All four are here:
context_namescomponent resolution moved fromtools/libintoPrivate/contextScopeper-endpoint field,schemaVersion2Get-PfbFleetMembertop-levelMemberName/FleetNameSet-PfbContextpipeline binding depends on itVerified present on the merge commit: a single canonical resolver in
Private/which thetools/librule checker dot-sources rather than duplicating;schemaVersion: 2with632 of 632 endpoints carrying
contextScope; the staleness check wired intoConnect-PfbArray; and the two new top-level properties onGet-PfbFleetMember.Nothing here introduces
-Context,Set-PfbContext,Invoke-PfbInContext,-AllArrays,or any injection behaviour — that is all Phase 1.
contextScopeships populated andnothing reads it at runtime yet. A user upgrading to this cannot tell the context work is
coming, except that
Get-PfbFleetMemberoutput is more readable and a warning may appear atconnect time.
To be precise about what is not here: #83 and #95 remain open, and neither is a Phase 1
prerequisite. #83 (
bodyConstraints) is a separate capability-map enhancement, and #95 isthe runtime half of #82. Neither appears in the design's Phase 1 scope, so both can land on
their own schedule without holding Phase 1 up.
What lands
Phase 0 (#96) — every one of the 632 capability-map endpoints gains a
contextScopefield,
{scope, provenance}, wherescope∈array/fleet/unknownandprovenance∈default/declared/live-tested/unknown. Derived from threex-pure-*spec extensionsplus a curated table.
unknownis a designed degradation, not a gap — it means the speccarries no signal, and the cmdlet is left to the array to accept or reject rather than being
guessed at client-side.
schemaVersionmoves to 2; nothing inPrivate/orPublic/readsthat field, so the bump is an inert label.
#71 —
MaxDepthdefaulted to 8, truncating fb2.12–2.16allOfchains. Five body fieldson
PATCH /password-policiescarriedintroducedVersion = 2.17when the specs say 2.16.Runtime-visible rather than cosmetic:
Assert-PfbApiCapabilityreadsbodyProperties, so themodule would have refused those five parameters against an array on REST 2.16. The 2.17 spec
restructuring flattened these chains, which is why the defect self-heals from 2.17 on and is
invisible against current data.
#82 — the walk never descended an array body's
items. Four batch endpoints recorded nousable
bodyProperties, hiding 23 fields from both the runtime gate and the drift report.The two compose: descending
itemsconsumes a depth level, so #82 needs #71's headroom.Neither fix does anything alone.
The design decision worth your attention
#82's obvious fix — teach
Add-PfbSchemaPropertyNodesto descenditems— would havesilently corrupted
Data/PfbResponseShapeMap.json. That walker is shared withGet-PfbSpecResponseShapes, whose entire contract is that an envelope's properties and itsitems[]element's properties stay two deliberately-separate levels. Collapsing themwould make the response-shape map's cross-version removal detection compare incomparable
sets, with nothing to warn.
So the
itemshop happens at theGet-PfbSpecCapabilitiescall site, matching the precedentGet-PfbSpecResponseShapesalready sets. The shared walker is untouched, andData/PfbResponseShapeMap.jsonis byte-identical throughout —423DE668…FEF6before Phase 0,after #97, and after the merge.
Verification of the merged state
Both PRs were green individually, but each PR's CI only ever saw its own merge preview — the
combined state had never been built. Run directly on the merge commit:
Full dual-edition suite:
Totals reconcile at 1832 per edition, so the 5.1 skips are the deliberate edition guards, not
containers failing to load.
Artifacts regenerated from the 29 cached specs and compared byte-for-byte:
Data/PfbCapabilityMap.json6B9F0865…F156Data/PfbResponseShapeMap.json423DE668…FEF6Worth knowing: no CI job performs that comparison.
cross-platform-tests.ymlneverfetches the gitignored
tools/specs/, so the ~27 real-artifact assertions skip silentlyin CI and read exactly like passes. Those checks were run locally with the spec cache present,
and the real-artifact
Describeblocks were confirmed to have actually executed rather thanskipped. A green CI summary on this repo is not evidence that the committed artifacts match
generator output — that has to be done by hand, and was.
Also verified: reverting only
tools/lib/PfbSpecTools.ps1to the pre-#97 base reproducesPhase 0's capability map byte-identically, so 100% of #97's map diff is attributable to its own
code rather than drift or staleness.
Live-tested on FB-A, REST 2.26 (7 ledger rows against #97's tip): create / read-back /
update / destroy / eradicate all pass,
PATCH /file-systemsshowed a real 1 → 2 GiB statechange, no residue left behind.
Two coverage gaps stated plainly rather than papered over:
endpoint's family is on the live-harness deny-list.
items) #82's batch gating is not live-verifiable either.Set-PfbWorkloadTagreturnedHTTP 400: Workload does not exist.— a resource rejection, not a body-field one, whichconfirms the
-is [IDictionary]guard short-circuits beforebodyPropertiesis consulted.Known-pre-existing, not caused by this work
Reports/PfbApiDriftReport.jsononmainwas one gap stale, which is why the "nothingvanishes" invariant in
Tests/Build-PfbApiDriftReport.Tests.ps1has been red locally. Rootcause:
New-PfbFileSystemReplicaLink's[Nullable[bool]]$RemoteDefaultExportsgoes through aconditional assignment the drift tracer cannot follow → confidence
high→partial→enrichment disabled →
systemicGaps/conventionStrength252 → 246. Not spec staleness;analysedVersionsis identical at 2.28.This was confirmed pre-existing three independent ways, and #97 separates it into its own
commit so it cannot be misread as damage from the schema-walk change. That test is now green.
The stale report is numerically identical to what
origin/automated/update-api-capability-mapalready holds — that workflow has failed at itsOpen pull requeststep on every run since 2026-07-24, somainnever received it. Worthdeciding separately whether that workflow should be repaired or deleted, since it regenerates
reports this work already regenerates.
Deliberately not in scope
Capability map records no body fields for array-bodied endpoints (schema walk never descends through
items) #82's runtime goal is not reached, and is not claimed.Assert-PfbApiCapability'sbody-field loop is guarded on
-is [System.Collections.IDictionary], and an array bodyarrives as
[hashtable[]], so the loop is skipped beforebodyPropertiesis consulted. The23 recovered fields are inert at runtime today.
Capability map records no body fields for array-bodied endpoints (schema walk never descends through
items) #82 is closed by this PR at its map-and-reporting scope, and Assert-PfbApiCapability cannot field-gate array bodies: the IDictionary guard outlives its premise once #82 lands #95 owns the runtime half.The split is deliberate rather than a loose end: recovering the fields into the map is a
generator fix, while making the runtime gate act on them means changing a type guard in a
live code path that every write cmdlet goes through — a different change, a different risk
profile, and a different reviewer's attention. Verified live rather than reasoned about:
Set-PfbWorkloadTagagainst FB-A returnedHTTP 400: Workload does not exist., a resourcerejection rather than a body-field one, which is direct evidence the guard short-circuits
before
bodyPropertiesis ever consulted.Unblocks one of Batch-operation cmdlets (nodes, resource-accesses, workload-tags, fleet-members) #44's two consequences — the drift-report half. The runtime half is Assert-PfbApiCapability cannot field-gate array bodies: the IDictionary guard outlives its premise once #82 lands #95.
Capability map cannot record array-body cardinality constraints (minItems/maxItems/uniqueItems) — needs schemaVersion 2 #83 (
bodyConstraints) is deferred and incrementsschemaVersionfrom whatever it finds.No version bump and no
CHANGELOG.mdentry — left as your call.