Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .claude/agent-memory/atomic-executor/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
- [Start-Process -ArgumentList strips quoting](project_startprocess_arglist_array_strips_quoting.md) · [Relative paths in pwsh hit the wrong worktree](project_relative_path_in_pwsh_dotnet_io_hits_wrong_worktree.md)
- [QuickFiler.Test coverage hang + build flags](project_quickfiler_test_coverage_hang_and_build_flags.md) — testhost can hang
- [Dot-sourcing Invoke-MSTestWithCoverage clobbers $CoverageOutput](project_dotsourcing_invoke_mstest_clobbers_coverageoutput_param.md) — param-block default wins; Cobertura lands at coverage\coverage.cobertura.xml, exit 0
- [vstest TestCaseFilter needs `|` not OR](project_vstest_testcasefilter_or_operator_and_env_setup.md) · [Test file name != partial class name](project_test_file_name_vs_partial_class_name.md)
- [vstest TestCaseFilter: `|` not OR, and `&` binds tighter](project_vstest_testcasefilter_or_operator_and_env_setup.md) · [Test file name != partial class name](project_test_file_name_vs_partial_class_name.md)
- [Analyzer HintPath skew breaks all four gates](project_analyzer_hintpath_skew_breaks_all_four_gates.md) · [Analyzer version skew on fresh worktree](project_analyzer_version_skew_fresh_worktree.md) — CS0006
- [SecurityCodeScan incompatible with Roslyn 5.6](project_securitycodescan_roslyn56_incompat.md) · [Missing VSTO runtime breaks baseline gates](project_missing_vsto_runtime_breaks_baseline_gates.md) — HISTORICAL
- [New sln member surfaces MSB3277](project_new_sln_member_surfaces_msb3277_pin_divergence.md) · [Legacy csproj: no transitive compile refs](project_legacy_csproj_no_transitive_compile_refs.md) — CS0012
Expand Down Expand Up @@ -79,7 +79,7 @@
- [C# canonical coverage artifact conversion](project_csharp_canonical_coverage_artifact_conversion.md) · [Cobertura runsettings `<Attributes>` override](project_cobertura_runsettings_attributes_override.md)
- [Package rollup must use the repo helper](project_cobertura_package_rollup_must_use_repo_helper.md) — a hand-written class-direct node count never equals the root attributes
- [Processed Cobertura filenames use backslashes](project_processed_cobertura_filenames_use_backslash.md) — forward-slash match returns zero rows; gate unevaluable
- [Cobertura hits vs MS-coverage partial](project_changed_line_coverage_cobertura_vs_mscoverage_partial.md) · [QFC #227 coverage tooling](project_qfc227_coverage_tooling.md)
- [Cobertura hits vs MS-coverage partial; non-executable changed lines have no hits](project_changed_line_coverage_cobertura_vs_mscoverage_partial.md) · [QFC #227 coverage tooling](project_qfc227_coverage_tooling.md)
- [#398 test-split gate gotchas](project_398_test_split_gate_gotchas.md) · [ExcludeFromCodeCoverage on partial = CS0579](project_excludefromcodecoverage_partial_class_cs0579.md)
- Closed one-offs: [#400](project_400_completeopenasync_unreachable_recovery_catch.md), [Swordfish](project_swordfish_removal_epic_incidental_coverage_sideeffect.md), [#298](project_taskvis_scocollection_and_livebridge_exemptions.md), [#328](project_328_rebuild_threading_olobjectsproxy_conflict.md)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ When proving a specific new/changed line meets the CLAUDE.md/csharp.md >= 90% ne

**How to apply:** For a precise per-line changed-line coverage proof, convert the same `.coverage` file with `dotnet-coverage merge -f cobertura` (global tool, not `dotnet tool run`) instead of (or in addition to) `Microsoft.CodeCoverage.Console.exe`. Cobertura's `<line number="N" hits="H">` reports `hits=1` (covered) for a line executed at least once, regardless of whether all its branches were taken — so the null-guard assignment line correctly shows as covered. Locate the exact `<class filename="...">` block for the changed production file (grep for the class-open/`</class>` line-number bracket, since large solutions produce a huge single XML with duplicate line numbers across many classes/modules), then check hits for the specific line numbers touched by the diff. Report BOTH figures when writing coverage-delta evidence: the MS-coverage-XML aggregate (for the class-level baseline-vs-post-change comparison) and the Cobertura per-line hit data (for the changed-line-specific >= 90% claim), explaining the partial-vs-hit distinction so a reviewer does not mistake "partially covered" for "under-tested."

**Non-executable changed lines have no `hits` value at all.** Cobertura emits a `<line>` element only for a line that carries IL. XML doc comments, blank lines, `using` directives, braces, enum members and interface method declarations therefore appear in a `git diff --unified=0` changed-line set but in no branch of the per-line map. A changed-line-coverage gate worded "every changed line is recorded with a `hits` value" is over-demanding for those lines, and an executor that fills them in as `hits = 0` inflates the uncovered-line count that the coverage argument rests on. Give such lines their own marker (`hits=non-executable`) and exclude them from both the `hits = 0` count and the regression count — the same shape as the `baseline=none` marker used for a hunk whose added and removed counts are unequal. The extreme case is an interface-only or enum-only file: every changed line in it can be non-executable, so the file yields no coverage datum even when the coverage document does contain a class element for it.

See also [[project_coverage_firstparty_denominator_method]] and [[project_qfc227_coverage_tooling]] for other coverage-tooling conventions in this repo.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: vstest-testcasefilter-or-operator-and-env-setup
description: vstest.console.exe 18.7.0 rejects literal "OR" in /TestCaseFilter (needs "|"); fresh worktree needs repo-local SDK install + NuGet restore before any MSBuild/vstest command works
description: vstest.console.exe 18.7.0 rejects literal "OR" in /TestCaseFilter (needs "|"), and "&" binds tighter than "|" so a category clause silently applies to only the first alternative; fresh worktree needs repo-local SDK install + NuGet restore before any MSBuild/vstest command works
metadata:
type: project
---
Expand All @@ -13,6 +13,8 @@ Two environment/tooling facts discovered during issue #244 execution that cost s

1. **`/TestCaseFilter` boolean operator.** This repo's vstest.console.exe (18.7.0, under `C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\`) does NOT accept the literal keyword `OR` between two `FullyQualifiedName~X` clauses — it prints `Incorrect format for TestCaseFilter Error: Invalid Condition` and then reports "No test matches" even for tests that exist. The correct operator is the pipe character: `FullyQualifiedName~A|FullyQualifiedName~B`. Verified by probing two known-existing test names: `OR` matched 0/2, `|` matched 2/2 and ran both. If a plan's task text literally specifies `OR`, substitute `|` when executing and document the substitution in evidence (same test-name targets, only the boolean-operator token differs) rather than treating the plan text as broken.

1b. **`&` binds tighter than `|` in a `/TestCaseFilter` expression, and there is no way to say otherwise in one flag.** A filter written `TestCategory!=LiveOutlook&FullyQualifiedName~A|FullyQualifiedName~B` parses as `(TestCategory!=LiveOutlook & FullyQualifiedName~A) | (FullyQualifiedName~B)`, so the category exclusion applies to the first alternative only and every `LiveOutlook` test in class B is silently selected. The filter grammar accepts parentheses in principle, but they are hostile to quote through PowerShell into a native exe, so the practical remedies are: (a) drop the category clause when the targeted classes provably declare no test in that category, and record the check (`Select-String -SimpleMatch 'TestCategory'` over those files returning 0) as the justification; or (b) run one invocation per alternative. A plan that combines a category clause with two or more `FullyQualifiedName` alternatives in a single filter has a defect even though the command exits 0 and runs tests — the wrong *set* of tests ran, which no exit code reveals. Preflight should read every multi-clause `/TestCaseFilter` for this shape.

2. **Fresh-worktree bootstrap order.** A brand-new git worktree of this repo has neither `.dotnet-sdk/` (global.json pins SDK 8.0.205 via a path-based `dotnet` shim that errors "repo-local .NET SDK is missing" until installed) nor `packages/` (legacy `packages.config` NuGet packages are not checked in). Before any `dotnet tool run csharpier ...`, `MSBuild`/`Invoke-VSBuild.ps1`, or `vstest.console.exe` command will succeed, run in this order:
- `pwsh -NoProfile -ExecutionPolicy Bypass -File ./scripts/vscode/Install-RepoDotNetSdk.ps1` (must be `pwsh` 7, not Windows PowerShell 5.1 — see [[project_repo_sdk_and_nullable_rebuild]]).
- `dotnet tool restore` — `Install-RepoDotNetSdk.ps1` does NOT do this. The manifest is at repo-root `dotnet-tools.json` (the legacy location, not `.config/dotnet-tools.json`; the SDK probes both) and pins csharpier `1.2.6`. Without it every `dotnet tool run csharpier check/format .` step fails.
Expand Down
1 change: 1 addition & 0 deletions .claude/agent-memory/atomic-planner/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- [#731](project_731_lifecycle_disposal_plan_seams.md) — Defects468Tests 498/500 forces a partial continuation; QfcQueue.cs already 505; spec said ONE comment line not three; R3 = all 4 defects in gate wiring; R4 = worktree not bootstrapped (no `.dotnet-sdk/`, no `packages/`), `DebugType=full` leaks host paths, 1:1 line mapping unsatisfiable for a 1-to-20 hunk; R5 = case-insensitive token absence collides with a retained method name, `.//line` double-counts on two axes, unconditional AC17 check-off vs two degraded branches; R6 = document state is DERIVED from exit code (`:236`/`:341` throw before the `:343` write), an exclusion keyed on the regression's own definition is vacuous, `line-rate` is a fraction not a percentage, unanchored filename suffix also selects `IQfcDatamodel.cs`
- [#781](project_781_excludefromcodecoverage_guard_plan_seams.md) — `[ExcludeFromCodeCoverage]` on the partial class makes changed-line coverage unmeasurable; `CaptureCurrent` voids a null-ambient success test; guard swap voids its own sibling comments; R1: `"Any CPU"` fails a project-file build, `<ref>...HEAD` can't see an uncommitted plan, no node axis reproduces Cobertura root counts
- [#735 R1](project_735_evidence_content_sanitization_seams.md) — name-only sanitization gate can't fail; TRX `runUser=`/`computerName=` leak in content; csproj "between" clause self-contradictory
- [#791](project_791_hc_deadline_cancel_teardown_plan_seams.md) — `QfcDatamodel` is `[ExcludeFromCodeCoverage]`; 7 gate tests break not the 4 the spec names; `IFilerFormController` forbids an optional param; R1: no shell var survives between tasks, `Glob`/`Grep` hide gitignored `packages/`, 500-line cap excludes `*.csproj` (524)

## Plan-structure traps

Expand Down
Loading
Loading