Epic: make the C# build, CI, and coverage gates report the truth - #566
Merged
drmoisan merged 81 commits intoAug 15, 2026
Conversation
Lane A of docs/research/2026-08-10-parallel-bug-flighting-and-surface-blockers.md, decomposed into five child features across three waves. Closes-in-children: #394, #441, #457, #478, #492, #494, #509, #512, #522 Excluded: #513 (collect_pr_context source lives in the drm-copilot repository) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Preparation-mode delivery for epic child utilitiescs-test-cs2002-duplicate-compile-entry-394 (wave 0 of epic build-ci-coverage-gate-fidelity). Adds issue.md, spec.md, user-story.md, the research artifact, and one preflight-cleared atomic plan. UtilitiesCS.Test/UtilitiesCS.Test.csproj carries two byte-identical <Compile Include> items for OutlookObjects\Folder\PercentageFormatterTests.cs. Re-derived on base edf3d34: they are at lines 304 and 356, both inside the same ItemGroup (lines 72-529). The potential entry's 288/338 line numbers and its "two ItemGroup sections" hypothesis are both stale. A sweep across every item type and packages.config found no other duplicate Include value. Work mode was downgraded from the requested minor-audit to full-bug: the GitHub issue body for #394 carries "(not provided in potential file)" in every section including "## Acceptance Criteria", so the minor-audit eligibility check fails and the lifecycle requires failing closed to the full path. issue.md is populated from the promoted potential entry instead. Research corrected two premises. First, /t:Build is vacuous here -- dated repo evidence shows a repeat /t:Build skips CoreCompile and never re-emits CS2002 -- so the fail-before gate uses /t:Rebuild. Second, /p:TreatWarningsAsErrors=true does not promote CS2002, verified against a green 2026-08-08 run of CI's exact command with the warning present, so the issue's "would break the build if promotion rules changed" framing is not supported by the evidence. The fix is justified by warning-signal hygiene; severity remains Low. Issue #510 tracks the identical defect and should be closed alongside #394. No production file is modified by this commit; execution is deferred to epic-orchestrator. Refs #394, #510 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Two findings from preparing issue #394. Direct single-csproj MSBuild requires /p:Platform=AnyCPU (no space) because UtilitiesCS.Test.csproj declares AnyCPU and keys its PropertyGroups off 'Debug|AnyCPU', while TaskMaster.sln uses 'Debug|Any CPU'. Passing the solution spelling to a direct project build fails _CheckForInvalidConfigurationAndPlatform. The asymmetry between the two commands is correct and must not be "fixed". /p:TreatWarningsAsErrors=true does not promote CS2002, verified against a green 2026-08-08 run of CI's exact command with the warning present and no NoWarn/WarningsNotAsErrors suppression anywhere in the repo. Also records `tr -cd '\r' | wc -c` as the reliable CR-byte count for the plan-validator LF requirement, replacing an od-based check that false-positives on files containing Windows paths, and notes that core.autocrlf=true means an LF blob materializes as CRLF in any fresh checkout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…ry-394 Preparation-mode fan-in. Issue #394. Wave 0. PREFLIGHT: ALL CLEAR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…tion outputs Issues 441 and 478. Preparation-mode outputs (issue.md, spec.md, user-story.md, research, atomic plan, evidence) committed by epic-planner to preserve work after the child orchestrator terminated on an API spend-limit error mid-run. Preflight clearance not yet reached. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…s preparation outputs Issues 457. Preparation-mode outputs (issue.md, spec.md, user-story.md, research, atomic plan, evidence) committed by epic-planner to preserve work after the child orchestrator terminated on an API spend-limit error mid-run. Preflight clearance not yet reached. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…ation outputs Issues 492, 509, 512 and 522. Preparation-mode outputs (issue.md, spec.md, user-story.md, research, atomic plan, evidence) committed by epic-planner to preserve work after the child orchestrator terminated on an API spend-limit error mid-run. Preflight clearance not yet reached. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…ess preparation outputs Issues 494. Preparation-mode outputs (issue.md, spec.md, user-story.md, research, atomic plan, evidence) committed by epic-planner to preserve work after the child orchestrator terminated on an API spend-limit error mid-run. Preflight clearance not yet reached. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…lity-integration' into bug/cobertura-coverage-arithmetic-441
…lity-integration' into bug/excludefromcodecoverage-nested-lambdas-457
…lity-integration' into bug/csharp-toolchain-gate-fidelity-512
…lity-integration' into bug/coverage-threshold-policy-reconciliation-494
Preflight-loop revisions preserved by epic-planner after the child orchestrator terminated on an API rate/spend-limit error. Preflight clearance not yet recorded in the checkpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…an revisions Preflight-loop revisions preserved by epic-planner after the child orchestrator terminated on an API rate/spend-limit error. Preflight clearance not yet recorded in the checkpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…ions Preflight-loop revisions preserved by epic-planner after the child orchestrator terminated on an API rate/spend-limit error. Preflight clearance not yet recorded in the checkpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…plan revisions Preflight-loop revisions preserved by epic-planner after the child orchestrator terminated on an API rate/spend-limit error. Preflight clearance not yet recorded in the checkpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…utor preflight Complete preparation for issue #494 (epic wave 2, build-ci-coverage-gate-fidelity). Resolves the plan through three atomic-executor preflight iterations to PREFLIGHT: ALL CLEAR. Iteration 1 returned twelve deltas (D-1..D-12); iteration 2 confirmed eleven and found D-5's recorded-blocker branch propagated to P5-T16 only, returning six more (R-1..R-6); iteration 3 returned ALL CLEAR. Plan revisions (in place, single canonical plan path, no sibling files; 97 tasks across 7 phases unchanged, no task added, removed or renumbered): - P3-T13 no longer contradicts P3-T7: the enforced repository-wide line floor is named as LineFloorPercent, with NewCodeFloorPercent and BranchGated enumerated separately as policy constants. - P0-T11, P4-T15 and P6-T4 authorize the Invoke-Pester fallback when the PoshQC MCP test tool fails, not only when it is absent. - P6-T2 is baseline-relative (zero new diagnostics) instead of demanding an absolute clean exit against a baseline that anticipates existing debt. - P5-T7..P5-T16 restrict AC check-off to the marker only, preserving criterion text byte-for-byte per acceptance-criteria-tracking. - P5-T1 records PROMOTION MCP UNAVAILABLE and continues instead of halting mid-plan; the branch is now propagated to P5-T2, P5-T3, P5-T4, P5-T16, P5-T17 and P6-T7, each with a satisfiable outcome. - P6-T9 commits .claude/agent-memory/** as a permitted-incidental path, making its clean-tree acceptance reachable. - P0-T4 spans, P0-T7 numeral pattern, P4-T8 authority anchor, P4-T1 fixture acceptance and the P0-T5 512-region bound corrected. spec.md: Risk 2 disposition (a) names the existing svgcontrol-coverage- uplift potential entry as the remediation route, resolving a contradiction with the Rollout section. The AC1-AC10 block remains byte-identical to issue.md. Preparation only. Atomic execution, PR authoring and CI monitoring are out of scope and are performed later by epic-orchestrator. Refs #494 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…#494 preparation Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Preparation-mode fan-in. Issue #494. Wave 2. PREFLIGHT: ALL CLEAR (3 iterations). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Three preflight rounds against plan.2026-08-10T14-07.md, ending PREFLIGHT: ALL CLEAR. Two blocking defect classes were found and corrected in place. Round 1 - StrictMode bare property read on `branch`. The test file sets Set-StrictMode -Version Latest, which propagates into the dot-sourced production functions, and Helpers.ps1:128 reads $line.branch by bare property access. Fixtures specified without a `branch` attribute would have thrown PropertyNotFoundStrict instead of producing P1-T7's pinned fail-before values, leaving the message half of that gate unsatisfiable while the count half still passed. Round 2 - the same class on `complexity` (Helpers.ps1:277-281 sums $_.complexity bare, reached only by the merge-path fixtures F3 and F6), plus a self-contradictory AC-16: it required every evidence artifact to carry EXIT_CODE while P7-T20 states the AC status summary records no command and carries none. AC-16 is now scoped to command-step artifacts, with narrative artifacts required to carry Timestamp and be enumerated in the final sweep. Round 3 - clear. The defect class was enumerated to exhaustion rather than patched per attribute: of the bare reads in Helpers.ps1, exactly four throw when absent (hits, branch, number, complexity); branch and complexity are now mandated by the plan and hits and number are already pinned by each fixture task's own text. The two `name` reads are safe via the .NET XmlNode.Name fallback. Read-only Pester probes reproduced all four pinned fail-before values (F1 6/4, F2 4/2, F3 '0.75', F4 3/2). Preparation only. Atomic execution is deferred to epic-orchestrator. Refs #441, #478 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Captures the defect class surfaced during #441 preflight: under Set-StrictMode -Version Latest, bare property access on an absent XML attribute throws rather than returning $null, so Cobertura fixtures must carry every attribute the production code reads by bare access. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
The orchestrator-state validator requires step, agent_id, skill_source, started_at, result_signal and artifact_paths on every delegation receipt; omitting them produced 24 errors across 4 receipts on the #441 resume. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Preparation-mode fan-in. Issues #441 and #478. Wave 0. PREFLIGHT: ALL CLEAR (3 iterations). Conflicts were confined to .claude/agent-memory/** shared notes written by sibling preparation children; no feature-folder file conflicted, confirming the decomposition produces disjoint deliverable trees. Resolved as a union: both index entries kept, and the two full-bug/user-story exception notes (#494 epic-prep route, #441 cross-reference instruction) merged into one note rather than discarding either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
…ight Apply three rounds of preflight revision deltas to the atomic plan and reach PREFLIGHT: ALL CLEAR at iteration 4. All revisions were applied in place on the canonical plan path; no timestamped sibling plan file was created. No task was added, removed, renumbered or reordered — the diff is acceptance text only (16 insertions, 16 deletions). Round 1 (7 items): - [P0-T15]/[P2-T8]/[P6-T2]: replace the "identical rule/file/line table" PoshQC-analyze gate with an identical count plus an identical (Severity, Rule, File) multiset, and require a per-finding line-number reconciliation. Phase 2 inserts lines above all three findings in Invoke-VSBuild.ps1 (47, 78, 137), so the old gate could not be satisfied by a correct implementation. - [P6-T4]: allow the branch-coverage obligation to be discharged by a recorded structural-unavailability statement. Pester 5.6.1 exposes no branch counter, so the old wording forced remediation-required on every execution. - [P0-T16]/[P6-T4]/[P7-T1]: add a PREEXISTING_COVERAGE_SHORTFALL branch for a sub-85% baseline. The floor is not lowered; an unmet floor is recorded as an unresolved blocking condition and folded into the follow-up entry. - [P7-T13]: scope the AC11 field assertion to artifacts produced by this plan; the nine pre-existing planning artifacts predate the fail-closed field convention and are not edited. - [P1-T3]/[P2-T7]: add a dual-channel test enumeration with an MCP_DETAIL_UNAVAILABLE fallback. - [P3-T4]: correct two locator strings that were not verbatim. - [P5-T8]/[P5-T9]/[P5-T10]: specify the transcript capture as 2>&1 | Tee-Object, which was named but never defined. Round 2 (5 items): propagate the branch-coverage discharge to [P6-T3]; remove the unmeasured MCP numeric-count assertions from [P0-T16] and [P6-T3]; reconcile [P6-T2]'s line-number-stability clause with [P6-T1]'s express permission for the formatter to rewrite the same file; replace [P7-T13]'s Phase-0-only re-capture requirement with a NO_RECAPTURE branch; re-quote the [P0-T16] Pester payload so a parent shell cannot interpolate $c. Round 3 (1 item): [P1-T3] no longer rests its [expect-fail] proof on a process exit code. Pester 5.6.1 defaults Run.Exit and Run.Throw to False, so the direct channel exits 0 even when It blocks fail. The discriminating red-state proof is now the enumerated failing It names and their verbatim failure messages. Preparation mode: promotion, research, feature documents, atomic planning and preflight clearance only. Atomic execution, PR authoring and CI monitoring are deferred to epic-orchestrator. Refs #492, #509, #512, #522 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three durable findings surfaced by the atomic-executor preflight gate while revising the csharp-toolchain-gate-fidelity plan: - Invoke-Pester does not exit non-zero on failure. Pester 5.6.1 defaults Run.Exit and Run.Throw to False, so a plan that rests an [expect-fail] gate on the process exit code proves nothing; the enumerated failing It names are the discriminating signal. - A pwsh -Command payload containing $-prefixed variables must be single-quoted, or the parent shell interpolates it before pwsh receives it. - When a task grants a discharge for an unobtainable measurement, the grant must be threaded through every downstream consumer of that measurement, or the consumer's precondition becomes unreachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nstraint enforce-model-routing-receipt.ps1 hardcodes the canonical artifacts/orchestration/orchestrator-state.json path, so a child-scoped checkpoint alone denies every gated delegation with MODEL_ROUTING_RECEIPT_BLOCKED. Records the mirroring workaround and when it is safe, since the child-scoped-path guidance and this hook's hardcoded path are in direct tension. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Preparation-mode fan-in. Issues #492, #509, #512 and #522. Wave 0. PREFLIGHT: ALL CLEAR (4 iterations). Conflicts again confined to .claude/agent-memory/** sibling notes; no feature-folder file conflicted. Resolved as a union. The full-bug/user-story note now carries all three observed exceptions (#494 epic-prep route, #441 cross-reference instruction, #512 audience-context request) under one shared principle: the protected rule is a single AC source file, not the absence of user-story.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xz7TQ7Xqc8jFsddSohGzKG
Feature review of bug/csharp-toolchain-gate-fidelity-512 against the epic integration branch. Zero blocking findings; all 13 acceptance criteria verified against evidence rather than accepted from the executor's check-off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ity-512 fix(toolchain): make the documented C# gates execute truthfully
Three updates following the delivery of issue #512 (PR #540): - Mark the "CLAUDE.md nullable command diverges from ci.yml" memory RESOLVED. The documented C# commands now match ci.yml, so the old advice ("reproduce ci.yml's command before accepting a nullable blocker") no longer describes a live divergence. A future appearance of `/p:Nullable=enable` or `/t:Build` in a documented command is now a regression of #512/#522, and the memory says so. Also records the measured 195-error UtilitiesCS figure, with its lower-bound qualification, for the #492 burn-down. - Add a memory for the untracked `coverage.xml` that PoshQC test runs drop at the repository root. It is in neither .gitignore nor .csharpierignore, so it inflates the CSharpier file count between two otherwise-identical runs and can be swept into a diff by `git add -A`. - Correct the analyzer-vacuity memory: the non-vacuity assertion must be a zero `Skipping target "CoreCompile"` count, not a csc.exe count. csc.exe occurrences are zero at verbosity=normal even for genuine compiles, so the previously recorded csc-count acceptance would have been unsatisfiable. The index was compacted concurrently by a sibling; this commit takes the sibling's compaction as the base and applies only the three deltas above. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chore(memory): record #512 toolchain-gate outcomes for the orchestrator
…g the final gate 512 merged via PR #540 (22eaee8), worktree removed. Wave 0 complete. Records that the integrated-tree workflow_dispatch run failed on a single intermittent test that also fails on main at this branch's base commit, with the same 6435 total, and characterizes its wall-clock race mechanism. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…minator A method-level [ExcludeFromCodeCoverage] does not suppress lambdas declared inside the attributed member: the compiler hoists them into a closure type that does not inherit the attribute, so their lines stay in the Cobertura denominator. Files adopting the repository's thin exempt production forwarder seam therefore carried a permanent, invisible coverage ceiling. Add scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1, a pure XML-to-XML transform that drops closure-class coverage whose declaring member is absent from the package's instrumented-member presence set. The presence set admits Type.<Member>d__<N> state-machine class names, so lambdas inside non-exempt async members are retained. An unrecognized compiler-generated name shape causes retention, never removal: over-exclusion is not an acceptable failure mode. Wire it into ConvertTo-KoverageCoberturaXml after path normalization and before Merge-CoberturaClassesByFilename. The ordering is a constraint, not a preference: a closure type shares its declaring type's filename, so the merge collapses it into a node named for the declaring type that carries neither the .<>c marker nor the <Member>b__ methods the filter resolves against. Placed after the merge the filter is a no-op, which regression case 6 pins end to end. Measured against the post-#441 arithmetic, repository lines-valid falls 62873 -> 62401 and the line rate rises 85.3514% -> 85.5355%. QuickFiler/Viewers/BreadcrumbPopupUiOperations.cs goes 90.70% -> 99.15%, and TaskVisualization/FlagTasks.cs leaves the report entirely because every member of the type is attributed. No coverage threshold is changed; threshold reconciliation is owned by issue #494. Three residuals are documented and handed off as potential entries rather than absorbed: lambdas inside exempt async members, local functions, and overload-name collisions. Closes #457 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…dings Feature review returned zero blocking findings. The reviewer verified the ordering constraint substantively rather than from evidence prose: applying merge-then-filter in a scratch probe leaves the exempt line in place and makes regression case 6 fail, confirming the test genuinely pins the constraint rather than passing vacuously. Promote the two non-blocking code-review findings to potential entries so they survive the merge rather than living only in a feature-folder artifact: - CR-1: the filter's retained-line rebuild duplicates the merge's line-map loop while omitting stale condition-coverage removal and <conditions> copying. The duplication was forced -- the helpers module sat at 455 of 500 lines and spec AC 13 fixed its change surface at exactly two added lines, so extracting a shared helper was unavailable to #457. - CR-3: SupportsShouldProcess on a pure in-memory transform means a session $WhatIfPreference of $true skips filtering silently, emitting a plausible but unfiltered denominator. The attribute was adopted because PSScriptAnalyzer raises PSUseShouldProcessForStateChangingFunctions against a bare Remove- verb and the analyze gate exits non-zero on a warning. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ted-lambdas-457 fix(coverage): exclude nested lambdas of exempt members from the denominator
- The Bash tool refuses compound commands it cannot statically prove stay inside an isolated agent worktree. The refusal is about verifiability, not an actual escape, and it lands squarely on the PR gate because synthesizing pr_context.summary.txt by hand has exactly the rejected shape. - #457's denominator fix raised the repository line rate rather than lowering it, because the removed lambda lines included covered ones. The epic kickoff quoted 85.0317% where measurement on the same tip gave 85.3514%, so #494 must re-measure rather than inherit the figure it is blocked on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ted-lambdas-457 chore(memory): record two orchestrator lessons from the #457 child run
…ited flaky test workflow_dispatch run 31493339489 at c7d398c passes the full CI suite across all four merged features - the first green full-CI signal for this epic's work, since integration-base child PRs are ineligible for the workflow. Dispositions the TimeoutAfter test previously recorded as a final-gate blocker: four samples on unchanged test code (two on main, two on the integration branch) establish it as intermittent and main-originated, not introduced by this epic. The underlying wall-clock determinism defect remains open and out of scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Capture three corrected-arithmetic coverage runs with 6,435 tests passing in each run - Record the absent upstream release receipt and leave all ten acceptance criteria unresolved - Add policy, code, and feature audits with a scoped remediation plan Refs: #494
- Add canonical policy, code, and feature review artifacts - Capture prompt-contract and receipt-gate evidence for the absent upstream release validation receipt - Complete Phase 0 and Phase 1 checklist items and halt before receipt-dependent Phase 2 Refs: #494
- Establish spec.md acceptance criteria as the sole check-off source - Document local-only scope, protected-path classification, and historical scenarios - Record scoped validation evidence for the remediation plan Refs: #494
- Mark the approved zero-delta analyzer baseline as no-regression evidence - Record PASS readiness and defer exact-head CI as post-review work Refs: #494
…econciliation-494 Enforce the 80% Cobertura line-coverage gate
…tion Brings in the CI parallel job split (#553, PR #556) and the TimeOutTask changes. Three Markdown conflicts resolved: - .claude/rules/csharp.md: both branches independently made the same toolchain correction (drop /p:Nullable=enable, use /t:Rebuild /m, csharpier format/check subcommands). Kept the integration branch's structured form plus main's dotnet tool restore note, and pointed each /t:Rebuild rationale at its actual post-split workflow (_build-analyzers.yml uses /t:Build /m; _build-nullable.yml uses /t:Rebuild /m). - .claude/skills/csharp-qa-gate/SKILL.md: took main (strict superset, adds dotnet tool restore). - .claude/agent-memory/feature-review/MEMORY.md: kept the integration branch's compacted index and appended main's single new #553 entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wave 2 closed: feature 494 merged via PR #551 (merge commit 85ff0c3) on 2026-08-15T02:27Z. The projection had it at worktree_created. Also corrects the "Issues Closed" claim. All nine issues are still open on GitHub because every child PR targeted the integration branch, and GitHub auto-closes only on merge into the default branch. Renamed the section to "Issues - Pending Closure" and recorded that the integration PR body carries the closing keywords. Records that the tree has moved past the last green workflow_dispatch run (31493339489 @ c7d398c): feature 494 and the main merge fb8eff9 (CI parallel job split #553/PR #556) are both uncovered by it, and the job split renamed the check runs. Regenerated from git and gh; artifacts/orchestration/epic-orchestrator-state.json is absent from this worktree and the commands are authoritative per the cache doctrine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Discharges feature 457's AC15, recorded as owed by the epic orchestrator at epic close (feature-audit.2026-08-11T01-33.md item 1). The three documented residuals of the closure filter are now tracked issues rather than draft potential entries: - #558 exempt-async-member-lambdas-remain-counted - #559 local-functions-in-exempt-members-remain-counted - #560 overload-name-collision-under-exclusion Each entry moves to docs/features/potential/promoted/ per the promotion lifecycle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two independent feature-review passes and one epic-review pass were run against the composed 404-file integration diff (base main, merge-base 0569ac0, head 22b5de0). Neither of the two things this review existed to cover had been reviewed before: the composed diff as a whole, and the hand-resolved Markdown conflicts in the main merge at fb8eff9. Both feature-review passes returned zero Blocking findings, and no remediation-inputs artifact was written. Both independently confirmed the fb8eff9 conflict resolutions dropped no incoming hunk from main. The five Major findings were filed as tracked issues rather than remediated inside the fan-in: #563 (coverage threshold contradiction unreconciled), #564 (CLAUDE.md cites ci.yml for relocated commands), #565 (threshold asserted before Set-Content). Orchestrator-side verification additionally filed #561 (CI enforces no coverage threshold) and #562 (no Pester job in CI, so this epic's PowerShell core logic has zero CI coverage). Also records the local Pester run on the integrated tree, 70/70 passing, which is the only executed gate covering the epic's core logic. Artifacts are written to the epic folder and mirrored under the 494 child folder because validate-feature-review-coverage.ps1 hard-codes a docs/features/active/ path regex and cannot advertise an epic path. Co-Authored-By: Claude Opus 5 (1M context) <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.
Epic: make the C# build, CI, and coverage gates report the truth
Fan-in of the
build-ci-coverage-gate-fidelityepic: five child features across three dependency waves, merged intoepic/build-ci-coverage-gate-fidelity-integrationand now proposed tomain.Summary
Invoke-MSTestWithCoverage.Helpers.ps1recomputed the Cobertura root attributes over the XPath descendant axis, counting every source line twice — once under<class>/<methods>/<method>/<lines>and once in the class-level<class>/<lines>rollup. A new pure helper reduces a class to a deduplicated per-line map and is called from both paths (Cobertura post-processing double-counts <line> nodes, inflating lines-valid and every coverage rate #441, Bug: merge-cobertura-classes-blends-union-with-primary-methods #478).CoreCompileon all 18 projects. Every prior "nullable gate passed" claim overstated what was verified (Bug: nullable-gate-cannot-fail-incremental-build #512, Bug: nullable-gate-masked-by-incremental-build #492, Bug: csharpier-documented-command-incompatible-with-pinned-version #509, Bug: claudemd-nullable-gate-diverges-from-ci #522).[ExcludeFromCodeCoverage]now suppresses nested lambdas. The compiler hoists lambdas out of an attributed member into a closure type that does not inherit the attribute, so their lines stayed in the denominator permanently uncovered. A post-processing closure filter removes closure-type coverage whose declaring member is absent from the instrumented method set (Bug: excludefromcodecoverage-does-not-suppress-nested-lambdas #457).Assert-CoberturaLineCoverageThresholdfails closed at 80% line coverage when the Cobertura artifact is missing or below the floor (Bug: conflicting-coverage-thresholds-across-policy-docs #494). The prose reconciliation that Bug: conflicting-coverage-thresholds-across-policy-docs #494 also set out to perform did not land; see Known gaps.UtilitiesCS.Test.csprojlistedPercentageFormatterTests.cstwice in the same<Compile>item group, warning on every local and CI build (Bug: utilitiescs-test-cs2002-duplicate-compile-entry #394).Why
These five defects share one failure mode: a gate that reports success without having measured anything. A skipped
CoreCompilereturns exit 0. A doubled line count inflates every coverage rate. A closure type silently pads the denominator. A contradictory threshold makes the gate unresolvable, so it gets improvised or skipped.The cost is compounding: every green result recorded while these were live is weaker evidence than it appears, which is why feature 512's user story is written as "a green result means my change is clean rather than that the command did nothing."
What Changed
Non-documentation changes (13 files)
scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1scripts/vscode/Invoke-VSBuild.ps1scripts/vscode/Invoke-MSTestWithCoverage.ps1UtilitiesCS.Test/UtilitiesCS.Test.csproj<Compile Include>removed (#394)tests/scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.Tests.ps1tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1tests/scripts/vscode/Invoke-VSBuild.Tests.ps1tests/scripts/vscode/Invoke-MSTest.RunSettings.Tests.ps1CLAUDE.md.claude/rules/csharp.md.claude/skills/csharp-qa-gate/SKILL.md.vscode/tasks.jsonNo
.cssource file changes in this PR.Docs and evidence (391 files) — per-feature folders under
docs/features/active/(issue, spec, user story, plan, research, evidence, review artifacts), 55 agent-memory files, and the epic folder. Six committed Cobertura evidence documents account for roughly 1.1M added lines on their own.Architecture / How It Fits Together
The coverage pipeline is a chain of pure XML-to-XML transforms over a document the pipeline already parses:
Get-CoberturaClassLineSummaryis the single deduplication point: both the merge path and the root-attribute recomputation call it, which is what removes the double count rather than patching each site.Verification
Completed
feature-reviewpasses against basemainat merge-base0569ac0b, head22b5de02, each executing the full review-workflow contract. Both returned zero Blocking findings. Artifacts:docs/features/epics/build-ci-coverage-gate-fidelity/{policy-audit,code-review,feature-audit}.2026-08-15T05-11.mdand...2026-08-15T05-25.md.mainmerge atfb8eff9bhand-resolved three Markdown conflicts. Both review passes diffed all three files against both merge parents and confirmed no incoming hunk frommainwas dropped.full-bug, sospec.mdis the sole AC source. All 67 acceptance criteria across the five specs are checked off and evaluate PASS (two Bug: conflicting-coverage-thresholds-across-policy-docs #494 criteria PASS-with-qualification). An independentepic-reviewpass found no AC whose check-off state is unsupported by evidence.docs/features/epics/build-ci-coverage-gate-fidelity/evidence/qa-gates/pester-integrated-tree.2026-08-15T05-10.md.evidence/tree — fail-before/pass-after regression captures, Pester runs, PoshQC format and analyze, and per-feature coverage deltas.Not previously covered — the last green integrated-tree run (
workflow_dispatch31493339489 atc7d398c2) predates both feature 494 (85ff0c34) and themainmerge (fb8eff9b). This PR's own CI is the first full-suite signal covering the current tree, and it is eligible because its base ismain— child PRs based on the integration branch were not, sinceci.ymltriggerspull_requestonly on[main, development].Recommended
Known gaps merging with this PR
These were found by the fan-in review, are filed as issues, and are not resolved here. They are recorded rather than absorbed, because remediating them inside a fan-in would widen its scope.
CLAUDE.mdand.claude/rules/csharp.mdstate 80%;.claude/rules/general-unit-test.mdand.claude/rules/quality-tiers.mdstate 85% line / 75% branch. #494's scope correction deferred the prose edits upstream. The newAssert-CoberturaLineCoverageThresholdenforces 80% while.claude/hooks/validate-feature-review-coverage.ps1fails below 85 — a figure in [80, 85) passes one live gate and fails the other.CLAUDE.mdcites.github/workflows/ci.ymlfor three toolchain commands that the #553 split relocated into_format-check.yml,_build-analyzers.yml, and_build-nullable.yml. The same fix was applied to.claude/rules/csharp.mdbut not toCLAUDE.md.Invoke-MSTestWithCoverage.ps1asserts the threshold beforeSet-Content, so a failing gate discards the post-processed document and leaves the raw un-post-processed Cobertura on disk._mstest-coverage.ymlnever converts to Cobertura or compares a floor, so a coverage regression cannot fail CI.Repository-wide PowerShell line coverage measures roughly 69-72% against the 85% floor. The reviewer dispositioned this non-blocking: the entire shortfall sits in five never-tested scripts absent from this diff, changed-line coverage is 100%, and the branch raises the figure from roughly 67.2%.
Backward Compatibility / Migration Notes
dotnet tool run csharpier .becomesdotnet tool run csharpier format ./check ., and the msbuild gates move from/t:Buildto/t:Rebuild /m./p:Nullable=enableis removed — no project carries a<Nullable>element, so it conscripted every un-annotated file and made the gate unpassable. Local runs are slower because they now genuinely recompile.Risks and Mitigations
<>cexclusion; both retention and removal cases are covered by the 443-line Pester suite. Three known residual gaps are tracked, not absorbed — see Follow-ups./t:Rebuildgates surface pre-existing debt as new failuresRollback: revert the merge commit. The coverage post-processing is a pure transform with no persisted state, so reverting restores prior behavior and prior numbers together.
Review Guide
Suggested order — the first four files carry essentially all the behavior:
scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1— the deduplicated line map (Cobertura post-processing double-counts <line> nodes, inflating lines-valid and every coverage rate #441) and the threshold assertion (Bug: conflicting-coverage-thresholds-across-policy-docs #494)scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1— the closure filter and its name-derivation regexes (Bug: excludefromcodecoverage-does-not-suppress-nested-lambdas #457)scripts/vscode/Invoke-VSBuild.ps1andInvoke-MSTestWithCoverage.ps1— invocation fidelity and wiringUtilitiesCS.Test/UtilitiesCS.Test.csproj— one-line deletion (Bug: utilitiescs-test-cs2002-duplicate-compile-entry #394)CLAUDE.mdand.claude/rules/csharp.md— the corrected toolchain commandstests/scripts/vscode/Low-signal, high-volume:
docs/features/active/**/evidence/**. The six*.cobertura.xmlcaptures under feature 494 are ~187,786 lines each and are committed as evidence, not as reviewable source.Follow-ups
async/iterator members), Bug: local-functions-in-exempt-members-remain-counted #559 (local functions in exempt members), Bug: overload-name-collision-under-exclusion #560 (overload name collision under exclusion).closure-filter-rebuild-drifts-from-merge-line-mapandclosure-filter-whatifpreference-silently-skips.tests/scripts/vscode/Invoke-VSBuild.Tests.ps1dot-sources the script's whole top-level body inBeforeAll, so a test run executesvswhere.exeandSync-PackageReferences.ps1, which writes.csprojfiles. This makes measured PowerShell coverage non-deterministic and needs an injectable seam before Pester can run on a clean runner (noted in No Pester job in CI: production PowerShell under scripts/vscode has zero CI coverage #562).TimeoutAfter_GenericTask_ShouldPropagateFaultedSourceException_WhenSourceFaultsLateris a confirmed intermittent test inherited frommain, not introduced here — it races a 100 ms wall-clock timeout against fault propagation, violating the determinism rules in.claude/rules/general-unit-test.md. Three sibling tests share the pattern. If CI trips it, re-run; do not adjust a threshold or gate.drm-copilotrepository. The PR-context collector also silently drops all 55.claude/agent-memory/**files from its classification (344 of 404 files accounted for), which is a further data point for that issue.GitHub Auto-close