Skip to content

Epic: make the C# build, CI, and coverage gates report the truth - #566

Merged
drmoisan merged 81 commits into
mainfrom
epic/build-ci-coverage-gate-fidelity-integration
Aug 15, 2026
Merged

Epic: make the C# build, CI, and coverage gates report the truth#566
drmoisan merged 81 commits into
mainfrom
epic/build-ci-coverage-gate-fidelity-integration

Conversation

@drmoisan

Copy link
Copy Markdown
Owner

Epic: make the C# build, CI, and coverage gates report the truth

Fan-in of the build-ci-coverage-gate-fidelity epic: five child features across three dependency waves, merged into epic/build-ci-coverage-gate-fidelity-integration and now proposed to main.

Summary

Why

These five defects share one failure mode: a gate that reports success without having measured anything. A skipped CoreCompile returns 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)

File Change
scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1 +389 / -0 — new closure-type coverage filter (#457)
scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 +151 / -17 — deduplicated line-map helper shared by both count paths, plus the threshold assertion (#441, #478, #494)
scripts/vscode/Invoke-VSBuild.ps1 +13 / -3 — build-invocation fidelity (#512)
scripts/vscode/Invoke-MSTestWithCoverage.ps1 +1 / -0 — filter wiring
UtilitiesCS.Test/UtilitiesCS.Test.csproj -1 — duplicate <Compile Include> removed (#394)
tests/scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.Tests.ps1 +443 / -0
tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +276 / -0
tests/scripts/vscode/Invoke-VSBuild.Tests.ps1 +23 / -2
tests/scripts/vscode/Invoke-MSTest.RunSettings.Tests.ps1 +9 / -1
CLAUDE.md +19 / -13 — corrected C# toolchain commands (#512)
.claude/rules/csharp.md +9 / -4 — same corrections, plus reusable-workflow references
.claude/skills/csharp-qa-gate/SKILL.md +2 / -0
.vscode/tasks.json +4 / -1 — task wiring for the corrected commands

No .cs source 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:

dotnet-coverage  ->  raw Cobertura
                     |
                     +-- Merge-CoberturaClassesByFilename       (unions same-filename classes)
                     +-- Remove-CoberturaExemptClosureCoverage  (#457 closure filter)
                     +-- Get-CoberturaClassLineSummary          (#441 dedup line map, shared)
                     |
                     v
                  post-processed Cobertura  ->  Assert-CoberturaLineCoverageThreshold (#494)

Get-CoberturaClassLineSummary is 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

  • Composed-diff review. Two independent feature-review passes against base main at merge-base 0569ac0b, head 22b5de02, 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.md and ...2026-08-15T05-25.md.
  • Merge-conflict resolutions audited. The main merge at fb8eff9b hand-resolved three Markdown conflicts. Both review passes diffed all three files against both merge parents and confirmed no incoming hunk from main was dropped.
  • Acceptance criteria. All five children resolve to work-mode full-bug, so spec.md is 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 independent epic-review pass found no AC whose check-off state is unsupported by evidence.
  • Pester suite on this exact head: 70 of 70 passed, 0 failed, across all five containers. Evidence: docs/features/epics/build-ci-coverage-gate-fidelity/evidence/qa-gates/pester-integrated-tree.2026-08-15T05-10.md.
  • Per-feature evidence is committed under each feature folder's 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_dispatch 31493339489 at c7d398c2) predates both feature 494 (85ff0c34) and the main merge (fb8eff9b). This PR's own CI is the first full-suite signal covering the current tree, and it is eligible because its base is main — child PRs based on the integration branch were not, since ci.yml triggers pull_request only on [main, development].

Recommended

# PowerShell suite — all core logic in this PR is PowerShell, and no CI job runs it (see #562)
pwsh -NoProfile -Command "Invoke-Pester tests/scripts/vscode -Output Detailed"

# C# toolchain, using the commands this epic corrected
dotnet tool restore
dotnet tool run csharpier check .
msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true
msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true

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.

Gap Issue
The coverage-threshold contradiction was not removed. CLAUDE.md and .claude/rules/csharp.md state 80%; .claude/rules/general-unit-test.md and .claude/rules/quality-tiers.md state 85% line / 75% branch. #494's scope correction deferred the prose edits upstream. The new Assert-CoberturaLineCoverageThreshold enforces 80% while .claude/hooks/validate-feature-review-coverage.ps1 fails below 85 — a figure in [80, 85) passes one live gate and fails the other. #563
CLAUDE.md cites .github/workflows/ci.yml for 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.md but not to CLAUDE.md. #564
Invoke-MSTestWithCoverage.ps1 asserts the threshold before Set-Content, so a failing gate discards the post-processed document and leaves the raw un-post-processed Cobertura on disk. #565
CI collects coverage but enforces no threshold: _mstest-coverage.yml never converts to Cobertura or compares a floor, so a coverage regression cannot fail CI. #561
No Pester job exists in CI, so the four PowerShell scripts that constitute this epic's core logic have zero CI coverage. #562

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

  • Coverage numbers move. Removing the double count and the exempt-closure lines changes reported percentages for every project. Post-change figures are not comparable to any pre-change baseline; re-baseline rather than treating the shift as a regression.
  • Documented toolchain commands changed. dotnet tool run csharpier . becomes dotnet tool run csharpier format . / check ., and the msbuild gates move from /t:Build to /t:Rebuild /m. /p:Nullable=enable is 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.
  • No public API changes. No renamed or removed source paths outside the feature-doc tree.

Risks and Mitigations

Risk Mitigation
The closure filter over-matches and drops legitimate coverage The rule keys on the declaring member being absent from the instrumented method set, not on a blanket <>c exclusion; both retention and removal cases are covered by the 443-line Pester suite. Three known residual gaps are tracked, not absorbed — see Follow-ups.
Corrected /t:Rebuild gates surface pre-existing debt as new failures Reproduced and dispositioned during feature 512; the gates were previously incapable of failing, so anything they now report is pre-existing rather than introduced here.
Large diff obscures a substantive change 13 of 404 files are non-documentation. See the Review Guide.
Two enforcement points disagree on the coverage floor Tracked as #563. Both gates fail closed, so the disagreement produces a spurious failure rather than a spurious pass.

Rollback: 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:

  1. 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)
  2. scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1 — the closure filter and its name-derivation regexes (Bug: excludefromcodecoverage-does-not-suppress-nested-lambdas #457)
  3. scripts/vscode/Invoke-VSBuild.ps1 and Invoke-MSTestWithCoverage.ps1 — invocation fidelity and wiring
  4. UtilitiesCS.Test/UtilitiesCS.Test.csproj — one-line deletion (Bug: utilitiescs-test-cs2002-duplicate-compile-entry #394)
  5. CLAUDE.md and .claude/rules/csharp.md — the corrected toolchain commands
  6. Tests under tests/scripts/vscode/

Low-signal, high-volume: docs/features/active/**/evidence/**. The six *.cobertura.xml captures under feature 494 are ~187,786 lines each and are committed as evidence, not as reviewable source.

Follow-ups

GitHub Auto-close

drmoisan and others added 30 commits August 10, 2026 14:04
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
drmoisan and others added 28 commits August 10, 2026 23:56
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>
441 merged via PR #538 (fb257cd), worktree removed. 457 launched: its only
dependency edge is 441, which is durably confirmed merged, so wave 1 opens
while sibling 512 remains in flight in wave 0.

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
457 merged via PR #542 (ee082ba), worktree removed. Corrected line-rate rose
from 0.853514 to 0.855355 because removed lambda lines included covered ones.
494 launched as the final feature.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…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 Phase 0 policy-read and baseline git-state evidence for issue #494
- Record executor and planner memory for #511 crash handling and Pester/MCP coverage-tool seams
- Revise the feature 494 plan to replace unsatisfiable gates and stale span assumptions

Refs: #494
- 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
- Record code, feature, and policy audit evidence for issue #494
- Document passing acceptance and Pester results with the analyzer gate finding

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
- Record that the approved zero-delta baseline requires no issue #494 action
- Preserve separate tracking for pre-existing analyzer-warning cleanup

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>
@drmoisan
drmoisan merged commit 8bd2355 into main Aug 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment