Skip to content

Tracking: coordinated changes to the capability-map generator and schema (#71, #82, #83, contextScope) #84

Description

Tracking issue. No work of its own — it exists so the four pending changes to
Data/PfbCapabilityMap.json land in a coherent order instead of colliding.

Why one issue

Four open changes converge on the same three things:

  • one function — Add-PfbSchemaPropertyNodes / Get-PfbSchemaPropertyNames in
    tools/lib/PfbSpecTools.ps1
  • one tracked, generated artifact — Data/PfbCapabilityMap.json, which has a live runtime
    consumer in Private/Assert-PfbApiCapability.ps1
  • one invariant — the "nothing vanishes" check in
    Tests/Build-PfbApiDriftReport.Tests.ps1

Sequenced independently they mean four regenerations of the same file, four diffs to
review, and near-certain conflicts between the branches. Sequenced together they are two
PRs with two reviewable diffs.

The four changes

# Change Schema change? Expected diff
#71 MaxDepth=8 truncates deep allOf chains in fb2.12–2.16 No 5 introducedVersion values move 2.17 → 2.16
#82 Walk never descends through items No 4 endpoints gain bodyProperties (23 fields), plus readOnlyBodyProperties
#83 No place to record minItems/maxItems/uniqueItems Yes 1 endpoint gains bodyConstraints
#25 / #72 Fusion contextScope, from the x-pure-* extensions plus a curated table Yes additive per-endpoint field; see rev 3 §12

Two are data corrections to existing fields; two need a new field.

The schema bump is cheaper than it sounds

Worth stating up front, because it drove the original grouping instinct and turns out not
to be the hard part:

  • Nothing in Private/ or Public/ reads schemaVersion. The runtime loader does not
    validate it.
  • Four generators — Build-PfbCapabilityMap, Build-PfbApiDriftReport,
    Build-PfbFieldCmdletMap, Build-PfbValueEnumMap — each write their own independent
    1. The version is per-artifact, not global.

So bumping to 2 is a label. The cost is a few fixtures in
Tests/Get-PfbCapabilityMap.Tests.ps1, not a migration. The real coordination cost is the
shared data-file diff and the invariant below.

The requirement none of the child issues carry

Tests/Build-PfbApiDriftReport.Tests.ps1 asserts that every query parameter and body
property the committed map lists, for an endpoint an existing cmdlet calls, lands in
exactly one of the drift report's missingQueryParameters /
missingBodyProperties / readOnlyFields, or the phantom-exclusion set.

Any change that adds fields to the map must account for them there in the same commit or
that test goes red. #82 adds 23 fields and is the immediate case. This requirement appears
in none of the individual issues, which is exactly the kind of thing a tracking issue is
for.

Note this test is guarded on the presence of tools/specs/, which is gitignored — it
silently skips in a fresh clone or worktree and only bites in CI. Do not read a green local
run as evidence.

What CI actually does

Correcting a plausible-sounding assumption: there is no byte-identical regeneration gate
on Data/PfbCapabilityMap.json.

  • .github/workflows/update-api-capability-map.yml regenerates all four artifacts weekly
    and on push to main touching Public/**, then opens a PR if Data/Reports
    changed. It does not fail on drift.
  • .github/workflows/cross-platform-tests.yml runs the full suite on every push and PR
    across four OS/edition combinations, which is where the invariant above is enforced.

Practical consequence: a hand-edit or an out-of-band regeneration will not be rejected — it
will quietly reappear as a conflicting auto-PR later. Regenerate via the tool, never by
hand.

Proposed sequencing

PR 1 — walker fixes, no schema change. #71 + #82.

Both are bugs in the same function with concrete, checkable expected diffs. They interact:
descending through items consumes a depth level, and #71's fix (an explicit -MaxDepth 32
at the Get-PfbSpecCapabilities call sites) supplies the headroom #82 needs. Landing #82
alone risks re-truncating on the historical specs where introducedVersion is determined.

Ships with the drift-report accounting for the 23 new fields, and a synthetic fixture whose
body is an array of an allOf-composed element schema.

PR 2 — schemaVersion 2. #83 + the contextScope half of #25.

Both add optional per-endpoint keys, absent where not applicable. One bump, one fixture
update, one diff. contextScope additionally needs the generator to read the x-pure-*
extensions and to carry the curated table at build time — see rev 3 §12 in #72 for that
design, which is the larger part of this PR.

Ordering matters: PR 2 rebuilds the map, so landing it on top of a not-yet-fixed walker
would bake the #71/#82 defects into a diff that also contains the schema change, making
both harder to review.

Downstream

#44 (batch-operation cmdlets for nodes, resource-accesses, workload-tags, fleet-members)
is scoped to exactly the four endpoints #82 makes visible. Until PR 1 lands, the drift
report shows zero addable body fields for all four, so it cannot inform the parameter design
of the cmdlets #44 proposes. #44 should wait on PR 1 rather than hand-transcribing the field
lists from the spec.

Explicit non-goals

Activity

  1. juemerson-at-purestorage commented on Aug 2, 2026

    @juemerson-at-purestorage
    CollaboratorAuthor

    Cross-link: #85 is a workflow-enablement blocker that sits ahead of both PRs planned here.

    The report generators emit records in filesystem-enumeration order, so regenerating on the
    Linux CI runner instead of a Windows workstation produces a 10,218-line diff in
    Reports/PfbFieldCmdletMap.json containing zero semantic change (2015 entries both sides,
    canonical diffs 0, all 2015 moved).

    Relevance to this tracker specifically:

    Also relevant to the assumption corrections already recorded here: the regeneration path is
    currently broken. update-api-capability-map.yml has failed on every run since 2026-07-24,
    always at the final Open pull request step (GitHub Actions is not permitted to create or approve pull requests — a repo setting, not a code fault). Generation and the full test suite
    pass, and the branch is still force-pushed, so origin/automated/update-api-capability-map
    holds regenerations that main has never received. Fixing that needs repo-admin access.

    Suggested order: #85 (sort) → permission toggle → PR 1 → PR 2. Unblocking the permission
    before the sort lands means the first auto-PR arrives as an unreviewable 11k-line diff.

  2. juemerson-at-purestorage commented on Aug 4, 2026

    @juemerson-at-purestorage
    CollaboratorAuthor

    contextScope is leaving PR 2 — the shared schemaVersion 2 pairing is dissolved

    This tracker plans PR 2 as #83 (array cardinality) + the Fusion contextScope field from
    #25, sharing one schemaVersion 2 bump. That pairing is dissolved. Recording it here so the
    plan above does not mislead whoever picks this up.

    What changed

    Before Now
    contextScope (#25) PR 2, shared bump Fusion Phase 0 PR, takes schemaVersion 2
    #83 array cardinality PR 2, shared bump Stays with this tracker; increments from whatever it finds
    #71 + #82 PR 1, no schema change Unchanged

    The Fusion work is now split into a Phase 0 (prerequisites) and Phase 1 (injection), spec'd on
    feat/fusion-context-phase-0. contextScope is a Phase 0 item because kind-vs-scope validation
    and every scope-aware error message in Phase 1 are map lookups and cannot be written before the
    field exists.

    Why not just absorb all of this into Phase 0 instead

    That was the alternative considered, and it would have preserved the one-bump intent. It was
    rejected for a structural reason rather than a size one: #71, #82 and #83 all land in
    Add-PfbSchemaPropertyNodes
    (tools/lib/PfbSpecTools.ps1:191) — a recursive schema walker
    whose MaxDepth threads through five call sites and which feeds both
    Data/PfbCapabilityMap.json and Data/PfbResponseShapeMap.json, plus every derived report.
    Changing it perturbs two runtime artifacts at once.

    It also has a downstream consumer with nothing to do with Fusion: #44 is blocked on PR 1,
    since its four endpoints are exactly the ones #82 makes visible. That is an argument for this
    work standing on its own regardless of what Fusion does.

    contextScope, by contrast, reads operation-level x-pure-* extensions and never touches the
    body-schema walk. The two changes are genuinely independent in code — they collide only on
    the generated artifact.

    Consequences

    • Two sequential regenerations of Data/PfbCapabilityMap.json. Accepted. Whichever PR lands
      second regenerates and resolves the conflict that way — a 632-endpoint generated file should
      never be hand-merged.

    • schemaVersion numbers are not pre-assigned. Phase 0 intends 2, but if Capability map cannot record array-body cardinality constraints (minItems/maxItems/uniqueItems) — needs schemaVersion 2 #83 lands first it
      takes 2 and Phase 0 takes 3. The number carries no meaning; only "newer than what I read"
      does. Confirmed: nothing in Private/ or Public/ reads schemaVersion at all, and it is
      per-artifact — five generators each write their own independent value, all currently 1:

      Artifact Generator
      Data/PfbCapabilityMap.json Build-PfbCapabilityMap.ps1:317
      Data/PfbResponseShapeMap.json Build-PfbResponseShapeMap.ps1:173
      Reports/PfbApiDriftReport.json Build-PfbApiDriftReport.ps1:637
      Reports/PfbFieldCmdletMap.json Build-PfbFieldCmdletMap.ps1:98
      Reports/PfbValueEnumMap.json Build-PfbValueEnumMap.ps1:153

      So the bump is a maintainer label, not a migration gate, and bumping one does not implicate the
      other four.

    Two notes on the regeneration path, still open

    #85 is closed, so the 10k-line phantom-diff hazard is gone — that was the blocker most likely
    to bury either PR's real diff.

    But the auto-PR step is still failing. update-api-capability-map.yml has failed on every run
    since 2026-07-24 at Open pull request
    (GitHub Actions is not permitted to create or approve pull requests — a repo setting, not a
    code fault). Generation and the full suite pass and the branch is still force-pushed, so
    origin/automated/update-api-capability-map holds regenerations main has never received.
    Anyone about to regenerate by hand — for either PR — should diff against that branch first rather
    than re-deriving work that already exists. Fixing the workflow needs repo-admin access.

    Sequencing between this tracker's PR 1 and the Fusion Phase 0 PR is still being decided; this
    comment only records that they are no longer one unit.

  3. juemerson-at-purestorage commented on Aug 5, 2026

    @juemerson-at-purestorage
    CollaboratorAuthor

    Sequencing decided: Fusion Phase 0 first, then PR 1

    My earlier comment
    dissolved the contextScope + #83 pairing but left the order between this tracker's
    PR 1 (#71 + #82) and the Fusion Phase 0 PR open. Recording the decision here
    before either PR opens, so both arrive with their context already stated.

    Order: Phase 0 lands first and takes schemaVersion 2. PR 1 follows immediately,
    rebases onto it, and regenerates once on the stable base.

    Why this order and not the reverse

    Both PRs regenerate Data/PfbCapabilityMap.json, and — as recorded above — there is
    no CI gate comparing the committed artifact against what the generator would produce.
    cross-platform-tests.yml never fetches the gitignored tools/specs/, so every
    real-artifact assertion skips silently in PR CI. That makes the human-readable artifact
    diff the only verification either PR has.

    The two PRs are not symmetric in how much they depend on that diff:

    • Phase 0's claim is "one additive field per operation, nothing else moved." That is
      only checkable against an unmoved base. Regenerating it on top of PR 1's 23 recovered
      bodyProperties and 5 introducedVersion corrections destroys the evidence.
    • PR 1's claim is backed by unit fixtures that fail before the fix and pass after,
      plus a diff characterised in advance down to the specific endpoints and field counts.
      That survives being re-derived on a moved base.

    So the PR whose verification is fragile goes first. Reversing it would cost Phase 0 its
    primary evidence and gain nothing.

    What this means in practice

    • Two consecutive regenerations of Data/PfbCapabilityMap.json are expected and
      deliberate
      — not churn, and not one PR undoing the other. Each PR body says so.
    • PR 1 will open carrying a not-yet-regenerated artifact. That is deliberate and it
      is what the missing CI gate makes viable: the branch will not go red for it. The
      regeneration lands as its own reviewable commits after Phase 0 merges. If it is easier
      to review that way, PR 1 can sit as a draft until then.
    • schemaVersion 2 goes to Phase 0. Capability map cannot record array-body cardinality constraints (minItems/maxItems/uniqueItems) — needs schemaVersion 2 #83 increments from whatever it finds — 3 if
      Phase 0 has landed. As established above, the number is a per-artifact maintainer
      label that nothing in Private/ or Public/ reads, so this costs nothing.
    • Whichever PR regenerates second resolves the JSON conflict by regenerating, never by
      hand.
      A 632-endpoint generated file should not be hand-merged.
    • Batch-operation cmdlets (nodes, resource-accesses, workload-tags, fleet-members) #44 is unblocked by PR 1, unchanged by this ordering — it just arrives one merge later.

    Unchanged and still open

    The auto-PR step of update-api-capability-map.yml is still failing at
    Open pull request (GitHub Actions is not permitted to create or approve pull requests)
    on every run since 2026-07-24, so origin/automated/update-api-capability-map still holds
    a Reports/-only regeneration main has never received. It is currently 1 ahead / 1 behind
    main. PR 1 will diff its regenerated reports against that branch rather than re-deriving
    the work. Fixing the workflow needs repo-admin access.

  4. juemerson-at-purestorage commented on Aug 5, 2026

    @juemerson-at-purestorage
    CollaboratorAuthor

    Status: three of the four are done, #83 is the only one left

    # Change Landed in State
    #25 / #72 contextScope from the x-pure-* extensions plus the curated table #96 done
    #71 MaxDepth=8 truncation #97 done
    #82 Walk never descends items #97 done at map/reporting scope; runtime half is #95
    #83 bodyConstraints for minItems/maxItems/uniqueItems — outstanding

    Both PRs were stacked on integration/capability-map-2026-08 and come to main together in
    #98, which carries the closing keywords for #71, #74 and #82. They were deliberately not
    merged to main one at a time: since both regenerate Data/PfbCapabilityMap.json, merging in
    parallel would have made each one's artifact diff unreadable. Stacking kept each diff clean and
    let the combined state be built and tested before anything reached main.

    The two-PR plan this issue called for held up exactly as written.

    Four things this issue got right, confirmed in practice

    • No byte-identical regeneration gate exists. Confirmed. Because of it, the map had to be
      verified by hand: reverting only tools/lib/PfbSpecTools.ps1 to the base reproduces the prior
      map byte-identically, which proves the generator deterministic and attributes 100% of the
      diff to the intended code rather than drift or staleness.
    • The schemaVersion bump really is just a label. Nothing in Private/ or Public/ reads
      it. Cost was a handful of fixtures, as predicted.
    • The "nothing vanishes" invariant is the requirement none of the child issues carried, and
      it bit exactly as described — Capability map records no body fields for array-bodied endpoints (schema walk never descends through items) #82's 23 new fields had to be accounted for in the drift report
      in the same change.
    • It silently skips without tools/specs/. Also confirmed, and worse than it sounds: a
      scoped run reporting 0 skipped still does not prove the real-artifact Describe blocks
      executed. They had to be checked explicitly (they did — 10 ran/0 skip and 14 ran/0 skip). See
      CI silently skips ~23% of the test suite, including the absolute-path regression guards #63.

    Two corrections for whoever does #83

    • Expect two order-only records, not one. Alongside the predicted
      PATCH /ssh-certificate-authority-policies, PATCH /file-systems also moved — top-level key
      order only, contextScope and readOnlyBodyProperties swapping position, all values identical.
      Deterministic across three regenerations. Diagnose an unexpected order-only diff rather than
      assuming it is noise, but know that two are normal here.
    • bodyProperties was never {} on the Capability map records no body fields for array-bodied endpoints (schema walk never descends through items) #82 endpoints. Each held one entry keyed by the
      empty string. Anyone grepping for {} to find affected endpoints will come up empty.

    #83 will inherit schemaVersion 2, not 1, and increments from there.

  5. juemerson-at-purestorage commented on Aug 20, 2026

    @juemerson-at-purestorage
    CollaboratorAuthor

    Closing: the coordination this issue existed for is done

    Three of the four changes landed as planned — contextScope (#96), #71 and #82 (both #97,
    reaching main together via #98). #83 is the only one outstanding, and it now collides with
    nothing but #95. Two issues that already cross-reference each other do not need a third place to
    keep in sync, so this closes rather than being retitled.

    The two-PR plan held up as written, and the four assumption-corrections recorded above were all
    confirmed in practice. What follows is the part that has not aged well, stated plainly because
    this issue's body reads the other way and would mislead whoever picks up #83.

    Three recorded facts have since been inverted

    This issue says Now Consequence
    There is no byte-identical regeneration gate on Data/PfbCapabilityMap.json — and the sequencing plan explicitly allowed a PR to "open carrying a not-yet-regenerated artifact" A gate exists. PR #130 added scripts/Assert-PfbDerivedArtifacts.ps1 and verify-derived-artifacts.yml, regenerating all eleven derived artifacts on pull_request and diffing against the committed copies #83 must regenerate in-PR. A stale artifact now fails the build instead of being caught by eye
    The real-artifact assertions "skip silently in a fresh clone or worktree and only bite in CI" They execute in CI. The prepare-specs job added for #63 publishes the spec cache as artifact pfb-specs to both test jobs, and Tests/coverage-baseline.psd1 names those blocks under RequiredDescribes The "artifact diff is the only verification either PR has" premise is gone. A green local run without a spec cache is still not evidence — that half stands
    The auto-PR step fails on a repo permission, so origin/automated/update-api-capability-map holds regenerations main never received — diff against it rather than re-deriving (said in two comments) Both halves are stale. update-api-capability-map.yml has been succeeding since at least 2026-08-14. That branch is 1 ahead / 186 behind main, its one commit Reports/-only and dated 2026-08-04 Following the instruction would reintroduce months of stale reports. Regenerate from main

    The gate in #130 is worth one more note, because it is the thing this issue asked for and
    assumed impossible. The reason a hard gate is safe now, where the advisory check in
    update-api-capability-map.yml was not: it pins the spec set to the committed map's own
    generatedFrom list, which separates PR-caused drift from spec-publication drift.

    What was transplanted, and where

    Nothing here is lost, but it is no longer here:

    The remaining decision

    One question, answered once for both halves: does the runtime consume what the map records, and
    how — record only, warn, or throw? #95 gates per-element field versions, #83 gates per-element
    cardinality; same function, same only-affected cmdlet (Set-PfbWorkloadTag), same nil blast
    radius until #44 lands callers for the other three array-bodied endpoints.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions