diff --git a/.claude/agent-memory/feature-review/MEMORY.md b/.claude/agent-memory/feature-review/MEMORY.md index ba2503431..45be2292d 100644 --- a/.claude/agent-memory/feature-review/MEMORY.md +++ b/.claude/agent-memory/feature-review/MEMORY.md @@ -1,64 +1,56 @@ -- [nullable-remediation-epic-review-pattern](project_nullable-remediation-epic-review-pattern.md) — recurring checks for the utilitiescs-nullable-remediation epic's 12 Wave children (#363+): epic-integration diff base, per-file #nullable enable, net481 constraints, solution-wide gate fails on pre-existing non-nullable warnings (use per-project AC1 proof) -- [gitignore-tracking-expands-diff-scope](project_gitignore-tracking-diff-scope.md) — un-ignoring `.claude/` makes the whole subtree show as added in the branch diff; audit scope must cover those files, not the plan's "single file" claim -- [powershell-coverage-mandatory-when-ps1-in-diff](feedback_powershell-coverage-gate.md) — validate-feature-review-coverage hook blocks termination without an explicit PASS/FAIL PowerShell coverage verdict when .ps1 files changed -- [pr-context-summary-misclassifies-cs](project_pr-context-summary-misclassifies-cs.md) — PR-context summary overview can mislabel C# changes as docs ("0 core logic files"); always verify scope against git diff -- [coverage-hook-trusts-misclassified-summary](project_coverage-hook-trusts-misclassified-summary.md) — the coverage hook reads changed languages from the summary overview, not git diff; a C#-as-docs misclassification makes it silently skip C# coverage enforcement -- [csharp-coverage-artifact-is-cobertura](project_csharp-coverage-artifact-is-cobertura.md) — `artifacts/csharp/coverage.xml` format VARIES (Cobertura / VS .coverage / JaCoCo); read the root element first — `` means the hook works and sub-85/sub-75 enforcement is live -- [csharpier-formats-xml-probe-verification](project_csharpier-formats-xml-probe-verification.md) — CSharpier 1.3.0 formats `*.xml` (no `.csharpierrc` so width=100, `.csharpierignore` lacks `*.xml`); reproduce a "formatter mandated this" escalation with a scratch probe file rather than accepting it -- [jacoco-summary-substitution-is-valid-coverage-evidence](project_jacoco-summary-substitution-is-valid-coverage-evidence.md) — since d0955dc4, committed evidence is package-level JaCoCo not raw Cobertura; re-sum counters, corroborate per-class figures arithmetically, distrust sub-noise deltas -- [csharp-local-fullsuite-coverage-blocked](project_csharp-local-fullsuite-coverage-blocked.md) — local full-assembly C# coverage fails on a Moq binding redirect; per-feature Cobertura is trimmed to affected classes (no repo-wide root), so repo-wide gate is the PR CI run -- [csharp-repowide-coverage-below-80](project_csharp-repowide-coverage-below-80.md) — raw all-DLL Cobertura merges read ~59-69% (vendor-inflated denominator); canonical first-party figures (Koverage ~79.4%, or manual by-name package exclusion ~91.22% per #253) both clear 80%/85% -- [feature-audit check-off heading case](feature-audit-checkoff-heading-case.md) — validator needs `## Acceptance Criteria Check-off` (lowercase off); template ships `Check-Off` and fails -- [feature-audit requires Summary heading](feature-audit-requires-summary-heading.md) — validator also needs a literal `## Summary`; `## Verdict` alone fails -- [policy-audit required structure](policy-audit-required-structure.md) — validator needs Appendix A heading, all 4 TS/PS coverage checklist lines, and a numeric Baseline/Post-change/Disposition comparison line -- [code-review findings table header](code-review-findings-table-header.md) — code-review validator requires the exact 7-column `| Severity | File | Location | Finding | Recommendation | Rationale | Evidence |` header -- [code-review/feature-audit required headings](code-review-required-headings.md) — code-review needs `## Executive Summary` + `## Findings Table`; feature-audit needs `## Acceptance Criteria Inventory` + `## Acceptance Criteria Evaluation` (exact text) -- [policy-audit comparison-line schema](policy-audit-comparison-line-schema.md) — per-language bullets under `### 1.2.1` need labelled `Baseline:` `Post-change:` `Change:` `Disposition:` `Evidence:` (and `New/changed-code coverage:` when row is non-N/A) -- [policy-audit validator uses full template](policy-audit-validator-uses-full-template.md) — THIS repo's validator needs the full bundled template headings (`## Executive Summary` + `## 1`..`## 7`); resolve the MCP template asset and fill it in -- [PowerShell line-count undercount](powershell-measure-object-line-undercount.md) — `(Get-Content | Measure-Object -Line).Lines` undercounts vs `awk NR`/`wc -l` -- [policy-audit section-7 row-label parser](policy-audit-section7-row-label-parser.md) — validator misreads non-standard section-7 Code-Quality row labels as coverage rows; reuse cycle-1 wording, no comma qualifiers in label cell -- [numeric new/changed-code coverage + Scope-and-Baseline](policy-audit-numeric-new-code-coverage.md) — policy-audit new-code-coverage field must be a literal percent (prose fails); feature-audit needs a `## Scope and Baseline` heading -- [test files count toward 500-line limit](feedback_test-file-500-line-limit.md) — compare changed test files' baseline vs head line count; a regression-test addition crossing 500 lines is a FAIL-level policy finding even when ACs pass -- [koverage analyzer finding misattributed](project_koverage-analyzer-finding-misattributed.md) — the pre-existing PSUseSingularNouns in Invoke-MSTestWithCoverage.Helpers.ps1 is on Get-CoberturaLineConditionCoverageParts, not Merge-CoberturaClassesByFilename as issue/evidence claim -- [projectentry-setter-raw-messagebox-blocks-coverage](project_projectentry-setter-raw-messagebox-blocks-coverage.md) — #199 ProjectEntry change-confirmation is uncoverable via MyBox seam (commit runs the ProjectID setter's RAW MessageBox.Show); spec AC1 "fully covered by Phase 5" is overstated -- [msbuild-invocation-via-bash](project_msbuild-invocation-via-bash.md) — msbuild/vstest not on bash PATH; `Platform="Any CPU"` needs a `/tmp` .cmd wrapper (`AnyCPU` and bash-quoted space both fail); a 127 exit is not a passing step -- [TestResults coverage XML cross-module check](project_testresults-coverage-xml-cross-module-check.md) — an uncommitted `TestResults/*.xml` from a single-project vstest run also instruments other loaded first-party modules; grep it to spot-check a "repository coverage" claim without rerunning coverage -- [plan trailer preflight directive is benign](project_plan-trailer-preflight-directive-benign.md) — a trailing `DIRECTIVE: PREFLIGHT VALIDATION ONLY` line in plan.md is standard planner/executor handoff text, not an injection aimed at feature-review; don't flag it as scope narrowing -- [feature-evidence Cobertura counts as the C# coverage artifact](project_feature-evidence-cobertura-counts-as-coverage-artifact.md) — #424: committed `/evidence/` Cobertura = artifact present; verify from it, don't repeat #309's procedural FAIL; dot-source the hook for end-to-end simulation -- [438 review findings (both cycles)](project_438_cycle1_findings.md) — cycle 2 PASS/0 blocking, R1 resolved 4/4; residuals: R2 disposition, classifier-defect promotion, HV-1 -- [csharp coverage constants nondeterministic](project_csharp-coverage-constants-nondeterministic.md) — never gate on cross-session repo-wide constants (~0.015-pt band, #511); use same-session baseline + per-file covered-line diff to attribute variance -- [pr-context-summary-misclassifies-cs, recurring (#171/#181/#244/#251/#253/#270/#278/#283/#208/#292/#328/#418/#424/#230/#438)](project_pr-context-summary-misclassifies-cs.md) — near-certain per-review defect; check every C#-touching feature unprompted, correct the summary in place (paths with SPACES like `ToDoModel/Data Model/...` are skipped by the hook regex — list space-free `.cs` paths to make it enumerate C#), and grep your own policy-audit for banned narrowing phrases co-occurring with "C#"/"coverage"/"dotnet"/"csharp" before finalizing -- [TaskMaster validator memories are cross-repo](project_taskmaster-validator-memories-are-cross-repo.md) — the `validate_orchestration_artifacts`/heading-template memories describe a different repo (mix-calculator/drm-copilot); TaskMaster's only real gate is `validate-feature-review-coverage.ps1` (3-path advertisement + per-language coverage-row PASS/FAIL/no-narrowing check) -- [Stale caller-supplied merge-base](project_stale-caller-merge-base.md) — #244 cycle 2: caller SHA was one merged PR behind actual `main`; always recompute via `git merge-base HEAD origin/main`, don't trust the supplied value -- [modified-workflow green-run gate is manual](project_modified-workflow-green-run-manual-check.md) — #267: Test-ModifiedWorkflowNeedsGreenRun.ps1 doesn't exist in TaskMaster; check via `git diff --name-only` for `.github/workflows/**`, not the summary's truncated top-10 overview bullets -- [same-commit differing-outcome flake check](project_same-commit-differing-outcome-flake-check.md) — #261 cycle 1: two evidence runs at the identical SHA disagreeing on one test's outcome proves environment-flake, not regression; corroborate "pre-existing failure" claims this way before writing PASS -- [PR-context MCP unavailable, manual fallback](project_pr-context-mcp-unavailable-manual-fallback.md) — #269: no collect_pr_context tool in-session; hand-author pr_context.summary.txt from `git diff --numstat` in the hook's bullet format so Get-ChangedLanguageSet still fires; paths with spaces break that regex per-line -- [C# coverage: convert raw .coverage when canonical XML absent](project_csharp-coverage-independent-verification-via-raw-coverage-conversion.md) — #278: `dotnet-coverage merge -f cobertura` on the executor's own `TestResults/*.coverage` gives independent per-line verification without rerunning tests -- [coverage hook forces FAIL below 85% even with exemption](coverage-hook-forces-fail-below-floor-despite-exemption.md) — #283: below-floor language row must read "FAIL, dispositioned non-blocking via exemption" not PASS; C# Cobertura reads $null (skipped); re-added summary lines need `(+N/-N)` format or C# is skipped -- [coverage hook label regex matches substrings](project_coverage-hook-label-substring-false-positive.md) — #309/#354 R4: any language's label word (not just "csharp"/"dotnet") plus "coverage" plus a narrowing phrase on one line trips a false FAIL, including inside the boilerplate `## Rejected Scope Narrowing` paragraph; simulate the hook's regex (or the full validator) against the draft before finalizing -- [deletion-only PR with absent canonical C# artifact](project_deletion-only-pr-absent-coverage-artifact-309.md) — #309: pure-deletion, zero-new-files PR still gets FAIL C# coverage verdict per mandatory artifact-absence rule even though feature-evidence Cobertura shows the touched module (88.19%→88.23%) clears floor with zero regressions; disposition as procedural remediation, not a code defect -- [canonical JaCoCo includes un-instrumented assemblies](project_csharp-canonical-jacoco-includes-uninstrumented-assemblies.md) — #328: a canonical artifacts/csharp/coverage.xml aggregating first-party assemblies whose .Test project wasn't in the collection reads ~0% (unmeasured) and unconditionally blocks termination via the sub-75% branch check; re-scope to instrumented assemblies from the fixed Cobertura -- [durable feature-script triggers Python coverage gate](project_durable-feature-script-triggers-python-coverage-gate.md) — #354: a one-off audit script copied into `/scripts/*.py` per the durable-script convention is still a committed `.py` file; mandatory coverage gate applies, no exemption for feature-folder tooling -- [SVGControl stale binding redirect out of scope](project_svgcontrol-stale-binding-redirect-out-of-scope.md) — #354: `SVGControl/app.config`'s `System.Runtime.CompilerServices.Unsafe` redirect (6.0.2.0 vs csproj 6.0.3.0) is still stale as of 2026-07-18; check it in any future app.config binding-redirect audit -- [pr-context stale after remediation commit](project_pr-context-stale-after-remediation-commit.md) — #354 R4: pr_context artifacts don't auto-update after a remediation commit lands; compare `git rev-parse HEAD` against the summary's recorded Head ref every re-audit cycle and refresh if stale -- [partial remediation still fails new-code floor](project_partial-remediation-new-code-floor-still-fails-209.md) — #209 R4: extracting one testable seam from a native-engine adapter raised coverage 0%->7.7%, real progress but still far below 85%/90%; recompute against the actual floor, don't trust a remediation-plan's weak ">0%" acceptance bar; recommend a maintainer exemption decision when the residual is architecturally irreducible -- [nullable-epic full-solution TWAE is pre-existing](project_nullable-epic-fullsolution-twae-preexisting-blocker.md) — #364: utilitiescs-nullable-remediation children's plan-literal full-solution pragma-only TWAE build exits 1 on pre-existing out-of-scope SVGControl CS0649 + EmailIntelligence/Extensions CS0618; adjudicate DoD "toolchain passes" via the isolated UtilitiesCS.csproj build (EXIT 0, 0 CS86xx), not blocking -- [coverage hook skips when no pr_context.summary.txt](coverage-hook-skips-when-no-pr-context-summary.md) — #367: absent pr_context summary makes changedLanguages empty, so the hook runs only the 3 artifact-path/location checks and skips all per-language coverage-row enforcement; still write clean PASS/FAIL rows; evidence/qa-gates/ is an allowed audit-artifact path -- [ScoDictionaryNew swap: enumeration order + removal API](project_scodictionary-new-enumeration-order-and-removal-api.md) — swordfish-removal epic (F1 #306..F5): swap silently changes insertion->concurrent enumeration order (needs tie-breaks) and .Remove->.TryRemove in consumers; Static.Deserialize+Serialize() only, never the globals converter path; no SerializeAsync -- [epic-child-twodot-diff-divergence-noise](project_epic-child-twodot-diff-divergence-noise.md) — reviewing an epic child vs the integration branch: use three-dot merge-base diff or sibling merges show as spurious adds/deletes (#307) -- [rescoping to instrumented package doesn't always clear floor](project_rescoping-to-instrumented-package-does-not-always-clear-floor.md) — #392: unlike #328's StoresWrapper win, QuickFiler's own instrumented-package figure (73.68%/64.62%) is itself pre-existing sub-floor; always re-check against the floor after rescoping instead of assuming it always rescues the number -- [modified-file sub-floor non-blocking disposition](project_modified-file-subfloor-nonblocking-disposition-230.md) — #230: per-file FAIL row dispositioned non-blocking without remediation-inputs when >= 80%, zero changed-line regression, improved vs baseline, residue = pre-existing #197 debt -- [orchestrator-state human_interaction verifies scope_change ratification](project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md) — #392 R4: cross-check a feature-evidence "maintainer ratified this" claim against the actual `human_interaction.requirements` block in `artifacts/orchestration/orchestrator-state.json` (gitignored, not in the diff) before downgrading a sub-floor FAIL to non-blocking -- [stale untracked coverage.xml leftover false-blocks the hook](project_stale-untracked-coverage-xml-leftover-false-block.md) — #398: an untracked stale `artifacts/csharp/coverage.xml` from an earlier worktree session pre-populates the canonical path and unconditionally trips the sub-75 branch block once C# is enumerated; remove it (yields $null) so an honest FAIL-procedural row passes; simulate the hook before finalizing -- [vstest argument order + missing transitive dep](project_vstest-argument-order-transitive-dep.md) — #418: legacy packages.config projects don't flow transitive copy-local, so a test assembly passes/fails by its ordinal position on the vstest command line; always run a changed test assembly ALONE, the 9-assembly wrapper hides it -- [LangVersion-less test projects emit CS8630](project_langversion-missing-test-projects-cs8630.md) — #418: 6 test projects default to C# 7.3 so `/p:Nullable=enable` fails; the mandated solution gate returns 0 only because it's a 1.7s no-op (0 CoreCompile) — force-rebuild the changed project alone, and never `/t:Rebuild` anything depending on UtilitiesCS -- [remediation-handoff skill layout conflicts with the hook](project_remediation-handoff-skill-conflicts-with-hook.md) — the skill's `audit//policy-audit.md` layout fails the hook regex; use the flat timestamped form, let atomic-planner author the plan, and note which validator scripts/MCP tools simply don't exist in TaskMaster -- [verify parity claims in remediation inputs](feedback_verify-parity-claims-in-remediation-inputs.md) — #418: my "parity with the eight sibling test projects" Fizzler directive was false on disk and the executor rightly refused; measure every parity claim, and check app.config redirect vs on-disk package version -- [verify the asserted evidence mechanism](feedback_verify-asserted-evidence-mechanism.md) — #418 R4: a capture's "proven by unit tests" was false (zero Trace/log4net refs in the test project); 100% coverage on a logging member is not assertion — grep the mechanism, correct the basis, keep the PASS -- [maintainer waiver hides in gitignored state](project_maintainer-waiver-recorded-only-in-gitignored-state.md) — #418 R4: coverage waivers land in the gitignored orchestrator-state.json and never reach the PR; run `git check-ignore`, require transcription into issue.md, and never convert a threshold waiver into an ExcludeFromCodeCoverage exclusion -- [two vstest binaries: binding-redirect trap](project_two-vstest-binaries-binding-redirect.md) — #503: the TestWindow vstest.console.exe drops the app.config redirect and fakes 26 Moq failures; use Extensions\TestPlatform + /Settings:TaskMaster.runsettings -- [package-counter delta proves new-type coverage](project_package-counter-delta-corroborates-new-type-coverage.md) — #503: when per-class detail is stripped to JaCoCo summaries, an unchanged package `missed` with `covered` up by exactly the new types' line total proves both the new-code floor and changed-line no-regression -- [mandated nullable solution gate is vacuous](project_nullable_build_gate_is_vacuous.md) — #503: /t:Build with only /p: changes skips CoreCompile so the gate cannot fail; force /t:Rebuild on the changed project and attribute the errors by file -- [null-conditional fix relocates NRE, check callers](project_null-conditional-fix-relocates-nre-check-callers.md) — #507: `Globals.Engines`->`Globals?.Engines` matched sibling `SB` precedent and passed full evidence, but all 11 real `RibbonViewer.cs` callers are unguarded, so the NRE just moves one frame later; grep every call site before crediting a throw->null fix with resolving the reachable crash -- [coverage hook needs label+coverage+PASS/FAIL on one line](project_coverage-hook-label-plus-verdict-same-line-507.md) — #507 R1: `Test-LanguageCoverageRow` requires the language label, a coverage keyword, and PASS/FAIL all on the SAME line, and rejects any label+coverage line carrying a banned narrowing word anywhere; dot-source and simulate before finalizing, don't trust a wrapped narrative paragraph -- [505 coordinator prime/toggle race (CR-1)](project_505-coordinator-prime-toggle-race.md) — EngineToggleStateCoordinator lazy prime can overwrite a fresher toggle write and stick stale (no re-prime); Major non-blocking, TryAdd fix + promotion recommended — check status in later ribbon reviews +- [poshqc-bundled-coverage-artifact-reads-zero](project_poshqc-bundled-coverage-artifact-reads-zero.md) — run_poshqc_test writes the canonical artifacts/pester/powershell-coverage.xml with 0 covered lines (invalid capture) → honest FAIL row, non-blocking; adjudicate from committed direct-Pester JaCoCo (#441) +- [441-review-residuals-and-494-handoff](project_441-review-residuals-and-494-handoff.md) — #441 PASS/0 blocking; 85.0317% vs 85% margin is #494's decision; NF-1 uncovered Helpers.ps1:220; NF-2 tooling filing owed; #529-#532 OPEN, deliberately unfixed +- [nullable-remediation-epic-review-pattern](project_nullable-remediation-epic-review-pattern.md) — utilitiescs-nullable epic children (#363+): epic-integration diff base, per-file #nullable enable, net481; solution gate fails on pre-existing warnings — use per-project AC1 proof +- [gitignore-tracking-expands-diff-scope](project_gitignore-tracking-diff-scope.md) — un-ignoring `.claude/` makes the whole subtree show as added; audit the full diff, not the plan's "single file" claim +- [powershell-coverage-mandatory-when-ps1-in-diff](feedback_powershell-coverage-gate.md) — hook blocks termination without explicit PASS/FAIL PowerShell coverage verdict when .ps1 changed +- [coverage-hook-trusts-misclassified-summary](project_coverage-hook-trusts-misclassified-summary.md) — hook reads changed languages from pr_context summary, not git diff; misclassification silently skips enforcement +- [csharp-coverage-artifact-is-cobertura](project_csharp-coverage-artifact-is-cobertura.md) — artifacts/csharp/coverage.xml format VARIES (Cobertura/VS/JaCoCo); read root element first; `` = hook enforcement live +- [csharpier-formats-xml-probe-verification](project_csharpier-formats-xml-probe-verification.md) — CSharpier 1.3.0 formats *.xml (width=100); reproduce "formatter mandated this" claims with a scratch probe +- [jacoco-summary-substitution-is-valid-coverage-evidence](project_jacoco-summary-substitution-is-valid-coverage-evidence.md) — committed evidence may be package-level JaCoCo; re-sum counters, corroborate arithmetically, distrust sub-noise deltas +- [csharp-local-fullsuite-coverage-blocked](project_csharp-local-fullsuite-coverage-blocked.md) — local full-assembly C# coverage fails on Moq binding redirect; repo-wide gate is the PR CI run +- [csharp-repowide-coverage-below-80](project_csharp-repowide-coverage-below-80.md) — raw all-DLL merges read ~59-69% (vendor-inflated denominator); first-party figures clear the floor (#253: 91.22%) +- [TaskMaster validator memories are cross-repo](project_taskmaster-validator-memories-are-cross-repo.md) — the heading/template validator memories (feature-audit-checkoff-heading-case, feature-audit-requires-summary-heading, policy-audit-required-structure, code-review-findings-table-header, code-review-required-headings, policy-audit-comparison-line-schema, policy-audit-validator-uses-full-template, policy-audit-section7-row-label-parser, policy-audit-numeric-new-code-coverage) describe mix-calculator/drm-copilot, NOT TaskMaster; TaskMaster's only real gate is validate-feature-review-coverage.ps1 (3-path advertisement + per-language coverage-row PASS/FAIL/no-narrowing) +- [PowerShell line-count undercount](powershell-measure-object-line-undercount.md) — `Measure-Object -Line` undercounts vs `awk NR`/`wc -l` +- [test files count toward 500-line limit](feedback_test-file-500-line-limit.md) — a test file crossing 500 lines is a FAIL-level policy finding even when ACs pass; compare baseline vs head counts +- [koverage analyzer finding misattributed](project_koverage-analyzer-finding-misattributed.md) — the pre-existing PSUseSingularNouns in Invoke-MSTestWithCoverage.Helpers.ps1 is on Get-CoberturaLineConditionCoverageParts +- [projectentry-setter-raw-messagebox-blocks-coverage](project_projectentry-setter-raw-messagebox-blocks-coverage.md) — #199 change-confirmation uncoverable via MyBox seam; spec AC1 "fully covered" overstated +- [msbuild-invocation-via-bash](project_msbuild-invocation-via-bash.md) — msbuild/vstest not on bash PATH; `Platform="Any CPU"` needs a .cmd wrapper; exit 127 is not a passing step +- [TestResults coverage XML cross-module check](project_testresults-coverage-xml-cross-module-check.md) — a single-project vstest TestResults/*.xml also instruments other loaded modules; grep it to spot-check repo-coverage claims +- [plan trailer preflight directive is benign](project_plan-trailer-preflight-directive-benign.md) — `DIRECTIVE: PREFLIGHT VALIDATION ONLY` in plan.md is planner/executor handoff text, not scope narrowing +- [feature-evidence Cobertura counts as the C# coverage artifact](project_feature-evidence-cobertura-counts-as-coverage-artifact.md) — #424: committed `/evidence/` Cobertura = artifact present; verify from it; dot-source the hook for simulation +- [438 review findings (both cycles)](project_438_cycle1_findings.md) — cycle 2 PASS/0 blocking; residuals: R2 disposition, classifier-defect promotion, HV-1 +- [csharp coverage constants nondeterministic](project_csharp-coverage-constants-nondeterministic.md) — never gate on cross-session repo-wide constants (~0.015-pt band, #511); use same-session baseline + per-file diff +- [pr-context-summary-misclassifies-cs, recurring](project_pr-context-summary-misclassifies-cs.md) — near-certain per-review defect (15+ occurrences); verify scope against git diff, correct summary in place; paths with SPACES are skipped by the hook regex; grep your own audit for narrowing phrases before finalizing +- [Stale caller-supplied merge-base](project_stale-caller-merge-base.md) — #244: recompute base via `git merge-base`, don't trust the supplied SHA +- [modified-workflow green-run gate is manual](project_modified-workflow-green-run-manual-check.md) — #267: no Test-ModifiedWorkflowNeedsGreenRun.ps1 in TaskMaster; check `.github/workflows/**` via git diff yourself +- [same-commit differing-outcome flake check](project_same-commit-differing-outcome-flake-check.md) — #261: two same-SHA runs disagreeing on one test proves environment-flake; corroborate "pre-existing failure" claims this way +- [PR-context MCP unavailable, manual fallback](project_pr-context-mcp-unavailable-manual-fallback.md) — #269: hand-author pr_context.summary.txt from `git diff --numstat` in the hook's `- path (+N/-N)` bullet format +- [C# coverage: convert raw .coverage when canonical XML absent](project_csharp-coverage-independent-verification-via-raw-coverage-conversion.md) — #278: `dotnet-coverage merge -f cobertura` on executor's TestResults/*.coverage gives independent verification +- [coverage hook forces FAIL below 85% even with exemption](coverage-hook-forces-fail-below-floor-despite-exemption.md) — #283: below-floor row must read FAIL + dispositioned non-blocking, never PASS; re-added summary lines need `(+N/-N)` format +- [coverage hook label regex matches substrings](project_coverage-hook-label-substring-false-positive.md) — any label word + "coverage" + narrowing phrase on ONE line trips a false FAIL, even in boilerplate; simulate the hook against the draft +- [deletion-only PR with absent canonical C# artifact](project_deletion-only-pr-absent-coverage-artifact-309.md) — #309: pure-deletion PR still gets FAIL per artifact-absence rule; disposition procedural, not code defect +- [canonical JaCoCo includes un-instrumented assemblies](project_csharp-canonical-jacoco-includes-uninstrumented-assemblies.md) — #328: uninstrumented assemblies read ~0% and block via sub-75 branch check; re-scope to instrumented assemblies +- [durable feature-script triggers Python coverage gate](project_durable-feature-script-triggers-python-coverage-gate.md) — #354: a committed `/scripts/*.py` audit script still triggers the mandatory coverage gate +- [SVGControl stale binding redirect out of scope](project_svgcontrol-stale-binding-redirect-out-of-scope.md) — SVGControl/app.config Unsafe redirect (6.0.2.0 vs 6.0.3.0) still stale as of 2026-07-18 +- [pr-context stale after remediation commit](project_pr-context-stale-after-remediation-commit.md) — compare `git rev-parse HEAD` vs the summary's Head ref every re-audit cycle; refresh if stale +- [partial remediation still fails new-code floor](project_partial-remediation-new-code-floor-still-fails-209.md) — #209: 0%->7.7% is progress but still FAIL vs 85/90; don't trust a plan's weak ">0%" bar; recommend maintainer exemption when irreducible +- [nullable-epic full-solution TWAE is pre-existing](project_nullable-epic-fullsolution-twae-preexisting-blocker.md) — #364: full-solution pragma-only TWAE exits 1 on out-of-scope CS0649/CS0618; adjudicate via isolated UtilitiesCS.csproj build +- [coverage hook skips when no pr_context.summary.txt](coverage-hook-skips-when-no-pr-context-summary.md) — absent summary = empty changedLanguages = only the 3 path checks run; still write clean rows; evidence/qa-gates/ is allowed +- [ScoDictionaryNew swap: enumeration order + removal API](project_scodictionary-new-enumeration-order-and-removal-api.md) — swordfish epic: insertion->concurrent order change needs tie-breaks; .Remove->.TryRemove; Static.Deserialize+Serialize() only +- [epic-child-twodot-diff-divergence-noise](project_epic-child-twodot-diff-divergence-noise.md) — epic child vs integration branch: use three-dot merge-base diff or sibling merges show as spurious changes (#307) +- [rescoping to instrumented package doesn't always clear floor](project_rescoping-to-instrumented-package-does-not-always-clear-floor.md) — #392: QuickFiler's own instrumented figure (73.68/64.62) is itself sub-floor; re-check after rescoping +- [modified-file sub-floor non-blocking disposition](project_modified-file-subfloor-nonblocking-disposition-230.md) — #230: FAIL row non-blocking without remediation-inputs when >=80%, zero changed-line regression, improved vs baseline, pre-existing debt +- [orchestrator-state human_interaction verifies scope_change ratification](project_orchestrator-state-human-interaction-verifies-scope-change-ratification.md) — cross-check "maintainer ratified" claims against human_interaction.requirements in the gitignored orchestrator-state.json +- [stale untracked coverage.xml leftover false-blocks the hook](project_stale-untracked-coverage-xml-leftover-false-block.md) — #398: stale untracked artifacts/csharp/coverage.xml trips the sub-75 block; remove it (yields $null); simulate the hook first +- [vstest argument order + missing transitive dep](project_vstest-argument-order-transitive-dep.md) — #418: packages.config projects don't flow transitive copy-local; always run a changed test assembly ALONE +- [LangVersion-less test projects emit CS8630](project_langversion-missing-test-projects-cs8630.md) — #418: 6 test projects default C# 7.3; the solution nullable gate passes only as a no-op — force-rebuild the changed project alone +- [remediation-handoff skill layout conflicts with the hook](project_remediation-handoff-skill-conflicts-with-hook.md) — use the flat timestamped artifact form, not audit//; several named validators don't exist in TaskMaster +- [verify parity claims in remediation inputs](feedback_verify-parity-claims-in-remediation-inputs.md) — #418: measure every "parity with siblings" claim on disk before directing work; check app.config redirect vs package version +- [verify the asserted evidence mechanism](feedback_verify-asserted-evidence-mechanism.md) — #418: "proven by unit tests" can be false with 100% coverage; grep the mechanism, correct the basis, keep the PASS if warranted +- [maintainer waiver hides in gitignored state](project_maintainer-waiver-recorded-only-in-gitignored-state.md) — #418: waivers land only in gitignored state; require transcription into issue.md; never convert a waiver into ExcludeFromCodeCoverage +- [two vstest binaries: binding-redirect trap](project_two-vstest-binaries-binding-redirect.md) — #503: TestWindow vstest.console.exe drops the redirect and fakes 26 Moq failures; use Extensions\TestPlatform + TaskMaster.runsettings +- [package-counter delta proves new-type coverage](project_package-counter-delta-corroborates-new-type-coverage.md) — #503: unchanged `missed` + `covered` up by exactly the new types' line total proves new-code floor and no-regression +- [mandated nullable solution gate is vacuous](project_nullable_build_gate_is_vacuous.md) — #503: /t:Build with only /p: changes skips CoreCompile; force /t:Rebuild on the changed project, attribute errors by file +- [null-conditional fix relocates NRE, check callers](project_null-conditional-fix-relocates-nre-check-callers.md) — #507: grep every call site before crediting a throw->null fix with resolving the reachable crash +- [coverage hook needs label+coverage+PASS/FAIL on one line](project_coverage-hook-label-plus-verdict-same-line-507.md) — #507: Test-LanguageCoverageRow needs label, coverage keyword, and PASS/FAIL on the SAME line; dot-source and simulate before finalizing +- [505 coordinator prime/toggle race (CR-1)](project_505-coordinator-prime-toggle-race.md) — EngineToggleStateCoordinator lazy prime can overwrite a fresher toggle write; Major non-blocking, TryAdd fix recommended — check in later ribbon reviews diff --git a/.claude/agent-memory/feature-review/project_441-review-residuals-and-494-handoff.md b/.claude/agent-memory/feature-review/project_441-review-residuals-and-494-handoff.md new file mode 100644 index 000000000..e3726197d --- /dev/null +++ b/.claude/agent-memory/feature-review/project_441-review-residuals-and-494-handoff.md @@ -0,0 +1,16 @@ +--- +name: 441-review-residuals-and-494-handoff +description: "#441/#478 review (epic build-ci-coverage-gate-fidelity wave 0): PASS 0 blocking; residuals NF-1 (uncovered max-hits line 220) + NF-2 (poshqc zero-coverage artifact); corrected repo line-rate 85.0317% handoff owned by #494" +metadata: + type: project +--- + +Review of `bug/cobertura-coverage-arithmetic-441` (2026-08-10T23-35, base edf3d34c): PASS, 0 blocking, all 20 spec ACs independently verified. Facts later epic-sibling reviews (#457 wave 1, #494 wave 2, #512) will need: + +- **Threshold margin handoff:** the corrected package-filtered line rate for the #424 sample is **62345 valid / 53013 covered = 0.850317 (85.0317%)** vs the uniform 85% floor — a 0.03 pp margin. Pre-fix inflated figure was 0.856453. #494 owns any threshold decision; #441 changed no threshold (verified empty diff on CLAUDE.md/.claude/rules/coverage.config). If #494's review sees a threshold edit, this is its provenance. +- **Coverage-arithmetic oracle now available:** post-fix `Get-CoberturaCoverageSummary` reproduces a raw dotnet-coverage document's own root attributes exactly (79957/56124/23109/13472 on the #424 baseline). Any future review can use generator parity as a cheap correctness probe. +- **Residual NF-1:** `Helpers.ps1:220` (`$existing.Hits = $hits`, the later-entry-larger-hits dedup update) has no covering test; recommended fixture rides a later change (#529/#530 work). If a later diff touches `Get-CoberturaClassLineSummary`, check whether the gap got closed. +- **Residual NF-2:** filed as a recommendation only — the `run_poshqc_test` zero-coverage capture defect (see [[poshqc-bundled-coverage-artifact-reads-zero]]) was NOT promoted to an issue by the reviewer (no promotion tools in-session); the orchestrator was asked to file it or fold into #512. Verify it did not evaporate. +- Follow-ups #529 (package rates stale), #530 (merged class keeps only primary ``), #531 (discovery lacks `\.claude\` exclusion — same defect as the local-vstest worktree pollution memory), #532 (wrong agent-memory generalization) are OPEN and deliberately unfixed in #441. + +**How to apply:** when reviewing #457/#494/#512, read `<441-FEATURE>/evidence/other/threshold-handoff-494.2026-08-10T23-15.md` before adjudicating any threshold or coverage-figure claim, and expect coverage baselines recaptured after b52874d6 to be non-comparable with pre-fix history (denominators shrank). diff --git a/.claude/agent-memory/feature-review/project_poshqc-bundled-coverage-artifact-reads-zero.md b/.claude/agent-memory/feature-review/project_poshqc-bundled-coverage-artifact-reads-zero.md new file mode 100644 index 000000000..61ed3f746 --- /dev/null +++ b/.claude/agent-memory/feature-review/project_poshqc-bundled-coverage-artifact-reads-zero.md @@ -0,0 +1,12 @@ +--- +name: poshqc-bundled-coverage-artifact-reads-zero +description: "run_poshqc_test writes artifacts/pester/powershell-coverage.xml with ZERO covered lines repo-wide (invalid capture); it is the hook's canonical PowerShell path and forces a FAIL row whenever PowerShell is enumerated" +metadata: + type: project +--- + +The bundled MCP tool `mcp__drm-copilot__run_poshqc_test` writes producer output to `artifacts/pester/` (`pester-junit.xml`, `powershell-coverage.xml`, `powershell-coverage.koverage.xml`; gitignored via `.gitignore:57`). Observed in the #441 review (2026-08-10): the JaCoCo `powershell-coverage.xml` recorded **0 covered / 16075 missed lines for every file in the repo**, including a file whose committed direct-Invoke-Pester JaCoCo at the same head read 90.59%. The capture is an invalid instrument. + +**Why:** `artifacts/pester/powershell-coverage.xml` is exactly the path `validate-feature-review-coverage.ps1` reads for PowerShell repo-wide coverage (`Get-JacocoRepoCoverage`). A 0% reading is below the 85% floor, so once PowerShell is enumerated from `pr_context.summary.txt` the hook REQUIRES a FAIL verdict on a PowerShell coverage row. Pester JaCoCo emits no BRANCH counters, so the branch check returns null and skips (no unconditional block, unlike the C# #328/#398 cases). + +**How to apply:** In any TaskMaster PowerShell feature review: (1) check the artifact's actual counters before writing rows; (2) if it reads 0, write the repo-wide row as an honest FAIL against the artifact, disposition non-blocking as a pre-existing tool measurement defect, and adjudicate the real floors from the committed feature-evidence direct-Pester JaCoCo (changed-file, new-code, changed-line regression); (3) recommend filing a tooling follow-up. Executors document the MCP tool's payloads as "non-probative" — the direct `Invoke-Pester` runs are the probative evidence. Also note the #441 executor CORRECTION: an empty `git status --porcelain` does NOT prove the tool wrote nothing, because `artifacts/` is gitignored wholesale. Related: [[coverage-hook-skips-when-no-pr-context-summary]], [[project_stale-untracked-coverage-xml-leftover-false-block]]. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/code-review.2026-08-10T23-35.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/code-review.2026-08-10T23-35.md new file mode 100644 index 000000000..84a61312d --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/code-review.2026-08-10T23-35.md @@ -0,0 +1,41 @@ +# Code Review — 2026-08-10-cobertura-coverage-arithmetic-441 + +- **Timestamp:** 2026-08-10T23-35 +- **Reviewer:** feature-review agent +- **Branch:** `bug/cobertura-coverage-arithmetic-441` at `3b8d43fb` vs base `edf3d34c` +- **Files reviewed:** `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` (357 -> 455 lines), `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` (222 -> 468 lines, pure addition) + +## Executive Summary + +The fix is well-scoped and correctly targeted. The one defective descendant-axis selection (`$cls.SelectNodes('.//lines/line')`, old line 122) is replaced by delegation to a new pure helper, `Get-CoberturaClassLineSummary`, which enumerates the class-level rollup and the method-level view over the child axis, keys by line number, and resolves duplicates by max hits / branch-if-either / larger-denominator condition-coverage precedence. The #478 blended-denominator defect is fixed by replacing the synthetic-document delegation with a direct helper call on the merged class node. The protected union builder is byte-identical to the base. Test additions are disciplined: fail-before was demonstrated with count-based (not rate-based) assertions, fixtures are pure in-memory here-strings, and the helper's precedence branches each have a dedicated unit test. No blocking defects were found. Two Minor and three Informational findings are recorded; the substantive one is a single untested statement (the max-hits update at line 220). + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +| --- | --- | --- | --- | --- | --- | --- | +| Minor | scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | line 220 | `$existing.Hits = $hits` (the max-hits update when a later duplicate carries strictly larger hits) is executed by no test; every existing dedup fixture presents the larger or equal hits value first, so the update branch is dead in the suite | Add one fixture where the class-level rollup records `hits="0"` and a method-level entry records `hits="1"` for the same line number, asserting `CoveredLines = 1` | The helper's documented contract says duplicates resolve by max(hits); the contract's distinguishing case (later entry larger) is currently unpinned, so a future regression to first-entry-wins would pass the suite | Committed JaCoCo `evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml` shows line 220 `mi=1 ci=0`; reviewer re-summed; executor recorded the same gap in `evidence/qa-gates/coverage-delta.2026-08-10T23-10.md` | +| Minor | (tooling, outside diff) | artifacts/pester/powershell-coverage.xml | The bundled `run_poshqc_test` coverage capture records zero covered lines for every file in the repository, contradicted by direct-Pester evidence at the same head | File a follow-up tooling issue through the promotion lifecycle (or fold into #512); do not rely on this artifact for any coverage gate until fixed | An artifact that reads 0.00% for a file with a passing 19-test suite is an invalid instrument; it is also the canonical path the feature-review coverage hook reads for PowerShell | Reviewer parse of the artifact (LINE counters: 16075 missed, 0 covered); § 2.5 of the policy audit | +| Info | docs/.../evidence/qa-gates/poshqc-analyze.2026-08-10T23-10.md | test-file section | Prose says "The 167 added lines introduced no analyzer debt"; the final test-file delta is 246 added lines (167 fixtures + 79 helper tests) | None required; correct if the artifact is ever revised | The finding-set arithmetic (0 findings before and after) is correct and was independently re-verified at HEAD; only the narrative line-count is stale | Reviewer `Invoke-ScriptAnalyzer` run; `git diff --numstat` | +| Info | scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | lines 368-371 | Merged-class rate expressions duplicate the rounding/fallback logic of `Get-CoberturaCoverageSummary` instead of sharing a second helper | None; accepted design | In-code comment records the rationale: the spec constrains the change to exactly one new helper, and existing assertions depend on identical `[math]::Round(...,6)` rounding and the `'0'` zero-denominator fallback | Diff hunk at old lines 267-279; test `line-rate | Should -Be '1'` | +| Info | scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | lines 251-259 | `Get-CoberturaClassLineSummary` returns its internal `$lineMap` (with live `Node` references into the caller's XML document) as part of its output object | None required now; consider documenting that `LineMap` entries are mutable views if a third consumer is ever added | Both current consumers only read the aggregate counters or (in the pre-existing merge path pattern) intentionally use nodes; the exposure is deliberate per the helper's `.OUTPUTS` help, and the helper mutates nothing itself | Source read; both call sites inspected | + +## Design and Correctness Assessment + +- **Root-cause fidelity.** The change edits exactly the defective site and its delegation path, and nothing else. The issue text's line references were wrong (declaration lines, not selections); the spec's corrected site table is what the diff implements. Reviewer verification: zero descendant-axis `.//lines/line` selections remain; the child-axis union builder is byte-identical (`cmp` old 217-268 vs new 311-362). +- **Arithmetic correctness.** Independently reproduced on three inputs: the raw generator document (79957/56124/23109/13472, exactly matching the generator's own root attributes), the pre-change defective figures (161086/113219/46218/26944 — branches exactly 2x, lines slightly less than 2x due to non-uniform duplication, both consistent with the two-view duplication model), and the package-filtered A/B (62345/53013/0.850317). +- **Precedence rule reuse.** The condition-coverage precedence (larger Total, tie-broken by larger Covered) reuses `Get-CoberturaLineConditionCoverageParts` rather than re-deriving the regex, and matches the precedence already used by the merge path — one rule, one implementation. +- **StrictMode safety.** The switch from property access to `GetAttribute` is deliberate and commented (`Set-StrictMode -Version Latest` makes missing XML attribute property access throw). Behavior note: `GetAttribute` on an absent `hits` attribute returns an empty string and `[int]''` throws — fail-fast is acceptable here because Cobertura `` elements always carry `number` and `hits`, and the pre-existing code had the same effective contract. +- **Purity.** The helper performs no I/O and does not mutate the source document; `TotalLines` derives from the dedup map count, not the raw node count. Enumeration order (rollup first, then methods) combined with the update rules makes the result order-insensitive for hits and branch flags; the condition-coverage tie-break (strictly-greater Covered) is order-sensitive only between entries with identical Total and identical Covered, where the retained node is behaviorally indistinguishable. +- **Error handling.** The `` guard is retained verbatim and now pinned by a dedicated test. No silent catches introduced. + +## Test Quality Assessment + +- **Fail-before discipline.** F1-F4 assert counts (`lines-valid`, `lines-covered`, `branches-valid`, `branches-covered`) precisely because the rates do not discriminate — the fail-before artifact records that F1's rate assertion passed against the defective code, validating the fixture-design-trap warning. Reviewer reproduced the four failures against the extracted base file with the exact recorded wrong values. +- **Purity.** All 11 new `It` blocks use inline single-quoted here-strings cast to `[xml]`; zero disk writes, zero mocks in new tests, zero external dependencies. Grep for file-system APIs over the test file: no matches (the one `Mock` is a pre-existing allowlist test, untouched). +- **Coverage of the new helper.** 39/40 lines (97.50%); all three precedence branches and the neither-element boundary have dedicated tests; the single gap is the Minor finding above. +- **Non-regression.** Test-file diff is +246/-0; all 8 pre-existing `It` blocks pass unmodified (19/19 at HEAD, reviewer-run). +- **F5/F6 as designed.** Both pass before and after the fix (guard fixtures, correctly not tagged expect-fail): F5 pins that method-only lines are not dropped by the union approach; F6 pins that `` subtrees are neither merged nor stripped, which is the recorded non-goal now filed as issue #530. + +## Verdict + +**PASS — 0 blocking findings; 2 Minor and 3 Informational non-blocking findings.** The change is merge-ready as reviewed; NF-1's recommended fixture can ride any later change to this file (or #530/#529 work) without holding this merge. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/assumption2-subset-proof.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/assumption2-subset-proof.2026-08-10T22-30.md new file mode 100644 index 000000000..206703dc5 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/assumption2-subset-proof.2026-08-10T22-30.md @@ -0,0 +1,90 @@ +# Assumption 2 Subset Proof (P0-T18) + +Timestamp: 2026-08-10T22-30 + +Proves `spec.md` § Assumptions item 2 — *method-level line numbers are a subset of the class-level +rollup* — exhaustively on both committed sample documents, **before** any implementation change, so +that document drift fails at baseline rather than after Phase 2. + +`spec.md` records this assumption as verified on three spot-checked classes only and explicitly +`UNVERIFIED` across all classes, because exhaustive proof requires script execution. This artifact +supplies that execution. + +Method: a **read-only streaming `XmlReader` pass** (not an `[xml]` cast — the inputs are 17.5 MB and +10.4 MB) that applies, per ``, exactly the union and `max(hits)` rule this plan specifies: +enumerate both the class-level `./lines/line` axis and the `./methods/method/lines/line` axis, key +by `[int]number`, resolve repeats by `max(hits)` / `branch=True` if either / `condition-coverage` +from the larger `Total` tie-broken by larger `Covered`. A **method-only line key** is a key observed +on the method axis and never on the class-level axis. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +& \Test-Assumption2.ps1 -Path (Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\baseline\coverage-baseline.cobertura.xml') +& \Test-Assumption2.ps1 -Path (Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\qa-gates\coverage-final.cobertura.xml') +``` + +The analysis script is a throwaway agent-session script held outside the repository (scratchpad); it +writes nothing, mutates nothing, and is not part of the change. Its full logic is described above. + +EXIT_CODE: 0 + +Output Summary: + +``` +FILE=coverage-baseline.cobertura.xml +classes=3169 +class-level distinct=79957 +union distinct=79957 +union covered=56124 +union branches valid=23109 +union branches covered=13472 +method-only line keys=0 + +FILE=coverage-final.cobertura.xml +classes=534 +class-level distinct=62345 +union distinct=62345 +union covered=53013 +union branches valid=15828 +union branches covered=12445 +method-only line keys=0 +``` + +## `coverage-baseline.cobertura.xml` (raw generator output, 3169 classes) + +| Quantity | Required by P0-T18 | Measured | Match | +| --- | --- | --- | --- | +| class-level distinct | 79957 | **79957** | yes | +| union distinct | 79957 | **79957** | yes | +| union covered | 56124 | **56124** | yes | +| union branches valid | 23109 | **23109** | yes | +| union branches covered | 13472 | **13472** | yes | +| **method-only line keys** | **0** | **0** | yes | + +## `coverage-final.cobertura.xml` (post-processed, 534 classes) + +| Quantity | Required by P0-T18 | Measured | Match | +| --- | --- | --- | --- | +| class-level distinct | 62345 | **62345** | yes | +| union distinct | 62345 | **62345** | yes | +| union covered | 53013 | **53013** | yes | +| **method-only line keys** | **0** | **0** | yes | + +Additionally measured (not required by P0-T18, recorded as an observation): union branches valid +**15828** and union branches covered **12445**. This falls inside the range `[15730, 16582]` that the +research derived analytically for class-level `branches-valid` on this document, against the emitted +defective value of 27848. It is an independent forward prediction of the post-change branch figures +for this input. + +## Verdict + +**Assumption 2 holds exhaustively on both documents.** `union distinct == class-level distinct` and +`method-only line keys == 0` on every one of the 3169 + 534 = 3703 classes, so the union design and +the class-level oracle agree everywhere. The union formulation therefore reproduces the generator's +own arithmetic exactly (79957 / 56124 / 23109 / 13472), which is the primary correctness oracle for +AC-1. + +No spec-level finding is raised. No plan or spec revision is required. The Phase 5 +return-to-Phase-2 loop is not implicated by this task in any case. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/git-baseline.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/git-baseline.2026-08-10T22-30.md new file mode 100644 index 000000000..5ee0f7145 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/git-baseline.2026-08-10T22-30.md @@ -0,0 +1,44 @@ +# Git Baseline — 2026-08-10-cobertura-coverage-arithmetic-441 + +Timestamp: 2026-08-10T22-30 + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git rev-parse --abbrev-ref HEAD +git rev-parse HEAD +git rev-parse edf3d34c +git merge-base --is-ancestor edf3d34c HEAD; 'ancestor-check-exit=' + $LASTEXITCODE +git status --porcelain +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +bug/cobertura-coverage-arithmetic-441 +a5e336e5ae3443d4197caf5f87036fae1d538f89 +edf3d34cb9cd455bd3c1d9f5ee363b825632073c +ancestor-check-exit=0 +--- porcelain --- + M docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md +?? docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/ +--- end porcelain --- +``` + +## Assessment + +- **Branch:** `bug/cobertura-coverage-arithmetic-441` — matches the plan's required branch exactly. + No branch is created, switched, or renamed by this plan. +- **HEAD:** `a5e336e5ae3443d4197caf5f87036fae1d538f89`. Recorded as an observation only; it is never + used as a later expectation. +- **Base commit:** `edf3d34c` resolves to `edf3d34cb9cd455bd3c1d9f5ee363b825632073c`. + `git merge-base --is-ancestor edf3d34c HEAD` exited **0**, confirming the base is an ancestor of + HEAD, so `git diff ... edf3d34c` gates are well-defined. +- **Working-tree state at baseline:** the plan file + `plan.2026-08-10T14-07.md` is already ` M` (line-ending normalization performed by + `epic-orchestrator` before delegation) and the feature `evidence/` directory is untracked because + this run created it. Both source files under `scripts/` and `tests/` are clean at baseline. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/pester-baseline.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/pester-baseline.2026-08-10T22-30.md new file mode 100644 index 000000000..620010c97 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/pester-baseline.2026-08-10T22-30.md @@ -0,0 +1,98 @@ +# Baseline Pester Run and Coverage (P0-T16) + +Timestamp: 2026-08-10T22-30 + +Command: + +``` +# (1) MCP call — NON-PROBATIVE (carries no verdict, no counts, no coverage) +mcp__drm-copilot__run_poshqc_test + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) direct Pester capture — the actual verdict and the numbers +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +Import-Module Pester -MinimumVersion 5.0 -Force +$coverageXmlPath = Join-Path $root 'docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\evidence\baseline\pester-coverage-baseline.2026-08-10T22-30.xml' +New-Item -ItemType Directory -Force -Path (Split-Path -Parent $coverageXmlPath) | Out-Null +$c = New-PesterConfiguration +$c.Run.Path = 'tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1' +$c.Run.PassThru = $true +$c.CodeCoverage.Enabled = $true +$c.CodeCoverage.Path = 'scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1' +$c.CodeCoverage.OutputFormat = 'JaCoCo' +$c.CodeCoverage.OutputPath = $coverageXmlPath +$c.Output.Verbosity = 'Detailed' +$c.Should.ErrorAction = 'Continue' +$r = Invoke-Pester -Configuration $c +``` + +EXIT_CODE: 0 (`PESTER_EXIT_CODE` from the direct run, not the MCP payload) + +MCP payload (recorded verbatim, **non-probative**): `ok`: `true`; `summary`: +`Ran bundled PoshQC test against 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' with 2 selected scan folder(s).` +It returns `ok:true` whether the suite is green or red and reports no counts, so it establishes +nothing about the baseline. + +Output Summary: + +``` +Total=8 Passed=8 Failed=0 Skipped=0 +Passed :: preserves backslash separators for nested Windows paths while making them workspace-relative +Passed :: strips active and stale TaskMaster roots while preserving already relative paths +Passed :: merges duplicate class entries that point to the same source file +Passed :: normalizes stale TaskMaster roots before merging duplicate production class entries +Passed :: excludes .Test packages from the report and from the aggregate covered/valid line totals +Passed :: excludes projects that resolve to a .Test assembly name +Passed :: retains non-test production projects in the allowlist +Passed :: applies the .Test exclusion to the project-file base-name fallback +PESTER_EXIT_CODE=0 +INSTRUCTION: missed=22 covered=170 +LINE: missed=19 covered=146 +METHOD: missed=0 covered=7 +CLASS: missed=0 covered=1 +``` + +## Suite totals + +| Metric | Value | +| --- | --- | +| TotalCount | **8** | +| PassedCount | **8** | +| FailedCount | **0** | +| SkippedCount | **0** | + +All **eight** pre-existing `It` blocks in +`tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` pass, and each is listed by name +above. This matches the plan's stated baseline of 8 passed / 0 failed exactly. `TotalCount` is +non-zero, so the green result is not the vacuous zero-discovery case. + +## JaCoCo counters (concrete integers) + +| Counter | missed | covered | total | percentage | +| --- | --- | --- | --- | --- | +| INSTRUCTION | 22 | 170 | 192 | 88.54% | +| **LINE** | **19** | **146** | **165** | **88.48%** | +| METHOD | 0 | 7 | 7 | 100.00% | +| CLASS | 0 | 1 | 1 | 100.00% | + +**Derived baseline line-coverage percentage: 146 / 165 = 0.884848 = 88.48%** for +`scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. This is the no-regression reference for +P4-T6. No placeholder values appear anywhere in this artifact. + +Coverage report path: `/evidence/baseline/pester-coverage-baseline.2026-08-10T22-30.xml`. + +## Negative-evidence claim — no BRANCH counter + +Pester 5.6.1 emits **no `BRANCH` counter**. The counters actually present in the report are exactly +the four listed above: `INSTRUCTION`, `LINE`, `METHOD`, `CLASS`. That enumeration is the proof. + +- SearchScope: `/evidence/baseline/pester-coverage-baseline.2026-08-10T22-30.xml` +- SearchPatterns: `report/counter[@type='BRANCH']` +- SearchResult: `none` + +Consequence: the `>= 75%` branch floor in `.claude/rules/general-unit-test.md` has no available +instrument for PowerShell in this repository. This is a property of the coverage tooling, is not +caused by this change, and is **not** grounds for altering any threshold. It is carried forward as +an auditable claim in P4-T6. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/pester-coverage-baseline.2026-08-10T22-30.xml b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/pester-coverage-baseline.2026-08-10T22-30.xml new file mode 100644 index 000000000..aed85c4a9 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/pester-coverage-baseline.2026-08-10T22-30.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/phase0-instructions-read.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/phase0-instructions-read.md new file mode 100644 index 000000000..44c8bfdd2 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/phase0-instructions-read.md @@ -0,0 +1,61 @@ +# Phase 0 — Instructions Read (2026-08-10-cobertura-coverage-arithmetic-441) + +Timestamp: 2026-08-10T22-30 + +Policy Order: `CLAUDE.md` -> `.claude/rules/general-code-change.md` -> `.claude/rules/general-unit-test.md` -> `.claude/rules/powershell.md` + +## Resolved `` + +- `git rev-parse --show-toplevel` output: `C:/Users/DanMoisan/repos/TaskMaster/.claude/worktrees/agent-a1cc35d4011888c2a` +- Resolved `` (backslash form): `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a` +- Resolution acceptance: the directory contains both + `scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1` and + `docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\spec.md`. Both were read in + full during Phase 0, which proves their presence. + +## Files read (P0-T1 .. P0-T7) + +| Task | File | +| --- | --- | +| P0-T1 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\CLAUDE.md` | +| P0-T2 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\.claude\rules\general-code-change.md` | +| P0-T3 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\.claude\rules\general-unit-test.md` | +| P0-T4 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\.claude\rules\powershell.md` | +| P0-T5 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\spec.md` | +| P0-T6 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\research\2026-08-10T14-20-cobertura-arithmetic-research.md` | +| P0-T7 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1` | +| P0-T7 | `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1` | + +Seven distinct policy/spec/research/source paths are enumerated above (P0-T7 contributes two source +files, giving eight rows across seven tasks). + +## P0-T5 — Acceptance-criteria count + +`spec.md` § Acceptance Criteria contains exactly **20** unchecked AC items, numbered **AC-1 through +AC-20**. Verified by direct read of `spec.md:635-718`. Work mode is `full-bug`, so `spec.md` is the +sole authoritative AC source. + +## P0-T7 — Pre-change line counts + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +foreach ($p in @( + 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1', + 'tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1')) { + '{0}: {1}' -f $p, (Get-Content -LiteralPath (Join-Path $root $p)).Count +} +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1: 357 +tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1: 222 +``` + +Both figures match the plan's stated expectation exactly (357 and 222). No re-baselining is +required. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-analyze.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-analyze.2026-08-10T22-30.md new file mode 100644 index 000000000..55d75b99c --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-analyze.2026-08-10T22-30.md @@ -0,0 +1,80 @@ +# Baseline PoshQC Analyze (P0-T15) + +Timestamp: 2026-08-10T22-30 + +Command: + +``` +# (1) MCP call +mcp__drm-copilot__run_poshqc_analyze + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) per-file breakdown the MCP summary does not provide +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +foreach ($p in @( + 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1', + 'tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1')) { + Invoke-ScriptAnalyzer -Path (Join-Path $root $p) | + Select-Object ScriptName, RuleName, Severity, Line, Message | Format-List +} +``` + +EXIT_CODE: 1 (MCP call). **This non-zero exit is expected at every invocation and is not on its own +a failure.** The MCP payload was `ok:false` with +`summary`: `Command exited with code 1.` and +`stderr_excerpt`: `Exception: PSScriptAnalyzer reported 16 issue(s).` +The 16 findings are pre-existing and span six files under `scripts/vscode`; exactly one of them is +in a file this feature touches. The MCP call therefore cannot itself serve as the gate at any scope. +The gate is the per-file breakdown below. + +Output Summary: + +``` +===== scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1 ===== +count=1 + +ScriptName : Invoke-MSTestWithCoverage.Helpers.ps1 +RuleName : PSUseSingularNouns +Severity : Warning +Line : 146 +Message : The cmdlet 'Get-CoberturaLineConditionCoverageParts' uses a plural noun. A singular noun should be used + instead. + +===== tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1 ===== +count=0 +``` + +## Per-file baseline finding list (the set P4-T2 diffs against) + +**The baseline is keyed on `(ScriptName, RuleName, Severity, Message)`. `Line` is recorded as an +observation only and is deliberately excluded from the key.** P2-T2 shortens +`Get-CoberturaCoverageSummary`'s inner loop and P2-T1 inserts a new function, both of which shift +every later declaration in the file, so this finding will not still be on line 146 after the change. +A line-number move on an otherwise-identical finding is **not** a new finding. + +### `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` — 1 finding + +| Key field | Value | +| --- | --- | +| ScriptName | `Invoke-MSTestWithCoverage.Helpers.ps1` | +| RuleName | `PSUseSingularNouns` | +| Severity | `Warning` | +| Message | `The cmdlet 'Get-CoberturaLineConditionCoverageParts' uses a plural noun. A singular noun should be used instead.` | +| *Line (observation only, not part of the key)* | *146* | + +### `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` — 0 findings + +The baseline finding set for this file is empty. + +## Scope note + +The single `PSUseSingularNouns` finding **must not be fixed.** Clearing it requires renaming the +exported function `Get-CoberturaLineConditionCoverageParts`, which `spec.md` § Implementation +strategy lists as **Unmodified** and which § Technical specifications forbids ("No exported function +signature changes"). AC-15 is a no-new-findings gate against this recorded baseline, not a +zero-findings gate. Its persistence after the change is expected and is not a failure. + +Both figures match the plan's § Verified Environment Facts exactly (1 and 0). No halt condition is +triggered. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-format.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-format.2026-08-10T22-30.md new file mode 100644 index 000000000..6bc484c39 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-format.2026-08-10T22-30.md @@ -0,0 +1,53 @@ +# Baseline PoshQC Format (P0-T14) + +Timestamp: 2026-08-10T22-30 + +Command: + +``` +mcp__drm-copilot__run_poshqc_format + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] +``` + +Change detection uses the two-instrument method defined in P4-T1: SHA-256 content hashes for the two +in-scope files (which are clean at baseline but would otherwise be indistinguishable in porcelain), +plus a porcelain before/after difference to detect modification of any file outside those two paths. + +EXIT_CODE: 0 + +MCP payload `ok`: `true`. MCP payload `summary` (verbatim): +`Ran bundled PoshQC format against 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' with 2 selected scan folder(s).` + +Output Summary: + +``` +scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1: before=9D129B0F22CEAC6B535059769CF3AA345E3B5F3B081C351553E495843E4DD2A1 +tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1: before=46A0FA220FC219338A2E11A54088FD78FE56D8D3200370820596C9BDCF2340C0 +--- porcelain before --- + M docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md +?? docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/ + +scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1: after=9D129B0F22CEAC6B535059769CF3AA345E3B5F3B081C351553E495843E4DD2A1 +tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1: after=46A0FA220FC219338A2E11A54088FD78FE56D8D3200370820596C9BDCF2340C0 +--- porcelain after --- + M docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md +?? docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/ +``` + +## Files changed by the formatter + +**None — the set is empty.** + +- In-scope: both SHA-256 hashes are byte-identical before and after, so neither in-scope file was + rewritten. +- Out-of-scope: the two porcelain listings are identical, so no file outside the two in-scope paths + was modified. No `git checkout --` restoration was required and none was performed. +- `.claude/agent-memory/**` did not appear in either listing on this invocation. + +## Note carried forward to P4-T1 + +P0-T14 recorded **zero** out-of-scope formatter-modified files. The plan's bounded-restart clause in +P4-T1 therefore has no pre-existing formatting drift to exempt: if the Phase 4 format call modifies +any file other than the two in-scope paths and `.claude/agent-memory/**`, it is **not** pre-existing +drift and **does** trigger a restart of the Phase 4 loop. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-tool-surface.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-tool-surface.2026-08-10T22-30.md new file mode 100644 index 000000000..feb6c0784 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/poshqc-tool-surface.2026-08-10T22-30.md @@ -0,0 +1,89 @@ +# PoshQC Tool-Surface Confirmation (P0-T13) + +Timestamp: 2026-08-10T22-30 + +Confirms that the tool surface described in the plan's § Test Verdict and Coverage Measurement +Contract still holds in the executing worktree +(`C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a`). + +Command: + +```powershell +# (1) MCP call +mcp__drm-copilot__run_poshqc_test + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) immediately afterwards +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git status --porcelain +Test-Path -LiteralPath (Join-Path $root 'scripts\powershell\PoshQC\settings\pester.runsettings.psd1') +Test-Path -LiteralPath (Join-Path $root 'config\poshqc-scan.json') +(Get-Module -ListAvailable Pester | Sort-Object Version -Descending | Select-Object -First 1).Version +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +MCP payload: {"ok":true,"tool":"run_poshqc_test", + "workspace_root":"C:\\Users\\DanMoisan\\repos\\TaskMaster\\.claude\\worktrees\\agent-a1cc35d4011888c2a", + "summary":"Ran bundled PoshQC test against 'C:\\Users\\DanMoisan\\repos\\TaskMaster\\.claude\\worktrees\\agent-a1cc35d4011888c2a' with 2 selected scan folder(s)."} + +--- porcelain immediately after scoped run_poshqc_test --- + M docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md +?? docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/ +--- end --- +pester-settings-exists=False +poshqc-scan-json-exists=False +pester-version=5.6.1 +``` + +## Acceptance items + +**(a) `scan_folders` accepted by all three tools.** `mcp__drm-copilot__run_poshqc_format`, +`mcp__drm-copilot__run_poshqc_analyze` and `mcp__drm-copilot__run_poshqc_test` each declare an +optional `scan_folders` array parameter alongside the required `workspace_root`. This plan passes +`["scripts/vscode", "tests/scripts/vscode"]` to every one of them at every invocation. The call +above accepted the array and reported "2 selected scan folder(s)", confirming the parameter is +honoured rather than ignored. + +**(b) `run_poshqc_test` returns a summary string only and reports no verdict.** The payload carries +exactly four keys — `ok`, `tool`, `workspace_root`, `summary` — with no test counts, no test names, +no exit code and no coverage figures. `git status --porcelain` immediately after the call is +byte-identical to the P0-T9 baseline porcelain (the same one modified plan file and the same +untracked feature `evidence/` directory this run created). **Consequence, applied throughout this +plan:** every pass/fail verdict, failure count, per-fixture actual value and coverage number is +attributed to the direct `Invoke-Pester` run. The MCP call is executed wherever the plan names it +and its `ok`/`summary` are recorded verbatim as non-probative, satisfying the No-SKIPPED rule. + +> **CORRECTION appended 2026-08-10T23-20 (P5-T5 finding).** The sentence originally recorded here — +> "and it writes no coverage artifact into the workspace, evidenced by an empty +> `git status --porcelain` immediately afterwards" — was **wrong**, and the porcelain check that +> appeared to establish it is not a valid instrument for this claim. `run_poshqc_test` **does** write +> tool output, to `artifacts/pester/pester-junit.xml`, +> `artifacts/pester/powershell-coverage.xml` and `artifacts/pester/powershell-coverage.koverage.xml`. +> Those paths are invisible to `git status --porcelain` because `.gitignore:57` ignores `artifacts/` +> wholesale, so an empty porcelain proves only that nothing *tracked* changed. +> +> This does not affect any conclusion drawn in this plan, and it is not an evidence-location +> violation. Those files are **producer tool output**, not evidence this plan writes: they are +> gitignored, untracked, never committed, and no artifact of this feature is read from or written to +> them. Every evidence artifact this plan produces lives under `/evidence//`, and the +> coverage numbers this plan cites come from the direct `Invoke-Pester` runs whose `OutputPath` is an +> explicit `/evidence//` path. The correction is recorded rather than silently fixed, +> because the original sentence stated a measured fact that measurement did not support. See +> `/evidence/other/evidence-location-audit.2026-08-10T23-20.md` for the full analysis. + +**(c) Neither settings file exists; bundled MCP settings are used.** +`scripts/powershell/PoshQC/settings/pester.runsettings.psd1` -> absent. +`config/poshqc-scan.json` -> absent. No attempt is made to create, restore, or point at either. The +absence of `config/poshqc-scan.json` is exactly why this plan mandates an explicit `scan_folders` +argument on every call instead of relying on an unscoped default set. + +**(d) Pester version.** 5.6.1, which satisfies the `>= 5.0` requirement. + +All four items match the plan's recorded values. No halt condition is triggered and no invocation +needs to be improvised. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/prechange-generator-parity.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/prechange-generator-parity.2026-08-10T22-30.md new file mode 100644 index 000000000..c59594e32 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/prechange-generator-parity.2026-08-10T22-30.md @@ -0,0 +1,58 @@ +# PRE-CHANGE Generator-Parity A/B (P0-T11) + +Timestamp: 2026-08-10T22-30 + +Deterministic A/B over a fixed committed input. This is **not** a test-suite run; it dot-sources +unmodified `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and evaluates +`Get-CoberturaCoverageSummary` against the committed **raw** `dotnet-coverage` document, whose own +root attributes are the generator's ground truth. + +Input: +`docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/baseline/coverage-baseline.cobertura.xml` +(17,473,869 bytes, raw generator output — absolute filenames, no `` element). + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +. (Join-Path $root 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1') +$sample = Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\baseline\coverage-baseline.cobertura.xml' +[xml]$doc = Get-Content -LiteralPath $sample -Raw -Encoding UTF8 +'INPUT root: lines-valid={0} lines-covered={1} branches-valid={2} branches-covered={3}' -f ` + $doc.coverage.'lines-valid', $doc.coverage.'lines-covered', $doc.coverage.'branches-valid', $doc.coverage.'branches-covered' +Get-CoberturaCoverageSummary -XmlDocument $doc | Format-List +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +INPUT root: lines-valid=79957 lines-covered=56124 branches-valid=23109 branches-covered=13472 + +LineRate : 0.702848 +BranchRate : 0.582976 +LinesCovered : 113219 +LinesValid : 161086 +BranchesCovered : 26944 +BranchesValid : 46218 +``` + +## Recorded figures + +| Quantity | Input document's own root attribute (ground truth) | `Get-CoberturaCoverageSummary` PRE-CHANGE | Inflated? | +| --- | --- | --- | --- | +| `lines-valid` / `LinesValid` | **79957** | **161086** | yes (+81129) | +| `lines-covered` / `LinesCovered` | **56124** | **113219** | yes (+57095) | +| `branches-valid` / `BranchesValid` | **23109** | **46218** | yes (+23109, exactly 2x) | +| `branches-covered` / `BranchesCovered` | **13472** | **26944** | yes (+13472, exactly 2x) | + +All four pre-change values are concrete integers and each is strictly greater than its ground-truth +counterpart. `LinesValid = 161086` matches the plan's stated expectation exactly. The branch figures +are exactly double the ground truth, which is the signature of the descendant-axis double count at +`Invoke-MSTestWithCoverage.Helpers.ps1:122` combined with the branch accumulator at `:128-131` +living inside that same loop. + +The document's own `line-rate` is `0.7019272859161799` and the pre-change recomputation reports +`0.702848`; the *rate* is nearly unchanged while the *counts* are roughly doubled, which is the +same trap § Fixture-Design Trap describes for `branch-rate`. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/prechange-package-filtered.2026-08-10T22-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/prechange-package-filtered.2026-08-10T22-30.md new file mode 100644 index 000000000..ed4f0e9d8 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/baseline/prechange-package-filtered.2026-08-10T22-30.md @@ -0,0 +1,49 @@ +# PRE-CHANGE Package-Filtered A/B (P0-T12) + +Timestamp: 2026-08-10T22-30 + +Deterministic A/B over a fixed committed input. Reprocesses the already post-processed committed +document through the full `ConvertTo-KoverageCoberturaXml` pipeline (package filtering, filename +normalization, class merging, root-attribute rewrite) against **unmodified** +`scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. + +Input: +`docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/qa-gates/coverage-final.cobertura.xml` +(10,398,170 bytes; 186,913 lines). + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +. (Join-Path $root 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1') +$sample = Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\qa-gates\coverage-final.cobertura.xml' +$content = Get-Content -LiteralPath $sample -Raw -Encoding UTF8 +[xml]$out = ConvertTo-KoverageCoberturaXml -XmlContent $content -RepoRoot $root -PathSeparator '\' +'lines-valid={0} lines-covered={1} line-rate={2} branches-valid={3} branches-covered={4} branch-rate={5}' -f ` + $out.coverage.'lines-valid', $out.coverage.'lines-covered', $out.coverage.'line-rate', ` + $out.coverage.'branches-valid', $out.coverage.'branches-covered', $out.coverage.'branch-rate' +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +lines-valid=110849 lines-covered=94937 line-rate=0.856453 branches-valid=27848 branches-covered=22001 branch-rate=0.790039 +``` + +## Recorded figures + +| Quantity | PRE-CHANGE value | +| --- | --- | +| `lines-valid` | **110849** | +| `lines-covered` | **94937** | +| `line-rate` | **0.856453** | +| `branches-valid` | 27848 | +| `branches-covered` | 22001 | +| `branch-rate` | 0.790039 | + +All three plan-required figures (`lines-valid = 110849`, `lines-covered = 94937`, +`line-rate = 0.856453`) reproduce exactly. The reprocessed output is byte-identical in these six +attributes to the input document's own committed root attributes, which is expected: the input was +itself produced by this same defective code path, so reprocessing it is idempotent pre-change. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/issue-updates/followups-441.2026-08-10T23-25.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/issue-updates/followups-441.2026-08-10T23-25.md new file mode 100644 index 000000000..7f76f3665 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/issue-updates/followups-441.2026-08-10T23-25.md @@ -0,0 +1,269 @@ +# Follow-Up Issue Candidates — #441 / #478 (P6-T1 .. P6-T5) + +Timestamp: 2026-08-10T23-25 + +PostedAs: unknown + +Four follow-up candidates were surfaced by the research and by `spec.md` § Rollout & Follow-up. +**None of the four is fixed in this change** (verified by P6-T6). Each was to be filed through the +MCP promotion lifecycle — `mcp__drm-copilot__new_potential_bug_entry` followed by +`mcp__drm-copilot__potential_to_issue` — rather than left as prose. + +Command: + +``` +mcp__drm-copilot__new_potential_bug_entry (attempted for candidates 1-4) +mcp__drm-copilot__potential_to_issue (attempted for candidates 1-4) +``` + +EXIT_CODE: 1 (tools unavailable; no invocation was possible) + +Output Summary: + +``` +POSTING BLOCKED for all four candidates. +Blocking condition: the promotion-lifecycle MCP tools are not exposed in this executing session. +gh CLI itself IS available and authenticated (drmoisan, scopes: gist, read:org, repo, workflow), +so the blockage is tool exposure, not GitHub connectivity. +Zero issue numbers were obtained. AC-20 is left UNCHECKED. +``` + +--- + +# POSTING BLOCKED + +## Blocking condition and exact tool state + +The MCP tool surface exposed to the executing agent in this session consists of exactly four tools: + +``` +mcp__drm-copilot__run_poshqc_format +mcp__drm-copilot__run_poshqc_analyze +mcp__drm-copilot__run_poshqc_test +mcp__drm-copilot__run_poshqc_analyze_autofix +``` + +Neither `mcp__drm-copilot__new_potential_bug_entry` nor `mcp__drm-copilot__potential_to_issue` is +present. There is therefore no error text from the promotion tool to quote: **the tools could not be +invoked at all, because they are not exposed in this session.** This is the second of the two +blocking conditions the plan's § Phase 6 Availability branch enumerates ("or the promotion MCP tools +are not exposed in the executing session at all"). + +For completeness, the underlying GitHub transport is **not** the problem: + +``` +$ gh auth status +github.com + ✓ Logged in to github.com account drmoisan (keyring) + - Active account: true + - Git operations protocol: https + - Token scopes: 'gist', 'read:org', 'repo', 'workflow' +``` + +`gh issue create` was **deliberately not used as a substitute.** The plan requires these follow-ups +to be filed *through the promotion lifecycle*, which creates a potential-entry record and then +promotes it; filing a bare issue with `gh` would bypass the mandated lifecycle and produce an issue +with no promotion provenance. Per the plan, fabricating or improvising an issue number is +prohibited. + +## Consequence + +- **P6-T1 through P6-T4 are checked off** on the basis of this recorded `POSTING BLOCKED` entry. + Each task's obligation is to attempt the filing and record the outcome truthfully, which is + discharged here. +- **AC-20 is left UNCHECKED.** AC-20 asserts that issue numbers exist and are recorded; no issue + number exists, so checking it off would certify a false statement. The reason is carried into the + P7-T22 AC status summary and the completion report. +- **Recommended follow-up action for `epic-orchestrator`:** re-run Phase 6 in a session where the + promotion MCP tools are exposed, using the four prepared entries below verbatim. + +--- + +## Prepared entries (verbatim, ready to file) + +### Candidate 1 + +**Title:** `Package-level line-rate and branch-rate are never recomputed after Cobertura package filtering and class merging` + +**Body:** + +> `ConvertTo-KoverageCoberturaXml` in `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` writes +> only the six root `` attributes and the `line-rate` / `branch-rate` / `complexity` +> attributes of **merged** class elements. It never recomputes `` or +> ``. +> +> After package filtering (which removes `.Test` packages) and class merging (which unions +> same-filename classes), every surviving `` therefore carries the rate the generator +> emitted for a different, larger class set. Those values are stale. +> +> The stale values are consumed by `scripts/temp-extract-coverage.ps1:47`, which reads +> `$pkg.'line-rate'` for per-assembly reporting. +> +> Deliberately out of scope for #441 / #478: recomputing package-level rates widens the diff without +> serving either issue, and `CLAUDE.md` § Bugfix Workflow step 2 mandates the minimal targeted fix. +> Recorded as follow-up candidate 1 in +> `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +**Labels (suggested):** `bug`, `coverage-tooling` + +--- + +### Candidate 2 + +**Title:** `A merged Cobertura class retains only the primary class's , so its methods do not account for all of its class-level lines` + +**Body:** + +> `Merge-CoberturaClassesByFilename` unions the class-level `` of same-filename classes but +> leaves `` un-merged: the merged class carries only the primary class's `` +> children. The emitted document's method-level lines therefore do not account for all of the +> merged class's class-level lines. For +> `QuickFiler\Controllers\QfcHomeController.Iteration.cs` in the #424 sample, the merged class-level +> rollup has 56 lines while the retained `` describe only 24. +> +> This was deliberately **not** fixed in #441 / #478, for recorded reasons: +> +> - Sibling classes sharing a filename are compiler-generated partners (`Foo` and `Foo.<>c`, async +> state machines) that routinely both declare `name=".ctor" signature="()"`. Appending sibling +> `` elements produces duplicate `(name, signature)` pairs, breaking any consumer that +> keys methods that way — including the per-method `line-rate` technique this repository uses for +> coverage-delta work. +> - Deduplicating by `(name, signature)` would be worse: it discards genuinely distinct methods. +> - Stripping `` was rejected outright: it destroys per-method `line-rate` data that +> coverage-delta work actively relies on. +> +> Fixture F6 in `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` currently **pins** +> the existing behaviour (methods neither merged nor stripped). Any fix here must update F6 +> deliberately, not incidentally. +> +> Recorded as follow-up candidate 2 in +> `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +**Labels (suggested):** `bug`, `coverage-tooling` + +--- + +### Candidate 3 + +**Title:** `Invoke-MSTestWithCoverage.ps1 test-assembly discovery lacks a \.claude\ exclusion and picks up stale sibling-worktree assemblies` + +**Body:** + +> `scripts/vscode/Invoke-MSTestWithCoverage.ps1:296-302` filters discovered `*.Test.dll` paths on +> `\bin\\`, `\obj\` and `\ref\` only. There is no `\.claude\` guard. +> +> Running with `-SearchRoot .` from the main checkout (`C:\Users\DanMoisan\repos\TaskMaster`) +> therefore descends into `.claude\worktrees\agent-*\**` and picks up stale sibling-worktree +> assemblies, producing bogus `AssemblyInitialize` signature failures and a coverage figure computed +> over the wrong assembly set. +> +> Suggested fix: add `-and $_.FullName -notmatch '\\\.claude\\'` to the existing discovery filter. +> +> Deliberately out of scope for #441 / #478: that is a production behaviour change to a file those +> issues do not otherwise touch, and AC-18 pins the #441 diff to exactly two source files. +> Recorded as follow-up candidate 3 in +> `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +**Labels (suggested):** `bug`, `dev-tooling` + +--- + +### Candidate 4 + +**Title:** `Stored agent memory records an incorrect generalization about Cobertura root-attribute deduplication` + +**Body:** + +> `.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md:34-36` +> asserts that the repository-wide root `` attributes "are already deduped and match a +> per-package all-descendant sum in this repo, so repo-level figures need no adjustment." +> +> That is true **only of raw `dotnet-coverage` output**. It is false for any post-processed +> `ConvertTo-KoverageCoberturaXml` artifact, where (before #441 was fixed) the root attributes *were* +> the all-descendant sum — the very defect #441 corrects. +> +> Measured on the two committed samples: +> +> - raw `coverage-baseline.cobertura.xml`: class-level `` count 79957 **equals** its own +> `lines-valid="79957"`; the all-descendant count is 161086. +> - post-processed `coverage-final.cobertura.xml`: the all-descendant count 110849 **equals** its +> emitted `lines-valid="110849"`; the class-level count is 62345. +> +> Now that #441 has landed, the memory should be corrected to state the distinction between raw and +> post-processed documents explicitly, so a future agent does not skip a needed adjustment. +> +> Recorded as follow-up candidate 4 in +> `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +**Labels (suggested):** `documentation`, `agent-memory` + +--- + +## Entry summary (exactly four entries, one per candidate) + +Superseded by the RESOLUTION section below. Retained as the record of the executing session's state. + +| # | Candidate | Issue number | URL | Status | +| --- | --- | --- | --- | --- | +| 1 | Package-level rates never recomputed | — | — | **POSTING BLOCKED** (superseded) | +| 2 | Merged class retains only the primary `` | — | — | **POSTING BLOCKED** (superseded) | +| 3 | `Invoke-MSTestWithCoverage.ps1` lacks a `\.claude\` discovery exclusion | — | — | **POSTING BLOCKED** (superseded) | +| 4 | Agent memory records an incorrect generalization | — | — | **POSTING BLOCKED** (superseded) | + +--- + +# RESOLUTION — filed by the orchestrator + +Timestamp: 2026-08-10T23-50 + +PostedAs: body + +Command: + +``` +mcp__drm-copilot__new_potential_bug_entry (candidates 1-4) +mcp__drm-copilot__potential_to_issue (candidates 1-4, promotion_type=bug, work_mode=minor-audit) +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +All four candidates filed through the MCP promotion lifecycle. The promotion tools were unavailable +to the executing atomic-executor session but ARE exposed to the orchestrator session, so the +POSTING BLOCKED branch above was resolved rather than deferred. Four issues created: #529, #530, +#531, #532. Each potential entry was authored from the prepared body verbatim, then promoted; the +promotion tool moved each entry to docs/features/potential/promoted/ and created the GitHub issue. +No active feature folder was created for any of the four: they are follow-ups, not work started now. +``` + +## Resolved entry summary (exactly four entries, one per candidate) + +| # | Candidate | Issue number | URL | Status | +| --- | --- | --- | --- | --- | +| 1 | Package-level `line-rate` / `branch-rate` never recomputed after filtering and merging | **529** | https://github.com/drmoisan/TaskMaster/issues/529 | **FILED** | +| 2 | Merged Cobertura class retains only the primary class's `` | **530** | https://github.com/drmoisan/TaskMaster/issues/530 | **FILED** | +| 3 | `Invoke-MSTestWithCoverage.ps1` discovery lacks a `\.claude\` exclusion | **531** | https://github.com/drmoisan/TaskMaster/issues/531 | **FILED** | +| 4 | Agent memory records an incorrect Cobertura dedup generalization | **532** | https://github.com/drmoisan/TaskMaster/issues/532 | **FILED** | + +## Promoted potential-entry provenance + +| # | Promoted potential entry | +| --- | --- | +| 1 | `docs/features/potential/promoted/2026-08-10-cobertura-package-rates-not-recomputed.md` | +| 2 | `docs/features/potential/promoted/2026-08-10-cobertura-merged-class-methods-incomplete.md` | +| 3 | `docs/features/potential/promoted/2026-08-10-mstest-coverage-discovery-claude-worktree-exclusion.md` | +| 4 | `docs/features/potential/promoted/2026-08-10-agent-memory-cobertura-dedup-generalization-wrong.md` | + +## None of the four is fixed in this change + +Re-verified after filing, per P6-T6: the diff touches exactly two source files +(`scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, +`tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`), and +`git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1 scripts/temp-extract-coverage.ps1` +returns empty. Candidate 4's target file +(`.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md`) +is likewise unmodified by the fix commit. + +AC-20 is therefore satisfied and is checked off in `spec.md`. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/ac-status-summary.2026-08-10T23-30.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/ac-status-summary.2026-08-10T23-30.md new file mode 100644 index 000000000..75d9ed24b --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/ac-status-summary.2026-08-10T23-30.md @@ -0,0 +1,82 @@ +# Acceptance Criteria Status Summary (P7-T22) + +Timestamp: 2026-08-10T23-30 + +Narrative artifact. It records no command and therefore carries no `Command:` or `EXIT_CODE:` field, +which is permitted for narrative artifacts by the 2026-08-10T21-40 amendment recorded in `spec.md` +§ Acceptance Criteria. Its existence and intended path were declared in advance in the `## Final +sweep` section of `/evidence/other/evidence-location-audit.2026-08-10T23-20.md`. + +### Acceptance Criteria Status + +- **Source:** `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` + (work mode `full-bug`, so `spec.md` is the sole authoritative AC source) +- **Total AC items:** 20 +- **Checked off (delivered):** 20 +- **Remaining (unchecked):** 0 +- **Items remaining:** none + +> **Superseding update, 2026-08-10T23-50 (orchestrator).** This summary originally recorded 19/20 +> with AC-20 unchecked. AC-20 has since been satisfied: the orchestrator session DOES expose the +> promotion-lifecycle MCP tools that the executing `atomic-executor` session lacked, so the +> `POSTING BLOCKED` branch was resolved rather than deferred. All four candidates were filed through +> `new_potential_bug_entry` -> `potential_to_issue` as issues **#529, #530, #531, #532**. See the +> RESOLUTION section of `evidence/issue-updates/followups-441.2026-08-10T23-25.md`. The narrative +> below is retained unedited as the record of the executing session's state; the counts above and +> the AC-20 row are the current values. + +## Per-item detail + +| AC | Subject | Status | Evidence pointer | +| --- | --- | --- | --- | +| AC-1 | generator parity (79957 / 56124 / 23109 / 13472) | **[x]** | `evidence/qa-gates/postchange-generator-parity.2026-08-10T23-15.md` | +| AC-2 | pre-change figure `LinesValid = 161086` | **[x]** | `evidence/baseline/prechange-generator-parity.2026-08-10T22-30.md` | +| AC-3 | package-filtered A/B (62345 / 53013 / 0.850317) | **[x]** | `evidence/qa-gates/postchange-package-filtered.2026-08-10T23-15.md` | +| AC-4 | per-file merged rate (F3, `'0.6'`, five ascending lines) | **[x]** | `evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md` | +| AC-5 | branch counts deduplicated (F2, 2 / 1) | **[x]** | `evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md` | +| AC-6 | helper contract | **[x]** | `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1:161-259`; `evidence/other/helper-branch-test-map.2026-08-10T23-10.md` | +| AC-7 | defect removed at its one site | **[x]** | P2-T4 fixed-string search: `'.//lines/line'` -> 0 matches | +| AC-8 | correct site untouched (`:217-268` byte-identical) | **[x]** | `evidence/qa-gates/union-builder-byte-identity.2026-08-10T22-55.md` (incl. the P4-T10 post-format re-verification) | +| AC-9 | delegation replaced | **[x]** | P2-T4 fixed-string search: `$classSummaryXml` -> 0 matches | +| AC-10 | structure preserved (F6) | **[x]** | `evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md` | +| AC-11 | six fixtures present and passing | **[x]** | `evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md` | +| AC-12 | fail-before evidence (6/4, 4/2, `'0.75'`, 3/2) | **[x]** | `evidence/regression-testing/fail-before-f1-f4.2026-08-10T22-45.md` | +| AC-13 | helper precedence branches covered | **[x]** | `evidence/regression-testing/helper-unit-tests.2026-08-10T23-05.md` | +| AC-14 | zero existing tests broken | **[x]** | `evidence/baseline/pester-baseline.2026-08-10T22-30.md`, `evidence/qa-gates/pester-final.2026-08-10T23-10.md`, `evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md` (0-deletions numstat) | +| AC-15 | toolchain green (no new analyzer findings) | **[x]** | `evidence/qa-gates/poshqc-format.2026-08-10T23-10.md`, `poshqc-analyze.2026-08-10T23-10.md`, `pester-final.2026-08-10T23-10.md` | +| AC-16 | canonical evidence locations | **[x]** | `evidence/other/evidence-location-audit.2026-08-10T23-20.md` § Final sweep | +| AC-17 | no threshold re-tuned | **[x]** | `evidence/qa-gates/threshold-no-change.2026-08-10T23-10.md`, `evidence/other/threshold-handoff-494.2026-08-10T23-15.md` | +| AC-18 | scope boundary held (exactly two source files) | **[x]** | `evidence/qa-gates/scope-lock.2026-08-10T23-10.md` | +| AC-19 | file ceiling (455 < 500) | **[x]** | `evidence/qa-gates/file-size-audit.2026-08-10T23-10.md` | +| **AC-20** | **follow-ups filed (#529, #530, #531, #532)** | **[x]** | `evidence/issue-updates/followups-441.2026-08-10T23-25.md` § RESOLUTION | + +## Why AC-20 was left unchecked by the executing session (historical; resolved 2026-08-10T23-50) + +AC-20 asserts that the four follow-up candidates "are filed as GitHub issues through the promotion +lifecycle, **with their issue numbers recorded** in this feature's evidence." + +**No issue number exists**, so checking the item off would certify a false statement. + +The blocking condition is that the promotion-lifecycle MCP tools +`mcp__drm-copilot__new_potential_bug_entry` and `mcp__drm-copilot__potential_to_issue` are **not +exposed in the executing session at all**. The agent's MCP tool surface consists solely of +`run_poshqc_format`, `run_poshqc_analyze`, `run_poshqc_test` and `run_poshqc_analyze_autofix`, so +the promotion tools could not be invoked and there is no tool error text to quote. The `gh` CLI is +itself available and authenticated (account `drmoisan`, scopes `gist, read:org, repo, workflow`), so +the blockage is tool exposure rather than GitHub connectivity. `gh issue create` was deliberately +**not** used as a substitute, because the plan requires filing *through the promotion lifecycle*; +a bare `gh` issue would carry no promotion provenance. + +Per the plan's § Phase 6 Availability branch this is the single sanctioned non-numeric outcome: +tasks P6-T1..P6-T4 are checked off on the basis of the recorded `POSTING BLOCKED` entry (their +obligation being to attempt the filing and record the outcome truthfully), while **only AC-20** +remains unchecked, because only AC-20 asserts that issue numbers exist. + +All four candidates are prepared verbatim — title, body and suggested labels — in +`/evidence/issue-updates/followups-441.2026-08-10T23-25.md`, ready to be filed unchanged. +**Recommended next action for `epic-orchestrator`:** re-run Phase 6 in a session where the promotion +MCP tools are exposed, then check off AC-20. + +P6-T6 independently confirms that none of the four candidates was fixed in this change +(`evidence/qa-gates/followups-not-fixed.2026-08-10T23-25.md`), so the deferral is complete and +correct even though the filing is outstanding. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/evidence-location-audit.2026-08-10T23-20.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/evidence-location-audit.2026-08-10T23-20.md new file mode 100644 index 000000000..dbb11b8e4 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/evidence-location-audit.2026-08-10T23-20.md @@ -0,0 +1,290 @@ +# Evidence Location and Schema Audit (P5-T5, and P7-T20 final sweep) + +Timestamp: 2026-08-10T23-20 + +Audits every artifact this plan has produced against +`.claude/skills/evidence-and-timestamp-conventions/SKILL.md` and against `spec.md` AC-16. + +## Sweep 1 — Phases 0 through 5 (P5-T5) + +This sweep deliberately **excludes** the Phase 6 and Phase 7 artifacts, which do not exist yet. They +are covered by the final sweep appended by P7-T20, and AC-16 is certified against that final sweep, +not against this one. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +$ev = Join-Path $root 'docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\evidence' +Get-ChildItem -Path $ev -Recurse -File | ForEach-Object { $_.FullName.Substring($ev.Length + 1) } +Get-ChildItem -Path $ev -Recurse -File -Filter '*.md' | ForEach-Object { + $t = Get-Content -LiteralPath $_.FullName -Raw + '{0} | Timestamp={1} Command={2} EXIT_CODE={3} OutputSummary={4}' -f ` + $_.Name, $t.Contains('Timestamp:'), $t.Contains('Command:'), $t.Contains('EXIT_CODE:'), $t.Contains('Output Summary:') +} +Get-ChildItem -Path (Join-Path $root 'artifacts') -Recurse -File +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +31 files under /evidence/, all within {baseline, regression-testing, qa-gates, other}. +26 markdown artifacts: 24 command-step artifacts carry all four schema fields; +2 narrative artifacts carry Timestamp only (permitted by the amended AC-16). +5 coverage XML data files carry no schema fields (they are tool output referenced by their + parent artifacts, not artifacts in their own right). +No evidence artifact exists under any artifacts/ path. +``` + +### 1.1 Location inventory (31 files, all canonical) + +**`/evidence/baseline/` — 10 files** + +| File | Kind | +| --- | --- | +| `phase0-instructions-read.md` | command-step | +| `git-baseline.2026-08-10T22-30.md` | command-step | +| `poshqc-tool-surface.2026-08-10T22-30.md` | command-step | +| `poshqc-format.2026-08-10T22-30.md` | command-step | +| `poshqc-analyze.2026-08-10T22-30.md` | command-step | +| `pester-baseline.2026-08-10T22-30.md` | command-step | +| `prechange-generator-parity.2026-08-10T22-30.md` | command-step | +| `prechange-package-filtered.2026-08-10T22-30.md` | command-step | +| `assumption2-subset-proof.2026-08-10T22-30.md` | command-step | +| `pester-coverage-baseline.2026-08-10T22-30.xml` | coverage data | + +**`/evidence/regression-testing/` — 6 files** + +| File | Kind | +| --- | --- | +| `fail-before-f1-f4.2026-08-10T22-45.md` | command-step | +| `pass-after-f1-f6.2026-08-10T22-55.md` | command-step | +| `helper-unit-tests.2026-08-10T23-05.md` | command-step | +| `pester-coverage-fail-before.2026-08-10T22-45.xml` | coverage data | +| `pester-coverage-pass-after.2026-08-10T22-55.xml` | coverage data | +| `pester-coverage-helper-unit-tests.2026-08-10T23-05.xml` | coverage data | + +**`/evidence/qa-gates/` — 13 files** + +| File | Kind | +| --- | --- | +| `union-builder-byte-identity.2026-08-10T22-55.md` | command-step (two sweeps: P2-T5 + P4-T10) | +| `poshqc-format.2026-08-10T23-10.md` | command-step | +| `poshqc-analyze.2026-08-10T23-10.md` | command-step | +| `pester-final.2026-08-10T23-10.md` | command-step | +| `toolchain-clean-pass.2026-08-10T23-10.md` | command-step | +| `file-size-audit.2026-08-10T23-10.md` | command-step | +| `coverage-delta.2026-08-10T23-10.md` | command-step | +| `scope-lock.2026-08-10T23-10.md` | command-step | +| `threshold-no-change.2026-08-10T23-10.md` | command-step | +| `postchange-generator-parity.2026-08-10T23-15.md` | command-step | +| `postchange-package-filtered.2026-08-10T23-15.md` | command-step | +| `coverage-arithmetic-delta.2026-08-10T23-15.md` | command-step | +| `pester-coverage-final.2026-08-10T23-10.xml` | coverage data | + +**`/evidence/other/` — 2 files** + +| File | Kind | +| --- | --- | +| `helper-branch-test-map.2026-08-10T23-10.md` | **narrative** | +| `threshold-handoff-494.2026-08-10T23-15.md` | **narrative** | + +All four sub-paths used — `baseline`, `regression-testing`, `qa-gates`, `other` — are canonical +under `.claude/skills/evidence-and-timestamp-conventions/SKILL.md`. `issue-updates/` is not yet +populated; it is written in Phase 6. + +### 1.2 Schema conformance + +- **24 command-step artifacts**: every one carries `Timestamp:`, `Command:`, `EXIT_CODE:` **and** + `Output Summary:`. Verified mechanically by the `.Contains(...)` check above; all four columns + returned `True` for all 24. +- **2 narrative artifacts** (`helper-branch-test-map`, `threshold-handoff-494`): each carries + `Timestamp:` and records no command, so it carries no `Command:` or `EXIT_CODE:`. This is + **permitted**: the 2026-08-10T21-40 amendment recorded in `spec.md` § Acceptance Criteria scopes + the `Command:`/`EXIT_CODE:` requirement to command-step artifacts, precisely because requiring + those fields of a narrative artifact would make AC-16 unsatisfiable. +- **5 coverage XML files**: JaCoCo reports written by the direct `Invoke-Pester` runs. They are + measurement data referenced by their parent markdown artifacts (each of which records the + `OutputPath`), not artifacts in their own right, and carry no schema fields by nature. + +### 1.3 Forbidden-path search + +- **SearchScope:** the whole worktree, specifically + `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\artifacts\` +- **SearchPatterns:** `artifacts/**` — in particular the forbidden evidence sub-paths + `artifacts/baselines/`, `artifacts/baseline/`, `artifacts/qa/`, `artifacts/qa-gates/`, + `artifacts/evidence/`, `artifacts/coverage/`, `artifacts/regression-testing/`, + `artifacts/post-change/` +- **SearchResult for evidence artifacts:** **none.** Not one artifact produced by this plan resides + under any `artifacts/` path. Every forbidden sub-path listed above is absent from the worktree + entirely. + +Four files do exist under `artifacts/`, and each is accounted for as **not** evidence produced by +this plan: + +| Path | Provenance | Status | +| --- | --- | --- | +| `artifacts/orchestration/orchestrator-state.json` | written by `epic-orchestrator` before this execution began (mtime 22:28, prior to the first Phase 0 command) | **allowed** — `artifacts/orchestration/` is the one permitted non-evidence `artifacts/` sub-path | +| `artifacts/pester/pester-junit.xml` | tool output written by the bundled MCP `run_poshqc_test` | producer output, not evidence | +| `artifacts/pester/powershell-coverage.xml` | tool output written by the bundled MCP `run_poshqc_test` | producer output, not evidence | +| `artifacts/pester/powershell-coverage.koverage.xml` | tool output written by the bundled MCP `run_poshqc_test` | producer output, not evidence | + +All four are **gitignored** (`.gitignore:57` ignores `artifacts/` wholesale, confirmed by +`git check-ignore -v`) and **untracked**, so none will be committed. No artifact of this feature is +read from or written to them; the plan's coverage numbers come exclusively from the direct +`Invoke-Pester` runs whose `OutputPath` is an explicit `/evidence//` path. + +### 1.4 Correction raised by this sweep + +The P0-T13 artifact originally asserted that `run_poshqc_test` "writes no coverage artifact into the +workspace, evidenced by an empty `git status --porcelain` immediately afterwards." **That assertion +was wrong, and the instrument did not support it.** The tool does write the three `artifacts/pester/` +files above; they are invisible to `git status --porcelain` because `artifacts/` is gitignored, so an +empty porcelain proves only that nothing *tracked* changed. + +The claim has been corrected in place with a dated `CORRECTION` block appended to +`/evidence/baseline/poshqc-tool-surface.2026-08-10T22-30.md`, rather than silently edited +away. No conclusion in this plan depends on the retracted sentence: the MCP call was treated as +non-probative throughout for independent reasons (it carries no verdict and no counts), and every +figure was taken from the direct Pester runs. + +## Sweep 1 verdict + +**PASS.** Every artifact produced by Phases 0 through 5 resides under +`/evidence/{baseline,regression-testing,qa-gates,other}/`; every command-step artifact +carries all four schema fields; every narrative artifact carries `Timestamp:`; and no evidence +artifact was written under any `artifacts/` path. + +--- + +## Final sweep + +Timestamp: 2026-08-10T23-30 + +Re-runs the audit over the **full** artifact set, including the Phase 6 and Phase 7 artifacts that +did not exist at Sweep 1. **AC-16 is certified against this sweep, not against Sweep 1.** This +section is written before the AC-16 check-off (P7-T21), so the criterion is certified against +evidence that already exists on disk. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +$ev = Join-Path $root 'docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\evidence' +Get-ChildItem -Path $ev -Recurse -File | ForEach-Object { $_.FullName.Substring($ev.Length + 1) } +Get-ChildItem -Path $ev -Recurse -File -Filter '*.md' | ForEach-Object { + $t = Get-Content -LiteralPath $_.FullName -Raw + '{0} | TS={1} Cmd={2} EXIT={3} OS={4}' -f ` + $_.Name, $t.Contains('Timestamp:'), $t.Contains('Command:'), $t.Contains('EXIT_CODE:'), $t.Contains('Output Summary:') +} +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +34 files under /evidence/ across five canonical sub-paths: + baseline 10 + regression-testing 6 + qa-gates 14 + other 3 + issue-updates 1 = 34. +29 markdown artifacts: 27 command-step artifacts carry all four schema fields; + 2 narrative artifacts carry Timestamp: only (permitted). +5 coverage XML data files carry no schema fields (tool output referenced by their parents). +Forbidden-path search: SearchResult none. +``` + +### Complete enumeration of every file under `/evidence/` (34 files) + +**`baseline/` (10)** + +1. `baseline\assumption2-subset-proof.2026-08-10T22-30.md` — command-step +2. `baseline\git-baseline.2026-08-10T22-30.md` — command-step +3. `baseline\pester-baseline.2026-08-10T22-30.md` — command-step +4. `baseline\pester-coverage-baseline.2026-08-10T22-30.xml` — coverage data +5. `baseline\phase0-instructions-read.md` — command-step +6. `baseline\poshqc-analyze.2026-08-10T22-30.md` — command-step +7. `baseline\poshqc-format.2026-08-10T22-30.md` — command-step +8. `baseline\poshqc-tool-surface.2026-08-10T22-30.md` — command-step (carries the dated CORRECTION block) +9. `baseline\prechange-generator-parity.2026-08-10T22-30.md` — command-step +10. `baseline\prechange-package-filtered.2026-08-10T22-30.md` — command-step + +**`regression-testing/` (6)** + +11. `regression-testing\fail-before-f1-f4.2026-08-10T22-45.md` — command-step +12. `regression-testing\helper-unit-tests.2026-08-10T23-05.md` — command-step +13. `regression-testing\pass-after-f1-f6.2026-08-10T22-55.md` — command-step +14. `regression-testing\pester-coverage-fail-before.2026-08-10T22-45.xml` — coverage data +15. `regression-testing\pester-coverage-helper-unit-tests.2026-08-10T23-05.xml` — coverage data +16. `regression-testing\pester-coverage-pass-after.2026-08-10T22-55.xml` — coverage data + +**`qa-gates/` (14)** + +17. `qa-gates\coverage-arithmetic-delta.2026-08-10T23-15.md` — command-step +18. `qa-gates\coverage-delta.2026-08-10T23-10.md` — command-step +19. `qa-gates\file-size-audit.2026-08-10T23-10.md` — command-step +20. `qa-gates\followups-not-fixed.2026-08-10T23-25.md` — command-step **(Phase 6, new since Sweep 1)** +21. `qa-gates\pester-coverage-final.2026-08-10T23-10.xml` — coverage data +22. `qa-gates\pester-final.2026-08-10T23-10.md` — command-step +23. `qa-gates\poshqc-analyze.2026-08-10T23-10.md` — command-step +24. `qa-gates\poshqc-format.2026-08-10T23-10.md` — command-step +25. `qa-gates\postchange-generator-parity.2026-08-10T23-15.md` — command-step +26. `qa-gates\postchange-package-filtered.2026-08-10T23-15.md` — command-step +27. `qa-gates\scope-lock.2026-08-10T23-10.md` — command-step +28. `qa-gates\threshold-no-change.2026-08-10T23-10.md` — command-step +29. `qa-gates\toolchain-clean-pass.2026-08-10T23-10.md` — command-step +30. `qa-gates\union-builder-byte-identity.2026-08-10T22-55.md` — command-step + +**`other/` (3)** + +31. `other\evidence-location-audit.2026-08-10T23-20.md` — command-step (this file) +32. `other\helper-branch-test-map.2026-08-10T23-10.md` — **narrative** +33. `other\threshold-handoff-494.2026-08-10T23-15.md` — **narrative** + +**`issue-updates/` (1)** + +34. `issue-updates\followups-441.2026-08-10T23-25.md` — command-step **(Phase 6, new since Sweep 1)** + +All five sub-paths in use — `baseline`, `regression-testing`, `qa-gates`, `other`, `issue-updates` +— are canonical under `.claude/skills/evidence-and-timestamp-conventions/SKILL.md` and are exactly +the five enumerated by the amended AC-16. + +### Schema conformance across the full set + +- **27 command-step markdown artifacts**: every one carries `Timestamp:`, `Command:`, `EXIT_CODE:` + and `Output Summary:`. All four columns returned `True` for all 27 in the mechanical check above. + This includes both artifacts new since Sweep 1 (`followups-441` and `followups-not-fixed`). +- **2 narrative markdown artifacts** (`helper-branch-test-map`, `threshold-handoff-494`): each + carries `Timestamp:`, records no command, and therefore carries no `Command:`/`EXIT_CODE:`. + Individually enumerated here as items 32 and 33, as AC-16 requires. Permitted by the + 2026-08-10T21-40 amendment recorded in `spec.md` § Acceptance Criteria, which scopes the + `Command:`/`EXIT_CODE:` requirement to command-step artifacts. +- **5 coverage XML data files** (items 4, 14, 15, 16, 21): JaCoCo reports written by the direct + `Invoke-Pester` runs, referenced by their parent markdown artifacts, carrying no schema fields by + nature. + +### Forbidden-path search (final) + +- **SearchScope:** `C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a\artifacts\` + and the entire worktree +- **SearchPatterns:** `artifacts/**`, specifically `artifacts/baselines/`, `artifacts/baseline/`, + `artifacts/qa/`, `artifacts/qa-gates/`, `artifacts/evidence/`, `artifacts/coverage/`, + `artifacts/regression-testing/`, `artifacts/post-change/` +- **SearchResult:** **none.** No evidence artifact for this feature exists under any `artifacts/` + path. The only files under `artifacts/` are the orchestrator checkpoint in the permitted + `artifacts/orchestration/` sub-path and three gitignored, untracked PoshQC producer outputs under + `artifacts/pester/`, all itemized and accounted for in Sweep 1 § 1.3. + +### One artifact is written after this sweep + +Exactly one artifact is created after this section: **`/evidence/other/ac-status-summary.2026-08-10T23-30.md`**, written by P7-T22. Its +intended path is stated here so the audit trail is complete. It is a **narrative** artifact — it +records no command and therefore carries no `EXIT_CODE:` field — and it carries `Timestamp:`. + +### Final sweep verdict + +**PASS.** Every one of the 34 artifacts resides under +`/evidence/{baseline,regression-testing,qa-gates,issue-updates,other}/`; every command-step +artifact carries `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`; every narrative +artifact carries `Timestamp:` and is individually enumerated; and no evidence artifact is written +under any `artifacts/` path. **AC-16 is satisfied.** diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/helper-branch-test-map.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/helper-branch-test-map.2026-08-10T23-10.md new file mode 100644 index 000000000..ea384b006 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/helper-branch-test-map.2026-08-10T23-10.md @@ -0,0 +1,41 @@ +# `Get-CoberturaClassLineSummary` — Branch-to-Test Map (P4-T7) + +Timestamp: 2026-08-10T23-10 + +This artifact is a **scenario-completeness map, not a coverage measurement.** It names, for every +branch of `Get-CoberturaClassLineSummary`, at least one `It` block that exercises it. The numeric +`>= 90%` new-code proof is carried by P4-T6 (39/40 = 97.50%); an enumeration of branches does not +satisfy a coverage threshold and is not offered as one. + +Function location in the post-change file: `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` +lines **161-259**. + +| # | Branch | Source | Named `It` block(s) that exercise it | Test file location | +| --- | --- | --- | --- | --- | +| 1 | **New-key insert** — line number not yet in the map | `:207-216` (`if (-not $lineMap.Contains($lineNumber))`, true path) | `counts each source line once when methods repeat the class-level rollup` (F1); also every other fixture, since the first entry for any line number takes this path | `Describe 'ConvertTo-KoverageCoberturaXml'` | +| 2 | **Repeat-key** — line number already present | `:218` onward (false path of the same `if`) | `deduplicates a repeated line number by taking the maximum hits value` (F4); `counts each source line once when methods repeat the class-level rollup` (F1) | `Describe 'ConvertTo-KoverageCoberturaXml'` | +| 3 | **Repeat-key `max(hits)`** — hits comparison on a repeated key | `:219` (`if ($hits -gt $existing.Hits)`) | `deduplicates a repeated line number by taking the maximum hits value` (F4) — line 5 appears with `hits=1` in `.ctor ()`, `hits=0` in `.ctor (int)` and `hits=1` in the class-level rollup, and the block asserts the deduplicated result `lines-valid` = `'1'`, `lines-covered` = `'1'` | `Describe 'ConvertTo-KoverageCoberturaXml'` | +| 4 | **Repeat-key branch promotion** — `branch=True` if either entry is a branch | `:223-225` (`if ($isBranch) { $existing.Branch = $true }`) | `counts each branch line once when methods repeat the class-level rollup` (F2) — line 12 carries `branch="True" condition-coverage="50% (1/2)"` on **both** axes, so the promotion assignment executes on the repeat key; asserts `branches-valid` = `'2'`, `branches-covered` = `'1'` | `Describe 'ConvertTo-KoverageCoberturaXml'` | +| 5 | **Precedence — candidate `Total` greater** | `:227-234` (first disjunct true) | `retains the candidate condition-coverage when its total is greater` — class-level `100% (2/2)` versus method-level `50% (2/4)`; asserts `TotalBranches` = 4, `CoveredBranches` = 2 | `Describe 'Get-CoberturaClassLineSummary'` | +| 6 | **Precedence — `Total` equal and `Covered` greater** | `:227-234` (second disjunct true) | `retains the candidate condition-coverage when totals tie and its covered count is greater` — class-level `0% (0/2)` versus method-level `50% (1/2)`; asserts `TotalBranches` = 2, `CoveredBranches` = 1 | `Describe 'Get-CoberturaClassLineSummary'` | +| 7 | **Precedence — neither condition holds** | `:227-234` (both disjuncts false; existing retained) | `retains the existing condition-coverage when neither precedence condition holds` — class-level `50% (2/4)` versus method-level `100% (2/2)`; asserts the class-level 4 / 2 survives | `Describe 'Get-CoberturaClassLineSummary'` | +| 8 | **Empty class** — no `` and no `` | `:196-197` selecting nothing; the `foreach` body never runs | `returns zero totals for a class with neither a lines nor a methods element` — asserts all four totals are 0 and `Should -Not -Throw` | `Describe 'Get-CoberturaClassLineSummary'` | +| 9 | **Rollup absent, methods present** (related boundary) | `:196` selects nothing, `:197` selects the method lines | `retains method-level lines when the class-level rollup element is absent` (F5) — asserts `lines-valid` = `'2'`, `lines-covered` = `'1'` | `Describe 'ConvertTo-KoverageCoberturaXml'` | +| 10 | **Accumulation — covered-line count** | `:242-244` (`if ($entry.Hits -gt 0)`), both outcomes | F1 (line 11 has `hits=0`, lines 10 and 12 have `hits=1`) | `Describe 'ConvertTo-KoverageCoberturaXml'` | +| 11 | **Accumulation — branch totals** | `:246-249` (`if ($entry.Branch)`), both outcomes | F2 (line 12 is a branch; lines 10 and 11 are not) | `Describe 'ConvertTo-KoverageCoberturaXml'` | + +**Every listed branch names at least one `It` block.** + +## Recorded qualification (do not read this map as a coverage claim) + +One statement inside branch 3 is not executed by any current test: **line 220, +`$existing.Hits = $hits`**, the assignment body of `if ($hits -gt $existing.Hits)`. The `if` +*condition* at line 219 is executed and evaluated; only the assignment body is not, because the +helper enumerates the class-level rollup first and in every fixture — and in both committed sample +documents — the class-level rollup already carries the maximum hits value for any repeated line +number, so a later candidate never exceeds it. F4 exercises the `max(hits)` rule in the direction +the real data produces and asserts the correct deduplicated outcome. + +This is stated here rather than papered over. It is quantified in P4-T6 (39 of 40 new-code lines +covered = 97.50%, against a `>= 90%` floor) and does not trigger the P4-T6 remediation path, which +fires only below a floor. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/review-findings-disposition.2026-08-11T00-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/review-findings-disposition.2026-08-11T00-10.md new file mode 100644 index 000000000..72ef4b8b5 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/review-findings-disposition.2026-08-11T00-10.md @@ -0,0 +1,56 @@ +# Review Findings Disposition (orchestrator) + +Timestamp: 2026-08-11T00-10 + +Narrative artifact. It records no command and therefore carries no `Command:` or `EXIT_CODE:` field, +which is permitted for narrative artifacts by the 2026-08-10T21-40 amendment recorded in `spec.md` +§ Acceptance Criteria. + +## Review verdict + +Feature review produced three artifacts at `/`: + +- `policy-audit.2026-08-10T23-35.md` +- `code-review.2026-08-10T23-35.md` +- `feature-audit.2026-08-10T23-35.md` + +Result: **blocking_count = 0** (zero FAIL findings and zero blocking-PARTIAL findings). All 20 +acceptance criteria in `spec.md` were independently verified PASS; no check mark required +unchecking. The remediation loop was therefore **not** entered, and no `remediation-inputs` artifact +was produced. Five non-blocking findings were recorded (2 Minor, 3 Informational). + +## Disposition of the two Minor findings + +Both Minor findings are dispositioned by filing a follow-up issue through the MCP promotion +lifecycle, not by widening this bugfix. Neither is a defect in the delivered behavior, and the +remediation loop's exit gate is keyed to blocking findings only. + +| Finding | Subject | Disposition | Issue | +| --- | --- | --- | --- | +| NF-1 | The `max(hits)` update assignment at `Helpers.ps1:220` is exercised by no test, so the dedup rule is pinned only for the first-entry-wins ordering | Filed as a follow-up. New-code coverage is 39/40 = 97.50% against a `>= 90%` floor, so no gate fails; the delivered arithmetic is correct and reproduces the oracle exactly. Adding a fixture now would mean re-opening a plan that is 85/85 complete and re-running the full audit for a Minor test-adequacy gap. | **#537** | +| NF-2 | `artifacts/pester/powershell-coverage.xml` (producer output of the bundled `run_poshqc_test`) records zero covered lines repo-wide | Filed as a follow-up. Verified independently by the orchestrator: aggregating all 1227 JaCoCo `` elements yields `LINE covered 0 / missed 16075`, `INSTRUCTION covered 0 / missed 21800`, `METHOD covered 0 / missed 1445`, `CLASS covered 0 / missed 168`. A literal zero repo-wide, for a head whose 19-test suite passes and whose direct-Pester capture measures the primary file at 90.59%, is an invalid capture by the tool. This branch changes no coverage-capture tooling, hook, or configuration, so the defect is pre-existing and out of this bugfix's mandated minimal scope. Candidate to fold into sibling feature #512 (toolchain gate fidelity). | **#536** | + +Note that the row 2.5 **FAIL** in `policy-audit.2026-08-10T23-35.md` is recorded honestly against +the artifact as it exists on disk. It is a verdict on an invalid measurement, not on this branch's +coverage, and it is dispositioned non-blocking on that basis. The change-scope coverage rows all +PASS: changed file 183/202 = 90.59% (`>= 85%`), new code 39/40 = 97.50% (`>= 90%`), and no +regression on changed lines (baseline 88.48%). + +## Threshold handoff to #494 (restated, unchanged) + +The corrected repository-wide line rate for the `-424` sample is **85.0317%** against the uniform +**85%** floor — a margin of **0.03 pp**. Recorded as fact in +`/evidence/other/threshold-handoff-494.2026-08-10T23-15.md`. **No threshold was changed by +this feature.** `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` returns +empty. Threshold reconciliation is owned by sibling feature #494 in wave 2. + +## Complete follow-up issue register for this feature + +| Source | Subject | Issue | +| --- | --- | --- | +| Plan Phase 6, candidate 1 | Package-level `line-rate` / `branch-rate` never recomputed after filtering and merging | #529 | +| Plan Phase 6, candidate 2 | Merged Cobertura class retains only the primary class's `` | #530 | +| Plan Phase 6, candidate 3 | `Invoke-MSTestWithCoverage.ps1` discovery lacks a `\.claude\` exclusion | #531 | +| Plan Phase 6, candidate 4 | Agent memory records an incorrect Cobertura dedup generalization | #532 | +| Review finding NF-2 | Bundled `run_poshqc_test` coverage capture records zero covered lines repo-wide | #536 | +| Review finding NF-1 | `max(hits)` update assignment is untested | #537 | diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/threshold-handoff-494.2026-08-10T23-15.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/threshold-handoff-494.2026-08-10T23-15.md new file mode 100644 index 000000000..ec322461e --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/threshold-handoff-494.2026-08-10T23-15.md @@ -0,0 +1,71 @@ +# Threshold Handoff to Child Feature #494 (P5-T4) + +Timestamp: 2026-08-10T23-15 + +This is a **record of fact and a handoff. It is not a proposal, and nothing in it is acted upon by +this feature.** + +## The measured fact + +The corrected repository-wide line rate for the #424 committed sample +(`docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/qa-gates/coverage-final.cobertura.xml`, +reprocessed through `ConvertTo-KoverageCoberturaXml` with the fix applied) is: + +| Quantity | Value | +| --- | --- | +| `lines-covered` | **53013** | +| `lines-valid` | **62345** | +| **`line-rate`** | **0.850317 = 85.0317%** | + +Measured in P5-T2; the pre-change figures for the same input were 94937 / 110849 / 0.856453 +(85.6453%), measured in P0-T12. + +## The threshold it is measured against + +`.claude/rules/general-unit-test.md` § Coverage Requirements states a **uniform line-coverage floor +of >= 85% across all tiers (T1-T4)**. `.claude/rules/quality-tiers.md` repeats the same figure in +its uniform gate matrix. (`CLAUDE.md` § UT2 states a laxer repository-wide floor of >= 80%; where +the two differ, the stricter 85% figure is the one recorded here.) + +**Margin: 85.0317% versus 85% = 0.03 percentage points.** + +The corrected figure is materially closer to the floor than the reported one: the defect was +inflating the reported rate by 0.61 pp for this document. The inflation is not uniform across +assemblies, because line duplication is not uniform across classes, so per-assembly margins will +differ from this repository-wide figure and must be re-measured rather than extrapolated. + +## Owner + +**Child feature #494 (epic `build-ci-coverage-gate-fidelity`, wave 2) owns threshold +reconciliation.** #494 runs after #457 (wave 1), which itself runs after this feature (wave 0). + +## What this feature does about it: nothing + +This feature **proposes no threshold change and makes no threshold edit.** Enforced mechanically by +P4-T9, which recorded: + +- `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` returns **empty + output** — no threshold-bearing file is touched; +- the single added line in the two changed source files matching `\b(85|90|75)\b` is a fixture rate + value (`0.75`, the defective per-file `line-rate` that fixture F3 exists to detect), not a + threshold. + +`spec.md` § Scope & Non-Goals item 1 and the plan's § Non-Goals item 1 both record this as a hard +scope boundary, and `spec.md` § Risks & Mitigations names "someone lowers the threshold to create +margin" as an identified risk whose mitigation is exactly this handoff. + +## Additional inputs #494 may find useful + +1. **Historical evidence is non-comparable.** Every committed Cobertura coverage artifact in the + repository produced by `ConvertTo-KoverageCoberturaXml` carries the inflated root attributes. + Twenty-one unmerged branches from epic #136 gate on per-file line rates computed by the defective + code. None touches any file this feature modifies, so there is no merge conflict, but their + committed coverage evidence will not reproduce against the corrected arithmetic. +2. **Branch coverage has no instrument for PowerShell here.** Pester 5.6.1 emits no `BRANCH` + counter, so the `>= 75%` branch floor cannot be evaluated for PowerShell modules in this + repository. Recorded as an auditable negative-evidence claim in + `/evidence/qa-gates/coverage-delta.2026-08-10T23-10.md` § 2. +3. **Package-level rates remain stale.** `ConvertTo-KoverageCoberturaXml` writes only root and + merged-class attributes, so every `` is stale after package filtering and + class merging. Filed as follow-up candidate 1; it is a separate defect from #441/#478 and is not + fixed here. Any #494 gate that reads a package-level rate is reading a stale value. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/coverage-arithmetic-delta.2026-08-10T23-15.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/coverage-arithmetic-delta.2026-08-10T23-15.md new file mode 100644 index 000000000..6f1c0ea8d --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/coverage-arithmetic-delta.2026-08-10T23-15.md @@ -0,0 +1,72 @@ +# Consolidated Coverage-Arithmetic A/B Delta (P5-T3) + +Timestamp: 2026-08-10T23-15 + +Tabulates pre-change versus post-change for both deterministic A/B experiments, using the concrete +integers captured in P0-T11, P0-T12, P5-T1 and P5-T2. Both experiments hold their input fixed — +neither involves a test run — so the deltas below isolate the arithmetic change and are not +confounded by `dotnet-coverage` denominator nondeterminism. + +Command: see the four source artifacts, each of which records its exact command and full output: + +- `/evidence/baseline/prechange-generator-parity.2026-08-10T22-30.md` (P0-T11) +- `/evidence/baseline/prechange-package-filtered.2026-08-10T22-30.md` (P0-T12) +- `/evidence/qa-gates/postchange-generator-parity.2026-08-10T23-15.md` (P5-T1) +- `/evidence/qa-gates/postchange-package-filtered.2026-08-10T23-15.md` (P5-T2) + +EXIT_CODE: 0 + +Output Summary: + +``` +Experiment A (raw baseline document, generator parity): + pre 161086 / 113219 / 46218 / 26944 + post 79957 / 56124 / 23109 / 13472 == the document's own root attributes +Experiment B (post-processed document, package-filtered): + pre 110849 / 94937 / 0.856453 + post 62345 / 53013 / 0.850317 +Every pre-change figure is strictly greater than its post-change counterpart. +``` + +--- + +## Experiment A — generator parity over the raw baseline document + +Input: +`.../424/evidence/baseline/coverage-baseline.cobertura.xml` +(raw `dotnet-coverage` output; its own root attributes are ground truth). + +| Quantity | PRE-change | POST-change | Ground truth | Delta | Pre > Post? | +| --- | --- | --- | --- | --- | --- | +| `LinesValid` | **161086** | **79957** | 79957 | **-81129** | **yes** | +| `LinesCovered` | **113219** | **56124** | 56124 | **-57095** | **yes** | +| `BranchesValid` | **46218** | **23109** | 23109 | **-23109** | **yes** | +| `BranchesCovered` | **26944** | **13472** | 13472 | **-13472** | **yes** | + +Every post-change figure lands **exactly** on ground truth. The pre-change branch figures were +precisely double ground truth, the signature of the descendant-axis double count. + +## Experiment B — package-filtered reprocessing of the post-processed document + +Input: +`.../424/evidence/qa-gates/coverage-final.cobertura.xml`. + +| Quantity | PRE-change | POST-change | Delta | Pre > Post? | +| --- | --- | --- | --- | --- | +| `lines-valid` | **110849** | **62345** | **-48504** | **yes** | +| `lines-covered` | **94937** | **53013** | **-41924** | **yes** | +| `line-rate` | **0.856453** | **0.850317** | **-0.006136** (-0.61 pp) | **yes** | +| `branches-valid` | 27848 | 15828 | -12020 | yes | +| `branches-covered` | 22001 | 12445 | -9556 | yes | +| `branch-rate` | 0.790039 | 0.786265 | -0.003774 (-0.38 pp) | yes | + +## Statement required by the task + +**Each pre-change figure is strictly greater than its post-change counterpart**, in both experiments +and for every quantity measured — lines, branches and the derived rates alike. The reductions are +the removal of double counting, not a loss of coverage: not one `` or `` element was +altered by this change, and no `hits` value differs from its input (pinned by fixture F6). + +The `-48504` reduction in Experiment B's `lines-valid` equals exactly the method-level `` +element count the research measured for that document (62345 class-level + 48504 method-level = +110849), so the accounting closes independently. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/coverage-delta.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/coverage-delta.2026-08-10T23-10.md new file mode 100644 index 000000000..f2164383b --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/coverage-delta.2026-08-10T23-10.md @@ -0,0 +1,125 @@ +# Coverage Delta — Baseline (P0-T16) vs Post-Change (P4-T3) (P4-T6) + +Timestamp: 2026-08-10T23-10 + +Target file: `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +$coverageXmlPath = Join-Path $root 'docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\evidence\qa-gates\pester-coverage-final.2026-08-10T23-10.xml' +$srcPath = 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1' +$src = Get-Content -LiteralPath (Join-Path $root $srcPath) +$first = ($src | Select-String -SimpleMatch 'function Get-CoberturaClassLineSummary').LineNumber +if (-not $first) { throw 'Get-CoberturaClassLineSummary not found; cannot compute new-code coverage.' } +$last = 259 +$jc = [xml](Get-Content -LiteralPath $coverageXmlPath -Raw) +$lines = @($jc.report.package.sourcefile.line | Where-Object { [int]$_.nr -ge $first -and [int]$_.nr -le $last }) +if ($lines.Count -eq 0) { throw 'No JaCoCo line records in the helper range; the range or the report path is wrong.' } +$cov = @($lines | Where-Object { [int]$_.ci -gt 0 }).Count +'new-code: {0}/{1} = {2:P2} (first={3} last={4})' -f $cov, $lines.Count, ($cov / $lines.Count), $first, $last +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +new-code: 39/40 = 97.50% (first=161 last=259) +covered=39 uncovered=1 +uncovered line numbers: 220 +``` + +--- + +## 1. Line coverage — whole file + +| Counter | Baseline (P0-T16) | Post-change (P4-T3) | Delta | +| --- | --- | --- | --- | +| LINE missed | 19 | 19 | 0 | +| LINE covered | 146 | 183 | +37 | +| LINE total | 165 | 202 | +37 | +| **LINE percentage** | **88.48%** (146/165) | **90.59%** (183/202) | **+2.11 pp** | +| INSTRUCTION | 170/192 = 88.54% | 213/236 = 90.25% | +1.71 pp | +| METHOD | 7/7 = 100% | 8/8 = 100% | 0 | +| CLASS | 1/1 = 100% | 1/1 = 100% | 0 | + +| Gate | Threshold | Post-change | Verdict | +| --- | --- | --- | --- | +| Line coverage floor (`.claude/rules/general-unit-test.md`, uniform T1-T4) | >= 85% | 90.59% | **PASS** | +| Line coverage floor (`CLAUDE.md` § UT2, repository-wide) | >= 80% | 90.59% | PASS | +| No regression versus baseline | >= 88.48% | 90.59% | **PASS** (+2.11 pp) | + +Where `CLAUDE.md` (>= 80%) and `.claude/rules/general-unit-test.md` (>= 85%) differ, **the stricter +figure — 85% — is the one recorded and applied.** No threshold is modified anywhere by this change; +threshold reconciliation is owned by #494. + +Every added production line is executed except one (see § 3). The 19 missed lines are the same 19 as +at baseline: they lie in `ConvertTo-KoverageRelativePath`, the two `throw` guards, and the +`Merge-CoberturaClassesByFilename` element-creation and condition-rewrite paths, all of which +pre-date this change. + +## 2. Branch coverage — NOT MEASURABLE, recorded as an auditable negative-evidence claim + +The `>= 75%` branch floor in `.claude/rules/general-unit-test.md` and +`.claude/rules/powershell.md:64` has **no available instrument for PowerShell in this repository.** +This is stated rather than silently omitted, and no number is invented in its place. + +- **SearchScope:** `/evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml` + (the P4-T3 `OutputPath`) +- **SearchPatterns:** `report/counter[@type='BRANCH']` +- **SearchResult:** `none` +- **Counter types the report actually contains:** `INSTRUCTION`, `LINE`, `METHOD`, `CLASS` — and + nothing else. That exhaustive enumeration is the proof of absence. +- **Pester version:** 5.6.1 + +This limitation is a property of the PowerShell coverage tooling. It is **not caused by this +change** — the identical four counters and no `BRANCH` counter were recorded at baseline in P0-T16, +before any production edit — and it is **not** grounds for altering any threshold. Thresholds are +owned by child feature #494 (wave 2). This feature proposes and makes no threshold change. + +## 3. New-code coverage — `Get-CoberturaClassLineSummary` + +| Quantity | Value | +| --- | --- | +| `$first` (function declaration line, post-change file) | **161** | +| `$last` (closing brace line, post-change file) | **259** | +| JaCoCo `` records in `[161, 259]` | **40** | +| Records with `ci > 0` (covered) | **39** | +| Records with `ci = 0` (uncovered) | **1** | +| **New-code line coverage** | **39 / 40 = 97.50%** | + +`$last = 259` was determined by reading the file: line 259 is the closing brace of +`Get-CoberturaClassLineSummary`, immediately preceding the blank line 260 and +`function Merge-CoberturaClassesByFilename {` at line 261. + +| Gate | Threshold | Observed | Verdict | +| --- | --- | --- | --- | +| New modules/classes/methods (`CLAUDE.md` § UT2) | >= 90% | **97.50%** | **PASS** | +| No regression on changed lines (`.claude/rules/powershell.md`) | no decrease | whole-file rate rose 88.48% -> 90.59%; the changed region is new code at 97.50% | **PASS** | + +The line set is non-empty (40 records), so no percentage is reported over a zero-length range. + +### The single uncovered statement, recorded precisely + +**Line 220: `$existing.Hits = $hits`** — the body of `if ($hits -gt $existing.Hits)`, i.e. the +assignment that fires only when a *later*-enumerated entry carries strictly more hits than the entry +already in the map. + +The enclosing `if` condition at line 219 **is** executed and evaluated (it is covered); only the +assignment body is not. The reason is structural: the helper enumerates the class-level rollup +first, and in every fixture and in both committed sample documents the class-level rollup already +carries the maximum hits value for a repeated line number, so the candidate never exceeds it. F4 +exercises the repeat-key `max(hits)` rule in the direction the real data produces — class-level +`hits=1` versus method-level `hits=1` and `hits=0` — and asserts the correct deduplicated result +(`lines-valid` = `'1'`, `lines-covered` = `'1'`). + +This is recorded as an observation, not concealed. It does **not** trigger the P4-T6 remediation +path: that path fires only when the whole-file line rate falls below 88.48% or the new-code rate +falls below 90%, and neither condition holds (90.59% and 97.50% respectively). Accordingly **N = 0** +`It` blocks were added, and the P4-T3 / P4-T4 acceptance count remains 19 + 0 = 19. + +No threshold was lowered, no line was excluded from measurement, and no shortfall is reported as a +pass. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/file-size-audit.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/file-size-audit.2026-08-10T23-10.md new file mode 100644 index 000000000..ac636ce4c --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/file-size-audit.2026-08-10T23-10.md @@ -0,0 +1,43 @@ +# Post-Format File-Size Audit (P4-T5) + +Timestamp: 2026-08-10T23-10 + +Enforces the 500-line file ceiling in `.claude/rules/general-code-change.md` § File Size Limit, +which applies to production code and test code alike. Measured **after** the P4-T1 format step, so +the figures are the ones that will be committed. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +Get-ChildItem -LiteralPath ` + 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1', ` + 'tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1' | + ForEach-Object { '{0}: {1}' -f $_.Name, (Get-Content -LiteralPath $_.FullName).Count } +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +Invoke-MSTestWithCoverage.Helpers.ps1: 455 +Invoke-MSTestWithCoverage.Helpers.Tests.ps1: 468 +``` + +| File | Pre-change | Post-change | Ceiling | Headroom | Verdict | +| --- | --- | --- | --- | --- | --- | +| `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` | 357 | **455** | 500 | 45 | **PASS** | +| `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` | 222 | **468** | 500 | 32 | **PASS** | + +Both files are under 500 lines. The production file's net addition is +98 lines (99-line helper +inserted, 11-line inner loop replaced by 5 lines, 7-line synthetic-document block replaced by +10 lines), consistent with the spec's 50-70 line estimate order of magnitude and comfortably inside +the ceiling. + +The test file grew by 246 lines and remains a single file: AC-18 pins the diff to exactly two source +files, so the budget was met by compacting the fixture here-strings per the plan's § Test-File Line +Budget rather than by adding a third test file. Per-block line counts, all within budget: F1 = 24 +(<= 24), F2 = 28 (<= 28), F3 = 34 (<= 34), F4 = 22 (<= 26), F5 = 19 (<= 24), F6 = 34 (<= 34), and +the `Describe 'Get-CoberturaClassLineSummary'` block spans lines 401-468 = **68** lines (<= 80). diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/followups-not-fixed.2026-08-10T23-25.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/followups-not-fixed.2026-08-10T23-25.md new file mode 100644 index 000000000..0209b15e4 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/followups-not-fixed.2026-08-10T23-25.md @@ -0,0 +1,49 @@ +# Follow-Ups Not Fixed In This Change (P6-T6) + +Timestamp: 2026-08-10T23-25 + +Confirms that none of the four follow-up candidates recorded in +`/evidence/issue-updates/followups-441.2026-08-10T23-25.md` was fixed by this change. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git diff --name-only edf3d34c -- scripts tests +git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1 scripts/temp-extract-coverage.ps1 +git diff --name-only edf3d34c -- .claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +=== scope-lock re-check === +scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 +tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +=== follow-up files (Invoke-MSTestWithCoverage.ps1, temp-extract-coverage.ps1) === +=== agent-memory file === +=== end (empty = unchanged) === +``` + +## Verdict + +| Check | Required | Observed | Verdict | +| --- | --- | --- | --- | +| P4-T8 scope-lock output still lists exactly two source files | exactly 2 | exactly 2 (`Invoke-MSTestWithCoverage.Helpers.ps1`, `Invoke-MSTestWithCoverage.Helpers.Tests.ps1`) | **PASS** | +| `git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1 scripts/temp-extract-coverage.ps1` | empty | **empty** | **PASS** | + +## Per-candidate confirmation + +| # | Candidate | File(s) that a fix would touch | Changed? | +| --- | --- | --- | --- | +| 1 | Package-level rates never recomputed | `Invoke-MSTestWithCoverage.Helpers.ps1` (`ConvertTo-KoverageCoberturaXml` root/merged-class attribute writes) and its consumer `scripts/temp-extract-coverage.ps1:47` | **not fixed** — `temp-extract-coverage.ps1` is unchanged, and the diff to `Helpers.ps1` is confined to four hunks (old lines 122-132, an insertion after 166, 270-273 and 275-276), none of which touches a `` attribute. No `SetAttribute` on a package node exists anywhere in the file, before or after. | +| 2 | Merged class retains only the primary `` | `Merge-CoberturaClassesByFilename` `` handling | **not fixed** — the `` handling at old lines 202-206 is untouched (no hunk covers that range), and fixture **F6 actively pins** the existing behaviour: the merged class must keep exactly the primary's one `` child. F6 passes. | +| 3 | `Invoke-MSTestWithCoverage.ps1` lacks a `\.claude\` discovery exclusion | `scripts/vscode/Invoke-MSTestWithCoverage.ps1:296-302` | **not fixed** — the file does not appear in the diff at all (empty output above; independently verified by P2-T6). | +| 4 | Agent memory records an incorrect generalization | `.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md` | **not fixed** — the file does not appear in the diff (empty output above). | + +All four candidates remain open and unfixed, exactly as `spec.md` § Rollout & Follow-up requires +("**NOT to be fixed in this feature**"). AC-20 remains unchecked because no issue number could be +obtained; see the `POSTING BLOCKED` record. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml new file mode 100644 index 000000000..4d7768a93 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/pester-final.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/pester-final.2026-08-10T23-10.md new file mode 100644 index 000000000..4b2e0f926 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/pester-final.2026-08-10T23-10.md @@ -0,0 +1,98 @@ +# Final QA — Pester (P4-T3) + +Timestamp: 2026-08-10T23-10 + +Toolchain step 3 of 3. Unconditional command task; `EXIT_CODE: SKIPPED` is not a valid outcome. + +Command: + +``` +# (1) MCP call — NON-PROBATIVE +mcp__drm-copilot__run_poshqc_test + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) direct Pester capture — the actual verdict and the numbers +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +Import-Module Pester -MinimumVersion 5.0 -Force +$c = New-PesterConfiguration +$c.Run.Path = 'tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1' +$c.Run.PassThru = $true +$c.CodeCoverage.Enabled = $true +$c.CodeCoverage.Path = 'scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1' +$c.CodeCoverage.OutputFormat = 'JaCoCo' +$c.CodeCoverage.OutputPath = '\evidence\qa-gates\pester-coverage-final.2026-08-10T23-10.xml' +$c.Output.Verbosity = 'Detailed' +$c.Should.ErrorAction = 'Continue' +$r = Invoke-Pester -Configuration $c +``` + +EXIT_CODE: 0 (`PESTER_EXIT_CODE` from the direct run) + +MCP payload (recorded verbatim, **non-probative**): `ok`: `true`; `summary`: +`Ran bundled PoshQC test against 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' with 2 selected scan folder(s).` + +**N = 0.** No `It` block was added under the P4-T6 remediation path, so the expected count is +19 + 0 = 19. + +Output Summary: + +``` +Total=19 Passed=19 Failed=0 Skipped=0 +(all 19 blocks listed Passed; see the per-test listing below) +PESTER_EXIT_CODE=0 +INSTRUCTION: missed=23 covered=213 -> 213/236 = 90.25% +LINE: missed=19 covered=183 -> 183/202 = 90.59% <-- post-change line coverage +METHOD: missed=0 covered=8 -> 8/8 = 100.00% +CLASS: missed=0 covered=1 -> 1/1 = 100.00% +``` + +Per-test listing: + +``` +Passed :: preserves backslash separators for nested Windows paths while making them workspace-relative +Passed :: strips active and stale TaskMaster roots while preserving already relative paths +Passed :: merges duplicate class entries that point to the same source file +Passed :: normalizes stale TaskMaster roots before merging duplicate production class entries +Passed :: excludes .Test packages from the report and from the aggregate covered/valid line totals +Passed :: counts each source line once when methods repeat the class-level rollup +Passed :: counts each branch line once when methods repeat the class-level rollup +Passed :: computes the merged per-file line-rate from the merged rollup alone +Passed :: deduplicates a repeated line number by taking the maximum hits value +Passed :: retains method-level lines when the class-level rollup element is absent +Passed :: preserves the primary class methods subtree and every hits value when merging +Passed :: still throws when the document has no packages node +Passed :: excludes projects that resolve to a .Test assembly name +Passed :: retains non-test production projects in the allowlist +Passed :: applies the .Test exclusion to the project-file base-name fallback +Passed :: retains the candidate condition-coverage when its total is greater +Passed :: retains the candidate condition-coverage when totals tie and its covered count is greater +Passed :: retains the existing condition-coverage when neither precedence condition holds +Passed :: returns zero totals for a class with neither a lines nor a methods element +``` + +## Verdict + +| Metric | Required | Observed | +| --- | --- | --- | +| FailedCount | 0 | **0** | +| PassedCount | 19 + N = **19** | **19** | +| TotalCount | 19 + N = **19** | **19** | + +`TotalCount` is neither 0 nor below 19, so the green result is not the vacuous zero-discovery case. + +## Post-change coverage for `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` + +| Counter | missed | covered | total | percentage | +| --- | --- | --- | --- | --- | +| INSTRUCTION | 23 | 213 | 236 | 90.25% | +| **LINE** | **19** | **183** | **202** | **90.59%** | +| METHOD | 0 | 8 | 8 | 100.00% | +| CLASS | 0 | 1 | 1 | 100.00% | + +All values are concrete integers; no placeholder appears. **Branch coverage is recorded as +tool-unsupported, not as a number** — Pester 5.6.1 emits no `BRANCH` counter. The auditable +negative-evidence claim is carried by P4-T6. + +Coverage report: `/evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml`. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/poshqc-analyze.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/poshqc-analyze.2026-08-10T23-10.md new file mode 100644 index 000000000..6e25e3ed3 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/poshqc-analyze.2026-08-10T23-10.md @@ -0,0 +1,84 @@ +# Final QA — PoshQC Analyze (P4-T2) + +Timestamp: 2026-08-10T23-10 + +Toolchain step 2 of 3. Unconditional command task; `EXIT_CODE: SKIPPED` is not a valid outcome. + +Command: + +``` +# (1) MCP call +mcp__drm-copilot__run_poshqc_analyze + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) per-file breakdown (the actual gate) +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +foreach ($p in @( + 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1', + 'tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1')) { + Invoke-ScriptAnalyzer -Path (Join-Path $root $p) | + Select-Object ScriptName, RuleName, Severity, Line, Message | Format-List +} +``` + +EXIT_CODE: 1 (MCP call) + +MCP payload: `ok`: `false`; `summary`: `Command exited with code 1.`; +`stderr_excerpt`: `Exception: PSScriptAnalyzer reported 16 issue(s).` + +**This non-zero exit is expected at every invocation and does not fail this gate.** Sixteen +pre-existing findings exist under `scripts/vscode` across six files (`Install-RepoDotNetSdk.ps1`, +`Invoke-MSTest.ps1`, `Invoke-MSTestWithCoverage.Helpers.ps1`, `Invoke-Restore.ps1`, +`Invoke-VSBuild.ps1`, `Sync-PackageReferences.ps1`), of which exactly one is in an in-scope file. +The count is unchanged from the P0-T15 baseline (16 -> 16). Only a finding absent from the P0-T15 +per-file baseline fails this gate. + +Output Summary: + +``` +===== scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1 ===== +count=1 + +ScriptName : Invoke-MSTestWithCoverage.Helpers.ps1 +RuleName : PSUseSingularNouns +Severity : Warning +Line : 140 +Message : The cmdlet 'Get-CoberturaLineConditionCoverageParts' uses a plural noun. A singular noun should be used + instead. + +===== tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1 ===== +count=0 +``` + +## Baseline diff — keyed on `(ScriptName, RuleName, Severity, Message)`, excluding `Line` + +### `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` + +| Key | In P0-T15 baseline | Post-change | Verdict | +| --- | --- | --- | --- | +| (`Invoke-MSTestWithCoverage.Helpers.ps1`, `PSUseSingularNouns`, `Warning`, `The cmdlet 'Get-CoberturaLineConditionCoverageParts' uses a plural noun. A singular noun should be used instead.`) | present | present | **pre-existing, expected to persist** | + +- Post-change finding set: **1**. Baseline finding set: **1**. +- Post-change set **is a subset of** the baseline set. **NEW findings: 0.** +- `Line` moved 146 -> **140**. This is the anticipated shift: P2-T2 replaced an 11-line inner loop + with a 5-line helper call, moving every later declaration up by 6 lines. `146 - 6 = 140`, exactly. + A line-number move on an otherwise-identical finding is **not** a new finding, which is why `Line` + is excluded from the comparison key. +- The `PSUseSingularNouns` finding is **not fixed**, deliberately. Clearing it requires renaming the + exported function `Get-CoberturaLineConditionCoverageParts`, which `spec.md` § Implementation + strategy lists as Unmodified and § Technical specifications forbids. A zero-findings acceptance is + deliberately not used here because it is unsatisfiable within scope; AC-15 is a no-new-findings + gate. +- The new function `Get-CoberturaClassLineSummary` introduced **no** finding of its own: its noun + (`CoberturaClassLineSummary`) is singular and its verb (`Get`) is approved. + +### `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` + +Baseline finding set: **0**. Post-change finding set: **0**. **NEW findings: 0.** The 167 added +lines introduced no analyzer debt. + +## Gate verdict + +**PASS — zero new findings on either changed file relative to the P0-T15 baseline.** diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/poshqc-format.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/poshqc-format.2026-08-10T23-10.md new file mode 100644 index 000000000..ad7aac06c --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/poshqc-format.2026-08-10T23-10.md @@ -0,0 +1,75 @@ +# Final QA — PoshQC Format (P4-T1) + +Timestamp: 2026-08-10T23-10 + +Toolchain step 1 of 3 for PowerShell (format -> analyze -> test; type checking is not applicable to +PowerShell and is intentionally absent). This is an unconditional command task; `EXIT_CODE: SKIPPED` +is not a valid outcome. + +Command: + +``` +# part 1 (before) +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +$targets = @('scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1', + 'tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1') +foreach ($t in $targets) { '{0}: before={1}' -f $t, (Get-FileHash -LiteralPath (Join-Path $root $t) -Algorithm SHA256).Hash } +(git status --porcelain) | Out-String + +# the format call +mcp__drm-copilot__run_poshqc_format + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# part 2 (after) — same two measurements +``` + +EXIT_CODE: 0 + +MCP payload `ok`: `true`. MCP payload `summary` (verbatim): +`Ran bundled PoshQC format against 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' with 2 selected scan folder(s).` + +Output Summary: + +``` +scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1: before=5D2961CEEA163EF32F9DC9D6439B8B20C20A8B569124E8B8A6DE905AD1D3E1D0 +tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1: before=D2BC1BE28579A6E322E86CC3789175FA1278FBD6CDF10B710AB4A144798782DD +--- porcelain before --- + M docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md + M scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 + M tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +?? docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/ + +scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1: after=5D2961CEEA163EF32F9DC9D6439B8B20C20A8B569124E8B8A6DE905AD1D3E1D0 +tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1: after=D2BC1BE28579A6E322E86CC3789175FA1278FBD6CDF10B710AB4A144798782DD +--- porcelain after --- + M docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md + M scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 + M tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +?? docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/ +``` + +## Set of files changed by the formatter + +**Empty. Zero files changed.** + +Change detection uses two instruments because neither alone suffices here. The MCP payload reports +no changed-file count at all. And by Phase 4 both in-scope files are already ` M` relative to `HEAD`, +so if the formatter had rewritten one of them its porcelain line would be *identical* before and +after and the porcelain difference would be empty — the gate would report "0 files changed" exactly +when a file did change. + +| Instrument | Scope | Result | +| --- | --- | --- | +| SHA-256 content hash, before vs after | the two in-scope files | **identical for both** — neither was rewritten | +| `git status --porcelain`, before vs after | every other path | **identical listings** — no new entry, so no out-of-scope file was modified | + +No `git checkout --` restoration was required and none was performed. No entry under +`.claude/agent-memory/**` appeared in either listing. + +## Restart determination + +P0-T14 recorded **zero** out-of-scope formatter-modified files, so there is no pre-existing +formatting drift to exempt. This invocation likewise modified nothing out of scope, so the bounded +restart clause is not triggered: the phase proceeds to P4-T2 without restarting. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/postchange-generator-parity.2026-08-10T23-15.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/postchange-generator-parity.2026-08-10T23-15.md new file mode 100644 index 000000000..3aab68221 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/postchange-generator-parity.2026-08-10T23-15.md @@ -0,0 +1,67 @@ +# POST-CHANGE Generator-Parity A/B (P5-T1) + +Timestamp: 2026-08-10T23-15 + +The primary acceptance oracle for AC-1. Identical procedure and identical fixed input to P0-T11, +run against the **fixed** `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. Deterministic; +no test run is involved. + +Input: +`docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/baseline/coverage-baseline.cobertura.xml` +(raw `dotnet-coverage` output; its own root attributes are the generator's ground truth). + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +. (Join-Path $root 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1') +$sample = Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\baseline\coverage-baseline.cobertura.xml' +[xml]$doc = Get-Content -LiteralPath $sample -Raw -Encoding UTF8 +'INPUT root: lines-valid={0} lines-covered={1} branches-valid={2} branches-covered={3}' -f ` + $doc.coverage.'lines-valid', $doc.coverage.'lines-covered', $doc.coverage.'branches-valid', $doc.coverage.'branches-covered' +Get-CoberturaCoverageSummary -XmlDocument $doc | Format-List +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +INPUT root: lines-valid=79957 lines-covered=56124 branches-valid=23109 branches-covered=13472 + +LineRate : 0.701927 +BranchRate : 0.582976 +LinesCovered : 56124 +LinesValid : 79957 +BranchesCovered : 13472 +BranchesValid : 23109 +``` + +## Verdict — exact generator parity + +| Quantity | Required (the document's own root attribute) | Post-change measured | Match | +| --- | --- | --- | --- | +| `LinesValid` | **79957** | **79957** | **exact** | +| `LinesCovered` | **56124** | **56124** | **exact** | +| `BranchesValid` | **23109** | **23109** | **exact** | +| `BranchesCovered` | **13472** | **13472** | **exact** | + +**All four figures reproduce the input document's own root attributes exactly.** The corrected +post-processor now returns, to the line, the figures the instrumentation tool itself computed. This +is the strongest available acceptance evidence and satisfies AC-1. + +As a further consistency check not required by the task: the derived `LineRate` is now `0.701927`, +which is the document's own `line-rate="0.7019272859161799"` rounded to six decimal places. The +pre-change run reported `0.702848`, a value the input document never carried. + +## A/B against P0-T11 + +| Quantity | PRE-change (P0-T11) | POST-change (this run) | Ground truth | Reduction | +| --- | --- | --- | --- | --- | +| `LinesValid` | 161086 | **79957** | 79957 | -81129 | +| `LinesCovered` | 113219 | **56124** | 56124 | -57095 | +| `BranchesValid` | 46218 | **23109** | 23109 | -23109 (was exactly 2x) | +| `BranchesCovered` | 26944 | **13472** | 13472 | -13472 (was exactly 2x) | + +Each pre-change figure is strictly greater than its post-change counterpart, and every post-change +figure lands exactly on ground truth. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/postchange-package-filtered.2026-08-10T23-15.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/postchange-package-filtered.2026-08-10T23-15.md new file mode 100644 index 000000000..b5159a280 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/postchange-package-filtered.2026-08-10T23-15.md @@ -0,0 +1,62 @@ +# POST-CHANGE Package-Filtered A/B (P5-T2) + +Timestamp: 2026-08-10T23-15 + +Identical procedure and identical fixed input to P0-T12, run against the **fixed** +`scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. Exercises the full +`ConvertTo-KoverageCoberturaXml` pipeline: package filtering, filename normalization, class merging +and root-attribute rewrite. + +Input: +`docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/qa-gates/coverage-final.cobertura.xml`. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +. (Join-Path $root 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1') +$sample = Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\qa-gates\coverage-final.cobertura.xml' +$content = Get-Content -LiteralPath $sample -Raw -Encoding UTF8 +[xml]$out = ConvertTo-KoverageCoberturaXml -XmlContent $content -RepoRoot $root -PathSeparator '\' +'lines-valid={0} lines-covered={1} line-rate={2} branches-valid={3} branches-covered={4} branch-rate={5}' -f ` + $out.coverage.'lines-valid', $out.coverage.'lines-covered', $out.coverage.'line-rate', ` + $out.coverage.'branches-valid', $out.coverage.'branches-covered', $out.coverage.'branch-rate' +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +lines-valid=62345 lines-covered=53013 line-rate=0.850317 branches-valid=15828 branches-covered=12445 branch-rate=0.786265 +``` + +## Verdict + +| Quantity | PRE-change (P0-T12) | POST-change (this run) | Required post-change | Match | +| --- | --- | --- | --- | --- | +| `lines-valid` | 110849 | **62345** | **62345** | **exact** | +| `lines-covered` | 94937 | **53013** | **53013** | **exact** | +| `line-rate` | 0.856453 | **0.850317** | **0.850317** | **exact** | +| `branches-valid` | 27848 | **15828** | (not pinned by the task) | — | +| `branches-covered` | 22001 | **12445** | (not pinned by the task) | — | +| `branch-rate` | 0.790039 | **0.786265** | (not pinned by the task) | — | + +All three required figures reproduce exactly. AC-3 is satisfied. + +### Independent corroboration of the branch figures + +The post-change `branches-valid = 15828` and `branches-covered = 12445` were **predicted before the +implementation existed**, by the independent streaming `XmlReader` pass in P0-T18, which measured +union branches valid = 15828 and union branches covered = 12445 on this same document. The +implementation reproduces both exactly. The value also falls inside the range `[15730, 16582]` that +the research derived analytically for class-level `branches-valid`, against the emitted defective +value of 27848. + +### Note on the line-rate delta + +`line-rate` falls from 0.856453 to 0.850317, a reduction of **0.61 percentage points**. The +corrected figure of **85.0317%** sits 0.03 pp above the uniform 85% line floor in +`.claude/rules/general-unit-test.md`. That observation is recorded as fact and handed off to child +feature #494 (see `/evidence/other/threshold-handoff-494.2026-08-10T23-15.md`). **No +threshold is changed by this feature.** diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/scope-lock.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/scope-lock.2026-08-10T23-10.md new file mode 100644 index 000000000..85956369d --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/scope-lock.2026-08-10T23-10.md @@ -0,0 +1,45 @@ +# Scope-Lock Diff Gate (P4-T8) + +Timestamp: 2026-08-10T23-10 + +Enforces `spec.md` AC-18: the diff touches exactly two source files. + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git diff --name-only edf3d34c -- scripts tests +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 +tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +``` + +## Verdict + +| Expected file | Present | Notes | +| --- | --- | --- | +| `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` | yes | the one production file changed | +| `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` | yes | the one test file changed | +| anything else | **none** | — | + +The gate lists **exactly** the two in-scope source files and nothing else. **PASS.** + +Notably absent, as required: + +- `scripts/vscode/Invoke-MSTestWithCoverage.ps1` — unchanged, including its missing `\.claude\` + discovery exclusion (separately verified by P2-T6 and P6-T6, and filed as follow-up candidate 3). +- `scripts/temp-extract-coverage.ps1` — unchanged. +- No third test file was created. The test-file line budget was met by compacting the fixture + here-strings per the plan's § Test-File Line Budget, because adding a third file would have broken + AC-18. + +The gate is scoped to `scripts` and `tests` deliberately: `docs/` (feature documents and evidence) +and `.claude/agent-memory/` are tracked and legitimately change during this work, and are covered by +the separate threshold gate (P4-T9) and the evidence-location audit (P5-T5, P7-T20). diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/threshold-no-change.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/threshold-no-change.2026-08-10T23-10.md new file mode 100644 index 000000000..760badb46 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/threshold-no-change.2026-08-10T23-10.md @@ -0,0 +1,57 @@ +# No-Threshold-Change Gate (P4-T9) + +Timestamp: 2026-08-10T23-10 + +Enforces `spec.md` AC-17 and the plan's § Non-Goals item 1: **no coverage threshold may be re-tuned +by this feature.** Threshold reconciliation is owned by child feature #494 (wave 2). + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config +git diff HEAD -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 | + Select-String '^\+' | Select-String -Pattern '\b(85|90|75)\b' +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +=== git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config === +=== end (empty = clean) === + +=== added lines matching \b(85|90|75)\b === ++ # case. The merged rate must be 3/5 = 0.6, not the blended 6/8 = 0.75. +``` + +## Part 1 — threshold-bearing files + +`git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` returned **empty output**. + +- `CLAUDE.md` — unchanged. +- Every file under `.claude/rules/` — unchanged. +- `coverage.config` — unchanged. + +**PASS.** + +## Part 2 — enumerated added lines containing `85`, `90` or `75` + +The judgment is bounded to the enumerated matches, so the check is mechanically reproducible. +Exactly **one** added line across both changed source files matches. + +| # | Added line | File | Classification | Justification | +| --- | --- | --- | --- | --- | +| 1 | ` # case. The merged rate must be 3/5 = 0.6, not the blended 6/8 = 0.75.` | `tests/.../Invoke-MSTestWithCoverage.Helpers.Tests.ps1` (fixture F3 comment) | **fixture rate value** | `75` matched inside the literal `0.75`, which is the *defective* per-file `line-rate` that F3 exists to detect — the miniature of the confirmed `QfcHomeController.Iteration.cs` case (`0.8625` -> `0.803571` at repository scale becomes `0.75` -> `0.6` at fixture scale). It is a Cobertura attribute value asserted by a regression test, **not** a coverage threshold. It configures nothing and gates nothing. | + +**No added line expresses a coverage threshold. PASS.** + +## Recorded handoff, acted upon nowhere + +The corrected repository-wide line rate for the #424 committed sample is **85.0317%** against the +uniform **85%** line floor in `.claude/rules/general-unit-test.md` — a margin of **0.03 percentage +points**. That observation is recorded as fact in +`/evidence/other/threshold-handoff-494..md` and is acted upon nowhere in this change. +This feature proposes no threshold change and makes no threshold edit. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/toolchain-clean-pass.2026-08-10T23-10.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/toolchain-clean-pass.2026-08-10T23-10.md new file mode 100644 index 000000000..106cc320a --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/toolchain-clean-pass.2026-08-10T23-10.md @@ -0,0 +1,40 @@ +# Toolchain Clean-Pass Confirmation (P4-T4) + +Timestamp: 2026-08-10T23-10 + +Confirms **one consecutive execution** of P4-T1 -> P4-T2 -> P4-T3 in which no step failed and no +step changed files. PowerShell toolchain order is format -> analyze -> test; type checking is not +applicable to PowerShell (`.claude/rules/powershell.md` step 3) and is intentionally absent from +this loop. The C# toolchain is not a gate for this change, and `CLAUDE.md`'s `/p:Nullable=enable` +command (a known defect, issue #522) was not invoked. + +Command: see the three per-step artifacts, which record the exact commands and full outputs: + +- `/evidence/qa-gates/poshqc-format.2026-08-10T23-10.md` +- `/evidence/qa-gates/poshqc-analyze.2026-08-10T23-10.md` +- `/evidence/qa-gates/pester-final.2026-08-10T23-10.md` + +EXIT_CODE: 0 + +Output Summary: + +``` +P4-T1 format : 0 files changed (both in-scope SHA-256 hashes identical before/after; + porcelain listings identical, so no out-of-scope file modified) +P4-T2 analyze: 0 NEW findings on either changed file vs the P0-T15 baseline + (1 pre-existing PSUseSingularNouns persists, line 146 -> 140; key unchanged) +P4-T3 test : FailedCount=0, PassedCount=TotalCount=19 (N=0); LINE 183/202 = 90.59% +Attempt count: 1 (no restart was required) +``` + +## Acceptance items + +| Requirement | Evidence | Verdict | +| --- | --- | --- | +| Format changed 0 files — **identical SHA-256 for both in-scope files before and after the P4-T1 invocation** | `Helpers.ps1` before = after = `5D2961CEEA163EF32F9DC9D6439B8B20C20A8B569124E8B8A6DE905AD1D3E1D0`; `Helpers.Tests.ps1` before = after = `D2BC1BE28579A6E322E86CC3789175FA1278FBD6CDF10B710AB4A144798782DD` | **PASS** | +| Empty `git status --porcelain` before/after difference for every path except `.claude/agent-memory/**` | the two porcelain listings are identical; no new entry appeared, and no `.claude/agent-memory/**` entry appeared at all | **PASS** | +| Analyze reported no new findings on the two in-scope files relative to the P0-T15 baseline | post-change set (1 finding on `Helpers.ps1`, 0 on the test file) is a subset of the baseline set under the key `(ScriptName, RuleName, Severity, Message)` | **PASS** | +| Direct Pester run: `FailedCount` = 0 with `PassedCount` = `TotalCount` = 19 + N | 0 failed, 19 passed, 19 total, **N = 0** (the P4-T6 remediation path did not fire: whole-file 90.59% >= 88.48% and new-code 97.50% >= 90%) | **PASS** | + +**No step failed and no step changed files, so the loop completed in a single pass. Attempt count: +1. No restart from P4-T1 was triggered, and there is no second attempt to record.** diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/union-builder-byte-identity.2026-08-10T22-55.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/union-builder-byte-identity.2026-08-10T22-55.md new file mode 100644 index 000000000..b0b5913ea --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/union-builder-byte-identity.2026-08-10T22-55.md @@ -0,0 +1,103 @@ +# Union-Builder Byte Identity (P2-T5, re-verified post-format by P4-T10) + +Timestamp: 2026-08-10T22-55 + +`spec.md` AC-8 and § Non-Goals item 5 require the merge union builder at pre-change +`Invoke-MSTestWithCoverage.Helpers.ps1:217-268` — including the child-axis selection +`$classNode.SelectNodes('./lines/line')` at pre-change `:219` — to remain byte-identical. +Editing `:219` would destroy the working half of the #478 merge. + +## P2-T5 — pre-format verification + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git diff HEAD -U0 -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | Select-String '^@@' +Select-String -LiteralPath 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1' -SimpleMatch ` + -Pattern 'foreach ($lineNode in @($classNode.SelectNodes(''./lines/line'')))' +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +--- git diff HEAD -U0 hunk headers --- +@@ -122,11 +122,5 @@ function Get-CoberturaCoverageSummary { +@@ -166,0 +161,100 @@ function Get-CoberturaLineConditionCoverageParts { +@@ -270,4 +364,8 @@ function Merge-CoberturaClassesByFilename { +@@ -275,2 +373,2 @@ function Merge-CoberturaClassesByFilename { + +--- union-builder literal occurrences --- +LITERAL foreach ($lineNode in @($classNode.SelectNodes('./lines/line'))) +occurrences=1 + :313: foreach ($lineNode in @($classNode.SelectNodes('./lines/line'))) { +``` + +### Hunk old-side range analysis + +The `HEAD` operand makes this check immune to staging. + +| Hunk header | Old-side range | Intersects 217-268? | +| --- | --- | --- | +| `@@ -122,11 +122,5 @@` | 122-132 | no (ends at 132) | +| `@@ -166,0 +161,100 @@` | insertion after 166 (zero old lines) | no | +| `@@ -270,4 +364,8 @@` | 270-273 | no (starts at 270) | +| `@@ -275,2 +373,2 @@` | 275-276 | no | + +**No hunk's old-side range intersects pre-change lines 217-268.** The union builder is untouched. +The nearest hunk on either side stops at old line 166 and resumes at old line 270, so lines 167-269 +— which contain the entire union builder plus the blank line after it — are unmodified. + +The literal `foreach ($lineNode in @($classNode.SelectNodes('./lines/line')))` still occurs +**exactly once**, now at line 313 (shifted down by the 100-line helper insertion at old line 166, +which is a pure relocation, not an edit). + +--- + +## P4-T10 — post-format re-verification + +Re-run **after** the P4-T1 PoshQC format step, to confirm the formatter did not reflow any line in +the protected range. + +Timestamp: 2026-08-10T23-10 + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git diff HEAD -U0 -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | Select-String '^@@' +Select-String -LiteralPath 'scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1' -SimpleMatch ` + -Pattern 'foreach ($lineNode in @($classNode.SelectNodes(''./lines/line'')))' +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +--- post-format hunk headers --- +@@ -122,11 +122,5 @@ function Get-CoberturaCoverageSummary { +@@ -166,0 +161,100 @@ function Get-CoberturaLineConditionCoverageParts { +@@ -270,4 +364,8 @@ function Merge-CoberturaClassesByFilename { +@@ -275,2 +373,2 @@ function Merge-CoberturaClassesByFilename { + +LITERAL foreach ($lineNode in @($classNode.SelectNodes('./lines/line'))) +occurrences=1 + :313: foreach ($lineNode in @($classNode.SelectNodes('./lines/line'))) { +``` + +The four hunk headers are **byte-identical to the P2-T5 pre-format capture**, and the union-builder +literal still occurs exactly once at line 313. No hunk's old-side range (122-132, insertion after +166, 270-273, 275-276) intersects pre-change lines **217-268**. + +This is consistent with P4-T1, which recorded that the formatter changed **zero** files: the SHA-256 +hash of `Invoke-MSTestWithCoverage.Helpers.ps1` was identical before and after the format call, so +no line anywhere in the file — inside or outside the protected range — was reflowed. No restoration +was needed and no restart from P4-T1 was triggered. + +**AC-8 verdict: the union builder at pre-change `:217-268`, including the child-axis selection +`$classNode.SelectNodes('./lines/line')` at pre-change `:219`, is byte-identical to `main`.** diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/fail-before-f1-f4.2026-08-10T22-45.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/fail-before-f1-f4.2026-08-10T22-45.md new file mode 100644 index 000000000..747ce4143 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/fail-before-f1-f4.2026-08-10T22-45.md @@ -0,0 +1,140 @@ +# Fail-Before Evidence — F1, F2, F3, F4 against unmodified `Helpers.ps1` (P1-T7, P1-T8) + +Timestamp: 2026-08-10T22-45 + +`CLAUDE.md` § Bugfix Workflow step 1 requires the regression tests to exist and be demonstrated +failing before any production change. This artifact records that demonstration. At the time of this +run, `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` is **unmodified** — proven below by +`git diff --name-only edf3d34c -- scripts` returning empty output. + +Command: + +``` +# (1) MCP call — NON-PROBATIVE +mcp__drm-copilot__run_poshqc_test + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) direct Pester capture — the actual verdict +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +Import-Module Pester -MinimumVersion 5.0 -Force +$coverageXmlPath = Join-Path $root 'docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\evidence\regression-testing\pester-coverage-fail-before.2026-08-10T22-45.xml' +$c = New-PesterConfiguration +$c.Run.Path = 'tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1' +$c.Run.PassThru = $true +$c.CodeCoverage.Enabled = $true +$c.CodeCoverage.Path = 'scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1' +$c.CodeCoverage.OutputFormat = 'JaCoCo' +$c.CodeCoverage.OutputPath = $coverageXmlPath +$c.Output.Verbosity = 'Detailed' +$c.Should.ErrorAction = 'Continue' +$r = Invoke-Pester -Configuration $c +``` + +EXIT_CODE: 1 (`PESTER_EXIT_CODE` from the direct run) + +MCP payload (recorded verbatim, **non-probative**): `ok`: `false`; `summary`: +`Command exited with code 4.` The MCP tool returns no counts, no test names and no failure detail, +and it returns `ok:true` whether the suite is green or red; **none of the values below is attributed +to it.** Every figure comes from the direct `Invoke-Pester` run. + +Output Summary: + +``` +Total=14 Passed=10 Failed=4 Skipped=0 +Passed :: preserves backslash separators for nested Windows paths while making them workspace-relative +Passed :: strips active and stale TaskMaster roots while preserving already relative paths +Passed :: merges duplicate class entries that point to the same source file +Passed :: normalizes stale TaskMaster roots before merging duplicate production class entries +Passed :: excludes .Test packages from the report and from the aggregate covered/valid line totals +Failed :: counts each source line once when methods repeat the class-level rollup :: Expected: '3' / But was: '6' ; Expected: '2' / But was: '4' +Failed :: counts each branch line once when methods repeat the class-level rollup :: Expected: '2' / But was: '4' ; Expected: '1' / But was: '2' +Failed :: computes the merged per-file line-rate from the merged rollup alone :: Expected: '0.6' / But was: '0.75' +Failed :: deduplicates a repeated line number by taking the maximum hits value :: Expected: '1' / But was: '3' ; Expected: '1' / But was: '2' +Passed :: retains method-level lines when the class-level rollup element is absent +Passed :: preserves the primary class methods subtree and every hits value when merging +Passed :: excludes projects that resolve to a .Test assembly name +Passed :: retains non-test production projects in the allowlist +Passed :: applies the .Test exclusion to the project-file base-name fallback +PESTER_EXIT_CODE=1 +``` + +## Verdict + +| Metric | Required | Observed | +| --- | --- | --- | +| FailedCount | **4** | **4** | +| PassedCount | **10** | **10** | +| TotalCount | 14 (8 pre-existing + F1..F6) | 14 | + +`TotalCount` is non-zero, so this is not the vacuous zero-discovery case. No other failure count was +observed, so the plan's halt condition is not triggered. + +## Per-fixture pre-fix actuals + +`$c.Should.ErrorAction = 'Continue'` was set, so every failed assertion inside each `It` is reported +rather than only the first. That is what makes the paired figures below observable. + +| Fixture | Assertion | Expected (post-fix) | **Actual against unmodified `Helpers.ps1`** | Matches plan | +| --- | --- | --- | --- | --- | +| **F1** | `lines-valid` | `'3'` | **`'6'`** | yes (6/4) | +| **F1** | `lines-covered` | `'2'` | **`'4'`** | yes | +| **F2** | `branches-valid` | `'2'` | **`'4'`** | yes (4/2) | +| **F2** | `branches-covered` | `'1'` | **`'2'`** | yes | +| **F3** | merged class `line-rate` | `'0.6'` | **`'0.75'`** | yes | +| **F4** | `lines-valid` | `'1'` | **`'3'`** | yes (3/2) | +| **F4** | `lines-covered` | `'1'` | **`'2'`** | yes | + +F1's `line-rate` assertion (`'0.666667'`) passed pre-fix, confirming the § Fixture-Design Trap +warning: a rate-only assertion is not a regression test. F1 and F2 are regression tests only because +they assert counts. + +F5 (`retains method-level lines when the class-level rollup element is absent`) and F6 (`preserves +the primary class methods subtree and every hits value when merging`) both **pass** pre-fix, exactly +as the plan specifies; neither is tagged `[expect-fail]`. All **eight** pre-existing `It` blocks are +listed `Passed`. + +Coverage report for this run: +`/evidence/regression-testing/pester-coverage-fail-before.2026-08-10T22-45.xml`. + +--- + +## P1-T8 — Fixtures written, no existing block modified, no production change yet + +Command: + +```powershell +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +git diff HEAD --numstat -- tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +git diff --name-only edf3d34c -- scripts +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +--- git diff HEAD --numstat -- tests/... --- +167 0 tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +--- git diff --name-only edf3d34c -- scripts --- +--- end (empty above means unchanged) --- +``` + +| Check | Required | Observed | Verdict | +| --- | --- | --- | --- | +| numstat record count | exactly 1 (empty result **fails**) | 1 | pass | +| additions | >= 100 | **167** | pass | +| deletions | exactly 0 | **0** | pass | +| `git diff --name-only edf3d34c -- scripts` | empty | empty | pass | + +The single numstat record proves the fixtures were actually written (an unmodified file emits no +record at all, which would satisfy a bare "0 deletions" reading while proving nothing). The `0` +deletions field proves no existing test block was modified. The `HEAD` operand makes both checks +immune to staging. The empty `scripts` diff proves no production file has changed at this point, so +the fail-before demonstration above is genuinely against unmodified code. + +Test-file line count after the six insertions: **389** (from 222). Per-fixture line counts, all +within the plan's § Test-File Line Budget: F1 = 24 (<= 24), F2 = 28 (<= 28), F3 = 34 (<= 34), +F4 = 22 (<= 26), F5 = 19 (<= 24), F6 = 34 (<= 34). diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/helper-unit-tests.2026-08-10T23-05.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/helper-unit-tests.2026-08-10T23-05.md new file mode 100644 index 000000000..1472ddd2f --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/helper-unit-tests.2026-08-10T23-05.md @@ -0,0 +1,102 @@ +# Helper Unit Tests — Green Run (P3-T8) + +Timestamp: 2026-08-10T23-05 + +Command: + +``` +# (1) MCP call — NON-PROBATIVE +mcp__drm-copilot__run_poshqc_test + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) direct Pester capture — the actual verdict +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +Import-Module Pester -MinimumVersion 5.0 -Force +$c = New-PesterConfiguration +$c.Run.Path = 'tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1' +$c.Run.PassThru = $true +$c.CodeCoverage.Enabled = $true +$c.CodeCoverage.Path = 'scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1' +$c.CodeCoverage.OutputFormat = 'JaCoCo' +$c.CodeCoverage.OutputPath = '\evidence\regression-testing\pester-coverage-helper-unit-tests.2026-08-10T23-05.xml' +$c.Output.Verbosity = 'Detailed' +$c.Should.ErrorAction = 'Continue' +$r = Invoke-Pester -Configuration $c +``` + +EXIT_CODE: 0 (`PESTER_EXIT_CODE` from the direct run) + +MCP payload (recorded verbatim, **non-probative**): `ok`: `true`; `summary`: +`Ran bundled PoshQC test against 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' with 2 selected scan folder(s).` + +Output Summary: + +``` +Total=19 Passed=19 Failed=0 Skipped=0 +Passed :: preserves backslash separators for nested Windows paths while making them workspace-relative +Passed :: strips active and stale TaskMaster roots while preserving already relative paths +Passed :: merges duplicate class entries that point to the same source file +Passed :: normalizes stale TaskMaster roots before merging duplicate production class entries +Passed :: excludes .Test packages from the report and from the aggregate covered/valid line totals +Passed :: counts each source line once when methods repeat the class-level rollup +Passed :: counts each branch line once when methods repeat the class-level rollup +Passed :: computes the merged per-file line-rate from the merged rollup alone +Passed :: deduplicates a repeated line number by taking the maximum hits value +Passed :: retains method-level lines when the class-level rollup element is absent +Passed :: preserves the primary class methods subtree and every hits value when merging +Passed :: still throws when the document has no packages node +Passed :: excludes projects that resolve to a .Test assembly name +Passed :: retains non-test production projects in the allowlist +Passed :: applies the .Test exclusion to the project-file base-name fallback +Passed :: retains the candidate condition-coverage when its total is greater +Passed :: retains the candidate condition-coverage when totals tie and its covered count is greater +Passed :: retains the existing condition-coverage when neither precedence condition holds +Passed :: returns zero totals for a class with neither a lines nor a methods element +PESTER_EXIT_CODE=0 +INSTRUCTION: missed=23 covered=213 +LINE: missed=19 covered=183 +METHOD: missed=0 covered=8 +CLASS: missed=0 covered=1 +``` + +## Verdict + +| Metric | Required | Observed | +| --- | --- | --- | +| FailedCount | 0 | **0** | +| PassedCount | **19** | **19** | +| TotalCount | **19** | **19** | + +19 = eight pre-existing blocks + F1..F6 + the five blocks added by P3-T3..P3-T7. `TotalCount` is +non-zero, so the gate is not satisfied by a zero-discovery run. + +### The five added blocks, listed Passed by name + +| Task | `It` name | Branch exercised | Result | +| --- | --- | --- | --- | +| P3-T3 | retains the candidate condition-coverage when its total is greater | precedence: candidate `Total` (4) > existing `Total` (2) — candidate retained, so `TotalBranches` = 4, `CoveredBranches` = 2 | **Passed** | +| P3-T4 | retains the candidate condition-coverage when totals tie and its covered count is greater | precedence: `Total` equal (2), candidate `Covered` (1) > existing (0) — candidate retained, so 2 / 1 | **Passed** | +| P3-T5 | retains the existing condition-coverage when neither precedence condition holds | precedence: candidate `Total` (2) < existing (4) — existing retained, so 4 / 2 | **Passed** | +| P3-T6 | returns zero totals for a class with neither a lines nor a methods element | boundary: all four totals 0, and `Should -Not -Throw` | **Passed** | +| P3-T7 | still throws when the document has no packages node | error handling: `Get-CoberturaCoverageSummary` still throws `Cobertura XML does not contain a node.` | **Passed** | + +P3-T7's block was added **inside** the existing `Describe 'ConvertTo-KoverageCoberturaXml'`, not +inside the new `Describe 'Get-CoberturaClassLineSummary'`, so it does not count against that +`Describe`'s 80-line budget. + +## Coverage at this point + +| Counter | missed | covered | total | percentage | +| --- | --- | --- | --- | --- | +| INSTRUCTION | 23 | 213 | 236 | 90.25% | +| **LINE** | **19** | **183** | **202** | **90.59%** | +| METHOD | 0 | 8 | 8 | 100.00% | +| CLASS | 0 | 1 | 1 | 100.00% | + +Line coverage rose from the 88.48% baseline to 90.59%; `METHOD` rose from 7 to 8 covered, the new +function being the eighth. The authoritative post-change figures are captured by P4-T3. + +Coverage report: +`/evidence/regression-testing/pester-coverage-helper-unit-tests.2026-08-10T23-05.xml`. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md new file mode 100644 index 000000000..3b496c18e --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pass-after-f1-f6.2026-08-10T22-55.md @@ -0,0 +1,124 @@ +# Pass-After Evidence — F1..F6 against the fixed `Helpers.ps1` (P3-T1, P3-T2) + +Timestamp: 2026-08-10T22-55 + +Command: + +``` +# (1) MCP call — NON-PROBATIVE +mcp__drm-copilot__run_poshqc_test + workspace_root = 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' + scan_folders = ['scripts/vscode', 'tests/scripts/vscode'] + +# (2) direct Pester capture — the actual verdict +$root = (git rev-parse --show-toplevel) -replace '/', '\' +Set-Location $root +Import-Module Pester -MinimumVersion 5.0 -Force +$c = New-PesterConfiguration +$c.Run.Path = 'tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1' +$c.Run.PassThru = $true +$c.CodeCoverage.Enabled = $true +$c.CodeCoverage.Path = 'scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1' +$c.CodeCoverage.OutputFormat = 'JaCoCo' +$c.CodeCoverage.OutputPath = '\evidence\regression-testing\pester-coverage-pass-after.2026-08-10T22-55.xml' +$c.Output.Verbosity = 'Detailed' +$c.Should.ErrorAction = 'Continue' +$r = Invoke-Pester -Configuration $c +``` + +EXIT_CODE: 0 (`PESTER_EXIT_CODE` from the direct run) + +MCP payload (recorded verbatim, **non-probative**): `ok`: `true`; `summary`: +`Ran bundled PoshQC test against 'C:\Users\DanMoisan\repos\TaskMaster\.claude\worktrees\agent-a1cc35d4011888c2a' with 2 selected scan folder(s).` +This payload carries no verdict and establishes nothing; it returned the identical `ok:true` string +while the suite was red at P1-T7. Every figure below comes from the direct run. + +Output Summary: + +``` +Total=14 Passed=14 Failed=0 Skipped=0 +Passed :: preserves backslash separators for nested Windows paths while making them workspace-relative +Passed :: strips active and stale TaskMaster roots while preserving already relative paths +Passed :: merges duplicate class entries that point to the same source file +Passed :: normalizes stale TaskMaster roots before merging duplicate production class entries +Passed :: excludes .Test packages from the report and from the aggregate covered/valid line totals +Passed :: counts each source line once when methods repeat the class-level rollup +Passed :: counts each branch line once when methods repeat the class-level rollup +Passed :: computes the merged per-file line-rate from the merged rollup alone +Passed :: deduplicates a repeated line number by taking the maximum hits value +Passed :: retains method-level lines when the class-level rollup element is absent +Passed :: preserves the primary class methods subtree and every hits value when merging +Passed :: excludes projects that resolve to a .Test assembly name +Passed :: retains non-test production projects in the allowlist +Passed :: applies the .Test exclusion to the project-file base-name fallback +PESTER_EXIT_CODE=0 +INSTRUCTION: missed=27 covered=209 +LINE: missed=23 covered=179 +METHOD: missed=0 covered=8 +CLASS: missed=0 covered=1 +``` + +## P3-T1 verdict + +| Metric | Required | Observed | +| --- | --- | --- | +| FailedCount | 0 | **0** | +| PassedCount | **14** | **14** | +| TotalCount | **14** | **14** | + +14 = eight pre-existing blocks + F1..F6. The five helper unit tests added by P3-T3..P3-T7 do not +exist yet, so 19 is correctly **not** the expected figure at this point. `TotalCount` is non-zero, +so the gate is not satisfied vacuously by a zero-discovery run. + +### F1..F6 individually listed Passed, with their post-fix values + +| Fixture | `It` name | Post-fix assertion values (from `spec.md` § Test Strategy) | Result | +| --- | --- | --- | --- | +| **F1** | counts each source line once when methods repeat the class-level rollup | `lines-valid` = `'3'`, `lines-covered` = `'2'`, `line-rate` = `'0.666667'` (pre-fix 6 / 4) | **Passed** | +| **F2** | counts each branch line once when methods repeat the class-level rollup | `branches-valid` = `'2'`, `branches-covered` = `'1'` (pre-fix 4 / 2) | **Passed** | +| **F3** | computes the merged per-file line-rate from the merged rollup alone | merged `line-rate` = `'0.6'` (3/5); merged `` has exactly 5 children `12,13,56,57,58` ascending (pre-fix `'0.75'`, 6/8) | **Passed** | +| **F4** | deduplicates a repeated line number by taking the maximum hits value | `lines-valid` = `'1'`, `lines-covered` = `'1'` (pre-fix 3 / 2) | **Passed** | +| **F5** | retains method-level lines when the class-level rollup element is absent | `lines-valid` = `'2'`, `lines-covered` = `'1'` (unchanged before and after) | **Passed** | +| **F6** | preserves the primary class methods subtree and every hits value when merging | merged class keeps a `` element with exactly one `` (`M`); merged line hits `12=0,13=0,56=1,57=1,58=1` (unchanged before and after) | **Passed** | + +## P3-T2 — zero existing tests broken + +All **eight** pre-existing `It` blocks are listed individually with `Result = Passed`, quoted +verbatim from the per-test output above: + +``` +Passed :: preserves backslash separators for nested Windows paths while making them workspace-relative +Passed :: strips active and stale TaskMaster roots while preserving already relative paths +Passed :: merges duplicate class entries that point to the same source file +Passed :: normalizes stale TaskMaster roots before merging duplicate production class entries +Passed :: excludes .Test packages from the report and from the aggregate covered/valid line totals +Passed :: excludes projects that resolve to a .Test assembly name +Passed :: retains non-test production projects in the allowlist +Passed :: applies the .Test exclusion to the project-file base-name fallback +``` + +This includes `normalizes stale TaskMaster roots before merging duplicate production class entries`, +the block carrying the `lines-valid | Should -Be '3'` assertion that `spec.md` § Why the existing +suite cannot detect either defect singles out. + +Command: + +```powershell +git diff HEAD --numstat -- tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +``` + +EXIT_CODE: 0 + +Output Summary: + +``` +167 0 tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +``` + +| Check | Required | Observed | Verdict | +| --- | --- | --- | --- | +| numstat record count | exactly 1 (empty result **fails**) | 1 | pass | +| deletions | exactly 0 | **0** | pass | +| additions | >= 100 | **167** | pass | + +Zero deletions proves no existing block was edited. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-fail-before.2026-08-10T22-45.xml b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-fail-before.2026-08-10T22-45.xml new file mode 100644 index 000000000..8a7fa0e90 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-fail-before.2026-08-10T22-45.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-helper-unit-tests.2026-08-10T23-05.xml b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-helper-unit-tests.2026-08-10T23-05.xml new file mode 100644 index 000000000..16714ec35 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-helper-unit-tests.2026-08-10T23-05.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-pass-after.2026-08-10T22-55.xml b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-pass-after.2026-08-10T22-55.xml new file mode 100644 index 000000000..9b33b6ed5 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/pester-coverage-pass-after.2026-08-10T22-55.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/feature-audit.2026-08-10T23-35.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/feature-audit.2026-08-10T23-35.md new file mode 100644 index 000000000..9ea8614e3 --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/feature-audit.2026-08-10T23-35.md @@ -0,0 +1,69 @@ +# Feature Audit — 2026-08-10-cobertura-coverage-arithmetic-441 + +- **Timestamp:** 2026-08-10T23-35 +- **Reviewer:** feature-review agent +- **Branch:** `bug/cobertura-coverage-arithmetic-441` at `3b8d43fb` vs base `edf3d34c` +- **Work mode:** `full-bug` — AC source is `spec.md` § Acceptance Criteria **only** (`user-story.md` exists but is not an AC source in this mode; `issue.md` § Acceptance Criteria is an explicit pointer, not a second source) +- **Spec version:** 1.1, with two accepted preparation-time amendments (AC-15 no-new-findings gate; AC-16 schema scoped to command-step artifacts) — applied as written, not re-litigated + +## Summary + +All 20 acceptance criteria in `spec.md` are verified **PASS**. Every item was independently verified rather than accepted from its check mark: the four headline arithmetic claims (AC-1, AC-2, AC-3, and the fail-before values behind AC-12) were re-executed by this reviewer from the committed inputs and the extracted base-revision code; the structural claims (AC-6 through AC-10, AC-18, AC-19) were verified against the diff and by byte comparison; the process claims (AC-11 through AC-17, AC-20) were verified by re-running the suite and analyzer, scanning the evidence tree, and confirming the four follow-up issues on GitHub. No AC required unchecking. 0 blocking findings. + +## Scope and Baseline + +- Adjudicated diff: `git diff edf3d34c..HEAD` (full branch diff, 118 files). Base verified as an ancestor of HEAD. +- Source changes: exactly `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`. All other changed paths are feature documents, evidence, sibling epic-preparation documents already on the integration branch within the diff window, promoted potential entries, and agent-memory markdown. +- Baseline figures used for comparison (independently reproduced): pre-change `LinesValid = 161086` over the #424 raw generator document; pre-change package-filtered figures 110849 / 94937 / 0.856453; baseline file coverage 146/165 = 88.48%; baseline file sizes 357 (production) and 222 (tests) lines. + +## Acceptance Criteria Inventory + +- Source: `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Acceptance Criteria +- Total AC items: 20 (AC-1 .. AC-20), all checked `[x]` at review start +- Format: markdown checkboxes; no phantom criteria added; criterion text unmodified + +## Acceptance Criteria Evaluation + +| AC | Subject | Verdict | Independent verification performed | +| --- | --- | --- | --- | +| AC-1 | Generator parity oracle 79957 / 56124 / 23109 / 13472 | **PASS** | Reviewer dot-sourced HEAD `Helpers.ps1` and ran `Get-CoberturaCoverageSummary` over the committed #424 raw baseline document: returned exactly 79957 / 56124 / 23109 / 13472, equal to the document's own root attributes (also read directly) | +| AC-2 | Pre-change figure `LinesValid = 161086`, all four counts concrete and strictly greater | **PASS** | Reviewer extracted `Helpers.ps1` at `edf3d34c` and re-ran the same procedure: 161086 / 113219 / 46218 / 26944 — each strictly greater than its AC-1 counterpart; matches `evidence/baseline/prechange-generator-parity.2026-08-10T22-30.md` verbatim | +| AC-3 | Package-filtered A/B: 62345 / 53013 / 0.850317 post-fix vs 110849 / 94937 / 0.856453 pre-fix | **PASS** | Reviewer re-ran `ConvertTo-KoverageCoberturaXml` at HEAD over the committed #424 `coverage-final.cobertura.xml`: root attributes exactly 62345 / 53013 / 0.850317; pre-fix values confirmed from `evidence/baseline/prechange-package-filtered.2026-08-10T22-30.md` and the threshold-handoff record | +| AC-4 | Merged per-file rate corrected; F3 asserts `'0.6'` with five ascending line children 12,13,56,57,58 | **PASS** | F3 read in the test file (asserts `line-rate` `'0.6'`, count 5, joined order `'12,13,56,57,58'`); passes at HEAD (19/19); fails against the base revision with actual `'0.75'` (reviewer-reproduced and recorded in fail-before evidence) | +| AC-5 | Branch counts deduplicated; F2 asserts `branches-valid`=`'2'`, `branches-covered`=`'1'`; no branch regression assertion relies on `branch-rate` alone | **PASS** | F2 read and re-run (passes at HEAD; fails at base with 4 and 2); grep confirms no new test asserts `branch-rate` as its regression signal — F2 asserts the two counts | +| AC-6 | Helper contract: name, mandatory `[System.Xml.XmlElement]` parameter, enumeration order, dedup rules, five output fields | **PASS** | Source read at `Helpers.ps1:161-259`: `[Parameter(Mandatory = $true)][System.Xml.XmlElement]$ClassNode`; enumerates `./lines/line` then `./methods/method/lines/line`; keys by `[int]` line number (via StrictMode-safe `GetAttribute('number')`, equivalent to the spec's `[int]$node.number`); max(hits) / branch-if-either / larger-Total tie-broken-by-larger-Covered via `Get-CoberturaLineConditionCoverageParts`; returns `LineMap`, `TotalLines`, `CoveredLines`, `TotalBranches`, `CoveredBranches` | +| AC-7 | `$cls.SelectNodes('.//lines/line')` gone; summary accumulates from the helper | **PASS** | Reviewer grep: 0 occurrences of `.//lines/line` in the file; the summary loop reads the four totals from `Get-CoberturaClassLineSummary` (diff inspected) | +| AC-8 | Union builder at `:217-268` byte-identical, including `./lines/line` child-axis selection | **PASS** | Reviewer `cmp` of base lines 217-268 vs HEAD lines 311-362: byte-identical; the child-axis selection survives at HEAD line 313 | +| AC-9 | `$classSummaryXml` synthetic-document block removed; merged rates set from a direct helper call | **PASS** | Reviewer grep: 0 occurrences of `$classSummaryXml`; diff shows `Get-CoberturaClassLineSummary -ClassNode $mergedClassNode` feeding `SetAttribute('line-rate', ...)` / `SetAttribute('branch-rate', ...)` | +| AC-10 | F6 passes: `` neither merged nor stripped; no `hits` value differs from input | **PASS** | F6 read (asserts exactly one primary `` named `M` and the exact `number=hits` join `'12=0,13=0,56=1,57=1,58=1'`); passes at HEAD and at base (guard fixture, correctly not expect-fail) | +| AC-11 | F1-F6 present as six new `It` blocks, inline single-quoted here-strings, no file on disk, no mock in any arithmetic path, all passing | **PASS** | All six located in the test file, each an inline `@'...'@` here-string cast to `[xml]`; grep for filesystem APIs over the test file: zero matches; the only `Mock` is in a pre-existing allowlist test; suite 19/19 at HEAD (reviewer-run) | +| AC-12 | F1-F4 demonstrated failing against unmodified `Helpers.ps1` with the stated pre-fix values; recorded under `evidence/regression-testing/` | **PASS** | Evidence artifact records FailedCount 4 / PassedCount 10 / Total 14, with per-assertion actuals 6/4, 4/2, `'0.75'`, 3/2, and a same-time empty `git diff --name-only edf3d34c -- scripts` proving unmodified production code; reviewer independently reproduced the same four failures with the same values against the extracted base file (three additional scratchpad-only failures were traced to the empty project allowlist in the scratchpad tree — an environment artifact affecting only the three pre-existing tests that omit `-ProjectNames`, verified directly) | +| AC-13 | All three condition-coverage precedence branches covered by direct unit tests | **PASS** | Three dedicated `It` blocks read (candidate Total greater; Total equal and Covered greater; neither — existing retained), each asserting the resulting `TotalBranches`/`CoveredBranches`; all pass at HEAD | +| AC-14 | All eight pre-existing `It` blocks pass unmodified; diff shows no edit to any of them | **PASS** | Test-file diff is +246/-0 (numstat and a zero count of deletion lines); the 8 baseline `It` blocks (5 ConvertTo + 3 allowlist, including `lines-valid | Should -Be '3'`) all pass at HEAD | +| AC-15 | Toolchain green: format no-change, analyzer no new findings vs baseline (the one `PSUseSingularNouns` baselined), Pester FailedCount 0; recorded under `qa-gates/` | **PASS** | Reviewer re-ran the analyzer at HEAD: exactly the one baselined warning (line 146 -> 140, key-identical) on the production file, zero on tests; reviewer re-ran Pester: 19/19; format evidence records hash-identical files; all three gate artifacts present under `qa-gates/` with full schema fields | +| AC-16 | Canonical evidence locations; command-step artifacts carry Timestamp/Command/EXIT_CODE (+ Output Summary for baselines); narrative artifacts carry Timestamp and are enumerated in the final sweep; nothing under `artifacts/` | **PASS** | Reviewer field-scanned all 30 evidence markdown files: 27 command-step artifacts carry all four fields; the 3 narrative artifacts (`ac-status-summary`, `helper-branch-test-map`, `threshold-handoff-494`) carry `Timestamp:` and are enumerated or declared-in-advance in the final sweep; the full evidence tree (35 files) sits under the five canonical `/evidence/` kinds; the branch diff contains zero files under any `artifacts/` path | +| AC-17 | No threshold re-tuned; 85.0317%-vs-85% recorded as a handoff to #494 and nowhere acted upon | **PASS** | Reviewer re-ran `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config`: empty; the handoff artifact states explicitly it is a record of fact, names #494 (wave 2) as owner, and its "what this feature does about it: nothing" section is borne out by the empty diff | +| AC-18 | Exactly two source files changed; `Invoke-MSTestWithCoverage.ps1` untouched including its missing `\.claude\` exclusion | **PASS** | Full-diff enumeration minus `docs/**` and `.claude/agent-memory/**` yields exactly the two files; `Invoke-MSTestWithCoverage.ps1` absent from the diff; the missing exclusion is now filed as issue #531 rather than fixed here | +| AC-19 | Production file remains under 500 lines (357 before) | **PASS** | `awk NR`: 455 lines at HEAD (baseline 357 confirmed); the test file is 468 (baseline 222) — both under the 500-line ceiling that applies to test code as well | +| AC-20 | Four follow-ups filed through the promotion lifecycle with issue numbers recorded in evidence; none fixed in this change | **PASS** | Reviewer confirmed issues #529, #530, #531, #532 exist and are OPEN on GitHub with titles matching the four candidates; the four promoted potential entries are committed under `docs/features/potential/promoted/`; the RESOLUTION section of `followups-441.2026-08-10T23-25.md` and the superseding update in `ac-status-summary` record the numbers; `followups-not-fixed` evidence plus the diff itself (package rates unrecomputed, `` un-merged per F6, discovery exclusion absent, agent-memory generalization uncorrected in the flagged file) confirm none was fixed here | + +## Check-off Reconciliation + +All 20 items were already checked `[x]` in `spec.md` and every one was independently verified PASS, so no check mark was changed and no item required unchecking. The AC-20 check-off history is honest: the executing session left it unchecked with a truthful `POSTING BLOCKED` record; the orchestrator later satisfied it and the audit trail of that sequence is preserved rather than rewritten. + +## Executor Self-Reported Items + +1. **AC-20 completed by the orchestrator:** verified genuinely satisfied; the blocked-then-resolved trail (original record retained, dated RESOLUTION appended, superseding update clearly marked) is legible and truthful. +2. **P0-T13 CORRECTION:** adequate — the retracted sentence is quoted, the invalid instrument is explained, and no downstream conclusion depended on it (all cited coverage figures come from direct `Invoke-Pester` runs writing to explicit `/evidence/` paths, verified per-artifact). + +### Acceptance Criteria Status + +- Source: `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` +- Total AC items: 20 +- Checked off (delivered): 20 +- Remaining (unchecked): 0 +- Items remaining: none + +## Verdict + +**PASS.** All 20 acceptance criteria independently verified. 0 blocking findings; 5 non-blocking findings recorded in `policy-audit.2026-08-10T23-35.md` (NF-1..NF-5) and `code-review.2026-08-10T23-35.md`. No remediation cycle required. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md index f9d4a6bca..0bde7a7cc 100644 --- a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/plan.2026-08-10T14-07.md @@ -174,13 +174,13 @@ budgets in § Test-File Line Budget. ### Phase 0 — Baseline Capture and Policy Reads -- [ ] [P0-T1] Read `\CLAUDE.md` in full. Acceptance: file read and its path recorded in the Phase 0 artifact file list. -- [ ] [P0-T2] Read `\.claude\rules\general-code-change.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. -- [ ] [P0-T3] Read `\.claude\rules\general-unit-test.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. -- [ ] [P0-T4] Read `\.claude\rules\powershell.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. -- [ ] [P0-T5] Read `\docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\spec.md` in full and record that it contains exactly 20 unchecked AC items numbered AC-1 through AC-20. Acceptance: the count 20 is recorded in the Phase 0 artifact. -- [ ] [P0-T6] Read `\docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\research\2026-08-10T14-20-cobertura-arithmetic-research.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. -- [ ] [P0-T7] Read `\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1` and `\tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1` and record their pre-change line counts, measured with the exact command below so the figure is reproducible. Acceptance: the recorded counts are exactly **357** and **222**. Any other value halts the plan for re-baselining. The test-file figure is 222, not 223: verified 2026-08-10T18-24 by both `(Get-Content).Count` and `wc -l`, with the file confirmed to terminate in a newline. +- [x] [P0-T1] Read `\CLAUDE.md` in full. Acceptance: file read and its path recorded in the Phase 0 artifact file list. +- [x] [P0-T2] Read `\.claude\rules\general-code-change.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. +- [x] [P0-T3] Read `\.claude\rules\general-unit-test.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. +- [x] [P0-T4] Read `\.claude\rules\powershell.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. +- [x] [P0-T5] Read `\docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\spec.md` in full and record that it contains exactly 20 unchecked AC items numbered AC-1 through AC-20. Acceptance: the count 20 is recorded in the Phase 0 artifact. +- [x] [P0-T6] Read `\docs\features\active\2026-08-10-cobertura-coverage-arithmetic-441\research\2026-08-10T14-20-cobertura-arithmetic-research.md` in full. Acceptance: file read and path recorded in the Phase 0 artifact file list. +- [x] [P0-T7] Read `\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1` and `\tests\scripts\vscode\Invoke-MSTestWithCoverage.Helpers.Tests.ps1` and record their pre-change line counts, measured with the exact command below so the figure is reproducible. Acceptance: the recorded counts are exactly **357** and **222**. Any other value halts the plan for re-baselining. The test-file figure is 222, not 223: verified 2026-08-10T18-24 by both `(Get-Content).Count` and `wc -l`, with the file confirmed to terminate in a newline. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -191,8 +191,8 @@ foreach ($p in @( } ``` -- [ ] [P0-T8] Write `/evidence/baseline/phase0-instructions-read.md` containing `Timestamp:`, `Policy Order:` (CLAUDE.md -> general-code-change.md -> general-unit-test.md -> powershell.md), the **resolved absolute value of ``** together with the `git rev-parse --show-toplevel` output it came from, and the explicit list of every file read in P0-T1..P0-T7. Acceptance: the file exists and contains all four fields plus seven file paths, and the recorded `` is the worktree the executor is actually running in. -- [ ] [P0-T9] Record the git baseline to `/evidence/baseline/git-baseline..md`: current branch, `git rev-parse HEAD`, `git rev-parse edf3d34c`, and `git status --porcelain`. Acceptance: artifact records the branch as `bug/cobertura-coverage-arithmetic-441`, confirms `git merge-base --is-ancestor edf3d34c HEAD` succeeds, and captures the porcelain output verbatim. The HEAD sha is recorded as an observation only and is never used as a later expectation. **If the branch name does not match, halt** — the worktree was provisioned incorrectly. Do not create, switch, or rename a branch from within this plan; branch provisioning belongs to `epic-orchestrator`. +- [x] [P0-T8] Write `/evidence/baseline/phase0-instructions-read.md` containing `Timestamp:`, `Policy Order:` (CLAUDE.md -> general-code-change.md -> general-unit-test.md -> powershell.md), the **resolved absolute value of ``** together with the `git rev-parse --show-toplevel` output it came from, and the explicit list of every file read in P0-T1..P0-T7. Acceptance: the file exists and contains all four fields plus seven file paths, and the recorded `` is the worktree the executor is actually running in. +- [x] [P0-T9] Record the git baseline to `/evidence/baseline/git-baseline..md`: current branch, `git rev-parse HEAD`, `git rev-parse edf3d34c`, and `git status --porcelain`. Acceptance: artifact records the branch as `bug/cobertura-coverage-arithmetic-441`, confirms `git merge-base --is-ancestor edf3d34c HEAD` succeeds, and captures the porcelain output verbatim. The HEAD sha is recorded as an observation only and is never used as a later expectation. **If the branch name does not match, halt** — the worktree was provisioned incorrectly. Do not create, switch, or rename a branch from within this plan; branch provisioning belongs to `epic-orchestrator`. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -204,8 +204,8 @@ git merge-base --is-ancestor edf3d34c HEAD; 'ancestor-check-exit=' + $LASTEXITCO git status --porcelain ``` -- [ ] [P0-T10] Verify both committed sample documents exist and are readable at `\docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\baseline\coverage-baseline.cobertura.xml` and `...\evidence\qa-gates\coverage-final.cobertura.xml`. Acceptance: both paths resolve; if either is absent the plan halts (the A/B evidence method depends on them). -- [ ] [P0-T11] Capture the PRE-CHANGE generator-parity A/B against unmodified `Helpers.ps1` and write `/evidence/baseline/prechange-generator-parity..md`. Acceptance: the artifact records `LinesValid` exactly `161086` plus `LinesCovered`, `BranchesValid` and `BranchesCovered` as concrete integers (no placeholder), alongside the input document's own root attributes `79957 / 56124 / 23109 / 13472`. This is a deterministic A/B over a fixed committed input, not a test-suite run. +- [x] [P0-T10] Verify both committed sample documents exist and are readable at `\docs\features\active\2026-08-06-quickfiler-high-confidence-queue-init-stall-424\evidence\baseline\coverage-baseline.cobertura.xml` and `...\evidence\qa-gates\coverage-final.cobertura.xml`. Acceptance: both paths resolve; if either is absent the plan halts (the A/B evidence method depends on them). +- [x] [P0-T11] Capture the PRE-CHANGE generator-parity A/B against unmodified `Helpers.ps1` and write `/evidence/baseline/prechange-generator-parity..md`. Acceptance: the artifact records `LinesValid` exactly `161086` plus `LinesCovered`, `BranchesValid` and `BranchesCovered` as concrete integers (no placeholder), alongside the input document's own root attributes `79957 / 56124 / 23109 / 13472`. This is a deterministic A/B over a fixed committed input, not a test-suite run. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -217,7 +217,7 @@ $sample = Join-Path $root 'docs\features\active\2026-08-06-quickfiler-high-confi Get-CoberturaCoverageSummary -XmlDocument $doc | Format-List ``` -- [ ] [P0-T12] Capture the PRE-CHANGE package-filtered A/B by reprocessing `coverage-final.cobertura.xml` through `ConvertTo-KoverageCoberturaXml`, and write `/evidence/baseline/prechange-package-filtered..md`. Acceptance: the artifact records `lines-valid = 110849`, `lines-covered = 94937`, `line-rate = 0.856453` as concrete values. Allow a generous timeout: the input is ~186,913 lines and the `[xml]` cast materializes a full DOM. +- [x] [P0-T12] Capture the PRE-CHANGE package-filtered A/B by reprocessing `coverage-final.cobertura.xml` through `ConvertTo-KoverageCoberturaXml`, and write `/evidence/baseline/prechange-package-filtered..md`. Acceptance: the artifact records `lines-valid = 110849`, `lines-covered = 94937`, `line-rate = 0.856453` as concrete values. Allow a generous timeout: the input is ~186,913 lines and the `[xml]` cast materializes a full DOM. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -230,9 +230,9 @@ $content = Get-Content -LiteralPath $sample -Raw -Encoding UTF8 $out.coverage.'branches-valid', $out.coverage.'branches-covered', $out.coverage.'branch-rate' ``` -- [ ] [P0-T13] Confirm the tool surface described in § Coverage Measurement Contract still holds in the executing worktree, and write `/evidence/baseline/poshqc-tool-surface..md`. Acceptance: the artifact records (a) that `mcp__drm-copilot__run_poshqc_format`, `..._analyze` and `..._test` all accept a `scan_folders` array and that this plan passes `["scripts/vscode", "tests/scripts/vscode"]` to each; (b) that `run_poshqc_test` returns a summary string and writes no coverage artifact, evidenced by an empty `git status --porcelain` immediately after a scoped call; (c) that `scripts/powershell/PoshQC/settings/pester.runsettings.psd1` and `config/poshqc-scan.json` are both absent and that the bundled MCP settings are used instead; and (d) the installed Pester version, which must be >= 5.0. If any of (a)-(d) differs from the recorded values, halt and re-baseline rather than improvising an invocation. -- [ ] [P0-T14] Run `mcp__drm-copilot__run_poshqc_format` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`. Write `/evidence/baseline/poshqc-format..md`. Acceptance: artifact records `EXIT_CODE:` and the set of files changed, measured by the two-instrument method below. If any file other than the two in-scope paths and `.claude/agent-memory/**` was modified, restore it with `git checkout -- ` and record the restoration in the artifact. `.claude/agent-memory/**` is tracked in this repository and is legitimately written by the executing agent; exclude it from the out-of-scope changed-file set and never `git checkout --` it. `EXIT_CODE:` records `0` when the payload is `ok:true` and `1` otherwise, and the payload's `summary` is quoted verbatim. See P4-T1 for the two-instrument snippet; the same method applies here. -- [ ] [P0-T15] Run `mcp__drm-copilot__run_poshqc_analyze` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, then run `Invoke-ScriptAnalyzer` directly on each of the two in-scope files to obtain a per-file, per-rule breakdown the MCP summary does not provide. Write `/evidence/baseline/poshqc-analyze..md`. Acceptance: the artifact records `EXIT_CODE:` for the MCP call and an explicit **per-file baseline finding list** for the two in-scope files. The expected baseline, verified 2026-08-10T18-24, is exactly one finding on `Invoke-MSTestWithCoverage.Helpers.ps1` (`PSUseSingularNouns`, line 146, `Get-CoberturaLineConditionCoverageParts`) and zero on `Invoke-MSTestWithCoverage.Helpers.Tests.ps1`. This recorded set is the baseline that P4-T2 diffs against. Do not fix the `PSUseSingularNouns` finding — see § Verified Environment Facts. **Record the baseline keyed on `(ScriptName, RuleName, Severity, Message)` and record `Line` as an observation only, excluded from the key.** P2-T2 shortens `Get-CoberturaCoverageSummary`'s inner loop, which shifts every later declaration upward, so the `PSUseSingularNouns` finding will not still be on line 146 after the change. A line-number move on an otherwise-identical finding is not a new finding. +- [x] [P0-T13] Confirm the tool surface described in § Coverage Measurement Contract still holds in the executing worktree, and write `/evidence/baseline/poshqc-tool-surface..md`. Acceptance: the artifact records (a) that `mcp__drm-copilot__run_poshqc_format`, `..._analyze` and `..._test` all accept a `scan_folders` array and that this plan passes `["scripts/vscode", "tests/scripts/vscode"]` to each; (b) that `run_poshqc_test` returns a summary string and writes no coverage artifact, evidenced by an empty `git status --porcelain` immediately after a scoped call; (c) that `scripts/powershell/PoshQC/settings/pester.runsettings.psd1` and `config/poshqc-scan.json` are both absent and that the bundled MCP settings are used instead; and (d) the installed Pester version, which must be >= 5.0. If any of (a)-(d) differs from the recorded values, halt and re-baseline rather than improvising an invocation. +- [x] [P0-T14] Run `mcp__drm-copilot__run_poshqc_format` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`. Write `/evidence/baseline/poshqc-format..md`. Acceptance: artifact records `EXIT_CODE:` and the set of files changed, measured by the two-instrument method below. If any file other than the two in-scope paths and `.claude/agent-memory/**` was modified, restore it with `git checkout -- ` and record the restoration in the artifact. `.claude/agent-memory/**` is tracked in this repository and is legitimately written by the executing agent; exclude it from the out-of-scope changed-file set and never `git checkout --` it. `EXIT_CODE:` records `0` when the payload is `ok:true` and `1` otherwise, and the payload's `summary` is quoted verbatim. See P4-T1 for the two-instrument snippet; the same method applies here. +- [x] [P0-T15] Run `mcp__drm-copilot__run_poshqc_analyze` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, then run `Invoke-ScriptAnalyzer` directly on each of the two in-scope files to obtain a per-file, per-rule breakdown the MCP summary does not provide. Write `/evidence/baseline/poshqc-analyze..md`. Acceptance: the artifact records `EXIT_CODE:` for the MCP call and an explicit **per-file baseline finding list** for the two in-scope files. The expected baseline, verified 2026-08-10T18-24, is exactly one finding on `Invoke-MSTestWithCoverage.Helpers.ps1` (`PSUseSingularNouns`, line 146, `Get-CoberturaLineConditionCoverageParts`) and zero on `Invoke-MSTestWithCoverage.Helpers.Tests.ps1`. This recorded set is the baseline that P4-T2 diffs against. Do not fix the `PSUseSingularNouns` finding — see § Verified Environment Facts. **Record the baseline keyed on `(ScriptName, RuleName, Severity, Message)` and record `Line` as an observation only, excluded from the key.** P2-T2 shortens `Get-CoberturaCoverageSummary`'s inner loop, which shifts every later declaration upward, so the `PSUseSingularNouns` finding will not still be on line 146 after the change. A line-number move on an otherwise-identical finding is not a new finding. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -245,23 +245,23 @@ foreach ($p in @( } ``` -- [ ] [P0-T16] Capture the baseline test run and numeric coverage. Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]` for the suite verdict, then run the direct Pester capture from § Coverage Measurement Contract with `OutputPath` = `/evidence/baseline/pester-coverage-baseline..xml` for the numbers. Write `/evidence/baseline/pester-baseline..md`. Acceptance: the artifact records suite totals (passed/failed/skipped), the fact that all **eight** pre-existing `It` blocks in `Invoke-MSTestWithCoverage.Helpers.Tests.ps1` pass, and the four JaCoCo counters (`INSTRUCTION`, `LINE`, `METHOD`, `CLASS`) as concrete missed/covered integers plus the derived baseline **line-coverage percentage**. No placeholder values. The expected baseline, verified 2026-08-10T18-24, is 8 passed / 0 failed and `LINE` 146 covered / 19 missed = 88.48%. Record explicitly that Pester emits **no `BRANCH` counter**, listing the counters actually present as the proof. +- [x] [P0-T16] Capture the baseline test run and numeric coverage. Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]` for the suite verdict, then run the direct Pester capture from § Coverage Measurement Contract with `OutputPath` = `/evidence/baseline/pester-coverage-baseline..xml` for the numbers. Write `/evidence/baseline/pester-baseline..md`. Acceptance: the artifact records suite totals (passed/failed/skipped), the fact that all **eight** pre-existing `It` blocks in `Invoke-MSTestWithCoverage.Helpers.Tests.ps1` pass, and the four JaCoCo counters (`INSTRUCTION`, `LINE`, `METHOD`, `CLASS`) as concrete missed/covered integers plus the derived baseline **line-coverage percentage**. No placeholder values. The expected baseline, verified 2026-08-10T18-24, is 8 passed / 0 failed and `LINE` 146 covered / 19 missed = 88.48%. Record explicitly that Pester emits **no `BRANCH` counter**, listing the counters actually present as the proof. -- [ ] [P0-T17] Verify the plan is running against the amended, satisfiable spec rather than a superseded one. Read `/spec.md` § Acceptance Criteria. Acceptance: AC-15 contains the phrase `absent from the recorded Phase 0 baseline`, and AC-16 enumerates `issue-updates` and `other` among the permitted evidence sub-paths. If either phrase is absent, **halt** — the plan's Phase 4 and Phase 7 gates were written against amended AC text and would otherwise check off criteria that cannot be satisfied. Do not edit `spec.md` to make this task pass; a mismatch is a planning defect requiring a plan and spec revision, not an execution workaround. -- [ ] [P0-T18] Prove `spec.md` Assumption 2 (method-level line numbers are a subset of the class-level rollup) on both committed sample documents **before** any implementation, so that a document drift fails at baseline rather than after Phase 2. Perform a read-only streaming pass over each sample with an `XmlReader`, applying the union and `max(hits)` rule this plan specifies, and write `/evidence/baseline/assumption2-subset-proof..md`. Acceptance: for `coverage-baseline.cobertura.xml` the artifact records class-level distinct = 79957, union distinct = 79957, union covered = 56124, union branches valid/covered = 23109/13472, and **method-only line keys = 0**; for `coverage-final.cobertura.xml` it records class-level distinct = 62345, union distinct = 62345, union covered = 53013, and **method-only line keys = 0**. Use a streaming reader, not an `[xml]` cast: these documents are 17 MB and 10 MB. A non-zero method-only key count is a **spec-level** finding — it means the union design and the class-level oracle disagree — and requires a plan and spec revision. It is explicitly **not** an implementation defect and must **not** trigger the Phase 5 return-to-Phase-2 loop. +- [x] [P0-T17] Verify the plan is running against the amended, satisfiable spec rather than a superseded one. Read `/spec.md` § Acceptance Criteria. Acceptance: AC-15 contains the phrase `absent from the recorded Phase 0 baseline`, and AC-16 enumerates `issue-updates` and `other` among the permitted evidence sub-paths. If either phrase is absent, **halt** — the plan's Phase 4 and Phase 7 gates were written against amended AC text and would otherwise check off criteria that cannot be satisfied. Do not edit `spec.md` to make this task pass; a mismatch is a planning defect requiring a plan and spec revision, not an execution workaround. +- [x] [P0-T18] Prove `spec.md` Assumption 2 (method-level line numbers are a subset of the class-level rollup) on both committed sample documents **before** any implementation, so that a document drift fails at baseline rather than after Phase 2. Perform a read-only streaming pass over each sample with an `XmlReader`, applying the union and `max(hits)` rule this plan specifies, and write `/evidence/baseline/assumption2-subset-proof..md`. Acceptance: for `coverage-baseline.cobertura.xml` the artifact records class-level distinct = 79957, union distinct = 79957, union covered = 56124, union branches valid/covered = 23109/13472, and **method-only line keys = 0**; for `coverage-final.cobertura.xml` it records class-level distinct = 62345, union distinct = 62345, union covered = 53013, and **method-only line keys = 0**. Use a streaming reader, not an `[xml]` cast: these documents are 17 MB and 10 MB. A non-zero method-only key count is a **spec-level** finding — it means the union design and the class-level oracle disagree — and requires a plan and spec revision. It is explicitly **not** an implementation defect and must **not** trigger the Phase 5 return-to-Phase-2 loop. ### Phase 1 — Regression Fixtures Authored and Demonstrated Red Bugfix Workflow (`CLAUDE.md` § Bugfix Workflow) applies: the regression tests come first and must be demonstrated failing against unmodified `Helpers.ps1`. All six fixtures are **new** `It` blocks appended inside the existing `Describe 'ConvertTo-KoverageCoberturaXml'` block in `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`. No existing block may be modified. Every fixture uses an inline single-quoted here-string (`@'` ... `'@`), creates no file on disk, uses no mock, and passes `-ProjectNames` explicitly for determinism. -- [ ] [P1-T1] [expect-fail] Add fixture **F1** (issue #441, lines) to `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`: one package, one class; `` with one `` carrying lines 10 (`hits=1`), 11 (`hits=0`), 12 (`hits=1`); class-level `` carrying the identical three. Acceptance: the block asserts root `lines-valid` = `'3'`, `lines-covered` = `'2'` and `line-rate` = `'0.666667'`, and the block is <= 24 lines. It must assert the counts, not the rate alone. -- [ ] [P1-T2] [expect-fail] Add fixture **F2** (issue #441, branches): as F1 (lines 10 and 11 retain `branch="False"`) plus line 12 carrying `branch="True" condition-coverage="50% (1/2)"` with a `` child on **both** axes. Acceptance: the block asserts root `branches-valid` = `'2'` and `branches-covered` = `'1'`; it contains **no** assertion on `branch-rate` alone; the block is <= 28 lines. -- [ ] [P1-T3] [expect-fail] Add fixture **F3** (issue #478, merge): two classes with the same `filename`; primary `Ns.Foo` with `` lines 56,57,58 (`hits=1`) and class-level `` 56,57,58; sibling `Ns.Foo.<>c` with `` lines 12,13 (`hits=0`) and class-level `` 12,13. Acceptance: the block asserts the merged class `line-rate` = `'0.6'` and that the merged class-level `` has exactly five `line` children numbered 12, 13, 56, 57, 58 in ascending order; the block is <= 34 lines. -- [ ] [P1-T4] [expect-fail] Add fixture **F4** (`max(hits)` dedup): one class where line 5 appears in `.ctor ()` with `hits=1` and in `.ctor (int)` with `hits=0`, and class-level `` has line 5 with `hits=1`. Acceptance: the block asserts root `lines-valid` = `'1'` and `lines-covered` = `'1'`; the block is <= 26 lines. -- [ ] [P1-T5] Add fixture **F5** (rollup-absent guard): one class with `` carrying lines 20 (`hits=1`) and 21 (`hits=0`) and **no class-level `` element at all**. Acceptance: the block asserts root `lines-valid` = `'2'` and `lines-covered` = `'1'`; the block is <= 24 lines. F5 passes both before and after the fix and is therefore **not** tagged `[expect-fail]`. -- [ ] [P1-T6] Add fixture **F6** (structure preservation): reuse the F3 document. Acceptance: the block asserts the merged class still carries a `` element with exactly one `` child (the primary's), and that every merged class-level `` retains its input `hits` value (12 -> `'0'`, 13 -> `'0'`, 56 -> `'1'`, 57 -> `'1'`, 58 -> `'1'`); the block is <= 34 lines. F6 passes both before and after the fix and is therefore **not** tagged `[expect-fail]`. -- [ ] [P1-T7] [expect-fail] Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]` against unmodified `Helpers.ps1` and record its `ok` and `summary` verbatim as non-probative, **then** run the direct Pester capture from § Test Verdict and Coverage Measurement Contract for the actual verdict. Write `/evidence/regression-testing/fail-before-f1-f4..md`. Acceptance: the direct run reports `FailedCount` = **4** and `PassedCount` = **10**, and its per-test listing names F1, F2, F3 and F4 as the four failures with `ErrorRecord` messages showing 6/4, 4/2, `'0.75'` and 3/2 respectively, while F5, F6 and all eight pre-existing blocks are listed `Passed`; `EXIT_CODE: 1` (`PESTER_EXIT_CODE`). Any other failure count halts the plan. The MCP payload must not be used to establish any of these values — it returns `ok:true` whether the suite is green or red. -- [ ] [P1-T8] Verify the fixtures were actually written, that no existing test block was modified, and that no production file has changed yet. Acceptance: `git diff HEAD --numstat -- tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` emits **exactly one** record whose additions field is **>= 100** and whose deletions field is **exactly 0**; an empty result **fails** this gate, because an unmodified file also emits nothing and would otherwise satisfy a bare "0 deletions" reading while proving the fixtures were never written. The `HEAD` operand makes the check immune to staging. `git diff --name-only edf3d34c -- scripts` returns empty. Append both outputs to the artifact created by P1-T7, reusing its `` — do not create a second file. +- [x] [P1-T1] [expect-fail] Add fixture **F1** (issue #441, lines) to `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`: one package, one class; `` with one `` carrying lines 10 (`hits=1`), 11 (`hits=0`), 12 (`hits=1`); class-level `` carrying the identical three. Acceptance: the block asserts root `lines-valid` = `'3'`, `lines-covered` = `'2'` and `line-rate` = `'0.666667'`, and the block is <= 24 lines. It must assert the counts, not the rate alone. +- [x] [P1-T2] [expect-fail] Add fixture **F2** (issue #441, branches): as F1 (lines 10 and 11 retain `branch="False"`) plus line 12 carrying `branch="True" condition-coverage="50% (1/2)"` with a `` child on **both** axes. Acceptance: the block asserts root `branches-valid` = `'2'` and `branches-covered` = `'1'`; it contains **no** assertion on `branch-rate` alone; the block is <= 28 lines. +- [x] [P1-T3] [expect-fail] Add fixture **F3** (issue #478, merge): two classes with the same `filename`; primary `Ns.Foo` with `` lines 56,57,58 (`hits=1`) and class-level `` 56,57,58; sibling `Ns.Foo.<>c` with `` lines 12,13 (`hits=0`) and class-level `` 12,13. Acceptance: the block asserts the merged class `line-rate` = `'0.6'` and that the merged class-level `` has exactly five `line` children numbered 12, 13, 56, 57, 58 in ascending order; the block is <= 34 lines. +- [x] [P1-T4] [expect-fail] Add fixture **F4** (`max(hits)` dedup): one class where line 5 appears in `.ctor ()` with `hits=1` and in `.ctor (int)` with `hits=0`, and class-level `` has line 5 with `hits=1`. Acceptance: the block asserts root `lines-valid` = `'1'` and `lines-covered` = `'1'`; the block is <= 26 lines. +- [x] [P1-T5] Add fixture **F5** (rollup-absent guard): one class with `` carrying lines 20 (`hits=1`) and 21 (`hits=0`) and **no class-level `` element at all**. Acceptance: the block asserts root `lines-valid` = `'2'` and `lines-covered` = `'1'`; the block is <= 24 lines. F5 passes both before and after the fix and is therefore **not** tagged `[expect-fail]`. +- [x] [P1-T6] Add fixture **F6** (structure preservation): reuse the F3 document. Acceptance: the block asserts the merged class still carries a `` element with exactly one `` child (the primary's), and that every merged class-level `` retains its input `hits` value (12 -> `'0'`, 13 -> `'0'`, 56 -> `'1'`, 57 -> `'1'`, 58 -> `'1'`); the block is <= 34 lines. F6 passes both before and after the fix and is therefore **not** tagged `[expect-fail]`. +- [x] [P1-T7] [expect-fail] Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]` against unmodified `Helpers.ps1` and record its `ok` and `summary` verbatim as non-probative, **then** run the direct Pester capture from § Test Verdict and Coverage Measurement Contract for the actual verdict. Write `/evidence/regression-testing/fail-before-f1-f4..md`. Acceptance: the direct run reports `FailedCount` = **4** and `PassedCount` = **10**, and its per-test listing names F1, F2, F3 and F4 as the four failures with `ErrorRecord` messages showing 6/4, 4/2, `'0.75'` and 3/2 respectively, while F5, F6 and all eight pre-existing blocks are listed `Passed`; `EXIT_CODE: 1` (`PESTER_EXIT_CODE`). Any other failure count halts the plan. The MCP payload must not be used to establish any of these values — it returns `ok:true` whether the suite is green or red. +- [x] [P1-T8] Verify the fixtures were actually written, that no existing test block was modified, and that no production file has changed yet. Acceptance: `git diff HEAD --numstat -- tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` emits **exactly one** record whose additions field is **>= 100** and whose deletions field is **exactly 0**; an empty result **fails** this gate, because an unmodified file also emits nothing and would otherwise satisfy a bare "0 deletions" reading while proving the fixtures were never written. The `HEAD` operand makes the check immune to staging. `git diff --name-only edf3d34c -- scripts` returns empty. Append both outputs to the artifact created by P1-T7, reusing its `` — do not create a second file. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -272,7 +272,7 @@ git diff --name-only edf3d34c -- scripts ### Phase 2 — Minimal Fix in Invoke-MSTestWithCoverage.Helpers.ps1 -- [ ] [P2-T1] Add the new pure function `Get-CoberturaClassLineSummary` to `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, inserted immediately after `Get-CoberturaLineConditionCoverageParts` (which ends at pre-change line 165) and before `function Merge-CoberturaClassesByFilename`. Acceptance: the function has `[CmdletBinding()]`, `[OutputType([pscustomobject])]`, a single mandatory `[System.Xml.XmlElement]$ClassNode` parameter, and returns exactly `LineMap`, `TotalLines`, `CoveredLines`, `TotalBranches`, `CoveredBranches`. It performs no I/O and mutates nothing in the source document. +- [x] [P2-T1] Add the new pure function `Get-CoberturaClassLineSummary` to `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, inserted immediately after `Get-CoberturaLineConditionCoverageParts` (which ends at pre-change line 165) and before `function Merge-CoberturaClassesByFilename`. Acceptance: the function has `[CmdletBinding()]`, `[OutputType([pscustomobject])]`, a single mandatory `[System.Xml.XmlElement]$ClassNode` parameter, and returns exactly `LineMap`, `TotalLines`, `CoveredLines`, `TotalBranches`, `CoveredBranches`. It performs no I/O and mutates nothing in the source document. Construction rule (each map entry carries `Node`, `Hits`, `Branch`, `Covered`, `Total`; `Branch` is required to express the "branch=True if either" semantics of AC-6): @@ -293,7 +293,7 @@ Construction rule (each map entry carries `Node`, `Hits`, `Branch`, `Covered`, ` # CoveredBranches = sum of Covered over entries whose Branch is $true ``` -- [ ] [P2-T2] Replace the inner loop body of `Get-CoberturaCoverageSummary` (pre-change lines 122-132) with one call to `Get-CoberturaClassLineSummary` per class, accumulating the four returned totals. Acceptance: the function keeps its `[xml]$XmlDocument` signature and its `throw 'Cobertura XML does not contain a node.'` guard verbatim, keeps the `LineRate`/`BranchRate` rounding (`[math]::Round($covered / $total, 6)`) and the `'0'` zero-denominator fallback, and no longer contains a descendant-axis line selection. +- [x] [P2-T2] Replace the inner loop body of `Get-CoberturaCoverageSummary` (pre-change lines 122-132) with one call to `Get-CoberturaClassLineSummary` per class, accumulating the four returned totals. Acceptance: the function keeps its `[xml]$XmlDocument` signature and its `throw 'Cobertura XML does not contain a node.'` guard verbatim, keeps the `LineRate`/`BranchRate` rounding (`[math]::Round($covered / $total, 6)`) and the `'0'` zero-denominator fallback, and no longer contains a descendant-axis line selection. ```powershell foreach ($cls in $pkg.SelectNodes('.//class')) { @@ -305,7 +305,7 @@ foreach ($cls in $pkg.SelectNodes('.//class')) { } ``` -- [ ] [P2-T3] Remove the `$classSummaryXml` synthetic-document block at pre-change lines 270-273 in `Merge-CoberturaClassesByFilename` and set the merged class's `line-rate` / `branch-rate` from a direct `Get-CoberturaClassLineSummary` call on `$mergedClassNode`. Acceptance: the token `$classSummaryXml` no longer appears anywhere in the file, no `ImportNode` call remains in that function, and the two rate strings are produced by the identical rounding and zero-denominator expression used in `Get-CoberturaCoverageSummary`. Do **not** introduce a second new function to share the formatting: `spec.md` § Proposed Fix specifies exactly one new helper. Add a short comment recording why the expression is duplicated. +- [x] [P2-T3] Remove the `$classSummaryXml` synthetic-document block at pre-change lines 270-273 in `Merge-CoberturaClassesByFilename` and set the merged class's `line-rate` / `branch-rate` from a direct `Get-CoberturaClassLineSummary` call on `$mergedClassNode`. Acceptance: the token `$classSummaryXml` no longer appears anywhere in the file, no `ImportNode` call remains in that function, and the two rate strings are produced by the identical rounding and zero-denominator expression used in `Get-CoberturaCoverageSummary`. Do **not** introduce a second new function to share the formatting: `spec.md` § Proposed Fix specifies exactly one new helper. Add a short comment recording why the expression is duplicated. ```powershell $mergedSummary = Get-CoberturaClassLineSummary -ClassNode $mergedClassNode @@ -317,27 +317,27 @@ $mergedClassNode.SetAttribute('line-rate', $mergedLineRate) $mergedClassNode.SetAttribute('branch-rate', $mergedBranchRate) ``` -- [ ] [P2-T4] Verify the defect is removed at its one site, **using fixed-string search only** (`rg -F` or `Select-String -SimpleMatch`) over `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. Acceptance: the literal `'.//lines/line'` (including its surrounding single quotes) returns **0** matches file-wide; the literal `$classSummaryXml` returns **0** matches file-wide; the literal `'./lines/line'` (including quotes) returns **exactly 2** matches file-wide — one inside `Get-CoberturaClassLineSummary` and the pre-existing one in the union builder; and the literal `'./methods/method/lines/line'` (including quotes) returns **exactly 1**. Regex search must not be used here: an unescaped `.` matches any character, so the pattern `./lines/line` also matches the substring `d/lines/line` inside `./methods/method/lines/line` and a correct implementation would return 2 where the gate expected 1. -- [ ] [P2-T5] Verify the union builder is untouched. Acceptance: in `git diff HEAD -U0 -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, no hunk's old-side range intersects pre-change lines 217-268, and the literal `foreach ($lineNode in @($classNode.SelectNodes('./lines/line')))` still occurs exactly once. The `HEAD` operand makes the check immune to staging. Record the hunk headers in `/evidence/qa-gates/union-builder-byte-identity..md`. -- [ ] [P2-T6] Verify `scripts/vscode/Invoke-MSTestWithCoverage.ps1` is unchanged. Acceptance: `git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1` returns empty output. +- [x] [P2-T4] Verify the defect is removed at its one site, **using fixed-string search only** (`rg -F` or `Select-String -SimpleMatch`) over `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. Acceptance: the literal `'.//lines/line'` (including its surrounding single quotes) returns **0** matches file-wide; the literal `$classSummaryXml` returns **0** matches file-wide; the literal `'./lines/line'` (including quotes) returns **exactly 2** matches file-wide — one inside `Get-CoberturaClassLineSummary` and the pre-existing one in the union builder; and the literal `'./methods/method/lines/line'` (including quotes) returns **exactly 1**. Regex search must not be used here: an unescaped `.` matches any character, so the pattern `./lines/line` also matches the substring `d/lines/line` inside `./methods/method/lines/line` and a correct implementation would return 2 where the gate expected 1. +- [x] [P2-T5] Verify the union builder is untouched. Acceptance: in `git diff HEAD -U0 -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, no hunk's old-side range intersects pre-change lines 217-268, and the literal `foreach ($lineNode in @($classNode.SelectNodes('./lines/line')))` still occurs exactly once. The `HEAD` operand makes the check immune to staging. Record the hunk headers in `/evidence/qa-gates/union-builder-byte-identity..md`. +- [x] [P2-T6] Verify `scripts/vscode/Invoke-MSTestWithCoverage.ps1` is unchanged. Acceptance: `git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1` returns empty output. ### Phase 3 — Green Verification and Helper Unit Tests -- [ ] [P3-T1] Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, recording its `ok` and `summary` separately and explicitly marked **non-probative**, then run the direct Pester capture from § Test Verdict and Coverage Measurement Contract for the verdict. Write `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: the direct run reports `FailedCount` = 0, `PassedCount` = **14** and `TotalCount` = **14** — eight pre-existing blocks plus F1-F6; the five helper unit tests added by P3-T3..P3-T7 do not exist yet, so 19 is not the expected figure at this point. F1-F6 are individually listed `Passed` in the per-test output with their post-fix values from `spec.md` § Test Strategy recorded; `EXIT_CODE: 0` (`PESTER_EXIT_CODE`). A `TotalCount` of 0 fails this gate: `FailedCount` = 0 alone is also satisfied by a run that discovered no tests. -- [ ] [P3-T2] Verify zero existing tests broke. Acceptance: the P3-T1 direct-run per-test listing names all **eight** pre-existing `It` blocks individually with `Result = Passed` (including the block asserting `lines-valid | Should -Be '3'`), quoted verbatim into the artifact; and `git diff HEAD --numstat -- tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` emits **exactly one** record whose deletions field is **exactly 0** and whose additions field is **>= 100**. An empty result fails this gate. Record both in the P3-T1 artifact. -- [ ] [P3-T3] Add a new `Describe 'Get-CoberturaClassLineSummary'` block to `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` with the first precedence-branch `It`: candidate `Total` **greater** than existing. Acceptance: the block builds a minimal `` element inline, calls `Get-CoberturaClassLineSummary -ClassNode` directly, and asserts the candidate's `condition-coverage` values are retained. -- [ ] [P3-T4] Add the second precedence-branch `It`: `Total` **equal** and `Covered` **greater**. Acceptance: the block asserts the candidate's values are retained. -- [ ] [P3-T5] Add the third precedence-branch `It`: **neither** condition holds. Acceptance: the block asserts the existing entry's values are retained. -- [ ] [P3-T6] Add a boundary `It`: a `` element with neither a `` element nor a `` element. Acceptance: the block asserts `TotalLines` = 0, `CoveredLines` = 0, `TotalBranches` = 0, `CoveredBranches` = 0 and that no exception is thrown. -- [ ] [P3-T7] Add an error-handling `It` **inside the existing `Describe 'ConvertTo-KoverageCoberturaXml'` block**, not inside the new `Describe 'Get-CoberturaClassLineSummary'` block created by P3-T3: `Get-CoberturaCoverageSummary` over a document with no `//packages` node. Acceptance: the block asserts it still throws `'Cobertura XML does not contain a node.'`, and it does not count against the 80-line budget for the `Get-CoberturaClassLineSummary` `Describe`. -- [ ] [P3-T8] Re-run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]` after the unit-test additions, recording its `ok` and `summary` as non-probative, then run the direct Pester capture for the verdict. Write `/evidence/regression-testing/helper-unit-tests..md`. Acceptance: the direct run reports `FailedCount` = 0, `PassedCount` = **19** and `TotalCount` = **19** — eight pre-existing blocks, F1-F6, and the five blocks added by P3-T3..P3-T7 — with every added precedence-branch and boundary `It` listed `Passed` by name; `EXIT_CODE: 0` (`PESTER_EXIT_CODE`). A `TotalCount` of 0 fails this gate. -- [ ] [P3-T9] Check the pre-format test-file line budget. Acceptance: `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` is <= 480 lines. If it exceeds 480, compact the here-strings per the Test-File Line Budget section — do **not** create a third test file, which would break AC-18. +- [x] [P3-T1] Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, recording its `ok` and `summary` separately and explicitly marked **non-probative**, then run the direct Pester capture from § Test Verdict and Coverage Measurement Contract for the verdict. Write `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: the direct run reports `FailedCount` = 0, `PassedCount` = **14** and `TotalCount` = **14** — eight pre-existing blocks plus F1-F6; the five helper unit tests added by P3-T3..P3-T7 do not exist yet, so 19 is not the expected figure at this point. F1-F6 are individually listed `Passed` in the per-test output with their post-fix values from `spec.md` § Test Strategy recorded; `EXIT_CODE: 0` (`PESTER_EXIT_CODE`). A `TotalCount` of 0 fails this gate: `FailedCount` = 0 alone is also satisfied by a run that discovered no tests. +- [x] [P3-T2] Verify zero existing tests broke. Acceptance: the P3-T1 direct-run per-test listing names all **eight** pre-existing `It` blocks individually with `Result = Passed` (including the block asserting `lines-valid | Should -Be '3'`), quoted verbatim into the artifact; and `git diff HEAD --numstat -- tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` emits **exactly one** record whose deletions field is **exactly 0** and whose additions field is **>= 100**. An empty result fails this gate. Record both in the P3-T1 artifact. +- [x] [P3-T3] Add a new `Describe 'Get-CoberturaClassLineSummary'` block to `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` with the first precedence-branch `It`: candidate `Total` **greater** than existing. Acceptance: the block builds a minimal `` element inline, calls `Get-CoberturaClassLineSummary -ClassNode` directly, and asserts the candidate's `condition-coverage` values are retained. +- [x] [P3-T4] Add the second precedence-branch `It`: `Total` **equal** and `Covered` **greater**. Acceptance: the block asserts the candidate's values are retained. +- [x] [P3-T5] Add the third precedence-branch `It`: **neither** condition holds. Acceptance: the block asserts the existing entry's values are retained. +- [x] [P3-T6] Add a boundary `It`: a `` element with neither a `` element nor a `` element. Acceptance: the block asserts `TotalLines` = 0, `CoveredLines` = 0, `TotalBranches` = 0, `CoveredBranches` = 0 and that no exception is thrown. +- [x] [P3-T7] Add an error-handling `It` **inside the existing `Describe 'ConvertTo-KoverageCoberturaXml'` block**, not inside the new `Describe 'Get-CoberturaClassLineSummary'` block created by P3-T3: `Get-CoberturaCoverageSummary` over a document with no `//packages` node. Acceptance: the block asserts it still throws `'Cobertura XML does not contain a node.'`, and it does not count against the 80-line budget for the `Get-CoberturaClassLineSummary` `Describe`. +- [x] [P3-T8] Re-run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]` after the unit-test additions, recording its `ok` and `summary` as non-probative, then run the direct Pester capture for the verdict. Write `/evidence/regression-testing/helper-unit-tests..md`. Acceptance: the direct run reports `FailedCount` = 0, `PassedCount` = **19** and `TotalCount` = **19** — eight pre-existing blocks, F1-F6, and the five blocks added by P3-T3..P3-T7 — with every added precedence-branch and boundary `It` listed `Passed` by name; `EXIT_CODE: 0` (`PESTER_EXIT_CODE`). A `TotalCount` of 0 fails this gate. +- [x] [P3-T9] Check the pre-format test-file line budget. Acceptance: `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` is <= 480 lines. If it exceeds 480, compact the here-strings per the Test-File Line Budget section — do **not** create a third test file, which would break AC-18. ### Phase 4 — Final QA Loop and Scope Gates Type checking is not applicable to PowerShell and is intentionally absent from this loop (`.claude/rules/powershell.md` step 3). Each of P4-T1, P4-T2 and P4-T3 is an unconditional command task; `EXIT_CODE: SKIPPED` is not a valid outcome for any of them. -- [ ] [P4-T1] Run `mcp__drm-copilot__run_poshqc_format` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`. Write `/evidence/qa-gates/poshqc-format..md`. Acceptance: artifact records `EXIT_CODE:` plus the set of files changed. If any file other than the two in-scope paths and `.claude/agent-memory/**` was modified, restore it with `git checkout -- `, record the restoration, and restart this phase from P4-T1. `.claude/agent-memory/**` is tracked in this repository and is legitimately written by the executing agent; exclude it from the out-of-scope changed-file set and never `git checkout --` it. **The restart is bounded.** `scan_folders` covers all of `scripts/vscode`, so the formatter may deterministically rewrite an unrelated pre-existing file such as `Install-RepoDotNetSdk.ps1`; restore-and-restart would then never terminate and P4-T4 would be unsatisfiable. If P0-T14 recorded the same out-of-scope file as formatter-modified, treat it as pre-existing formatting drift: restore it, record it in the artifact as known non-blocking drift, and proceed **without** restarting. Only an out-of-scope file that was *not* modified at P0-T14 triggers a restart. `EXIT_CODE:` records `0` when the payload is `ok:true` and `1` otherwise, and the payload's `summary` is quoted verbatim. +- [x] [P4-T1] Run `mcp__drm-copilot__run_poshqc_format` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`. Write `/evidence/qa-gates/poshqc-format..md`. Acceptance: artifact records `EXIT_CODE:` plus the set of files changed. If any file other than the two in-scope paths and `.claude/agent-memory/**` was modified, restore it with `git checkout -- `, record the restoration, and restart this phase from P4-T1. `.claude/agent-memory/**` is tracked in this repository and is legitimately written by the executing agent; exclude it from the out-of-scope changed-file set and never `git checkout --` it. **The restart is bounded.** `scan_folders` covers all of `scripts/vscode`, so the formatter may deterministically rewrite an unrelated pre-existing file such as `Install-RepoDotNetSdk.ps1`; restore-and-restart would then never terminate and P4-T4 would be unsatisfiable. If P0-T14 recorded the same out-of-scope file as formatter-modified, treat it as pre-existing formatting drift: restore it, record it in the artifact as known non-blocking drift, and proceed **without** restarting. Only an out-of-scope file that was *not* modified at P0-T14 triggers a restart. `EXIT_CODE:` records `0` when the payload is `ok:true` and `1` otherwise, and the payload's `summary` is quoted verbatim. **Change detection uses two instruments, because neither alone is sufficient.** The MCP payload reports no changed-file count at all. And by Phase 4 both in-scope files are already ` M` relative to `HEAD`, so if the formatter rewrites one of them its `git status --porcelain` line is *identical* before and after and the porcelain difference is empty — the gate would report "0 files changed" precisely when a file did change. Therefore: detect **in-scope** changes by SHA-256 content hash before and after the call, and use the porcelain before/after difference **only** to detect modification of files outside the two in-scope paths, which are clean and so do appear as new entries. @@ -372,10 +372,10 @@ foreach ($t in $targets) { ``` Compare the two hash listings and the two porcelain listings **in the artifact**. An in-scope file whose `before` and `after` hashes differ was changed by the formatter; a porcelain entry present only in the `after` listing, other than under `.claude/agent-memory/**`, is an out-of-scope modification. -- [ ] [P4-T2] Run `mcp__drm-copilot__run_poshqc_analyze` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, then re-run the per-file `Invoke-ScriptAnalyzer` breakdown from P0-T15. Write `/evidence/qa-gates/poshqc-analyze..md`. Acceptance: **zero NEW findings** on `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` relative to the P0-T15 baseline — that is, the post-change per-file finding set is a subset of the baseline set. **The comparison is keyed on `(ScriptName, RuleName, Severity, Message)` and excludes `Line`**, because P2-T2 shortens `Get-CoberturaCoverageSummary`'s inner loop and will shift the `PSUseSingularNouns` finding off line 146; a line-number move on an otherwise-identical finding is not a new finding. The single pre-existing `PSUseSingularNouns` finding on `Get-CoberturaLineConditionCoverageParts` is **expected to persist** and its persistence is not a failure; clearing it would require renaming an exported function that `spec.md` marks Unmodified. A zero-findings acceptance is deliberately **not** used here because it is unsatisfiable within scope. The MCP call's non-zero exit is **expected at every invocation** and does not fail this gate: 16 pre-existing findings exist under `scripts/vscode` across six files, of which exactly one is in an in-scope file. Only a finding absent from the P0-T15 baseline fails this gate. -- [ ] [P4-T3] Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, recording its `ok` and `summary` as non-probative, then run the direct Pester capture from § Test Verdict and Coverage Measurement Contract with `OutputPath` = `/evidence/qa-gates/pester-coverage-final..xml`. Write `/evidence/qa-gates/pester-final..md`. Acceptance: the direct run reports `FailedCount` = 0 with `PassedCount` = `TotalCount` = **19 + N**, where **N** is the number of additional `It` blocks added under the P4-T6 remediation path and is **0** on the first pass through this phase; the value of N in force is stated in the artifact. A `TotalCount` of 0, or any `TotalCount` below 19, fails this gate, since zero discovery also yields zero failures. `EXIT_CODE: 0` (`PESTER_EXIT_CODE`); and the four JaCoCo counters plus the derived post-change **line-coverage percentage** for `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` are recorded in `Output Summary:` as concrete integers. No placeholder values. Branch coverage is recorded as tool-unsupported per P4-T6, not as a number. -- [ ] [P4-T4] Confirm a single clean pass. Acceptance: one consecutive execution of P4-T1 -> P4-T2 -> P4-T3 in which format changed 0 files — evidenced by **identical SHA-256 hashes for both in-scope files before and after the P4-T1 invocation** and an empty `git status --porcelain` before/after difference for every other path except `.claude/agent-memory/**` — analyze reported no new findings on the two in-scope files relative to the P0-T15 baseline, and the direct Pester run reported `FailedCount` = 0 with `PassedCount` = `TotalCount` = 19 + N (N as defined in P4-T3; N = 0 unless the P4-T6 remediation path has fired). If any step failed or changed files, restart from P4-T1 and record each attempt as its own artifact. -- [ ] [P4-T5] Run the post-format file-size audit and write `/evidence/qa-gates/file-size-audit..md`. Acceptance: `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` < 500 lines and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` < 500 lines, both counts recorded numerically. +- [x] [P4-T2] Run `mcp__drm-copilot__run_poshqc_analyze` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, then re-run the per-file `Invoke-ScriptAnalyzer` breakdown from P0-T15. Write `/evidence/qa-gates/poshqc-analyze..md`. Acceptance: **zero NEW findings** on `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` relative to the P0-T15 baseline — that is, the post-change per-file finding set is a subset of the baseline set. **The comparison is keyed on `(ScriptName, RuleName, Severity, Message)` and excludes `Line`**, because P2-T2 shortens `Get-CoberturaCoverageSummary`'s inner loop and will shift the `PSUseSingularNouns` finding off line 146; a line-number move on an otherwise-identical finding is not a new finding. The single pre-existing `PSUseSingularNouns` finding on `Get-CoberturaLineConditionCoverageParts` is **expected to persist** and its persistence is not a failure; clearing it would require renaming an exported function that `spec.md` marks Unmodified. A zero-findings acceptance is deliberately **not** used here because it is unsatisfiable within scope. The MCP call's non-zero exit is **expected at every invocation** and does not fail this gate: 16 pre-existing findings exist under `scripts/vscode` across six files, of which exactly one is in an in-scope file. Only a finding absent from the P0-T15 baseline fails this gate. +- [x] [P4-T3] Run `mcp__drm-copilot__run_poshqc_test` with `workspace_root` = `` and `scan_folders` = `["scripts/vscode", "tests/scripts/vscode"]`, recording its `ok` and `summary` as non-probative, then run the direct Pester capture from § Test Verdict and Coverage Measurement Contract with `OutputPath` = `/evidence/qa-gates/pester-coverage-final..xml`. Write `/evidence/qa-gates/pester-final..md`. Acceptance: the direct run reports `FailedCount` = 0 with `PassedCount` = `TotalCount` = **19 + N**, where **N** is the number of additional `It` blocks added under the P4-T6 remediation path and is **0** on the first pass through this phase; the value of N in force is stated in the artifact. A `TotalCount` of 0, or any `TotalCount` below 19, fails this gate, since zero discovery also yields zero failures. `EXIT_CODE: 0` (`PESTER_EXIT_CODE`); and the four JaCoCo counters plus the derived post-change **line-coverage percentage** for `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` are recorded in `Output Summary:` as concrete integers. No placeholder values. Branch coverage is recorded as tool-unsupported per P4-T6, not as a number. +- [x] [P4-T4] Confirm a single clean pass. Acceptance: one consecutive execution of P4-T1 -> P4-T2 -> P4-T3 in which format changed 0 files — evidenced by **identical SHA-256 hashes for both in-scope files before and after the P4-T1 invocation** and an empty `git status --porcelain` before/after difference for every other path except `.claude/agent-memory/**` — analyze reported no new findings on the two in-scope files relative to the P0-T15 baseline, and the direct Pester run reported `FailedCount` = 0 with `PassedCount` = `TotalCount` = 19 + N (N as defined in P4-T3; N = 0 unless the P4-T6 remediation path has fired). If any step failed or changed files, restart from P4-T1 and record each attempt as its own artifact. +- [x] [P4-T5] Run the post-format file-size audit and write `/evidence/qa-gates/file-size-audit..md`. Acceptance: `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` < 500 lines and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` < 500 lines, both counts recorded numerically. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -386,7 +386,7 @@ Get-ChildItem -LiteralPath ` ForEach-Object { '{0}: {1}' -f $_.Name, (Get-Content -LiteralPath $_.FullName).Count } ``` -- [ ] [P4-T6] Write the coverage delta artifact `/evidence/qa-gates/coverage-delta..md` comparing the P0-T16 baseline against the P4-T3 post-change figures for `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. Acceptance, line coverage: the artifact records baseline and post-change `LINE` counters as concrete missed/covered integers plus both derived percentages, and confirms post-change line coverage is **>= 85%** (`.claude/rules/general-unit-test.md`) with **no regression** versus the baseline of 88.48%. Where `CLAUDE.md` and `general-unit-test.md` differ, the stricter figure is recorded; no threshold is modified anywhere. Acceptance, branch coverage: the >= 75% branch floor is **not measurable with the available instrument**, and the artifact must say so as an auditable negative-evidence claim rather than reporting a number or silently omitting the floor. Record `SearchScope:` the Pester JaCoCo report at the P4-T3 `OutputPath`; `SearchPatterns:` `report/counter[@type='BRANCH']`; `SearchResult:` `none`, together with the full list of counter types the report does contain (`INSTRUCTION`, `LINE`, `METHOD`, `CLASS`) and the Pester version. State explicitly that this limitation is a property of the PowerShell coverage tooling, is not caused by this change, and is **not** grounds for altering any threshold — thresholds are owned by #494. Acceptance, new-code coverage: the artifact additionally records the first and last line numbers of `Get-CoberturaClassLineSummary` in the post-change file, the count of JaCoCo `` records in that range with `ci > 0` and with `ci = 0`, and the derived new-code line-coverage percentage, which must be **>= 90%** per `CLAUDE.md` § UT2 and must show no regression on changed lines per `.claude/rules/powershell.md`. `$first` and `$last` must be recorded in the artifact as concrete integers, and a zero-length line set **fails** the gate rather than reporting a percentage. Compute it from the P4-T3 `OutputPath` report with this self-contained snippet — it defines every variable it uses, because shell state does not persist between tool invocations: +- [x] [P4-T6] Write the coverage delta artifact `/evidence/qa-gates/coverage-delta..md` comparing the P0-T16 baseline against the P4-T3 post-change figures for `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. Acceptance, line coverage: the artifact records baseline and post-change `LINE` counters as concrete missed/covered integers plus both derived percentages, and confirms post-change line coverage is **>= 85%** (`.claude/rules/general-unit-test.md`) with **no regression** versus the baseline of 88.48%. Where `CLAUDE.md` and `general-unit-test.md` differ, the stricter figure is recorded; no threshold is modified anywhere. Acceptance, branch coverage: the >= 75% branch floor is **not measurable with the available instrument**, and the artifact must say so as an auditable negative-evidence claim rather than reporting a number or silently omitting the floor. Record `SearchScope:` the Pester JaCoCo report at the P4-T3 `OutputPath`; `SearchPatterns:` `report/counter[@type='BRANCH']`; `SearchResult:` `none`, together with the full list of counter types the report does contain (`INSTRUCTION`, `LINE`, `METHOD`, `CLASS`) and the Pester version. State explicitly that this limitation is a property of the PowerShell coverage tooling, is not caused by this change, and is **not** grounds for altering any threshold — thresholds are owned by #494. Acceptance, new-code coverage: the artifact additionally records the first and last line numbers of `Get-CoberturaClassLineSummary` in the post-change file, the count of JaCoCo `` records in that range with `ci > 0` and with `ci = 0`, and the derived new-code line-coverage percentage, which must be **>= 90%** per `CLAUDE.md` § UT2 and must show no regression on changed lines per `.claude/rules/powershell.md`. `$first` and `$last` must be recorded in the artifact as concrete integers, and a zero-length line set **fails** the gate rather than reporting a percentage. Compute it from the P4-T3 `OutputPath` report with this self-contained snippet — it defines every variable it uses, because shell state does not persist between tool invocations: ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -406,8 +406,8 @@ $cov = @($lines | Where-Object { [int]$_.ci -gt 0 }).Count **Remediation path if a figure is below its floor.** If the post-change whole-file line rate is below 88.48% or the new-code rate is below 90%, add further direct unit tests for the uncovered lines of `Get-CoberturaClassLineSummary` inside the `Describe` created by P3-T3, staying within its 80-line budget, then restart the QA loop from P4-T1. Do **not** lower a threshold, do **not** exclude any line from measurement, and do **not** report the shortfall as a pass. Record the number of `It` blocks added as **N** and carry it into the P4-T3 and P4-T4 acceptance counts on the restarted pass; a resulting count of 19 + N is not a gate failure. -- [ ] [P4-T7] Write `/evidence/other/helper-branch-test-map..md` mapping every branch of `Get-CoberturaClassLineSummary` to the named test that exercises it (new-key insert, repeat-key `max(hits)`, repeat-key branch promotion, precedence `Total` greater, precedence `Total` equal / `Covered` greater, precedence neither, empty class). Acceptance: every listed branch names at least one `It` block. This artifact is a **scenario-completeness map, not a coverage measurement**; the numeric >= 90% new-code proof is carried by P4-T6. Do not claim that an enumeration of branches satisfies a coverage threshold. -- [ ] [P4-T8] Run the scope-lock diff gate and write `/evidence/qa-gates/scope-lock..md`. Acceptance: `git diff --name-only edf3d34c -- scripts tests` lists **exactly** `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` and nothing else. The gate is scoped to `scripts` and `tests` deliberately: `docs/` and `.claude/agent-memory/` are tracked and legitimately change during this work. +- [x] [P4-T7] Write `/evidence/other/helper-branch-test-map..md` mapping every branch of `Get-CoberturaClassLineSummary` to the named test that exercises it (new-key insert, repeat-key `max(hits)`, repeat-key branch promotion, precedence `Total` greater, precedence `Total` equal / `Covered` greater, precedence neither, empty class). Acceptance: every listed branch names at least one `It` block. This artifact is a **scenario-completeness map, not a coverage measurement**; the numeric >= 90% new-code proof is carried by P4-T6. Do not claim that an enumeration of branches satisfies a coverage threshold. +- [x] [P4-T8] Run the scope-lock diff gate and write `/evidence/qa-gates/scope-lock..md`. Acceptance: `git diff --name-only edf3d34c -- scripts tests` lists **exactly** `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` and nothing else. The gate is scoped to `scripts` and `tests` deliberately: `docs/` and `.claude/agent-memory/` are tracked and legitimately change during this work. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -415,7 +415,7 @@ Set-Location $root git diff --name-only edf3d34c -- scripts tests ``` -- [ ] [P4-T9] Run the no-threshold-change gate and write `/evidence/qa-gates/threshold-no-change..md`. Acceptance: `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` returns empty output; **and** every added line in the two changed source files that contains the token `85`, `90` or `75` is enumerated in the artifact with a one-line justification. The gate fails if any added line expresses a coverage threshold rather than a fixture line number, a `hits` value or a count. The judgment is bounded to the enumerated matches, so the check is mechanically reproducible. +- [x] [P4-T9] Run the no-threshold-change gate and write `/evidence/qa-gates/threshold-no-change..md`. Acceptance: `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` returns empty output; **and** every added line in the two changed source files that contains the token `85`, `90` or `75` is enumerated in the artifact with a one-line justification. The gate fails if any added line expresses a coverage threshold rather than a fixture line number, a `hits` value or a count. The judgment is bounded to the enumerated matches, so the check is mechanically reproducible. ```powershell $root = (git rev-parse --show-toplevel) -replace '/', '\' @@ -425,17 +425,17 @@ git diff HEAD -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 tests/scri Select-String '^\+' | Select-String -Pattern '\b(85|90|75)\b' ``` -- [ ] [P4-T10] Re-verify union-builder byte identity **after** formatting and append the result to `/evidence/qa-gates/union-builder-byte-identity..md`. Acceptance: `git diff HEAD -U0 -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` still shows no hunk whose old-side range intersects pre-change lines 217-268. The `HEAD` operand makes the check immune to staging. If the formatter reflowed any line in that range, restore those lines to their pre-change bytes and restart from P4-T1. +- [x] [P4-T10] Re-verify union-builder byte identity **after** formatting and append the result to `/evidence/qa-gates/union-builder-byte-identity..md`. Acceptance: `git diff HEAD -U0 -- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` still shows no hunk whose old-side range intersects pre-change lines 217-268. The `HEAD` operand makes the check immune to staging. If the formatter reflowed any line in that range, restore those lines to their pre-change bytes and restart from P4-T1. ### Phase 5 — Post-Change Evidence and Oracle Verification If any figure in P5-T1 or P5-T2 does not match its required value, return to Phase 2, correct the implementation, and re-execute Phases 3, 4 and 5 in full. -- [ ] [P5-T1] Re-run the generator-parity A/B command from P0-T11 against the fixed `Helpers.ps1` and write `/evidence/qa-gates/postchange-generator-parity..md`. Acceptance: the artifact records `LinesValid = 79957`, `LinesCovered = 56124`, `BranchesValid = 23109`, `BranchesCovered = 13472` exactly, reproducing the input document's own root attributes. -- [ ] [P5-T2] Re-run the package-filtered A/B command from P0-T12 against the fixed `Helpers.ps1` and write `/evidence/qa-gates/postchange-package-filtered..md`. Acceptance: the artifact records `lines-valid = 62345`, `lines-covered = 53013` and `line-rate = 0.850317`, alongside the pre-change values 110849 / 94937 / 0.856453. -- [ ] [P5-T3] Write the consolidated A/B delta artifact `/evidence/qa-gates/coverage-arithmetic-delta..md`. Acceptance: the artifact tabulates pre-change versus post-change for both experiments using the concrete integers captured in P0-T11, P0-T12, P5-T1 and P5-T2, and states that each pre-change figure is strictly greater than its post-change counterpart. -- [ ] [P5-T4] Write the threshold handoff record `/evidence/other/threshold-handoff-494..md`. Acceptance: the artifact states as fact that the corrected repository-wide line rate for the #424 committed sample is 85.0317% against the uniform 85% line floor in `.claude/rules/general-unit-test.md` — a margin of 0.03 percentage points — identifies child feature #494 as the owner of threshold reconciliation, and states explicitly that this feature proposes and makes no threshold change. -- [ ] [P5-T5] Audit evidence locations and schema for the artifacts that exist at this point. Acceptance: every artifact produced by **Phases 0 through 5** resides under `/evidence/{baseline,regression-testing,qa-gates,issue-updates,other}/`, each command-step artifact carries `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`, and a repository search confirms no artifact was written under any `artifacts/` path. Record the audit in `/evidence/other/evidence-location-audit..md`. This sweep deliberately excludes the Phase 6 and Phase 7 artifacts, which do not exist yet; they are covered by the final sweep in P7-T20, and AC-16 is certified against that final sweep, not against this one. +- [x] [P5-T1] Re-run the generator-parity A/B command from P0-T11 against the fixed `Helpers.ps1` and write `/evidence/qa-gates/postchange-generator-parity..md`. Acceptance: the artifact records `LinesValid = 79957`, `LinesCovered = 56124`, `BranchesValid = 23109`, `BranchesCovered = 13472` exactly, reproducing the input document's own root attributes. +- [x] [P5-T2] Re-run the package-filtered A/B command from P0-T12 against the fixed `Helpers.ps1` and write `/evidence/qa-gates/postchange-package-filtered..md`. Acceptance: the artifact records `lines-valid = 62345`, `lines-covered = 53013` and `line-rate = 0.850317`, alongside the pre-change values 110849 / 94937 / 0.856453. +- [x] [P5-T3] Write the consolidated A/B delta artifact `/evidence/qa-gates/coverage-arithmetic-delta..md`. Acceptance: the artifact tabulates pre-change versus post-change for both experiments using the concrete integers captured in P0-T11, P0-T12, P5-T1 and P5-T2, and states that each pre-change figure is strictly greater than its post-change counterpart. +- [x] [P5-T4] Write the threshold handoff record `/evidence/other/threshold-handoff-494..md`. Acceptance: the artifact states as fact that the corrected repository-wide line rate for the #424 committed sample is 85.0317% against the uniform 85% line floor in `.claude/rules/general-unit-test.md` — a margin of 0.03 percentage points — identifies child feature #494 as the owner of threshold reconciliation, and states explicitly that this feature proposes and makes no threshold change. +- [x] [P5-T5] Audit evidence locations and schema for the artifacts that exist at this point. Acceptance: every artifact produced by **Phases 0 through 5** resides under `/evidence/{baseline,regression-testing,qa-gates,issue-updates,other}/`, each command-step artifact carries `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`, and a repository search confirms no artifact was written under any `artifacts/` path. Record the audit in `/evidence/other/evidence-location-audit..md`. This sweep deliberately excludes the Phase 6 and Phase 7 artifacts, which do not exist yet; they are covered by the final sweep in P7-T20, and AC-16 is certified against that final sweep, not against this one. ### Phase 6 — Follow-Up Issue Filing @@ -443,37 +443,37 @@ Each follow-up is filed through the MCP promotion lifecycle (`mcp__drm-copilot__ **Availability branch (applies to P6-T1 through P6-T4).** If the promotion tool reports that `gh` is unavailable or unauthenticated, **or the promotion MCP tools are not exposed in the executing session at all**, **do not fabricate an issue number.** Record the prepared title and body verbatim in the P6-T5 artifact under a `POSTING BLOCKED` header together with the tool's exact error text, leave AC-20 unchecked, and report the blockage in the completion summary. This is the only sanctioned non-numeric outcome for Phase 6. Under this branch P6-T1..P6-T4 **are** checked off, on the basis of the recorded `POSTING BLOCKED` entry in the P6-T5 artifact — the task's obligation is to attempt the filing and record the outcome truthfully. Only AC-20 remains unchecked, because only AC-20 asserts that issue numbers exist. -- [ ] [P6-T1] File follow-up candidate 1: package-level `line-rate` / `branch-rate` are never recomputed after package filtering and class merging in `ConvertTo-KoverageCoberturaXml`, leaving stale values consumed by `scripts/temp-extract-coverage.ps1:47`. Acceptance: a GitHub issue number is returned and recorded. -- [ ] [P6-T2] File follow-up candidate 2: a merged Cobertura class retains only the primary class's ``, so the emitted document's methods do not account for all of its class-level lines; merging carries a duplicate `(name, signature)` hazard on compiler-generated sibling classes. Acceptance: a GitHub issue number is returned and recorded. -- [ ] [P6-T3] File follow-up candidate 3: `scripts/vscode/Invoke-MSTestWithCoverage.ps1:296-302` lacks a `\.claude\` discovery exclusion, so `-SearchRoot .` descends into `.claude\worktrees\agent-*\**` and picks up stale sibling-worktree assemblies. Acceptance: a GitHub issue number is returned and recorded. -- [ ] [P6-T4] File follow-up candidate 4: `.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md:34-36` records an incorrect generalization — root attributes are deduped only in raw `dotnet-coverage` output, not in post-processed `ConvertTo-KoverageCoberturaXml` artifacts. Acceptance: a GitHub issue number is returned and recorded. -- [ ] [P6-T5] Write `/evidence/issue-updates/followups-441..md` recording each candidate's title, `PostedAs:`, and either its issue number and GitHub URL or a `POSTING BLOCKED` header. Acceptance: the artifact lists exactly four entries, one per candidate, each carrying **either** an issue number and URL **or** a `POSTING BLOCKED` header with the blocking error and the prepared body verbatim. -- [ ] [P6-T6] Confirm none of the four follow-ups was fixed in this change. Acceptance: the P4-T8 scope-lock output still lists exactly two source files, and `git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1 scripts/temp-extract-coverage.ps1` returns empty output. +- [x] [P6-T1] File follow-up candidate 1: package-level `line-rate` / `branch-rate` are never recomputed after package filtering and class merging in `ConvertTo-KoverageCoberturaXml`, leaving stale values consumed by `scripts/temp-extract-coverage.ps1:47`. Acceptance: a GitHub issue number is returned and recorded. +- [x] [P6-T2] File follow-up candidate 2: a merged Cobertura class retains only the primary class's ``, so the emitted document's methods do not account for all of its class-level lines; merging carries a duplicate `(name, signature)` hazard on compiler-generated sibling classes. Acceptance: a GitHub issue number is returned and recorded. +- [x] [P6-T3] File follow-up candidate 3: `scripts/vscode/Invoke-MSTestWithCoverage.ps1:296-302` lacks a `\.claude\` discovery exclusion, so `-SearchRoot .` descends into `.claude\worktrees\agent-*\**` and picks up stale sibling-worktree assemblies. Acceptance: a GitHub issue number is returned and recorded. +- [x] [P6-T4] File follow-up candidate 4: `.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md:34-36` records an incorrect generalization — root attributes are deduped only in raw `dotnet-coverage` output, not in post-processed `ConvertTo-KoverageCoberturaXml` artifacts. Acceptance: a GitHub issue number is returned and recorded. +- [x] [P6-T5] Write `/evidence/issue-updates/followups-441..md` recording each candidate's title, `PostedAs:`, and either its issue number and GitHub URL or a `POSTING BLOCKED` header. Acceptance: the artifact lists exactly four entries, one per candidate, each carrying **either** an issue number and URL **or** a `POSTING BLOCKED` header with the blocking error and the prepared body verbatim. +- [x] [P6-T6] Confirm none of the four follow-ups was fixed in this change. Acceptance: the P4-T8 scope-lock output still lists exactly two source files, and `git diff --name-only edf3d34c -- scripts/vscode/Invoke-MSTestWithCoverage.ps1 scripts/temp-extract-coverage.ps1` returns empty output. ### Phase 7 — Acceptance Criteria Check-Off and Commit AC source is `/spec.md` § Acceptance Criteria (work mode `full-bug`). Per `.claude/skills/acceptance-criteria-tracking/SKILL.md`, change only `- [ ]` to `- [x]` and never alter criterion text. One AC per task, each citing its own evidence pointer. -- [ ] [P7-T1] Check off **AC-1** (generator parity) in `/spec.md`, citing `/evidence/qa-gates/postchange-generator-parity..md`. Acceptance: AC-1 is `[x]` and the cited artifact shows 79957 / 56124 / 23109 / 13472. -- [ ] [P7-T2] Check off **AC-2** (pre-change figure) in `/spec.md`, citing `/evidence/baseline/prechange-generator-parity..md`. Acceptance: AC-2 is `[x]` and the cited artifact shows `LinesValid = 161086` plus three concrete integers each strictly greater than its AC-1 counterpart. -- [ ] [P7-T3] Check off **AC-3** (package-filtered A/B) in `/spec.md`, citing `/evidence/qa-gates/postchange-package-filtered..md`. Acceptance: AC-3 is `[x]` and the cited artifact shows 62345 / 53013 / 0.850317 against 110849 / 94937 / 0.856453. -- [ ] [P7-T4] Check off **AC-4** (per-file merged rate) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-4 is `[x]` and F3 passes with `line-rate` = `'0.6'` and five ascending line children 12, 13, 56, 57, 58. -- [ ] [P7-T5] Check off **AC-5** (branch counts deduplicated) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-5 is `[x]`, F2 asserts `branches-valid` = `'2'` and `branches-covered` = `'1'`, and no branch assertion in the suite relies on `branch-rate` alone. -- [ ] [P7-T6] Check off **AC-6** (helper contract) in `/spec.md`, citing `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `/evidence/other/helper-branch-test-map..md`. Acceptance: AC-6 is `[x]` and the helper matches the stated signature, enumeration order, key rule, precedence rule and five returned properties. -- [ ] [P7-T7] Check off **AC-7** (defect removed at its one site) in `/spec.md`, citing the P2-T4 grep results. Acceptance: AC-7 is `[x]` and `.//lines/line` returns 0 matches in the production file. -- [ ] [P7-T8] Check off **AC-8** (correct site untouched) in `/spec.md`, citing `/evidence/qa-gates/union-builder-byte-identity..md`. Acceptance: AC-8 is `[x]` and the artifact records the post-format re-verification from P4-T10. -- [ ] [P7-T9] Check off **AC-9** (delegation replaced) in `/spec.md`, citing the P2-T4 grep for `$classSummaryXml`. Acceptance: AC-9 is `[x]` and the token returns 0 matches. -- [ ] [P7-T10] Check off **AC-10** (structure preserved) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-10 is `[x]` and F6 passes. -- [ ] [P7-T11] Check off **AC-11** (six fixtures present and passing) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-11 is `[x]`, all six fixtures pass, and none creates a file on disk or mocks an arithmetic path. -- [ ] [P7-T12] Check off **AC-12** (fail-before evidence) in `/spec.md`, citing `/evidence/regression-testing/fail-before-f1-f4..md`. Acceptance: AC-12 is `[x]` and the artifact records F1 6/4, F2 4/2, F3 `'0.75'`, F4 3/2 against unmodified `Helpers.ps1`. -- [ ] [P7-T13] Check off **AC-13** (helper precedence branches covered) in `/spec.md`, citing `/evidence/regression-testing/helper-unit-tests..md`. Acceptance: AC-13 is `[x]` and all three precedence-branch tests pass. -- [ ] [P7-T14] Check off **AC-14** (zero existing tests broken) in `/spec.md`, citing `/evidence/baseline/pester-baseline..md`, `/evidence/qa-gates/pester-final..md` and `/evidence/regression-testing/pass-after-f1-f6..md` (which carries the 0-deletions numstat record written by P3-T2). Acceptance: AC-14 is `[x]`, all eight pre-existing blocks pass in both runs, and the test-file diff shows 0 deletions. -- [ ] [P7-T15] Check off **AC-15** (toolchain green) in `/spec.md`, citing the three P4 artifacts `poshqc-format..md`, `poshqc-analyze..md` and `pester-final..md`. Acceptance: AC-15 is `[x]` and the artifacts record a single clean pass. -- [ ] [P7-T16] Check off **AC-17** (no threshold re-tuned) in `/spec.md`, citing `/evidence/qa-gates/threshold-no-change..md` and `/evidence/other/threshold-handoff-494..md`. Acceptance: AC-17 is `[x]` and the diff gate returned empty output. -- [ ] [P7-T17] Check off **AC-18** (scope boundary held) in `/spec.md`, citing `/evidence/qa-gates/scope-lock..md`. Acceptance: AC-18 is `[x]` and the gate lists exactly the two in-scope source files. -- [ ] [P7-T18] Check off **AC-19** (file ceiling) in `/spec.md`, citing `/evidence/qa-gates/file-size-audit..md`. Acceptance: AC-19 is `[x]` and `Invoke-MSTestWithCoverage.Helpers.ps1` is recorded under 500 lines. -- [ ] [P7-T19] Check off **AC-20** (follow-ups filed) in `/spec.md`, citing `/evidence/issue-updates/followups-441..md`. Acceptance: AC-20 is `[x]` **only if** four issue numbers are recorded, none of them fixed in this change. If any candidate carries a `POSTING BLOCKED` header instead of an issue number, **leave AC-20 unchecked** and record the reason in the P7-T22 summary; do not check off a criterion the evidence does not support. -- [ ] [P7-T20] Re-run the evidence-location and schema audit over the **full** artifact set, including the Phase 6 and Phase 7 artifacts that did not exist at P5-T5, and append the result to `/evidence/other/evidence-location-audit..md` under a `## Final sweep` heading, reusing the P5-T5 timestamp so a single audit artifact carries both sweeps. Acceptance: the appended section enumerates every file under `/evidence/` by path, confirms each command-step artifact carries `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`, and records `SearchScope:`, `SearchPatterns:` `artifacts/**` and `SearchResult:` `none` for the forbidden-path search. This task runs **before** the AC-16 check-off so that the criterion is certified against evidence that already exists. Note in the appended section that exactly one artifact is written after this sweep — `/evidence/other/ac-status-summary..md` from P7-T22 — and state its intended path so the audit trail is complete; it is not a command-step artifact and carries no `EXIT_CODE:` field. -- [ ] [P7-T21] Check off **AC-16** (canonical evidence locations) in `/spec.md`, citing the `## Final sweep` section of `/evidence/other/evidence-location-audit..md` written by P7-T20. Acceptance: AC-16 is `[x]` and the final sweep shows no `artifacts/` evidence path and complete schema fields across the full artifact set including the Phase 6 and Phase 7 artifacts. -- [ ] [P7-T22] Write the AC status summary to `/evidence/other/ac-status-summary..md` in the format required by `.claude/skills/acceptance-criteria-tracking/SKILL.md` (Source, Total AC items, Checked off, Remaining, Items remaining). Acceptance: the artifact reports Source = `/spec.md`, Total = 20, and lists any unchecked item explicitly. -- [ ] [P7-T23] Commit all changes on branch `bug/cobertura-coverage-arithmetic-441` with the message `fix(coverage): dedupe Cobertura line and branch arithmetic (#441, #478)`. Acceptance: `git status --porcelain` returns empty output after the commit, and `git show --stat HEAD` lists the two source files plus the feature documents and evidence artifacts. `.claude/agent-memory/**` is tracked in this repository and is legitimately written by the executing agent during the run; commit any such changes as a **separate commit before** the feature commit, so that `git show --stat HEAD` lists only the two source files, the feature documents and the evidence artifacts. No PR is created and no CI run is triggered from this plan. **Expected residual dirt:** writing the `[x]` for this task into the plan file necessarily modifies the plan after the commit it verifies, so the worktree ends with exactly one modified file — this plan. That is the only permitted residual; `epic-orchestrator` amends the feature commit or follows it with a plan-checklist commit. Any other residual modification is a defect. +- [x] [P7-T1] Check off **AC-1** (generator parity) in `/spec.md`, citing `/evidence/qa-gates/postchange-generator-parity..md`. Acceptance: AC-1 is `[x]` and the cited artifact shows 79957 / 56124 / 23109 / 13472. +- [x] [P7-T2] Check off **AC-2** (pre-change figure) in `/spec.md`, citing `/evidence/baseline/prechange-generator-parity..md`. Acceptance: AC-2 is `[x]` and the cited artifact shows `LinesValid = 161086` plus three concrete integers each strictly greater than its AC-1 counterpart. +- [x] [P7-T3] Check off **AC-3** (package-filtered A/B) in `/spec.md`, citing `/evidence/qa-gates/postchange-package-filtered..md`. Acceptance: AC-3 is `[x]` and the cited artifact shows 62345 / 53013 / 0.850317 against 110849 / 94937 / 0.856453. +- [x] [P7-T4] Check off **AC-4** (per-file merged rate) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-4 is `[x]` and F3 passes with `line-rate` = `'0.6'` and five ascending line children 12, 13, 56, 57, 58. +- [x] [P7-T5] Check off **AC-5** (branch counts deduplicated) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-5 is `[x]`, F2 asserts `branches-valid` = `'2'` and `branches-covered` = `'1'`, and no branch assertion in the suite relies on `branch-rate` alone. +- [x] [P7-T6] Check off **AC-6** (helper contract) in `/spec.md`, citing `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `/evidence/other/helper-branch-test-map..md`. Acceptance: AC-6 is `[x]` and the helper matches the stated signature, enumeration order, key rule, precedence rule and five returned properties. +- [x] [P7-T7] Check off **AC-7** (defect removed at its one site) in `/spec.md`, citing the P2-T4 grep results. Acceptance: AC-7 is `[x]` and `.//lines/line` returns 0 matches in the production file. +- [x] [P7-T8] Check off **AC-8** (correct site untouched) in `/spec.md`, citing `/evidence/qa-gates/union-builder-byte-identity..md`. Acceptance: AC-8 is `[x]` and the artifact records the post-format re-verification from P4-T10. +- [x] [P7-T9] Check off **AC-9** (delegation replaced) in `/spec.md`, citing the P2-T4 grep for `$classSummaryXml`. Acceptance: AC-9 is `[x]` and the token returns 0 matches. +- [x] [P7-T10] Check off **AC-10** (structure preserved) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-10 is `[x]` and F6 passes. +- [x] [P7-T11] Check off **AC-11** (six fixtures present and passing) in `/spec.md`, citing `/evidence/regression-testing/pass-after-f1-f6..md`. Acceptance: AC-11 is `[x]`, all six fixtures pass, and none creates a file on disk or mocks an arithmetic path. +- [x] [P7-T12] Check off **AC-12** (fail-before evidence) in `/spec.md`, citing `/evidence/regression-testing/fail-before-f1-f4..md`. Acceptance: AC-12 is `[x]` and the artifact records F1 6/4, F2 4/2, F3 `'0.75'`, F4 3/2 against unmodified `Helpers.ps1`. +- [x] [P7-T13] Check off **AC-13** (helper precedence branches covered) in `/spec.md`, citing `/evidence/regression-testing/helper-unit-tests..md`. Acceptance: AC-13 is `[x]` and all three precedence-branch tests pass. +- [x] [P7-T14] Check off **AC-14** (zero existing tests broken) in `/spec.md`, citing `/evidence/baseline/pester-baseline..md`, `/evidence/qa-gates/pester-final..md` and `/evidence/regression-testing/pass-after-f1-f6..md` (which carries the 0-deletions numstat record written by P3-T2). Acceptance: AC-14 is `[x]`, all eight pre-existing blocks pass in both runs, and the test-file diff shows 0 deletions. +- [x] [P7-T15] Check off **AC-15** (toolchain green) in `/spec.md`, citing the three P4 artifacts `poshqc-format..md`, `poshqc-analyze..md` and `pester-final..md`. Acceptance: AC-15 is `[x]` and the artifacts record a single clean pass. +- [x] [P7-T16] Check off **AC-17** (no threshold re-tuned) in `/spec.md`, citing `/evidence/qa-gates/threshold-no-change..md` and `/evidence/other/threshold-handoff-494..md`. Acceptance: AC-17 is `[x]` and the diff gate returned empty output. +- [x] [P7-T17] Check off **AC-18** (scope boundary held) in `/spec.md`, citing `/evidence/qa-gates/scope-lock..md`. Acceptance: AC-18 is `[x]` and the gate lists exactly the two in-scope source files. +- [x] [P7-T18] Check off **AC-19** (file ceiling) in `/spec.md`, citing `/evidence/qa-gates/file-size-audit..md`. Acceptance: AC-19 is `[x]` and `Invoke-MSTestWithCoverage.Helpers.ps1` is recorded under 500 lines. +- [x] [P7-T19] Check off **AC-20** (follow-ups filed) in `/spec.md`, citing `/evidence/issue-updates/followups-441..md`. Acceptance: AC-20 is `[x]` **only if** four issue numbers are recorded, none of them fixed in this change. If any candidate carries a `POSTING BLOCKED` header instead of an issue number, **leave AC-20 unchecked** and record the reason in the P7-T22 summary; do not check off a criterion the evidence does not support. +- [x] [P7-T20] Re-run the evidence-location and schema audit over the **full** artifact set, including the Phase 6 and Phase 7 artifacts that did not exist at P5-T5, and append the result to `/evidence/other/evidence-location-audit..md` under a `## Final sweep` heading, reusing the P5-T5 timestamp so a single audit artifact carries both sweeps. Acceptance: the appended section enumerates every file under `/evidence/` by path, confirms each command-step artifact carries `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`, and records `SearchScope:`, `SearchPatterns:` `artifacts/**` and `SearchResult:` `none` for the forbidden-path search. This task runs **before** the AC-16 check-off so that the criterion is certified against evidence that already exists. Note in the appended section that exactly one artifact is written after this sweep — `/evidence/other/ac-status-summary..md` from P7-T22 — and state its intended path so the audit trail is complete; it is not a command-step artifact and carries no `EXIT_CODE:` field. +- [x] [P7-T21] Check off **AC-16** (canonical evidence locations) in `/spec.md`, citing the `## Final sweep` section of `/evidence/other/evidence-location-audit..md` written by P7-T20. Acceptance: AC-16 is `[x]` and the final sweep shows no `artifacts/` evidence path and complete schema fields across the full artifact set including the Phase 6 and Phase 7 artifacts. +- [x] [P7-T22] Write the AC status summary to `/evidence/other/ac-status-summary..md` in the format required by `.claude/skills/acceptance-criteria-tracking/SKILL.md` (Source, Total AC items, Checked off, Remaining, Items remaining). Acceptance: the artifact reports Source = `/spec.md`, Total = 20, and lists any unchecked item explicitly. +- [x] [P7-T23] Commit all changes on branch `bug/cobertura-coverage-arithmetic-441` with the message `fix(coverage): dedupe Cobertura line and branch arithmetic (#441, #478)`. Acceptance: `git status --porcelain` returns empty output after the commit, and `git show --stat HEAD` lists the two source files plus the feature documents and evidence artifacts. `.claude/agent-memory/**` is tracked in this repository and is legitimately written by the executing agent during the run; commit any such changes as a **separate commit before** the feature commit, so that `git show --stat HEAD` lists only the two source files, the feature documents and the evidence artifacts. No PR is created and no CI run is triggered from this plan. **Expected residual dirt:** writing the `[x]` for this task into the plan file necessarily modifies the plan after the commit it verifies, so the worktree ends with exactly one modified file — this plan. That is the only permitted residual; `epic-orchestrator` amends the feature commit or follows it with a plan-checklist commit. Any other residual modification is a defect. diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/policy-audit.2026-08-10T23-35.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/policy-audit.2026-08-10T23-35.md new file mode 100644 index 000000000..5559b859c --- /dev/null +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/policy-audit.2026-08-10T23-35.md @@ -0,0 +1,162 @@ +# Policy Compliance Audit — 2026-08-10-cobertura-coverage-arithmetic-441 + +- **Timestamp:** 2026-08-10T23-35 +- **Reviewer:** feature-review agent +- **Branch under review:** `bug/cobertura-coverage-arithmetic-441` +- **Head:** `3b8d43fb90a3be7dc0bc9e5624509e473a56ca80` +- **Diff base:** `edf3d34cb9cd455bd3c1d9f5ee363b825632073c` (recorded pre-change baseline; verified an ancestor of HEAD via `git merge-base HEAD edf3d34c` = `edf3d34c`) +- **Work mode:** `full-bug` (persisted marker in `issue.md`; AC source is `spec.md` only) +- **Applicable policies:** `CLAUDE.md`, `.claude/rules/general-code-change.md`, `.claude/rules/general-unit-test.md`, `.claude/rules/powershell.md` +- **Overall verdict:** PASS — 0 blocking findings, 5 non-blocking findings + +## Executive Summary + +The branch fixes two compounding Cobertura arithmetic defects (#441 descendant-axis double count, #478 blended merge denominator) in `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, with regression tests in `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`. The full diff against `edf3d34c` spans 118 files, but exactly two are source files; the remainder are feature documents, evidence, epic-preparation documents for sibling features (already-merged integration-branch content inside the diff window), promoted potential entries, and agent-memory markdown. Every policy gate examined passes. This reviewer independently reproduced the headline correctness oracle, the pre-change defective figures, the package-filtered A/B figures, the fail-before demonstration for F1-F4, the byte-identity of the protected union builder, the analyzer no-new-findings gate, and the 19/19 test result. Five non-blocking findings are recorded (two Minor, three Informational); none requires remediation before merge. + +## Scope and Baseline + +- Diff command adjudicated: `git diff edf3d34c..HEAD` (full branch diff; 118 files, +17251/-34). +- Source files changed (full list after excluding `docs/**` and `.claude/agent-memory/**`): + 1. `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` (+132/-34 net; 357 -> 455 lines) + 2. `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` (+246/-0; 222 -> 468 lines) +- Verified independently: no changes under `.github/workflows/**`, no changes to any hook, skill, or policy document, no `.ts`/`.tsx`/`.py`/`.cs` files changed anywhere in the diff. +- The caller-supplied base `edf3d34c` was accepted after verifying it is an ancestor of HEAD; the epic-preparation commits between the base and the two feature commits (`b52874d6`, `3b8d43fb`) are documentation-only and were included in the adjudicated diff rather than excluded. +- PR context artifacts were absent at review start; the `collect_pr_context` MCP tool is not exposed in this session, so `artifacts/pr_context.summary.txt` and `artifacts/pr_context.appendix.txt` were hand-authored by this reviewer from `git diff edf3d34c..HEAD --numstat` in the canonical locations per `pr-context-artifacts`. + +No scope narrowing was attempted by the caller. The caller directive restricts the toolchain to PowerShell, which matches the observed fact that the branch diff contains PowerShell source changes only, and it explicitly instructs full-diff adjudication. + +## 1. Toolchain Gates (PowerShell: format -> analyze -> test) + +### 1.1 Formatting (PoshQC / Invoke-Formatter) + +- **Verdict: PASS.** +- Evidence: `evidence/qa-gates/poshqc-format.2026-08-10T23-10.md` records a clean final pass — SHA-256 content hashes of both in-scope files identical before and after the formatter, and `git status --porcelain` listings identical before and after (no out-of-scope file touched). Baseline artifact `evidence/baseline/poshqc-format.2026-08-10T22-30.md` recorded zero pre-existing drift. +- The dual-instrument approach (content hash + porcelain diff) is sound: it closes the gap where an already-modified file would not change its porcelain line when rewritten. + +### 1.2 Linting (PSScriptAnalyzer) + +- **Verdict: PASS (no-new-findings gate per spec Amendment 1).** +- Independent re-run by this reviewer at HEAD: `Invoke-ScriptAnalyzer` returns exactly **1 finding** on `Invoke-MSTestWithCoverage.Helpers.ps1` — `PSUseSingularNouns` / Warning / line 140 / `Get-CoberturaLineConditionCoverageParts` — and **0 findings** on the test file. +- This is the single baselined pre-existing finding recorded in `evidence/baseline/poshqc-analyze.2026-08-10T22-30.md`, keyed on `(ScriptName, RuleName, Severity, Message)` with `Line` as observation only. The move from line 146 to 140 equals the 6-line contraction from the P2-T2 loop replacement and is not a new finding. Per the accepted spec amendment, clearing it would require renaming an exported function the spec marks Unmodified; it is correctly left in place. Zero new analyzer findings introduced. +- The new function `Get-CoberturaClassLineSummary` uses an approved verb and a singular noun and introduces no analyzer debt. + +### 1.3 Type checking + +- Skipped by rule: `.claude/rules/powershell.md` § Toolchain step 3 states type checking is skipped for PowerShell. The `CLAUDE.md` C# `/p:Nullable=enable` command is not a gate for this change (zero C# files in the diff; the command itself is the known defect tracked as issue #522 and was directed not to be raised as a finding). + +### 1.4 Testing (Pester v5) + +- **Verdict: PASS.** +- Independent re-run by this reviewer at HEAD: `Invoke-Pester` on `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` — **Total=19 Passed=19 Failed=0 Skipped=0**. Matches `evidence/qa-gates/pester-final.2026-08-10T23-10.md`. +- The MCP `run_poshqc_test` payloads are recorded in evidence as non-probative (the tool returns no counts); all probative figures come from direct `Invoke-Pester` runs. That distinction is documented in each test-gate artifact. + +## 2. Coverage Verification (PowerShell) + +Changed languages in the branch diff: **PowerShell only** (confirmed from `artifacts/pr_context.summary.txt` and independently from `git diff --name-only`). TypeScript, Python, and C# each have zero changed files in the branch diff, so no coverage verdict is required for them. + +Floors applied per `.claude/rules/general-unit-test.md` and `.claude/rules/quality-tiers.md`: line >= 85%, branch >= 75%, new code >= 90% (`CLAUDE.md` UT2), no regression on changed lines. + +| # | Scope | Figure | Floor | Verdict | +| --- | --- | --- | --- | --- | +| 2.1 | PowerShell changed production file line coverage, `Invoke-MSTestWithCoverage.Helpers.ps1` (committed Pester JaCoCo `evidence/qa-gates/pester-coverage-final.2026-08-10T23-10.xml`, re-summed by this reviewer) | 183/202 = **90.59%** | >= 85% | **PASS** | +| 2.2 | PowerShell new-code line coverage, `Get-CoberturaClassLineSummary` lines 161-259 (same artifact, re-summed) | 39/40 = **97.50%** | >= 90% | **PASS** | +| 2.3 | PowerShell changed-line regression check: baseline 146/165 = 88.48% (`evidence/baseline/pester-coverage-baseline.2026-08-10T22-30.xml`, re-summed) -> 183/202 = 90.59%; the single uncovered statement (line 220) is new code that never existed in the baseline denominator, so no previously covered changed line lost coverage | +2.11 pp | no regression | **PASS** | +| 2.4 | PowerShell branch-behavior coverage: Pester's JaCoCo writer emits zero BRANCH counters, so no numeric branch percentage exists in any artifact; branch adequacy is adjudicated from direct tests — all three `condition-coverage` precedence branches plus the zero-input boundary each have a dedicated passing unit test (`evidence/other/helper-branch-test-map.2026-08-10T23-10.md`, corroborated by reading the four `It` blocks at tests file lines 402-467) | 3/3 precedence branches + boundary tested | >= 75% | **PASS** (with one untested statement-level branch recorded as finding NF-1) | +| 2.5 | PowerShell repo-wide line coverage read from the canonical artifact `artifacts/pester/powershell-coverage.xml` | 0/16075 lines covered = **0.00%** | >= 85% | **FAIL** — invalid capture; see disposition below | + +### Disposition of row 2.5 (FAIL, non-blocking) + +The canonical `artifacts/pester/powershell-coverage.xml` (written 2026-08-10 22:58 by the bundled `mcp__drm-copilot__run_poshqc_test` tool; gitignored producer output, never committed) records **zero covered lines for every file in the repository**, including `Invoke-MSTestWithCoverage.Helpers.ps1`, which the committed direct-Pester JaCoCo evidence for this same head demonstrates at 90.59%. A reading of literal zero for a file with a passing 19-test suite exercising it is a measurement defect of the bundled tool's coverage capture, not a property of this branch: the diff contains no change to any coverage-capture tooling, hook, or configuration. The FAIL verdict on row 2.5 is therefore recorded honestly against the artifact as it exists, and is dispositioned **non-blocking** for this feature on the following basis: + +1. The probative, committed coverage evidence (rows 2.1-2.3) adjudicates every floor relevant to the changed code and passes each one; this reviewer re-summed all three XML artifacts independently rather than trusting the recorded prose. +2. The zero-reading capture defect pre-exists this branch and sits in shared MCP tooling whose fidelity is the subject of this very epic (`build-ci-coverage-gate-fidelity`); fixing producer tooling inside this bugfix would violate the minimal-fix mandate of `CLAUDE.md` § Bugfix Workflow step 2 and the feature's own scope boundary. +3. The executor's dated CORRECTION block in `evidence/baseline/poshqc-tool-surface.2026-08-10T22-30.md` already documents that these `artifacts/pester/` files are producer output invisible to `git status --porcelain`, and that no feature evidence is read from or written to them. + +**Recommended follow-up (non-blocking):** file a tooling issue through the promotion lifecycle for "`run_poshqc_test` bundled coverage capture records zero covered lines repo-wide," or fold the observation into sibling feature #512 (toolchain gate fidelity). Recorded as finding NF-2. + +## 3. General Code Change Policy + +| Gate | Verdict | Evidence | +| --- | --- | --- | +| Bugfix workflow order (failing regression test first, then minimal fix, then verify) | PASS | Fail-before at 22-45 precedes the production commit; `git diff --name-only edf3d34c -- scripts` empty at fail-before time (`evidence/regression-testing/fail-before-f1-f4.2026-08-10T22-45.md`); this reviewer reproduced F1-F4 failing against the baseline `Helpers.ps1` extracted from `edf3d34c` (4 arithmetic failures with the exact recorded wrong values 6/4, 4/2, '0.75', 3/2) | +| Minimal, targeted fix; no opportunistic refactor | PASS | Production diff confined to the defective summary loop, one new pure helper, and the merge-rate block; protected union builder byte-identical (verified by `cmp`, old lines 217-268 == new lines 311-362); deeper design problems filed as issues #529-#532 instead of widening scope | +| 500-line file ceiling | PASS | `Helpers.ps1` 455 lines; test file 468 lines (both measured with `awk NR`; baseline 357 and 222 respectively — neither file crosses the ceiling) | +| Fail fast / error handling | PASS | Pre-existing `` guard retained verbatim and pinned by a new test; new helper validates mandatory typed parameter; `GetAttribute` used deliberately for StrictMode safety with an explanatory comment | +| Naming, docs, comments | PASS | Full comment-based help on the new helper; comments explain why (StrictMode rationale, duplication rationale), not what | +| Dependencies | PASS | No new dependency introduced | +| Public API compatibility | PASS | `Get-CoberturaCoverageSummary`, `Merge-CoberturaClassesByFilename`, `ConvertTo-KoverageCoberturaXml` signatures unchanged; one new exported helper added | +| Reusability | PASS | The dedup arithmetic is factored once into `Get-CoberturaClassLineSummary` and consumed by both call sites; the two rate-rounding expressions are duplicated deliberately with an in-code rationale (spec constrains the change to exactly one new helper; existing assertions depend on identical rounding). Recorded as accepted-design observation NF-5 | + +## 4. General Unit Test Policy + +| Gate | Verdict | Evidence | +| --- | --- | --- | +| Independence / isolation / determinism / speed | PASS | All 11 new `It` blocks operate on inline single-quoted here-string XML fixtures; no wall clock, no RNG, no ordering dependence; full suite runs in seconds | +| No temporary files, no external dependencies | PASS | Reviewer grep for `TestDrive|New-Item|Set-Content|Out-File|Remove-Item|[System.IO.File]` over the test file: zero matches; every new fixture is an in-memory `[xml]` cast. The only `Mock` in the file is in a pre-existing test (allowlist fallback), untouched by this diff | +| Arrange-Act-Assert and documented intent | PASS | Each new test carries a scenario comment naming the issue or branch it pins | +| Scenario completeness | PASS with gap noted | Positive, negative (throw guard retained), boundary (class with neither `lines` nor `methods`), and all three condition-coverage precedence branches are covered; the `max(hits)` update assignment (`Helpers.ps1:220`) is exercised by no test — finding NF-1 | +| Tests in `tests/` tree mirroring source | PASS | `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` mirrors `scripts/vscode/` | +| Existing tests treated as spec | PASS | Test-file diff is +246/-0 (verified by numstat and by counting deletion lines: 0); all 8 pre-existing `It` blocks unmodified and passing, including the `lines-valid | Should -Be '3'` assertion | + +## 5. PowerShell Rules (`.claude/rules/powershell.md`) + +| Gate | Verdict | Evidence | +| --- | --- | --- | +| Toolchain order format -> analyze -> test, restart on change | PASS | `evidence/qa-gates/toolchain-clean-pass.2026-08-10T23-10.md` records a single clean pass; formatter changed nothing so no restart was owed | +| PowerShell 7+ compatibility | PASS | Analyzer clean under repo settings; no PS7-incompatible construct introduced | +| Advanced functions, CmdletBinding, mandatory typed parameters | PASS | `Get-CoberturaClassLineSummary` uses `[CmdletBinding()]`, `[OutputType]`, `[Parameter(Mandatory = $true)][System.Xml.XmlElement]` | +| No global state; pure helper | PASS | Helper is pure (no I/O, mutates nothing in the source document; class-level `.Contains`/hashtable use is local) | +| Approved verbs / singular nouns | PASS | New function compliant; the one plural-noun warning is the baselined pre-existing finding on an out-of-scope exported function | +| Change budget (<= 2 production files direct-mode) | PASS | 1 production file + 1 test file | +| Mock sparingly; no executable mocking | PASS | Zero mocks in new tests | +| No analyzer debt deferral, no weakened assertions, no sleeps | PASS | Zero new findings; fail-before values assert counts (not rates) precisely because rates do not discriminate — assertions were strengthened, not weakened | + +## 6. Scope-Boundary and Threshold Invariants (blocking if violated — all held) + +| Invariant | Verdict | Evidence | +| --- | --- | --- | +| No coverage threshold re-tuned, lowered, or relaxed | **HELD** | `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` re-run by this reviewer: empty output. The 85.0317%-vs-85% margin (0.03 pp) is recorded purely as fact and handed off to #494 in `evidence/other/threshold-handoff-494.2026-08-10T23-15.md`, which states explicitly that nothing in it is acted upon | +| No edit to `CLAUDE.md` or `.claude/rules/**` | **HELD** | Same empty diff; sibling ownership (#512, #494) respected | +| AC-18 two-source-file boundary | **HELD** | Full-diff enumeration minus `docs/**` and `.claude/agent-memory/**` yields exactly the two named files; `Invoke-MSTestWithCoverage.ps1` unchanged including its missing `\.claude\` discovery exclusion (now filed as issue #531) | +| AC-8 union builder byte-identity | **HELD** | Reviewer `cmp` of `edf3d34c` lines 217-268 vs HEAD lines 311-362: byte-identical | + +## 7. Evidence Location Compliance + +- Reviewer scan of the full branch diff for files written under `artifacts/baselines/`, `artifacts/qa/`, `artifacts/evidence/`, or `artifacts/coverage/`: **zero occurrences.** No committed file in the diff lives under any `artifacts/` path. +- All 35 evidence files reside under `/evidence/{baseline,regression-testing,qa-gates,issue-updates,other}/` — the canonical layout per `evidence-and-timestamp-conventions`. +- `scripts/dev_tools/validate_evidence_locations.py` does not exist in this repository (consistent with prior review findings that several validator scripts named in agent instructions are not ported to TaskMaster); the scan above was performed manually with `git diff --name-only` and a recursive listing instead. +- Every command-step evidence artifact carries `Timestamp:`, `Command:`, `EXIT_CODE:`, and `Output Summary:`; the three narrative artifacts (`ac-status-summary`, `helper-branch-test-map`, `threshold-handoff-494`) carry `Timestamp:` only, as permitted by the accepted AC-16 amendment, and each is enumerated (or declared in advance) in the final sweep of `evidence/other/evidence-location-audit.2026-08-10T23-20.md`. +- The untracked `artifacts/pester/*.xml` files are producer tool output (gitignored via `.gitignore:57`), not evidence artifacts, per the executor's CORRECTION block — see § 2.5 disposition and finding NF-2. + +## 8. Executor Self-Reported Items (both evaluated) + +1. **AC-20 completed by the orchestrator, not the executor.** Verified genuine and honest. The executor's `POSTING BLOCKED` record is preserved verbatim (tool surface of exactly four PoshQC tools, no promotion tools; `gh` deliberately not used as a lifecycle bypass; AC-20 left unchecked rather than falsified). The orchestrator's RESOLUTION section (2026-08-10T23-50) appends rather than rewrites, names the actor and mechanism (`new_potential_bug_entry` -> `potential_to_issue`), and records issues #529-#532. This reviewer confirmed all four issues exist and are OPEN on GitHub with matching titles, and that the four promoted potential entries are committed under `docs/features/potential/promoted/`. The `ac-status-summary` superseding update is clearly marked and retains the original narrative. The blocked-then-resolved audit trail is legible and truthful. **PASS.** +2. **P0-T13 CORRECTION block.** Adequate. The retracted sentence ("writes no coverage artifact") is quoted verbatim, the reason the original instrument was invalid is explained (`.gitignore:57` hides `artifacts/` from porcelain), the correction is dated and attributed, and the claim that no downstream conclusion depended on it holds: every coverage figure cited in evidence comes from direct `Invoke-Pester` runs whose `OutputPath` is an explicit `/evidence//` path (verified in each artifact's recorded command), and no feature artifact reads from `artifacts/pester/`. The correction-not-silent-edit practice matches the evidence-integrity expectation. **PASS.** (The corrected fact also surfaces the row 2.5 observation — the very files the tool writes contain the zero-coverage reading — reinforcing that the correction was material and worth recording.) + +## Findings Register (all non-blocking) + +| ID | Severity | Finding | Disposition | +| --- | --- | --- | --- | +| NF-1 | Minor | The `max(hits)` update assignment at `Helpers.ps1:220` is uncovered: no test presents a duplicate line number whose later entry carries strictly larger hits, so the stated "max(hits)" resolution rule is pinned only for the first-entry-wins ordering | Recommend one added fixture (class rollup `hits="0"`, method entry `hits="1"`, assert `CoveredLines = 1`); does not gate merge — new-code coverage 97.50% clears every floor and the executor recorded the gap explicitly | +| NF-2 | Minor | Canonical repo-wide PowerShell coverage artifact (`artifacts/pester/powershell-coverage.xml`, producer output of `run_poshqc_test`) records zero covered lines repo-wide — an invalid capture contradicted by committed direct-Pester evidence at the same head | Row 2.5 FAIL recorded against the artifact; dispositioned non-blocking (pre-existing tool measurement defect, outside this bugfix's mandated minimal scope); recommend filing a tooling follow-up or folding into #512 | +| NF-3 | Info | `evidence/qa-gates/poshqc-analyze.2026-08-10T23-10.md` states "The 167 added lines introduced no analyzer debt" for the test file; by final QA the test file had 246 added lines (167 fixture lines plus 79 helper-test lines) | Prose figure is stale; the finding-set arithmetic (0 findings) is correct and was independently re-verified at HEAD; no action required | +| NF-4 | Info | The final sweep records 34 evidence files / 2 narrative artifacts; the current tree holds 35 and 3 because `ac-status-summary.2026-08-10T23-30.md` was written after the sweep (its path was declared in advance inside the sweep, as the sweep itself states) and the AC-20 RESOLUTION was later appended to an already-enumerated artifact | Compliant via the declared-in-advance mechanism; noted for completeness | +| NF-5 | Info | The merged-class `line-rate`/`branch-rate` rounding expressions duplicate the equivalent expressions in `Get-CoberturaCoverageSummary` rather than sharing a second helper | Accepted design: in-code comment records the rationale (spec constrains the change to exactly one new helper; existing `Should -Be '1'`-style assertions depend on identical rounding and the `'0'` zero-denominator fallback); no action required | + +## Appendix A — Reviewer-Executed Verification Commands + +1. `git merge-base HEAD edf3d34c` -> `edf3d34c` (base is ancestor). +2. `git diff edf3d34c..HEAD --stat` / `--name-only` (full-diff enumeration; source-file isolation). +3. `git diff --name-only edf3d34c -- CLAUDE.md .claude/rules coverage.config` -> empty (threshold invariant). +4. Dot-source HEAD `Helpers.ps1`; `Get-CoberturaCoverageSummary` over the #424 raw baseline document -> 79957 / 56124 / 23109 / 13472 (equals the document's own root attributes; AC-1 oracle reproduced exactly). +5. Dot-source `edf3d34c` `Helpers.ps1` (extracted to scratchpad); same run -> 161086 / 113219 / 46218 / 26944 (AC-2 pre-change figures reproduced exactly; each strictly greater than its post-fix counterpart). +6. `ConvertTo-KoverageCoberturaXml` at HEAD over the #424 `coverage-final.cobertura.xml` -> lines-valid 62345, lines-covered 53013, line-rate 0.850317 (AC-3 reproduced exactly). +7. `Invoke-Pester` at HEAD -> 19/19 passed. +8. `Invoke-Pester` of the HEAD test file against the `edf3d34c` production file in a scratchpad tree -> F1, F2, F3, F4 fail with the recorded pre-fix arithmetic values; F5, F6 and the throw guard pass; three additional failures are environment artifacts of the scratchpad tree (the three pre-existing tests that omit `-ProjectNames` resolve an empty project allowlist there, verified directly), matching the executor's in-repo fail-before run of 14 tests / 4 failures. +9. `cmp` of old lines 217-268 vs new lines 311-362 -> byte-identical (AC-8). +10. `grep` for `.//lines/line` (0 matches) and `$classSummaryXml` (0 matches) in HEAD `Helpers.ps1` (AC-7, AC-9). +11. `Invoke-ScriptAnalyzer` on both changed files -> 1 baselined pre-existing warning, 0 on tests (AC-15 analyzer leg). +12. Re-summed the three committed JaCoCo artifacts (baseline, fail-before, final) for rows 2.1-2.3; confirmed line 220 (`mi=1 ci=0`) is the single uncovered new statement. +13. Field-scan of all 30 evidence markdown artifacts for `Timestamp:` / `Command:` / `EXIT_CODE:` / `Output Summary:` (AC-16 schema). +14. `gh issue view 529 530 531 532` -> all four OPEN with matching titles (AC-20). +15. `awk NR` line counts: Helpers 455, tests 468, baselines 357 and 222 (AC-19 and file ceiling). diff --git a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md index 6209455d4..de0b10e99 100644 --- a/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md +++ b/docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md @@ -632,63 +632,63 @@ and must be filed through the MCP promotion lifecycle rather than left as prose: **Amendment 2026-08-10T18-24 (AC-15 and AC-16 only; preparation-time, before any execution).** Two criteria were unsatisfiable as originally written and were corrected so that execution cannot check off a false statement. AC-15 had required *zero* PSScriptAnalyzer findings on both changed files; `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` carries one pre-existing `PSUseSingularNouns` finding on `Get-CoberturaLineConditionCoverageParts`, and clearing it would require renaming an exported function that § Implementation strategy lists as Unmodified and § Technical specifications forbids changing. AC-15 is now a no-new-findings gate against a recorded Phase 0 baseline. AC-16 had enumerated only `{baseline, qa-gates, regression-testing}`; the plan legitimately also writes to `issue-updates/` and `other/`, both canonical under `.claude/skills/evidence-and-timestamp-conventions/SKILL.md`, so the enumeration was completed. No other criterion text is altered, no threshold is affected, and no criterion is weakened in substance. A further preparation-time correction on 2026-08-10T21-40 scoped AC-16's schema-field requirement to command-step artifacts, because the plan legitimately writes narrative artifacts (for example the AC status summary) that record no command and therefore carry no `EXIT_CODE:`; requiring that field of every artifact made the criterion unsatisfiable and would have forced a false check-off. No threshold is affected and the criterion is not weakened in substance. -- [ ] **AC-1 (headline: generator parity).** With the fix applied, dot-sourcing +- [x] **AC-1 (headline: generator parity).** With the fix applied, dot-sourcing `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and running `Get-CoberturaCoverageSummary` over `docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/baseline/coverage-baseline.cobertura.xml` returns exactly `LinesValid = 79957`, `LinesCovered = 56124`, `BranchesValid = 23109`, `BranchesCovered = 13472` — reproducing that document's own root attributes. -- [ ] **AC-2 (pre-change figure).** The pre-change run of the same procedure against unmodified +- [x] **AC-2 (pre-change figure).** The pre-change run of the same procedure against unmodified `Helpers.ps1` is recorded numerically and shows `LinesValid = 161086`, with `LinesCovered`, `BranchesValid` and `BranchesCovered` recorded as concrete integers, each strictly greater than its AC-1 post-change counterpart. -- [ ] **AC-3 (package-filtered A/B).** Reprocessing +- [x] **AC-3 (package-filtered A/B).** Reprocessing `.../424/evidence/qa-gates/coverage-final.cobertura.xml` through `ConvertTo-KoverageCoberturaXml` yields root `lines-valid = 62345`, `lines-covered = 53013`, and `line-rate` = 0.850317 (post-fix), recorded against the pre-fix values 110849 / 94937 / 0.856453. -- [ ] **AC-4 (per-file merged rate).** The merged-class `line-rate` defect is corrected from +- [x] **AC-4 (per-file merged rate).** The merged-class `line-rate` defect is corrected from 0.8625 (69/80) to 0.803571 (45/56) for the `QfcHomeController.Iteration.cs` case, demonstrated in the suite by fixture F3, whose merged class asserts `line-rate` = `'0.6'` (3/5) where the unmodified code produces `'0.75'` (6/8), and whose merged class-level `` contains exactly five `line` children numbered 12, 13, 56, 57, 58 in ascending order. -- [ ] **AC-5 (branch counts deduplicated).** Fixture F2 asserts `branches-valid` = `'2'` and +- [x] **AC-5 (branch counts deduplicated).** Fixture F2 asserts `branches-valid` = `'2'` and `branches-covered` = `'1'` where the unmodified code produces 4 and 2. No branch regression assertion in the suite relies on `branch-rate` alone. -- [ ] **AC-6 (helper contract).** A new pure function `Get-CoberturaClassLineSummary` exists in +- [x] **AC-6 (helper contract).** A new pure function `Get-CoberturaClassLineSummary` exists in `Helpers.ps1`, takes a mandatory `-ClassNode [System.Xml.XmlElement]`, enumerates `./lines/line` then `./methods/method/lines/line`, keys by `[int]$node.number`, resolves repeats by `max(hits)` / `branch=True` if either / `condition-coverage` from the larger `Total` (tie-broken by larger `Covered`) via `Get-CoberturaLineConditionCoverageParts`, and returns `LineMap`, `TotalLines`, `CoveredLines`, `TotalBranches`, `CoveredBranches`. -- [ ] **AC-7 (defect removed at its one site).** The expression +- [x] **AC-7 (defect removed at its one site).** The expression `$cls.SelectNodes('.//lines/line')` no longer appears anywhere in `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`, and `Get-CoberturaCoverageSummary` accumulates its four totals from `Get-CoberturaClassLineSummary`. -- [ ] **AC-8 (correct site untouched).** The union builder at `Helpers.ps1:217-268`, including the +- [x] **AC-8 (correct site untouched).** The union builder at `Helpers.ps1:217-268`, including the child-axis selection `$classNode.SelectNodes('./lines/line')`, is byte-identical to `main` in the diff. -- [ ] **AC-9 (delegation replaced).** The `$classSummaryXml` synthetic-document block formerly at +- [x] **AC-9 (delegation replaced).** The `$classSummaryXml` synthetic-document block formerly at `Helpers.ps1:270-273` is removed, and the merged class's `line-rate` / `branch-rate` attributes are set from a direct `Get-CoberturaClassLineSummary` call on `$mergedClassNode`. -- [ ] **AC-10 (structure preserved).** Fixture F6 passes: the merged class still carries a +- [x] **AC-10 (structure preserved).** Fixture F6 passes: the merged class still carries a `` element containing exactly the primary class's one `` child, and no `` element's `hits` attribute differs from the input. The `` subtrees are neither merged nor stripped. -- [ ] **AC-11 (six fixtures present and passing).** Fixtures F1-F6 exist as six new `It` blocks in +- [x] **AC-11 (six fixtures present and passing).** Fixtures F1-F6 exist as six new `It` blocks in `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1`, use inline single-quoted here-strings matching the file's existing style, create no file on disk, use no mock in any arithmetic path, and all pass. -- [ ] **AC-12 (fail-before evidence).** F1, F2, F3 and F4 are demonstrated to fail against +- [x] **AC-12 (fail-before evidence).** F1, F2, F3 and F4 are demonstrated to fail against unmodified `Helpers.ps1` with the pre-fix values stated in § Test Strategy, and the failing run is recorded under `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/regression-testing/`. -- [ ] **AC-13 (helper precedence branches covered).** Direct unit tests cover all three branches of +- [x] **AC-13 (helper precedence branches covered).** Direct unit tests cover all three branches of the `condition-coverage` precedence rule: candidate `Total` greater; `Total` equal and `Covered` greater; neither (existing value retained). -- [ ] **AC-14 (zero existing tests broken).** All eight pre-existing `It` blocks in +- [x] **AC-14 (zero existing tests broken).** All eight pre-existing `It` blocks in `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` pass unmodified, and the diff shows no edit to any of them — including the `lines-valid | Should -Be '3'` assertion. -- [ ] **AC-15 (toolchain green).** A single clean pass of PoshQC format +- [x] **AC-15 (toolchain green).** A single clean pass of PoshQC format (`mcp__drm-copilot__run_poshqc_format`, no files changed), PSScriptAnalyzer (`mcp__drm-copilot__run_poshqc_analyze` plus a per-file `Invoke-ScriptAnalyzer` breakdown, **no finding on either changed file that is absent from the recorded Phase 0 baseline**; the single @@ -696,23 +696,23 @@ and must be filed through the MCP promotion lifecycle rather than left as prose: baselined and out of scope, since clearing it requires renaming an exported function this spec marks Unmodified), and Pester (direct `Invoke-Pester` run, `FailedCount` = 0) is recorded under `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/qa-gates/`. -- [ ] **AC-16 (canonical evidence locations).** Every evidence artifact for this feature lives under +- [x] **AC-16 (canonical evidence locations).** Every evidence artifact for this feature lives under `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/{baseline,qa-gates,regression-testing,issue-updates,other}/`, every command-step artifact carries `Timestamp`, `Command` and `EXIT_CODE` fields (baseline artifacts additionally carry `Output Summary`), every narrative artifact that records no command carries `Timestamp` and is individually enumerated in the final sweep, and no evidence artifact is written under any `artifacts/` path. -- [ ] **AC-17 (no threshold re-tuned).** The diff contains no change to `CLAUDE.md`, to any file +- [x] **AC-17 (no threshold re-tuned).** The diff contains no change to `CLAUDE.md`, to any file under `.claude/rules/`, to `coverage.config`, or to any other file that states a coverage threshold. The 85.0317%-versus-85% observation is recorded in evidence as a handoff to #494 and nowhere acted upon. -- [ ] **AC-18 (scope boundary held).** The diff touches exactly two source files — +- [x] **AC-18 (scope boundary held).** The diff touches exactly two source files — `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` and `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` — plus evidence and feature documents. `scripts/vscode/Invoke-MSTestWithCoverage.ps1` is unchanged, including its missing `\.claude\` discovery exclusion. -- [ ] **AC-19 (file ceiling).** `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` remains under +- [x] **AC-19 (file ceiling).** `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` remains under 500 lines (357 before the change). -- [ ] **AC-20 (follow-ups filed).** The four follow-up candidates in § Rollout & Follow-up are filed +- [x] **AC-20 (follow-ups filed).** The four follow-up candidates in § Rollout & Follow-up are filed as GitHub issues through the promotion lifecycle, with their issue numbers recorded in this feature's evidence, and none of them is fixed in this change. diff --git a/docs/features/potential/promoted/2026-08-10-agent-memory-cobertura-dedup-generalization-wrong.md b/docs/features/potential/promoted/2026-08-10-agent-memory-cobertura-dedup-generalization-wrong.md new file mode 100644 index 000000000..ae82c0a91 --- /dev/null +++ b/docs/features/potential/promoted/2026-08-10-agent-memory-cobertura-dedup-generalization-wrong.md @@ -0,0 +1,68 @@ +# agent-memory-cobertura-dedup-generalization-wrong (Issue #532) + +- Date captured: 2026-08-10 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/agent-memory-cobertura-dedup-generalization-wrong/ (Issue #532) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #532 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/532 +- Last Updated: 2026-08-11 +## Summary + +Stored agent memory records an incorrect generalization about Cobertura root-attribute deduplication: the claim holds only for raw `dotnet-coverage` output, not for post-processed `ConvertTo-KoverageCoberturaXml` artifacts. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: n/a (documentation defect) +- Command/flags used: n/a +- Data source or fixture: `.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md:34-36` + +## Steps to Reproduce + +1. Read `.claude/agent-memory/atomic-executor/project_coverage_delta_reproduce_baseline_counting_method.md:34-36`. +2. Apply its stated rule to a post-processed `ConvertTo-KoverageCoberturaXml` artifact. +3. Compare the resulting figure against the class-level rollup count. + +## Expected Behavior + +The memory should state the distinction between raw generator output and post-processed artifacts explicitly, so a future agent does not skip a needed adjustment. + +## Actual Behavior + +The memory asserts that the repository-wide root `` attributes "are already deduped and match a per-package all-descendant sum in this repo, so repo-level figures need no adjustment." That is true only of raw `dotnet-coverage` output. It was false for any post-processed `ConvertTo-KoverageCoberturaXml` artifact, where (before #441 was fixed) the root attributes *were* the all-descendant sum — the very defect #441 corrects. + +## Logs / Screenshots + +- [x] Attached minimal logs or snippet +- Snippet: measured on the two committed samples — + - raw `coverage-baseline.cobertura.xml`: class-level `` count 79957 **equals** its own `lines-valid="79957"`; the all-descendant count is 161086. + - post-processed `coverage-final.cobertura.xml`: the all-descendant count 110849 **equals** its emitted `lines-valid="110849"`; the class-level count is 62345. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [ ] Medium +- [x] Low + +A future agent following the memory verbatim could skip a needed adjustment when reading a post-processed artifact. No production code is affected. + +## Suspected Cause / Notes + +The memory was written from observations of raw generator output only and generalized beyond the evidence. Now that #441 has landed, the post-processed root attributes are deduplicated too, so the correction should record the historical distinction and the date it stopped applying rather than simply deleting the claim. + +Recorded as follow-up candidate 4 in `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: n/a (documentation) +- [x] Integration scenario to retest: n/a +- [x] Manual verification notes: rewrite lines 34-36 to distinguish raw `dotnet-coverage` output from post-processed `ConvertTo-KoverageCoberturaXml` artifacts and cite the measured figures above. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/potential/promoted/2026-08-10-cobertura-max-hits-update-branch-untested.md b/docs/features/potential/promoted/2026-08-10-cobertura-max-hits-update-branch-untested.md new file mode 100644 index 000000000..ca012abdd --- /dev/null +++ b/docs/features/potential/promoted/2026-08-10-cobertura-max-hits-update-branch-untested.md @@ -0,0 +1,64 @@ +# cobertura-max-hits-update-branch-untested (Issue #537) + +- Date captured: 2026-08-10 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/cobertura-max-hits-update-branch-untested/ (Issue #537) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #537 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/537 +- Last Updated: 2026-08-11 +## Summary + +The `max(hits)` update assignment in `Get-CoberturaClassLineSummary` is exercised by no test, so the deduplication rule is pinned only for the first-entry-wins ordering and a regression to first-wins would pass the suite. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: n/a (PowerShell / Pester 5) +- Command/flags used: `Invoke-Pester tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 -CodeCoverage scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` +- Data source or fixture: `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1:220` + +## Steps to Reproduce + +1. Check out the branch that landed #441 / #478. +2. Run the Pester suite for `Invoke-MSTestWithCoverage.Helpers.Tests.ps1` with code coverage over `Invoke-MSTestWithCoverage.Helpers.ps1`. +3. Inspect coverage of the `max(hits)` update assignment inside `Get-CoberturaClassLineSummary`. + +## Expected Behavior + +Every branch of the stated deduplication contract should be pinned by a test. `Get-CoberturaClassLineSummary` documents `max(hits)` resolution for duplicate line numbers, so both orderings should be covered: the case where the first entry already carries the larger hits value, and the case where a later entry carries a strictly larger value and must overwrite it. + +## Actual Behavior + +Only the first ordering is tested. Fixture F4 presents line 5 with `hits=1` in `.ctor ()` and `hits=0` in `.ctor (int)`, so the maximum is established by the first entry encountered and the update assignment at line 220 never executes. Coverage confirms it: new code measures 39/40 covered statements, and the single uncovered statement is that assignment. A regression that replaced `max(hits)` with first-entry-wins would leave the suite fully green. + +## Logs / Screenshots + +- [x] Attached minimal logs or snippet +- Snippet: new-code coverage 39/40 = 97.50%; the one uncovered statement is the `max(hits)` update at `Helpers.ps1:220`. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [ ] Medium +- [x] Low + +No defect in current behavior — the implementation is correct and the arithmetic oracle (79957 / 56124 / 23109 / 13472) is reproduced exactly. This is a test-adequacy gap on the headline `max(hits)` semantics of #441, raised in that feature's review as non-blocking finding NF-1. New-code coverage of 97.50% already clears the `>= 90%` floor, so no gate is failing. + +## Suspected Cause / Notes + +Fixture F4 was designed to prove deduplication happens at all, and its `hits` ordering happens to make the update path unreachable. The gap was recorded explicitly rather than concealed in `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/evidence/other/ac-status-summary.2026-08-10T23-30.md` and in the review's finding NF-1. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: add one fixture where the class-level rollup carries `hits="0"` for a line and a method-level entry carries `hits="1"` for the same line, asserting `CoveredLines = 1`. +- [x] Integration scenario to retest: confirm new-code statement coverage reaches 40/40 and that a deliberate first-entry-wins mutation now fails the suite. +- [x] Manual verification notes: the fixture must follow the existing pattern — inline single-quoted here-string, no file on disk, no mock, explicit `-ProjectNames` — and respect the per-block line budgets in the #441 plan's § Test-File Line Budget. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/potential/promoted/2026-08-10-cobertura-merged-class-methods-incomplete.md b/docs/features/potential/promoted/2026-08-10-cobertura-merged-class-methods-incomplete.md new file mode 100644 index 000000000..c9d316fb5 --- /dev/null +++ b/docs/features/potential/promoted/2026-08-10-cobertura-merged-class-methods-incomplete.md @@ -0,0 +1,72 @@ +# cobertura-merged-class-methods-incomplete (Issue #530) + +- Date captured: 2026-08-10 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/cobertura-merged-class-methods-incomplete/ (Issue #530) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #530 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/530 +- Last Updated: 2026-08-11 +## Summary + +A merged Cobertura class retains only the primary class's ``, so the emitted document's method-level lines do not account for all of that class's class-level lines. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: n/a (PowerShell) +- Command/flags used: `ConvertTo-KoverageCoberturaXml` / `Merge-CoberturaClassesByFilename` in `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` +- Data source or fixture: `docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/qa-gates/coverage-final.cobertura.xml` + +## Steps to Reproduce + +1. Dot-source `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. +2. Reprocess a Cobertura document containing two or more `` elements that share a `filename` (for example `Ns.Foo` and its compiler-generated partner `Ns.Foo.<>c`). +3. Inspect the merged class: compare the count of `` children in its class-level `` rollup against the set of lines described by its `` subtree. + +## Expected Behavior + +The merged class's `` subtree should describe the same source lines as its merged class-level `` rollup, or the divergence should be an explicitly documented and tested contract. + +## Actual Behavior + +`Merge-CoberturaClassesByFilename` unions the class-level `` of same-filename classes but leaves `` un-merged; the merged class carries only the primary class's `` children. For `QuickFiler\Controllers\QfcHomeController.Iteration.cs` in the `-424` sample, the merged class-level rollup has 56 lines while the retained `` describe only 24. + +## Logs / Screenshots + +- [x] Attached minimal logs or snippet +- Snippet: merged class-level rollup 56 lines vs. retained `` 24 lines for `QfcHomeController.Iteration.cs`. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [x] Medium +- [ ] Low + +Per-method `line-rate` data in the emitted document is incomplete for merged classes. Root and per-file line rates are correct as of #441 / #478. + +## Suspected Cause / Notes + +This was deliberately **not** fixed in #441 / #478, for recorded reasons: + +- Sibling classes sharing a filename are compiler-generated partners (`Foo` and `Foo.<>c`, async state machines) that routinely both declare `name=".ctor" signature="()"`. Appending sibling `` elements produces duplicate `(name, signature)` pairs, breaking any consumer that keys methods that way — including the per-method `line-rate` technique this repository uses for coverage-delta work. +- Deduplicating by `(name, signature)` would be worse: it discards genuinely distinct methods. +- Stripping `` was rejected outright: it destroys per-method `line-rate` data that coverage-delta work actively relies on. + +Fixture F6 in `tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1` currently **pins** the existing behaviour (methods neither merged nor stripped). Any fix here must update F6 deliberately, not incidentally. + +Recorded as follow-up candidate 2 in `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: a fixture asserting the merged class's method-described line set against its class-level rollup, with an explicit disambiguation rule for duplicate `(name, signature)` pairs. +- [x] Integration scenario to retest: reprocess the `-424` sample and confirm no consumer keyed on `(name, signature)` regresses. +- [x] Manual verification notes: F6 must be updated deliberately as part of any fix. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/potential/promoted/2026-08-10-cobertura-package-rates-not-recomputed.md b/docs/features/potential/promoted/2026-08-10-cobertura-package-rates-not-recomputed.md new file mode 100644 index 000000000..110984661 --- /dev/null +++ b/docs/features/potential/promoted/2026-08-10-cobertura-package-rates-not-recomputed.md @@ -0,0 +1,66 @@ +# cobertura-package-rates-not-recomputed (Issue #529) + +- Date captured: 2026-08-10 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/cobertura-package-rates-not-recomputed/ (Issue #529) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #529 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/529 +- Last Updated: 2026-08-11 +## Summary + +Package-level `line-rate` and `branch-rate` are never recomputed after Cobertura package filtering and class merging, so every surviving `` carries a stale rate computed over a different, larger class set. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: n/a (PowerShell) +- Command/flags used: `ConvertTo-KoverageCoberturaXml -XmlContent -RepoRoot -PathSeparator '\'` +- Data source or fixture: `docs/features/active/2026-08-06-quickfiler-high-confidence-queue-init-stall-424/evidence/qa-gates/coverage-final.cobertura.xml` + +## Steps to Reproduce + +1. Dot-source `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1`. +2. Run `ConvertTo-KoverageCoberturaXml` over a raw `dotnet-coverage` Cobertura document that contains `.Test` packages and multiple same-filename classes. +3. Inspect the `line-rate` and `branch-rate` attributes of any surviving `` element in the output and compare them against a rate recomputed from that package's post-filter, post-merge class set. + +## Expected Behavior + +After package filtering removes `.Test` packages and class merging unions same-filename classes, each surviving `` should carry `line-rate` and `branch-rate` recomputed over its own post-processing class set. + +## Actual Behavior + +`ConvertTo-KoverageCoberturaXml` writes only the six root `` attributes and the `line-rate` / `branch-rate` / `complexity` attributes of merged class elements. It never recomputes `` or ``, so each package retains the value the generator emitted for a different, larger class set. + +## Logs / Screenshots + +- [x] Attached minimal logs or snippet +- Snippet: the stale values are consumed downstream by `scripts/temp-extract-coverage.ps1:47`, which reads `$pkg.'line-rate'` for per-assembly reporting. + +## Impact / Severity + +- [ ] Blocker +- [ ] High +- [x] Medium +- [ ] Low + +Per-assembly reported coverage is wrong wherever filtering or merging changed a package's class set. Repository-wide root attributes are unaffected (those were corrected by #441). + +## Suspected Cause / Notes + +The rate-recomputation pass in `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` operates at the root and merged-class levels only; the intermediate `` level was never included. + +Deliberately out of scope for #441 / #478: recomputing package-level rates widens the diff without serving either issue, and `CLAUDE.md` § Bugfix Workflow step 2 mandates the minimal targeted fix. Recorded as follow-up candidate 1 in `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: add a Pester fixture asserting a package's `line-rate` after a filter-and-merge pass equals the rate derived from its surviving classes. +- [x] Integration scenario to retest: reprocess the committed `-424` sample and confirm per-package rates agree with a recomputation from the emitted class set. +- [x] Manual verification notes: cross-check against `scripts/temp-extract-coverage.ps1` per-assembly output. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/potential/promoted/2026-08-10-mstest-coverage-discovery-claude-worktree-exclusion.md b/docs/features/potential/promoted/2026-08-10-mstest-coverage-discovery-claude-worktree-exclusion.md new file mode 100644 index 000000000..bbb88c9d6 --- /dev/null +++ b/docs/features/potential/promoted/2026-08-10-mstest-coverage-discovery-claude-worktree-exclusion.md @@ -0,0 +1,66 @@ +# mstest-coverage-discovery-claude-worktree-exclusion (Issue #531) + +- Date captured: 2026-08-10 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/mstest-coverage-discovery-claude-worktree-exclusion/ (Issue #531) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #531 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/531 +- Last Updated: 2026-08-11 +## Summary + +`Invoke-MSTestWithCoverage.ps1` test-assembly discovery lacks a `\.claude\` exclusion, so running with `-SearchRoot .` descends into agent worktrees and picks up stale sibling-worktree assemblies. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: n/a (PowerShell) +- Command/flags used: `scripts/vscode/Invoke-MSTestWithCoverage.ps1 -SearchRoot .` +- Data source or fixture: main checkout `C:\Users\DanMoisan\repos\TaskMaster` containing `.claude\worktrees\agent-*\` + +## Steps to Reproduce + +1. Create at least one agent worktree under `.claude\worktrees\agent-*\` and build it so it contains `*.Test.dll` assemblies. +2. From the main checkout, run `scripts/vscode/Invoke-MSTestWithCoverage.ps1 -SearchRoot .`. +3. Inspect the discovered assembly list and the resulting coverage figure. + +## Expected Behavior + +Discovery should enumerate only the current checkout's test assemblies and ignore any path under `.claude\worktrees\`. + +## Actual Behavior + +`scripts/vscode/Invoke-MSTestWithCoverage.ps1:296-302` filters discovered `*.Test.dll` paths on `\bin\\`, `\obj\` and `\ref\` only. There is no `\.claude\` guard, so discovery descends into `.claude\worktrees\agent-*\**` and picks up stale sibling-worktree assemblies, producing bogus `AssemblyInitialize` signature failures and a coverage figure computed over the wrong assembly set. + +## Logs / Screenshots + +- [x] Attached minimal logs or snippet +- Snippet: bogus `AssemblyInitialize` signature failures originating from assemblies under `.claude\worktrees\agent-*\`. + +## Impact / Severity + +- [ ] Blocker +- [x] High +- [ ] Medium +- [ ] Low + +Local coverage runs report a figure computed over the wrong assembly set, and spurious failures obscure real ones. + +## Suspected Cause / Notes + +The discovery filter predates the agent-worktree layout under `.claude\worktrees\`. Suggested fix: add `-and $_.FullName -notmatch '\\\.claude\\'` to the existing discovery filter. Note that the exclusion must be applied to the path relative to the search root, so that a legitimate run *inside* an agent worktree still discovers its own assemblies. + +Deliberately out of scope for #441 / #478: that is a production behaviour change to a file those issues do not otherwise touch, and #441's AC-18 pins its diff to exactly two source files. Recorded as follow-up candidate 3 in `docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/spec.md` § Rollout & Follow-up. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: a Pester test over the discovery filter asserting that a `.claude\worktrees\` path is excluded and an in-worktree relative path is retained. +- [x] Integration scenario to retest: run with `-SearchRoot .` from the main checkout with at least one populated agent worktree present. +- [x] Manual verification notes: confirm the discovered assembly count matches the checkout's own test projects. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/potential/promoted/2026-08-10-poshqc-test-coverage-capture-records-zero.md b/docs/features/potential/promoted/2026-08-10-poshqc-test-coverage-capture-records-zero.md new file mode 100644 index 000000000..a18367b10 --- /dev/null +++ b/docs/features/potential/promoted/2026-08-10-poshqc-test-coverage-capture-records-zero.md @@ -0,0 +1,75 @@ +# poshqc-test-coverage-capture-records-zero (Issue #536) + +- Date captured: 2026-08-10 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/poshqc-test-coverage-capture-records-zero/ (Issue #536) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #536 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/536 +- Last Updated: 2026-08-11 +## Summary + +The bundled `mcp__drm-copilot__run_poshqc_test` coverage capture writes `artifacts/pester/powershell-coverage.xml` with **zero covered lines for every file in the repository**, making the canonical repo-wide PowerShell coverage artifact unusable as a gate input. + +## Environment + +- OS/version: Windows 11 Pro 10.0.26200 +- Python version: n/a (PowerShell / Pester 5) +- Command/flags used: `mcp__drm-copilot__run_poshqc_test` with `scan_folders = ["scripts/vscode", "tests/scripts/vscode"]` +- Data source or fixture: `artifacts/pester/powershell-coverage.xml`, written 2026-08-10 22:58 + +## Steps to Reproduce + +1. Run `mcp__drm-copilot__run_poshqc_test` against a workspace whose Pester suite passes and demonstrably exercises production code. +2. Read the generated `artifacts/pester/powershell-coverage.xml`. +3. Aggregate its JaCoCo `` elements by type. + +## Expected Behavior + +The emitted JaCoCo counters should reflect the lines actually executed by the passing suite. For the run in question, `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` was exercised by 19 passing tests and independently measured at 183/202 = 90.59% LINE coverage by a direct `Invoke-Pester` capture at the same head. + +## Actual Behavior + +Every counter records zero covered. Aggregated over all 1227 `` elements in the file: + +``` +CLASS covered 0 / missed 168 +INSTRUCTION covered 0 / missed 21800 +LINE covered 0 / missed 16075 +METHOD covered 0 / missed 1445 +``` + +A reading of literal zero repo-wide, for a run whose suite passed and whose direct-Pester capture at the same head reports 90.59% on the primary file, is a measurement defect in the capture rather than a property of the code. + +## Logs / Screenshots + +- [x] Attached minimal logs or snippet +- Snippet: the aggregation above, reproduced from `artifacts/pester/powershell-coverage.xml` (gitignored producer output via `.gitignore:57`, so it is not committed). + +## Impact / Severity + +- [ ] Blocker +- [x] High +- [ ] Medium +- [ ] Low + +The canonical repo-wide PowerShell coverage artifact reads 0.00% against an `>= 85%` floor. Any gate that consumes it fails unconditionally, and any agent that trusts it will either report a false blocker or route around the canonical artifact. Discovered during feature review of #441 (recorded there as finding NF-2 and dispositioned non-blocking for that bugfix, because the defect is pre-existing and #441 changes no coverage-capture tooling, hook, or configuration). + +## Suspected Cause / Notes + +Likely a coverage-path or `-CodeCoverage` scoping mismatch inside the bundled PoshQC test invocation: the instrumented file set appears to be resolved independently of the files the tests actually load, so nothing the suite executes is attributed. Compare the bundled invocation against the direct `Invoke-Pester` capture used as evidence in #441, which produces correct non-zero counters over the same tree. + +Candidate to fold into sibling feature #512 (toolchain gate fidelity) rather than fix standalone. + +## Proposed Fix / Validation Ideas + +- [x] Unit coverage areas: a check asserting that a known-covered file reports non-zero LINE covered after a bundled `run_poshqc_test` run. +- [x] Integration scenario to retest: run the bundled tool and a direct `Invoke-Pester -CodeCoverage` capture over the same tree and assert the two agree within tolerance. +- [x] Manual verification notes: aggregate the JaCoCo counters and confirm covered > 0 repo-wide. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 b/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 index e6ed522d2..2af807650 100644 --- a/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 +++ b/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 @@ -119,17 +119,11 @@ function Get-CoberturaCoverageSummary { } foreach ($cls in $pkg.SelectNodes('.//class')) { - foreach ($line in $cls.SelectNodes('.//lines/line')) { - $totalLines++ - if ([int]$line.hits -gt 0) { - $coveredLines++ - } - - if ($line.branch -eq 'True' -and $line.HasAttribute('condition-coverage') -and $line.'condition-coverage' -match '\(([0-9]+)/([0-9]+)\)') { - $coveredBranches += [int]$Matches[1] - $totalBranches += [int]$Matches[2] - } - } + $classSummary = Get-CoberturaClassLineSummary -ClassNode $cls + $totalLines += $classSummary.TotalLines + $coveredLines += $classSummary.CoveredLines + $totalBranches += $classSummary.TotalBranches + $coveredBranches += $classSummary.CoveredBranches } } @@ -164,6 +158,106 @@ function Get-CoberturaLineConditionCoverageParts { } } +function Get-CoberturaClassLineSummary { + <# + .SYNOPSIS + Reduces one Cobertura element to a deduplicated per-line summary. + + .DESCRIPTION + Cobertura repeats every source line under // and again in the + class-level rollup, so counting over the descendant axis counts each line twice + (issue #441). This helper enumerates the class-level rollup first and then the method-level + view, keys by line number, and resolves a repeated key by taking the maximum hits value, + treating the line as a branch if either entry is a branch, and retaining the + condition-coverage of the entry with the larger denominator (ties broken by the larger + covered count). The precedence rule is the one already used by the merge path and is + evaluated through Get-CoberturaLineConditionCoverageParts rather than re-derived. + + The function is pure: it performs no I/O and mutates nothing in the source document. + + .PARAMETER ClassNode + A Cobertura element. A class with no element, no element, or + neither is valid input and yields TotalLines = 0. + + .OUTPUTS + A pscustomobject carrying LineMap, TotalLines, CoveredLines, TotalBranches and + CoveredBranches. + #> + [CmdletBinding()] + [OutputType([pscustomobject])] + param( + [Parameter(Mandatory = $true)] + [System.Xml.XmlElement]$ClassNode + ) + + $lineMap = @{} + + # Class-level rollup first, then the method-level view of the same lines. + $lineNodes = @($ClassNode.SelectNodes('./lines/line')) + + @($ClassNode.SelectNodes('./methods/method/lines/line')) + + foreach ($lineNode in $lineNodes) { + $lineNumber = [int]$lineNode.GetAttribute('number') + # GetAttribute is used rather than bare property access because Set-StrictMode -Version + # Latest makes a missing XML attribute throw instead of returning $null. + $hits = [int]$lineNode.GetAttribute('hits') + $isBranch = $lineNode.GetAttribute('branch') -eq 'True' + $candidateCoverage = Get-CoberturaLineConditionCoverageParts -LineNode $lineNode + + if (-not $lineMap.Contains($lineNumber)) { + $lineMap[$lineNumber] = [pscustomobject]@{ + Node = $lineNode + Hits = $hits + Branch = $isBranch + Covered = $candidateCoverage.Covered + Total = $candidateCoverage.Total + } + continue + } + + $existing = $lineMap[$lineNumber] + if ($hits -gt $existing.Hits) { + $existing.Hits = $hits + } + + if ($isBranch) { + $existing.Branch = $true + } + + if ( + $candidateCoverage.Total -gt $existing.Total -or + ($candidateCoverage.Total -eq $existing.Total -and $candidateCoverage.Covered -gt $existing.Covered) + ) { + $existing.Node = $lineNode + $existing.Covered = $candidateCoverage.Covered + $existing.Total = $candidateCoverage.Total + } + } + + $coveredLines = 0 + $totalBranches = 0 + $coveredBranches = 0 + + foreach ($entry in $lineMap.Values) { + if ($entry.Hits -gt 0) { + $coveredLines++ + } + + if ($entry.Branch) { + $totalBranches += $entry.Total + $coveredBranches += $entry.Covered + } + } + + [pscustomobject]@{ + LineMap = $lineMap + TotalLines = $lineMap.Count + CoveredLines = $coveredLines + TotalBranches = $totalBranches + CoveredBranches = $coveredBranches + } +} + function Merge-CoberturaClassesByFilename { [CmdletBinding()] param( @@ -267,13 +361,17 @@ function Merge-CoberturaClassesByFilename { [void]$linesNode.AppendChild($lineMap[$lineNumber].Node) } - $classSummaryXml = [xml]"" - $classSummaryClasses = $classSummaryXml.SelectSingleNode('//classes') - [void]$classSummaryClasses.AppendChild($classSummaryXml.ImportNode($mergedClassNode, $true)) - $classSummary = Get-CoberturaCoverageSummary -XmlDocument $classSummaryXml + $mergedSummary = Get-CoberturaClassLineSummary -ClassNode $mergedClassNode + + # The rate expressions below are duplicated from Get-CoberturaCoverageSummary rather + # than shared through a second helper: the spec specifies exactly one new helper, and + # existing assertions such as line-rate | Should -Be '1' depend on this rounding and on + # the '0' zero-denominator fallback matching that function exactly. + $mergedLineRate = if ($mergedSummary.TotalLines -gt 0) { [string]([math]::Round($mergedSummary.CoveredLines / $mergedSummary.TotalLines, 6)) } else { '0' } + $mergedBranchRate = if ($mergedSummary.TotalBranches -gt 0) { [string]([math]::Round($mergedSummary.CoveredBranches / $mergedSummary.TotalBranches, 6)) } else { '0' } - $mergedClassNode.SetAttribute('line-rate', $classSummary.LineRate) - $mergedClassNode.SetAttribute('branch-rate', $classSummary.BranchRate) + $mergedClassNode.SetAttribute('line-rate', $mergedLineRate) + $mergedClassNode.SetAttribute('branch-rate', $mergedBranchRate) $mergedClassNode.SetAttribute('complexity', [string]( ($group | ForEach-Object { if ($_.complexity) { [double]$_.complexity } else { 0d } diff --git a/tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 b/tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 index 9ade8aefb..01fc714e3 100644 --- a/tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 +++ b/tests/scripts/vscode/Invoke-MSTestWithCoverage.Helpers.Tests.ps1 @@ -180,6 +180,183 @@ Describe 'ConvertTo-KoverageCoberturaXml' { $resultXml.coverage.'lines-covered' | Should -Be '1' $resultXml.coverage.'lines-valid' | Should -Be '2' } + + It 'counts each source line once when methods repeat the class-level rollup' { + # Regression for Issue #441. Assert counts, not the rate: the rate does not discriminate. + $inputXml = @' + + + + + + + + + + + + + +'@ + + [xml]$resultXml = ConvertTo-KoverageCoberturaXml -XmlContent $inputXml -RepoRoot 'C:\repo' -PathSeparator '\' -ProjectNames @('Ns') + + $resultXml.coverage.'lines-valid' | Should -Be '3' + $resultXml.coverage.'lines-covered' | Should -Be '2' + $resultXml.coverage.'line-rate' | Should -Be '0.666667' + } + + It 'counts each branch line once when methods repeat the class-level rollup' { + # Regression for Issue #441 branch arithmetic. The branch RATIO is unchanged by the + # double count, so this must assert branches-valid/branches-covered, never branch-rate. + $inputXml = @' + + + + + + + + + + + + + + + + + +'@ + + [xml]$resultXml = ConvertTo-KoverageCoberturaXml -XmlContent $inputXml -RepoRoot 'C:\repo' -PathSeparator '\' -ProjectNames @('Ns') + + $resultXml.coverage.'branches-valid' | Should -Be '2' + $resultXml.coverage.'branches-covered' | Should -Be '1' + } + + It 'computes the merged per-file line-rate from the merged rollup alone' { + # Regression for Issue #478: miniature of the confirmed QfcHomeController.Iteration.cs + # case. The merged rate must be 3/5 = 0.6, not the blended 6/8 = 0.75. + $inputXml = @' + + + + + + + + + + + + + + + + + + + + +'@ + + [xml]$resultXml = ConvertTo-KoverageCoberturaXml -XmlContent $inputXml -RepoRoot 'C:\repo' -PathSeparator '\' -ProjectNames @('Ns') + $mergedClass = $resultXml.SelectSingleNode('//class[@filename="Ns\Foo.cs"]') + $mergedLines = @($mergedClass.SelectNodes('./lines/line')) + + $mergedClass.'line-rate' | Should -Be '0.6' + $mergedLines.Count | Should -Be 5 + (@($mergedLines | ForEach-Object { $_.number }) -join ',') | Should -Be '12,13,56,57,58' + } + + It 'deduplicates a repeated line number by taking the maximum hits value' { + # Line 5 appears in two constructor overloads with differing hits and once in the + # class-level rollup. It must count exactly once, and as covered. + $inputXml = @' + + + + + + + + + + + +'@ + + [xml]$resultXml = ConvertTo-KoverageCoberturaXml -XmlContent $inputXml -RepoRoot 'C:\repo' -PathSeparator '\' -ProjectNames @('Ns') + + $resultXml.coverage.'lines-valid' | Should -Be '1' + $resultXml.coverage.'lines-covered' | Should -Be '1' + } + + It 'retains method-level lines when the class-level rollup element is absent' { + # Guard: a bare child-axis switch would silently drop these two lines. Passes both + # before and after the fix, and pins the union behaviour against that regression. + $inputXml = @' + + + + + + + + +'@ + + [xml]$resultXml = ConvertTo-KoverageCoberturaXml -XmlContent $inputXml -RepoRoot 'C:\repo' -PathSeparator '\' -ProjectNames @('Ns') + + $resultXml.coverage.'lines-valid' | Should -Be '2' + $resultXml.coverage.'lines-covered' | Should -Be '1' + } + + It 'preserves the primary class methods subtree and every hits value when merging' { + # Locks the decision not to merge or strip . Reuses the F3 document. + $inputXml = @' + + + + + + + + + + + + + + + + + + + + +'@ + + [xml]$resultXml = ConvertTo-KoverageCoberturaXml -XmlContent $inputXml -RepoRoot 'C:\repo' -PathSeparator '\' -ProjectNames @('Ns') + $mergedClass = $resultXml.SelectSingleNode('//class[@filename="Ns\Foo.cs"]') + $methodNodes = @($mergedClass.SelectNodes('./methods/method')) + $hitsByLine = @($mergedClass.SelectNodes('./lines/line')) | ForEach-Object { '{0}={1}' -f $_.number, $_.hits } + + $methodNodes.Count | Should -Be 1 + $methodNodes[0].name | Should -Be 'M' + ($hitsByLine -join ',') | Should -Be '12=0,13=0,56=1,57=1,58=1' + } + + It 'still throws when the document has no packages node' { + # Error handling: the existing guard must survive the arithmetic rewrite verbatim. + [xml]$doc = @' + +'@ + + { Get-CoberturaCoverageSummary -XmlDocument $doc } | + Should -Throw 'Cobertura XML does not contain a node.' + } } Describe 'Get-KoverageProjectAllowlist' { @@ -220,3 +397,72 @@ Describe 'Get-KoverageProjectAllowlist' { $allowlist | Should -Not -Contain 'Sample.Test' } } + +Describe 'Get-CoberturaClassLineSummary' { + It 'retains the candidate condition-coverage when its total is greater' { + # Precedence branch 1: the method-level denominator (4) exceeds the class-level one (2). + [xml]$doc = @' + + + + + + +'@ + + $summary = Get-CoberturaClassLineSummary -ClassNode $doc.SelectSingleNode('//class') + + $summary.TotalBranches | Should -Be 4 + $summary.CoveredBranches | Should -Be 2 + } + + It 'retains the candidate condition-coverage when totals tie and its covered count is greater' { + # Precedence branch 2: equal denominators (2), method-level covered (1) beats class-level (0). + [xml]$doc = @' + + + + + + +'@ + + $summary = Get-CoberturaClassLineSummary -ClassNode $doc.SelectSingleNode('//class') + + $summary.TotalBranches | Should -Be 2 + $summary.CoveredBranches | Should -Be 1 + } + + It 'retains the existing condition-coverage when neither precedence condition holds' { + # Precedence branch 3: method-level total (2) is smaller, so the class-level 2/4 is kept. + [xml]$doc = @' + + + + + + +'@ + + $summary = Get-CoberturaClassLineSummary -ClassNode $doc.SelectSingleNode('//class') + + $summary.TotalBranches | Should -Be 4 + $summary.CoveredBranches | Should -Be 2 + } + + It 'returns zero totals for a class with neither a lines nor a methods element' { + # Boundary: valid input per the helper contract; must yield zeros and must not throw. + [xml]$doc = @' + +'@ + $classNode = $doc.SelectSingleNode('//class') + + { Get-CoberturaClassLineSummary -ClassNode $classNode } | Should -Not -Throw + $summary = Get-CoberturaClassLineSummary -ClassNode $classNode + + $summary.TotalLines | Should -Be 0 + $summary.CoveredLines | Should -Be 0 + $summary.TotalBranches | Should -Be 0 + $summary.CoveredBranches | Should -Be 0 + } +}