Skip to content

Give every feature a verification story - #11

Merged
BiosSystem merged 1 commit into
masterfrom
feat/verification-coverage
Sep 5, 2026
Merged

Give every feature a verification story#11
BiosSystem merged 1 commit into
masterfrom
feat/verification-coverage

Conversation

@BiosSystem

@BiosSystem BiosSystem commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Closes Track 2 of the v3.4.0 plan. All 112 features now declare how they are verified, and routing is driven by that metadata instead of a hardcoded list.

The gap was smaller than the plan claimed

The plan counted ten unverifiable features. That was measured from Features.json metadata alone, and it overstated things — three of the ten were already verified at runtime by a hardcoded FeatureId list inside Test-WinSwiftFeature:

if ($FeatureId -in @('RemoveApps', 'RemoveGamingApps', 'RemoveHPApps')) { ... }

The metadata was incomplete, not the coverage. That's its own problem though: routing by hardcoded list means Features.json doesn't describe how a feature is verified, and the unit suite can't distinguish a declared gap from an undeclared one — which is exactly why the miscount happened.

FeatureId State before Resolution
RemoveApps, RemoveGamingApps, RemoveHPApps Verified, but by hardcoded list Declared AppxAbsence; routing moved to metadata
ForceRemoveEdge Genuinely unverified New EdgeRemoved adapter
ClearStart, ClearStartAllUsers, ReplaceStart, ReplaceStartAllUsers Genuinely unverified New StartLayout adapter
Apps Genuinely unverified Exempt — a value-carrying CLI parameter, not a toggle
CreateRestorePoint Genuinely unverified Exempt — a one-shot action, not a desired state

Real new verification work was five features. Apps turned out to be a parameter ("the selection of apps to remove, specified as a comma separated list") that happens to live in Features.json — there is no state to read back, so the plan's single-exemption assumption became two.

What's built

  • Metadata-driven routing. Hardcoded list gone; Features.json is now the single source of truth.
  • AppxAbsence — resolves the app list via the existing Get-WinSwiftFeatureAppIds, checks installed and provisioned state. Provisioned state matters: a package can be uninstalled per-user while still provisioned and due to return on the next servicing pass.
  • EdgeRemoved — checks the Edge uninstall key in the 32-bit registry view plus the four autostart values Remove-EdgeAutostartValue clears. Those are exactly the artifacts ForceRemoveEdge manipulates, so their absence is a true applied-state signal.
  • StartLayout — SHA-256 compares the on-disk start2.bin against the expected template (bundled blank for ClearStart*, caller-supplied for ReplaceStart*). All-users variants check every user profile plus the default profile, since new users inherit from it.
  • NotApplicable — new status, reported distinctly, feeding neither FailedCount nor ErrorCount so it cannot affect exit code 2.

Tests

Two guards that make the gap non-recurring:

  • Every feature must declare a RegistryKey or a VerificationAdapter — a new feature can't be added without a verification story.
  • The exemption list is pinned to exactly Apps and CreateRestorePoint, so NotApplicable can't become a dumping ground for features nobody wanted to verify.

Plus coverage for the new adapters, the NotApplicable counting rule, the no-adapter Unsupported path, and unknown-adapter error handling.

Note the existing RemoveApps test fixture needed a VerificationAdapter added — under the old hardcoded routing it didn't need one, which is the same latent problem in miniature.

Verification

  • Tests\Invoke-StaticValidation.ps1 — 103 files and all config JSON
  • Both new adapters exercised directly against real registry and filesystem state, negative cases included: template match → True, content differs → False, file missing → False, missing ReplaceStart template → throws (engine surfaces it as Error), and EdgeRemovedFalse on a machine with Edge installed
  • The three Features.json assertions replicated by hand: 112/112 covered, 0 gaps, exemptions exactly as pinned
  • Standalone rebuilt and parse-checked

Pester deferred to CI — this machine has only Pester 3.4.0 and CI requires 5.7.1, so the six new tests run here for the first time.

Not verified end to end: a full -Verify sweep across all 112 features on a live machine needs elevation and belongs to the Track 3 integration suite.

Also corrects §4 of the plan, which listed the three already-covered features as gaps.

Closes the Track 2 gap in the v3.4.0 plan. All 112 features in
Features.json now declare how they are verified, and the routing is
driven by that metadata rather than by a hardcoded list.

The gap was smaller than the plan claimed. Three of the ten features
counted as unverifiable were already verified at runtime by a hardcoded
FeatureId list inside Test-WinSwiftFeature:

    if ($FeatureId -in @('RemoveApps','RemoveGamingApps','RemoveHPApps'))

The metadata was incomplete, not the coverage. That is its own problem:
routing by hardcoded list means Features.json does not describe how a
feature is verified, and the unit suite cannot tell a declared gap from
an undeclared one. Real new verification work was five features.

Changes:

- Replace the hardcoded FeatureId branch with dispatch on the declared
  VerificationAdapter, making Features.json the single source of truth.
- Add the AppxAbsence adapter for the three app-removal features,
  checking installed and provisioned state. Provisioned state matters
  because a package can be uninstalled per-user while still provisioned
  and due to return on the next servicing pass.
- Add the EdgeRemoved adapter for ForceRemoveEdge, checking the Edge
  uninstall key in the 32-bit registry view plus the four autostart
  values Remove-EdgeAutostartValue clears. Those are exactly the
  artifacts ForceRemoveEdge manipulates.
- Add the StartLayout adapter for the four start menu features,
  SHA-256 comparing the on-disk start2.bin against the expected
  template. The all-users variants check every user profile plus the
  default profile, since new users inherit from it.
- Add the NotApplicable status for entries with no persistent desired
  state, reported distinctly and feeding neither the failure nor the
  error count so it cannot affect exit code 2. Apps is a value-carrying
  CLI parameter and CreateRestorePoint is a one-shot action; neither has
  a state to read back.

Tests:

- Assert every feature declares a RegistryKey or a VerificationAdapter,
  so a new feature cannot be added without a verification story.
- Pin the exemption list to exactly Apps and CreateRestorePoint, so
  NotApplicable cannot become a dumping ground.
- Cover the new adapters, the NotApplicable counting rule, the
  no-adapter Unsupported path, and unknown-adapter error handling.

Corrects section 4 of the v3.4.0 plan, which listed the three
already-covered features as gaps, and rebuilds the standalone bundle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BiosSystem
BiosSystem merged commit 1d94a1e into master Sep 5, 2026
1 check passed
@BiosSystem
BiosSystem deleted the feat/verification-coverage branch September 5, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant