Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/agent-memory/atomic-executor/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@
- [poshqc analyze exits 1 on a Warning](project_poshqc_analyze_exit1_on_warning.md) — "EXIT_CODE 0 with zero error-severity" is self-contradictory; Helpers.ps1 carries a pre-existing PSUseSingularNouns; `Remove-*` needs SupportsShouldProcess
- [BOM breaks grep ^ anchor](project_bom_grep_anchor_false_negative.md) — bash grep `^#nullable` misses BOM-prefixed files; use the Grep tool for opt-in classification, never bash grep
- [StrictMode Latest + missing XML attribute throws](project_pester_strictmode_xml_attribute_property_access.md) — a fixture omitting `branch` (or `complexity` on a merge-path `<class>`) throws PropertyNotFoundStrict instead of the assertion diff; enumerate ALL bare `$node.attr` reads on the traversed path, not one attribute at a time
- [Pester 5 result shape: no container .Tests, no -CI + -CodeCoverage](project_pester5_result_shape_container_tests_and_ci_codecoverage.md) — `$_.Tests.Count` silently renders 0 per file (use `TotalCount`); `-CI` and `-CodeCoverage` are different parameter sets and cannot be combined
- [pwsh -Command needs single-quoted outer](project_pwsh_command_quoting_from_bash.md) — a double-quoted outer wrapper lets bash eat `$` → empty counts + inverted exit gates or a hard ParserError; `''` inside single quotes is not an escape

- [Compile-time red needs body-level refs](project_compile_red_needs_body_level_references.md) — a missing type in a method SIGNATURE suppresses body binding, so an `[expect-fail]` task requiring N named CS0246s reports only 1; construct the types inline in test bodies

## Test execution & isolation
- [Tests must mock GUI; no visible window](feedback_tests_must_mock_gui_no_visible_window.md) — use headless seams (mocked viewers, injected show/focus delegates), never Form.Show/Application.Run
- [#511 is a test-host crash, not N failing tests](project_511_is_a_testhost_crash_not_n_failing_tests.md) — load-driven abort with `Total tests: Unknown` (no readable verdict); `/InIsolation` loop gave 0 failed; never gate on a pinned failing-name count
- [WinFormsPumpHost tests are load-flaky](project_winformspumphost_tests_load_flaky.md) — QfcItemController_InitializationTests fail with "window handle has been created"/60s timeouts when the box is CPU-saturated; re-run when load drops, don't treat as a red baseline
- [vstest /InIsolation + FilePathHelper serialization](project_vstest_isolation_and_filepathhelper_serialization.md) — Moq assemblies need /InIsolation (else STTE Setup FileNotFound); FilePathHelper.FilePath is "" default but null after JSON deserialize
- [Invoke-MSTest.ps1 dies on a single test assembly](project_418_invoke_mstest_single_assembly_bug.md) — StrictMode + `.Count` on a scalar String throws before vstest runs; call vstest.console.exe directly with the script's arg list
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: 511-is-a-testhost-crash-not-n-failing-tests
description: Issue #511 is a load-driven test-host crash with "Total tests: Unknown", not a fixed set of failing tests; plans that pin a failing-name count cannot be evaluated
metadata:
type: project
---

Issue #511 on the 9-assembly single-process coverage run is an **intermittent test-host crash**, not a
deterministic set of failing tests. The authoritative in-repo record is
`docs/features/active/2026-08-10-cobertura-coverage-arithmetic-441/research/2026-08-10T14-20-cobertura-arithmetic-research.md:738-752`
and `.../441/spec.md:569-579`:

- symptom: `The active test run was aborted. Reason: Test host process crashed` / `Test Run Aborted. Total tests: Unknown`
- character: environmental, load-driven, concentrated in the `QuickFiler.Test` `WinFormsPumpHost`
message-pump family — explicitly "**not a test failure**"
- `Total tests: Unknown` means **no verdict can be read from the run**
- documented recovery: loop the 9 assemblies through `vstest.console.exe <dll> /InIsolation`, which on
#505 produced **6435 passed, 1 skipped, 0 failed**; that loop yields nine separate `.coverage` files
that need a `dotnet-coverage merge` before a repository-wide Cobertura figure exists

**Why:** a plan asserted "#511 leaves two `*ThroughThePumpHost*` MSTest cases failing", made a
per-failing-name #511 determination a gate, and declared "any third failing name" a halt. There are five
`*ThroughThePumpHost*` test methods, the two names are enumerated nowhere, and the real failure mode
produces zero named failures plus an unreadable total.
**How to apply:** never gate on a pinned count of #511 failures. Gate on the failing-name **pattern**,
require an artifact-existence check before reading the Cobertura file, and give the abort case an
explicit branch (re-run, or the `/InIsolation` + merge recovery). Related:
[[project_winformspumphost_tests_load_flaky]], [[project_timedout_mstest_leaves_detached_runner]].
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
name: pester5-result-shape-container-tests-and-ci-codecoverage
description: Pester 5.6.1 container objects have no Tests property (so $_.Tests.Count is silently 0) and Invoke-Pester -CI cannot be combined with -CodeCoverage
metadata:
type: project
---

Two verified facts about direct `Invoke-Pester` (Pester 5.6.1, the version installed here) that turn
plausible-looking gates into gates that can never pass:

1. A container result object exposes
`Name,Type,Item,Data,Blocks,Result,Duration,FailedCount,PassedCount,SkippedCount,InconclusiveCount,NotRunCount,TotalCount,ErrorRecord,...`
— there is **no** `Tests` property. `"$($_.Tests.Count)"` therefore renders `0` for every file even
when tests ran and passed (no StrictMode error, because the run happens in a plain `-Command` scope).
Use `$_.TotalCount` (and `$_.FailedCount`) for the per-file inventory, or group the flattened
`$r.Tests` by `$_.ScriptBlock.File`.
2. `-CodeCoverage` lives only in the **Legacy** parameter set; `-CI` lives in **Simple**. Combining them
fails with "Parameter set cannot be resolved using the specified named parameters." For a scoped
per-file coverage figure, use a configuration object (`$c.CodeCoverage.Path = @('<one file>')`) and
read `$r.CodeCoverage.CoveragePercent`.

Also confirmed: `$r.CodeCoverage.CoveragePercent` is real; `$_.Item.FullName` is real; Pester discovers
test files under a dot-prefixed parent such as `tests/.claude/hooks/` without `-Force`; and the second
figure in the "Covered X% / Y%" console line is `CoveragePercentTarget`, not a branch metric.

**Why:** an atomic plan required an "executed-file inventory with a non-zero test count per named file"
and a `-CI -CodeCoverage` fallback; both were unsatisfiable as written across three tasks.
**How to apply:** before accepting any plan clause that reads a number off a Pester result object, run
the expression once against a throwaway fixture. See [[project_poshqc_pester_mcp_exit_minus1]] for the
MCP-route counterpart (no counts, no exit code at all).
3 changes: 2 additions & 1 deletion .claude/agent-memory/atomic-planner/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
- [Named coverage exception: verify the member body](named-coverage-exception-verify-member-body.md) — read the member before writing "untestable branch"; put gap-closure BEFORE the toolchain-clean-pass task; pin line-rate vs branch-rate
- [Acceptance edits must be false-before/true-after](acceptance-edits-must-be-false-before-true-after.md) — a revised acceptance clause already true at branch head is a no-op gate (#494 P3-T7); anchor a region by the heading that OPENS it, not the next one
- [Zero-hit grep gates need carve-outs](zero-hit-grep-gates-need-carveouts.md) — denial-sentence replacement text and non-coverage numerals (mutation 75%, determinism, format 100%) make "grep returns no hits" unsatisfiable by construction
- [PoshQC MCP + msbuild invocation facts](poshqc-mcp-and-msbuild-invocation-facts.md) — run_poshqc_test takes only workspace_root/scan_folders; can exit -1 while "available"; hidden-parent test dirs may never be collected; msbuild needs vswhere
- [PoshQC MCP + msbuild invocation facts](poshqc-mcp-and-msbuild-invocation-facts.md) — MCP tools return no counts/severities/coverage (#536 zero-line artifact); pair unconditionally with direct runs, never "fallback"; Invoke-VSBuild DOES take -Target Rebuild
- [#494 threshold reconciliation plan seams](project_494_threshold_reconciliation_plan_seams.md) — coverage runner throws before Koverage post-processing (out-of-band per-run rewrite); reported-and-tracked floor must not become hook-Blocking; dangling-citation dispositions valid
- [Never plan a mid-plan halt on MCP availability](never-plan-a-mid-plan-halt-on-mcp-availability.md) — executor and orchestrator tool surfaces differ; Phase 0 probe + record-blocker-and-continue, never "halt", never a different route
- [Single-numeral gates must name the role](single-numeral-gates-must-name-the-role.md) — count "enforced repo-wide line floor compared against in executable code", enumerate doc/policy-constant occurrences; inventory patterns need prose alternations
- [Thread granted discharges through consumers](thread-granted-discharges-through-consumers.md) — softening one task's measurement clause without the intermediate producer task makes the discharge unreachable and the middle task uncheckable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
---
name: poshqc-mcp-and-msbuild-invocation-facts
description: "PoshQC MCP test tool takes only workspace_root/scan_folders (no runsettings path), Pester discovery misses hidden-parent test dirs, and msbuild must be vswhere-resolved because Invoke-VSBuild.ps1 hard-codes /t:Build"
description: "PoshQC MCP tools return no counts/coverage/severities (pair unconditionally with direct runs, never a 'fallback'); Invoke-VSBuild.ps1 DOES support -Target Rebuild; hidden-parent test dirs may never be collected"
metadata:
type: reference
---

Command-shape facts that make PowerShell/C# plan tasks executable in this repo.

- **`mcp__drm-copilot__run_poshqc_test` accepts only `workspace_root` and `scan_folders`.** It supplies its
own bundled Pester settings. `scripts/powershell/PoshQC/settings/pester.runsettings.psd1` is a *bundled
extension resource* cited by `.claude/rules/powershell.md:18`; `scripts/powershell/` does not exist in this
repository, so naming that path as a config argument in a plan task is a defect.
- **"Available" is not "working."** `run_poshqc_test` can terminate with exit `-1` and no per-test detail while
remaining callable. A fallback authorization worded "if the MCP tool is *unavailable*" therefore leaves a
final-QC task demanding `EXIT_CODE: 0` with no non-SKIPPED completion path. Word every MCP fallback trigger as
"unavailable **or** returns a non-zero/negative exit code without per-test diagnostic detail", require both the
MCP attempt and the fallback to be recorded with their exit codes, and bind the `EXIT_CODE: 0` requirement to
*the route that produced the reported figures*.
- **Test-file discovery is not guaranteed.** `config/poshqc-scan.json` does not exist here and every existing
Pester file lives under `tests/scripts/vscode/`. A new test file under a hidden-parent directory
(e.g. `tests/.claude/hooks/`) may never be collected, so a green suite proves nothing. Any suite-run task
must require the artifact to *enumerate executed test files* with a non-zero test count each, and to record
which discovery route was used (default scan set vs explicit `scan_folders: tests`).
- **`msbuild` is not on `PATH`.** `scripts/vscode/Invoke-VSBuild.ps1` hard-codes `/t:Build` at line 64 and
exposes no target parameter, so it cannot deliver `/t:Rebuild`. Resolve MSBuild the way that wrapper does at
lines 127-134: `& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires
Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1`. A rebuild task
should also require a non-zero `CoreCompile` project count so an up-to-date no-op cannot pass as a build.
- **`mcp__drm-copilot__run_poshqc_test` accepts only `workspace_root` and `scan_folders`** and its payload is
exactly `{ok, tool, workspace_root, summary}` — no exit code, no pass/fail counts, no per-test names, no
executed-file inventory, no coverage figure. It returns `ok: true` even with no `config/poshqc-scan.json`
present. `scripts/powershell/PoshQC/settings/pester.runsettings.psd1` is a *bundled extension resource*
cited by `.claude/rules/powershell.md:17` (not 18); `scripts/powershell/` does not exist in this repository.
- **Never write an MCP "fallback" trigger — write an unconditional pairing.** A fallback gated on
"unavailable or non-zero exit without diagnostics" is unreachable: the tool returns `ok: true` with no data,
so the trigger never fires while every required number is missing (#494 preflight blocking finding B3). The
correct plan shape: run the MCP tool unconditionally for the `.claude/rules/powershell.md` step 4 policy
record (EXIT_CODE 0/1 from `ok`), and unconditionally pair it with a direct `Invoke-Pester` run
(`New-PesterConfiguration`, `Run.PassThru`, `CodeCoverage.OutputFormat = "JaCoCo"`) that supplies the numbers
and a `FILE=`/`TESTS=` executed-file inventory via `$r.Containers`. Prefix with
`New-Item -ItemType Directory -Force -Path artifacts/pester | Out-Null` — Pester's JaCoCo writer does not
create parent directories.
- **Issue #536: the MCP-written `artifacts/pester/powershell-coverage.xml` reports zero covered lines
repository-wide.** Any figure read from it is false, and a "no regression" claim computed 0% → 0% from it is
vacuous. The direct run overwrites that same path with truthful figures; coverage-delta tasks must name the
direct-route artifacts as the comparison source.
- **`mcp__drm-copilot__run_poshqc_analyze` reports only a total issue count** — no rule names, files, or
severities — and exits 1 on any Warning. `scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1` carries a
pre-existing unsuppressed `PSUseSingularNouns` warning, so baseline `EXIT_CODE: 1` is legitimate. Severity /
rule-by-rule gates need a paired direct `Invoke-ScriptAnalyzer -Path . -Recurse` run (exclude
`\.claude\worktrees\` paths) and the gate must be the diagnostic-set diff, not the exit code.
- **`PSUseSingularNouns` is active** — a new plural-named function (e.g. `Get-CoberturaCoverageRates`) emits a
new diagnostic and fails a zero-new-diagnostics final gate. Plan the suppression up front using the pattern
at `.claude/hooks/enforce-pr-author-skill.ps1:78`, and have the authoring task record it so final lint can
classify it as authored. New `.ps1` files also need a UTF-8 BOM (`PSUseBOMForUnicodeEncodedFile`).
- **Test-file discovery is not guaranteed.** `config/poshqc-scan.json` does not exist here and pre-existing
Pester files live under `tests/scripts/vscode/` — there are **five** (`Install-RepoDotNetSdk`,
`Invoke-MSTest.RunSettings`, `Invoke-MSTestWithCoverage.ClosureFilter`, `Invoke-MSTestWithCoverage.Helpers`,
`Invoke-VSBuild`), not four; spec prose that says "four" undercounts. A new test file under a hidden-parent
directory (e.g. `tests/.claude/hooks/`) may never be collected, so suite tasks must require the artifact to
enumerate executed files by name with non-zero test counts.
- **`msbuild` is not on `PATH`, but `scripts/vscode/Invoke-VSBuild.ps1` DOES support rebuild** — script-level
`[string]$Target = 'Build'` with `ValidateSet('Build','Rebuild')` at line 13, interpolated `"/t:$Target"` at
line 73, passed through at 158, vswhere resolution at 137-142. (An earlier version of this memory claimed it
hard-coded `/t:Build`; that claim was refuted by the #494 preflight — verify wrapper capabilities before
asserting absence.) Prefer the wrapper (`-Target Rebuild`) over hand-rolled vswhere resolution per
`policy-compliance-order`'s "prefer repo-defined tasks/commands". Rebuild tasks should require a non-zero
`CoreCompile` project count so an up-to-date no-op cannot pass.
- **`mcp__drm-copilot__potential_to_issue` requires `potential_path`** pointing at an existing
`docs/features/potential/*.md`. A plan that files follow-up issues must include a task that *authors* those
potential entries first, or the promotion call has no input. See [[feature-promotion-lifecycle]] usage in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: project_494_threshold_reconciliation_plan_seams
description: "#494 revision-pass seams: coverage runner throws before Koverage post-processing (out-of-band ConvertTo-KoverageCoberturaXml per run), reported-and-tracked floor must not become hook-Blocking, dangling-citation dispositions are valid, D8 wants committed producer not artifact"
metadata:
type: project
---

Plan seams from the #494 coverage-threshold-policy-reconciliation revision pass (2026-08-11), epic
`build-ci-coverage-gate-fidelity` wave 2. Twelve blocking findings, all instances of "a gate that cannot
fail or cannot pass".

- **`Invoke-MSTestWithCoverage.ps1` throws at line 236 on non-zero vstest exit, BEFORE the
`ConvertTo-KoverageCoberturaXml` post-processing at 326-342.** With #511's two pre-existing
`*ThroughThePumpHost*` failures, every run exits non-zero, so the on-disk Cobertura is the RAW artifact —
it has root `line-rate`/`lines-valid` attributes (satisfying a naive acceptance) but pre-#441/#478/#457
arithmetic. Remeasurement tasks must apply the post-processing out of band per run
(`. ./scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1; ConvertTo-KoverageCoberturaXml -XmlContent
... -RepoRoot ...`), read root attributes AFTER it, name every failing test with a per-name #511
determination (a third name halts), and treat non-zero exit as expected. The runner hard-codes
`/TestCaseFilter:TestCategory!=LiveOutlook` at line 76 and exposes no filter parameter.
**Why:** the poisoned figure trips no acceptance criterion — the classic silently-vacuous gate.
**How to apply:** any plan measuring C# coverage on a branch with known failing tests.
- **Do not make the repository-wide floor Blocking in the hook.** Spec D5 / Appendix A5 semantics: below-floor
with artifact present fails ONLY when no coverage row in the policy audit carries a FAIL verdict; only the
artifact-absent/malformed path is unconditionally fail-closed. Wording the below-floor clause as itself
blocking contradicts the authority text this feature installs (found as B5). Hook tests need all four cases:
absent artifact, branch-below-line-above, below-floor-with-FAIL-verdict (passing), below-floor-without (failing).
- **A knowingly-dangling citation with a recorded disposition is a valid outcome.** `.claude/rules/powershell.md:63`
cites quality-tiers 85% content this feature deletes, but that file is out of edit scope (deferred FU-A). The
acceptance must scope "no hit points at deleted content" to the authorized edit path list and record the
dangling citation as `deferred to FU-A` resolved interim by the authority conflict-resolution rule.
- **`spec.md` D8 item 1 requires a committed *producer*, not a committed *artifact*** — both post-change
coverage artifact paths (`coverage/*`, `artifacts/*`) are gitignored. `.NOTES` producer inventories must
cover every path the hook reads post-edit (four, incl. TypeScript/Python `NO PRODUCER` rows that fail closed
by design).
- **Dot-source guard polarity:** the repo pattern is `if ($MyInvocation.InvocationName -ne '.')`
(`Invoke-MSTestWithCoverage.ps1:346`) — `-eq` inverts it and breaks every `BeforeAll` dot-source.
- **A supplied delta can itself be arithmetically wrong:** the A4 carve-out said "nineteen [x] boxes plus
AC10 [ ] in both files" — 20 total minus 2 unchecked is eighteen. Applied as eighteen and reported the
deviation. Check delta arithmetic before transcribing.
- CLAUDE.md spans in this worktree: § UT2 298-313 (Scenario Completeness 314), C#1 181-217, CUT3 383-395,
C# Toolchain 403-411 (trailing sentence 410, Key Skills Reference 412). feature-review.md:126-128 holds the
literal prose forms ("If repo-wide coverage is below 80%...", "if line coverage is below 90%...",
"or is below 80%").
Loading
Loading