fix(coverage): scope the .claude discovery exclusion to paths relative to the search root (#752) - #776
Merged
drmoisan merged 1 commit intoSep 4, 2026
Conversation
…e to the search root (#752) Invoke-MSTestWithCoverage.ps1's test-assembly discovery predicate excluded every candidate assembly whenever the checkout running the script was itself located under .claude/worktrees/ (the normal case for every parallel-run and epic-run child worktree), because the predicate matched the candidate's absolute FullName for a \.claude\ segment. The fix matches the candidate path computed relative to the search root instead, so only a nested sibling worktree is excluded and the running checkout's own root is no longer self-excluded. Adds a new 3-case Pester regression suite covering the self-exclusion fix, the continued nested-sibling exclusion, and a double-nested edge case. Preserves the pre-existing regression test byte-for-byte unmodified. All 6 acceptance criteria in spec.md are met; full PowerShell toolchain (format, analyze, test) passes clean, independently reaudited with 0 blocking findings. This commit intentionally squashes the branch's prior commit history: an earlier commit in that history carried an absolute host path that leaked into committed research markdown during preparation and was later sanitized, but the pre-sanitization blob remained reachable through that earlier commit. Squashing to one commit is what keeps that content out of main, since this repository's branch protection on main permits only the ordinary merge method. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TzGiZSnVySFZcoC1BHN5Vv
drmoisan
force-pushed
the
bug/coverage-assembly-discovery-excludes-own-worktree-root-752
branch
from
September 4, 2026 04:23
e1b4856 to
68e9022
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Suggested title
fix(coverage): scope the
.claudediscovery exclusion to paths relative to the search rootSummary
scripts/vscode/Invoke-MSTestWithCoverage.ps1's test-assembly discovery predicate, which excluded every candidate assembly (not just sibling agent worktrees) whenever the checkout running the script was itself located under.claude/worktrees/— the normal case for every parallel-run and epic-run child worktree.FullNamefor a\.claude\segment.Get-ChildItem -Recursealways prefixes candidates with the search root, so when the search root itself contains.claude\worktrees\agent-<id>\, that segment appears in every candidate and the discovered assembly set is always empty, producing a misleadingNo test assemblies found ... Build first.error.[System.IO.Path]::GetRelativePath) using the anchored pattern(^|\\)\.claude\\, so only a.claudesegment that appears after the search root (a nested sibling worktree) is excluded — self-exclusion of the running checkout's own root no longer occurs.tests/scripts/vscode/Invoke-MSTestWithCoverage.AssemblyDiscovery.Tests.ps1, covering both the self-exclusion fix and the continued nested-sibling-worktree exclusion.tests/scripts/vscode/Invoke-MSTest.RunSettings.Tests.ps1:416-442byte-for-byte unmodified; it continues to pass.spec.mdare met; full PowerShell toolchain (PoshQC format → PoshQC analyze → Pester test) passes clean.Why
Item #733 (PR #748) added the
.claudeexclusion clause specifically to keep a sibling agent worktree nested beneath the search root out of test-assembly discovery when the wrapper script runs from the main checkout. That filter did not anticipate the wrapper script itself running from a checkout under.claude/worktrees/agent-<id>/, which is the standard topology for every parallel-orchestration and epic-orchestration child. In that topology the exclusion clause fired on every candidate unconditionally, breaking coverage collection for every C# item executed through the parallel/epic surfaces. CI is unaffected because CI runner checkouts never contain a.claudepath segment.What Changed
Core fix
scripts/vscode/Invoke-MSTestWithCoverage.ps1(Invoke-MSTestWithCoverageMain, 1 line): the.claudeexclusion clause now matches[System.IO.Path]::GetRelativePath($resolvedSearchRoot, $_.FullName)against(^|\\)\.claude\\instead of matching$_.FullNameagainst\\\.claude\\. The\bin\<Configuration>\,\obj\, and\ref\clauses are unchanged.Tests
tests/scripts/vscode/Invoke-MSTestWithCoverage.AssemblyDiscovery.Tests.ps1(new, 99 lines): exercisesInvoke-MSTestWithCoverageMaindirectly via AST-parsedScriptBlockwith in-memory[pscustomobject]fixtures, covering (1) an assembly discovered directly beneath a.claude\worktrees\agent-<id>\search root — must be included, and (2) a sibling agent worktree nested beneath the search root — must remain excluded, and (3) a double-nested case (search root itself under.claude\worktrees\agent-N\, containing a further-nested sibling worktree) exercising the^branch of the anchor.tests/scripts/vscode/Invoke-MSTest.RunSettings.Tests.ps1is unmodified (verified byte-identical by blob-hash comparison before and after).Docs / tooling
docs/features/active/2026-09-03-coverage-assembly-discovery-excludes-own-worktree-root-752/): issue, spec, research, atomic plan, evidence artifacts, and remediation plan/audit artifacts for this item. No production or test code is affected by these files.Architecture / How It Fits Together
Invoke-MSTestWithCoverageMainresolves$resolvedSearchRoot(already in scope before the discovery block) and then runsGet-ChildItem -Path $resolvedSearchRoot -Recurseto enumerate candidate assemblies. The discoveryWhere-Objectpredicate filters that candidate set against four clauses (\bin\<Configuration>\,\obj\,\ref\, and.claude); only the.claudeclause's match target changed. The corrected predicate now measures "does a.claudesegment appear after the search root in this candidate's path" rather than "does the candidate's absolute path contain.claudeanywhere," which is the distinction that separates a legitimate nested-sibling exclusion from an unintended self-exclusion. No change toInvoke-MSTestWithCoverageMain's parameters, return shape, or downstream coverage-collection invocation.Verification
Completed (evidence committed under the feature folder's
evidence/tree):tests/scripts/vscodesuite post-fix (baseline 92 + 3 new cases); fail-before capture confirmed the new suite's self-exclusion case failed pre-fix (Passed=1 Failed=2with the exactNo test assemblies found ... Build first.message) and passed post-fix.Invoke-MSTest.RunSettings.Tests.ps1) passes unchanged; its blob hash matches the pre-change baseline exactly..claude-clause sibling-defect sweep (git grep -e ".claude" -- scripts/) found no other file in the repository carrying the same absolute-path-vs-.claudediscovery defect.Recommended
pwsh -NoProfile -File scripts/vscode/Invoke-MSTest.ps1from a checkout under.claude/worktrees/agent-<id>/with a prior build, to confirm end-to-end that the wrapper no longer throws the misleading build-first error in that topology.Backward Compatibility / Migration Notes
None. The fix strictly widens the discovered-assembly set to include the previously-misclassified self-root case while preserving the previously-excluded nested-sibling-worktree case; no consumer of the discovered assembly list observes a shape change. No public API, CLI flag, or config schema is affected.
Risks and Mitigations
research/research-findings.2026-09-03T00-00.mdline 5 (and, on closer branch-diff enumeration, three further lines acrossspec.md,issue.md, and the promoted potential-entry copy) recorded an operator account name and full worktree directory layout. Mitigation: a remediation plan (independently preflight-cleared across 4 rounds) sanitized all identified lines with class-based placeholders, and a reaudit confirmed zero remaining matches across the full branch diff, plus caught and sanitized one additional leak introduced by the audit process itself (an example verification command that had spelled the same identifiers). No production or test file was touched by this remediation. This PR must be merged with a squash merge, not a merge commit — a sanitizing commit removes the value from the branch tip but leaves the pre-sanitization blob reachable through an earlier commit in the branch's history; squashing is what keeps the identifier out ofmain.Review Guide
scripts/vscode/Invoke-MSTestWithCoverage.ps1— the one-line predicate change; this is the entire behavioral fix.tests/scripts/vscode/Invoke-MSTestWithCoverage.AssemblyDiscovery.Tests.ps1— new regression coverage; review the threeItcases and mock setup.docs/features/active/2026-09-03-coverage-assembly-discovery-excludes-own-worktree-root-752/spec.mdandplan.2026-09-03T07-23.md— root-cause analysis and design rationale, if deeper context is wanted.Follow-ups
scripts/vscode/Invoke-MSTest.ps1'sGet-MSTestAssemblyPathListpipeline, which carries no.claudeclause today. Research confirmed this is a pre-existing, deliberately out-of-scope gap distinct from this defect.Invoke-MSTestWithCoverage.Helpers.ps1'sGet-KoverageProjectAllowlistproject-file filter carries no.claudeclause either; flagged as a distinct, unreported concern outside this issue's scope.No test assemblies found ... Build first.throw message does not yet exist (pre-existing gap, not introduced here).Invoke-MSTest.RunSettings.Tests.ps1, forced by the latter's 488/500-line cap; a shared setup helper undertests/scripts/vscode/could remove it.GitHub Auto-close
None
🤖 Generated with Claude Code
https://claude.ai/code/session_01TzGiZSnVySFZcoC1BHN5Vv