Skip to content

Move context_names component resolution from tools/lib into Private/ before the Fusion injection path is built #74

Description

Summary

Private/Test-PfbContextMultiValueCapable.ps1 shipped in #73, but the code that produces its -ContextComponent input did not. The predicate is loaded into the module and cannot be fed from inside it.

Demonstrated against main at 191b7a9:

Import-Module .\PureStorageFlashBladePowerShell.psd1 -Force

Test-PfbContextMultiValueCapable   exported: False   internal: True
Get-PfbContextParameterFact        internal: False

The .psm1 dot-sources only Private/ and Public/; tools/ is never loaded and the manifest has no FileList reaching it. So the three-step component-resolution contract — override key-present-but-null, override key-absent, then the top-level default — is currently reachable only from the maintainer toolchain.

Why it matters

This is a silent failure mode, not a loud one. Nothing is broken today: the predicate is simply inert, with no runtime callers. But the next piece of work is the Fusion context_names injection path, and that path needs resolved component names to call the predicate at all. Built in the obvious order, it will not error — it will quietly reimplement the resolution inside Private/, giving the codebase two copies of a rule whose entire design premise is that it has exactly one declared home.

The key-present-but-null vs key-absent distinction is the part most likely to be reimplemented subtly wrong, because both look like "no value" to a casual reading and only one of them means "this endpoint's parameter has no component."

What needs to happen

Split the resolution step out of Get-PfbContextParameterFact (tools/lib/PfbContextRuleTools.ps1) into Private/, then have both sides consume it:

  • tools/lib/PfbContextRuleTools.ps1 — for the maintainer drift check, unchanged in behaviour.
  • The Fusion injection path — to produce -ContextComponent for Test-PfbContextMultiValueCapable.

Only the resolution belongs in Private/. Get-PfbContextParameterFact currently also does record-shaping and HTTP 207 merging, and both of those are maintainer-toolchain concerns that should stay in tools/. Untangling those three responsibilities is the actual work; the move itself is small.

Note that tools/ can freely depend on Private/, but not the reverse — which is what fixes the direction of the dependency rather than just relocating it.

Where this is specified

Design doc docs/design/fusion-context-injection.md, section 5, implementation prerequisite 3 — currently in flight as #72 (stacked on #22), which revises the doc to rev 3.

Relationship to the open PRs

Verification

Whoever does it: after the move, Get-PfbContextParameterFact must still be internal-only to tools/, the resolver must be reachable inside the module, and Tests/PfbContextRuleTools.Tests.ps1 (54 tests) plus Tests/Build-PfbApiDriftReport.Tests.ps1 (60 tests) must both stay green — the latter includes the check that the committed Reports/ artifacts still reproduce byte-for-byte.

Activity

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

    @juemerson-at-purestorage
    CollaboratorAuthor

    Resolved in #96 as item 1 of the Fusion Phase 0 prerequisites, reaching main via #98 (which
    carries the closing keyword — #96 targeted an integration branch, and closing keywords only fire
    on merges into the default branch).

    The resolution contract now lives in Private/Resolve-PfbParameterComponent.ps1 and is loaded
    into the module. Verified against the merged integration tip, mirroring this issue's own repro
    against main:

    Resolve-PfbParameterComponent          internal: True   exported: False
    Test-PfbContextMultiValueCapable       internal: True   exported: False
    

    Both internal, neither exported — the predicate can now be fed from inside the module, which was
    the whole complaint.

    The duplication risk this issue was really about is closed too.
    tools/lib/PfbContextRuleTools.ps1 does not carry its own copy: it dot-sources the module's
    Private/ file by declared path and throws if it is absent, so the maintainer toolchain and the
    runtime consume one canonical implementation. The key-present-but-null vs key-absent distinction
    therefore exists in exactly one place, which was the part most likely to be reimplemented subtly
    wrong.

    Phase 1 can now call the cardinality predicate with resolved component names rather than
    reinventing the resolution inside Private/.

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