Enforce the C# coverage floors in CI and add a Pester gate for scripts/vscode - #897
Merged
Merged
Conversation
…eflight-cleared plan Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hold-and-pester-gates-869
…fied plan premises Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hold-and-pester-gates-869
…d tree Round 1 repaired the five premises item 873 invalidated: re-based the eleven P0-T4 baseline line counts (Invoke-MSTestWithCoverage.ps1 351->438, Invoke-MSTest.RunSettings.Tests.ps1 496->498, AssemblyDiscovery.Tests.ps1 99->106, Invoke-MSTest.Main.Tests.ps1 144->146); retargeted P2-T3 onto the enriched fixture at line 55, adding the branch attributes rather than deleting the per-line data 873 depends on; retargeted P2-T4 onto sites 382, 416 and 418 and corrected its unsatisfiable 496-line acceptance to 498; corrected P10-T10's stale pre-change figure; and replaced P0-T7's single halt branch with five disjoint branches so a projection or reconciliation failure below the report statement is no longer misattributed to a below-floor coverage figure. Round 2 repaired three further defects measured afterwards: all 46 occurrences of the bare-dot git form, which resolved to the coordinator session worktree rather than this item's, are converted and a working-directory rule added to the preamble; the two distinct coverage denominators are now labelled and never compared across; and the tool manifest location, the Pester version-qualified import and the coverage ignore rule are recorded. Structure preserved at 11 phases and 114 tasks. The MCP plan validator returns ok with no warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… atomic plan Executor preflight returned REVISIONS REQUIRED with six defects. D1 was blocking: run-actionlint.ps1 line 11 invokes the binary with no path argument, so an absolute -File path fixed only the binary lookup while actionlint still resolved its lint target from the working directory, which is the coordinator session worktree. All three actionlint spans would have linted the wrong tree, and because both trees hold the same seven workflow files the baseline would have passed silently, leaving the post-change gates unable to observe _pester.yml or the edits to ci.yml. D2 removed assertions over file counts and path names that actionlint never prints, replacing them with a Glob listing taken in the item worktree. D3 replaced an unreachable batch-budget halt branch, since the hook derives its state root from PSScriptRoot and allows out-of-root candidates, so no denial can occur. D4 restated a justification that described a superseded document state. D5 gave the AC-02 agreement predicate a mechanical string-equality definition across the two coverage denominators. D6 named the batch-1 formatter step in the commit exoneration clause. Two further defects were then measured and repaired: the dotnet-coverage version source was inverted, because the tool prints build metadata that NuGet cannot resolve and the workflow pin required byte-identity; and the repo-local .NET SDK was added as an explicit prerequisite, since global.json pins 8.0.205 and no worktree carried it. Structure preserved at 11 phases and 114 tasks. The MCP plan validator returns ok with no warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…dual plan citations The confirming preflight verified that all eight earlier repairs landed without breaking any command quoting, then reported six residual defects. E1 is the substantive one: the solution build depends on Meziantou.Analyzer 3.0.203, which fifteen project files reference through an unconditional Analyzer item while every packages.config that names the package pins 3.0.235, so nuget restore can never produce it and a fresh clone fails with CS0006. The same divergence is present on origin/main, so it is pre-existing and outside this delivery's scope; P0-T6 now records the prerequisite, checks for the assembly before building, and forbids repairing it by editing a project file, since every project file sits outside the declared write set. E2 through E6 correct a standing-rule enumeration that a later round invalidated, a wrong line count, a mis-cited render line at three sites, ordinals that a new prerequisite made ambiguous, and a detached qualifier. A final round corrected a claim that altcover had been installed: that package is absent from packages.config and reachable only through Exists-guarded imports, so installing it would activate imports intended to stay dormant. It was removed and a full rebuild confirms it is not load-bearing. Structure preserved at 11 phases and 114 tasks; the MCP plan validator returns ok with no warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng assertion site Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…main functions Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e and SDK install targets Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-uplift coverage basis Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e survives strict mode Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ance criteria Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lan checklist Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The executor modified tests/scripts/vscode/Invoke-MSTestWithCoverage.ResultsDirectory.Tests.ps1 and escalated it rather than absorbing it silently, because the path was absent from the declared write set in the plan and the specification. The file arrived with the merged evidence-projection item after this write set was authored, and carries the same mocked post-processed Cobertura document with no branch-rate that P2-T3 and P2-T4 repair in two sibling files, which the branch assertion added by P2-T1 and wired into the entry point by P2-T2 turns red. The same minimal in-place repair was applied, adding branch-rate and branches-valid and leaving the file at 268 lines. Reverting was not an option, since it would leave failing tests in the delivered suite, so the documents were the defect rather than the change. Both write sets now name the path and record its provenance, which restores P10-T14's write-set acceptance to true. No task, acceptance condition or checkbox state was altered; the plan holds 114 checked tasks and 11 phases and the MCP plan validator returns ok. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mok2zkoP23ArYqAjHvRqd1
5 tasks
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.
Enforce the C# coverage floors in CI and add a Pester gate for scripts/vscode
Summary
scripts/vscode/Invoke-MSTestWithCoverage.ps1instead of an inlinevstest.console.exeblock, which is what makes the pre-existing 80 percent line assertion actually execute in CI..github/workflows/_pester.yml, a new reusable callee that runs thetests/scripts/vscodesuite with JaCoCo coverage and fails the job below an 80 percent LINE figure or on any test failure.Invoke-VSBuild.ps1andInvoke-Restore.ps1behind extracted main functions, so dot-sourcing them in a test no longer builds the solution or rewrites project files.scripts/vscodeline coverage from 78.90 percent to 83.93 percent, clearing the floor the new gate enforces.Why
Two coverage gates were specified but not enforced. The MSTest workflow produced a Cobertura document through an inline vstest invocation that never reached the threshold assertion, so the 80 percent line floor existed in code and was never evaluated by CI. No PowerShell coverage gate existed at all, despite
scripts/vscodecarrying the repository's build, restore, test and coverage tooling.A second, narrower defect blocked the PowerShell gate from being added safely.
Invoke-VSBuild.ps1andInvoke-Restore.ps1executed their work at file scope, so any Pester file that dot-sourced them ran a real build and, through the package-reference sync script, rewrote.csprojfiles in the working tree. Coverage of those scripts was therefore both unmeasurable and destructive to measure. Extracting the bodies behind an invocation guard is what makes the measurement deterministic, which the determinism proof in this change demonstrates against the pre-change per-file counters.What Changed
CI workflows
.github/workflows/_mstest-coverage.yml— replaces the inline vstest block with a single pwsh step invoking the coverage entry point; pins thedotnet-coverageinstall version; repoints the artifact upload atcoverage/coverage.cobertura.xmland changes the no-files behaviour fromwarntoerror..github/workflows/_pester.yml— new reusable callee:windows-latest, 10-minute job timeout,contents: read, bothworkflow_callandworkflow_dispatch, no concurrency block, pinned Pester 5.x install and import, artifact upload withif-no-files-found: error..github/workflows/ci.yml— adds thepestercaller job in the existing leading-dot relative form..github/workflows/README.md— updates the gate table, records both pinned tool versions, and adds the new required check-run context. The prior claim that the vstest invocation was moved rather than edited is withdrawn, because this change deliberately replaces it and thereby changes the gate's pass criterion.Production PowerShell
scripts/vscode/Invoke-MSTestWithCoverage.Threshold.ps1— addsAssert-CoberturaBranchCoverageThreshold, with distinct terminating messages for a missing rate, a non-numeric rate, a rate outside the closed unit interval, zero valid branches, and a below-floor rate.scripts/vscode/Invoke-MSTestWithCoverage.ps1— one statement, calling the branch assertion on the same post-processed document, immediately after the line assertion and before the first-party report.scripts/vscode/Invoke-VSBuild.ps1— body extracted intoInvoke-VSBuildMainbehind an invocation guard; three wrapper seams (Get-MSBuildPath,Invoke-SyncPackageReferences,Invoke-MSBuildExe). Guard order, guard messages and the argument list are unchanged.scripts/vscode/Invoke-Restore.ps1— the same pattern viaInvoke-RestoreMain, with deliberately distinct seam names (Get-RestoreMSBuildPath,Invoke-RestoreMSBuildExe) because Pester runs every container in one runspace and same-named functions dot-sourced from two files would shadow each other.Tests
New:
Invoke-Restore.Tests.ps1,TestProcessCleanup.Tests.ps1. Extended: the threshold, assembly-discovery, merge, SDK-install, main and VSBuild suites. Repaired: three files whose mocked post-processed Cobertura document carried nobranch-rateand which the new assertion would otherwise turn red.Documentation and evidence
Feature documents and the full per-phase evidence tree, including fail-before artifacts for every regression test and negative-path proofs for both gates.
Architecture / How It Fits Together
ci.ymlcalls both reusable callees. The MSTest callee runs the coverage entry point, which collects viadotnet-coverage, post-processes the Cobertura document, then evaluates the line assertion and the new branch assertion against the document root before emitting the first-party report and the JaCoCo projection. The Pester callee builds a Pester configuration overtests/scripts/vscodewith coverage scoped toscripts/vscode, writes JaCoCo to an explicit path, prints the counts and the measured figure, and only then evaluates its exit condition — the explicit exit is placed after the figure-emitting statements so a failure is always diagnosable from the log.Note that the C# gate is judged on the document-root attributes, which are computed over every package surviving third-party removal. The
First-party coverage:line the run prints is computed over the first-party allowlist and is a different denominator. Both are recorded, labelled, and compared only like with like.Verification
Completed in this change. All figures below come from the final toolchain pass, which completed cleanly in a single pass.
scripts/vscodecheck: 1639 files, unformatted list identical to the baseline list. No C# source file is modified by this change.Recommended for the reviewer.
Backward Compatibility / Migration Notes
Risks and Mitigations
Review Guide
scripts/vscode/Invoke-MSTestWithCoverage.Threshold.ps1and the single call site — the smallest and most load-bearing diff..github/workflows/_pester.yml, particularly the ordering of the exit statement relative to the figure-emitting statements.scripts/vscode/Invoke-VSBuild.ps1andInvoke-Restore.ps1— the largest production diffs, but mechanical extraction; guard order and messages are unchanged.Follow-ups
.csprojand therefore outside this change's declared write set. Fifteen project files carry an unconditional analyzer item namingMeziantou.Analyzer.3.0.203while the restore manifests pin3.0.235, so a clean clone fails to build. CI does not currently surface this, because the NuGet cache step uses a prefix fallback key that carries the older package forward. It warrants its own issue.GitHub Auto-close
🤖 Generated with Claude Code
https://claude.ai/code/session_01Mok2zkoP23ArYqAjHvRqd1