(%), branches / (%)`. The Threshold part file throws `Cobertura line coverage
% is below the required 80% threshold.` and `Cobertura branch coverage
% is below the required 75% threshold.`. scripts/vscode/Invoke-MSTest.TrxSummary.ps1 declares `Get-TrxRunSummary` (12 to 101) and `Format-TrxRunSummary` (103 to 150), whose five rendered lines begin `Test run outcome:`, `Total `, `Skipped `, `Figures reported verbatim by the test platform:` and `Failed tests:`.
+14. scripts/vscode/Invoke-Restore.ps1 runs `msbuild /t:Restore /p:Configuration=Debug "/p:Platform=Any CPU" /p:RestorePackagesConfig=true /m` with parameters `SolutionPath`, `Configuration`, `Platform`. scripts/vscode/Install-RepoDotNetSdk.ps1 installs SDK 8.0.205 into .dotnet-sdk at the repository root (line 36), which global.json names in its `paths` array; dotnet-tools.json at the repository root pins csharpier 1.2.6 (commands `format` and `check`; a clean `check .` prints `Checked N files in Xms.` and `format .` prints `Formatted N files in Xms.`, N being the processed count in both).
+15. .csharpierignore (18 lines) excludes every evidence tree, cobertura XML, coverage, coveragexml and trx documents, csproj, props and targets files, and every packages.config and app.config; CSharpier 1.2.6 processes C# source and XML files. .gitignore ignores every test-results directory (line 39), coverage and coveragexml documents (140 to 141), everything under the coverage directory except its gitkeep marker (144 to 145) and the packages directory (190); a trx document is not ignored by any pattern, which is why every TRX in this plan is written under coverage\.
+16. The four UtilitiesCS.Test classes that stall a local vstest run on this workstation (observed 2026-09-04, excluded by the issue #781 and #900 plans) are `HelperClasses.ShellUtilities_Tests`, `HelperClasses.ShellUtilitiesStatic_Tests`, `HelperClasses.SysImageListHelperTests` and `EmailIntelligence.OSBrowser_Tests`; `DictionaryExtensions_Tests.TryAddValuesAsync_UpdatesExistingValue` is a known intermittent failure under parallel coverage runs (issue #780). Neither QuickFiler.Test nor UtilitiesCS.Test carries a `LiveOutlook` test category (grep over both trees: zero hits), so the two full-assembly parallel-suite runs need no category filter.
+17. The #900 precedent run recorded exactly the #906 failure: its final coverage iteration 1 failed on `FileInfoWrapper_Tests.OpenRead_ShouldReturnReadableStreamForWrappedFile` with the solution file held by a resident MSBuild node-reuse worker left by the plan's own multi-process rebuilds (docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/evidence/qa-gates/p5-t5-mstest-coverage.2026-09-17T02-35.md, loop-context section).
+18. The feature issue document line 12 is `- Work Mode: full-bug`. The spec's `## Acceptance Criteria` holds nineteen lines beginning `- [ ] AC1. ` through `- [ ] AC19. ` (lines 236 to 254), each on one line; the check-off edit changes only `- [ ] ACn.` to `- [x] ACn.`. No user-story document exists. The spec's header line 9 declares full-bug and names the spec the sole AC source.
+19. .claude/hooks/validate-planner-output.ps1 requires every task's opening line to carry a slash- or backslash-bearing path token (line 95), the phase heading form with an em dash (238), sequential task ids per phase (299 to 302), a policy-read and a baseline task in Phase 0 (325 to 330) and QA vocabulary in the final phase (339).
+20. .claude/rules/plan-acceptance-gates.md: G8 reports a `git diff` with no ref operand; G8b reports a name-listing diff with neither a `git add` nor a `git status --porcelain` companion in the same task; G7 reads a fixed six-entry write-mode register that does not include CSharpier, so the csharpier tasks carry their own before-and-after tree observation rather than relying on the exit code.
+21. FluentAssertions failure phrases relied on by the negative controls: `Be(0)` on an integer prints `to be 0, but found 1`; `BeNull()` on a reference prints `to be ` followed by `but found` and the found object's type name; `BeFalse(reason)` and `NotBe(value, reason)` append `because `; `BeSameAs(expected)` prints `to refer to`. The phrase `to contain` was observed verbatim in the #900 P3-T1 artifact for `Contain`, which confirms the family's message shape on this FluentAssertions version.
+22. .claude/hooks/enforce-orchestration-preimplementation-gate.ps1 reads its checkpoint from artifacts/orchestration/orchestrator-state.json (line 31) and its readiness predicate requires the string properties `issue-num` and `feature-folder` (235 to 236), `route_id` or, when absent, `path_selected` (237 to 239), and a truthy `lifecycle_ready` (242 to 243); an absent file or a missing property denies with the reason at 429. The helpers file's issue #539 exemption admits only `git add` and `git commit` segments whose every operand lies under docs/features/epics/, docs/features/parallel/, docs/features/active/, docs/features/potential/ or artifacts/orchestration/, models only `-m` and `--message` forms, requires at least one pathspec operand, and denies any line containing `$`, backtick, `>` or `<`.
+23. Line endings. .gitattributes line 4 declares `* text=auto`, so git stores every text file with LF endings in the index and writes the working-tree ending that `core.autocrlf` selects; the executing session's `core.autocrlf` is `true` (observed during the round-4 preflight: `git config --show-origin --get-all core.autocrlf` issued against this worktree reports `true` from the Git for Windows system configuration file, and `git ls-files --eol` reports i/lf and w/crlf for the four existing Write Set files and for the two production files the controls mutate; P2-T10 still observes the effect through the working-tree column, the w/ column, rather than relying on the setting). A file the Write tool creates has LF endings, so its working-tree bytes differ from the bytes `git checkout` writes for the same path even though `git diff --exit-code HEAD` exits 0, because the diff compares normalized content. .editorconfig line 669, inside the `[*.{cs,vb}]` section that opens at line 619, sets `end_of_line = crlf`; no .csharpierrc file exists anywhere in the worktree, and CSharpier documents reading `end_of_line` from .editorconfig, so a CRLF `.cs` file is a fixed point of `csharpier format`, and P2-T7's scoped format may already have converted the two new files (P2-T10 records which case occurred under `EOL-RESTORED:`; either case satisfies P2-T10). A CRLF file restored by P2-T10 is therefore not rewritten by P4-T1; whether the pinned formatter did rewrite any Write Set file is observed through P4-T1's `REWRITTEN:` count, never assumed.
+
+## Decisions
+
+- **D-1 Helper contract is identical to the removed private helper.** `DedicatedWorkerThread.Run(Action)` carries the body of `RunOnDedicatedWorkerThread` unchanged (no null-argument guard is added, because the spec requires the identical body and AC7 requires no assertion), the removed remark's text, with its issue reference widened to name issue #931 and its lines re-wrapped so the census literal sits on one line, plus one added sentence stating that the helper asserts nothing itself (Target Source A is the exact content). Every local name is pinned (`captured`, `thread`, `error`) so count gates cannot drift.
+- **D-2 Part2 partial carries no `[TestClass]`.** The repository convention for continuation partials (fact 4) puts the attribute on the primary declaration only; the attribute applies to the whole type. The primary file keeps `[TestClass]` and gains `partial`.
+- **D-3 Direct runs use the root runsettings file.** Spec AC16 names the root file; its Workers and Scope equal the CLI twin's. The collector it declares is recorded, not gated (Execution Conventions).
+- **D-4 Coverage route is selected by a recorded observation.** `COVERAGE-ROUTE: RUNNER` runs scripts/vscode/Invoke-MSTestWithCoverage.ps1 verbatim (CLAUDE.md step 4). `COVERAGE-ROUTE: DIRECT` runs the runner's inner collector invocation directly, built from the same functions (`ConvertTo-DerivedCoverageSettingsXml`, the same assembly discovery filter, the CLI runsettings, the isolation switch, the LiveOutlook category filter) with the four-class exclusion appended, and post-processes with the runner's own helpers, so both routes yield the same three committed forms: the JaCoCo package projection, the one-line first-party summary and the TRX-derived summary. Under either route the raw Cobertura and the TRX stay under coverage\ and are never copied into the feature folder.
+- **D-5 The fix is committed before the controls (P2-T10).** Each revert is then anchored: `git checkout -- ` restores exactly the committed fixed state, `git diff --exit-code HEAD -- ` exits 0 and the SHA-256 equals `FIX-HASH-:`.
+- **D-6 Predicted failing assertion per control.** M1 (P3-T1): `Dispatch` runs the action inline on the worker, `captured` is null, and `executions.Should().Be(0)` fails with a message containing `to be 0, but found 1`. M2 (P3-T3): the restored context-reference guard throws on the worker (its ambient context is null and the captured context is not), so `captured.Should().BeNull(...)` fails with a message containing `InvalidOperationException` and `but found`. M3 (P3-T5): `action()` runs on the caller before the thread starts, so each of the four in-thread preconditions fails with a message containing `dedicated worker thread must not be` and the thread is never started. M4 (P3-T7): the mock returns a second stream, so `stream.Should().BeSameAs(sentinel)` fails with a message containing `to refer to`. Any other failing assertion, or a passing control, is `MUTATION PREDICTION MISMATCH`: stop and report the message text; do not adjust the test.
+- **D-7 Coverage comparison (AC18).** The gated quantities are per-package: for the packages named `UtilitiesCS` and `QuickFiler` in the two JaCoCo projections, the final LINE rate and BRANCH rate, each computed as covered divided by covered plus missed, must be no lower than the baseline rate. The repository-wide first-party totals are recorded with a comparability note only, because the collector's cross-assembly merge is order-sensitive. If a package rate reads lower, the final coverage command is re-run once, identically, as a second measurement (recorded as such); if it still reads lower, `AC18: NOT MET` is recorded and the run stops with the two projections quoted. No test is retried by this rule; only the measurement is repeated.
+- **D-8 Analyzer and nullable gates.** Each rebuild must exit 0 (AC17), print zero `Skipping target "CoreCompile"` lines (the spec's non-vacuity clause), echo at least one compiler line for each of QuickFiler.Test.dll and UtilitiesCS.Test.dll, and carry no warning or error line naming any Write Set `.cs` file. Warning counts are recorded against the Phase 0 values as an observation.
+- **D-9 Follow-ups are handed off, not filed, by the executor.** The executor has no MCP surface; P4-T13 writes the four potential-entry bodies from the spec's Rollout list and the orchestrator files them.
+- **D-10 No production change and no UNAFFECTED-site change.** P4-T11 requires the anchored name-listing diff of QuickFiler.Test to list exactly the four QuickFiler.Test Write Set paths and the anchored diff of QuickFiler/, UtilitiesCS/, the two runsettings files and config/ to be empty.
+- **D-11 AC15 footprint.** `THIS-ITEM-FOOTPRINT:` is the anchored diff plus porcelain union minus `INHERITED-CLAUSE-A:` minus the Clause B prefix, with both subtractions listed in the artifact; it must equal the six code files plus paths under FEATURE/.
+- **D-12 Known #780 flake is not retried.** `DictionaryExtensions_Tests.TryAddValuesAsync_UpdatesExistingValue` (fact 16) is admissible only in the Phase 0 baseline failed sets, as one member of `BASELINE-ADMISSIBLE` (D-17). Spec AC16 requires zero failed tests and admits no filter other than the shell-icon exclusion, AC17 requires the coverage route to pass, and the operator prohibits retries, so no task in this plan re-runs a command because that test failed: its failure in P4-T5 or P4-T6 is `AC16: NOT MET` and in P4-T7 is `AC17: NOT MET`, by design, and the run stops and reports the `MESSAGE` or `Failed tests:` line. The P4-T8 second measurement repeats a coverage measurement whose tests all passed; it is not a retry of a failed test.
+- **D-13 Fail-before evidence.** No committed test can deterministically reproduce either defect without mutating process-global thread-pool state or spawning a handle-holding external process, both prohibited; P1-T1 writes the exception dossier and the four controls are the deterministic observed-failing evidence.
+- **D-14 Rooted fixture literal.** The three metadata tests read the rooted literal C:\Repo\fixture.sln through the private constant `FixturePath`; the assertions mirror `file.Exists` and never read `Length`, so the outcome is identical whether or not the path exists on a machine.
+- **D-15 Wrap-tolerant tokens.** CSharpier may break a `.Should()` chain across lines, so gates use `.BeNull(`, `.NotBe(`, `owner.CheckAccess()` and the reason-text literals rather than a whole chain; the reason literals are pinned to sit on one source line each.
+- **D-16 Node reuse off.** Every msbuild invocation this plan issues or starts carries /nodeReuse:false, or for the restore script the environment variable MSBUILDDISABLENODEREUSE set to 1 (Execution Conventions), so that the node-reuse workers which produced the #906 failure in the #900 run (fact 17) do not exist when the parallel-suite runs execute. The switch is additive to the canonical commands and changes no diagnostic.
+- **D-17 Baseline runs are recorded, not gated on exit 0.** P0-T10 and P0-T11 record failed-test sets so the final runs can report `NEWLY-FAILING:`. `BASELINE-ADMISSIBLE` is exactly: Properties_ShouldMirrorWrappedFileInfo, ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory, OpenRead_ShouldReturnReadableStreamForWrappedFile, ToString_ShouldDelegateToWrappedFileInfo, Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction, TryAddValuesAsync_UpdatesExistingValue (the five rewritten tests whose pre-edit forms can fail because of the defect, plus the #780 flake; the sixth rewritten test, InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow, is not admissible, because its pre-edit form passes on any thread while its owner is null, so a baseline failure of it is not this defect). A baseline failed set that is a subset of `BASELINE-ADMISSIBLE` is recorded and the task completes (P0-T11 records `FAILED-SET:` and post-processes with the `RAW` rule of `CMD-COVERAGE-POST`); a set containing any other name is `BASELINE NOT GREEN` and stops the run, because AC16 and AC17 cannot then be met by a test-only change.
+
+## Command Reference
+
+Each block is a payload in the sense of the command-channel convention: one complete statement per line (a braced block on one line is one statement), no line continuations, no single-quote characters, no comment lines. Uppercase tokens `WORKTREE`, `PATH`, `TASKID`, `TESTPROJECT`, `PRODUCTION`, `GATEARGS`, `ASSEMBLY`, `FILTERARG`, `NAMES`, `STAGE`, `RAW` and `MERGE-BASE` (the P0-T3 value, used by `CMD-ADDED-LINES`) are substituted by the executor as each task states. Names for the `NAMES` token: `NAMES-AFFINITY` is the seven quoted method names of fact 1 (`"InitializeBreadcrumbPipeline_ConstructedInsideDispatcherOperation_SucceedsUnderDifferentAmbientContext", "InitializeBreadcrumbPipeline_OwningThreadNullAmbientContext_DoesNotThrow", "InitializeBreadcrumbPipeline_OwningThreadDifferentPlainContext_DoesNotThrow", "ConfigureBreadcrumbDropDown_OwningThreadInsideDispatcherOperation_DoesNotThrow", "InitializeBreadcrumbPipeline_WorkerThread_ThrowsBoundaryDiagnostic", "ConfigureBreadcrumbDropDown_WorkerThread_ThrowsBoundaryDiagnostic", "InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow"`); `NAMES-QF` is `NAMES-AFFINITY` plus `"Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction"`; `NAMES-FOUR` is the last three names of `NAMES-AFFINITY` plus `"Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction"`; `NAMES-FIW` is the eight quoted method names of fact 5 (`"Constructor_WhenFileInfoIsNull_ThrowsArgumentNullException", "Properties_ShouldMirrorWrappedFileInfo", "ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory", "OpenRead_ShouldReturnReadableStreamForWrappedFile", "ToString_ShouldDelegateToWrappedFileInfo", "PropertyDelegates_ShouldMirrorMockedIFileInfo", "StreamAndCopyMethods_ShouldDelegateToWrappedIFileInfo", "AccessControlAndLifecycleMethods_ShouldDelegateToWrappedIFileInfo"`); `NAMES-NONE` is the empty list `@()` written as an empty `NAMES` substitution.
+
+Filters (each is substituted for `FILTERARG` as one double-quoted argument beginning with the test-case-filter switch): `FILTER-FOUR` is `FullyQualifiedName~InitializeBreadcrumbPipeline_WorkerThread_ThrowsBoundaryDiagnostic|FullyQualifiedName~ConfigureBreadcrumbDropDown_WorkerThread_ThrowsBoundaryDiagnostic|FullyQualifiedName~InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow|FullyQualifiedName~Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` (expected total 4); `FILTER-DISPATCHER` is `FullyQualifiedName~Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` (total 1); `FILTER-NULLOWNER` is `FullyQualifiedName~InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` (total 1); `FILTER-OPENREAD` is `FullyQualifiedName~OpenRead_ShouldReturnReadableStreamForWrappedFile` (total 1); `FILTER-AFFINITY-CLASS` is `FullyQualifiedName~ItemViewerBreadcrumbThreadAffinityTests` (total 7); `FILTER-FIW-CLASS` is `FullyQualifiedName~FileInfoWrapper_Tests` (total 8); `FILTER-STALL` is `FullyQualifiedName~HelperClasses.ShellUtilities_Tests|FullyQualifiedName~HelperClasses.ShellUtilitiesStatic_Tests|FullyQualifiedName~HelperClasses.SysImageListHelperTests|FullyQualifiedName~EmailIntelligence.OSBrowser_Tests`; `FILTER-UCS-EXCLUDE` is `FullyQualifiedName!~HelperClasses.ShellUtilities_Tests&FullyQualifiedName!~HelperClasses.ShellUtilitiesStatic_Tests&FullyQualifiedName!~HelperClasses.SysImageListHelperTests&FullyQualifiedName!~EmailIntelligence.OSBrowser_Tests`. `UCS-FILTERARG` (fixed by P0-T9) is empty under `STALL-PROBE: CLEAR` and is `FILTER-UCS-EXCLUDE` under `REPRODUCES`. A run whose `total` differs from the expected value is a failure of that task, not a pass: vstest.console.exe reports a zero-match filter without a non-zero exit.
+
+Assemblies: `ASSEMBLY-QF` is QuickFiler.Test\bin\Debug\QuickFiler.Test.dll; `ASSEMBLY-UCS` is UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll.
+
+**CMD-CENSUS** (token census over one file; `PATH` substituted; prints one `TOKEN = ` line per token, `LINES = ` and `SHA256 = `):
+
+ Set-Location -LiteralPath "WORKTREE"
+ $path = "PATH"
+ $content = Get-Content -LiteralPath $path -Raw -Encoding UTF8
+ $tokens = @("Task.Run(", ".GetAwaiter()", "DedicatedWorkerThread.Run(", "RunOnDedicatedWorkerThread", "ClearViewerDispatcher(", "partial class", "[TestMethod]", "action();", "new Thread(", "IsBackground = true", "thread.Join();", "Join(", "Join()", "using System.Reflection;", "using QuickFiler.Test.TestSupport;", "namespace QuickFiler.Test.TestSupport", "internal static class DedicatedWorkerThread", "internal static Exception Run(Action action)", ".Should()", "dedicated worker thread must not be", "distinct from every live thread by construction", "unconditionally", "owner.CheckAccess()", ".NotBe(", "ownerThreadId", ".BeNull(", "executions.Should().Be(0)", "cannot marshal", "GetSolutionFile", "TaskMaster.sln", "AppDomain", "File.Exists(", "File.Create", "File.WriteAll", "File.Delete", "Path.GetTemp", "FileMode.", "FileMode.Open", "FileAccess.", "FileAccess.Read", "FileShare.ReadWrite", "Assembly.Location", "FixturePath", "using var sentinel = new FileStream(", "BeSameAs(sentinel)", ".Returns(decoy)", "wrapper.OpenRead()", "stream.CanRead.Should().BeTrue()", "stream.Length.Should().BeGreaterThan(0)", "return true;", "_ownerThreadId.HasValue", "System.Threading.SynchronizationContext.Current", "Include=""Viewers\ItemViewerBreadcrumbThreadAffinityTests.cs""", "Include=""Viewers\ItemViewerBreadcrumbThreadAffinityTests.Part2.cs""", "Include=""TestSupport\DedicatedWorkerThread.cs""", "Thread.Sleep", "Task.Delay", "[Timeout", "DoNotParallelize", "Retry")
+ foreach ($t in $tokens) { Write-Output ("TOKEN " + $t + " = " + [regex]::Matches($content, [regex]::Escape($t)).Count) }
+ Write-Output ("LINES = " + @(Get-Content -LiteralPath $path).Count)
+ Write-Output ("SHA256 = " + (Get-FileHash -Algorithm SHA256 -LiteralPath $path).Hash)
+
+The three project-file tokens are referred to in prose as INCLUDE-AFFINITY, INCLUDE-PART2 and INCLUDE-HELPER (the doubled quotes are PowerShell's escape for a quote inside a double-quoted literal).
+
+**CMD-BUILD** (project build after a source edit; `TESTPROJECT` is `QuickFiler.Test` or `UtilitiesCS.Test`, `PRODUCTION` is `QuickFiler` or `UtilitiesCS`, `TASKID` is the lower-case task id):
+
+ Set-Location -LiteralPath "WORKTREE"
+ $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
+ $msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1
+ $dll = "TESTPROJECT\bin\Debug\TESTPROJECT.dll"
+ $before = if (Test-Path -LiteralPath $dll) { (Get-Item -LiteralPath $dll).LastWriteTimeUtc } else { [datetime]::MinValue }
+ New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null
+ $global:LASTEXITCODE = 0
+ & $msbuild "TESTPROJECT\TESTPROJECT.csproj" /t:Build /m /nodeReuse:false /p:Configuration=Debug /p:Platform=AnyCPU "/flp:LogFile=coverage\logs\TASKID.msbuild.log;Verbosity=normal"
+ Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE)
+ $log = Get-Content -LiteralPath "coverage\logs\TASKID.msbuild.log" -Raw -Encoding UTF8
+ Write-Output ("CSC_OUT_LINES: " + [regex]::Matches($log, [regex]::Escape("/out:obj\Debug\TESTPROJECT.dll")).Count)
+ Write-Output ("PROD_CSC_OUT_LINES: " + [regex]::Matches($log, [regex]::Escape("/out:obj\Debug\PRODUCTION.dll")).Count)
+ Write-Output ("ZERO_ERRORS_LINES: " + [regex]::Matches($log, [regex]::Escape(" 0 Error(s)")).Count)
+ Write-Output ("DLL_ADVANCED: " + ((Get-Item -LiteralPath $dll).LastWriteTimeUtc -gt $before))
+
+`ZERO_ERRORS_LINES` counts the literal with its leading space because `0 Error(s)` is a substring of `10 Error(s)`. `CMD-BUILD-QF` denotes this block with `QuickFiler.Test` and `QuickFiler`; `CMD-BUILD-UCS` denotes it with `UtilitiesCS.Test` and `UtilitiesCS`.
+
+**CMD-REBUILD** (solution rebuild gate; `GATEARGS` is either `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` (analyzer gate) or the single TreatWarningsAsErrors property switch (nullable gate); `TASKID` substituted):
+
+ Set-Location -LiteralPath "WORKTREE"
+ $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
+ $msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1
+ New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null
+ $global:LASTEXITCODE = 0
+ & $msbuild TaskMaster.sln /t:Rebuild /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS "/flp:LogFile=coverage\logs\TASKID.msbuild.log;Verbosity=normal"
+ Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE)
+ $log = Get-Content -LiteralPath "coverage\logs\TASKID.msbuild.log" -Raw -Encoding UTF8
+ Write-Output ("SKIP_CORECOMPILE_LINES: " + [regex]::Matches($log, [regex]::Escape("Skipping target ""CoreCompile""")).Count)
+ Write-Output ("QF_TEST_CSC_OUT_LINES: " + [regex]::Matches($log, [regex]::Escape("/out:obj\Debug\QuickFiler.Test.dll")).Count)
+ Write-Output ("UCS_TEST_CSC_OUT_LINES: " + [regex]::Matches($log, [regex]::Escape("/out:obj\Debug\UtilitiesCS.Test.dll")).Count)
+ Write-Output ("ZERO_ERRORS_LINES: " + [regex]::Matches($log, [regex]::Escape(" 0 Error(s)")).Count)
+ Write-Output ("WARNINGS: " + [regex]::Match($log, "(\d+) Warning\(s\)").Groups[1].Value)
+ Write-Output ("ERRORS: " + [regex]::Match($log, "(\d+) Error\(s\)").Groups[1].Value)
+ Write-Output ("WRITESET_DIAGNOSTIC_LINES: " + @(Get-Content -LiteralPath "coverage\logs\TASKID.msbuild.log" | Where-Object { ($_ -like "*ItemViewerBreadcrumbThreadAffinityTests*" -or $_ -like "*DedicatedWorkerThread.cs*" -or $_ -like "*BreadcrumbPopupBoundaryCoverageTests.cs*" -or $_ -like "*FileInfoWrapper_Tests.cs*") -and ($_ -like "*warning *" -or $_ -like "*error *") }).Count)
+
+**CMD-VSTEST** (one assembly under the root runsettings with the isolation switch; `ASSEMBLY`, `FILTERARG` (may be empty), `NAMES`, `TASKID` substituted; the results directory is private to the task):
+
+ Set-Location -LiteralPath "WORKTREE"
+ $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
+ $vstest = & $vswhere -latest -products * -find "Common7\IDE\Extensions\TestPlatform\vstest.console.exe" | Select-Object -First 1
+ $assembly = (Resolve-Path -LiteralPath "ASSEMBLY").Path
+ $settings = (Resolve-Path -LiteralPath "TaskMaster.runsettings").Path
+ $results = Join-Path (Get-Location).Path "coverage\test-results\931\TASKID"
+ if (Test-Path -LiteralPath $results) { Remove-Item -LiteralPath $results -Recurse -Force }
+ New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null
+ Write-Output ("RUNSETTINGS-HASH-NOW: " + (Get-FileHash -Algorithm SHA256 -LiteralPath "TaskMaster.runsettings").Hash)
+ $names = @(NAMES)
+ $global:LASTEXITCODE = 0
+ & $vstest $assembly "/Settings:$settings" /InIsolation FILTERARG "/ResultsDirectory:$results" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" 2>&1 | Tee-Object -FilePath "coverage\logs\TASKID.vstest.log"
+ Write-Output ("VSTEST_EXIT_CODE: " + $LASTEXITCODE)
+ $trxPath = Join-Path $results "TASKID.trx"
+ Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath $trxPath))
+ Write-Output ("SEQUENCE_FILES: " + @(Get-ChildItem -LiteralPath $results -Recurse -Filter "Sequence_*.xml" -ErrorAction SilentlyContinue).Count)
+ Write-Output ("COLLECTOR_LINES: " + @(Get-Content -LiteralPath "coverage\logs\TASKID.vstest.log" | Where-Object { $_ -like "*Code Coverage*" }).Count)
+ if (-not (Test-Path -LiteralPath $trxPath)) { Write-Output "TRX ABSENT: the run aborted before writing its result document"; exit 3 }
+ [xml]$trx = Get-Content -LiteralPath $trxPath -Raw -Encoding UTF8
+ $ns = New-Object System.Xml.XmlNamespaceManager($trx.NameTable)
+ $ns.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010")
+ $counters = $trx.SelectSingleNode("//t:ResultSummary/t:Counters", $ns)
+ Write-Output ("COUNTERS total=" + $counters.GetAttribute("total") + " executed=" + $counters.GetAttribute("executed") + " passed=" + $counters.GetAttribute("passed") + " failed=" + $counters.GetAttribute("failed"))
+ $all = @($trx.SelectNodes("//t:UnitTestResult", $ns))
+ Write-Output ("RESULT_COUNT: " + $all.Count)
+ foreach ($r in $all) { if ($names -contains $r.GetAttribute("testName")) { Write-Output ("RESULT " + $r.GetAttribute("testName") + " = " + $r.GetAttribute("outcome")) } }
+ foreach ($r in $all) { if ($r.GetAttribute("outcome") -eq "Failed") { Write-Output ("MESSAGE " + $r.GetAttribute("testName") + " :: " + $r.SelectSingleNode("t:Output/t:ErrorInfo/t:Message", $ns).InnerText) } }
+
+The failed-result filter is applied in PowerShell rather than in XPath so no payload needs an embedded quote. The `Command:` field records `vstest.console.exe "/Settings:TaskMaster.runsettings" /InIsolation "/ResultsDirectory:coverage\test-results\931\" "/Logger:trx;LogFileName=.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"` with the note `resolved through vswhere`.
+
+**CMD-COVERAGE-RUNNER** (CLAUDE.md step 4 route; `STAGE` is `baseline` or `final`; used when `COVERAGE-ROUTE: RUNNER`):
+
+ Set-Location -LiteralPath "WORKTREE"
+ New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null
+ foreach ($f in @("coverage\coverage.cobertura.xml", "coverage\coverage.cobertura.jacoco.xml", "coverage\test-results\mstest-coverage-run.trx", "coverage\test-results\mstest-coverage-run.summary.txt")) { if (Test-Path -LiteralPath $f) { Remove-Item -LiteralPath $f -Force } }
+ foreach ($f in @("coverage\STAGE-931.cobertura.xml", "coverage\STAGE-931.trx", "coverage\STAGE-931.jacoco.xml")) { if (Test-Path -LiteralPath $f) { Remove-Item -LiteralPath $f -Force } }
+ $script = Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.ps1"
+ $global:LASTEXITCODE = 0
+ & pwsh -NoProfile -File $script 2>&1 | Tee-Object -FilePath "coverage\logs\STAGE-931.runner.log"
+ Write-Output ("RUNNER_EXIT_CODE: " + $LASTEXITCODE)
+ $log = Get-Content -LiteralPath "coverage\logs\STAGE-931.runner.log" -Raw -Encoding UTF8
+ Write-Output ("DISCOVERED_LINE: " + [regex]::Match($log, "Discovered \d+ test assemblies\.").Value)
+ Write-Output ("FIRST_PARTY_LINE: " + [regex]::Match($log, "First-party coverage: [^\r\n]*").Value)
+ Write-Output ("THRESHOLD_MESSAGE: " + [regex]::Match($log, "Cobertura (line|branch) coverage [^\r\n]*threshold\.").Value)
+ Write-Output ("COLLECT_FAILURE_MESSAGE: " + [regex]::Match($log, "MSTest with coverage failed with exit code \d+").Value)
+ Write-Output ("DOCUMENT_PRESENT: " + (Test-Path -LiteralPath "coverage\coverage.cobertura.xml"))
+ Write-Output ("PROJECTION_PRESENT: " + (Test-Path -LiteralPath "coverage\coverage.cobertura.jacoco.xml"))
+ Write-Output ("SUMMARY_PRESENT: " + (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.summary.txt"))
+ Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.trx"))
+ if (Test-Path -LiteralPath "coverage\coverage.cobertura.xml") { Copy-Item -LiteralPath "coverage\coverage.cobertura.xml" -Destination "coverage\STAGE-931.cobertura.xml" -Force }
+ if (Test-Path -LiteralPath "coverage\test-results\mstest-coverage-run.trx") { Copy-Item -LiteralPath "coverage\test-results\mstest-coverage-run.trx" -Destination "coverage\STAGE-931.trx" -Force }
+ if (Test-Path -LiteralPath "coverage\coverage.cobertura.jacoco.xml") { Write-Output ("RUNNER_PROJECTION_SHA256: " + (Get-FileHash -Algorithm SHA256 -LiteralPath "coverage\coverage.cobertura.jacoco.xml").Hash) }
+
+The runner's own log line naming the resolved vstest path and its `Coverage output:` line carry absolute paths and stay in the git-ignored log; only the named `_LINE`, `_MESSAGE` and `_PRESENT` values are transcribed. The `Command:` field records `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1` (the payload resolves the absolute script path at run time through `Join-Path (Get-Location).Path`, per the command-channel convention). The stale-output removal (the runner's four fixed-name outputs: the Cobertura document, its JaCoCo projection, the TRX and the TRX summary, fact 12) makes each `_PRESENT` value an observation of this run; the baseline documents were already copied under their stage names. The second removal line clears this stage's own copies (`STAGE` is substituted, so the `final` run never touches a `baseline` copy) for the same reason: a stage copy left by an earlier session would otherwise satisfy the (d0) stage-TRX test of P0-T11 and P4-T7 and be read by `CMD-COVERAGE-POST`.
+
+**CMD-COVERAGE-DIRECT** (the runner's inner invocation issued directly with the four-class exclusion; used when `COVERAGE-ROUTE: DIRECT`; `STAGE` substituted):
+
+ Set-Location -LiteralPath "WORKTREE"
+ . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.ps1")
+ $ErrorActionPreference = "Continue"
+ $repo = (Get-Location).Path
+ New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null
+ foreach ($f in @("coverage\STAGE-931.cobertura.xml", "coverage\STAGE-931.trx", "coverage\STAGE-931.jacoco.xml")) { if (Test-Path -LiteralPath $f) { Remove-Item -LiteralPath $f -Force } }
+ $canonical = Get-Content -LiteralPath "coverage.config" -Raw -Encoding UTF8
+ $derived = ConvertTo-DerivedCoverageSettingsXml -CanonicalSettingsXml $canonical
+ $effective = Join-Path $repo "coverage\effective-coverage-931.config"
+ Set-Content -LiteralPath $effective -Value $derived -Encoding UTF8 -NoNewline
+ $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe"
+ $vstest = & $vswhere -latest -products * -find "Common7\IDE\Extensions\TestPlatform\vstest.console.exe" | Select-Object -First 1
+ $rootLen = $repo.TrimEnd([char]92).Length
+ $asm = @(Get-ChildItem -Path $repo -Recurse -Filter "*.Test.dll" | Where-Object { $_.FullName -like "*\bin\Debug\*" -and $_.FullName -notlike "*\obj\*" -and $_.FullName -notlike "*\ref\*" -and $_.FullName.Substring($rootLen) -notlike "\.claude\*" } | Select-Object -ExpandProperty FullName)
+ $filter = "TestCategory!=LiveOutlook&FullyQualifiedName!~HelperClasses.ShellUtilities_Tests&FullyQualifiedName!~HelperClasses.ShellUtilitiesStatic_Tests&FullyQualifiedName!~HelperClasses.SysImageListHelperTests&FullyQualifiedName!~EmailIntelligence.OSBrowser_Tests"
+ $output = Join-Path $repo "coverage\STAGE-931.cobertura.xml"
+ $settings = Join-Path $repo "scripts\vscode\TaskMaster.cli.runsettings"
+ $results = Join-Path $repo "coverage\test-results\931\STAGE"
+ if (Test-Path -LiteralPath $results) { Remove-Item -LiteralPath $results -Recurse -Force }
+ $global:LASTEXITCODE = 0
+ & dotnet-coverage collect --output $output --output-format cobertura --settings $effective -- $vstest @asm "/Settings:$settings" /InIsolation "/TestCaseFilter:$filter" "/ResultsDirectory:$results" "/Logger:trx;LogFileName=STAGE-931.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" 2>&1 | Tee-Object -FilePath "coverage\logs\STAGE-931.collect.log"
+ Write-Output ("COLLECT_EXIT_CODE: " + $LASTEXITCODE)
+ Write-Output ("ASSEMBLY_COUNT: " + $asm.Count)
+ $asm | ForEach-Object { Write-Output ("ASSEMBLY: " + $_.Substring($rootLen)) }
+ Write-Output ("SEQUENCE_FILES: " + @(Get-ChildItem -LiteralPath $results -Recurse -Filter "Sequence_*.xml" -ErrorAction SilentlyContinue).Count)
+ if (Test-Path -LiteralPath (Join-Path $results "STAGE-931.trx")) { Copy-Item -LiteralPath (Join-Path $results "STAGE-931.trx") -Destination "coverage\STAGE-931.trx" -Force }
+ Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath "coverage\STAGE-931.trx"))
+
+The stage-copy removal at the top of the block makes `TRX_PRESENT:` an observation of this run, and the results-directory removal does the same for `SEQUENCE_FILES:`. The `ASSEMBLY:` lines print the path relative to the worktree root (the leading backslash is the first character). The discovery filter uses `-like` wildcards and `[char]92` rather than a regex ending in a backslash, so no payload line ends a literal with a backslash before its closing quote. The `Command:` field records `dotnet-coverage collect --output coverage\-931.cobertura.xml --output-format cobertura --settings coverage\effective-coverage-931.config -- vstest.console.exe "/Settings:scripts\vscode\TaskMaster.cli.runsettings" /InIsolation "/TestCaseFilter:" "/ResultsDirectory:coverage\test-results\931\" "/Logger:trx;LogFileName=-931.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"`.
+
+**CMD-COVERAGE-POST** (post-process, project and summarise one stage's documents with the runner's own functions; `STAGE` substituted). `RAW` is `True` under the DIRECT route, and under the RUNNER route whenever the run's `COLLECT_FAILURE_MESSAGE:` is non-empty (the runner throws at its lines 261 to 263 before post-processing, so the document a failed run leaves behind is raw collector output); `RAW` is `False` only for a RUNNER run whose log carries no collector-failure message, because the runner post-processed that document in place before asserting the floors. The TRX summary and `FAILED-SET:` are printed before the floor checks and before the projection, so a `throw` from `Assert-JacocoProjectionReconciliation` (the one call in this payload that is not wrapped) cannot suppress them:
+
+ Set-Location -LiteralPath "WORKTREE"
+ . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTestWithCoverage.Helpers.ps1")
+ . (Join-Path (Get-Location).Path "scripts\vscode\Invoke-MSTest.TrxSummary.ps1")
+ $ErrorActionPreference = "Continue"
+ $repo = (Get-Location).Path
+ $summary = Get-TrxRunSummary -TrxContent (Get-Content -LiteralPath "coverage\STAGE-931.trx" -Raw -Encoding UTF8)
+ Write-Output "SUMMARY-BEGIN"
+ Write-Output (Format-TrxRunSummary -Summary $summary)
+ Write-Output "SUMMARY-END"
+ Write-Output ("FAILED-SET: " + (@($summary.FailedTestName) -join ", "))
+ $doc = Get-Content -LiteralPath "coverage\STAGE-931.cobertura.xml" -Raw -Encoding UTF8
+ if ("RAW" -eq "True") { $doc = ConvertTo-KoverageCoberturaXml -XmlContent $doc -RepoRoot $repo; Set-Content -LiteralPath "coverage\STAGE-931.cobertura.xml" -Value $doc -Encoding UTF8 -NoNewline }
+ try { Assert-CoberturaLineCoverageThreshold -CoberturaXml $doc; Write-Output "LINE-FLOOR: MET" } catch { Write-Output ("LINE-FLOOR: NOT MET " + $_.Exception.Message) }
+ try { Assert-CoberturaBranchCoverageThreshold -CoberturaXml $doc; Write-Output "BRANCH-FLOOR: MET" } catch { Write-Output ("BRANCH-FLOOR: NOT MET " + $_.Exception.Message) }
+ Write-Output (Get-CoberturaFirstPartyCoverageReport -CoberturaXml $doc)
+ [xml]$xml = $doc
+ $projection = ConvertTo-JacocoPackageProjection -XmlDocument $xml
+ Assert-JacocoProjectionReconciliation -XmlDocument $xml -ProjectionXml $projection
+ Set-Content -LiteralPath "coverage\STAGE-931.jacoco.xml" -Value $projection -Encoding UTF8
+ Write-Output "PROJECTION-BEGIN"
+ Write-Output $projection
+ Write-Output "PROJECTION-END"
+
+The order of the printed blocks is therefore summary, `FAILED-SET:`, floors, first-party line, projection. The projection printed between `PROJECTION-BEGIN` and `PROJECTION-END` and the five summary lines between `SUMMARY-BEGIN` and `SUMMARY-END` are the two committed forms; they carry package names, counters and test names only.
+
+**CMD-PACKAGE-COMPARE** (per-package comparison of the two JaCoCo projections for AC18):
+
+ Set-Location -LiteralPath "WORKTREE"
+ [xml]$b = Get-Content -LiteralPath "coverage\baseline-931.jacoco.xml" -Raw -Encoding UTF8
+ [xml]$f = Get-Content -LiteralPath "coverage\final-931.jacoco.xml" -Raw -Encoding UTF8
+ $q = [char]39
+ foreach ($name in @("UtilitiesCS", "QuickFiler")) { foreach ($type in @("LINE", "BRANCH")) { $xp = "/report/package[@name=" + $q + $name + $q + "]/counter[@type=" + $q + $type + $q + "]"; $bc = $b.SelectSingleNode($xp); $fc = $f.SelectSingleNode($xp); if ($null -eq $bc -or $null -eq $fc) { Write-Output ("PACKAGE " + $name + " " + $type + " MISSING baseline=" + ($null -ne $bc) + " final=" + ($null -ne $fc)); continue }; $bCov = [int]$bc.GetAttribute("covered"); $bVal = $bCov + [int]$bc.GetAttribute("missed"); $fCov = [int]$fc.GetAttribute("covered"); $fVal = $fCov + [int]$fc.GetAttribute("missed"); $bRate = if ($bVal -gt 0) { [math]::Round($bCov / $bVal, 6) } else { 0 }; $fRate = if ($fVal -gt 0) { [math]::Round($fCov / $fVal, 6) } else { 0 }; Write-Output ("PACKAGE " + $name + " " + $type + " baseline=" + $bCov + "/" + $bVal + " rate=" + $bRate + " final=" + $fCov + "/" + $fVal + " rate=" + $fRate + " NOT-LOWER=" + ($fRate -ge $bRate)) } }
+
+**CMD-ADDED-LINES** (the seven `ADDED-` counts of P4-T11 over the added lines of the anchored `-U0` diff of the two test projects; `MERGE-BASE` substituted with the P0-T3 value; each count is the number of added lines, lines beginning with a single `+`, whose text contains the pattern under the ordinal, case-sensitive `String.Contains` comparison):
+
+ Set-Location -LiteralPath "WORKTREE"
+ $added = @(git diff -U0 MERGE-BASE -- QuickFiler.Test UtilitiesCS.Test | Where-Object { $_.StartsWith("+") -and -not $_.StartsWith("+++") })
+ Write-Output ("ADDED-LINE-COUNT: " + $added.Count)
+ Write-Output ("ADDED-DONOTPARALLELIZE: " + @($added | Where-Object { $_.Contains("DoNotParallelize") }).Count)
+ Write-Output ("ADDED-THREAD-SLEEP: " + @($added | Where-Object { $_.Contains("Thread.Sleep") }).Count)
+ Write-Output ("ADDED-TASK-DELAY: " + @($added | Where-Object { $_.Contains("Task.Delay") }).Count)
+ Write-Output ("ADDED-TIMEOUT: " + @($added | Where-Object { $_.Contains("[Timeout") -or $_.Contains("Timeout=") }).Count)
+ Write-Output ("ADDED-RETRY: " + @($added | Where-Object { $_.Contains("Retry") }).Count)
+ Write-Output ("ADDED-WORKERS: " + @($added | Where-Object { $_.Contains("") -or $_.Contains("Workers =") }).Count)
+ Write-Output ("ADDED-SCOPE: " + @($added | Where-Object { $_.Contains("") -or $_.Contains("ExecutionScope") }).Count)
+
+`ADDED-LINE-COUNT:` is the positive control: it is at least 1 by construction (the fix adds lines), so seven zeros over an empty diff cannot pass. The two element-tag arguments are the opening tags a runsettings edit would carry. Re-derived against the pre-edit primary affinity file: the two remarks moved verbatim into the Part2 file (pre-edit lines 210 and 266) carry the text Workers=0 inside a code element, which contains neither element-tag argument nor `Workers =`; the only occurrences of the text Scope in that file are the `ViewerScope` identifier (lines 92, 132, 171, 222, 272, 322, 440, 467, 471), which contains neither the Scope element tag nor `ExecutionScope`; no line of that file contains `Retry` or `Timeout`; and none of the Target Source A, B, D, E or F content contains any of the seven patterns. The block, not the prose, is the definition of each count.
+
+**CMD-SWEEP** (host-identifier and raw-document sweep over the feature folder; the tokens are derived at run time and never written into an artifact):
+
+ Set-Location -LiteralPath "WORKTREE"
+ $folder = "docs\features\active\2026-09-28-tests-depend-on-uncontrolled-environment-931"
+ $all = @(Get-ChildItem -LiteralPath $folder -Recurse -File)
+ $evidence = @(Get-ChildItem -LiteralPath (Join-Path $folder "evidence") -Recurse -File) + @(Get-Item -LiteralPath (Join-Path $folder "plan.2026-09-28T20-01.md"))
+ $account = [regex]::Escape($env:USERNAME)
+ $profileLeaf = [regex]::Escape((Split-Path -Leaf $env:USERPROFILE))
+ $machine = [regex]::Escape($env:COMPUTERNAME)
+ $root = [regex]::Escape((Get-Location).Path)
+ Write-Output ("FILES: " + $all.Count)
+ Write-Output ("ACCOUNT-TOKEN-MATCHES: " + @($evidence | Select-String -Pattern ("(?i)\b" + $account + "\b")).Count)
+ Write-Output ("PROFILE-LEAF-MATCHES: " + @($evidence | Select-String -Pattern ("(?i)\b" + $profileLeaf + "\b")).Count)
+ Write-Output ("MACHINE-TOKEN-MATCHES: " + @($evidence | Select-String -Pattern ("(?i)\b" + $machine + "\b")).Count)
+ Write-Output ("WORKTREE-ROOT-MATCHES: " + @($all | Select-String -Pattern ("(?i)" + $root)).Count)
+ Write-Output ("USERS-PATH-MATCHES: " + @($all | Select-String -Pattern "(?i)[a-z]:[\\/]users[\\/]").Count)
+ Write-Output ("RAW-DOCUMENT-FILES: " + @($all | Where-Object { $_.Extension -in @(".trx", ".xml", ".coverage") }).Count)
+
+The account, profile-leaf and machine sweeps run over the evidence tree and this plan (the committed evidence AC19 governs); the two path sweeps and the raw-document sweep run over the whole feature folder, because a drive-rooted user-profile path or a raw document is prohibited wherever it appears. The spec's `Owner:` field is a GitHub handle authored by the maintainer and is outside the evidence tree.
+
+## Token Census Expectations
+
+File aliases: `AFF` is the primary affinity file, `PART2` its continuation, `HELPER` the new helper, `BND` the boundary-coverage file, `FIW` the FileInfoWrapper test file, `CSPROJ` the QuickFiler.Test project file, `DISP` QuickFiler/Viewers/BreadcrumbUiDispatcher.cs, `IVB` QuickFiler/Viewers/ItemViewer.Breadcrumb.cs. `pre` is the pre-edit value P0-T12 records; `post` is the value after Phase 2 (P2-T7) that holds through the end of the run except where a control column says otherwise. Tokens not listed for a file are recorded and not gated.
+
+| File | Token | pre | post | under control |
+| --- | --- | --- | --- | --- |
+| AFF | `LINES` | 490 | at most 500 (expected 294: 490 minus 197 deleted lines 199 to 347 and 357 to 404, minus the `using System.Reflection;` line, plus two remark lines; the line 30 replacement is net zero) | unchanged |
+| AFF | `Task.Run(` | 1 | 0 | |
+| AFF | `.GetAwaiter()` | 1 | 0 | |
+| AFF | `RunOnDedicatedWorkerThread` | 5 | 0 | |
+| AFF | `ClearViewerDispatcher(` | 2 | 0 | |
+| AFF | `partial class` | 0 | 1 | |
+| AFF | `[TestMethod]` | 7 | 4 | |
+| AFF | `action();`, `new Thread(`, `IsBackground = true`, `thread.Join();` | 1 each | 0 each | |
+| AFF | `using System.Reflection;` | 1 | 0 | |
+| AFF | `dedicated worker thread must not be` | 2 | 0 | |
+| AFF | `distinct from every live thread by construction` | 1 | 0 | |
+| AFF | `DedicatedWorkerThread.Run(`, `using QuickFiler.Test.TestSupport;`, `Thread.Sleep`, `Task.Delay`, `[Timeout`, `DoNotParallelize` | 0 each | 0 each | |
+| PART2 | `LINES` | (absent) | at most 500 (expected about 202 before formatting: 22 header lines, the 53- and 52-line moved tests, the 55-line null-owner test, the 15-line `ClearViewerDispatcher`, three separating blank lines and two closing braces) | |
+| PART2 | `Task.Run(`, `.GetAwaiter()`, `RunOnDedicatedWorkerThread`, `action();`, `Thread.Sleep`, `Task.Delay`, `[Timeout`, `DoNotParallelize`, `Retry` | (absent) | 0 each | |
+| PART2 | `DedicatedWorkerThread.Run(` | (absent) | 3 | |
+| PART2 | `ClearViewerDispatcher(` | (absent) | 2 | |
+| PART2 | `partial class`, `using QuickFiler.Test.TestSupport;`, `using System.Reflection;`, `owner.CheckAccess()`, `.BeNull(`, `unconditionally` | (absent) | 1 each | |
+| PART2 | `[TestMethod]` | (absent) | 3 | |
+| PART2 | `dedicated worker thread must not be` | (absent) | 3 | |
+| HELPER | `LINES` | (absent) | at most 500 (expected about 50) | M3: one more |
+| HELPER | `namespace QuickFiler.Test.TestSupport`, `internal static class DedicatedWorkerThread`, `internal static Exception Run(Action action)`, `new Thread(`, `IsBackground = true`, `thread.Join();`, `distinct from every live thread by construction` | (absent) | 1 each | |
+| HELPER | `action();` | (absent) | 1 | M3 (P3-T5): 2; after P3-T6: 1 |
+| HELPER | `Join(` equals `Join()` | (absent) | equal | |
+| HELPER | `.Should()`, `Task.Run(`, `Thread.Sleep`, `Task.Delay`, `[Timeout`, `DoNotParallelize`, `Retry` | (absent) | 0 each | |
+| BND | `LINES` | 361 | at most 500 (expected about 386: 361 minus the 11 replaced lines plus the 35-line replacement plus one using line) | |
+| BND | `Task.Run(` | 1 | 0 | |
+| BND | `.GetAwaiter()` | 4 | 3 | |
+| BND | `DedicatedWorkerThread.Run(`, `using QuickFiler.Test.TestSupport;`, `.NotBe(`, `.BeNull(`, `dedicated worker thread must not be` | 0 each | 1 each | |
+| BND | `ownerThreadId` | 0 | 2 | |
+| BND | `executions.Should().Be(0)`, `cannot marshal`, `partial class` | 1 each | 1 each | |
+| BND | `Thread.Sleep`, `Task.Delay`, `[Timeout`, `DoNotParallelize` | 0 each | 0 each | |
+| FIW | `LINES` | 359 | at most 500 (expected about 357: 359 minus the 20 deleted lines 338 to 357, minus the 57 replaced lines 25 to 81, plus the 70-line replacement, plus the 4-line constant block and its blank line) | M4: six more (the second six-line `using var` declaration) |
+| FIW | `GetSolutionFile` | 5 | 0 | |
+| FIW | `TaskMaster.sln`, `AppDomain`, `File.Exists(` | 1 each | 0 each | |
+| FIW | `File.Create`, `File.WriteAll`, `File.Delete`, `Path.GetTemp`, `Thread.Sleep`, `Task.Delay`, `[Timeout`, `DoNotParallelize` | 0 each | 0 each | |
+| FIW | `FileMode.` equals `FileMode.Open` | 12 = 12 | 13 = 13 | |
+| FIW | `FileAccess.` equals `FileAccess.Read` | 10 = 10 | 11 = 11 | |
+| FIW | `FileShare.ReadWrite` | 8 | 9 | M4: 10 |
+| FIW | `Assembly.Location` | 6 | 7 | M4 (P3-T7): 8; after P3-T8: 7 |
+| FIW | `FixturePath` | 0 | 4 | |
+| FIW | `using var sentinel = new FileStream(`, `BeSameAs(sentinel)` | 0 each | 1 each | |
+| FIW | `.Returns(decoy)` | 0 | 0 | M4: 1; after P3-T8: 0 |
+| FIW | `wrapper.OpenRead()` | 2 | 2 | |
+| FIW | `stream.CanRead.Should().BeTrue()`, `stream.Length.Should().BeGreaterThan(0)` | 1 each | 1 each | |
+| FIW | `[TestMethod]` | 8 | 8 | |
+| CSPROJ | INCLUDE-AFFINITY | 1 | 1 | |
+| CSPROJ | INCLUDE-PART2, INCLUDE-HELPER | 0 each | 1 each | |
+| DISP | `return true;` | recorded as N1 | N1 | M1 (P3-T1): N1 plus 1; after P3-T2: N1 |
+| DISP | `_ownerThreadId.HasValue` | recorded as N2 (at least 1) | N2 | M1: N2 minus 1; after P3-T2: N2 |
+| IVB | `System.Threading.SynchronizationContext.Current` | 0 | 0 | M2 (P3-T3): 1; after P3-T4: 0 |
+
+The remarks the executor writes must not contain the literals `Task.Run(`, `.GetAwaiter()` or `RunOnDedicatedWorkerThread`; they refer to those members as `Task.Run`, `GetResult()` and `DedicatedWorkerThread.Run`. The BND remark must not contain the phrase `cannot marshal`. No remark or comment contains the identifier `FixturePath`, the word `unconditionally` outside the one pinned sentence in PART2, or the phrase `dedicated worker thread must not be` outside the pinned reason literals.
+
+## Target Source
+
+Each block is shown with its outermost lines at column 0; the executor writes members at the file's member indentation and P2-T7's format pass normalizes whitespace. Whitespace is not a counted token.
+
+### A. New file QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs (entire content)
+
+ using System;
+ using System.Threading;
+
+ namespace QuickFiler.Test.TestSupport
+ {
+ ///
+ /// Runs a delegate on a dedicated, joined background thread for tests that must exercise a
+ /// thread-identity guard from a thread that is provably not the calling thread.
+ ///
+ ///
+ /// Issue #900 and issue #931: a Task.Run work item is not guaranteed to run on a
+ /// thread other than the caller's, so it cannot stand in for a different thread in a
+ /// thread-identity test. A thread this method constructs is
+ /// distinct from every live thread by construction. The untimed Join() is a
+ /// completion wait on one bounded synchronous call, not a sleep or a wall-clock wait, and
+ /// the waiting thread and the waited-for thread are never both thread-pool workers, so
+ /// the wait cannot starve the pool under parallel execution. The helper asserts nothing
+ /// itself: each test states its own distinctness precondition inside its delegate so that
+ /// a failure names the guard under test rather than the helper.
+ ///
+ internal static class DedicatedWorkerThread
+ {
+ ///
+ /// Runs on a dedicated background thread, joins it, and
+ /// returns the exception it threw, or when it completed
+ /// normally.
+ ///
+ internal static Exception Run(Action action)
+ {
+ Exception captured = null;
+ var thread = new Thread(() =>
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception error)
+ {
+ captured = error;
+ }
+ });
+ thread.IsBackground = true;
+ thread.Start();
+ thread.Join();
+ return captured;
+ }
+ }
+ }
+
+The remark line `/// distinct from every live thread by construction. The untimed Join() is a` is written exactly as shown so the census token sits on one line (D-15); the file carries no nullable directive, matching the file the helper came from.
+
+### B. New file QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs (entire content)
+
+ using System;
+ using System.Drawing;
+ using System.Reflection;
+ using System.Windows.Threading;
+ using FluentAssertions;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+ using Moq;
+ using QuickFiler.Test.TestSupport;
+ using QuickFiler.Viewers;
+ using UtilitiesCS.OutlookObjects.Folder;
+
+ namespace QuickFiler.Test.Viewers
+ {
+ ///
+ /// Continuation partial of holding the
+ /// three cross-thread cases, each of which runs its guarded call on a dedicated thread
+ /// created by . The owner-thread admission cases, the
+ /// shared InertOperations factory and the nested helper types live in the primary
+ /// partial so that each file stays under the 500-line limit (issue #931).
+ ///
+ public sealed partial class ItemViewerBreadcrumbThreadAffinityTests
+ {
+ ` (pre-edit
+ line 228) becomes `DedicatedWorkerThread.Run(() =>`>
+
+
+
+ ///
+ /// A viewer with no owning dispatcher stays inert, which is what keeps
+ /// FormatterServices.GetUninitializedObject-built viewers in other test files from
+ /// throwing. This is the only test covering the null-owner escape.
+ ///
+ ///
+ /// Issue #931: the guarded call is made from a dedicated thread created by
+ /// DedicatedWorkerThread.Run, and the delegate asserts through the owner captured
+ /// before the dispatcher is cleared that it is not on the owner thread. The call is
+ /// therefore off the owner thread unconditionally, so the test discriminates against the
+ /// pre-#781 context-reference guard: that guard would read the non-null captured context,
+ /// find the worker's null ambient context different from it, and reject the call, whereas
+ /// the null-owner escape admits it. Seeding first and repeating the same provider are
+ /// still required: a first-time initialization under a null ambient context would throw
+ /// at BreadcrumbUiDispatcher.CaptureCurrent() regardless of the guard, and only the
+ /// already-initialized early return can witness the escape.
+ ///
+ [TestMethod]
+ public void InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow()
+ {
+ // Arrange
+ using (var scope = new ViewerScope())
+ {
+ BreadcrumbPopupUiOperations operations = InertOperations();
+ var provider = new Mock(MockBehavior.Strict);
+ scope.Viewer.InitializeBreadcrumbPipeline(provider.Object, operations);
+ object before = scope.Viewer.BreadcrumbCoordinator;
+ Dispatcher owner = scope.Viewer.UiDispatcher;
+ owner.Should().NotBeNull("the viewer must own a dispatcher before it is cleared");
+ ClearViewerDispatcher(scope.Viewer);
+
+ // Act
+ Exception captured = DedicatedWorkerThread.Run(() =>
+ {
+ bool isOwnerThread = owner.CheckAccess();
+ isOwnerThread
+ .Should()
+ .BeFalse(
+ "the dedicated worker thread must not be the owner thread, or the "
+ + "null-owner escape would be witnessed on the owner thread and "
+ + "the test would pass vacuously"
+ );
+ scope.Viewer.InitializeBreadcrumbPipeline(provider.Object, operations);
+ });
+
+ // Assert
+ captured
+ .Should()
+ .BeNull(
+ "a viewer with no owning dispatcher has no boundary to enforce and must "
+ + "stay inert"
+ );
+ scope.Viewer.BreadcrumbCoordinator.Should().BeSameAs(before);
+ }
+ }
+
+
+ }
+ }
+
+The word `unconditionally` appears exactly once, in the remark above. The reason literal segment `"the dedicated worker thread must not be the owner thread, or the "` sits on one line. The two moved remarks carry the pre-edit lines 210 and 266, each containing the text Workers=0 inside a code element; they are moved verbatim, and the P4-T11 `ADDED-WORKERS:` count is defined so that this text does not match it.
+
+### C. Edits to QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs (pre-edit numbering; apply bottom-up)
+
+1. Delete lines 357 through 404 (`ClearViewerDispatcher`, the blank line, `RunOnDedicatedWorkerThread` with its remark, and the following blank line), so that the blank line 356 is followed by the `InertDropDownHost` summary that was at 405.
+2. Delete lines 199 through 347 (the three cross-thread tests and the blank line after the third), so that the blank line 198 is followed by the `InertOperations` summary that was at 348.
+3. Replace line 30 ` public sealed class ItemViewerBreadcrumbThreadAffinityTests` with ` public sealed partial class ItemViewerBreadcrumbThreadAffinityTests`.
+4. Insert one remark line after line 27 (`/// needs no message pump.`) reading `/// The three cross-thread cases and ClearViewerDispatcher live in the continuation` and, on the following line, `/// partial file (issue #931).` (two lines, matching the existing three-slash remark indentation).
+5. Delete line 4 `using System.Reflection;` (the only member that used it moved to the Part2 file).
+
+No other line changes. The resulting file holds the four owner-thread admission tests, `InertOperations`, `InertDropDownHost`, `DrainableSynchronizationContext` and `ViewerScope`.
+
+### D. Edits to QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs (pre-edit numbering)
+
+Replace lines 52 through 62 with:
+
+ ///
+ /// An owner-only dispatcher (null context) reached from a thread that is not its owner
+ /// must report a marshalling failure and must not run the action.
+ ///
+ ///
+ /// Issue #931: the worker is a dedicated thread created by
+ /// DedicatedWorkerThread.Run, never a Task.Run work item. A blocking wait on
+ /// a pool work item queued from a pool thread can run the delegate inline on the owner
+ /// thread, in which case the owner-thread-id branch of IsCurrentBoundary() admits
+ /// the call, the action runs, and the test fails spuriously. The delegate asserts it is
+ /// off the owner thread before it dispatches. The rejection path reports and returns a
+ /// completed task synchronously, so no task wait is needed.
+ ///
+ [TestMethod]
+ public void Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction()
+ {
+ var errors = new List();
+ int ownerThreadId = Environment.CurrentManagedThreadId;
+ BreadcrumbUiDispatcher dispatcher = CreateOwnerOnlyDispatcher(errors.Add);
+ int executions = 0;
+ Exception captured = DedicatedWorkerThread.Run(() =>
+ {
+ Environment
+ .CurrentManagedThreadId.Should()
+ .NotBe(
+ ownerThreadId,
+ "the dedicated worker thread must not be the owner thread the dispatcher "
+ + "was built for, or the rejection path would never be reached"
+ );
+ dispatcher.Dispatch(() => executions++);
+ });
+ captured.Should().BeNull();
+ executions.Should().Be(0);
+ errors.Should().ContainSingle().Which.Message.Should().Contain("cannot marshal");
+ }
+
+Then insert `using QuickFiler.Test.TestSupport;` as a new line between line 11 (`using Moq;`) and line 12 (`using QuickFiler.Viewers;`). The last two statements of the test are the two pre-existing assertions, unchanged. `System.Threading.Tasks` stays imported because other tests in the file use `Task`.
+
+### E. Edits to UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs (pre-edit numbering; apply bottom-up)
+
+1. Delete lines 338 through 357 (the blank line and `GetSolutionFile`), so that line 337 (` }`) is followed by the class-closing brace that was at 358.
+2. Replace lines 25 through 81 (the four tests) with:
+
+ [TestMethod]
+ public void Properties_ShouldMirrorWrappedFileInfo()
+ {
+ // Arrange
+ var file = new FileInfo(FixturePath);
+ var wrapper = new FileInfoWrapper(file);
+
+ // Assert
+ wrapper.Exists.Should().Be(file.Exists);
+ wrapper.FullName.Should().Be(file.FullName);
+ wrapper.Name.Should().Be(file.Name);
+ wrapper.Extension.Should().Be(".sln");
+ wrapper.DirectoryName.Should().Be(file.DirectoryName);
+ wrapper.Directory.FullName.Should().Be(file.Directory.FullName);
+ }
+
+ [TestMethod]
+ public void ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory()
+ {
+ // Arrange
+ var wrapper = new FileInfoWrapper(new FileInfo(FixturePath));
+
+ // Act
+ var directoryWrapper = (DirectoryInfoWrapper)wrapper;
+
+ // Assert
+ directoryWrapper.FullName.Should().Be(wrapper.Directory.FullName);
+ directoryWrapper.Name.Should().Be(wrapper.Directory.Name);
+ }
+
+ [TestMethod]
+ public void OpenRead_ShouldReturnReadableStreamForWrappedFile()
+ {
+ // Arrange: the sentinel is a stream this test opens and owns over the running test
+ // host's own loaded assembly image, read-only with read-write sharing, so no other
+ // process's handle can deny the open and no repository or temporary file is involved
+ // (issue #931). The seam's OpenRead member returns the concrete FileStream type, so a
+ // MemoryStream cannot stand in for it through the seam.
+ using var sentinel = new FileStream(
+ typeof(FileInfoWrapper_Tests).Assembly.Location,
+ FileMode.Open,
+ FileAccess.Read,
+ FileShare.ReadWrite
+ );
+ var fileInfo = new Mock(MockBehavior.Strict);
+ fileInfo.Setup(x => x.OpenRead()).Returns(sentinel);
+ var wrapper = new FileInfoWrapper(fileInfo.Object);
+
+ // Act
+ FileStream stream = wrapper.OpenRead();
+
+ // Assert
+ stream.Should().BeSameAs(sentinel);
+ stream.CanRead.Should().BeTrue();
+ stream.Length.Should().BeGreaterThan(0);
+ }
+
+ [TestMethod]
+ public void ToString_ShouldDelegateToWrappedFileInfo()
+ {
+ // Arrange
+ var file = new FileInfo(FixturePath);
+ var wrapper = new FileInfoWrapper(file);
+
+ // Act
+ var result = wrapper.ToString();
+
+ // Assert
+ result.Should().Be(file.ToString());
+ }
+
+3. Insert after line 14 (the class opening brace) the constant and its comment, followed by one blank line:
+
+ // Issue #931: a rooted literal that is not expected to exist and does not point into the
+ // repository. The three metadata tests assert only path computations and Exists, so no
+ // file handle is opened and the outcome does not depend on any other process.
+ private const string FixturePath = @"C:\Repo\fixture.sln";
+
+The comment names the constant nowhere, so the `FixturePath` census is exactly the declaration plus three uses. The Arrange comment of the OpenRead test contains none of `FileShare.ReadWrite`, `OpenRead()`, `Assembly.Location`, `FileMode.`, `FileAccess.` or `.Length`, so the FIW census after this edit is `FileShare.ReadWrite` 9 (eight pre-existing plus the sentinel argument), `Assembly.Location` 7, `FileMode.` 13 and `FileAccess.` 11, and the `ADDED-OPENREAD:` count of P2-T6 is exactly the two code lines that call the member. No other line changes.
+
+### F. Project-file edits to QuickFiler.Test/QuickFiler.Test.csproj
+
+Insert ` ` immediately after the entry for the primary affinity file (line 98), and ` ` immediately after the entry for TestSupport\WinFormsPumpHost.cs (line 227 pre-edit, 228 after the first insertion). Both use the project-relative backslash form of the neighbouring entries.
+
+### G. Negative-control mutations (each applied by the Edit tool and reverted by `git checkout -- `)
+
+- M1 (P3-T1), QuickFiler/Viewers/BreadcrumbUiDispatcher.cs: replace lines 276 to 277 (`return _ownerThreadId.HasValue` and `&& Environment.CurrentManagedThreadId == _ownerThreadId.Value;`) with the single line `return true;`.
+- M2 (P3-T3), QuickFiler/Viewers/ItemViewer.Breadcrumb.cs: replace line 437 (`return;`) with the four lines `if (!ReferenceEquals(System.Threading.SynchronizationContext.Current, UiSyncContext))`, `{`, `throw new InvalidOperationException("mutation 931: pre-781 context-reference guard");`, `}` followed by `return;` on a fifth line, all inside the `if (owning == null)` block at 435 to 438.
+- M3 (P3-T5), QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs: insert `action();` as the first statement of `Run`, immediately before `Exception captured = null;`.
+- M4 (P3-T7), UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs: in `OpenRead_ShouldReturnReadableStreamForWrappedFile`, insert a second `using var decoy = new FileStream(` declaration with the same four arguments immediately after the `sentinel` declaration, and change `.Returns(sentinel)` to `.Returns(decoy)`.
+
+## Planner Self-Review Record (initial authoring, 2026-09-28)
+
+This section persists the planner's adversarial self-review so a reader can locate it without the conversation transcript. Every citation below was re-derived against the assigned worktree during this authoring pass; none is carried forward from the research record without a fresh read. Sibling regions re-checked: the whole of each of the three edited test files (all 490, 361 and 359 lines were read), lines 60 to 285 of BreadcrumbUiDispatcher.cs, lines 1 to 30 and 420 to 459 of ItemViewer.Breadcrumb.cs, lines 1 to 40 and 150 to 217 of FileInfoWrapper.cs, the whole runner script and its Projection, FirstParty, Threshold and TrxSummary part files.
+
+Findings that changed the plan relative to the spec's text: (1) the runner script now writes the JaCoCo package projection and the TRX summary itself and asserts both floors before doing so (fact 12), so the coverage tasks name those outputs and carry a threshold branch; (2) the runner's hard-coded filter (fact 12, line 91) and the documented local stall (fact 16) make the coverage route a recorded selection (P0-T9), with the direct route built from the runner's own functions; (3) the #906 failure was produced in the #900 run by the plan's own node-reuse workers (fact 17), so every msbuild invocation here turns node reuse off (D-16); (4) ItemViewer.Breadcrumb.cs does not import `System.Threading` (fact 9), so mutation M2 uses the fully qualified name; (5) the FluentAssertions subject name in a failure message depends on caller identification, so every predicted message is gated on its outcome phrase, not on a subject name (fact 21). No acceptance-criterion text was amended.
+
+Revision round 1 (2026-09-28), applied after the first executor preflight returned seventeen deltas. Every citation the round's edits touched was re-derived against the assigned worktree in the same pass, together with its sibling region: `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` lines 199 to 304 re-read (the removed helper name occurs once in each moved remark, at 204 and 259, and once in each call, at 228 and 277, so Target Source B names one remark substitution per test, not two; the moved remarks carry the text Workers=0 inside a code element at 210 and 266, which fixed the P4-T11 `ADDED-WORKERS:` definition); `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` lines 55 to 84 re-read (line 66 is `stream.Length.Should().BeGreaterThan(0);`, byte-identical to the rewritten assertion, so `ADDED-LENGTH:` is defined alignment-independently and expected 0; line 62 `using var stream = wrapper.OpenRead();` differs from the rewritten `FileStream stream = wrapper.OpenRead();`, so `ADDED-OPENREAD:` is deterministically 2 once the Arrange comment names no member); the spec's AC16 line 251 re-read (zero failed tests, the shell-icon exclusion the only permitted filter, no admission of the #780 flake, which fixed D-12 as no re-run and `AC16: NOT MET` / `AC17: NOT MET` by design); the promoted record docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md confirmed present in the worktree; the Token Census Expectations line expectations re-derived arithmetically (AFF 294, PART2 about 202, BND about 386, FIW about 357, M4 six more lines); and the whole plan swept once more for backticked forward-slash repository paths outside the Write Set, the feature folder and the two feature documents (four spans converted: the runner `Command:` form, the P0-T5 `Command:` form, the three P3-T2 pathspecs and the P4-T11 CLI runsettings pathspec; `origin/main` at two sites is a ref, not a path, and is unchanged).
+
+Revision round 2 (2026-09-28), applied after the second executor preflight confirmed fifteen of the seventeen round-1 fixes and returned five deltas (R2-D1 through R2-D5). Every citation the round's edits touched was re-derived against the assigned worktree in the same pass, together with its sibling region: `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` lines 199 to 404 re-read (the pre-edit null-owner test at 306 to 346 runs its guarded call through `Task.Run` against a viewer whose owner is null, and the null-owner escape returns on any thread, so its pre-edit form cannot fail from the defect, which fixed the D-17 admissibility note; the pre-edit helper remark at 377 to 384 matches Target Source A's remark text apart from the widened issue reference and the re-wrap, which fixed D-1; the only occurrences of the text Scope in the file are the `ViewerScope` identifier at 92, 132, 171, 222, 272, 322, 440, 467 and 471, the only occurrences of Workers are the code-element text at 210 and 266, and no line contains Retry or Timeout, which fixed the `ADDED-SCOPE:` definition and the new `CMD-ADDED-LINES` block); scripts/vscode/Invoke-MSTestWithCoverage.ps1 lines 275 to 304 and 375 to 439 re-read (the fixed output names `coverage\coverage.cobertura.xml`, `coverage\test-results` and `mstest-coverage-run.trx` at 282 to 284, the projection path beside the Cobertura at 393 to 395, the summary at 408 to 425; the runner removes no stale output itself, so the stale-output removal added to `CMD-COVERAGE-RUNNER` is the only thing that makes each `_PRESENT` value an observation of this run); the ordering of the baseline stage copies (`CMD-COVERAGE-RUNNER` lines that copy to `coverage\baseline-931.cobertura.xml` and `coverage\baseline-931.trx` in P0-T11, and `CMD-COVERAGE-POST` writing `coverage\baseline-931.jacoco.xml`) against the P4-T7 removal, which names the runner's fixed-name outputs and this stage's own `final-931` copies only, so no baseline document is removed by the final run; and a sibling finding the deltas did not name: a stage copy left by an earlier session would pre-satisfy the new (d0) stage-TRX test under both routes, so both coverage payloads now clear this stage's own copies before the run and the DIRECT payload prints `TRX_PRESENT:` after its copy, which makes `TRX_PRESENT:` a both-routes observation (recorded as such in the P0-T11 Output Summary list in place of the delta's RUNNER-only annotation). The P4-T7 acceptance count after the two prepended items is eight (the delta's figure); the floor-line acceptance item is now route-independent because `CMD-COVERAGE-POST` prints the floor lines on every run. The plan was grepped for the DIRECT-only floor wording and the six-count wording after the edits: none remains.
+
+Revision round 3 (2026-09-28), applied after the third executor preflight confirmed the round-2 fixes and returned three deltas (R3-D1: line endings against the revert hash anchors; R3-D2: the runner's summary file in the stale-output removal; R3-D3: the (c) wording of P0-T11). Every citation the round's edits touched was re-derived against the assigned worktree in the same pass, together with its sibling region: .gitattributes read in full (`* text=auto` at line 4; the only other attribute line is the issue #400 whitespace exception at line 9; every other non-blank line is a comment); the .editorconfig section headers enumerated (`[*.cs]` at 1, `[*.vb]` at 597, `[*.{cs,vb}]` at 619) and lines 655 to 677 read (`end_of_line = crlf` at 669 lies in the `[*.{cs,vb}]` section and so governs every `.cs` file); the worktree globbed for a .csharpierrc file (none); scripts/vscode/Invoke-MSTestWithCoverage.ps1 lines 405 to 426 re-read (the summary path at 418 to 420 is the results directory joined with the TRX base name plus `.summary.txt`, so the fixed name is coverage\test-results\mstest-coverage-run.summary.txt and the R3-D2 array entry is correct); every plan site that compares a hash against a `FIX-HASH-` anchor enumerated by grep (Execution Conventions Refs and Negative-control mechanics, D-5, P2-T10, P3-T6, P3-T8, P3-T9, P4-T9) and re-read, so each anchor is now defined as the post-restore value; `SUMMARY_PRESENT:` occurs at one site (`CMD-COVERAGE-RUNNER`) and its removal entry now precedes the run; P0-T11 (c) was the only site carrying the replaced `RAW` sentence (P4-T7 (c) already read `if it has not already run`). One finding the deltas did not name: because `end_of_line = crlf` governs `.cs` files and CSharpier documents reading that key from .editorconfig, P2-T7's scoped format may already have converted the two new files to CRLF, in which case `EOL-RESTORED:` reads `NONE` and the R3-D1 restore is a no-op; the delta is conditional on the working-tree column, the w/ column, of `git ls-files --eol`, so it is applied unchanged and the record decides which case occurred (fact 23). The value `core.autocrlf=true` is the round-3 preflight's statement; it was re-read from the worktree in round 4 (system configuration file, value `true`), and the `EOL-BEFORE:` and `EOL-AFTER:` spans of P2-T10 are the in-plan observations that replace the assumption. The CSharpier .editorconfig behaviour is documented behaviour and was not observed in this pass; the plan does not depend on it, because P4-T1's `REWRITTEN:` count and P4-T9's `POST-FORMAT-HASH-:` branch record whatever the pinned formatter does. Acceptance counts after the round: P2-T10 four (unchanged), P0-T11 five (unchanged), P4-T7 eight (unchanged), P3-T6 five, P3-T8 five, P3-T9 three, P4-T9 three, P4-T1 three (all unchanged); the task count remains 66.
+
+Revision round 4 (2026-09-28), applied after the fourth executor preflight confirmed the round-3 fixes and returned three deltas (R4-D1: the P4-T9 hash comparison keyed to a single `REWRITTEN:` value although the loop may iterate; R4-D2: artifact naming and selection for tasks re-run inside a loop; R4-D3: the provenance of the `core.autocrlf` value). Every plan site the round's edits touched was re-read in the same pass together with its siblings: the P4-T9 third acceptance item now compares each `.cs` hash with the after-format hash of the final P4-T1 iteration, and equality with the `FIX-HASH-` anchor is asserted only when every iteration recorded `REWRITTEN: 0`; P4-T1 was re-read as the sibling that supplies that value: its payload captures the SHA-256 of each of the five Write Set `.cs` files immediately before and immediately after the command and its `Output Summary:` records the ten hashes, so the per-file after-format hash was already recorded and the one permitted sibling addition labels the ten values per file and as before or after, which changes no count (P4-T1 acceptance stays three); P4-T10 was re-read as the other consumer named by R4-D2 and already records `ITERATIONS:` as the number of times the loop started from P4-T1, which is the quantity the new P4-T9 clause conditions on; the Artifact filenames convention now defines the per-iteration `ITERATION:` line and the highest-value selection rule, and P2-T9's repair sentence (`re-run P2-T7 through P2-T9, and record each iteration`) and the Phase 4 preamble (`the loop restarts from P4-T1 and P4-T10 records the iteration count`) were re-read as the two loops the convention names; P2-T10's clause `has a hash equal to its P2-T7 post-format hash` was re-read as the comparison the convention resolves to the highest-`ITERATION:` P2-T7 artifact. Fact 23 now attributes the `core.autocrlf` value to the round-4 preflight's observation (`git config --show-origin --get-all core.autocrlf` against this worktree, reporting `true` from the Git for Windows system configuration file, and `git ls-files --eol` reporting i/lf and w/crlf for the four existing Write Set files and the two production files the controls mutate), and the round-3 record's sentence claiming the value could not be re-read from inside the worktree is corrected: a system-level or global git configuration value is readable from any worktree, and the round-3 claim confused the common git directory (which holds the repository-level configuration file) with the configuration scopes git reads. The planner's file-only tool surface in this round could not itself issue the git command, so the fact records the preflight's observation as such; the plan continues to gate on the in-plan `EOL-BEFORE:` and `EOL-AFTER:` spans of P2-T10 rather than on the setting. Acceptance counts after the round: P4-T9 three, P4-T1 three, P4-T10 four, P2-T10 four (all unchanged); the task count remains 66.
+
+SELF-REVIEW: RE-DERIVED THIS PASS
+- `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` | lines 1 to 490 read in full: `[TestMethod]` at 38, 88, 128, 167, 218, 268, 318; `Task.Run(` only at 332; `RunOnDedicatedWorkerThread` at 204 (remark), 228 (call), 259 (remark), 277 (call), 385; `Workers=0` inside a code element at 210 and 266; `ClearViewerDispatcher(` at 328 and 361; `action();` at 392; reason literal on one line at 234 and 283; `distinct from every live thread by construction` on one line at 380; blank lines at 198, 347, 356, 372, 404; no `partial`, no nullable directive.
+- `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs` | lines 1 to 140 and 340 to 361 read; `Task.Run(` at 58 only; `.GetAwaiter()` at 59, 297, 305, 308 (grep over the file); `partial class` at 17; `using Moq;` at 11; `CreateOwnerOnlyDispatcher` 124 to 134; no `.NotBe(`, `.BeNull(`, `ownerThreadId`, `Thread.Sleep`, `Task.Delay`, `[Timeout` (grep); total 361 lines.
+- `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` | lines 1 to 359 read in full; `[TestMethod]` at 15, 25, 41, 55, 69, 83, 183, 297; `GetSolutionFile` at 29, 45, 59, 73, 339; `FileMode.` on 12 lines and `FileAccess.` on 10 lines (grep enumerated); `Assembly.Location` at 190, 196, 202, 208, 214, 220; `wrapper.OpenRead()` at 62 (`using var stream = wrapper.OpenRead();`) and 287; `stream.Length.Should().BeGreaterThan(0);` at 66; blank line 338; class close at 358.
+- docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md | present in the assigned worktree (Glob); the caller states it was committed on this branch deliberately before execution, so P4-T11 records it under `INHERITED-PROMOTION-RECORD:` and Clause A subtracts it.
+- `QuickFiler.Test/QuickFiler.Test.csproj` | Compile Include for the affinity file at 98, boundary pair at 105 to 106, TestSupport pair at 227 to 228; Platform default at 12; OutputPath at 36; ProjectReference to QuickFiler at 510.
+- UtilitiesCS.Test/UtilitiesCS.Test.csproj | FileInfoWrapper_Tests.cs at 234; Platform default at 11; OutputPath at 51; ProjectReference to UtilitiesCS at 963.
+- QuickFiler/QuickFiler.csproj and UtilitiesCS/UtilitiesCS.csproj | OutputType Library and AssemblyName at 9 and 12, 12 and 15; no TreatWarningsAsErrors property in QuickFiler.csproj.
+- QuickFiler/Viewers/BreadcrumbUiDispatcher.cs | `Dispatch` 71 to 151, `IsCurrentBoundary()` call at 78, null-context rejection 97 to 105, `IsCurrentBoundary` 255 to 278, `return true;` at 260, context branch 269 to 272, owner-id branch 276 to 277.
+- QuickFiler/Viewers/ItemViewer.Breadcrumb.cs | usings 1 to 9 (no `System.Threading`); `ThrowIfOffUiBoundary` 432 to 447; null-owner escape 435 to 438; throw 440 to 446.
+- QuickFiler/Viewers/ItemViewer.cs | `_uiDispatcher = Dispatcher.CurrentDispatcher;` at 27; `UiSyncContext` at 59; `UiDispatcher` at 65 to 67 (grep).
+- UtilitiesCS/HelperClasses/FileSystem/FileInfoWrapper.cs | public constructor 14 to 19; seam 21 to 24; `OpenRead()` 153 to 156; `ToString()` 191 to 194; explicit cast 211 to 214.
+- UtilitiesCS/HelperClasses/FileSystem/PhysicalFileInfoAdapter.cs | `Directory` 104, `DirectoryName` 108, `OpenRead()` 154, `ToString()` 181.
+- UtilitiesCS/Interfaces/IHelperClasses/IFileInfo.cs | `FileStream OpenRead();` at 26.
+- UtilitiesCS/Properties/AssemblyInfo.cs | `InternalsVisibleTo("UtilitiesCS.Test")` at 19.
+- QuickFiler.Test/TestSupport/WinFormsPumpHost.cs | `namespace QuickFiler.Test.TestSupport` at 9.
+- QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.Part2.cs | continuation partial declaration without `[TestClass]` at 23.
+- TaskMaster.runsettings | 30 lines; Workers 0 at 5; Scope ClassLevel at 6; Code Coverage collector 9 to 29. scripts/vscode/TaskMaster.cli.runsettings | 9 lines; same Workers and Scope; no collector.
+- scripts/vscode/Invoke-MSTestWithCoverage.ps1 | parameters 1 to 13; filter at 91; results directory and logger at 92 to 93; `ConvertTo-DerivedCoverageSettingsXml` 97 to 134; collector throw 261 to 263; strict mode and stop preference 271 to 272; fixed results names 283 to 284; repo root 305; runsettings 312; discovery 330 to 337; post-process 382 to 384; floors 386 to 387; first-party line 388; projection 393 to 401; summary 408 to 425; retention 427 to 431; entry guard 437.
+- scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | part-file dot-sources 2 to 6; `ConvertTo-KoverageCoberturaXml` at 407. scripts/vscode/Invoke-MSTestWithCoverage.Projection.ps1 | `ConvertTo-JacocoPackageProjection` 14 to 81; `Assert-JacocoProjectionReconciliation` 83 to 146; `Test-RawCoverageDocumentRetained` 148 to 197. scripts/vscode/Invoke-MSTestWithCoverage.FirstParty.ps1 | `Format-CoberturaFirstPartyCoverageSummary` 95 to 121 (line shape at 117 to 120); `Get-CoberturaFirstPartyCoverageReport` 123 to 162. scripts/vscode/Invoke-MSTestWithCoverage.Threshold.ps1 | line floor message at 54; branch floor message at 124. scripts/vscode/Invoke-MSTest.TrxSummary.ps1 | `Get-TrxRunSummary` 12 to 101; `Format-TrxRunSummary` 103 to 150.
+- scripts/vscode/Invoke-Restore.ps1 | parameters 1 to 10; vswhere shape at 31. scripts/vscode/Install-RepoDotNetSdk.ps1 | install directory at 36. global.json | SDK 8.0.205 and `.dotnet-sdk` path.
+- .csharpierignore | 18 lines as listed in fact 15. .gitignore | lines 39, 140 to 141, 144 to 145, 190.
+- .claude/hooks/validate-planner-output.ps1 | path regex 95; phase regex 238; task regex 239; sequential check 299 to 302; Phase 0 checks 325 to 330; final-phase check 339.
+- .claude/hooks/enforce-orchestration-preimplementation-gate.ps1 | checkpoint path 31; readiness properties 235 to 243; deny reason 429.
+- docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/spec.md | header line 9 (full-bug, sole AC source); Write Set 222 to 232; AC lines 236 to 254, all unchecked; Rollout follow-ups 274 to 278.
+- docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/issue.md | line 12 `- Work Mode: full-bug`.
+- docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/evidence/qa-gates/p5-t5-mstest-coverage.2026-09-17T02-35.md | loop-context section recording the iteration-1 `OpenRead_ShouldReturnReadableStreamForWrappedFile` failure caused by a node-reuse worker.
+- This plan | every task line carries a path token; phase headings use the em dash; task ids are sequential per phase (13, 1, 10, 9, 33); the final phase title and tasks carry the QA vocabulary the hook requires.
+- Round 2: `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` | lines 199 to 404 re-read: null-owner test 306 to 346 (`Task.Run(` 332, `.GetAwaiter()` 335, `ClearViewerDispatcher(` 328); helper remark 377 to 384 with the census literal on line 380; `ViewerScope` at 92, 132, 171, 222, 272, 322, 440, 467, 471; `Workers=0` inside a code element at 210 and 266; no `Retry`, no `Timeout`, no `ExecutionScope`, no Scope or Workers element tag.
+- Round 2: scripts/vscode/Invoke-MSTestWithCoverage.ps1 | lines 275 to 304 and 375 to 439 re-read: defaults `coverage\coverage.cobertura.xml`, `coverage\test-results`, `mstest-coverage-run.trx` at 282 to 284; helper dot-sources 289 and 295; post-process 381 to 384; floors 386 to 387; first-party line 388; projection path 393 to 395 and write 396 to 400; summary 408 to 425; retention 427 to 431; entry guard 437 to 439; no stale-output removal anywhere in the script.
+- Round 2: this plan | P0-T11 and P4-T7 were the only two tasks carrying the DIRECT-only floor wording (grep before the edit: four hits at P0-T11, two at P4-T7; none after); P4-T11 and P4-T29 were the only two sites carrying the six-count wording (none after); the P4-T7 acceptance list holds eight items after the edit; `CMD-COVERAGE-RUNNER` copies the stage documents at its two `Copy-Item` lines before any later removal can run, and the removal lists name no `baseline` document when `STAGE` is `final`.
+- Round 3: .gitattributes | 69 lines read in full; `* text=auto` at 4; `whitespace=-blank-at-eof` for the issue #400 record at 9; every other non-blank line is a `#` comment.
+- Round 3: .editorconfig | section headers `[*.cs]` 1, `[*.vb]` 597, `[*.{cs,vb}]` 619 (grep); lines 655 to 677 read: `end_of_line = crlf` at 669 inside the `[*.{cs,vb}]` section; no .csharpierrc file anywhere in the worktree (Glob).
+- Round 3: scripts/vscode/Invoke-MSTestWithCoverage.ps1 | lines 405 to 426 re-read: `Get-TrxRunSummary` call 409 to 412; summary path 418 to 420 (results directory joined with the TRX base name plus `.summary.txt`); `Set-Content` 421 to 424; the `Test-result summary:` line 425.
+- Round 3: this plan | grep for `FIX-HASH`: Execution Conventions Refs, Negative-control mechanics, D-5, P2-T10, P3-T6, P3-T8, P3-T9, P4-T9, each now naming the post-restore anchor; `SUMMARY_PRESENT` once, in `CMD-COVERAGE-RUNNER`, whose removal array now carries the summary path; the replaced P0-T11 (c) sentence occurred once; P2-T10 acceptance still reads `all four required`; P0-T11 still reads `all five required`.
+- Round 4: this plan, P4-T1 | task re-read in full: the payload hashes each of the five Write Set `.cs` files before and after `dotnet tool run csharpier format .`; the `Output Summary:` lists the ten hashes (now labelled per file and as before or after), `REWRITTEN:`, `FORMAT_CHANGED_TREE:` and the porcelain output; acceptance count three; the restart sentence and the `FORMAT NOT IDEMPOTENT` stop are unchanged.
+- Round 4: this plan, P4-T9 | third acceptance item replaced with the R4-D1 text; the item count remains three; `POST-FORMAT-HASH-:` occurs at this one acceptance site, in the round-3 record and in this entry.
+- Round 4: this plan, P4-T10 | `ITERATIONS:` is defined as the number of times the loop started from P4-T1 and the task is re-run after a restart; acceptance count four; unchanged.
+- Round 4: this plan, Execution Conventions, Artifact filenames | R4-D2 sentences appended; the `-..md` span contains no slash and is not a path token; `ITERATION:` occurs in this bullet and in the round-4 review text only, as a convention that governs every loop re-run; no task field list was edited to repeat it.
+- Round 4: this plan, P2-T9 and P2-T10 | P2-T9 closes with `re-run P2-T7 through P2-T9, and record each iteration`; P2-T10 compares unrestored `.cs` hashes with `its P2-T7 post-format hash`; both sentences unchanged and now resolved by the convention to the highest-`ITERATION:` artifact.
+- Round 4: this plan, fact 23 and the round-3 record | the parenthetical beginning `(stated by the round-3 preflight;` occurred once and was replaced; the sentence `it cannot be re-read from inside the worktree` occurred once and was replaced; no other plan sentence claims the setting is unreadable from the worktree (grep for `common git directory` after the edits: the round-4 record's corrective sentence and this entry only; the fact-23 and round-3 sites no longer carry the phrase).
+- Round 4: .claude/agent-memory/atomic-planner/project_931_uncontrolled_environment_tests_plan_seams.md | entry 13(c) corrected to record that `git config --show-origin --get-all core.autocrlf` is readable from inside a worktree.
+
+PLANNER-INTERNAL-REVIEW: PASS
+CITATION-TO-TREE: PASS
+AC-TRACEABILITY: PASS
+SCOPE-BOUNDARY: PASS
+CITATION: QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs | 490 lines; tests 199-251, 253-304, 306-346; `Task.Run(` 332; `ClearViewerDispatcher` 357-371; `RunOnDedicatedWorkerThread` 373-403, remark occurrences 204 and 259, calls 228 and 277; `Workers=0` remarks 210 and 266
+CITATION: QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs | 361 lines; test 52-62; `Task.Run(` 58; `CreateOwnerOnlyDispatcher` 124-134; usings 1-12
+CITATION: UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs | 359 lines; tests 25-81; `wrapper.OpenRead()` 62; `stream.Length.Should().BeGreaterThan(0);` 66; `GetSolutionFile` 339-357; sentinel pattern 189-224
+CITATION: docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md | present; pre-committed promoted record recorded by P4-T11
+CITATION: QuickFiler.Test/QuickFiler.Test.csproj | Compile Include 98, 105-106, 227-228; Platform 12; ProjectReference 510
+CITATION: UtilitiesCS.Test/UtilitiesCS.Test.csproj | Compile Include 234; Platform 11; ProjectReference 963
+CITATION: QuickFiler/Viewers/BreadcrumbUiDispatcher.cs | `Dispatch` 71-151; rejection 97-105; `IsCurrentBoundary` 255-278; owner-id branch 276-277
+CITATION: QuickFiler/Viewers/ItemViewer.Breadcrumb.cs | usings 1-9; `ThrowIfOffUiBoundary` 432-447; escape 435-438
+CITATION: QuickFiler/Viewers/ItemViewer.cs | `_uiDispatcher` 27; `UiSyncContext` 59; `UiDispatcher` 65-67
+CITATION: UtilitiesCS/HelperClasses/FileSystem/FileInfoWrapper.cs | constructors 14-24; `OpenRead` 153-156; `ToString` 191-194; cast 211-214
+CITATION: UtilitiesCS/HelperClasses/FileSystem/PhysicalFileInfoAdapter.cs | `Directory` 104; `OpenRead` 154; `ToString` 181
+CITATION: UtilitiesCS/Interfaces/IHelperClasses/IFileInfo.cs | `FileStream OpenRead();` 26
+CITATION: UtilitiesCS/Properties/AssemblyInfo.cs | InternalsVisibleTo 18-19
+CITATION: QuickFiler.Test/TestSupport/WinFormsPumpHost.cs | namespace 9
+CITATION: QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.Part2.cs | partial declaration 23
+CITATION: TaskMaster.runsettings | Workers 5; Scope 6; collector 9-29
+CITATION: scripts/vscode/TaskMaster.cli.runsettings | 9 lines
+CITATION: scripts/vscode/Invoke-MSTestWithCoverage.ps1 | filter 91; derived settings 97-134; throw 261-263; fixed output names 282-284; discovery 330-337; post-process 381-384; floors 386-387; projection 393-401; summary 408-425; retention 427-431; guard 437
+CITATION: scripts/vscode/Invoke-MSTestWithCoverage.Helpers.ps1 | dot-sources 2-6; `ConvertTo-KoverageCoberturaXml` 407
+CITATION: scripts/vscode/Invoke-MSTestWithCoverage.Projection.ps1 | 14-81; 83-146; 148-197
+CITATION: scripts/vscode/Invoke-MSTestWithCoverage.FirstParty.ps1 | 95-121; 123-162
+CITATION: scripts/vscode/Invoke-MSTestWithCoverage.Threshold.ps1 | messages 54 and 124
+CITATION: scripts/vscode/Invoke-MSTest.TrxSummary.ps1 | 12-101; 103-150
+CITATION: scripts/vscode/Invoke-Restore.ps1 | parameters 1-10; vswhere 31
+CITATION: scripts/vscode/Install-RepoDotNetSdk.ps1 | install directory 36
+CITATION: .csharpierignore | 18 lines
+CITATION: .gitignore | 39, 140-141, 144-145, 190
+CITATION: .gitattributes | `* text=auto` 4
+CITATION: .editorconfig | `[*.{cs,vb}]` 619; `end_of_line = crlf` 669
+CITATION: .claude/hooks/validate-planner-output.ps1 | 95, 238-239, 299-302, 325-330, 339
+CITATION: .claude/hooks/enforce-orchestration-preimplementation-gate.ps1 | 31, 235-243, 429
+CITATION: docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/spec.md | header 9; Write Set 222-232; AC 236-254; Rollout 274-278
+CITATION: docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/issue.md | line 12
+CITATION: docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/evidence/qa-gates/p5-t5-mstest-coverage.2026-09-17T02-35.md | loop-context section
+AC-INVENTORY: AC1, AC2, AC3, AC4, AC5, AC6, AC7, AC8, AC9, AC10, AC11, AC12, AC13, AC14, AC15, AC16, AC17, AC18, AC19
+AC-MAPPING: AC1 | IMPLEMENTATION: P2-T5 (Target Source D) | TESTS: P3-T1 control and P4-T5 full run | EVIDENCE: p4-t9 census of BND, mutation-owner-only-dispatcher-guard.md, parallel-suite-quickfiler-test.md; check-off P4-T14
+AC-MAPPING: AC2 | IMPLEMENTATION: P2-T3 (Target Source B null-owner test) | TESTS: P3-T3 control and P4-T5 | EVIDENCE: p4-t9 census of PART2, mutation-null-owner-escape.md, parallel-suite-quickfiler-test.md; check-off P4-T15
+AC-MAPPING: AC3 | IMPLEMENTATION: D-10 (no edit outside the four QuickFiler.Test Write Set paths) | TESTS: P4-T11 anchored name-listing diff of QuickFiler.Test | EVIDENCE: p4-t11-scope-boundary; check-off P4-T16
+AC-MAPPING: AC4 | IMPLEMENTATION: P2-T3 and P2-T4 (split into partials) | TESTS: P4-T5 seven affinity results | EVIDENCE: p4-t9 census of AFF and PART2, parallel-suite-quickfiler-test.md; check-off P4-T17
+AC-MAPPING: AC5 | IMPLEMENTATION: P2-T1 through P2-T7 | TESTS: P4-T9 post-format line counts | EVIDENCE: p4-t9-post-format-census; check-off P4-T18
+AC-MAPPING: AC6 | IMPLEMENTATION: P2-T2 (Target Source F) | TESTS: P4-T5 four named results | EVIDENCE: p4-t9 census of CSPROJ, parallel-suite-quickfiler-test.md; check-off P4-T19
+AC-MAPPING: AC7 | IMPLEMENTATION: P2-T1 (Target Source A) | TESTS: P3-T5 control | EVIDENCE: p4-t9 census of HELPER, mutation-inline-precondition.md; check-off P4-T20
+AC-MAPPING: AC8 | IMPLEMENTATION: P2-T6 (Target Source E) | TESTS: P4-T6 full run | EVIDENCE: p4-t9 census of FIW, parallel-suite-utilitiescs-test.md; check-off P4-T21
+AC-MAPPING: AC9 | IMPLEMENTATION: P2-T6 (OpenRead test) | TESTS: P3-T7 control and P4-T6 | EVIDENCE: p4-t9 census of FIW, mutation-openread-sentinel.md; check-off P4-T22
+AC-MAPPING: AC10 | IMPLEMENTATION: P2-T6 (three metadata tests) | TESTS: P4-T6 | EVIDENCE: p2-t6 added-line counts, parallel-suite-utilitiescs-test.md; check-off P4-T23
+AC-MAPPING: AC11 | IMPLEMENTATION: P3-T1 and P3-T2 | TESTS: FILTER-DISPATCHER runs | EVIDENCE: mutation-owner-only-dispatcher-guard.md; check-off P4-T24
+AC-MAPPING: AC12 | IMPLEMENTATION: P3-T3 and P3-T4 | TESTS: FILTER-NULLOWNER runs | EVIDENCE: mutation-null-owner-escape.md; check-off P4-T25
+AC-MAPPING: AC13 | IMPLEMENTATION: P3-T5 and P3-T6 | TESTS: FILTER-FOUR runs | EVIDENCE: mutation-inline-precondition.md; check-off P4-T26
+AC-MAPPING: AC14 | IMPLEMENTATION: P3-T7 and P3-T8 | TESTS: FILTER-OPENREAD runs | EVIDENCE: mutation-openread-sentinel.md; check-off P4-T27
+AC-MAPPING: AC15 | IMPLEMENTATION: P3-T9, D-11 and the Commits convention (no .claude or config path is ever staged) | TESTS: P4-T11 footprint, COMMITTED-CLAUDE-OR-CONFIG and INHERITED-PROMOTION-RECORD | EVIDENCE: p3-t9-post-control-clean-tree, p4-t11-scope-boundary; check-off P4-T28
+AC-MAPPING: AC16 | IMPLEMENTATION: P4-T5 and P4-T6 under TaskMaster.runsettings | TESTS: full QuickFiler.Test and UtilitiesCS.Test runs | EVIDENCE: parallel-suite-quickfiler-test.md, parallel-suite-utilitiescs-test.md, p4-t11 added-line counts; check-off P4-T29
+AC-MAPPING: AC17 | IMPLEMENTATION: P4-T1 through P4-T7 loop | TESTS: P4-T7 coverage route | EVIDENCE: toolchain-pass.md; check-off P4-T30
+AC-MAPPING: AC18 | IMPLEMENTATION: P0-T11, P4-T7, P4-T8 | TESTS: CMD-PACKAGE-COMPARE | EVIDENCE: coverage-baseline.md, coverage-final.md; check-off P4-T31
+AC-MAPPING: AC19 | IMPLEMENTATION: P4-T12 and the artifact-hygiene convention | TESTS: CMD-SWEEP | EVIDENCE: p4-t12-hygiene-sweep; check-off P4-T32
+UNRESOLVED-GAPS: NONE
+PREFLIGHT: VALIDATION REQUESTED (DIRECTIVE: PREFLIGHT VALIDATION ONLY through atomic-executor; the planner-side record above is not executor clearance)
+
+---
+
+### Phase 0 — Policy Reads, Preconditions and Toolchain Baseline
+
+- [x] [P0-T1] Read, in this exact order, the policy and instruction files CLAUDE.md, .claude/rules/general-code-change.md, .claude/rules/general-unit-test.md, .claude/rules/quality-tiers.md, .claude/rules/csharp.md, .claude/rules/tonality.md, .claude/rules/plan-acceptance-gates.md, .claude/skills/atomic-plan-contract/SKILL.md, .claude/skills/acceptance-criteria-tracking/SKILL.md, .claude/skills/evidence-and-timestamp-conventions/SKILL.md, then FEATURE/spec.md, FEATURE/issue.md and FEATURE/research/2026-09-28T20-15-tests-depend-on-uncontrolled-environment-research.md, and write FEATURE/evidence/baseline/phase0-instructions-read.md with `Timestamp:`, `Policy Order:` (the ordered list above) and one line per file giving its repository-relative path and line count. The filename carries no timestamp suffix because the atomic-plan contract names it exactly. Acceptance: the artifact exists at that exact path and lists all thirteen files, each with an integer line count.
+
+- [x] [P0-T2] Verify the full-bug preconditions read-only and record them in FEATURE/evidence/baseline/p0-t2-mode-preconditions..md. Acceptance, all five required: FEATURE/issue.md contains the exact line `- Work Mode: full-bug`; FEATURE/spec.md contains a heading line whose text is exactly `## Acceptance Criteria`; the box-state-independent inventory regex `^- \[[ x]\] AC([1-9]|1[0-9])\. ` matches exactly 19 lines of FEATURE/spec.md and every one of them begins `- [ ] ` (none is checked before execution starts); FEATURE/user-story.md does not exist; the spec's `## Write Set` section lists the six code and project paths of this plan's Write Set and no other code path. Any failure is `MODE PRECONDITION FAILED`: stop and report; the executor does not edit FEATURE/spec.md to repair it.
+
+- [x] [P0-T3] Record the working context, the diff anchors, the inherited-path set and the pre-implementation gate readiness in FEATURE/evidence/baseline/p0-t3-worktree-context..md. This task uses only `git` invocations and the Read tool. Run, in this order: `git rev-parse --abbrev-ref HEAD`; `git rev-parse HEAD` (recorded as `BASE-SHA:`); `git fetch origin main` (recorded as `FETCH-EXIT:`; a non-zero value is recorded and the task continues with the origin/main ref already present); `git merge-base HEAD origin/main` (recorded as `MERGE-BASE:`); `git diff --name-only MERGE-BASE...HEAD` and `git status --porcelain --untracked-files=all`, whose union of paths is recorded under `INHERITED-CLAUSE-A:` (the name-listing diff cannot see untracked paths, which is why the porcelain span is its companion). Record `TOPLEVEL CONTAINS FEATURE:` as `YES` or `NO` by testing that FEATURE/spec.md exists beneath the `git rev-parse --show-toplevel` value, and `TOPLEVEL LEAF:` as only that value's final path segment; do not record the value itself. Then read artifacts/orchestration/orchestrator-state.json with the Read tool (never `git add`, never edit) and record `CHECKPOINT-EXISTS:`, `CHECKPOINT-ISSUE-NUM:`, `CHECKPOINT-FEATURE-FOLDER:`, `CHECKPOINT-ROUTE:` (the `route_id` value, else `path_selected`, else `ABSENT`) and `CHECKPOINT-LIFECYCLE-READY:` (fact 22), and `PRE-IMPLEMENTATION GATE READY:` as `YES` only when the file exists, `CHECKPOINT-ISSUE-NUM:` is `931`, `CHECKPOINT-FEATURE-FOLDER:` begins docs/features/active/, `CHECKPOINT-ROUTE:` is not `ABSENT` and `CHECKPOINT-LIFECYCLE-READY:` is `true`; otherwise `NO`. Acceptance, all six required: the abbreviated branch name equals bug/tests-depend-on-uncontrolled-environment-931 (otherwise report `BRANCH MISMATCH` and stop; do not create or switch branches); `BASE-SHA:` and `MERGE-BASE:` are each a 40-character hexadecimal value; `INHERITED-CLAUSE-A:` is present and lists none of the six Write Set code paths (a listed one is `WRITE SET ALREADY DIRTY`: stop and report); `TOPLEVEL CONTAINS FEATURE: YES`; `PRE-IMPLEMENTATION GATE READY:` is recorded, and when it is `NO` the executor reports `PRE-IMPLEMENTATION GATE NOT SEEDED` with the five `CHECKPOINT-` values and stops at this task; the artifact contains no absolute filesystem path (any absolute value the checkpoint carries is recorded as ``).
+
+- [x] [P0-T4] Probe the command channel, then bootstrap and prove the C# toolchain, writing FEATURE/evidence/baseline/p0-t4-channel-and-toolchain..md. Part 1: run `pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; Write-Output ("PROBE-OK " + (Test-Path -LiteralPath "TaskMaster.sln"))'` and record `CHANNEL:` as `COMMAND` when the line `PROBE-OK True` is observed, else `UNAVAILABLE` with the refusal or error text verbatim (absolute paths and account names replaced before writing). When `CHANNEL: UNAVAILABLE`, report `CHANNEL UNAVAILABLE` and stop at this task; the executor does not modify hook or permission configuration to obtain a channel. Part 2 (only under `CHANNEL: COMMAND`): record `RUNSETTINGS-HASH:` (TaskMaster.runsettings), `CLI-RUNSETTINGS-HASH:` (scripts/vscode/TaskMaster.cli.runsettings), `PRE-EDIT-HASH-AFF:`, `PRE-EDIT-HASH-BND:`, `PRE-EDIT-HASH-FIW:` and `PRE-EDIT-HASH-CSPROJ:` (the four existing Write Set files) as `Get-FileHash -Algorithm SHA256 -LiteralPath` values, and `NEW-FILES-ABSENT:` as whether neither `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` nor `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` exists; then run, inside payloads, `& pwsh -NoProfile -File (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1")` (idempotent), test that the directory .dotnet-sdk\sdk\8.0.205 exists (`SDK-MARKER:`), `dotnet --version`, `dotnet tool restore`, `dotnet tool list --local` (the `csharpier` row must read `1.2.6`), resolve MSBuild.exe and vstest.console.exe through vswhere (recorded as `MSBUILD-RESOLVED:` and `VSTEST-RESOLVED:`, `YES` or `NO`, plus only the path segment after `Microsoft Visual Studio\`), and `dotnet-coverage --version` (if not found, run `dotnet tool install --global dotnet-coverage` and then `dotnet-coverage --version` again in a separate invocation). Acceptance, all nine required: `CHANNEL: COMMAND`; the six hashes are each 64-character hexadecimal values; `NEW-FILES-ABSENT: True`; `SDK-MARKER: YES`; `dotnet --version` and `dotnet tool restore` exit 0; the `csharpier` row shows `1.2.6`; `MSBUILD-RESOLVED: YES` and `VSTEST-RESOLVED: YES`; `dotnet-coverage --version` exits 0 with its version string recorded; the artifact contains no absolute filesystem path.
+
+- [x] [P0-T5] Restore NuGet packages by running one payload whose statements after the `Set-Location` are, in this order: `New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null` (the log directory does not exist in a fresh worktree and `Tee-Object` creates no parent directory); `$env:MSBUILDDISABLENODEREUSE = "1"` (D-16: the restore script issues its own msbuild invocation without the /nodeReuse:false switch, and the inherited environment variable is MSBuild's equivalent); `& pwsh -NoProfile -File (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1") 2>&1 | Tee-Object -FilePath "coverage\logs\p0-t5.restore.log"`; `Write-Output ("RESTORE_EXIT_CODE: " + $LASTEXITCODE)`; `Write-Output ("PACKAGE-DIR-COUNT: " + @(Get-ChildItem -LiteralPath packages -Directory).Count)`. Write FEATURE/evidence/baseline/p0-t5-nuget-restore..md with `Timestamp:`, `Command:` (`pwsh -NoProfile -File scripts\vscode\Invoke-Restore.ps1`, with the note that the payload resolved the absolute script path at run time through `Join-Path (Get-Location).Path` and set MSBUILDDISABLENODEREUSE to 1), `EXIT_CODE:` (the printed `RESTORE_EXIT_CODE:`) and an `Output Summary:` recording `PACKAGE-DIR-COUNT:`. A fresh agent worktree has no packages directory and no bin\Debug output, so every later build and test task depends on this step. Acceptance, both required: `EXIT_CODE: 0`; `PACKAGE-DIR-COUNT:` is at least 1.
+
+- [x] [P0-T6] Capture the baseline formatting state by running `dotnet tool run csharpier check .` inside a payload from the worktree root and write FEATURE/evidence/baseline/p0-t6-csharpier-check..md with `Timestamp:`, `Command:`, `EXIT_CODE:` and an `Output Summary:` quoting the tool's final summary line verbatim (shape `Checked N files in Xms.`; the check command never prints a `Formatted` line) and recording `CHECKED-FILES:` as that N. Acceptance, both required: `EXIT_CODE: 0`; `CHECKED-FILES:` is a positive integer. A non-zero exit means pre-existing drift the repository owns, and the later repository-wide format would fold that repair into this branch: stop and report `FORMAT BASELINE NOT CLEAN` with the tool's file list; do not run `format` to repair it.
+
+- [x] [P0-T7] Capture the baseline analyzer state with `CMD-REBUILD` using the analyzer `GATEARGS` and `TASKID` `p0-t7` (`Command:` records `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`, resolved through vswhere, plus /nodeReuse:false) and write FEATURE/evidence/baseline/p0-t7-msbuild-analyzers..md with `Timestamp:`, `Command:`, `EXIT_CODE:` (the printed `MSBUILD_EXIT_CODE:`, also recorded as `ANALYZE-BASELINE-EXIT:`), and an `Output Summary:` recording `SKIP_CORECOMPILE_LINES:`, `QF_TEST_CSC_OUT_LINES:`, `UCS_TEST_CSC_OUT_LINES:`, `ZERO_ERRORS_LINES:`, `WARNINGS:` (also `ANALYZE-BASELINE-WARNINGS:`), `ERRORS:` and `WRITESET_DIAGNOSTIC_LINES:`. Acceptance, all five required: `EXIT_CODE: 0`; `SKIP_CORECOMPILE_LINES: 0`; `QF_TEST_CSC_OUT_LINES:` and `UCS_TEST_CSC_OUT_LINES:` are each at least 1 (the two test projects were compiled, not skipped); `ERRORS: 0`; `WRITESET_DIAGNOSTIC_LINES: 0` (a non-zero value is `WRITE SET DIAGNOSTIC BASELINE NOT CLEAN`: stop and report the matching log lines, because P4-T3 requires the same value to be 0 and a pre-existing diagnostic in a Write Set file could not be attributed to this change). A non-zero exit or a non-zero error count is `ANALYZER BASELINE NOT CLEAN`: stop and report, because AC17 cannot then be met by a test-only change.
+
+- [x] [P0-T8] Capture the baseline nullable and type-check state with `CMD-REBUILD` using the nullable `GATEARGS` and `TASKID` `p0-t8` (`Command:` records `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`, resolved through vswhere, plus /nodeReuse:false; no Nullable property override, no incremental Build target) and write FEATURE/evidence/baseline/p0-t8-msbuild-nullable..md with the P0-T7 field set (`NULLABLE-BASELINE-EXIT:`, `NULLABLE-BASELINE-WARNINGS:`) plus `QF-TEST-DLL-EXISTS:` and `UCS-TEST-DLL-EXISTS:` as whether QuickFiler.Test\bin\Debug\QuickFiler.Test.dll and UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll exist after the command. Acceptance, all six required: `EXIT_CODE: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `_CSC_OUT_LINES:` values at least 1; `ERRORS: 0`; `WRITESET_DIAGNOSTIC_LINES: 0` (otherwise `WRITE SET DIAGNOSTIC BASELINE NOT CLEAN`: stop and report the matching log lines); both `-DLL-EXISTS:` values `True` (the precondition for P0-T9 and P0-T10). A non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop and report.
+
+- [x] [P0-T9] Run the stall probe with `CMD-VSTEST` using `ASSEMBLY-UCS`, `FILTER-STALL`, `NAMES-NONE` and `TASKID` `p0-t9`, and write FEATURE/evidence/baseline/p0-t9-stall-probe..md with `Timestamp:`, `Command:`, `EXIT_CODE:` (the printed `VSTEST_EXIT_CODE:`, or the payload exit code 3 when the TRX is absent), `ExpectedExitCode:` equal to the observed value when it is non-zero (presentational; this task gates nothing on the exit code), and an `Output Summary:` recording `RUNSETTINGS-HASH-NOW:`, `TRX_PRESENT:`, `SEQUENCE_FILES:`, `COLLECTOR_LINES:`, the `COUNTERS` line when present and every `MESSAGE` line. Then record exactly one `STALL-PROBE:` line: `CLEAR` when `EXIT_CODE: 0`, `failed` is 0 and `SEQUENCE_FILES: 0`; otherwise `REPRODUCES`. From it record exactly one `UCS-FILTERARG:` line (`NONE` under `CLEAR`; the quoted `FILTER-UCS-EXCLUDE` argument verbatim under `REPRODUCES`) and exactly one `COVERAGE-ROUTE:` line (`RUNNER` under `CLEAR`; `DIRECT` under `REPRODUCES`), with the sentence that the four excluded classes are a pre-existing local stall reproduced on main and are executed by CI (fact 16). Acceptance, all three required: `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:` from P0-T4; exactly one `STALL-PROBE:`, one `UCS-FILTERARG:` and one `COVERAGE-ROUTE:` line are present with the values the rule above derives; the probe took at most one invocation (it is never re-run). Both `STALL-PROBE:` values complete this task.
+
+- [x] [P0-T10] Capture the baseline parallel-suite runs of the two affected assemblies, before any edit, and write FEATURE/evidence/baseline/test-run-baseline.md (fixed name per the spec). Run `CMD-VSTEST` with `ASSEMBLY-QF`, empty `FILTERARG`, `NAMES-QF` and `TASKID` `p0-t10-qf`, then `CMD-VSTEST` with `ASSEMBLY-UCS`, `UCS-FILTERARG` (P0-T9), `NAMES-FIW` and `TASKID` `p0-t10-ucs`. The artifact carries `Timestamp:`, `Command:` (both commands, the runsettings file name, the isolation switch and the UtilitiesCS filter verbatim or `NONE`), `EXIT_CODE:` (scoped to the QuickFiler.Test run; the UtilitiesCS.Test exit code under `UCS-VSTEST-EXIT:`), `ExpectedExitCode:` equal to the QuickFiler.Test exit code when it is non-zero (presentational), and an `Output Summary:` with, per assembly, `RUNSETTINGS-HASH-NOW:`, the `COUNTERS` line, `RESULT_COUNT:`, `SEQUENCE_FILES:`, `COLLECTOR_LINES:`, every `RESULT` line and every `MESSAGE` line, plus `BASELINE-FAILED-QF:` and `BASELINE-FAILED-UCS:` listing every `Failed` test name or `NONE`. Acceptance, all five required: both `RUNSETTINGS-HASH-NOW:` values equal `RUNSETTINGS-HASH:`; both `COUNTERS` lines are present with `executed` at least 1; the seven `NAMES-AFFINITY` names and `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` each appear in a `RESULT` line of the QuickFiler.Test run, and the eight `NAMES-FIW` names each appear in a `RESULT` line of the UtilitiesCS.Test run (this is the pre-edit population the final runs are compared against); both `SEQUENCE_FILES:` values are 0; both `BASELINE-FAILED-` lines are present. When either failed set is non-empty, this task records it (D-17): a failed set that is a subset of `BASELINE-ADMISSIBLE` (the six names D-17 lists) completes the task without any re-run; a set containing any other name is `BASELINE NOT GREEN`, reported with the `MESSAGE` lines, and the run stops.
+
+- [x] [P0-T11] Capture the baseline repository-wide test and coverage run by the route P0-T9 fixed and write FEATURE/evidence/baseline/coverage-baseline.md (fixed name per the spec). Under `COVERAGE-ROUTE: RUNNER` run `CMD-COVERAGE-RUNNER` with `STAGE` `baseline`; under `DIRECT` run `CMD-COVERAGE-DIRECT` with `STAGE` `baseline`; then, under either route and unless branch (d0) below applies, run `CMD-COVERAGE-POST` with `STAGE` `baseline`. In both cases `RAW` is the value the Command Reference rule derives: `True` under `DIRECT`; under `RUNNER` `True` when the run's `COLLECT_FAILURE_MESSAGE:` is non-empty and `False` otherwise; the artifact records `RAW:` with the value used. Under `RUNNER` a run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report (the runner passes no blame argument). The artifact carries `Timestamp:`, `Command:` (both payloads named, the route, and the filter the route applied), `EXIT_CODE:` (`RUNNER_EXIT_CODE:` or `COLLECT_EXIT_CODE:`), `ExpectedExitCode:` equal to the observed value when it is non-zero and the task completes under branch (b), and an `Output Summary:` recording `COVERAGE-ROUTE:`, `RAW:`, `DISCOVERED_LINE:` or `ASSEMBLY_COUNT:` with every `ASSEMBLY:` line, `SEQUENCE_FILES:` (DIRECT) and `TRX_PRESENT:` (both routes; the DIRECT payload prints it after its stage copy), `THRESHOLD_MESSAGE:` (RUNNER), and `LINE-FLOOR:` and `BRANCH-FLOOR:` as printed by `CMD-COVERAGE-POST` (every run), the `First-party coverage:` line, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the five summary lines verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, and `FAILED-SET:`. Branches, decided by the recorded values and checked in the order (d0), (c), (b), (a), (d): (d0), checked first: a run with `SEQUENCE_FILES:` greater than 0 (DIRECT) or with no stage TRX at coverage\baseline-931.trx is `COVERAGE RUN ABORTED`: stop and report `COLLECT_FAILURE_MESSAGE:` or the last lines of the collector log with absolute paths replaced; do not re-run. The stage-TRX test is the run payload's `TRX_PRESENT:` value under both routes, so (d0) is decided before `CMD-COVERAGE-POST` runs, and when (d0) applies that payload is not run (its first statement reads the stage TRX); under DIRECT the report also names the last test the Sequence document lists. (c) runner exit non-zero with a `THRESHOLD_MESSAGE:`, or, whenever `RAW:` is `True` (every `DIRECT` run, and a `RUNNER` run whose `COLLECT_FAILURE_MESSAGE:` is non-empty), a `LINE-FLOOR: NOT MET` or `BRANCH-FLOOR: NOT MET` line printed by `CMD-COVERAGE-POST`: run `CMD-COVERAGE-POST` with `RAW` per the Command Reference rule if it has not already run, so the projection is recorded (under this task's opening instruction it has already run whenever (d0) does not apply, and it is not run a second time), record `FLOOR: NOT MET` with the message, and stop with `COVERAGE FLOOR BASELINE NOT MET`, because AC17 requires the coverage route to pass and this item changes no production line. (b) non-zero exit with no floor failure whose `FAILED-SET:` is non-empty and is a subset of `BASELINE-ADMISSIBLE` (D-17): record the set, and complete with no re-run (D-12); `RAW` is `True` for such a run under either route, because a failed RUNNER run throws before post-processing. A `FAILED-SET:` containing any other name is `BASELINE NOT GREEN`: stop and report with the `Failed tests:` summary line. (a) exit 0 with both floors met: complete. (d) any outcome that (c), (b) and (a) do not decide, in particular a non-zero exit with an empty `FAILED-SET:`, is `COVERAGE RUN ABORTED` with the same handling. Acceptance, all five required: the projection block contains a `package` element named `UtilitiesCS` and one named `QuickFiler`, each with a `LINE` and a `BRANCH` counter; the `First-party coverage:` line is present with four integer counts and two percentages; the summary block's first line begins `Test run outcome:`; `FAILED-SET:` is present; the artifact contains no absolute path (the runner's own path-bearing lines are not transcribed). coverage\baseline-931.jacoco.xml and coverage\baseline-931.cobertura.xml remain on disk, git-ignored, for P4-T8.
+
+- [x] [P0-T12] Record the pre-edit census by running `CMD-CENSUS` once for each of `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs`, `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs`, `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`, `QuickFiler.Test/QuickFiler.Test.csproj`, QuickFiler/Viewers/BreadcrumbUiDispatcher.cs and QuickFiler/Viewers/ItemViewer.Breadcrumb.cs, and write FEATURE/evidence/baseline/p0-t12-pre-edit-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line per file, plus `N1:` (the DISP `return true;` count) and `N2:` (the DISP `_ownerThreadId.HasValue` count). These are the positive controls every later count gate is measured against. Acceptance, all four required: every value listed in the `pre` column of the Token Census Expectations table matches (in particular AFF `LINES = 490`, `Task.Run(` 1, `RunOnDedicatedWorkerThread` 5, `partial class` 0; BND `LINES = 361`, `Task.Run(` 1, `.GetAwaiter()` 4, `ownerThreadId` 0; FIW `LINES = 359`, `GetSolutionFile` 5, `TaskMaster.sln` 1, `FileMode.` 12, `FileAccess.` 10, `FixturePath` 0; CSPROJ INCLUDE-PART2 0 and INCLUDE-HELPER 0; IVB `System.Threading.SynchronizationContext.Current` 0); `N2:` is at least 1; the four `SHA256` values for the existing Write Set files equal the four `PRE-EDIT-HASH-` values of P0-T4; any mismatch is `TREE DIVERGED FROM PLAN` and is reported before any edit.
+
+- [x] [P0-T13] Commit the Phase 0 evidence and the feature documents in the exemption-eligible form. Run, as two separate invocations, `git add -- docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931` and then `git commit -m "docs(931): phase 0 baseline evidence for the uncontrolled-environment test fix" -- docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931` (the subject `-m` paragraph, then, when the session requires an attribution trailer, a second `-m` paragraph in the bare-address form the Commits convention fixes, one pathspec operand after `--`, one command segment, no `$`, backtick, `<` or `>` character anywhere on the line; every staged path is under docs/features/active/), then `git status --porcelain -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test` and `git rev-parse HEAD`. Write FEATURE/evidence/baseline/p0-t13-phase0-commit..md recording `COMMIT-EXIT:`, `PHASE0-HEAD:` and the scoped porcelain output verbatim (`EXIT_CODE:` is scoped to the porcelain span). Acceptance, all three required: `COMMIT-EXIT: 0`; the scoped porcelain output is empty, proving no source edit preceded Phase 1; `PHASE0-HEAD:` differs from `BASE-SHA:`. A PreToolUse refusal is `PRE-IMPLEMENTATION GATE BLOCKED`: stop and report. The artifact this task writes is committed by P2-T10.
+
+### Phase 1 — Fail-Before Evidence
+
+- [x] [P1-T1] Write the fail-before exception dossier FEATURE/evidence/regression-testing/fail-before-exception..md, where `` equals its own `Timestamp:` field, with these sections: `Timestamp:`; `WhyFailingRunImpossible:` (one to three sentences: the distinct-thread defect is decided by whether the runtime inlines a pool work item onto the waiting thread, a branch no committed test can force without mutating process-global thread-pool state; the file-handle defect is decided by whether another process holds the solution file with a share mode excluding readers, which a committed test cannot arrange without starting an external process; both are prohibited by the unit-test policy); `## Alternative Proof` (the spec's Repro steps 1 to 3 with their line citations as re-recorded by P0-T12, fact 17's record of the observed #906 failure in the #900 run, and the sentence that P3-T1, P3-T3, P3-T5 and P3-T7 later demonstrate deterministically that each rewritten test fails against a deliberately broken guard, without stating those results as observations); `SearchScope:` (FEATURE/evidence/regression-testing/), `SearchPatterns:` (`fail-before-exception.*.md`), `SearchResult:` (this file); `## Output Summary`. Acceptance, all four required: the file exists with all named sections; its filename timestamp equals its `Timestamp:` field; `WhyFailingRunImpossible:` is non-empty; the text contains the literal `P3-T1` and the literal `inline` at least once each.
+
+### Phase 2 — Fix
+
+- [x] [P2-T1] Create `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` with exactly the Target Source A content, then run `CMD-CENSUS` on it and write FEATURE/evidence/regression-testing/p2-t1-helper-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line. Acceptance, all four required: `namespace QuickFiler.Test.TestSupport` 1, `internal static class DedicatedWorkerThread` 1, `internal static Exception Run(Action action)` 1, `new Thread(` 1, `IsBackground = true` 1, `thread.Join();` 1, `action();` 1, `distinct from every live thread by construction` 1; `Join(` equals `Join()`; `.Should()` 0, `Task.Run(` 0, `Thread.Sleep` 0, `Task.Delay` 0, `[Timeout` 0, `DoNotParallelize` 0, `Retry` 0; `LINES` at most 500. A PreToolUse refusal of the file creation is `PRE-IMPLEMENTATION GATE BLOCKED`: stop and report.
+
+- [x] [P2-T2] Register the two new files in `QuickFiler.Test/QuickFiler.Test.csproj` exactly as Target Source F states, then run `CMD-CENSUS` on the project file, `git diff --numstat HEAD -- QuickFiler.Test/QuickFiler.Test.csproj` and `git status --porcelain -- QuickFiler.Test/QuickFiler.Test.csproj`, and write FEATURE/evidence/regression-testing/p2-t2-csproj-census..md transcribing the three project-file `TOKEN` lines, the numstat line and the porcelain line. Acceptance, all three required: INCLUDE-AFFINITY 1, INCLUDE-PART2 1, INCLUDE-HELPER 1; the numstat line reads 2 added and 0 deleted; the porcelain line begins ` M` for the project file.
+
+- [x] [P2-T3] Create `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` with exactly the Target Source B content (the two verbatim test blocks copied from pre-edit lines 199 to 251 and 253 to 304 of the primary file with the two named substitutions each, the rewritten null-owner test, and `ClearViewerDispatcher` copied from pre-edit lines 357 to 371), then run `CMD-CENSUS` on it and write FEATURE/evidence/regression-testing/p2-t3-part2-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line. Acceptance, all four required: `DedicatedWorkerThread.Run(` 3, `ClearViewerDispatcher(` 2, `[TestMethod]` 3, `dedicated worker thread must not be` 3; `partial class` 1, `using QuickFiler.Test.TestSupport;` 1, `using System.Reflection;` 1, `owner.CheckAccess()` 1, `.BeNull(` 1, `unconditionally` 1; `Task.Run(` 0, `.GetAwaiter()` 0, `RunOnDedicatedWorkerThread` 0, `action();` 0, `Thread.Sleep` 0, `Task.Delay` 0, `[Timeout` 0, `DoNotParallelize` 0, `Retry` 0; `LINES` at most 500.
+
+- [x] [P2-T4] Apply the five Target Source C edits to `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs`, then run `CMD-CENSUS` on it, `git diff --numstat HEAD -- QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` and `git status --porcelain -- QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs`, and write FEATURE/evidence/regression-testing/p2-t4-primary-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line, the numstat line and the porcelain line. Acceptance, all five required: `partial class` 1 and `[TestMethod]` 4; `Task.Run(` 0, `.GetAwaiter()` 0, `RunOnDedicatedWorkerThread` 0, `ClearViewerDispatcher(` 0, `action();` 0, `new Thread(` 0, `IsBackground = true` 0, `thread.Join();` 0, `using System.Reflection;` 0, `dedicated worker thread must not be` 0, `distinct from every live thread by construction` 0, `DedicatedWorkerThread.Run(` 0; `LINES` at most 500 and at least 280; the numstat line reads at most 4 added and at least 197 deleted (the edits add the `partial` declaration line and two remark lines and delete 199 lines); the porcelain line begins ` M`.
+
+- [x] [P2-T5] Apply the Target Source D edits to `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs`, then run `CMD-CENSUS` on it and `git status --porcelain -- QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs`, and write FEATURE/evidence/regression-testing/p2-t5-boundary-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line and the porcelain line. Acceptance, all four required: `Task.Run(` 0 and `.GetAwaiter()` 3 (each one less than P0-T12); `DedicatedWorkerThread.Run(` 1, `using QuickFiler.Test.TestSupport;` 1, `.NotBe(` 1, `.BeNull(` 1, `dedicated worker thread must not be` 1, `ownerThreadId` 2; `executions.Should().Be(0)` 1, `cannot marshal` 1, `partial class` 1, `Thread.Sleep` 0, `Task.Delay` 0, `[Timeout` 0, `DoNotParallelize` 0; `LINES` at most 500.
+
+- [x] [P2-T6] Apply the three Target Source E edits to `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`, then run `CMD-CENSUS` on it, `git diff -U0 HEAD -- UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` and `git status --porcelain -- UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`, and write FEATURE/evidence/regression-testing/p2-t6-fileinfowrapper-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line, the porcelain line, and these counts over the diff's added lines (lines beginning with a single `+`): `ADDED-LENGTH:` (added lines containing `.Length`, excluding any line whose trimmed text is exactly `stream.Length.Should().BeGreaterThan(0);`, because that line is byte-identical to pre-edit line 66 and whether the diff reports it as added or as unchanged context depends on hunk alignment), `ADDED-OPENREAD:` (lines containing `OpenRead()`), `ADDED-FIXTUREPATH:` (lines containing `FixturePath`), `ADDED-OPEN-CREATE-WRITE:` (lines containing `.Open(`, `.Create(` or `.OpenWrite(`). Acceptance, all six required: `GetSolutionFile` 0, `TaskMaster.sln` 0, `AppDomain` 0, `File.Exists(` 0, `File.Create` 0, `File.WriteAll` 0, `File.Delete` 0, `Path.GetTemp` 0; `FileMode.` equals `FileMode.Open` (13) and `FileAccess.` equals `FileAccess.Read` (11); `FileShare.ReadWrite` 9, `Assembly.Location` 7, `FixturePath` 4, `using var sentinel = new FileStream(` 1, `BeSameAs(sentinel)` 1, `.Returns(decoy)` 0, `wrapper.OpenRead()` 2, `stream.CanRead.Should().BeTrue()` 1, `stream.Length.Should().BeGreaterThan(0)` 1, `[TestMethod]` 8; `ADDED-LENGTH: 0` (the only `.Length` in the rewritten tests is the excluded assertion line; the Arrange comment names no member), `ADDED-OPENREAD: 2` (the mock setup line and the `FileStream stream = wrapper.OpenRead();` line, which differs from pre-edit line 62), `ADDED-FIXTUREPATH: 4`, `ADDED-OPEN-CREATE-WRITE: 0` (the three metadata tests open no stream and call none of `Open`, `Create` or `OpenWrite`, AC10); `LINES` at most 500; the porcelain line begins ` M`.
+
+- [x] [P2-T7] Format and verify the five Write Set `.cs` files with the pinned CSharpier. Inside one payload: capture the SHA-256 of each of `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs`, `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs`, `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs`, `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs` and `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`; run `dotnet tool run csharpier format` with those five paths as arguments; capture the five hashes again; run `dotnet tool run csharpier check` with the same five paths; then run `CMD-CENSUS` on each of the five. Write FEATURE/evidence/regression-testing/p2-t7-csharpier-scoped..md with `Timestamp:`, `Command:` (all three), `EXIT_CODE:` (of the `check`), and an `Output Summary:` recording the ten hashes, `REWRITTEN:` as the number of files whose hash changed (the console line `Formatted 5 files in Xms.` is a processed-file count and is not this value), the `check` command's final summary line verbatim, and every `TOKEN` and `LINES` line per file. Acceptance, all three required: the `check` exits 0; every `post` value of the Token Census Expectations table holds for all five files (formatting re-wraps lines but every counted token is single-line by construction); every `LINES` value is at most 500. If any `LINES` exceeds 500, stop and report `FILE SIZE LIMIT EXCEEDED` with the file and value; do not shorten remarks to recover, because the remarks carry pinned census literals.
+
+- [x] [P2-T8] Build both test projects from the edited source with `CMD-BUILD-QF` (`TASKID` `p2-t8-qf`) and `CMD-BUILD-UCS` (`TASKID` `p2-t8-ucs`) and write FEATURE/evidence/regression-testing/p2-t8-build-after-fix..md with `Timestamp:`, `Command:` (`msbuild QuickFiler.Test\QuickFiler.Test.csproj /t:Build /m /p:Configuration=Debug /p:Platform=AnyCPU` and the UtilitiesCS.Test twin, resolved through vswhere, plus /nodeReuse:false), `EXIT_CODE:` (scoped to the QuickFiler.Test build; the other under `UCS-MSBUILD-EXIT:`), and an `Output Summary:` recording, per build, `CSC_OUT_LINES:`, `PROD_CSC_OUT_LINES:`, `ZERO_ERRORS_LINES:` and `DLL_ADVANCED:`. Acceptance, all three required: both exit codes 0; both `CSC_OUT_LINES:` at least 1 and both `DLL_ADVANCED: True` (the edited files were compiled into the assemblies the next task loads); both `ZERO_ERRORS_LINES:` at least 1.
+
+- [x] [P2-T9] Run the rewritten tests before any control, as confirming runs (the measured runs are P4-T5 and P4-T6). Run `CMD-VSTEST` four times: `ASSEMBLY-QF` with `FILTER-FOUR`, `NAMES-FOUR`, `TASKID` `p2-t9-four`; `ASSEMBLY-UCS` with `FILTER-OPENREAD`, `NAMES-FIW`, `TASKID` `p2-t9-openread`; `ASSEMBLY-QF` with `FILTER-AFFINITY-CLASS`, `NAMES-AFFINITY`, `TASKID` `p2-t9-affinity`; `ASSEMBLY-UCS` with `FILTER-FIW-CLASS`, `NAMES-FIW`, `TASKID` `p2-t9-fiw`. Write FEATURE/evidence/regression-testing/p2-t9-pass-before-controls..md with `Timestamp:`, `Command:` (all four), `EXIT_CODE:` (scoped to the first run; the other three under named lines) and an `Output Summary:` recording, per run, `RUNSETTINGS-HASH-NOW:`, the `COUNTERS` line, every `RESULT` line and every `MESSAGE` line. Acceptance, all five required: all four exit codes 0; `total` is 4, 1, 7 and 8 respectively with `failed` 0 in each (a different total means a stale assembly or an unregistered file and fails this task); every `RESULT` line reads `Passed`; the four `NAMES-FOUR` names, the seven `NAMES-AFFINITY` names and the eight `NAMES-FIW` names each appear in a `RESULT` line of the run that targets them; every `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`. If any test fails, the fix is wrong: repair within the Target Source contract, re-run P2-T7 through P2-T9, and record each iteration; do not proceed to P2-T10 with a failing run.
+
+- [x] [P2-T10] Commit the fix and the Phase 1 and Phase 2 evidence. Run `git add -- QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs QuickFiler.Test/QuickFiler.Test.csproj UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931`, then `git commit -m "test(931): remove scheduler and file-handle dependence from breadcrumb affinity and FileInfoWrapper tests"` (with, when the session requires an attribution trailer, a second `-m` paragraph in the bare-address form the Commits convention fixes; no angle bracket, dollar sign or backtick anywhere on the line), then `git ls-files --eol -- QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs QuickFiler.Test/QuickFiler.Test.csproj UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` (recorded verbatim as `EOL-BEFORE:`). For each of those six paths whose entry does not read w/crlf (the working-tree column), run one payload whose statement after the `Set-Location` is `Remove-Item -LiteralPath "PATH"`, with `PATH` the path in backslash separators, then, as a separate invocation, `git checkout -- ` (the same path in the forward-slash form the `git ls-files --eol` span uses). This makes the working-tree bytes identical to the bytes every later `git checkout` of that path writes under `core.autocrlf=true`; files created with the Write tool have LF endings (fact 23). Then run the same `git ls-files --eol` span again (`EOL-AFTER:`) and record the restored paths under `EOL-RESTORED:` (or `NONE`). Then run `git diff --exit-code HEAD -- QuickFiler.Test UtilitiesCS.Test`, `git show --name-only --format= HEAD`, `git rev-parse HEAD`, and the SHA-256 of each of the six code files, so every `FIX-HASH-` anchor is taken after the restore. Write FEATURE/evidence/regression-testing/p2-t10-fix-commit..md recording `COMMIT-EXIT:`, `EOL-BEFORE:`, `EOL-AFTER:`, `EOL-RESTORED:`, `FIX-HEAD:`, `FIX-HASH-AFF:`, `FIX-HASH-PART2:`, `FIX-HASH-HELPER:`, `FIX-HASH-BND:`, `FIX-HASH-CSPROJ:`, `FIX-HASH-FIW:` (the anchors every Phase 3 revert compares against), the `git show` path list verbatim, and `EXIT_CODE:` scoped to the `git diff --exit-code` span. Acceptance, all four required: `COMMIT-EXIT: 0`; `EXIT_CODE: 0`, proving the committed state equals the working tree for both test projects; the `git show` list contains exactly the six code paths plus paths under docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/ and nothing else; every `EOL-AFTER:` entry reads i/lf (index column) and w/crlf (working-tree column), and each `.cs` path not listed in `EOL-RESTORED:` has a hash equal to its P2-T7 post-format hash (a restored path differs from P2-T7 in line terminators only, which the `git diff --exit-code HEAD` span exiting 0 after the restore proves). This commit stages paths outside every exempt tree; a PreToolUse refusal is `PRE-IMPLEMENTATION GATE BLOCKED`: stop and report.
+
+### Phase 3 — Negative Controls (each rewritten guard test observed failing against a broken guard)
+
+- [x] [P3-T1] [expect-fail] Control M1, owner-only dispatcher guard disabled: apply mutation M1 (Target Source G) to QuickFiler/Viewers/BreadcrumbUiDispatcher.cs, run `CMD-CENSUS` on that file, `CMD-BUILD-QF` with `TASKID` `p3-t1`, then `CMD-VSTEST` with `ASSEMBLY-QF`, `FILTER-DISPATCHER`, `NAMES-FOUR`, `TASKID` `p3-t1`. Write FEATURE/evidence/regression-testing/mutation-owner-only-dispatcher-guard.md (fixed name) with `Timestamp:`, `Command:` (all three), `EXIT_CODE:` (the `VSTEST_EXIT_CODE:` of this mutated run; the artifact states that its exit-code row is scoped to the mutated run), `ExpectedExitCode: 1`, and a section `## Mutated run` recording the mutated file (QuickFiler/Viewers/BreadcrumbUiDispatcher.cs) and hunk (lines 276 to 277 replaced by `return true;`), `TOKEN return true; = N1 plus 1` and `TOKEN _ownerThreadId.HasValue = N2 minus 1` (with the P0-T12 values named), `MSBUILD_EXIT_CODE:`, `PROD_CSC_OUT_LINES:`, the runsettings file name, the isolation switch, the filter verbatim, `RUNSETTINGS-HASH-NOW:`, the `COUNTERS` line, every `RESULT` line and every `MESSAGE` line verbatim. Predicted failure (D-6): the test `Failed` with a `MESSAGE` line containing `to be 0, but found 1`. Acceptance, all five required: the two census transitions hold; `MSBUILD_EXIT_CODE: 0` and `PROD_CSC_OUT_LINES:` at least 1 (the mutated production file was compiled); `total` 1, `failed` 1; the `MESSAGE` line contains `to be 0, but found 1`; `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`. A passing run, or a `MESSAGE` naming a different assertion, is `MUTATION PREDICTION MISMATCH`: stop and report the text.
+
+- [x] [P3-T2] Revert control M1 and confirm: run `git checkout -- QuickFiler\Viewers\BreadcrumbUiDispatcher.cs`, then `git diff --exit-code HEAD -- QuickFiler\Viewers\BreadcrumbUiDispatcher.cs`, `git status --porcelain -- QuickFiler\Viewers\BreadcrumbUiDispatcher.cs` (backslash pathspecs per the Commits convention), `CMD-CENSUS` on that file, `CMD-BUILD-QF` with `TASKID` `p3-t2`, and `CMD-VSTEST` with `ASSEMBLY-QF`, `FILTER-DISPATCHER`, `NAMES-FOUR`, `TASKID` `p3-t2`. Append to FEATURE/evidence/regression-testing/mutation-owner-only-dispatcher-guard.md a section `## Revert and confirming run` recording `REVERT-DIFF-EXIT:` (the `git diff --exit-code` exit), the porcelain output verbatim (`EMPTY` when it printed nothing), `TOKEN return true; = N1`, `TOKEN _ownerThreadId.HasValue = N2`, `SHA256:` (the census line) beside the P0-T12 `SHA256` of the same file, `PROD_CSC_OUT_LINES:`, `CONFIRMING-RUN-EXIT:`, `RUNSETTINGS-HASH-NOW:`, the `COUNTERS` line and every `RESULT` line. Acceptance, all six required: `REVERT-DIFF-EXIT: 0`; the porcelain output is empty; both census values equal the P0-T12 values; the post-revert SHA256 equals the P0-T12 SHA256 of the same file; `PROD_CSC_OUT_LINES:` at least 1 (the reverted production file was recompiled before the confirming run); `CONFIRMING-RUN-EXIT: 0` with `total` 1, `passed` 1 and the `RESULT` line `Passed`.
+
+- [x] [P3-T3] [expect-fail] Control M2, null-owner escape replaced by the pre-#781 context-reference throw: apply mutation M2 (Target Source G) to QuickFiler/Viewers/ItemViewer.Breadcrumb.cs, run `CMD-CENSUS` on that file, `CMD-BUILD-QF` with `TASKID` `p3-t3`, then `CMD-VSTEST` with `ASSEMBLY-QF`, `FILTER-NULLOWNER`, `NAMES-FOUR`, `TASKID` `p3-t3`. Write FEATURE/evidence/regression-testing/mutation-null-owner-escape.md (fixed name) with the same field set and `## Mutated run` content as P3-T1 (mutated file QuickFiler/Viewers/ItemViewer.Breadcrumb.cs, hunk lines 435 to 438, `TOKEN System.Threading.SynchronizationContext.Current = 1`), `EXIT_CODE:` scoped to the mutated run, `ExpectedExitCode: 1`. Predicted failure (D-6): the test `Failed` at the `captured` null assertion with a `MESSAGE` line containing `InvalidOperationException` and `but found`; this is the discrimination the old `Task.Run` shape had only when the work item was not inlined. Acceptance, all five required: the census value is 1; `MSBUILD_EXIT_CODE: 0` and `PROD_CSC_OUT_LINES:` at least 1; `total` 1, `failed` 1; the `MESSAGE` line contains both `InvalidOperationException` and `but found`; `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`. A passing run, or a `MESSAGE` naming `NullReferenceException`, is `MUTATION PREDICTION MISMATCH`: stop and report.
+
+- [x] [P3-T4] Revert control M2 and confirm, with the P3-T2 mechanics applied to QuickFiler/Viewers/ItemViewer.Breadcrumb.cs (`git checkout`, anchored `git diff --exit-code HEAD`, scoped porcelain, `CMD-CENSUS`, `CMD-BUILD-QF` with `TASKID` `p3-t4`, `CMD-VSTEST` with `ASSEMBLY-QF`, `FILTER-NULLOWNER`, `NAMES-FOUR`, `TASKID` `p3-t4`), appending `## Revert and confirming run` to FEATURE/evidence/regression-testing/mutation-null-owner-escape.md, including the census `SHA256:` line beside the P0-T12 `SHA256` of the same file. Acceptance, all six required: `REVERT-DIFF-EXIT: 0`; the porcelain output is empty; `TOKEN System.Threading.SynchronizationContext.Current = 0`; the post-revert SHA256 equals the P0-T12 SHA256 of the same file; `PROD_CSC_OUT_LINES:` at least 1; `CONFIRMING-RUN-EXIT: 0` with `total` 1, `passed` 1 and the `RESULT` line `Passed`.
+
+- [x] [P3-T5] [expect-fail] Control M3, precondition run inline: apply mutation M3 (Target Source G) to `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs`, run `CMD-CENSUS` on it, `CMD-BUILD-QF` with `TASKID` `p3-t5`, then `CMD-VSTEST` with `ASSEMBLY-QF`, `FILTER-FOUR`, `NAMES-FOUR`, `TASKID` `p3-t5`. Write FEATURE/evidence/regression-testing/mutation-inline-precondition.md (fixed name) with the P3-T1 field set (mutated file `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs`, hunk: `action();` inserted before `Exception captured = null;`, `TOKEN action(); = 2`, `CSC_OUT_LINES:` instead of the production line count), `EXIT_CODE:` scoped to the mutated run, `ExpectedExitCode: 1`. Predicted failure (D-6): all four tests `Failed` at their in-thread distinctness precondition, each `MESSAGE` line containing `dedicated worker thread must not be`. Acceptance, all five required: `TOKEN action(); = 2`; `MSBUILD_EXIT_CODE: 0` and `CSC_OUT_LINES:` at least 1; `total` 4, `failed` 4; each of the four `MESSAGE` lines contains `dedicated worker thread must not be`; `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`. Any passing test is `MUTATION PREDICTION MISMATCH`: stop and report.
+
+- [x] [P3-T6] Revert control M3 and confirm, with the P3-T2 mechanics applied to `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` (`git checkout`, anchored `git diff --exit-code HEAD`, scoped porcelain, `CMD-CENSUS`, SHA-256 equality with `FIX-HASH-HELPER:`, `CMD-BUILD-QF` with `TASKID` `p3-t6`, `CMD-VSTEST` with `ASSEMBLY-QF`, `FILTER-FOUR`, `NAMES-FOUR`, `TASKID` `p3-t6`), appending `## Revert and confirming run` to FEATURE/evidence/regression-testing/mutation-inline-precondition.md. Acceptance, all five required: `REVERT-DIFF-EXIT: 0`; the porcelain output is empty; `TOKEN action(); = 1` and the SHA-256 equals `FIX-HASH-HELPER:` (the post-restore anchor of P2-T10, fact 23: the checkout writes the CRLF ending the anchor was taken over); `CSC_OUT_LINES:` at least 1 with `DLL_ADVANCED: True`; `CONFIRMING-RUN-EXIT: 0` with `total` 4, `passed` 4 and all four `RESULT` lines `Passed`.
+
+- [x] [P3-T7] [expect-fail] Control M4, OpenRead sentinel replaced: apply mutation M4 (Target Source G) to `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`, run `CMD-CENSUS` on it, `CMD-BUILD-UCS` with `TASKID` `p3-t7`, then `CMD-VSTEST` with `ASSEMBLY-UCS`, `FILTER-OPENREAD`, `NAMES-FIW`, `TASKID` `p3-t7`. Write FEATURE/evidence/regression-testing/mutation-openread-sentinel.md (fixed name) with the P3-T5 field set (mutated file `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`, hunk: the `decoy` declaration and the `.Returns(decoy)` change, `TOKEN .Returns(decoy) = 1`, `TOKEN Assembly.Location = 8`, `TOKEN BeSameAs(sentinel) = 1`), `EXIT_CODE:` scoped to the mutated run, `ExpectedExitCode: 1`. Predicted failure (D-6): the test `Failed` at the same-instance assertion with a `MESSAGE` line containing `to refer to`. Acceptance, all five required: the three census values hold; `MSBUILD_EXIT_CODE: 0` and `CSC_OUT_LINES:` at least 1; `total` 1, `failed` 1; the `MESSAGE` line contains `to refer to`; `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`. A passing run is `MUTATION PREDICTION MISMATCH`: stop and report.
+
+- [x] [P3-T8] Revert control M4 and confirm, with the P3-T6 mechanics applied to `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` (`git checkout`, anchored `git diff --exit-code HEAD`, scoped porcelain, `CMD-CENSUS`, SHA-256 equality with `FIX-HASH-FIW:`, `CMD-BUILD-UCS` with `TASKID` `p3-t8`, `CMD-VSTEST` with `ASSEMBLY-UCS`, `FILTER-OPENREAD`, `NAMES-FIW`, `TASKID` `p3-t8`), appending `## Revert and confirming run` to FEATURE/evidence/regression-testing/mutation-openread-sentinel.md. Acceptance, all five required: `REVERT-DIFF-EXIT: 0`; the porcelain output is empty; `TOKEN .Returns(decoy) = 0`, `TOKEN Assembly.Location = 7` and the SHA-256 equals `FIX-HASH-FIW:` (the post-restore anchor of P2-T10, fact 23); `CSC_OUT_LINES:` at least 1 with `DLL_ADVANCED: True`; `CONFIRMING-RUN-EXIT: 0` with `total` 1, `passed` 1 and the `RESULT` line `Passed`.
+
+- [x] [P3-T9] Prove the tree is clean after the controls: run `git diff --exit-code HEAD -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test`, `git status --porcelain -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test`, and the SHA-256 of each of the six Write Set code files, and write FEATURE/evidence/regression-testing/p3-t9-post-control-clean-tree..md with `Timestamp:`, `Command:`, `EXIT_CODE:` (the `git diff --exit-code` exit) and an `Output Summary:` recording the porcelain output verbatim (`EMPTY` when nothing printed) and the six hashes beside their `FIX-HASH-` anchors. Acceptance, all three required: `EXIT_CODE: 0`; the porcelain output is empty; all six hashes equal their P2-T10 anchors (taken after that task's line-ending restore, fact 23; since then each Write Set file has been written only by the controls' Edit-then-`git checkout` cycles, which end in the anchored CRLF form). This is the mechanical proof, for AC11 through AC15, that no temporary production edit and no control residue survives into Phase 4.
+
+### Phase 4 — Parallel-Suite Runs, Toolchain Loop, Coverage Comparison, Acceptance Check-Off and Commit
+
+The loop is P4-T1 through P4-T7 in order. When P4-T1 rewrites a Write Set file, the loop restarts from P4-T1 and P4-T10 records the iteration count; that formatter output is the only change to a Write Set file this phase admits. When any other task in the loop fails its acceptance, the executor reports the stop condition the task names and stops. No task in this phase edits any file outside the Write Set. An edit to a Write Set `.cs` file in Phase 4, other than the P4-T1 formatter output, is `WRITE SET CHANGED AFTER CONTROLS`: stop and request a plan revision, because the four negative controls of Phase 3 observed the committed P2-T10 state and an edited file would carry no fail-before evidence.
+
+- [x] [P4-T1] Run the repository-wide formatter `dotnet tool run csharpier format .` from the worktree root inside one payload that also captures, immediately before and immediately after the command, the SHA-256 of each of the five Write Set `.cs` files and the SHA-256 of the text printed by `git diff MERGE-BASE -- QuickFiler.Test UtilitiesCS.Test` (the anchored patch, because the formatter can rewrite a file the plan already changed without changing a name list), then runs `git status --porcelain -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test`. Write FEATURE/evidence/qa-gates/p4-t1-csharpier-format..md with `Timestamp:`, `Command:`, `EXIT_CODE:` and an `Output Summary:` recording the tool's `Formatted N files in Xms.` line verbatim (a processed-file count, not a rewrite count), the ten file hashes, each labelled with its file and as taken before or after the command (the five after-command values are the per-file after-format hashes P4-T9 compares against), `REWRITTEN:` (the number of the five whose hash changed), `FORMAT_CHANGED_TREE:` (whether the two patch hashes differ) and the porcelain output verbatim. Acceptance, all three required: `EXIT_CODE: 0`; `FORMAT_CHANGED_TREE:` and `REWRITTEN:` are recorded; the porcelain output lists only paths in the Write Set (any other path was clean at P0-T6 and cannot have been rewritten by a correct pinned formatter: `FORMAT SCOPE BREACH`, stop and report). When `REWRITTEN:` is greater than 0 the loop restarts from this task after `CMD-CENSUS` confirms the `post` token table still holds for the rewritten files; a second consecutive iteration with `REWRITTEN:` greater than 0 is `FORMAT NOT IDEMPOTENT`: stop and report. A line-ending rewrite by the formatter is formatter output under the Phase 4 preamble and is observed through `REWRITTEN:` like any other rewrite; fact 23 predicts none, because P2-T10 leaves every Write Set `.cs` file at the CRLF ending .editorconfig assigns to `.cs` files, and the prediction is recorded as met or not met by this task's value, never assumed.
+
+- [x] [P4-T2] Run `dotnet tool run csharpier check .` from the worktree root and write FEATURE/evidence/qa-gates/p4-t2-csharpier-check..md with `Timestamp:`, `Command:`, `EXIT_CODE:` and an `Output Summary:` quoting the `Checked N files in Xms.` line verbatim and recording `CHECKED-FILES:` and `CHECKED-DELTA:` as that N minus the P0-T6 `CHECKED-FILES:` value. Acceptance, both required: `EXIT_CODE: 0`; `CHECKED-DELTA:` is exactly 2 (this plan adds two `.cs` files and removes none; the project file is excluded by .csharpierignore). A non-zero exit restarts the loop from P4-T1.
+
+- [x] [P4-T3] Run the analyzer gate with `CMD-REBUILD` using the analyzer `GATEARGS` and `TASKID` `p4-t3` and write FEATURE/evidence/qa-gates/p4-t3-msbuild-analyzers..md with the P0-T7 field set plus `WARNINGS-DELTA:` (this run's `WARNINGS:` minus `ANALYZE-BASELINE-WARNINGS:`, an observation). Acceptance, all five required (D-8): `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; `QF_TEST_CSC_OUT_LINES:` and `UCS_TEST_CSC_OUT_LINES:` each at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`. A diagnostic naming a Write Set file is not repaired in this phase (Phase 4 preamble, `WRITE SET CHANGED AFTER CONTROLS`): stop and request a plan revision quoting the diagnostic; any other failure is reported and stops.
+
+- [x] [P4-T4] Run the nullable gate with `CMD-REBUILD` using the nullable `GATEARGS` and `TASKID` `p4-t4` (no Nullable property override, no incremental Build target) and write FEATURE/evidence/qa-gates/p4-t4-msbuild-nullable..md with the P0-T8 field set plus `WARNINGS-DELTA:` against `NULLABLE-BASELINE-WARNINGS:`. Acceptance, all five required: `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `_CSC_OUT_LINES:` at least 1; `WRITESET_DIAGNOSTIC_LINES: 0`. Same restart rule as P4-T3.
+
+- [x] [P4-T5] Run the full QuickFiler.Test assembly in the parallel regime (the measured run for AC1, AC2, AC4, AC6 and the QuickFiler half of AC16) with `CMD-VSTEST` using `ASSEMBLY-QF`, empty `FILTERARG`, `NAMES-QF` and `TASKID` `p4-t5`, and write FEATURE/evidence/regression-testing/parallel-suite-quickfiler-test.md (fixed name) with `Timestamp:`, `Command:` (naming TaskMaster.runsettings, the isolation switch and `no test-case filter`), `EXIT_CODE:` and an `Output Summary:` recording `RUNSETTINGS-HASH-NOW:`, the `COUNTERS` line, `RESULT_COUNT:`, `SEQUENCE_FILES:`, `COLLECTOR_LINES:`, every `RESULT` line (the eight `NAMES-QF` names), every `MESSAGE` line, and `NEWLY-FAILING:` (names in this run's failed set that are not in `BASELINE-FAILED-QF:`, or `NONE`). Acceptance, all six required: `EXIT_CODE: 0`; `failed` is 0 and `executed` is at least the P0-T10 QuickFiler `executed` value; `SEQUENCE_FILES: 0`; the eight `RESULT` lines are present and each reads `Passed` (this is the proof that the Part2 file and the helper compiled and were discovered, AC6); `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`; `NEWLY-FAILING: NONE`. A failing test in a Write Set file is not repaired in this phase (Phase 4 preamble, `WRITE SET CHANGED AFTER CONTROLS`): stop and request a plan revision with the `MESSAGE` line; any other failure, including a failure of `TryAddValuesAsync_UpdatesExistingValue` (D-12; spec AC16 requires zero failed tests and admits no re-run), is `AC16: NOT MET` by design, reported with the `MESSAGE` lines, and the run stops.
+
+- [x] [P4-T6] Run the full UtilitiesCS.Test assembly in the parallel regime (the measured run for AC8, AC9, AC10 and the UtilitiesCS half of AC16) with `CMD-VSTEST` using `ASSEMBLY-UCS`, `UCS-FILTERARG` (P0-T9), `NAMES-FIW` and `TASKID` `p4-t6`, and write FEATURE/evidence/regression-testing/parallel-suite-utilitiescs-test.md (fixed name) with the P4-T5 field set; `Command:` records the filter verbatim (or `no test-case filter`) and, when the filter is present, the four excluded class names and the sentence that the stall is pre-existing on main and covered by CI. Acceptance, all six required: `EXIT_CODE: 0`; `failed` 0 and `executed` at least 1; `SEQUENCE_FILES: 0`; the eight `NAMES-FIW` `RESULT` lines are present and each reads `Passed`; `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`; `NEWLY-FAILING: NONE`. Same failure handling as P4-T5: in particular a failure of `DictionaryExtensions_Tests.TryAddValuesAsync_UpdatesExistingValue`, which lives in this assembly, is `AC16: NOT MET` by design (D-12), recorded with its `MESSAGE` line, and the run stops without re-running.
+
+- [x] [P4-T7] Run the final repository-wide test and coverage pass by the route P0-T9 fixed (`CMD-COVERAGE-RUNNER` or `CMD-COVERAGE-DIRECT` with `STAGE` `final`, then, unless branch (d0) below applies, `CMD-COVERAGE-POST` with `STAGE` `final`, `RAW` being the value the Command Reference rule derives from the run: `True` under `DIRECT`; under `RUNNER` `True` when `COLLECT_FAILURE_MESSAGE:` is non-empty, else `False`; recorded as `RAW:`) and write FEATURE/evidence/qa-gates/coverage-final.md (fixed name) with the P0-T11 field set, plus `NEWLY-FAILING:` (names in `FAILED-SET:` not in the P0-T11 `FAILED-SET:`, or `NONE`). Under `RUNNER` a run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report. Branches, checked in the order (d0), (c), (b), (a), (d): (d0), checked first: a run with `SEQUENCE_FILES:` greater than 0 (DIRECT) or with no stage TRX at coverage\final-931.trx (the run payload's `TRX_PRESENT:` value under both routes) is `COVERAGE RUN ABORTED`: stop and report `COLLECT_FAILURE_MESSAGE:` or the last lines of the collector log with absolute paths replaced, do not run `CMD-COVERAGE-POST` (its first statement reads the stage TRX), and do not re-run; (a) exit 0 with `FAILED-SET:` empty and both floors met completes; (b) a non-empty `FAILED-SET:` naming a test in a Write Set file is not repaired in this phase (Phase 4 preamble, `WRITE SET CHANGED AFTER CONTROLS`): stop and request a plan revision with the `Failed tests:` summary line; a non-empty `FAILED-SET:` naming any other test, including `TryAddValuesAsync_UpdatesExistingValue` (D-12: no re-run), is `AC17: NOT MET`: stop and report with the `Failed tests:` summary line; (c) a floor failure under either route (a runner `THRESHOLD_MESSAGE:`, or, whenever `RAW:` is `True` (every `DIRECT` run, and a `RUNNER` run whose `COLLECT_FAILURE_MESSAGE:` is non-empty), a `LINE-FLOOR: NOT MET` or `BRANCH-FLOOR: NOT MET` line printed by `CMD-COVERAGE-POST`) is `AC17: NOT MET`: run `CMD-COVERAGE-POST` if it has not already run so the projection is recorded, record `FLOOR: NOT MET` with the message, and stop and report; (d) any outcome that (c), (b) and (a) do not decide, in particular a non-zero exit with an empty `FAILED-SET:`, is `COVERAGE RUN ABORTED` with the (d0) handling. Acceptance, all eight required: `EXIT_CODE: 0`; under `COVERAGE-ROUTE: DIRECT`, `SEQUENCE_FILES: 0`; the projection block contains the `UtilitiesCS` and `QuickFiler` package elements with `LINE` and `BRANCH` counters; the `First-party coverage:` line is present; the summary block's first line begins `Test run outcome:` and `FAILED-SET:` is empty on the completing run; `NEWLY-FAILING: NONE`; `LINE-FLOOR: MET` and `BRANCH-FLOOR: MET` as printed by `CMD-COVERAGE-POST` under either route (under `RUNNER` the runner's exit 0 has already asserted both floors and `THRESHOLD_MESSAGE:` is empty); the artifact contains no absolute path. coverage\final-931.jacoco.xml remains on disk for P4-T8.
+
+- [x] [P4-T8] Compare the final coverage projection against the baseline (AC18) by running `CMD-PACKAGE-COMPARE` and append to FEATURE/evidence/qa-gates/coverage-final.md a section `## Comparison against coverage-baseline.md` carrying the four `PACKAGE` lines verbatim, `BASELINE-FIRST-PARTY:` and `FINAL-FIRST-PARTY:` (the two `First-party coverage:` lines), and exactly one `COMPARABILITY:` line: `A` when the two first-party `lines` denominators differ by at most 1 percent of the baseline denominator, else `B` with the one-sentence note that the repository-wide totals were measured over different merged denominators and are recorded, not gated (D-7); and `CHANGED-PRODUCTION-LINES: 0` with the sentence that the P2-T10 `git show` path list contains no production path, so every changed line of this item is test code outside the coverage denominator and the policy's no-regression-on-changed-lines clause has an empty subject. Acceptance, all four required: no `PACKAGE` line reads `MISSING`; all four `PACKAGE` lines read `NOT-LOWER=True`; exactly one `COMPARABILITY:` line is present; `CHANGED-PRODUCTION-LINES: 0` is present and the P2-T10 path list it cites contains no path under QuickFiler/ or UtilitiesCS/. When a `PACKAGE` line reads `NOT-LOWER=False`, run P4-T7 once more as a second measurement (identical command, recorded as `MEASUREMENT: 2` in coverage-final.md) and re-run this comparison; a second `False` is `AC18: NOT MET`: stop and report with both projections quoted.
+
+- [x] [P4-T9] Audit file size and the token table after the repository-wide format by running `CMD-CENSUS` on each of the five Write Set `.cs` files and `QuickFiler.Test/QuickFiler.Test.csproj`, and write FEATURE/evidence/qa-gates/p4-t9-post-format-census..md transcribing every `TOKEN`, `LINES` and `SHA256` line per file. Acceptance, all three required: every `LINES` value for the five `.cs` files is at most 500 (AC5; a larger value is `FILE SIZE LIMIT EXCEEDED`: stop and report rather than shortening remarks); every `post` value of the Token Census Expectations table holds, including `action();` 1 in HELPER, `.Returns(decoy)` 0 and `Assembly.Location` 7 in FIW, INCLUDE-PART2 1 and INCLUDE-HELPER 1 (no control residue); each `.cs` `SHA256` equals the after-format hash that the final P4-T1 iteration recorded for that file; when every P4-T1 iteration of this run recorded `REWRITTEN: 0` (the loop started from P4-T1 exactly once), that value also equals the file's `FIX-HASH-` anchor (the post-restore P2-T10 value, fact 23), and otherwise the artifact records it under `POST-FORMAT-HASH-:` naming the iteration in which the rewrite occurred.
+
+- [x] [P4-T10] Close the toolchain loop and write FEATURE/evidence/qa-gates/toolchain-pass.md (fixed name) with `Timestamp:`, `Command:` (`reconciliation of P4-T1 through P4-T9`), `EXIT_CODE: 0` (scoped to the reconciliation), and an `Output Summary:` carrying one line per toolchain command of the final clean iteration in CLAUDE.md order, each naming the command, its exit code and its artifact: `dotnet tool run csharpier check .` (P4-T2), the analyzer rebuild (P4-T3) with its `SKIP_CORECOMPILE_LINES:` value, the nullable rebuild (P4-T4) with its `SKIP_CORECOMPILE_LINES:` value, and the MSTest-with-coverage route (P4-T7) with `COVERAGE-ROUTE:`; then the two parallel-suite runs (P4-T5, P4-T6); `ITERATIONS:` (the number of times the loop started from P4-T1); one `EXPECTATION-MET:` line per task P4-T1 through P4-T9; and `LOOP: CLEAN PASS`. Acceptance, all four required: the four toolchain lines each record exit 0; both `SKIP_CORECOMPILE_LINES:` values are 0; nine `EXPECTATION-MET:` lines each read `YES`; `LOOP: CLEAN PASS` is present. If any expectation is not met because P4-T1 rewrote a file, the loop restarts from P4-T1 and this task is re-run after it (the only admissible restart, Phase 4 preamble); any other unmet expectation is the stop condition its task names; nothing is written until every expectation holds.
+
+- [x] [P4-T11] Verify the footprint and scope boundary (AC3, AC15, AC16's diff clause) and write FEATURE/evidence/qa-gates/p4-t11-scope-boundary..md. Run: `git diff --name-only MERGE-BASE` (working tree); `git status --porcelain --untracked-files=all`; `git diff --name-only MERGE-BASE -- QuickFiler.Test`; `git diff --name-only MERGE-BASE -- .claude`; `git diff --name-only MERGE-BASE..HEAD -- .claude config` (the committed diff of the two trees the executor never commits to); `git log --oneline --diff-filter=A MERGE-BASE..HEAD -- docs\features\potential` (backslash pathspec per the Commits convention); `git diff --exit-code MERGE-BASE -- QuickFiler UtilitiesCS TaskMaster.runsettings scripts\vscode\TaskMaster.cli.runsettings config`; `git diff -U0 MERGE-BASE -- QuickFiler.Test UtilitiesCS.Test` (issued inside `CMD-ADDED-LINES`); and the SHA-256 of TaskMaster.runsettings. Record the two full path lists verbatim; `INHERITED-AND-EXCLUDED:` (the paths removed by Clause A and Clause B, each listed with its clause); `THIS-ITEM-FOOTPRINT:` (the union of the first two lists minus those); `QUICKFILER-TEST-CHANGED:` (the third list); `CLAUDE-CHANGED:` (the fourth list); `COMMITTED-CLAUDE-OR-CONFIG:` (the fifth list, or `NONE`); `INHERITED-PROMOTION-RECORD:` (the `git log` output verbatim: the promoted record docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md was committed on this branch deliberately before execution, lies in `INHERITED-CLAUSE-A:` because it is in the committed MERGE-BASE...HEAD diff, and is subtracted from `THIS-ITEM-FOOTPRINT:` under Clause A); `PRODUCTION-AND-CONFIG-DIFF-EXIT:`; over the added lines (single leading `+`) of the `-U0` diff, computed by `CMD-ADDED-LINES` and transcribed with its `ADDED-LINE-COUNT:`, `ADDED-DONOTPARALLELIZE:`, `ADDED-THREAD-SLEEP:`, `ADDED-TASK-DELAY:`, `ADDED-TIMEOUT:` (lines containing `[Timeout` or `Timeout=`), `ADDED-RETRY:` (lines containing `Retry`), `ADDED-WORKERS:` (lines containing the XML element form, the text Workers enclosed in angle brackets as an element tag, or the text `Workers =`; defined this way because the two remark lines moved verbatim into the Part2 file, pre-edit lines 210 and 266, contain the text Workers=0 inside a code element, which matches neither form, and a runsettings edit would carry the element form); `ADDED-SCOPE:` (lines containing the text Scope enclosed in angle brackets as an element tag, or the text `ExecutionScope`; the `ViewerScope` identifier in the moved tests matches neither); every `ADDED-` count is case-sensitive and counts lines; and `RUNSETTINGS-HASH-NOW:`. Acceptance, all nine required: `THIS-ITEM-FOOTPRINT:` is exactly the six Write Set code paths plus paths under docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/ and nothing else; `QUICKFILER-TEST-CHANGED:` is exactly the four QuickFiler.Test Write Set paths, so none of the eight files AC3 names and neither `[DoNotParallelize]` carrier changed; every path in `CLAUDE-CHANGED:` begins .claude/agent-memory/ (Clause B; agent memory writes are not this item's footprint); `COMMITTED-CLAUDE-OR-CONFIG: NONE`; `INHERITED-PROMOTION-RECORD:` is recorded; `PRODUCTION-AND-CONFIG-DIFF-EXIT: 0`; the seven `ADDED-` counts are all 0 over an `ADDED-LINE-COUNT:` of at least 1; `RUNSETTINGS-HASH-NOW:` equals `RUNSETTINGS-HASH:`; the porcelain span is present as the name-listing diff's companion.
+
+- [x] [P4-T12] Sweep the feature folder for host identifiers and raw documents (AC19) by running `CMD-SWEEP` and write FEATURE/evidence/qa-gates/p4-t12-hygiene-sweep..md with `Timestamp:`, `Command:`, `EXIT_CODE:` and an `Output Summary:` recording `FILES:`, `ACCOUNT-TOKEN-MATCHES:`, `PROFILE-LEAF-MATCHES:`, `MACHINE-TOKEN-MATCHES:`, `WORKTREE-ROOT-MATCHES:`, `USERS-PATH-MATCHES:` and `RAW-DOCUMENT-FILES:`; the tokens themselves are never written. Also run `git diff --name-only MERGE-BASE` with `git status --porcelain --untracked-files=all` and record `RAW-EXTENSION-PATHS:` as every listed path ending `.trx`, `.xml` or `.coverage` outside `INHERITED-CLAUSE-A:`, or `NONE`. Acceptance, all three required: the six match counts are 0; `RAW-DOCUMENT-FILES: 0`; `RAW-EXTENSION-PATHS: NONE`. A non-zero count is repaired by replacing the offending text with ``, ``, `` or `` in the artifact that carries it (never by deleting the artifact), or by removing a raw document from the feature folder, and the sweep is re-run until every count is 0.
+
+- [x] [P4-T13] Write the follow-up handoff record FEATURE/evidence/qa-gates/p4-t13-follow-up-handoff..md for the orchestrator, who files the potential entries through the promotion lifecycle's MCP tool (D-9; the executor creates nothing under docs/features/potential/). The record carries `Timestamp:` and, for each of the four follow-ups in the spec's Rollout list, a `short_name`, a one-paragraph body with the spec's file and line citations, and the sentence `not fixed under #931`: (1) `physicalfilesystemadapters-tests-open-repository-solution-file` (the same `GetSolutionFile` pattern, the swallowed `IOException` catches and the real solution-file opens in UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs); (2) `directoryinfowrapper-tests-enumerate-repository-solution-file` (UtilitiesCS.Test/HelperClasses/DirectoryInfoWrapper_Tests.cs lines 60, 79 and 381); (3) `breadcrumb-dispatchvalue-message-wording-broader-than-mechanism` (QuickFiler.Test/Viewers/BreadcrumbUiThreadDispatchTests.cs line 305, wording only); (4) `issue-900-handoff-misattributes-dispatchvalue-site-to-owner-thread-id-check` (documentation correction to the #900 follow-up handoff record). Acceptance, all three required: the record exists; it names the four `short_name` values exactly once each; it contains the literal `not fixed under #931` at least four times.
+
+Check-off protocol for P4-T14 through P4-T32: each task changes exactly one line of `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/spec.md`, the line beginning `- [ ] ACn. `, to begin `- [x] ACn. `, preserving the criterion text; its acceptance is that exactly one line begins `- [x] ACn. `, no line begins `- [ ] ACn. `, and no other AC line changed in that task. When the named evidence does not hold, the box is left unchecked and `ACn: NOT MET` is recorded with the failing value in FEATURE/evidence/qa-gates/p4-t-ac-checkoff..md; the task completes in either case.
+
+- [x] [P4-T14] Check off AC1 in FEATURE/spec.md. Evidence: P4-T9 census of BND (`Task.Run(` 0, `.GetAwaiter()` 3 down from 4, `DedicatedWorkerThread.Run(` 1, `ownerThreadId` 2, `.NotBe(` 1, `.BeNull(` 1, `executions.Should().Be(0)` 1, `cannot marshal` 1), P3-T1 (fails at the executions assertion when the guard is disabled) and P4-T5 (`Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` Passed).
+
+- [x] [P4-T15] Check off AC2 in FEATURE/spec.md. Evidence: P4-T9 census of PART2 (`Task.Run(` 0, `.GetAwaiter()` 0, `DedicatedWorkerThread.Run(` 3, `owner.CheckAccess()` 1, `.BeNull(` 1, `unconditionally` 1), P3-T3 (fails at the captured null assertion under the restored context-reference guard) and P4-T5 (`InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` Passed).
+
+- [x] [P4-T16] Check off AC3 in FEATURE/spec.md. Evidence: P4-T11 (`QUICKFILER-TEST-CHANGED:` is exactly the four QuickFiler.Test Write Set paths; `ADDED-DONOTPARALLELIZE: 0`).
+
+- [x] [P4-T17] Check off AC4 in FEATURE/spec.md. Evidence: P4-T9 census of AFF (`partial class` 1, `[TestMethod]` 4, `RunOnDedicatedWorkerThread` 0, `ClearViewerDispatcher(` 0) and PART2 (`partial class` 1, `[TestMethod]` 3, `ClearViewerDispatcher(` 2, `RunOnDedicatedWorkerThread` 0), and P4-T5 (the seven `NAMES-AFFINITY` names present and Passed, the same seven P0-T10 recorded, so no test was renamed or removed).
+
+- [x] [P4-T18] Check off AC5 in FEATURE/spec.md. Evidence: P4-T9 (`LINES` at most 500 for each of the five `.cs` files, measured after the repository-wide format).
+
+- [x] [P4-T19] Check off AC6 in FEATURE/spec.md. Evidence: P4-T9 census of CSPROJ (INCLUDE-PART2 1, INCLUDE-HELPER 1) and P4-T5 (the four named tests present and Passed in the committed QuickFiler.Test projection).
+
+- [x] [P4-T20] Check off AC7 in FEATURE/spec.md. Evidence: P4-T9 census of HELPER (`namespace QuickFiler.Test.TestSupport` 1, `internal static class DedicatedWorkerThread` 1, `internal static Exception Run(Action action)` 1, `new Thread(` 1, `IsBackground = true` 1, `thread.Join();` 1, `Join(` equals `Join()`, `distinct from every live thread by construction` 1, `.Should()` 0, `Thread.Sleep` 0, `Task.Delay` 0, `[Timeout` 0, `Retry` 0), the `DedicatedWorkerThread.Run(` counts 3 (PART2) plus 1 (BND), and P3-T5 (all four tests fail together when the helper runs the delegate inline).
+
+- [x] [P4-T21] Check off AC8 in FEATURE/spec.md. Evidence: P4-T9 census of FIW (`GetSolutionFile` 0, `TaskMaster.sln` 0, `AppDomain` 0, `File.Create` 0, `File.WriteAll` 0, `File.Delete` 0, `Path.GetTemp` 0, `FileMode.` equal to `FileMode.Open`, `FileAccess.` equal to `FileAccess.Read`) and P4-T6 (the four named tests present and Passed).
+
+- [x] [P4-T22] Check off AC9 in FEATURE/spec.md. Evidence: P4-T9 census of FIW (`using var sentinel = new FileStream(` 1, `Assembly.Location` 7, `FileShare.ReadWrite` 9, `BeSameAs(sentinel)` 1, `stream.CanRead.Should().BeTrue()` 1, `stream.Length.Should().BeGreaterThan(0)` 1), P3-T7 (fails at the same-instance assertion under a second stream) and P4-T6 (`OpenRead_ShouldReturnReadableStreamForWrappedFile` Passed).
+
+- [x] [P4-T23] Check off AC10 in FEATURE/spec.md. Evidence: P2-T6 (`ADDED-FIXTUREPATH: 4`, `ADDED-LENGTH: 0` under its alignment-independent definition, `ADDED-OPENREAD: 2`, `ADDED-OPEN-CREATE-WRITE: 0`, so the three metadata tests use the rooted literal, open no stream and assert none of the excluded members) and P4-T6 (the three tests Passed).
+
+- [x] [P4-T24] Check off AC11 in FEATURE/spec.md. Evidence: FEATURE/evidence/regression-testing/mutation-owner-only-dispatcher-guard.md (P3-T1 mutated run failing on `to be 0, but found 1` with the mutated file and hunk, the filter, the runsettings name and the isolation switch recorded; P3-T2 revert with `REVERT-DIFF-EXIT: 0`, empty porcelain and the confirming pass).
+
+- [x] [P4-T25] Check off AC12 in FEATURE/spec.md. Evidence: FEATURE/evidence/regression-testing/mutation-null-owner-escape.md (P3-T3 and P3-T4, with the same recorded fields).
+
+- [x] [P4-T26] Check off AC13 in FEATURE/spec.md. Evidence: FEATURE/evidence/regression-testing/mutation-inline-precondition.md (P3-T5: all four `MESSAGE` lines carry the precondition reason; P3-T6 revert and confirming pass).
+
+- [x] [P4-T27] Check off AC14 in FEATURE/spec.md. Evidence: FEATURE/evidence/regression-testing/mutation-openread-sentinel.md (P3-T7 failing on `to refer to`; P3-T8 revert and confirming pass).
+
+- [x] [P4-T28] Check off AC15 in FEATURE/spec.md. Evidence: P4-T11 (`THIS-ITEM-FOOTPRINT:` equals the six Write Set code paths plus feature-folder documents; `PRODUCTION-AND-CONFIG-DIFF-EXIT: 0`; every `CLAUDE-CHANGED:` path is agent memory outside this item's footprint; `COMMITTED-CLAUDE-OR-CONFIG: NONE`, so no commit on this branch touched .claude or config; `INHERITED-PROMOTION-RECORD:` names the deliberately pre-committed promoted record that Clause A subtracts) and P3-T9 (no control residue).
+
+- [x] [P4-T29] Check off AC16 in FEATURE/spec.md. Evidence: FEATURE/evidence/regression-testing/parallel-suite-quickfiler-test.md and FEATURE/evidence/regression-testing/parallel-suite-utilitiescs-test.md (P4-T5 and P4-T6: exit 0, `failed` 0, the runsettings file, the isolation switch and the filter recorded verbatim, `RUNSETTINGS-HASH-NOW:` unchanged) and P4-T11 (the seven `ADDED-` counts 0).
+
+- [x] [P4-T30] Check off AC17 in FEATURE/spec.md. Evidence: FEATURE/evidence/qa-gates/toolchain-pass.md (P4-T10: `LOOP: CLEAN PASS`, four toolchain commands at exit 0, both `SKIP_CORECOMPILE_LINES:` 0).
+
+- [x] [P4-T31] Check off AC18 in FEATURE/spec.md. Evidence: FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-final.md (P0-T11, P4-T7 and P4-T8: both projections with the `First-party coverage:` line, four `PACKAGE` lines `NOT-LOWER=True`).
+
+- [x] [P4-T32] Check off AC19 in FEATURE/spec.md. Evidence: P4-T12 (six match counts 0, `RAW-DOCUMENT-FILES: 0`, `RAW-EXTENSION-PATHS: NONE`).
+
+- [x] [P4-T33] Commit the final state, write the closure record, and leave the source and feature trees clean. Steps, in order: (1) run `CMD-SWEEP` once more and stop with `HYGIENE SWEEP FAILED` if any count is non-zero; (2) run `git add -- QuickFiler.Test UtilitiesCS.Test docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931` then `git commit -m "docs(931): evidence, acceptance check-off and plan state for the uncontrolled-environment test fix"` (with, when the session requires an attribution trailer, a second `-m` paragraph in the bare-address form the Commits convention fixes; no angle bracket, dollar sign or backtick anywhere on the line), recording `COMMIT-1-EXIT:`; (3) write FEATURE/evidence/qa-gates/p4-t33-closure..md with `Timestamp:`, `Command:` (the git spans of this task), `COMMIT-1-EXIT:`, `HEAD-AFTER-COMMIT:` (`git rev-parse HEAD`), the confirming footprint lists `git diff --name-only MERGE-BASE..HEAD -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test` and `git diff --name-only origin/main...HEAD -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test` (each must list exactly the six Write Set code paths; the step-2 `git add` span is their companion), the pointer to P4-T13, and the acceptance-criteria status summary in the exact form the acceptance-criteria-tracking skill requires (`Source:` FEATURE/spec.md, `Total AC items: 19`, `Checked off (delivered): ` counted from the `- [x] AC` lines on disk, `Remaining (unchecked): <19 minus n>`, `Items remaining:` listing any unchecked criterion text or `none`); (4) tick every remaining unchecked checkbox in `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/plan.2026-09-28T20-01.md`, including this task's own; (5) run `git add -- docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931` then `git commit --amend --no-edit`, recording `COMMIT-2-EXIT:` in the executor's final message (the artifact cannot record it); (6) run `git status --porcelain -- QuickFiler QuickFiler.Test UtilitiesCS UtilitiesCS.Test docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931` and `git rev-parse HEAD`; (7) append `EXIT_CODE:` (scoped to the step-6 porcelain span), `POST-AMEND-PORCELAIN:` (`EMPTY` or the lines verbatim) and `HEAD-BEFORE-FINAL-AMEND:` (the step-6 `git rev-parse HEAD` value, which the step-8 amend supersedes) to the closure record; (8) run `git add -- docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931` then `git commit --amend --no-edit` once more, recording `COMMIT-3-EXIT:` in the executor's final message, and write no file afterwards; (9) run `git rev-parse HEAD` and report its value as `FINAL-HEAD:` in the executor's final message only (no artifact can record the commit that contains it). Paths under .claude/agent-memory/ are outside every span above by pathspec and are not this task's residual. Acceptance, all five required: `COMMIT-1-EXIT: 0`; both confirming footprint lists are exactly the six Write Set code paths; the status summary reports `Total AC items: 19` and its `Checked off` count equals the number of `- [x] AC` lines in FEATURE/spec.md; `POST-AMEND-PORCELAIN: EMPTY`; the executor's final message reports the plan path, `FINAL-HEAD:` (from step 9), `COMMIT-2-EXIT:`, `COMMIT-3-EXIT:`, the status summary and the P4-T13 follow-up pointer. A PreToolUse refusal at any git span is `PRE-IMPLEMENTATION GATE BLOCKED`: stop and report the refusal text verbatim; do not alter hooks, checkpoints, permission configuration or another item's files.
+
diff --git a/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/policy-audit.2026-09-29T20-15.md b/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/policy-audit.2026-09-29T20-15.md
new file mode 100644
index 000000000..63ef8b418
--- /dev/null
+++ b/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/policy-audit.2026-09-29T20-15.md
@@ -0,0 +1,278 @@
+# Policy Compliance Audit: tests-depend-on-uncontrolled-environment (Issue #931)
+
+- Component: QuickFiler.Test and UtilitiesCS.Test (test-only change; no production file changed)
+- Branch: bug/tests-depend-on-uncontrolled-environment-931
+- Head under review: ce744bbba3db4701e070785fd782c8b45289f2da (origin/main c4ff0e2be0bc9c51acc43dacd2cc5954a448676c merged at 55a50e922 with no conflicts)
+- Resolved base: origin/main (merge base for the pre-merge evidence: 177b6d78e1b2408e5aedbd794cef3aad6b7fb372; post-merge evidence measured at 55a50e922)
+- Work mode: full-bug (spec.md is the sole acceptance-criteria source)
+- Audit timestamp: 2026-09-29T20-15
+- Reviewer: feature-review agent (no-Bash run; evidence read from committed projections, the current worktree files, the git-ignored raw coverage documents left in the worktree, and the orchestrator-supplied git facts)
+- Total blocking findings in this artifact: 0
+- Total non-blocking findings in this artifact: 8
+
+## Executive Summary
+
+Verdict: PASS. Zero blocking findings.
+
+The branch rewrites two thread-identity tests and four file-handle tests so that their outcomes no longer depend on thread-pool scheduling or on which other process holds the repository solution file. It introduces one shared internal test helper (`DedicatedWorkerThread.Run`), splits a 490-line test class into two partial files (294 and 202 lines), and registers both new files in `QuickFiler.Test.csproj`. Six code files changed; every one is a test-project file. No file under `QuickFiler/`, `UtilitiesCS/`, `.claude/` or `config/` changed (orchestrator-supplied `git diff --name-only origin/main...HEAD`, corroborated by `evidence/qa-gates/p4-t11-scope-boundary.2026-09-29T09-44.md` and `p4-t33-closure.2026-09-29T09-47.md`).
+
+Gate results (pre-merge Phase 4 and post-merge re-run, both exit 0): csharpier check clean (1625 files); analyzer rebuild 0 errors / 0 warnings / 0 skipped CoreCompile; TreatWarningsAsErrors rebuild 0 errors / 0 warnings / 0 skipped CoreCompile; QuickFiler.Test 1468 then 1469 executed, 0 failed; UtilitiesCS.Test 4922 then 4924 executed, 0 failed; repository-wide coverage run 7320 then 7323 executed, 0 failed. C# first-party coverage 85.32% lines / 79.73% branches pre-merge and 85.31% lines / 79.73% branches post-merge, both above the 85% line and 75% branch floors: C# coverage verdict PASS.
+
+Four negative controls each show the rewritten test failing against a deliberately broken guard or sentinel and passing after the revert, with porcelain output proving no residual production change. Committed evidence is projections only; the feature folder contains no `.trx`, `.xml` or `.coverage` file and no absolute host path, account name or host name.
+
+Non-blocking findings: PA-1 spec AC15 wording omits the promotion-lifecycle record that the branch necessarily carries (disclosed by the plan and the footprint gate); PA-2 `quality-tiers.yml` absent at repo root (pre-existing); PA-3 coverage-floor documentation conflict (CLAUDE.md 80%/90% versus `.claude/rules` 85%/75%), both satisfied here; PA-4 post-merge UtilitiesCS package line rate 0.893789 is below the pre-merge baseline 0.893884, attributable to production edits origin/main contributed plus run-to-run collector variance, with zero production lines changed by this item; PA-5 canonical `artifacts/csharp/coverage.xml` not emitted in the worktree, evidence held as committed projections plus git-ignored raw documents; PA-6 PR-context artifacts absent in the worktree, scope taken from orchestrator-supplied git facts; PA-7 disclosed command substitutions (`/nodeReuse:false`, DIRECT coverage route); PA-8 repository test layout `.Test/` rather than `tests/` (pre-existing convention).
+
+## Rejected Scope Narrowing
+
+None detected. The caller's prompt supplied a code diff restricted to QuickFiler.Test and UtilitiesCS.Test together with the complete `git diff --name-only origin/main...HEAD` list (seven entries including the feature folder and the promoted potential record), and this audit covers the complete list. The caller's binding no-Bash directive constrains the verification method, not the audit scope; where it prevented a direct observation the check is recorded as UNVERIFIED with the reason (section 8). The caller's instruction not to raise the four unfiled follow-ups as a finding is a disposition instruction about work the spec explicitly places out of scope (spec `## Rollout & Follow-up`), not a narrowing of language or file coverage; it is honoured and recorded in section 8.
+
+## Evidence Location Compliance
+
+- Files in the branch diff under `artifacts/baselines/`, `artifacts/qa/`, `artifacts/evidence/` or `artifacts/coverage/`: none. The orchestrator-supplied name list contains no `artifacts/` path; a Glob of `artifacts/**` in the worktree returns nothing (the worktree has no `artifacts/` directory).
+- All evidence written by execution lives under `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/evidence/{baseline,regression-testing,qa-gates}/`, the canonical `/evidence//` layout. 43 evidence files pre-merge plus 8 post-merge files.
+- `validate_evidence_locations.py --root .`: not run (no Bash in this run). Disposition by inspection: PASS. Recorded as UNVERIFIED-by-script in section 8.
+- Canonical coverage artifact reconciliation: `artifacts/csharp/coverage.xml` is absent in the worktree. The committed coverage evidence is the CLAUDE.md-mandated projection form (`coverage-baseline.md`, `coverage-final.md`, `post-merge-coverage-final.md`), and the raw documents remain git-ignored under `coverage/` in the worktree (`baseline-931`, `final-931`, `postmerge-931`, `postmerge2-931` in both `.cobertura.xml` and `.jacoco.xml` forms). The reviewer read the four `.jacoco.xml` documents directly and confirmed each equals its committed projection counter for counter. Treated as artifact present (see finding PA-5).
+- EVIDENCE_LOCATION_OVERRIDE_REJECTED: none (no caller instruction named a non-canonical evidence path).
+
+## 1. General Unit Test Policy Compliance
+
+### 1.1 Core principles
+
+| Principle | Verdict | Evidence |
+|---|---|---|
+| Independence | PASS | Each rewritten test constructs and disposes its own viewer, dispatcher or stream; no shared static state introduced (diff read in full). Full assemblies pass under Workers=0 / ClassLevel parallelism (`parallel-suite-quickfiler-test.md`, `parallel-suite-utilitiescs-test.md`, post-merge twins). |
+| Isolation | PASS | Each test targets one guard or one wrapper member; the in-thread precondition assertions name the guard under test in their reason strings. |
+| Fast execution | PASS | Each rewritten test starts and joins exactly one thread; the post-merge QuickFiler.Test run of 1469 tests and UtilitiesCS.Test run of 4924 tests completed in the recorded runs with no timeout. |
+| Determinism | PASS | The defect being fixed is non-determinism. The distinct-thread precondition now holds by construction (a `Thread` the test creates is never the calling thread); the file test opens a read-shared handle on the test host's own loaded image, which no other process can deny. Negative control M3 (`mutation-inline-precondition.md`) proves the preconditions are live. |
+| Readability | PASS | Every rewritten test carries a `` and `` explaining the mechanism and the issue reference; reason strings are explicit. |
+
+### 1.2 Coverage requirements
+
+Languages with changed files on the branch: C# only (`.cs` x 5 and `.csproj` x 1). TypeScript, Python and PowerShell have zero changed files.
+
+### Coverage Evidence Checklist
+
+- C# baseline coverage artifact: `evidence/baseline/coverage-baseline.md` (committed JaCoCo package projection plus first-party summary; raw `coverage/baseline-931.jacoco.xml` and `.cobertura.xml` git-ignored in the worktree, read directly by the reviewer) - PASS
+- C# post-change coverage artifact: `evidence/qa-gates/coverage-final.md` (pre-merge, MEASUREMENT 2) and `evidence/qa-gates/post-merge-coverage-final.md` (post-merge, MEASUREMENT 2); raw `coverage/final-931.*` and `coverage/postmerge2-931.*` git-ignored in the worktree - PASS
+- TypeScript baseline coverage artifact: `N/A - out of scope`
+- TypeScript post-change coverage artifact: `N/A - out of scope`
+- PowerShell baseline coverage artifact: `N/A - out of scope`
+- PowerShell post-change coverage artifact: `N/A - out of scope`
+- Python baseline coverage artifact: `N/A - out of scope`
+- Python post-change coverage artifact: `N/A - out of scope`
+- Per-language comparison summary: section 1.2.1 of this document
+
+### 1.2.1 Per-Language Coverage Comparison
+
+- C#: Baseline: 85.32% lines (56085/65737) / 79.73% branches (13595/17052) -> Post-change: 85.32% lines (56085/65737) / 79.73% branches (13595/17052). Change: 0.00% lines / 0.00% branches (identical first-party totals on the pre-merge final MEASUREMENT 2; per-package UtilitiesCS and QuickFiler LINE and BRANCH counters equal to baseline; the post-merge re-run reads 85.31% lines (56080/65736) / 79.73% branches (13597/17054), see section 5.3). New/changed-code coverage: N/A - no new executable production code (test-only change; zero production lines changed). Disposition: PASS. Evidence: evidence/baseline/coverage-baseline.md; evidence/qa-gates/coverage-final.md; evidence/qa-gates/post-merge-coverage-final.md; worktree coverage/*.jacoco.xml read directly.
+- TypeScript: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero TypeScript files changed on this branch.
+- PowerShell: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero PowerShell files changed on this branch.
+- Python: Baseline: N/A. Post-change: N/A. Change: N/A. Disposition: N/A. Evidence: N/A - zero Python files changed on this branch.
+
+### 1.2.2 Coverage Artifact State
+
+**Coverage Metrics by Language:**
+
+| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage |
+|---|---|---|---|---|---|---|
+| C# | 6 (5 .cs, 1 .csproj; all test-project files) | 8 rewritten or relocated; 1469 + 4924 executed post-merge | PASS (0 failed) | 85.32% lines / 79.73% branches | 85.32% lines / 79.73% branches (pre-merge final); 85.31% lines / 79.73% branches (post-merge) | N/A - no new production code |
+| TypeScript | 0 | 0 | N/A | N/A | N/A | N/A |
+| PowerShell | 0 | 0 | N/A | N/A | N/A | N/A |
+| Python | 0 | 0 | N/A | N/A | N/A | N/A |
+
+C# threshold evaluation (single line for the record): C# repository-wide first-party coverage 85.31% lines and 79.73% branches post-merge (85.32% / 79.73% pre-merge) meets the 85% line floor and the 75% branch floor of `.claude/rules/quality-tiers.md`, and the 80% / 75% floors of CLAUDE.md UT2: C# coverage verdict PASS.
+
+Per-file production coverage on the classes the rewritten tests exercise, read from the raw Cobertura documents (class rows, identical in baseline, final and post-merge documents): `QuickFiler\Viewers\BreadcrumbUiDispatcher.cs` line-rate 1.000, branch-rate 0.972; `UtilitiesCS\HelperClasses\FileSystem\FileInfoWrapper.cs` line-rate 1.000, branch-rate 0.750. No changed-line regression is possible: zero production lines changed (`CHANGED-PRODUCTION-LINES: 0` in `coverage-final.md`, confirmed by the name-only diff).
+
+### 1.3 Scenario completeness
+
+| Scenario class | Verdict | Evidence |
+|---|---|---|
+| Positive flows | PASS | Owner-thread admission tests retained in the primary partial (4 tests); `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` positive escape. |
+| Negative flows | PASS | Two cross-thread `ThrowsBoundaryDiagnostic` tests; `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` rejection path. |
+| Edge cases | PASS | Null-owner escape; owner-only (null context) dispatcher. |
+| Error handling | PASS | Exception type, message content and non-`ObjectDisposedException` asserted. |
+| Concurrency | PASS | Every cross-thread case runs on a dedicated thread with an in-thread distinctness precondition; full suites pass under class-level parallelism. |
+| State transitions | PASS | `BreadcrumbCoordinator` identity asserted before and after the repeated initialization. |
+
+### 1.4 Structure, diagnostics, external dependencies
+
+- Arrange-Act-Assert: PASS. The three relocated tests and the rewritten null-owner test carry explicit `// Arrange`, `// Act`, `// Assert` markers; `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` follows the unmarked compact style of its file (see code-review CR-3, informational).
+- Clear failure messages: PASS. Every precondition assertion has a reason string; negative controls captured them verbatim (`mutation-inline-precondition.md`).
+- No external dependencies: PASS. No network, database, external process or repository-tracked file. The `OpenRead` sentinel is the running host's own loaded assembly image opened read-only with `FileShare.ReadWrite` (six pre-existing opens of the same shape exist in the same file).
+- Temporary files: PASS. No `File.Create`, `File.WriteAll`, `File.Delete`, `Path.GetTempFileName`, `Path.GetTempPath` in `FileInfoWrapper_Tests.cs` (Grep of the current file); the only `FileMode` is `Open` and the only `FileAccess` is `Read`. The rooted literal `C:\Repo\fixture.sln` is never opened or created.
+- Determinism infrastructure: PASS. No `Thread.Sleep`, `Task.Delay`, `[Timeout]`, retry attribute or loop, `DateTime.Now`, or RNG in the added lines (`p4-t11-scope-boundary` seven `ADDED-` counts 0 over 306 added lines; reviewer Grep of the four QuickFiler.Test files confirms `Task.Run` 0 and `Thread.Sleep` 0; the only `GetAwaiter().GetResult()` occurrences are the pre-existing `PumpSynchronizationContext` drain at lines 322-333 of `BreadcrumbPopupBoundaryCoverageTests.cs`, unchanged).
+- Parallel-regime invariants: PASS. `[DoNotParallelize]` occurrences in QuickFiler.Test remain exactly the two pre-existing ones (`Helper Classes/EmailMoveMonitorTests.cs:24`, `Helper Classes/ViewerQueueStaticWrapperTests.cs:11`); `TaskMaster.runsettings` SHA-256 unchanged across every run (`RUNSETTINGS-HASH-NOW` equals the P0-T4 value in every projection).
+- Test file location: PASS with note. Tests live in `QuickFiler.Test/` and `UtilitiesCS.Test/`, the repository's established `.Test/` convention; no test file is colocated with production source. The `tests/` mirror-tree wording of `.claude/rules/general-unit-test.md` is a cross-repository rule not adopted by this repository's C# layout (PA-8, pre-existing).
+
+## 2. General Code Change Policy Compliance
+
+| Requirement | Verdict | Evidence |
+|---|---|---|
+| Bugfix workflow step 1 (failing regression test first) | PASS with disclosed substitution | `evidence/regression-testing/fail-before-exception.2026-09-29T09-07.md` explains that a committed failing run is structurally impossible without mutating process-global thread-pool state or spawning a handle-holding process (both prohibited); plan D-13 substitutes four negative controls (M1-M4) as the deterministic observed-failing evidence. All four controls show the rewritten test failing against the mutation and passing after the revert. |
+| Bugfix workflow step 2 (minimal, targeted fix) | PASS | Two affected sites rewritten; the twenty UNAFFECTED `Task.Run` sites untouched (name-only diff excludes all eight files AC3 names); no production file changed. |
+| Bugfix workflow step 3 (full toolchain, restart on change) | PASS | `toolchain-pass.md` records one clean iteration in CLAUDE.md order; `post-merge-toolchain-pass.md` repeats all gates at 55a50e922. |
+| Simplicity / reusability | PASS | The private helper is promoted to one shared `internal static` type with an identical body; duplication removed rather than added. |
+| Separation of concerns | PASS | No I/O added to production; test helper contains only threading glue. |
+| Fail fast / no broad catch | PASS with note | `DedicatedWorkerThread.Run` catches `Exception` on the worker and returns it to the caller for assertion; this is the capture mechanism at a test-helper boundary, documented in its remarks (code-review CR-2). |
+| 500-line file limit | PASS | Current line counts (reviewer Read of each file; matches `p4-t9-post-format-census`): `DedicatedWorkerThread.cs` 48; `ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` 202; `ItemViewerBreadcrumbThreadAffinityTests.cs` 294 (was 490); `BreadcrumbPopupBoundaryCoverageTests.cs` 386 (was 361); `FileInfoWrapper_Tests.cs` 357 (was 359). `QuickFiler.Test.csproj` 570 lines is a project file, exempt. |
+| Naming, docs, comments | PASS | `PascalCase` type and member, `camelCase` locals; XML ``/`` on the helper and every rewritten test; comments explain why. |
+| Dependencies | PASS | No package added; `packages.config` untouched. The `.csproj` analyzer version strings changed only through the merge from origin/main (MSTest.Analyzers 4.4.1, Meziantou.Analyzer 3.0.290), taken from main. |
+| Public API compatibility | PASS | `DedicatedWorkerThread` is `internal` to the test assembly; no production API touched. |
+| Supporting documents updated | PASS | spec.md acceptance criteria checked off; plan tasks checked off; evidence tree complete. |
+| Committed Test Evidence Format (CLAUDE.md) | PASS | Projections only: JaCoCo package projections, one-line first-party summaries, TRX-derived summaries stating derived figures ("Skipped 0, derived as total minus executed"). No `.trx`, `.xml`, `.coverage` under the feature folder (Glob) or in the diff name list. |
+| Tonality | PASS | Evidence artifacts and code comments are neutral and factual. |
+
+## 3. Language-Specific Code Change Policy Compliance
+
+C# (`.claude/rules/csharp.md`, CLAUDE.md C#1-C#7):
+
+| Requirement | Verdict | Evidence |
+|---|---|---|
+| Formatting via `dotnet tool run csharpier` | PASS | `p4-t1-csharpier-format` REWRITTEN 0; `p4-t2-csharpier-check` exit 0, 1625 files (delta +2 for the two new `.cs` files); `post-merge-csharpier-check` exit 0, no file reported. |
+| Analyzer rebuild (`/t:Rebuild`, EnableNETAnalyzers, EnforceCodeStyleInBuild) | PASS | `p4-t3-msbuild-analyzers` exit 0, 0 errors, 0 warnings, SKIP_CORECOMPILE_LINES 0, both test assemblies compiled; `post-merge-msbuild-analyzers` exit 0, 0/0, SKIP 0, merged analyzer versions loaded (MSTest.Analyzers.4.4.1 x36, Meziantou.Analyzer.3.0.290 x32). |
+| Nullable / TreatWarningsAsErrors rebuild (`/t:Rebuild`, no `/p:Nullable=enable`) | PASS | `p4-t4-msbuild-nullable` exit 0, 0/0, SKIP 0, "no Nullable property override"; `post-merge-msbuild-nullable` exit 0, 0/0, SKIP 0. |
+| Approved command forms | PASS with disclosure | Each msbuild invocation adds `/nodeReuse:false` (plan D-16; additive, changes no diagnostic) to avoid resident node-reuse workers, the very mechanism behind the #906 failure. Recorded verbatim in each artifact (PA-7). |
+| Explicit `using` directives, `internal` for non-public | PASS | `System.Reflection` removed from the primary partial and added to Part2 where `FieldInfo` moved; helper is `internal static`. |
+| No `dotnet format` | PASS | Not used anywhere in the evidence. |
+| Analyzer stack / severity ordering | PASS (not touched) | No `.editorconfig` or `` change originates on this branch. |
+| Banned symbols (`Thread.Sleep`, `Task.Delay`, `DateTime.Now`, `Random.Shared`) | PASS | None added (section 1.4). |
+| Prohibited behaviours (weakening assertions, sleeps/retries/timing hacks, unrun toolchain) | PASS | Assertions were added (in-thread precondition, `captured` null, `BeSameAs(sentinel)`), none removed; no timing hack; toolchain evidence complete. |
+
+## 4. Language-Specific Unit Test Policy Compliance
+
+C# unit test policy (CLAUDE.md CUT1-CUT3, `.claude/rules/csharp.md` Testing Standards):
+
+| Requirement | Verdict | Evidence |
+|---|---|---|
+| MSTest framework (`[TestClass]`, `[TestMethod]`) | PASS | Both partials share one `[TestClass]` on the primary declaration (plan D-2); every rewritten test carries `[TestMethod]`; no xUnit/NUnit. |
+| Moq for mocks | PASS | `Mock(MockBehavior.Strict)` for the `OpenRead` seam; `Mock(MockBehavior.Strict)` in the affinity tests. |
+| FluentAssertions preferred | PASS | All assertions use `.Should()`; no MSTest `Assert` introduced. |
+| Test toolchain step 4 (MSTest with coverage route) | PASS with disclosure | COVERAGE-ROUTE DIRECT (plan D-4): the runner's own inner `dotnet-coverage` collect around `vstest.console.exe` with the runner's functions, because the runner hard-codes its filter and cannot apply the recorded shell-icon exclusion; the same three committed forms are produced. Exit 0, 7320 then 7323 executed, 0 failed, LINE-FLOOR MET, BRANCH-FLOOR MET: PASS. |
+| Deterministic test rules (no PATH/profile/cwd assumptions) | PASS | The old `AppDomain.CurrentDomain.BaseDirectory` walk-up to `TaskMaster.sln` is removed; the fixture is a rooted literal and the stream is the host's own image. |
+| Seam-based mocking for filesystem | PASS | The existing internal `FileInfoWrapper(IFileInfo)` seam is used; no seam added to production. |
+| New module/class coverage >= 90% | PASS (vacuous) | No new production module, class or method; `DedicatedWorkerThread` is test code outside the denominator. |
+| Coverage regression on changed lines | PASS | Zero changed production lines. |
+
+## 5. Test Coverage Detail
+
+### 5.1 Baseline (pre-edit, 2026-09-29T09-05, merge base 177b6d78e)
+
+First-party: lines 56085/65737 (85.32%), branches 13595/17052 (79.73%). Package counters (JaCoCo projection, verified against `coverage/baseline-931.jacoco.xml`): QuickFiler LINE 10461 covered / 2293 missed, BRANCH 2518 / 699; UtilitiesCS LINE 38816 / 4608, BRANCH 9411 / 1858. Sum of the nine package LINE counters: 56085 covered, 9652 missed, 65737 total (reviewer arithmetic agrees with the first-party line).
+
+### 5.2 Final (pre-merge, 2026-09-29T09-42, MEASUREMENT 2)
+
+First-party: lines 56085/65737 (85.32%), branches 13595/17052 (79.73%). QuickFiler LINE 10461 / 2293, BRANCH 2518 / 699; UtilitiesCS LINE 38816 / 4608, BRANCH 9411 / 1858. All four PACKAGE comparisons NOT-LOWER=True. MEASUREMENT 1 read QuickFiler LINE one covered line lower (10460) on an identical denominator with zero production change and was re-measured once per plan D-7; D-7 repeats the measurement, not a test, and no test failed in either measurement.
+
+### 5.3 Post-merge (55a50e922, 2026-09-29T19-59, MEASUREMENT 2)
+
+First-party: lines 56080/65736 (85.31%), branches 13597/17054 (79.73%). QuickFiler LINE 10461 / 2293, BRANCH 2518 / 699 (equal to baseline); UtilitiesCS LINE 38811 / 4612 (rate 0.893789 versus baseline 0.893884, NOT-LOWER=False), BRANCH 9413 / 1858 (rate 0.835152, NOT-LOWER=True).
+
+Reviewer assessment of the UtilitiesCS LINE finding (PA-4, non-blocking): the executor's per-file attribution is corroborated by the raw Cobertura class rows. `UtilitiesCS\NewtonsoftHelpers\SDIL Reader\ILGlobals.cs` reads line-rate 0.95 (38/40) in baseline and final and 0.947368 (36/38) post-merge; `UtilitiesCS\Threading\UiThread.cs` reads 0.977444 (130/133) in baseline and final and 0.977612 (131/134) post-merge. Those two files were changed by origin/main (issue #930), not by this branch. Removing 2 covered lines and adding 1 covered line yields 38815/43423 = 0.893881, already below 0.893884 by 0.000003 on arithmetic alone; the remaining 4 covered lines move between unchanged files across the two identical measurements, which is collector variance. The comparison of a post-merge run against a pre-merge baseline is not like-for-like (denominator 43424 -> 43423) and no baseline exists at the merged main. This item changed zero production lines, so the no-regression-on-changed-lines clause has an empty subject. AC18 is defined on `coverage-final.md` (pre-merge, like-for-like), where every package comparison holds.
+
+### 5.4 Floors
+
+| Floor source | Line floor | Branch floor | Pre-merge | Post-merge | Verdict |
+|---|---|---|---|---|---|
+| `.claude/rules/quality-tiers.md`, `general-unit-test.md` | 85% | 75% | 85.32% / 79.73% | 85.31% / 79.73% | PASS (line margin 0.31 points post-merge) |
+| CLAUDE.md UT2 (maintainer decision 2026-09-11, #563) | 80% | 75% | 85.32% / 79.73% | 85.31% / 79.73% | PASS |
+
+The two floor documents disagree (PA-3, pre-existing); both are satisfied.
+
+## 6. Test Execution Metrics
+
+| Run | Assembly / route | Runsettings | Filter | Total | Passed | Failed | Evidence |
+|---|---|---|---|---|---|---|---|
+| Baseline | QuickFiler.Test | TaskMaster.runsettings, /InIsolation | none | 1468 | 1468 | 0 | `evidence/baseline/test-run-baseline.md` |
+| Baseline | UtilitiesCS.Test | TaskMaster.runsettings, /InIsolation | shell-icon exclusion (4 classes named) | 4922 | 4922 | 0 | same |
+| Baseline | 9 assemblies, coverage route DIRECT | TaskMaster.cli.runsettings, /InIsolation | LiveOutlook + shell-icon exclusion | 7320 | 7320 | 0 | `evidence/baseline/coverage-baseline.md` |
+| Post-fix | QuickFiler.Test | TaskMaster.runsettings, /InIsolation | none | 1468 | 1468 | 0 | `evidence/regression-testing/parallel-suite-quickfiler-test.md` |
+| Post-fix | UtilitiesCS.Test | TaskMaster.runsettings, /InIsolation | shell-icon exclusion | 4922 | 4922 | 0 | `evidence/regression-testing/parallel-suite-utilitiescs-test.md` |
+| Post-fix | 9 assemblies, coverage route DIRECT (x2) | TaskMaster.cli.runsettings, /InIsolation | LiveOutlook + shell-icon exclusion | 7320 | 7320 | 0 | `evidence/qa-gates/coverage-final.md` |
+| Post-merge | QuickFiler.Test | TaskMaster.runsettings, /InIsolation | none | 1469 | 1469 | 0 | `evidence/qa-gates/post-merge-parallel-suite-quickfiler-test.md` |
+| Post-merge | UtilitiesCS.Test | TaskMaster.runsettings, /InIsolation | shell-icon exclusion | 4924 | 4924 | 0 | `evidence/qa-gates/post-merge-parallel-suite-utilitiescs-test.md` |
+| Post-merge | 9 assemblies, coverage route DIRECT (x2) | TaskMaster.cli.runsettings, /InIsolation | LiveOutlook + shell-icon exclusion | 7323 | 7323 | 0 | `evidence/qa-gates/post-merge-coverage-final.md` |
+
+Test-count arithmetic: QuickFiler.Test 1468 before and after the split proves the three relocated tests were discovered from the Part2 file and none was lost; the post-merge +1 is a test main contributed (`QfcItemController.UiThreadDispatcherFixtureTests.cs`). UtilitiesCS.Test 4922 before and after; post-merge +2 from main's changes to `ILGlobals_Tests.cs` and `UiThreadApartmentMeasurement_Tests.cs`. The eight `ItemViewerBreadcrumbThreadAffinityTests` and `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` names and the eight `FileInfoWrapper_Tests` names are listed as Passed in every relevant projection.
+
+Negative controls (each: mutated run exit 1 with the predicted failure text; revert proven by `git diff --exit-code HEAD` 0, porcelain EMPTY, SHA-256 equal to the anchor; confirming run exit 0):
+
+| Control | Mutation | Failed test(s) and message excerpt | Evidence |
+|---|---|---|---|
+| M1 | `BreadcrumbUiDispatcher.IsCurrentBoundary` thread-id branch replaced by `return true;` | `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction`: "Expected executions to be 0, but found 1" | `mutation-owner-only-dispatcher-guard.md` |
+| M2 | `ItemViewer.ThrowIfOffUiBoundary` null-owner `return;` replaced by the pre-#781 context-reference throw | `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow`: "Expected captured to be ... but found System.InvalidOperationException" | `mutation-null-owner-escape.md` |
+| M3 | `action();` inserted as first statement of `DedicatedWorkerThread.Run` | all four dedicated-thread tests fail at their precondition ("dedicated worker thread must not be ...") | `mutation-inline-precondition.md` |
+| M4 | mock `OpenRead()` pointed at a second `FileStream` | `OpenRead_ShouldReturnReadableStreamForWrappedFile`: "Expected stream to refer to ..." | `mutation-openread-sentinel.md` |
+
+## 7. Code Quality Checks
+
+| Check | Command / method | Result |
+|---|---|---|
+| Confidentiality masking scan | Grep of the feature folder for the developer account name, the `:/Users` and `:\Users` prefixes, the 8.3 short-name alias of the profile directory, `Program Files`, `DESKTOP-`, `LAPTOP-`, `runUser`, `MachineName`, `COMPUTERNAME`, UNC prefixes; Grep `-o` for every drive-letter path | PASS. Zero account, profile, host or `Program Files` matches. The only drive-letter paths are the fixture literal `C:\Repo` (spec.md:89, plan lines 95/130/695, research line 259). The one `COMPUTERNAME` hit is the plan's sweep command reading `$env:COMPUTERNAME` as a token source, not a value. Placeholders `` used in the mutation projections. |
+| Suppression scan (added lines) | Read of the full diff | PASS. No `#pragma warning disable`, `[SuppressMessage]`, `[ExcludeFromCodeCoverage]`, or `.editorconfig` change. |
+| Workflow change scan | Name-only diff | PASS. No `.github/workflows` file changed on this branch. |
+| Raw evidence document scan | Glob `**/*.{trx,xml,coverage}` under the feature folder; diff name list | PASS. Zero matches. |
+| Coverage exclusion policy | Read of `coverage.config` | PASS (not changed). The `` excludes name only third-party modules (Deedle, FSharp, Castle.Core, FluentAssertions, Moq, Microsoft.Testing, MSTest); no production path excluded; file not in the diff. |
+| Architecture boundaries | Read of the diff | PASS (the diff contains no production code; the test code adds no VSTO/Interop reference beyond the pre-existing `System.Windows.Threading.Dispatcher` and WinForms `Panel` uses already in these files). |
+| Runsettings integrity | `RUNSETTINGS-HASH-NOW` in every projection | PASS. 199408CA...7FFA in every run, equal to P0-T4. |
+
+## 8. Gaps and Exceptions
+
+| ID | Severity | Blocking | Finding | Evidence | Disposition |
+|---|---|---|---|---|---|
+| PA-1 | Low | Non-blocking | Spec AC15 states the changed-file set "equals the Write Set entries plus documents inside the feature folder", but the branch diff also carries `docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md`, written by the promotion-lifecycle commit (e13757267) before execution. The plan (D-11, Execution Conventions Clause A) defines the footprint gate as subtracting inherited paths, and `p4-t11-scope-boundary` records the subtraction explicitly as `INHERITED-PROMOTION-RECORD`. The AC's protective purpose (no production, `.claude`, or `config` change; no stray file) is met. | `p4-t11-scope-boundary.2026-09-29T09-44.md` lines 91-94, 123-124; plan D-11 | Accepted as a spec-wording residual; AC15 graded PASS with disclosure in the feature audit. Recommend the spec template name the promoted record in future footprint criteria. |
+| PA-2 | Low | Non-blocking | `quality-tiers.yml` is absent at the repository root, so tier-dependent gates (property tests, mutation score) cannot be evaluated for any project. Pre-existing on main; outside the diff. | Glob `quality-tiers.yml` returns nothing | Pre-existing; record only. |
+| PA-3 | Low | Non-blocking | Coverage-floor documentation conflict: CLAUDE.md UT2 states 80% line / 90% new-code; `.claude/rules/general-unit-test.md` and `quality-tiers.md` state 85% line / 75% branch. Both are satisfied here (85.31% / 79.73% post-merge). | Section 5.4 | Pre-existing; report against both; the policy-compliance-order skill lists CLAUDE.md first. |
+| PA-4 | Low | Non-blocking | Post-merge UtilitiesCS package LINE rate 0.893789 is below the pre-merge baseline 0.893884 in both post-merge measurements. Attributable to production edits from origin/main (`ILGlobals.cs` -2 lines / -2 covered; `UiThread.cs` +1 / +1; both verified in the raw Cobertura class rows) plus 4 covered lines of run-to-run variance in unchanged files. Zero production lines changed by this item. | `post-merge-coverage-final.md`; section 5.3 | Not a regression of this branch; AC18 is defined on the pre-merge like-for-like comparison, which holds. No remediation. |
+| PA-5 | Info | Non-blocking | Canonical `artifacts/csharp/coverage.xml` not emitted in the worktree. Coverage evidence is the CLAUDE.md-mandated committed projections plus the git-ignored raw documents under `coverage/`, which the reviewer read and reconciled. The session checkout holds a stale Cobertura document at that canonical path from another branch (line-rate 0.848316), which is not this branch's evidence. | Section "Evidence Location Compliance" | Artifact treated as present; C# coverage verdict PASS from the reconciled figures. |
+| PA-6 | Info | Non-blocking | `artifacts/pr_context.summary.txt` / `.appendix.txt` absent in the worktree and the PR-context MCP unavailable in this no-Bash run; scope taken from the orchestrator-supplied `git diff --name-only origin/main...HEAD` list and the supplied three-dot code patch. UNVERIFIED by the reviewer: byte-identity of the eight AC3 files (established instead by their absence from the name-only diff), `validate_evidence_locations.py` execution, and a live `git merge-base`. | Caller prompt; `p4-t33-closure` diff listings | Best-effort assumptions documented; no finding depends on the unverified items. |
+| PA-7 | Info | Non-blocking | Two disclosed command substitutions: `/nodeReuse:false` appended to every msbuild invocation (plan D-16) and the DIRECT coverage route replacing the runner entry point because the runner hard-codes its filter (plan D-4). Neither changes a diagnostic or a committed form. | `toolchain-pass.md`; `coverage-final.md`; plan D-4, D-16 | Accepted. |
+| PA-8 | Info | Non-blocking | Repository C# test layout is `.Test/`, not the `tests/` mirror tree named in `general-unit-test.md`. Pre-existing convention; the new files follow it and no test is colocated with production source. | `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` | Pre-existing; record only. |
+
+Follow-ups deliberately not filed on this branch (four potential entries in `evidence/qa-gates/p4-t13-follow-up-handoff.2026-09-29T09-46.md`): the promotion tools write under `docs/features/potential/`, which would contradict AC15, so the orchestrator files them from a separate branch and names them in the PR body. Not a finding against this branch (caller instruction, consistent with spec `## Rollout & Follow-up`).
+
+## 9. Summary of Changes
+
+- `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` (new, 48 lines): `internal static Exception Run(Action)` runs the delegate on a dedicated background thread, joins it without a timeout, returns the captured exception or null; no assertion, sleep, delay, timeout or retry.
+- `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` (490 -> 294 lines): class becomes `partial`; retains the four owner-thread admission tests, `InertOperations`, and the three nested helper types; loses the three cross-thread tests, `ClearViewerDispatcher` and `RunOnDedicatedWorkerThread`; `using System.Reflection` removed.
+- `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` (new, 202 lines): the three cross-thread tests now calling `DedicatedWorkerThread.Run`; `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` rewritten to capture the owning `Dispatcher` before it is cleared and assert `owner.CheckAccess()` is false inside the delegate; `ClearViewerDispatcher` relocated.
+- `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs` (361 -> 386 lines): `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` rewritten from `Task.Run` + blocking wait to `DedicatedWorkerThread.Run` with an in-thread `CurrentManagedThreadId` distinctness assertion; remark added.
+- `QuickFiler.Test/QuickFiler.Test.csproj`: two `Compile Include` entries added in the neighbouring backslash form (lines 99, 229); analyzer version strings updated by the merge from main only.
+- `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` (359 -> 357 lines): `GetSolutionFile()` and the `AppDomain` walk-up removed; three metadata tests use the rooted literal `FixturePath`; `OpenRead` test routed through the internal `FileInfoWrapper(IFileInfo)` seam with a strict mock returning a test-owned `FileStream` over the test assembly image, asserting `BeSameAs(sentinel)`, `CanRead` and `Length > 0`.
+- Feature folder: research record, spec, plan, 43 pre-merge and 8 post-merge evidence projections.
+
+## 10. Compliance Verdict
+
+PASS. Blocking findings: 0. Non-blocking findings: 8 (PA-1 to PA-8), none requiring remediation. No `remediation-inputs` artifact is produced.
+
+## Appendix A: Test Inventory
+
+Rewritten or relocated tests (all `[TestMethod]`, MSTest, FluentAssertions, Moq where a mock is used):
+
+| Test | File | Change | Status (post-merge run) |
+|---|---|---|---|
+| `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` | `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs` | rewritten (dedicated thread, in-thread id precondition) | Passed |
+| `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` | `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` | relocated and rewritten (owner captured before clear, in-thread `CheckAccess()` precondition) | Passed |
+| `InitializeBreadcrumbPipeline_WorkerThread_ThrowsBoundaryDiagnostic` | same | relocated; helper call retargeted | Passed |
+| `ConfigureBreadcrumbDropDown_WorkerThread_ThrowsBoundaryDiagnostic` | same | relocated; helper call retargeted | Passed |
+| `Properties_ShouldMirrorWrappedFileInfo` | `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` | rewritten (rooted literal fixture) | Passed |
+| `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory` | same | rewritten (rooted literal fixture) | Passed |
+| `OpenRead_ShouldReturnReadableStreamForWrappedFile` | same | rewritten (seam + test-owned sentinel) | Passed |
+| `ToString_ShouldDelegateToWrappedFileInfo` | same | rewritten (rooted literal fixture) | Passed |
+
+Unchanged tests in the touched classes (all Passed): `InitializeBreadcrumbPipeline_ConstructedInsideDispatcherOperation_SucceedsUnderDifferentAmbientContext`, `InitializeBreadcrumbPipeline_OwningThreadNullAmbientContext_DoesNotThrow`, `InitializeBreadcrumbPipeline_OwningThreadDifferentPlainContext_DoesNotThrow`, `ConfigureBreadcrumbDropDown_OwningThreadInsideDispatcherOperation_DoesNotThrow`; `Constructor_WhenFileInfoIsNull_ThrowsArgumentNullException`, `PropertyDelegates_ShouldMirrorMockedIFileInfo`, `StreamAndCopyMethods_ShouldDelegateToWrappedIFileInfo`, `AccessControlAndLifecycleMethods_ShouldDelegateToWrappedIFileInfo`; the four other tests of `BreadcrumbPopupBoundaryCoverageTests`.
+
+Test helper (not a test): `QuickFiler.Test.TestSupport.DedicatedWorkerThread.Run(Action)`, four call sites.
+
+## Appendix B: Toolchain Commands Reference
+
+Commands as recorded in `evidence/qa-gates/toolchain-pass.md` and `post-merge-toolchain-pass.md` (CLAUDE.md order; each exit 0 in the final pass):
+
+1. `dotnet tool run csharpier format .` (REWRITTEN 0) then `dotnet tool run csharpier check .` (Checked 1625 files).
+2. `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` plus `/nodeReuse:false` (0 errors, 0 warnings, SKIP_CORECOMPILE_LINES 0).
+3. `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true` plus `/nodeReuse:false` (0 errors, 0 warnings, SKIP_CORECOMPILE_LINES 0).
+4. MSTest-with-coverage route, DIRECT: `dotnet-coverage collect --output-format cobertura --settings coverage\effective-coverage-931.config -- vstest.console.exe <9 test assemblies> "/Settings:scripts\vscode\TaskMaster.cli.runsettings" /InIsolation "/TestCaseFilter:TestCategory!=LiveOutlook&" "/ResultsDirectory:coverage\test-results\931\" "/Logger:trx;LogFileName=-931.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"`, post-processed with the runner's own helpers into the JaCoCo package projection and first-party summary: exit 0, floors met, PASS.
+
+Parallel-suite runs: `vstest.console.exe "/Settings:TaskMaster.runsettings" /InIsolation [shell-icon exclusion filter for UtilitiesCS.Test] "/ResultsDirectory:coverage\test-results\931\" "/Logger:trx;LogFileName=.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"`.
+
+Reviewer verification method (this run): Read, Grep and Glob over the worktree only; no command executed. Git facts (head, base, merge commit, name-only diff) were supplied by the orchestrator and cross-checked against `p4-t11-scope-boundary` and `p4-t33-closure`.
diff --git a/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/research/2026-09-28T20-15-tests-depend-on-uncontrolled-environment-research.md b/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/research/2026-09-28T20-15-tests-depend-on-uncontrolled-environment-research.md
new file mode 100644
index 000000000..bcb4c9a02
--- /dev/null
+++ b/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/research/2026-09-28T20-15-tests-depend-on-uncontrolled-environment-research.md
@@ -0,0 +1,320 @@
+# Research: tests depend on uncontrolled environment (issue #931)
+
+- Issue: #931 (consolidates #905 and #906)
+- Work mode: full-bug; run `bugs-2026-09-28`
+- Branch: `bug/tests-depend-on-uncontrolled-environment-931`
+- Inputs read: `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/issue.md`, `spec.md`, `plan.2026-09-28T20-01.md`; `docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md`; the #900 feature folder evidence under `docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/`.
+- Evidence basis: every file and line cited below was read from the current tree of this worktree with `Read`/`Grep`. No command was executed; no test was run. Line numbers are those of the current tree, which differ from the issue text in places (noted where relevant).
+
+## 0. Summary of findings
+
+1. The #900 pattern is present at `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:228-238` (in-thread precondition) and `:385-403` (`RunOnDedicatedWorkerThread`).
+2. `QuickFiler.Test` contains exactly 22 `Task.Run` call sites (derived twice, see the numeric section). Exactly **2 are AFFECTED**: `BreadcrumbPopupBoundaryCoverageTests.cs:58` and `ItemViewerBreadcrumbThreadAffinityTests.cs:332`. The other 6 cited candidates are UNAFFECTED because the guard they exercise decides by ambient `SynchronizationContext` reference identity or by an executing-callback marker, never by thread identity; 14 further sites are TCS completions or thread-agnostic.
+3. `FileInfoWrapper` already has a seam: `internal FileInfoWrapper(IFileInfo)` at `UtilitiesCS/HelperClasses/FileSystem/FileInfoWrapper.cs:21-24`, reachable through `InternalsVisibleTo("UtilitiesCS.Test")` at `UtilitiesCS/Properties/AssemblyInfo.cs:19`. No production change is required.
+4. A `MemoryStream` cannot replace the `OpenRead()` fixture because `IFileInfo.OpenRead()` returns the concrete `FileStream` type (`UtilitiesCS/Interfaces/IHelperClasses/IFileInfo.cs:26`). The test-owned stream must be a `FileStream`; the repository's established handle-free-of-contention shape is a read-only, `FileShare.ReadWrite` open of the test's own loaded assembly (already used in the same file at lines 189-224). The three property/cast/`ToString` tests need no handle at all.
+5. Parallel regime: `TaskMaster.runsettings:4-7` and `scripts/vscode/TaskMaster.cli.runsettings:4-7` both set `Workers=0`, `Scope=ClassLevel`. None of the seven candidate classes nor `FileInfoWrapper_Tests` carries `[DoNotParallelize]`.
+
+---
+
+## A. Distinct-thread defect (issue #905 portion)
+
+### A1. The canonical #900 / PR #904 shape (verified in the current tree)
+
+Helper, `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:385-403`:
+
+```csharp
+private static Exception RunOnDedicatedWorkerThread(Action action)
+{
+ Exception captured = null;
+ var thread = new Thread(() =>
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception error)
+ {
+ captured = error;
+ }
+ });
+ thread.IsBackground = true;
+ thread.Start();
+ thread.Join();
+ return captured;
+}
+```
+
+In-thread precondition placed before the guarded call, `:228-238` (the second instance is identical at `:277-291`):
+
+```csharp
+Exception captured = RunOnDedicatedWorkerThread(() =>
+{
+ bool isOwnerThread = scope.Viewer.UiDispatcher.CheckAccess();
+ isOwnerThread
+ .Should()
+ .BeFalse(
+ "the dedicated worker thread must not be the thread that constructed "
+ + "the viewer, or the boundary assertion would pass vacuously"
+ );
+ scope.Viewer.InitializeBreadcrumbPipeline(provider.Object, operations);
+});
+```
+
+Shape: `new Thread(...)`, `IsBackground = true`, `Start()`, untimed `Join()`, exception marshalled back by field, precondition asserted inside the delegate, no apartment state set. The XML remarks at `:203-217` and `:377-384` record the rationale (a `Task.Run` work item queued from a pool thread lands on that thread's local queue and a blocking wait can run it inline; a constructed `Thread` is distinct from every live thread by construction; the `Join()` parks no pool slot waiting on another pool slot).
+
+Other dedicated-thread helpers exist in the project but serve different purposes and are not the pattern: `Controllers/QfcItemController.TestSupport.cs:251-271` (`StartRunningDispatcher`, STA + `Dispatcher.Run()`), `Controllers/QfcItemController.UiThreadDispatcherFixture.cs:175-203` (parked STA dispatcher), `Controllers/BayesianPerformanceController.TestSupport.cs:16-53` (STA viewer host), `TestSupport/WinFormsPumpHost.cs:53` (WinForms pump), `Helper Classes/EmailMoveMonitorTests.cs:281-287` (marshal-target thread inside a test delegate).
+
+### A2. Complete enumeration of `Task.Run` call sites in `QuickFiler.Test` (22 sites)
+
+Guard semantics that decide the classification (all verified in `QuickFiler/Viewers/BreadcrumbUiDispatcher.cs`):
+
+- `Dispatch(Action)` (`:71-151`) calls `IsCurrentBoundary()` (`:255-278`). With a captured context (`_context != null`) the boundary is `ReferenceEquals(SynchronizationContext.Current, _context)` (`:269-272`); **thread identity is never consulted**. Only when `_context == null` (owner-only test dispatcher from `CreateForCurrentThreadTests()` `:62-65`, or the private 3-argument constructor with a null context) does the guard compare `Environment.CurrentManagedThreadId` with `_ownerThreadId` (`:276-277`).
+- `DispatchValue` (`:157-235`) never calls `IsCurrentBoundary()`. It runs inline only when `ReferenceEquals(_executingDispatcher, this)` (`:166`); with `_context == null` it faults with "cannot marshal cross-thread UI work" for **every** other caller, on any thread (`:180-188`); otherwise it posts.
+- `BreadcrumbBridgeCoordinator`'s public 2-argument constructor captures a context-backed dispatcher (`QuickFiler/Viewers/BreadcrumbBridgeCoordinator.cs:39-43`); inbound messages go `OnMessageReceived` -> `ObserveInboundAsync` -> `_dispatcher.Dispatch(...)` (`:273-315`).
+- `ItemViewer.ThrowIfOffUiBoundary` (`QuickFiler/Viewers/ItemViewer.Breadcrumb.cs:432-447`): returns when `UiDispatcher == null` (`:435-438`), else throws when `!owning.CheckAccess()` (`:440-446`). `_uiDispatcher = Dispatcher.CurrentDispatcher` is captured at `QuickFiler/Viewers/ItemViewer.cs:27`. WPF `CheckAccess()` is `Thread` object identity.
+- `BreadcrumbDropDownHost.Close` (`QuickFiler/Viewers/BreadcrumbDropDownHost.cs:247-263`), `Reset` (`:275-280`) and `Dispose` (`:283-290`) contain no thread guard; they schedule through the open lifetime, which dispatches through the context-backed operations.
+- `EmailMoveMonitor.UnhookItem` marshals unconditionally through `_marshalToSta` (`QuickFiler/Helper Classes/EmailMoveMonitor.cs:67-76`).
+
+| # | Site (repo-relative, `QuickFiler.Test/`) | Containing test method | Verdict | Reason |
+|---|---|---|---|---|
+| 1 | `Helper Classes/EmailMoveMonitorTests.cs:298` | `UnhookItem_InvokedFromThreadPoolThread_RunsComAccessOnMarshalTargetThread` | UNAFFECTED | The asserted property (`recordedBodyThreadId != callingThreadId`, `:308-314`) is guaranteed by the fresh `new Thread` inside the marshal delegate (`:281-287`), which is distinct from every live thread including an inlined caller; `UnhookItem` marshals unconditionally. Class carries a pre-existing `[DoNotParallelize]` at `:24` (leave as is). |
+| 2 | `Viewers/BreadcrumbCoordinatorLifecycleTests.cs:350` | `QueuedCompletion_DisposedBeforeOwnerDrain_DoesNotPublish` | UNAFFECTED | Completes a `TaskCompletionSource` (`gate.SetResult(key)`); continuations run asynchronously (`RunContinuationsAsynchronously`). |
+| 3 | `Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:58` | `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` | **AFFECTED** | Owner-only dispatcher (`CreateOwnerOnlyDispatcher`, `:124-134`, null context, owner id = test thread). `Dispatch` reaches the thread-id branch (`BreadcrumbUiDispatcher.cs:276-277`). The blocking `GetAwaiter().GetResult()` at `:59` can inline the work item on the test thread, in which case the action runs inline, `executions == 1`, `errors` empty, and the test fails spuriously (`:60-61`). Outcome is decided by scheduling. |
+| 4 | `Viewers/BreadcrumbPopupControlDispatchTests.cs:29` | `SurfaceFactory_WorkerCompletion_DispatchesEveryStageAndCleanup` | UNAFFECTED | Operations built with a context-backed dispatcher (`:311`); every stage goes through `RunAsync` -> `DispatchValue`, which posts unless inside an executing callback and never reads thread identity. The sibling test at `:53` calls `CreateSurface` directly on the test thread and asserts the same stage sequence. No blocking wait at the start (`Drain` waits on a monitor, `:249-279`). |
+| 5 | `Viewers/BreadcrumbPopupControlDispatchTests.cs:111` | `Readiness_DisposeFromAmbientNullWorker_DispatchesHandlerDetachment` | UNAFFECTED | `readiness.Dispose()` -> `Cancel` -> `dispatcher.Dispatch(detachHandlers)` (`BreadcrumbPopupUiOperations.cs:418`); with a captured context the guard compares ambient context by reference; the body sets ambient null itself (`:113`), so the post occurs on any thread. `fixture.Drain(disposing)` is not a task wait, so the body is not inlined and the unrestored null context lands on a pool thread only. |
+| 6 | `Viewers/BreadcrumbPopupControlDispatchTests.cs:300` | helper `CompleteOnWorker` (used by test 1 at `:31,33`) | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| 7 | `Viewers/BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192` | `CaptureCurrent_NullAndControlledContexts_FailFastAndCapture` | UNAFFECTED | `captured` is a `CaptureCurrent()` dispatcher whose `_context` is the `PumpSynchronizationContext`; `PostAsync` -> `Dispatch` compares ambient context by reference. On the test thread the ambient context is the restored previous one (`WithContext`, `:136-148`), never `context`, so the post happens on any thread; `PostCount == 1` (`:197`) pins the posted path. |
+| 8 | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:61` | `WorkerProviderAndSelectorToggle_MarshalPostsAndCallbackEntryToOwningBoundary` | UNAFFECTED | Completes a `TaskCompletionSource`. |
+| 9 | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:75` | same as 8 | UNAFFECTED | Coordinator built via the public constructor (context-backed). `messenger.Receive` -> inbound `Dispatch(HandleSelectorMessage)` posts whenever ambient context is not the captured one; the body forces ambient null through `InvokeAmbientNull` (`:325-337`). `context.WaitForPost()` (`:76`) precedes the blocking `GetResult()` (`:77`) and is a semaphore wait, so the body has already run on another pool thread before any wait-inlining is possible. |
+| 10 | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:137` | `PopupHost_WorkerCompletions_RunOnlyWhenCreatorThreadDrainsBoundary` | UNAFFECTED | Completes a `TaskCompletionSource` (factory completion). |
+| 11 | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:148` | same as 10 | UNAFFECTED | Completes a `TaskCompletionSource` (readiness). |
+| 12 | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:154` | same as 10 | UNAFFECTED | `host.Close(...)` has no thread guard (`BreadcrumbDropDownHost.cs:247-263`); the test asserts that callbacks and drained work ran on the creator thread (`:164-169`), which the drain enforces; the origin thread of `Close` is not asserted. |
+| 13 | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:161` | same as 10 | UNAFFECTED | `host.Reset` has no thread guard (`:275-280`); same reasoning as 12. |
+| 14 | `Viewers/BreadcrumbSelectorOpenRetryTests.cs:37` | `MouseToggle_FirstOpenFaultsAfterAwait_SecondClickRetriesCleanly` | UNAFFECTED | Completes a `TaskCompletionSource` (`SetException`). |
+| 15 | `Viewers/BreadcrumbSelectorOpenRetryTests.cs:210` | `Dispose_WhenResetAndOpenWorkAreQueued_HasNoLateActivity` | UNAFFECTED | Completes a `TaskCompletionSource`. |
+| 16 | `Viewers/BreadcrumbSelectorOpenRetryTests.cs:218` | same as 15 | UNAFFECTED | `host.Reset` has no thread guard; assertions are about operation counts after drain (`:222-229`). |
+| 17 | `Viewers/BreadcrumbSelectorOpenRetryTests.cs:219` | same as 15 | UNAFFECTED | `host.Dispose` has no thread guard (`:283-290`). |
+| 18 | `Viewers/BreadcrumbUiThreadDispatchTests.cs:51` | `SetSuggestionsAsync_WorkerProviderCompletion_SchedulesPostOnOwningContext` | UNAFFECTED | Completes a `TaskCompletionSource`; awaited, so no wait-inlining. |
+| 19 | `Viewers/BreadcrumbUiThreadDispatchTests.cs:90` | `InboundWorkerMessage_SchedulesEveryPostAndCallbackOnOwningContext` | UNAFFECTED | Awaited (no wait-inlining). Coordinator is context-backed; `Dispatch` posts whenever ambient context is not the captured one; on the test thread the ambient context is the restored `previous` (`:86`), so the outcome is identical on any thread. |
+| 20 | `Viewers/BreadcrumbUiThreadDispatchTests.cs:301` | `ProductionCaptureWithoutUiContext_FailsFast` | UNAFFECTED | `DispatchValue` on an owner-only dispatcher (`CreateForCurrentThreadTests()`, `:298-299`) faults for every caller that is not inside an executing dispatcher callback (`BreadcrumbUiDispatcher.cs:166-188`); it never reads `_ownerThreadId`. The expected `InvalidOperationException` is produced on the owner thread as well as on any other thread. This corrects the second citation of Entry 1 in `docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/evidence/other/p5-t14-follow-up-handoff.2026-09-17T02-39.md:40-42`, which attributed this site to the owner-thread-id check. The message text "cross-thread" is broader than the mechanism; a wording-only follow-up, not a fix under #931. |
+| 21 | `Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:332` | `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` | **AFFECTED** | The blocking `GetAwaiter().GetResult()` at `:335-336` can inline the work item onto the owner thread. The remark at `:311-317` claims the test discriminates against the pre-#781 context-reference guard; that holds only when the call is genuinely off the owner thread (on the inlined branch the ambient context equals the captured one and the pre-fix guard would admit the call). Issue #931 lists this site as remaining; the #900 handoff records the same conditionality (Entry 2, `:63-94`). |
+| 22 | `Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs:222` | `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` | UNAFFECTED | Asserts the value observed by a second gate acquirer (`observedByB`, `:244-254`), not any thread identity; `secondCallerStarted.Wait()` (`:239`) is a `ManualResetEventSlim` wait, not a task wait, so the body runs on a separate pool thread. Its intermittency is tracked under #823 (`:197-202`) for unrelated reasons. |
+
+Result: AFFECTED = {3, 21}; UNAFFECTED = the remaining 20. Of the 8 sites the issue named as candidates, 6 are UNAFFECTED (4, 5, 7, 9, 19, 20).
+
+### A3. AFFECTED sites: guard, in-thread assertion, apartment state
+
+**Site 3, `BreadcrumbPopupBoundaryCoverageTests.cs:58`**
+
+- Guard under test: `BreadcrumbUiDispatcher.IsCurrentBoundary()` owner-thread branch (`BreadcrumbUiDispatcher.cs:274-277`), reached from `Dispatch` (`:78`) with `_context == null`; the rejection path reports "cannot marshal" and returns without running the action (`:97-105`).
+- In-thread assertion: the dispatcher exposes no `CheckAccess`. Capture `int ownerThreadId = Environment.CurrentManagedThreadId;` in Arrange (this is the value `CreateOwnerOnlyDispatcher` passes at `:133`) and assert inside the delegate, before `dispatcher.Dispatch(...)`: `Environment.CurrentManagedThreadId.Should().NotBe(ownerThreadId, "...")`. Optionally also `ReferenceEquals(Thread.CurrentThread, ownerThread).Should().BeFalse()`.
+- Apartment state: none required. No control is touched; a null-context dispatcher never posts. Use the same background MTA thread shape as the canonical helper.
+- After the rewrite the existing assertions (`executions == 0`, one error containing "cannot marshal") stay unchanged.
+
+**Site 21, `ItemViewerBreadcrumbThreadAffinityTests.cs:332`**
+
+- Guard under test: the null-owner escape of `ThrowIfOffUiBoundary` (`ItemViewer.Breadcrumb.cs:434-438`), entered from `InitializeBreadcrumbPipeline` (`:51`); the call then takes the already-initialized early return (`:60-72`).
+- In-thread assertion: `scope.Viewer.UiDispatcher` is null after `ClearViewerDispatcher` (`:361-371`), so capture the owner first: `Dispatcher owner = scope.Viewer.UiDispatcher;` before `ClearViewerDispatcher(scope.Viewer);`, then inside the delegate assert `owner.CheckAccess().Should().BeFalse("...")` before the guarded call. This is the same `Thread`-identity proof used at `:230-236`.
+- Apartment state: none required. The call path touches no control (early return); the two existing dedicated-thread tests call the same member without STA.
+- The `Action act` / `NotThrow` shape can be preserved by asserting `RunOnDedicatedWorkerThread(...)` returned null, plus `BreadcrumbCoordinator.Should().BeSameAs(before)`.
+
+### A4. Demonstrating failure against a deliberately broken guard
+
+Precedent: #900 recorded two reverted, test-only mutations with before/after census and TRX-derived summaries (`.../evidence/regression-testing/p3-t1-mutation-guard-disabled.2026-09-17T02-24.md`, `p3-t3-mutation-inline-precondition.2026-09-17T02-25.md`), each followed by a revert record. The same evidence discipline applies here: record the mutated file's line count and token census, build, run the scoped test with a `/TestCaseFilter`, record the TRX-derived summary, revert with `git checkout -- `, and prove the revert with `git status --porcelain` showing no residual change to that file.
+
+**Site 3**
+
+- Test seam or fake: none. `BreadcrumbUiDispatcher` is `sealed`, `IsCurrentBoundary` is private, and every argument combination of the reflected 3-argument constructor yields a correct guard (null owner and null context -> reports; a different owner id -> a different owner, not a broken check).
+- M1 (temporary production edit, reverted in the same task): replace `BreadcrumbUiDispatcher.cs:276-277` with `return true;`. `Dispatch` then runs the action inline on the worker thread: `executions == 1` and `errors` is empty, so the rewritten test fails at `executions.Should().Be(0)`. Expected failure text: "Expected executions to be 0, but found 1".
+- M2 (test-only, reverted): insert `action();` as the first statement of the dedicated-thread helper, exactly as #900 P3-T3 did. The in-thread precondition fails ("Expected ... not to be "), proving the precondition is live.
+- Fallback without any production edit: construct the owner-only dispatcher inside the dedicated thread (owner = worker). `Dispatch` then runs inline and the same assertions fail. This models a wrong owner rather than a broken check, so M1 is preferred when a temporary production edit is acceptable.
+- Feasibility: M1 and M2 both feasible; M1 requires the temporary production edit the delegation permits.
+
+**Site 21**
+
+- Test seam or fake: none for the escape itself. `ClearViewerDispatcher` (reflection on `_uiDispatcher`) is the existing seam that *selects* the escape; it cannot disable it. #900's P3-T1 mutation (inserting `ClearViewerDispatcher` into the throw-tests) is not applicable here because the escape is the branch under test.
+- M1 (temporary production edit, reverted): at `ItemViewer.Breadcrumb.cs:435-438` replace the bare `return;` with the pre-#781 context-reference check (`if (!ReferenceEquals(SynchronizationContext.Current, UiSyncContext)) throw new InvalidOperationException(...)`). On the dedicated thread the ambient context is null and differs from `UiSyncContext`, so the guard throws and the rewritten test fails at `NotThrow`. On the owner thread (the inlined branch of the old shape) the ambient context equals `scope.Context`, so the old test would have passed; this is the discrimination the rewrite restores and the remark at `:311-317` can then state unconditionally.
+- M1-alt: delete the null check so `owning.CheckAccess()` dereferences null. Fails on any thread with `NullReferenceException`; proves the escape is reached but not the thread dependence. Use only as a supplement.
+- M2 (test-only, reverted): inline `action();` in the helper; `owner.CheckAccess()` is then true and the `BeFalse` precondition fails with the "vacuously" reason text, as in P3-T3.
+- Feasibility: M1, M1-alt and M2 all feasible.
+
+### A5. `ItemViewerBreadcrumbThreadAffinityTests.cs`: structure, split, line counts, csproj
+
+Current file: **490 lines**, one `[TestClass] public sealed class ItemViewerBreadcrumbThreadAffinityTests` (`:29-30`), namespace `QuickFiler.Test.Viewers`.
+
+| Lines | Member | Grouping |
+|---|---|---|
+| 39-75 | `InitializeBreadcrumbPipeline_ConstructedInsideDispatcherOperation_SucceedsUnderDifferentAmbientContext` | owner-thread admission |
+| 89-122 | `InitializeBreadcrumbPipeline_OwningThreadNullAmbientContext_DoesNotThrow` | owner-thread admission |
+| 129-160 | `InitializeBreadcrumbPipeline_OwningThreadDifferentPlainContext_DoesNotThrow` | owner-thread admission |
+| 168-197 | `ConfigureBreadcrumbDropDown_OwningThreadInsideDispatcherOperation_DoesNotThrow` | owner-thread admission |
+| 219-251 | `InitializeBreadcrumbPipeline_WorkerThread_ThrowsBoundaryDiagnostic` | cross-thread (dedicated thread) |
+| 269-304 | `ConfigureBreadcrumbDropDown_WorkerThread_ThrowsBoundaryDiagnostic` | cross-thread (dedicated thread) |
+| 319-346 | `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` | cross-thread (site 21, to be rewritten) |
+| 352-355 | `InertOperations()` | shared helper |
+| 361-371 | `ClearViewerDispatcher()` | used only by the null-owner test |
+| 385-403 | `RunOnDedicatedWorkerThread()` | used only by the cross-thread tests |
+| 406-434 | `InertDropDownHost` (nested) | used by tests at 176 and 274 |
+| 442-461 | `DrainableSynchronizationContext` (nested) | used by `InertOperations` |
+| 467-488 | `ViewerScope` (nested) | used by all tests except the first |
+
+Proposed split (repository convention for continuation partials: `*.Part2.cs`, e.g. `BreadcrumbPopupBoundaryCoverageTests.Part2.cs:17,23`, and 16 further `.Part2.cs` entries in `QuickFiler.Test.csproj`):
+
+- Make the class `partial`. Primary file `Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` keeps the four owner-thread admission tests (lines 1-197), `InertOperations` (348-355), and the three nested types (405-488). Estimated **about 290 lines**.
+- New file `Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` holds the three cross-thread tests (199-346) and `ClearViewerDispatcher` (357-371). With the rewritten null-owner test (about +12 lines) and a file header (about 16 lines) the estimate is **about 200 lines**; about 230 if `RunOnDedicatedWorkerThread` stays in it. Private nested types and private static helpers remain accessible across partial declarations, so no accessibility is widened.
+- `RunOnDedicatedWorkerThread` is needed by site 3 in another class. Recommended: move it to a new internal static helper `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` (namespace `QuickFiler.Test.TestSupport`, matching `TestSupport/WinFormsPumpHost.cs:9`), keeping the same contract (`Exception Run(Action)`), and optionally adding a built-in generic precondition (`ReferenceEquals(Thread.CurrentThread, caller)` must be false) in addition to the site-specific precondition each test keeps in its delegate. Estimated 40-60 lines.
+
+`QuickFiler.Test.csproj` uses explicit `` entries. Existing entry for the file: `QuickFiler.Test/QuickFiler.Test.csproj:98` (``). Neighbouring entries for the other candidate files: `:77-79` (`BreadcrumbUiThreadDispatchTests.cs`, `BreadcrumbSelectorToggleUiBoundaryTests.cs`, `BreadcrumbPopupControlDispatchTests.cs`), `:105-106` (`BreadcrumbPopupBoundaryCoverageTests.cs`, `.Part2.cs`); `TestSupport` entries at `:227-228`.
+
+Line counts of every file the rewrite would edit:
+
+| File | Now | After (estimate) | Under 500 |
+|---|---|---|---|
+| `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` | 490 | about 290 | yes |
+| `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` (new) | 0 | about 200-230 | yes |
+| `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` (new) | 0 | about 40-60 | yes |
+| `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs` | 361 | about 375 (site 3 rewrite) | yes |
+| `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` | 359 | about 355-370 (`GetSolutionFile` removed, seam test added) | yes |
+| `QuickFiler.Test/QuickFiler.Test.csproj` | build file | +2 `` entries | not subject to the code-file limit |
+
+Not edited (for reference): `BreadcrumbPopupControlDispatchTests.cs` 486, `BreadcrumbPopupBoundaryCoverageTests.Part2.cs` 483, `BreadcrumbSelectorToggleUiBoundaryTests.cs` 478, `BreadcrumbUiThreadDispatchTests.cs` 480. Each of these has fewer than 25 lines of headroom, which is a further reason not to classify their sites as affected without necessity and not to add helpers to them.
+
+---
+
+## B. File-handle defect (issue #906 portion)
+
+### B6. The `TaskMaster.sln` fixture in `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs`
+
+Test (current lines 55-67; the issue cites 56-62):
+
+```csharp
+[TestMethod]
+public void OpenRead_ShouldReturnReadableStreamForWrappedFile()
+{
+ // Arrange
+ var wrapper = new FileInfoWrapper(GetSolutionFile());
+
+ // Act
+ using var stream = wrapper.OpenRead();
+
+ // Assert
+ stream.CanRead.Should().BeTrue();
+ stream.Length.Should().BeGreaterThan(0);
+}
+```
+
+Helper (current lines 339-357; the issue cites 340-352):
+
+```csharp
+private static FileInfo GetSolutionFile()
+{
+ var current = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
+
+ while (current is not null)
+ {
+ var solutionPath = Path.Combine(current.FullName, "TaskMaster.sln");
+ if (File.Exists(solutionPath))
+ {
+ return new FileInfo(solutionPath);
+ }
+
+ current = current.Parent;
+ }
+
+ throw new InvalidOperationException(
+ "The TaskMaster solution file could not be located from the test assembly path."
+ );
+}
+```
+
+Tests in this file that use `GetSolutionFile()` (all through the public `FileInfoWrapper(FileInfo)` constructor):
+
+| Lines | Test | Call site | Real-file dependency |
+|---|---|---|---|
+| 26-39 | `Properties_ShouldMirrorWrappedFileInfo` | `:29` | Reads `Exists`, `FullName`, `Name`, `Extension`, `DirectoryName`, `Directory.FullName` of `TaskMaster.sln` (metadata only, no handle). |
+| 42-53 | `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory` | `:45` | Path-derived only. |
+| 55-67 | `OpenRead_ShouldReturnReadableStreamForWrappedFile` | `:59` | **Opens a read handle on `TaskMaster.sln`** (`FileInfo.OpenRead()` requests `FileShare.Read`); this is the #906 failure (IOException observed once in the #900 run: `p5-t14-follow-up-handoff...md:157-164`). |
+| 70-81 | `ToString_ShouldDelegateToWrappedFileInfo` | `:73` | Path-derived only. |
+
+Other real-file use in the same file: `StreamAndCopyMethods_ShouldDelegateToWrappedIFileInfo` (184-295) opens the test's own loaded assembly six times (`typeof(FileInfoWrapper_Tests).Assembly.Location`, `FileMode.Open`, `FileAccess.Read`, `FileShare.ReadWrite`; lines 189-224) purely as identity sentinels returned by a strict `Mock`. This is a build output the test host already holds mapped, opened with a sharing mode that admits every other read or write holder; it is not a repository source file and it is not the #906 defect. The same shape is the repository's stated precedent (`UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs:247-248, 258-275`).
+
+Out of #931 scope but the same defect class (record as follow-up potential entries, do not fix here): `PhysicalFileSystemAdapters_Tests.cs:173` and `:318` (`GetSolutionFile()`), `:186-198` (a `catch (IOException)` that swallows contention), `:213-234` (read opens on the real `.sln`); `DirectoryInfoWrapper_Tests.cs:60, 79, 381` (asserts that `TaskMaster.sln` is enumerated from the repository root).
+
+### B7. Existing seam in production (no production change required)
+
+- `UtilitiesCS/HelperClasses/FileSystem/FileInfoWrapper.cs:14-24`: public `FileInfoWrapper(FileInfo)` wraps a `PhysicalFileInfoAdapter`; `internal FileInfoWrapper(IFileInfo fileInfo)` (`:21-24`) is the seam. Every member of the wrapper delegates to `_fileInfo` (`:26-209`); `OpenRead()` is `:153-156`.
+- Visibility: `UtilitiesCS/Properties/AssemblyInfo.cs:19` `[assembly: InternalsVisibleTo("UtilitiesCS.Test")]` (also `DynamicProxyGenAssembly2` at `:18`, so Moq can proxy internal members).
+- The seam is already exercised in this file at `:139`, `:271`, `:322` (`new FileInfoWrapper(fileInfo.Object)`), including `OpenRead` delegation at `:263` and `:287` (`wrapper.OpenRead().Should().BeSameAs(openReadStream)`).
+- Second, narrower seam one level down: `PhysicalFileInfoAdapter`'s internal delegate constructor (`UtilitiesCS/HelperClasses/FileSystem/PhysicalFileInfoAdapter.cs:34-48`) seams `AppendText`, `Open(FileMode)`, `Open(FileMode, FileAccess)` and `OpenWrite` only (`:118, :146-149, :158`); `OpenRead()` is unseamed (`:154`). Therefore `OpenRead` cannot be exercised through the public `FileInfoWrapper(FileInfo)` path without a real file; through the `IFileInfo` seam it can.
+- Prior art searched: `PhysicalFileInfoAdapter` (production, above, and `PhysicalFileSystemAdapters_Tests.cs:170-311`), "sentinel" (`PhysicalFileSystemAdapters_Tests.cs:244-282`, `Bootstrap/AssemblyBindingFallbackTests.cs:28-29`), injectable `Func<>` seams (`UtilitiesCS/OneDriveHelpers/OneDriveDownloader.cs:106-133`).
+
+Conclusion: the issue's condition "add a seam to the wrapper only if one does not already exist" resolves to **no seam addition**.
+
+### B8. Replacement design and what each rewritten test verifies
+
+Type constraint: `IFileInfo.OpenRead()`, `Open(...)`, `Create()`, `OpenWrite()` return the concrete `FileStream` (`IFileInfo.cs:16, 23-26, 28`). A `MemoryStream` is not a `FileStream`, so an in-memory stream cannot be returned through the seam for these members. `MemoryStream` is usable only behind `StreamReader`/`StreamWriter` members (`AppendText`, `CreateText`, `OpenText`, `:13, 17, 27`), which the existing test already does at `:186-188`. A pipe-backed `FileStream` is rejected as an option because it is non-seekable and the test asserts `Length`.
+
+Recommended rewrite (all four tests keep their names and scenario intent; `GetSolutionFile()` and the `AppDomain` walk-up are deleted):
+
+1. `Properties_ShouldMirrorWrappedFileInfo`, `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory`, `ToString_ShouldDelegateToWrappedFileInfo`: construct `new FileInfo(@"C:\Repo\fixture.sln")` (style matches the rooted literals already used at `:87, :113`). `FileInfo` construction, `FullName`, `Name`, `Extension`, `DirectoryName`, `Directory`, and `ToString()` are path computations with no handle; `Exists` is a metadata query with no handle, and the assertion mirrors `file.Exists` so it is deterministic either way. Do not assert `Length` on this fixture (it throws for a missing file). What is verified: the public constructor path `FileInfoWrapper(FileInfo)` -> `PhysicalFileInfoAdapter` (`FileInfoWrapper.cs:14-19`; adapter `:25-32, 68-108, 181`) and the explicit `DirectoryInfoWrapper` cast (`:211-214`), which the mocked tests cannot reach because a `Mock` is not a `DirectoryInfoWrapper`. This keeps coverage of `FileInfoWrapper.cs:18` and the adapter's property lines without any file handle.
+2. `OpenRead_ShouldReturnReadableStreamForWrappedFile`: construct the wrapper through the seam with a strict `Mock` whose `OpenRead()` returns a test-owned `FileStream` opened on `typeof(FileInfoWrapper_Tests).Assembly.Location` with `FileMode.Open`, `FileAccess.Read`, `FileShare.ReadWrite` (the pattern at `:189-224` and `PhysicalFileSystemAdapters_Tests.cs:258-275`), disposed by `using`. Assert `wrapper.OpenRead().Should().BeSameAs(sentinel)`, `CanRead` true and `Length > 0`. What is verified: the wrapper's own behaviour is delegation (`FileInfoWrapper.cs:153-156`); identity of the returned instance is the complete contract, and the readability assertions confirm the returned object is the live stream. No repository source file and no temporary file is involved. This test then overlaps `:263/:287`; deleting it instead would lose no production coverage (line 155 stays covered) and is an acceptable alternative if the reviewer prefers fewer tests, but the rewrite preserves the named scenario the issue cites.
+
+Rejected alternatives: temporary files (prohibited by policy); adding an `OpenRead` delegate to `PhysicalFileInfoAdapter` (a production change the issue forbids when a seam exists); asserting through `PhysicalFileInfoAdapter` directly (that adapter's own tests are out of scope).
+
+---
+
+## C. Toolchain facts
+
+### C9. Project files
+
+- `QuickFiler.Test/QuickFiler.Test.csproj` (explicit `` model) gains two entries: `Viewers\ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` (adjacent to `:98`) and `TestSupport\DedicatedWorkerThread.cs` (adjacent to `:227-228`). Files present on disk but absent from the csproj are not compiled, so a missing entry fails silently at the test-count level; the executor should confirm the new test names appear in the TRX-derived summary.
+- `UtilitiesCS.Test/UtilitiesCS.Test.csproj` also uses explicit entries (`:232-234` list `DirectoryInfoWrapper_Tests.cs`, `PhysicalFileSystemAdapters_Tests.cs`, `FileInfoWrapper_Tests.cs`). The recommended rewrite creates no new file there, so no entry is needed.
+
+### C10. Parallel regime and `[DoNotParallelize]`
+
+- `TaskMaster.runsettings:3-8`: `0ClassLevel` (plus a Code Coverage collector configuration at `:9-29`). `scripts/vscode/TaskMaster.cli.runsettings:3-8` carries the same `Workers=0`, `Scope=ClassLevel` without the collector (this is the file the #900 evidence ran under).
+- `[DoNotParallelize]` today (grep over `*.cs`): none on `ItemViewerBreadcrumbThreadAffinityTests`, `BreadcrumbPopupBoundaryCoverageTests` (either partial), `BreadcrumbPopupControlDispatchTests`, `BreadcrumbSelectorToggleUiBoundaryTests`, `BreadcrumbUiThreadDispatchTests`, `BreadcrumbSelectorOpenRetryTests`, `BreadcrumbCoordinatorLifecycleTests`, `QfcItemController.UiThreadDispatcherFixtureTests`, or `FileInfoWrapper_Tests`. In `QuickFiler.Test` it appears only on `Helper Classes/EmailMoveMonitorTests.cs:24` and `Helper Classes/ViewerQueueStaticWrapperTests.cs:11` (both unaffected by this issue; leave untouched). The fix must add none.
+
+---
+
+## D. Candidate approaches and recommendation
+
+**Approach 1 (recommended): dedicated-thread helper shared through `TestSupport`, partial split of the affinity file, seam-only rewrite of the file tests.**
+Advantages: one implementation of the #900 helper reused at both affected sites (no copy-paste); the affinity file lands near 290 lines with headroom; no production code changes anywhere; matches the issue's stated fix and existing `.Part2.cs`/`TestSupport` conventions. Limitations: two new files and two csproj entries.
+
+**Approach 2 (rejected): duplicate the private helper into `BreadcrumbPopupBoundaryCoverageTests.cs` and split the affinity file.** Rejected for copy-paste of a 19-line helper whose rationale comment is the load-bearing part.
+
+**Approach 3 (rejected): move the helper out and skip the split.** The primary file would land near 475 lines, below the "with headroom" bar and contrary to the issue's stated fix.
+
+**Approach 4 (rejected): rewrite all eight cited candidate sites.** Six of them do not exercise a thread-identity guard (A2); rewriting them adds dedicated threads to files with fewer than 25 lines of headroom for no discriminating benefit.
+
+## E. Behaviour semantics and requirements mapping
+
+- AC "apply the #900 pattern at every triaged site": sites 3 and 21 only; record the UNAFFECTED verdicts for the six other candidates with the reasons in A2 so the reviewer can see the triage.
+- AC "split and register": Part2 partial plus `TestSupport/DedicatedWorkerThread.cs`, both registered (C9).
+- AC "replace the `.sln` fixture; add a seam only if none exists": B7/B8; no production edit.
+- AC "each rewritten test shown to fail against a deliberately broken guard": A4 gives M1/M2 per site, each reverted in the same task with census and `git status --porcelain` evidence, never committed. For the file test the analogous demonstration is: point the mock's `OpenRead()` at a second sentinel (or make it throw) and observe `BeSameAs` fail, then restore.
+- AC "run the full `QuickFiler.Test` and `UtilitiesCS.Test` suites in the parallel regime": run under `/Settings:TaskMaster.runsettings` (or the CLI twin) with `/InIsolation`, summarise from the TRX; no `Workers=1`, `[DoNotParallelize]`, or retry is permitted.
+- Files changed: `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` (make partial, remove cross-thread tests and helpers), new `...ThreadAffinityTests.Part2.cs`, new `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs`, `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:52-62`, `QuickFiler.Test/QuickFiler.Test.csproj`, `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs:25-81, 339-357`. No file under `QuickFiler/` or `UtilitiesCS/` changes.
+
+## F. Testing implications
+
+- The rewritten tests are deterministic under `Workers=0`/`ClassLevel`: a constructed `Thread` is distinct from every live thread; the in-thread precondition makes an inlined or same-thread execution fail loudly instead of passing vacuously (site 21) or failing spuriously (site 3).
+- No wall-clock waits are introduced: `Thread.Join()` is a completion wait on one bounded synchronous call (rationale at `ItemViewerBreadcrumbThreadAffinityTests.cs:377-384`).
+- Coverage: production lines touched by these tests are unchanged in count; `FileInfoWrapper.cs:18, 44-78, 153-156, 211-214` remain covered by the rewritten tests. New test-support code is excluded from the production denominator.
+- Evidence to commit: TRX-derived summaries for the mutation runs and the final parallel suite runs, the JaCoCo projection and one-line coverage summary; never the raw TRX or Cobertura documents (CLAUDE.md "Committed Test Evidence Format").
+- Follow-ups to promote as potential entries, not fixed here: the `.sln` usages in `PhysicalFileSystemAdapters_Tests.cs` and `DirectoryInfoWrapper_Tests.cs` (B6); the "cross-thread" wording at `BreadcrumbUiThreadDispatchTests.cs:305` (A2 row 20); the mischaracterised second citation in the #900 handoff Entry 1.
+
+## Numeric Derivation Evidence
+
+- Complete Family: EmailMoveMonitorTests.cs:298, BreadcrumbCoordinatorLifecycleTests.cs:350, BreadcrumbPopupBoundaryCoverageTests.cs:58, BreadcrumbPopupControlDispatchTests.cs:29, BreadcrumbPopupControlDispatchTests.cs:111, BreadcrumbPopupControlDispatchTests.cs:300, BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192, BreadcrumbSelectorToggleUiBoundaryTests.cs:61, BreadcrumbSelectorToggleUiBoundaryTests.cs:75, BreadcrumbSelectorToggleUiBoundaryTests.cs:137, BreadcrumbSelectorToggleUiBoundaryTests.cs:148, BreadcrumbSelectorToggleUiBoundaryTests.cs:154, BreadcrumbSelectorToggleUiBoundaryTests.cs:161, BreadcrumbSelectorOpenRetryTests.cs:37, BreadcrumbSelectorOpenRetryTests.cs:210, BreadcrumbSelectorOpenRetryTests.cs:218, BreadcrumbSelectorOpenRetryTests.cs:219, BreadcrumbUiThreadDispatchTests.cs:51, BreadcrumbUiThreadDispatchTests.cs:90, BreadcrumbUiThreadDispatchTests.cs:301, ItemViewerBreadcrumbThreadAffinityTests.cs:332, QfcItemController.UiThreadDispatcherFixtureTests.cs:222
+- Exhaustive Search Scope: the entire `QuickFiler.Test` source tree of the repository (every file under `QuickFiler.Test/`, all subdirectories: `Controllers/`, `Helper Classes/`, `TestSupport/`, `Viewers/`, and any other), searched with ripgrep from the directory root with no path filter beyond the tree itself; both strategies were run over this whole tree.
+- Inclusion Rules: a line of C# code in which the identifier `Task.Run` is invoked (followed by `(` or a generic argument list), whether the result is awaited, blocked on, stored, or discarded, and whether inside a test method or a helper; every overload of `Task.Run` (`Action`, `Func`, `Func`, `Func>`, with or without a `CancellationToken`) is in the family.
+- Exclusion Rules: (a) occurrences inside XML documentation or `//` comments (`ItemViewerBreadcrumbThreadAffinityTests.cs:205, 260, 378`; `QfcItemController.FolderHandlingTests.Part2.cs:315`; `QfcItemController.EventHandlersTests.cs:261`; `QfcItemControllerTests.cs:62`); (b) occurrences inside string literals (`QfcItemController.FolderHandlingTests.Part2.cs:351`); (c) substring matches on other identifiers (`flagTask.Run(` at `QfcItemController.EventHandlersTests.cs:261`, `TimeOutTask.RunWithTimeout` at `QfcItemControllerTests.cs:62`); (d) sibling APIs that are not `Task.Run` (`Task.Factory.StartNew`, `ThreadPool.QueueUserWorkItem`, `ThreadPool.UnsafeQueueUserWorkItem`: a grep over the same tree returned zero matches, so no sibling site exists to exclude).
+- Primary Search Strategy: ripgrep regex `Task\.Run` over every file under `QuickFiler.Test/` (no glob, no type filter, unlimited results), returning 29 matching lines, then applying exclusion rules (a)-(c) by reading each line; retained members: EmailMoveMonitorTests.cs:298, BreadcrumbCoordinatorLifecycleTests.cs:350, BreadcrumbPopupBoundaryCoverageTests.cs:58, BreadcrumbPopupControlDispatchTests.cs:29, BreadcrumbPopupControlDispatchTests.cs:111, BreadcrumbPopupControlDispatchTests.cs:300, BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192, BreadcrumbSelectorToggleUiBoundaryTests.cs:61, BreadcrumbSelectorToggleUiBoundaryTests.cs:75, BreadcrumbSelectorToggleUiBoundaryTests.cs:137, BreadcrumbSelectorToggleUiBoundaryTests.cs:148, BreadcrumbSelectorToggleUiBoundaryTests.cs:154, BreadcrumbSelectorToggleUiBoundaryTests.cs:161, BreadcrumbSelectorOpenRetryTests.cs:37, BreadcrumbSelectorOpenRetryTests.cs:210, BreadcrumbSelectorOpenRetryTests.cs:218, BreadcrumbSelectorOpenRetryTests.cs:219, BreadcrumbUiThreadDispatchTests.cs:51, BreadcrumbUiThreadDispatchTests.cs:90, BreadcrumbUiThreadDispatchTests.cs:301, ItemViewerBreadcrumbThreadAffinityTests.cs:332, QfcItemController.UiThreadDispatcherFixtureTests.cs:222 (7 lines excluded: 205, 260, 378 of ItemViewerBreadcrumbThreadAffinityTests.cs; 315, 351 of QfcItemController.FolderHandlingTests.Part2.cs; 261 of QfcItemController.EventHandlersTests.cs; 62 of QfcItemControllerTests.cs)
+- Primary Member Set: EmailMoveMonitorTests.cs:298, BreadcrumbCoordinatorLifecycleTests.cs:350, BreadcrumbPopupBoundaryCoverageTests.cs:58, BreadcrumbPopupControlDispatchTests.cs:29, BreadcrumbPopupControlDispatchTests.cs:111, BreadcrumbPopupControlDispatchTests.cs:300, BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192, BreadcrumbSelectorToggleUiBoundaryTests.cs:61, BreadcrumbSelectorToggleUiBoundaryTests.cs:75, BreadcrumbSelectorToggleUiBoundaryTests.cs:137, BreadcrumbSelectorToggleUiBoundaryTests.cs:148, BreadcrumbSelectorToggleUiBoundaryTests.cs:154, BreadcrumbSelectorToggleUiBoundaryTests.cs:161, BreadcrumbSelectorOpenRetryTests.cs:37, BreadcrumbSelectorOpenRetryTests.cs:210, BreadcrumbSelectorOpenRetryTests.cs:218, BreadcrumbSelectorOpenRetryTests.cs:219, BreadcrumbUiThreadDispatchTests.cs:51, BreadcrumbUiThreadDispatchTests.cs:90, BreadcrumbUiThreadDispatchTests.cs:301, ItemViewerBreadcrumbThreadAffinityTests.cs:332, QfcItemController.UiThreadDispatcherFixtureTests.cs:222
+- Primary Count: 22
+- Cross-check Search Strategy: ripgrep regex `(^|[^A-Za-z0-9_.])Task\.Run\s*[(<]` with glob `*.cs` over every file under `QuickFiler.Test/` (unlimited results; the leading character class rejects identifier-suffix matches such as `flagTask.Run(` and the trailing class requires an invocation or generic list, which also rejects `TimeOutTask.RunWithTimeout`), returning 27 matching lines, then applying exclusion rules (a)-(b) by reading each line; retained members: EmailMoveMonitorTests.cs:298, BreadcrumbCoordinatorLifecycleTests.cs:350, BreadcrumbPopupBoundaryCoverageTests.cs:58, BreadcrumbPopupControlDispatchTests.cs:29, BreadcrumbPopupControlDispatchTests.cs:111, BreadcrumbPopupControlDispatchTests.cs:300, BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192, BreadcrumbSelectorToggleUiBoundaryTests.cs:61, BreadcrumbSelectorToggleUiBoundaryTests.cs:75, BreadcrumbSelectorToggleUiBoundaryTests.cs:137, BreadcrumbSelectorToggleUiBoundaryTests.cs:148, BreadcrumbSelectorToggleUiBoundaryTests.cs:154, BreadcrumbSelectorToggleUiBoundaryTests.cs:161, BreadcrumbSelectorOpenRetryTests.cs:37, BreadcrumbSelectorOpenRetryTests.cs:210, BreadcrumbSelectorOpenRetryTests.cs:218, BreadcrumbSelectorOpenRetryTests.cs:219, BreadcrumbUiThreadDispatchTests.cs:51, BreadcrumbUiThreadDispatchTests.cs:90, BreadcrumbUiThreadDispatchTests.cs:301, ItemViewerBreadcrumbThreadAffinityTests.cs:332, QfcItemController.UiThreadDispatcherFixtureTests.cs:222 (5 lines excluded: 205, 260, 378 of ItemViewerBreadcrumbThreadAffinityTests.cs, matched through the `<` of `
` in XML remarks; 315, 351 of QfcItemController.FolderHandlingTests.Part2.cs)
+- Cross-check Member Set: EmailMoveMonitorTests.cs:298, BreadcrumbCoordinatorLifecycleTests.cs:350, BreadcrumbPopupBoundaryCoverageTests.cs:58, BreadcrumbPopupControlDispatchTests.cs:29, BreadcrumbPopupControlDispatchTests.cs:111, BreadcrumbPopupControlDispatchTests.cs:300, BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192, BreadcrumbSelectorToggleUiBoundaryTests.cs:61, BreadcrumbSelectorToggleUiBoundaryTests.cs:75, BreadcrumbSelectorToggleUiBoundaryTests.cs:137, BreadcrumbSelectorToggleUiBoundaryTests.cs:148, BreadcrumbSelectorToggleUiBoundaryTests.cs:154, BreadcrumbSelectorToggleUiBoundaryTests.cs:161, BreadcrumbSelectorOpenRetryTests.cs:37, BreadcrumbSelectorOpenRetryTests.cs:210, BreadcrumbSelectorOpenRetryTests.cs:218, BreadcrumbSelectorOpenRetryTests.cs:219, BreadcrumbUiThreadDispatchTests.cs:51, BreadcrumbUiThreadDispatchTests.cs:90, BreadcrumbUiThreadDispatchTests.cs:301, ItemViewerBreadcrumbThreadAffinityTests.cs:332, QfcItemController.UiThreadDispatcherFixtureTests.cs:222
+- Cross-check Count: 22
+- Member-set Comparison: the two sets are identical (22 members each, same files and line numbers, no member present in one set and absent from the other); the two strategies differ in their regular expression, their file filter, and the number of raw lines they returned (29 versus 27) and converge on the same member set after the declared exclusions, so the family count of 22 and the AFFECTED subset count of 2 (BreadcrumbPopupBoundaryCoverageTests.cs:58, ItemViewerBreadcrumbThreadAffinityTests.cs:332) are asserted.
diff --git a/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/spec.md b/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/spec.md
new file mode 100644
index 000000000..0f8c77af7
--- /dev/null
+++ b/docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/spec.md
@@ -0,0 +1,279 @@
+# 2026-09-28-tests-depend-on-uncontrolled-environment (Spec)
+
+- **Issue:** #931 (consolidates #905 and #906)
+- **Parent (optional):** none
+- **Owner:** drmoisan
+- **Last Updated:** 2026-09-28T20-45
+- **Status:** Ready for atomic planning
+- **Version:** 1.0
+- **Work Mode:** full-bug. This file is the sole authoritative acceptance-criteria source for this feature. No user-story.md exists or is required.
+- **Research record:** docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/research/2026-09-28T20-15-tests-depend-on-uncontrolled-environment-research.md (authoritative for site triage, line numbers, seam analysis and the split). Every line citation below was re-verified against the current tree of this worktree on 2026-09-28.
+
+> Formatting contract for the blast-radius scheduler: the only backtick-delimited repository paths in this document are the entries under `## Write Set`. Every other file reference, including the unaffected triaged sites, the production files cited for the guards, the runsettings file, the sibling defect sites, and the research and evidence artifacts, is written in plain prose without backticks. Backticked tokens elsewhere are C# identifiers, attribute names, or the mandated toolchain command strings from CLAUDE.md (which contain spaces and are not harvestable). Do not "fix" this formatting.
+
+## Context
+
+Two consolidated defects share one root cause: a unit test depends on environment state it does not control, so its outcome is decided by the scheduler or by other processes rather than by the code under test.
+
+**Distinct-thread defect (from #905).** Tests use `Task.Run` to obtain "another thread". `Task.Run` guarantees a thread-pool work item, not a different thread from the caller. When the test itself runs on a thread-pool thread, which is the case for every test under the parallel run configured in TaskMaster.runsettings (Workers zero, Scope ClassLevel), a blocking wait on the work item's task can execute the work item inline on the waiting thread. A guard that decides by thread identity is then either never exercised (it admits the call) or reports a result the test did not intend. PR #904 (issue #900) replaced two such sites in QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs with a dedicated, joined `Thread` and an in-thread distinctness precondition. The issue listed one remaining site (line 332 of that file) and six candidate sites to triage: BreadcrumbSelectorToggleUiBoundaryTests.cs line 75; BreadcrumbPopupControlDispatchTests.cs lines 29 and 111; BreadcrumbPopupBoundaryCoverageTests.cs line 58; BreadcrumbPopupBoundaryCoverageTests.Part2.cs line 192; BreadcrumbUiThreadDispatchTests.cs lines 90 and 301 (all under QuickFiler.Test/Viewers). The research enumerated every `Task.Run` call site in QuickFiler.Test (22 sites; derivation in the research record's Numeric Derivation Evidence section) and classified exactly two as AFFECTED: BreadcrumbPopupBoundaryCoverageTests.cs line 58 and ItemViewerBreadcrumbThreadAffinityTests.cs line 332. The remaining 20, including the other six candidates, either only complete a `TaskCompletionSource` or exercise a guard that decides by `SynchronizationContext` reference identity or by an executing-callback marker, so the identity of the calling thread cannot change their outcome.
+
+**File-handle defect (from #906).** Four tests in UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs (lines 25 to 81) obtain the repository's own TaskMaster.sln through a private `GetSolutionFile()` helper (lines 339 to 357) that walks up from `AppDomain.CurrentDomain.BaseDirectory`. One of them, `OpenRead_ShouldReturnReadableStreamForWrappedFile` (lines 55 to 67), opens a read handle on that file with the default `FileShare.Read` sharing mode of `FileInfo.OpenRead()`. Any other process that holds the solution file with a share mode that excludes readers (resident MSBuild node-reuse workers, an IDE, a hook) makes the open throw `IOException`. The outcome depends on build history, not on the wrapper. An `IOException` from exactly this test was recorded once during the #900 run.
+
+Environment:
+- OS/version: Windows 11 Pro 10.0.26200
+- Runtime: C#, MSTest, net48; Moq and FluentAssertions
+- Command/flags used: parallel regime through the TaskMaster.runsettings file at the repository root (Workers zero, Scope ClassLevel); the CLI twin under scripts/vscode carries the same two values
+- Data source or fixture: files listed above, main at 177b6d78e
+
+Impact / Severity:
+- [ ] Blocker
+- [ ] High
+- [x] Medium
+- [ ] Low
+
+## Repro & Evidence
+
+Steps to Reproduce (static, verified on 2026-09-28 in this worktree):
+1. Open QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs at lines 52 to 62. `CreateOwnerOnlyDispatcher` (lines 124 to 134) builds a `BreadcrumbUiDispatcher` with a null context and the test thread's managed id as owner. The test queues `dispatcher.Dispatch(...)` through `Task.Run` (line 58) and blocks on it (line 59), then asserts the action did not run and one "cannot marshal" error was reported. If the work item is inlined onto the test thread, `IsCurrentBoundary()` (QuickFiler/Viewers/BreadcrumbUiDispatcher.cs lines 255 to 278; the thread-id branch is lines 276 to 277) returns true, the action runs inline, `executions` is one and no error is reported: the test fails spuriously.
+2. Open QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs at lines 319 to 346. The test nulls the viewer's owning dispatcher through `ClearViewerDispatcher` (lines 361 to 371), then re-enters `InitializeBreadcrumbPipeline` from a `Task.Run` work item (line 332) with a blocking wait (lines 335 to 336). If the work item is inlined onto the owner thread, the call is on-thread and the pre-#781 context-reference guard the remark at lines 311 to 317 claims to discriminate against would also have admitted it: the test passes vacuously. The null-owner escape under test is at QuickFiler/Viewers/ItemViewer.Breadcrumb.cs lines 434 to 438.
+3. Open UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs at lines 55 to 67 and 339 to 357 and confirm `GetSolutionFile()` resolves the repository's TaskMaster.sln and that `OpenRead()` opens it.
+
+Expected:
+- A test that needs a distinct thread uses a dedicated `Thread` that is started, joined, and asserts inside its own delegate that it is not the owner thread (for example `CheckAccess()` is false, or `Environment.CurrentManagedThreadId` differs from the captured owner id) before it exercises the guard. This is the pattern PR #904 introduced at lines 228 to 238 and 385 to 403 of ItemViewerBreadcrumbThreadAffinityTests.cs.
+- A file-handle test uses a stream the test itself owns, supplied through the wrapper's existing `IFileInfo` seam. It never opens a repository-tracked file and never creates a temporary file.
+
+Actual:
+- Both thread-affinity guards can go unexercised (or produce a spurious result) depending on where the thread pool runs the work item; the file-open test can pass or fail depending on which other processes hold TaskMaster.sln.
+
+Logs / Screenshots:
+- Static findings, verified present on 2026-09-28. The single observed `IOException` for the file test is recorded in the #900 follow-up handoff under docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/evidence/other/ (plain reference, not modified by this feature).
+
+## Scope & Non-Goals
+
+- In scope:
+ - Rewrite the two AFFECTED `Task.Run` sites (`Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` and `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow`) to the dedicated-thread pattern with an in-thread distinctness assertion.
+ - Move the private `RunOnDedicatedWorkerThread` helper into a shared internal test-support type so both affected classes use one implementation.
+ - Split ItemViewerBreadcrumbThreadAffinityTests.cs (490 total lines today) into a partial class across the existing file and a new Part2 file, and register the new files in the QuickFiler.Test project file.
+ - Remove `GetSolutionFile()` and every TaskMaster.sln dependency from FileInfoWrapper_Tests.cs; route the `OpenRead` scenario through the existing internal `FileInfoWrapper(IFileInfo)` seam with a test-owned `FileStream`; convert the three metadata-only tests to a rooted literal `FileInfo` that needs no handle.
+ - Demonstrate, with committed Markdown projections, that each rewritten guard test fails against a deliberately broken guard and passes after the mutation is reverted.
+ - Run both affected suites in the parallel regime and the full C# toolchain; commit projections only.
+- Out of scope / non-goals (paths in this list are deliberately unbackticked; none of them is modified):
+ - The 20 UNAFFECTED `Task.Run` sites in QuickFiler.Test enumerated in the Triage table below. They are not rewritten, not reformatted, and not annotated.
+ - Any production file. No file under QuickFiler/ or UtilitiesCS/ changes; the seam already exists (UtilitiesCS/HelperClasses/FileSystem/FileInfoWrapper.cs lines 21 to 24, reachable through the `InternalsVisibleTo("UtilitiesCS.Test")` attribute at UtilitiesCS/Properties/AssemblyInfo.cs line 19).
+ - TaskMaster.runsettings and scripts/vscode/TaskMaster.cli.runsettings. Workers and Scope stay as they are.
+ - The two pre-existing `[DoNotParallelize]` attributes in QuickFiler.Test (Helper Classes/EmailMoveMonitorTests.cs line 24 and Helper Classes/ViewerQueueStaticWrapperTests.cs line 11). They are unrelated to this issue and stay untouched.
+ - Same-class defects in UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs (lines 173 and 318 call its own `GetSolutionFile()`; lines 186 to 198 swallow `IOException`; lines 213 to 234 open the real solution file) and UtilitiesCS.Test/HelperClasses/DirectoryInfoWrapper_Tests.cs (lines 60, 79 and 381 assert that TaskMaster.sln is enumerated from the repository root). Recorded as follow-ups in Rollout & Follow-up.
+ - The message wording "cannot marshal cross-thread UI work" asserted at QuickFiler.Test/Viewers/BreadcrumbUiThreadDispatchTests.cs line 305 (broader than the mechanism; wording-only follow-up).
+ - The .claude directory and the two JSON files under config.
+- Explicitly excluded systems, integrations, or datasets: Outlook, VSTO, and any live UI pump. Neither rewritten test touches a control.
+
+## Root Cause Analysis
+
+**Distinct-thread defect.** `Task.Run` queues a work item to the thread pool. When the queuing thread is itself a pool thread, the item lands on that thread's local queue. A blocking wait (`GetAwaiter().GetResult()`) on a not-yet-started task attempts inline execution through the task scheduler, and the thread-pool scheduler permits inlining when the waiting thread is a pool thread. Under the parallel regime every test method runs on a pool thread, so the "other thread" can be the calling thread. A guard that decides by thread identity (`Dispatcher.CheckAccess()`, which is `Thread` object identity; or the `_ownerThreadId` comparison in `BreadcrumbUiDispatcher.IsCurrentBoundary()`) then sees the owner and admits the call. A dedicated `Thread` object constructed by the test is distinct from every live thread by construction, so an in-thread precondition asserted on it holds under any scheduler, and an untimed `Join()` on it parks no pool slot waiting on another pool slot.
+
+The six other candidate sites do not share the defect because the guards they reach do not consult thread identity: `Dispatch` with a captured context compares `SynchronizationContext.Current` to the captured context by reference (BreadcrumbUiDispatcher.cs lines 269 to 272); `DispatchValue` never calls `IsCurrentBoundary()` and faults for every caller outside an executing callback when the context is null; `BreadcrumbDropDownHost.Close`, `Reset` and `Dispose` carry no thread guard; and `TaskCompletionSource` completions have no thread-dependent outcome. Full per-site reasoning is in the Triage table.
+
+**File-handle defect.** The test reaches for a file that exists in every checkout, the solution file, because the public `FileInfoWrapper(FileInfo)` constructor wraps a `PhysicalFileInfoAdapter` whose `OpenRead()` is not seamed (UtilitiesCS/HelperClasses/FileSystem/PhysicalFileInfoAdapter.cs line 154), so a real file is the only way to exercise `OpenRead` through the public constructor. `FileInfo.OpenRead()` requests `FileShare.Read`, so any concurrent holder that denies shared reads makes the open fail. The internal `FileInfoWrapper(IFileInfo)` constructor removes the need for a real file for the wrapper's own contract, which is pure delegation (FileInfoWrapper.cs lines 153 to 156 for `OpenRead`). The three metadata tests never needed a handle: `FullName`, `Name`, `Extension`, `DirectoryName`, `Directory`, `ToString()` are path computations and `Exists` is a metadata query.
+
+Both patterns were copied from earlier tests and survived because they usually pass. They must not be fixed by serialising the run: Workers one, `[DoNotParallelize]`, retries, sleeps and timeouts are prohibited as fixes for this issue.
+
+## Proposed Fix
+
+### Design summary (what changes where)
+
+1. **Shared dedicated-thread helper.** New internal static class `DedicatedWorkerThread` in the QuickFiler.Test TestSupport folder (namespace `QuickFiler.Test.TestSupport`, matching the existing WinFormsPumpHost.cs). One member, `internal static Exception Run(Action action)`, with the identical body and XML remarks of the private `RunOnDedicatedWorkerThread` at ItemViewerBreadcrumbThreadAffinityTests.cs lines 373 to 403: construct a `Thread`, set `IsBackground = true`, `Start()`, untimed `Join()`, return the exception captured by the delegate or null. The helper adds no assertion of its own: each test states its site-specific distinctness precondition inside its delegate so that a failure names the guard under test. The helper contains no `Thread.Sleep`, `Task.Delay`, timeout, or retry. Estimated 40 to 60 total lines.
+2. **Affinity file split.** `ItemViewerBreadcrumbThreadAffinityTests` becomes `partial`. The existing file keeps the four owner-thread admission tests (current lines 39 to 197), `InertOperations` (lines 348 to 355) and the three nested types `InertDropDownHost`, `DrainableSynchronizationContext`, `ViewerScope` (lines 405 to 488). The new Part2 file holds the three cross-thread tests (current lines 199 to 346) and `ClearViewerDispatcher` (lines 357 to 371). `RunOnDedicatedWorkerThread` is deleted from the class; the three cross-thread tests call `DedicatedWorkerThread.Run`. Private nested types and private static helpers remain visible across partial declarations, so no accessibility is widened. Estimated totals: about 290 lines for the existing file and 200 to 230 for the Part2 file, both under the 500 total-line ceiling.
+3. **Site rewrite, `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` (Part2 file).** Before `ClearViewerDispatcher(scope.Viewer)`, capture `Dispatcher owner = scope.Viewer.UiDispatcher;` (requires a `using System.Windows.Threading;` directive in the Part2 file). Replace the `Task.Run` block with `Exception captured = DedicatedWorkerThread.Run(() => { ... })` whose delegate first asserts `owner.CheckAccess().Should().BeFalse(reason)` and then calls `scope.Viewer.InitializeBreadcrumbPipeline(provider.Object, operations)`. Assert `captured.Should().BeNull(reason)` and `scope.Viewer.BreadcrumbCoordinator.Should().BeSameAs(before)` unchanged. Rewrite the remark at current lines 311 to 317 so it states unconditionally that the call is off the owner thread and therefore discriminates against the pre-#781 context-reference guard.
+4. **Site rewrite, `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` (BreadcrumbPopupBoundaryCoverageTests.cs, current lines 52 to 62).** Capture `int ownerThreadId = Environment.CurrentManagedThreadId;` in Arrange (this is the value `CreateOwnerOnlyDispatcher` passes as owner). Replace the `Task.Run` and its blocking wait with `Exception captured = DedicatedWorkerThread.Run(() => { ... })` whose delegate first asserts `Environment.CurrentManagedThreadId.Should().NotBe(ownerThreadId, reason)` and then calls `dispatcher.Dispatch(() => executions++)`. The rejection path reports and returns `Task.CompletedTask` synchronously (BreadcrumbUiDispatcher.cs lines 97 to 105), so no task wait is needed. Assert `captured.Should().BeNull()`, then the two existing assertions unchanged: `executions.Should().Be(0)` and a single error whose message contains "cannot marshal". The file is 361 total lines today and stays under the ceiling. No apartment state is set: no control is touched and a null-context dispatcher never posts.
+5. **File tests (FileInfoWrapper_Tests.cs).** Delete `GetSolutionFile()` and the `AppDomain` walk-up. The four tests keep their names and scenario intent:
+ - `Properties_ShouldMirrorWrappedFileInfo`, `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory`, `ToString_ShouldDelegateToWrappedFileInfo` construct a `FileInfo` over the rooted literal C:\Repo\fixture.sln (the rooted-literal style already used at lines 87 and 113 of the same file). The path is not expected to exist and does not point into the repository. The assertions stay as they are (`Exists` mirrors `file.Exists`, so it is deterministic either way; `Extension` is ".sln"; `FullName`, `Name`, `DirectoryName`, `Directory.FullName`, the explicit `DirectoryInfoWrapper` cast, and `ToString()` are path computations). `Length` is not asserted on this fixture because it throws for a missing file. These three tests are what keeps the public constructor path (FileInfoWrapper.cs lines 14 to 19 into `PhysicalFileInfoAdapter`) and the explicit cast operator covered; the mocked tests cannot reach them because a `Mock` is not a `DirectoryInfoWrapper`.
+ - `OpenRead_ShouldReturnReadableStreamForWrappedFile` constructs the wrapper through the internal seam with a strict `Mock` whose `OpenRead()` returns a test-owned `FileStream` sentinel, and asserts `wrapper.OpenRead().Should().BeSameAs(sentinel)`, `CanRead` is true, and `Length` is greater than zero. The wrapper's contract for `OpenRead` is delegation, so instance identity is the complete contract; the two readability assertions confirm the returned object is the live stream the test opened.
+
+**Chosen stream design and justification (binding operator constraint).** The sentinel is `new FileStream(typeof(FileInfoWrapper_Tests).Assembly.Location, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)`, disposed by a `using` declaration in the test.
+- It is test-owned: the test constructs it, holds the only reference, passes it to a mock it also owns, and disposes it. Nothing outside the test method touches it.
+- It is not a repository-tracked file: the test assembly is a build output under the test project's bin directory, which git ignores. It is not TaskMaster.sln or any source file.
+- It is not a temporary file: the test creates nothing, writes nothing, and deletes nothing. The file pre-exists as the running test host's own loaded image and remains after the test. The unit-test policy's prohibition on temporary files is satisfied by construction, not by cleanup.
+- It cannot be denied by another process's handle in any observed mode: the open requests read access only and `FileShare.ReadWrite`, so every concurrent reader and writer is admitted, and the running host's image mapping keeps the file in existence for the whole run. This exact open shape already executes six times in the same file (lines 189 to 224) and in PhysicalFileSystemAdapters_Tests.cs lines 258 to 275 with no recorded contention incident.
+- Why not `MemoryStream`: `IFileInfo.OpenRead()` returns the concrete `FileStream` type (UtilitiesCS/Interfaces/IHelperClasses/IFileInfo.cs line 26), as do `Create()`, `Open(...)` and `OpenWrite()`; a `MemoryStream` cannot be returned through the seam for these members. `MemoryStream` remains usable only behind the `StreamReader` and `StreamWriter` members, which the existing test at lines 186 to 188 already does.
+- Handle-free alternative evaluated and rejected: `FileStream` on net48 needs a path or a `SafeFileHandle`. The only file-less handle sources available are anonymous pipes and device pseudo-files; a `FileStream` over either is non-seekable, so `Length` throws `NotSupportedException`, and it is still an OS handle rather than "no file". Dropping the `Length` assertion to admit a pipe would weaken the scenario the issue names. The assembly-location `FileStream` is therefore the chosen design.
+
+### Boundaries and invariants to preserve
+
+- The parallel regime is unchanged: Workers zero, Scope ClassLevel. No `[DoNotParallelize]`, no `Workers` change, no retry attribute or loop, no `Thread.Sleep`, `Task.Delay`, timeout or wall-clock wait is introduced anywhere in the diff.
+- No production file changes. The negative-control mutations in Test Strategy are applied and reverted inside the same task; the final diff contains no change under QuickFiler/ or UtilitiesCS/.
+- Every existing test name in the three edited test files survives; no test is deleted or renamed.
+- The two rewritten guard tests keep their original observable assertions and add only the in-thread precondition and the `captured` null check.
+- The helper's contract is identical to the #900 private helper so the two existing worker-thread tests behave exactly as before.
+
+### Dependencies or blocked work
+
+- None. The seam exists in production; the split and the helper are test-project-only.
+
+### Implementation strategy (what changes, not sequencing)
+
+#### Files/modules to change
+See `## Write Set`. Three existing test files are modified, two test files are created, one test project file gains two Compile entries, and three evidence directories are created under the feature folder.
+
+#### Functions/classes/CLI commands impacted
+- `ItemViewerBreadcrumbThreadAffinityTests` (becomes `partial`; loses `RunOnDedicatedWorkerThread`; three tests and `ClearViewerDispatcher` relocate to the Part2 file).
+- `BreadcrumbPopupBoundaryCoverageTests.Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` (rewritten body).
+- `FileInfoWrapper_Tests` (four test bodies rewritten; `GetSolutionFile` removed).
+- New `QuickFiler.Test.TestSupport.DedicatedWorkerThread.Run(Action)`.
+- No CLI command changes.
+
+#### Data flow and validation changes
+None in production. In tests, the distinctness precondition moves from "assumed by `Task.Run`" to "asserted inside the dedicated thread".
+
+#### Error handling and logging updates
+None.
+
+#### Rollback/feature-flag considerations (if applicable)
+Not applicable; test-only change. Reverting the commit restores the prior tests.
+
+### Technical specifications (interfaces/contracts)
+
+#### Inputs/outputs and formats
+- `DedicatedWorkerThread.Run(Action action)`: runs `action` on a new background thread, joins it, returns the `Exception` the delegate threw or null. Same contract as the removed private helper.
+
+#### Required configuration keys and defaults
+None.
+
+#### Backward-compatibility expectations
+No public API changes. `DedicatedWorkerThread` is `internal` to QuickFiler.Test.
+
+#### Performance constraints (latency/throughput/memory)
+Each rewritten test starts and joins exactly one thread; the join is a completion wait on one bounded synchronous call. No measurable suite-time change is expected.
+
+## Triage of every Task.Run site in QuickFiler.Test
+
+Verdicts and reasons follow the research record section A2 and were re-verified against the current tree. File paths are project-relative under QuickFiler.Test and deliberately unbackticked; only the two AFFECTED rows are edited.
+
+| Site (file:line) | Containing test or helper | Verdict | Reason |
+|---|---|---|---|
+| Helper Classes/EmailMoveMonitorTests.cs:298 | `UnhookItem_InvokedFromThreadPoolThread_RunsComAccessOnMarshalTargetThread` | UNAFFECTED | The asserted property is guaranteed by a fresh `new Thread` inside the marshal delegate, distinct from every live thread including an inlined caller; `UnhookItem` marshals unconditionally. Class already carries `[DoNotParallelize]` for unrelated reasons; left as is. |
+| Viewers/BreadcrumbCoordinatorLifecycleTests.cs:350 | `QueuedCompletion_DisposedBeforeOwnerDrain_DoesNotPublish` | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:58 | `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` | **AFFECTED** | Owner-only dispatcher (null context) reaches the thread-id branch of `IsCurrentBoundary()`; the blocking wait can inline the work item onto the owner thread, in which case the action runs and the test fails spuriously. |
+| Viewers/BreadcrumbPopupControlDispatchTests.cs:29 | `SurfaceFactory_WorkerCompletion_DispatchesEveryStageAndCleanup` | UNAFFECTED | Context-backed dispatcher; every stage goes through `DispatchValue`, which posts unless inside an executing callback and never reads thread identity. |
+| Viewers/BreadcrumbPopupControlDispatchTests.cs:111 | `Readiness_DisposeFromAmbientNullWorker_DispatchesHandlerDetachment` | UNAFFECTED | Guard compares ambient context by reference; the body sets ambient null itself, so the post occurs on any thread. |
+| Viewers/BreadcrumbPopupControlDispatchTests.cs:300 | helper `CompleteOnWorker` | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| Viewers/BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192 | `CaptureCurrent_NullAndControlledContexts_FailFastAndCapture` | UNAFFECTED | `PostAsync` compares ambient context by reference; on the test thread the ambient context is the restored previous one, never the captured one, so the post happens on any thread; `PostCount` pins the posted path. |
+| Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:61 | `WorkerProviderAndSelectorToggle_MarshalPostsAndCallbackEntryToOwningBoundary` | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:75 | same test | UNAFFECTED | Context-backed coordinator; inbound `Dispatch` posts whenever ambient context is not the captured one and the body forces ambient null; a semaphore wait precedes the blocking wait, so the body has already run on another pool thread before inlining is possible. |
+| Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:137 | `PopupHost_WorkerCompletions_RunOnlyWhenCreatorThreadDrainsBoundary` | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:148 | same test | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:154 | same test | UNAFFECTED | `host.Close(...)` has no thread guard; the test asserts where drained work ran, not where `Close` originated. |
+| Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:161 | same test | UNAFFECTED | `host.Reset` has no thread guard; same reasoning. |
+| Viewers/BreadcrumbSelectorOpenRetryTests.cs:37 | `MouseToggle_FirstOpenFaultsAfterAwait_SecondClickRetriesCleanly` | UNAFFECTED | Completes a `TaskCompletionSource` (`SetException`). |
+| Viewers/BreadcrumbSelectorOpenRetryTests.cs:210 | `Dispose_WhenResetAndOpenWorkAreQueued_HasNoLateActivity` | UNAFFECTED | Completes a `TaskCompletionSource` only. |
+| Viewers/BreadcrumbSelectorOpenRetryTests.cs:218 | same test | UNAFFECTED | `host.Reset` has no thread guard; assertions are operation counts after drain. |
+| Viewers/BreadcrumbSelectorOpenRetryTests.cs:219 | same test | UNAFFECTED | `host.Dispose` has no thread guard. |
+| Viewers/BreadcrumbUiThreadDispatchTests.cs:51 | `SetSuggestionsAsync_WorkerProviderCompletion_SchedulesPostOnOwningContext` | UNAFFECTED | Completes a `TaskCompletionSource`; awaited, so no wait-inlining. |
+| Viewers/BreadcrumbUiThreadDispatchTests.cs:90 | `InboundWorkerMessage_SchedulesEveryPostAndCallbackOnOwningContext` | UNAFFECTED | Awaited; context-backed coordinator posts whenever ambient context is not the captured one; outcome identical on any thread. |
+| Viewers/BreadcrumbUiThreadDispatchTests.cs:301 | `ProductionCaptureWithoutUiContext_FailsFast` | UNAFFECTED | `DispatchValue` on an owner-only dispatcher faults for every caller outside an executing callback and never reads `_ownerThreadId`; the expected exception is produced on the owner thread too. (The message wording is a follow-up, not a fix here.) |
+| Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:332 | `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` | **AFFECTED** | The blocking wait can inline the work item onto the owner thread, on which branch the pre-#781 guard would also have admitted the call; the test then passes without discriminating. |
+| Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs:222 | `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` | UNAFFECTED | Asserts a value observed by a second gate acquirer, not thread identity; the wait is a `ManualResetEventSlim`, not a task wait. Its intermittency is tracked under #823. |
+
+## Assumptions, Constraints, Dependencies
+
+- Assumptions (environment, data, access): the test host loads the UtilitiesCS.Test assembly from its bin output, so `typeof(FileInfoWrapper_Tests).Assembly.Location` is a readable on-disk path for the whole run (already relied on six times in the same file). The `InternalsVisibleTo("UtilitiesCS.Test")` attribute remains in place.
+- Constraints (budget, performance, compatibility): temporary files prohibited; parallel regime unchanged; no production change; 500 total-line ceiling on every .cs file in the Write Set; MSTest, Moq, FluentAssertions only; committed evidence is projections only, with no absolute host path, account name or host name.
+- External dependencies (services, libraries, releases): none.
+
+## Data / API / Config Impact
+
+- User-facing or API changes: none.
+- Data or migration considerations: none.
+- Logging/telemetry updates (if any): none.
+- Compatibility notes (CLI flags, config schemas, versioning): none. The project file gains two Compile Include entries in the existing explicit-include style.
+
+## Test Strategy
+
+**Baseline (before any edit).** Run both suites under the root runsettings file with the isolation switch and record a TRX-derived summary as a Markdown projection named test-run-baseline.md under the baseline evidence directory. Run the CLAUDE.md step-4 coverage route and record the package-level JaCoCo projection and the one-line first-party coverage summary as coverage-baseline.md in the same directory. Record total line counts of the three test files to be edited (490, 361, 359 today).
+
+**Regression tests updated (no new production behaviour, so no new production test).**
+- `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` (BreadcrumbPopupBoundaryCoverageTests.cs): rewritten per Proposed Fix item 4.
+- `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` (moved to the Part2 file): rewritten per Proposed Fix item 3.
+- `InitializeBreadcrumbPipeline_WorkerThread_ThrowsBoundaryDiagnostic` and `ConfigureBreadcrumbDropDown_WorkerThread_ThrowsBoundaryDiagnostic` (moved to the Part2 file): body unchanged except the helper call target.
+- `Properties_ShouldMirrorWrappedFileInfo`, `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory`, `OpenRead_ShouldReturnReadableStreamForWrappedFile`, `ToString_ShouldDelegateToWrappedFileInfo` (FileInfoWrapper_Tests.cs): rewritten per Proposed Fix item 5.
+
+**Negative controls (each rewritten guard test must be observed failing).** Every mutation is applied, built, run with a scoped test-case filter on the fully qualified test name under the root runsettings file with the isolation switch, recorded, reverted with a git checkout of the mutated file, and the revert proven with the porcelain status output showing no residual change to that file. Each control is then re-run unmutated in the same artifact so the before/after pair is visible. Controls are never committed; the final diff contains no production change.
+1. Owner-only dispatcher guard (temporary production edit): replace the thread-id comparison at BreadcrumbUiDispatcher.cs lines 276 to 277 with `return true;`. Expected: the rewritten `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` fails at the `executions` assertion ("Expected executions to be 0, but found 1") because the action ran inline on the worker. Projection: mutation-owner-only-dispatcher-guard.md.
+2. Null-owner escape (temporary production edit): at ItemViewer.Breadcrumb.cs lines 435 to 438 replace the bare `return;` with the pre-#781 context-reference check that throws `InvalidOperationException` when `SynchronizationContext.Current` is not the captured context. Expected: the rewritten `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` fails at the `captured` null assertion because the dedicated thread's ambient context is null. This is the discrimination the old `Task.Run` shape only had conditionally. Projection: mutation-null-owner-escape.md.
+3. Inline precondition (test-only edit): insert `action();` as the first statement of `DedicatedWorkerThread.Run` so the delegate also runs on the caller. Expected: both rewritten tests and the two existing worker-thread tests fail at their in-thread precondition with the stated reason text, proving the preconditions are live. Projection: mutation-inline-precondition.md.
+4. OpenRead sentinel (test-only edit): point the mock's `OpenRead()` setup at a second, distinct `FileStream` sentinel. Expected: `OpenRead_ShouldReturnReadableStreamForWrappedFile` fails at `BeSameAs`. Projection: mutation-openread-sentinel.md.
+
+Isolation note for the controls: each projection records the runsettings file used, the isolation switch, the exact test-case filter, the mutated file and hunk, the failed test names with the failure message excerpt, and the porcelain status output after revert. A control that passes while the mutation is applied means the test does not exercise the guard and is a blocking finding.
+
+**Full suites in the parallel regime.** After the rewrite and after every control is reverted, run the full QuickFiler.Test and UtilitiesCS.Test assemblies under the root runsettings file (Workers zero, Scope ClassLevel) with the isolation switch. Record TRX-derived summaries as parallel-suite-quickfiler-test.md and parallel-suite-utilitiescs-test.md under the regression-testing evidence directory. The QuickFiler.Test summary must list the three cross-thread test names and `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction`, which is the proof that the Part2 file and the helper compiled (an unregistered file is silently absent from the run). If the local machine's known shell-icon stall in UtilitiesCS.Test reproduces, the filter that excludes those classes must be recorded verbatim in the projection together with the excluded class names and a note that the stall is pre-existing on main; no other exclusion is permitted.
+
+**Toolchain (CLAUDE.md order; restart from step one on any change or failure).**
+1. `dotnet tool run csharpier format .` then `dotnet tool run csharpier check .`
+2. `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true`
+3. `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true`
+4. The `test: MSTest with Coverage (Koverage)` route (Invoke-MSTestWithCoverage.ps1 under scripts/vscode), which writes the fixed-name TRX under the coverage results directory that the summary is derived from.
+Record commands, exit codes and the count of "Skipping target CoreCompile" lines (must be zero for both rebuilds, proving the analyzers and the nullable gate actually compiled) in toolchain-pass.md under the qa-gates evidence directory. Record the post-change package-level JaCoCo projection and one-line summary as coverage-final.md in the same directory.
+
+**Coverage impact.** Changed lines are all test lines and sit outside the production denominator. Production lines the four file tests keep covered: FileInfoWrapper.cs lines 14 to 19 (public constructor), 21 to 24 (seam), 153 to 156 (`OpenRead`), 211 to 214 (explicit cast), and the `PhysicalFileInfoAdapter` property lines. The QuickFiler rewrite covers the same production branches as before. The final projection's per-package figures for UtilitiesCS and QuickFiler must be no lower than the baseline projection.
+
+**Evidence hygiene.** Committed evidence is Markdown projections only (CLAUDE.md "Committed Test Evidence Format" and the maintainer decision on issue 671): no raw TRX, Cobertura XML or .coverage document is added anywhere. Every projection replaces absolute host paths, account names and host names with the placeholders repo-root, user-profile, user and host, each enclosed in angle brackets. Projection filenames are fixed (not timestamped); the run timestamp is a Timestamp field inside each artifact.
+
+**Manual validation steps.** None.
+
+## Write Set
+
+- `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs` (modify: make partial; remove the three cross-thread tests, `ClearViewerDispatcher` and `RunOnDedicatedWorkerThread`)
+- `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.Part2.cs` (create: partial continuation with the three cross-thread tests and `ClearViewerDispatcher`)
+- `QuickFiler.Test/TestSupport/DedicatedWorkerThread.cs` (create: shared dedicated-thread helper)
+- `QuickFiler.Test/Viewers/BreadcrumbPopupBoundaryCoverageTests.cs` (modify: rewrite `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction`)
+- `QuickFiler.Test/QuickFiler.Test.csproj` (modify: two Compile Include entries)
+- `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs` (modify: rewrite four tests; remove `GetSolutionFile`)
+- `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/evidence/baseline/` (create: baseline projections)
+- `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/evidence/regression-testing/` (create: mutation and parallel-suite projections)
+- `docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/evidence/qa-gates/` (create: toolchain and coverage projections)
+
+## Acceptance Criteria
+
+- [x] AC1. `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` in BreadcrumbPopupBoundaryCoverageTests.cs contains no `Task.Run` call and no `GetAwaiter().GetResult()` wait; it captures the owner thread id before constructing the owner-only dispatcher, runs the guarded `Dispatch` call through `DedicatedWorkerThread.Run`, and the delegate asserts that `Environment.CurrentManagedThreadId` differs from the captured owner id before `Dispatch` is invoked; the returned exception is asserted null and the two pre-existing assertions (zero executions; exactly one reported error whose message contains "cannot marshal") are present unchanged.
+- [x] AC2. `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow`, now in the Part2 file, contains no `Task.Run` call and no `GetAwaiter().GetResult()` wait; it captures the owning `Dispatcher` before `ClearViewerDispatcher` is called, runs the guarded `InitializeBreadcrumbPipeline` call through `DedicatedWorkerThread.Run`, and the delegate asserts that the captured owner's `CheckAccess()` is false before the guarded call; the returned exception is asserted null and `BreadcrumbCoordinator` is asserted to be the same instance as before; the remark on the test states the off-owner-thread discrimination unconditionally.
+- [x] AC3. Every other `Task.Run` site in QuickFiler.Test, the twenty UNAFFECTED rows of the Triage table, is byte-identical to the merge base: a diff of QuickFiler.Test against the merge base touches no line of EmailMoveMonitorTests.cs, BreadcrumbCoordinatorLifecycleTests.cs, BreadcrumbPopupControlDispatchTests.cs, BreadcrumbPopupBoundaryCoverageTests.Part2.cs, BreadcrumbSelectorToggleUiBoundaryTests.cs, BreadcrumbSelectorOpenRetryTests.cs, BreadcrumbUiThreadDispatchTests.cs or QfcItemController.UiThreadDispatcherFixtureTests.cs, and the two pre-existing `[DoNotParallelize]` attributes in QuickFiler.Test are untouched.
+- [x] AC4. `ItemViewerBreadcrumbThreadAffinityTests` is declared `partial` in both ItemViewerBreadcrumbThreadAffinityTests.cs and ItemViewerBreadcrumbThreadAffinityTests.Part2.cs; the existing file holds the four owner-thread admission tests, `InertOperations` and the three nested types; the Part2 file holds the three cross-thread tests and `ClearViewerDispatcher`; no test method in the class is renamed or removed; the private `RunOnDedicatedWorkerThread` helper no longer exists in the class.
+- [x] AC5. Each of ItemViewerBreadcrumbThreadAffinityTests.cs, ItemViewerBreadcrumbThreadAffinityTests.Part2.cs, DedicatedWorkerThread.cs, BreadcrumbPopupBoundaryCoverageTests.cs and FileInfoWrapper_Tests.cs is at or under five hundred total lines, measured as total newline-terminated lines; the project file and Markdown artifacts are exempt from this ceiling by policy.
+- [x] AC6. The QuickFiler.Test project file contains a Compile Include entry for the Part2 file and a Compile Include entry for the DedicatedWorkerThread file, both in the project-relative backslash form used by the neighbouring entries, and the committed QuickFiler.Test parallel-suite projection lists `InitializeBreadcrumbPipeline_WorkerThread_ThrowsBoundaryDiagnostic`, `ConfigureBreadcrumbDropDown_WorkerThread_ThrowsBoundaryDiagnostic`, `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` and `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` as executed and passed.
+- [x] AC7. `DedicatedWorkerThread` is an `internal static` class in namespace `QuickFiler.Test.TestSupport` exposing `internal static Exception Run(Action action)` whose body constructs a `Thread`, sets `IsBackground` true, starts it, joins it without a timeout, and returns the exception the delegate threw or null; it carries the dedicated-thread rationale remark from the removed private helper; and it contains no `Thread.Sleep`, `Task.Delay`, timeout argument, retry loop, or assertion. All four dedicated-thread tests in QuickFiler.Test call this member.
+- [x] AC8. FileInfoWrapper_Tests.cs contains no method named `GetSolutionFile`, no occurrence of the text "TaskMaster.sln", no reference to `AppDomain.CurrentDomain.BaseDirectory`, and no call that creates, writes to, or deletes a file: no `File.Create`, `File.WriteAll`, `File.Delete`, `Path.GetTempFileName`, `Path.GetTempPath`, no `FileMode` other than `Open`, and no `FileAccess` other than `Read`; the four tests `Properties_ShouldMirrorWrappedFileInfo`, `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory`, `OpenRead_ShouldReturnReadableStreamForWrappedFile` and `ToString_ShouldDelegateToWrappedFileInfo` keep their names.
+- [x] AC9. `OpenRead_ShouldReturnReadableStreamForWrappedFile` constructs the wrapper through the internal `FileInfoWrapper(IFileInfo)` constructor with a strict Moq mock of `IFileInfo` whose `OpenRead()` returns a `FileStream` the test opens over `typeof(FileInfoWrapper_Tests).Assembly.Location` with `FileMode.Open`, `FileAccess.Read` and `FileShare.ReadWrite` inside a `using` declaration; it asserts the wrapper's `OpenRead()` result is the same instance as that sentinel, that `CanRead` is true, and that `Length` is greater than zero.
+- [x] AC10. `Properties_ShouldMirrorWrappedFileInfo`, `ExplicitDirectoryCast_ShouldReturnWrappedContainingDirectory` and `ToString_ShouldDelegateToWrappedFileInfo` construct their `FileInfo` from a rooted literal path that does not point into the repository, open no stream, and assert none of `Length`, `OpenRead`, `Open`, `Create` or `OpenWrite`; their existing assertions on `Exists`, `FullName`, `Name`, `Extension`, `DirectoryName`, `Directory`, the explicit `DirectoryInfoWrapper` cast and `ToString()` are retained.
+- [x] AC11. A committed Markdown projection named mutation-owner-only-dispatcher-guard.md under the regression-testing evidence directory shows that, with the thread-id comparison in `BreadcrumbUiDispatcher.IsCurrentBoundary()` temporarily replaced by an unconditional true, the rewritten `Dispatcher_OwnerOnlyWorker_ReportsWithoutRunningAction` fails at its `executions` assertion under the root runsettings file with the isolation switch and a scoped test-case filter, and that the same filter passes after the revert; the projection records the mutated file and hunk, the failed test name, the failure message excerpt, and porcelain status output after the revert showing no residual change.
+- [x] AC12. A committed projection named mutation-null-owner-escape.md under the regression-testing evidence directory shows that, with the null-owner `return` in `ItemViewer.ThrowIfOffUiBoundary` temporarily replaced by the pre-fix context-reference throw, the rewritten `InitializeBreadcrumbPipeline_NullOwningDispatcher_DoesNotThrow` fails at its `captured` null assertion, and passes after the revert, with the same recorded fields as AC11.
+- [x] AC13. A committed projection named mutation-inline-precondition.md under the regression-testing evidence directory shows that, with `action()` temporarily inserted as the first statement of `DedicatedWorkerThread.Run`, all four dedicated-thread tests fail at their in-thread distinctness precondition, and pass after the revert, with the same recorded fields as AC11.
+- [x] AC14. A committed projection named mutation-openread-sentinel.md under the regression-testing evidence directory shows that, with the mock's `OpenRead()` setup temporarily pointed at a second distinct `FileStream`, `OpenRead_ShouldReturnReadableStreamForWrappedFile` fails at its same-instance assertion, and passes after the revert, with the same recorded fields as AC11.
+- [x] AC15. The final diff against the merge base modifies no file under the QuickFiler or UtilitiesCS production directories, nothing under the .claude directory, and neither JSON file under config; the set of changed, added and deleted repository files equals the Write Set entries plus documents inside the feature folder.
+- [x] AC16. The full QuickFiler.Test and UtilitiesCS.Test assemblies pass under the root runsettings file (Workers zero, Scope ClassLevel) with the isolation switch, with zero failed tests, and the committed projections parallel-suite-quickfiler-test.md and parallel-suite-utilitiescs-test.md under the regression-testing evidence directory record the totals, the runsettings file, the switch and any test-case filter verbatim; the only permitted filter excludes the pre-existing local shell-icon stall classes in UtilitiesCS.Test and names them; the diff introduces no `[DoNotParallelize]`, no change to Workers or Scope, no retry attribute or loop, and no `Thread.Sleep`, `Task.Delay`, timeout or wall-clock wait.
+- [x] AC17. The full C# toolchain passes in one final pass in CLAUDE.md order (csharpier check; analyzer rebuild; TreatWarningsAsErrors rebuild; the MSTest-with-coverage route), and the committed projection toolchain-pass.md under the qa-gates evidence directory records each command, its exit code, and that both rebuild logs contain zero "Skipping target CoreCompile" lines.
+- [x] AC18. The committed projections coverage-baseline.md (baseline evidence directory) and coverage-final.md (qa-gates evidence directory) each contain a package-level JaCoCo projection and the one-line first-party coverage summary, and the final per-package line and branch figures for UtilitiesCS and QuickFiler are not lower than the baseline figures.
+- [x] AC19. The diff adds no file with a .trx, .xml or .coverage extension anywhere in the repository, and no committed evidence artifact contains an absolute host path, an account name or a host name; the placeholders repo-root, user-profile, user and host (each enclosed in angle brackets) are used in their place.
+
+## Numeric Derivation Evidence
+
+The acceptance criteria above contain no standalone digits; every count is written in words. The population claims they rely on (22 `Task.Run` sites in QuickFiler.Test, of which exactly two are AFFECTED and twenty UNAFFECTED) are derived in the research record's `## Numeric Derivation Evidence` section, which supplies the complete family, exhaustive scope, inclusion and exclusion rules, two independently constructed search strategies with distinct regular expressions and file filters, two independently enumerated member sets, both counts, and an explicit member-set comparison showing the sets are identical. That section is at docs/features/active/2026-09-28-tests-depend-on-uncontrolled-environment-931/research/2026-09-28T20-15-tests-depend-on-uncontrolled-environment-research.md. The primary grep was re-run in this worktree on 2026-09-28 and returned the same 29 raw lines and the same 22 retained members.
+
+## Risks & Mitigations
+
+- Technical or operational risks:
+ - The Part2 file or the helper is created on disk but not registered in the project file, so the moved tests silently disappear from the run. Mitigation: AC6 requires the four test names to appear as executed in the committed suite projection.
+ - A negative control passes while the mutation is applied (the rewritten test does not exercise the guard). Mitigation: AC11 to AC14 require an observed failure with message excerpt; a passing control is a blocking finding.
+ - A temporary production mutation is left in the tree. Mitigation: each control's projection records the porcelain status after revert, and AC15 forbids any production change in the final diff.
+ - The rooted literal path used by the three metadata tests happens to exist on some machine. Mitigation: the assertions mirror `file.Exists` and never assert `Length`, so the outcome is identical either way.
+ - The local shell-icon stall in UtilitiesCS.Test prevents a full local run. Mitigation: AC16 permits exactly that documented exclusion and no other; CI executes the excluded classes.
+- Mitigations and rollbacks: test-only change; revert the commit to restore the previous tests.
+
+## Rollout & Follow-up
+
+- Release/rollout steps: merge through the normal PR gate after the toolchain and the parallel suite runs pass. No deployment impact.
+- Post-fix monitoring or clean-up tasks: none beyond the follow-ups below.
+- Follow-ups to record as potential entries through the promotion lifecycle (not fixed here; paths deliberately unbackticked):
+ 1. UtilitiesCS.Test/HelperClasses/PhysicalFileSystemAdapters_Tests.cs: its own `GetSolutionFile()` (lines 173, 318, 373 to 376), the `catch (IOException)` blocks at lines 43 and 195 that swallow contention, and the read opens on the real solution file at lines 213 to 234. Same defect class as #906.
+ 2. UtilitiesCS.Test/HelperClasses/DirectoryInfoWrapper_Tests.cs lines 60, 79 and 381: assertions that TaskMaster.sln is enumerated from the repository root. Same defect class as #906.
+ 3. QuickFiler.Test/Viewers/BreadcrumbUiThreadDispatchTests.cs line 305: the asserted message "cannot marshal cross-thread UI work" describes the mechanism more broadly than `DispatchValue` implements (it faults for every caller outside an executing callback, on any thread). Wording-only.
+ 4. The #900 follow-up handoff (docs/features/active/breadcrumb-thread-affinity-tests-assume-taskrun-distinct-thread-900/evidence/other/) attributes BreadcrumbUiThreadDispatchTests.cs line 301 to the owner-thread-id check; the research record shows it reaches `DispatchValue`, which never reads the owner id. Documentation correction only.
+- Links: issue #931 (https://github.com/drmoisan/TaskMaster/issues/931), consolidated issues #905 and #906, precedent PR #904 (issue #900), maintainer decision on issue 671 (projections-only evidence), research record cited in the header.
diff --git a/docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md b/docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md
new file mode 100644
index 000000000..2382d3761
--- /dev/null
+++ b/docs/features/potential/promoted/2026-09-28-tests-depend-on-uncontrolled-environment.md
@@ -0,0 +1,67 @@
+# tests-depend-on-uncontrolled-environment (Issue #931)
+
+- Date captured: 2026-09-28
+- Author: Dan Moisan
+- Status: Promoted -> docs/features/active/tests-depend-on-uncontrolled-environment/ (Issue #931)
+
+> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template.
+
+- Issue: #931
+- Issue URL: https://github.com/drmoisan/TaskMaster/issues/931
+- Last Updated: 2026-09-28
+## Summary
+Consolidates #905 and #906. The shared root cause: a unit test depends on environment state it does not control, so its result depends on scheduling or on other processes rather than on the code under test.
+
+1. **#905:** tests use `Task.Run` as the "other thread". `Task.Run` guarantees only a thread-pool thread, never a different one, so under parallel execution the guard under test can go unexercised. PR #904 (#900) fixed two instances. Remaining:
+ - `QuickFiler.Test/Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:332`. The file is 490 lines, so the fix requires a split to stay under 500.
+ - Candidates to triage:
+ - `BreadcrumbSelectorToggleUiBoundaryTests.cs:75`
+ - `BreadcrumbPopupControlDispatchTests.cs:29,111`
+ - `BreadcrumbPopupBoundaryCoverageTests.cs:58`
+ - `BreadcrumbPopupBoundaryCoverageTests.Part2.cs:192`
+ - `BreadcrumbUiThreadDispatchTests.cs:90,301`
+ - `Task.Run(() => tcs.SetResult(...))` calls that only complete a task are not affected.
+2. **#906:** `UtilitiesCS.Test/HelperClasses/FileInfoWrapper_Tests.cs:56-62` opens the repository's own `TaskMaster.sln`, found by `GetSolutionFile()` at lines 340-352, as a fixture. Resident MSBuild node-reuse workers can hold that file open, so the outcome depends on build history.
+
+## Environment
+- OS/version: Windows 11 Pro 10.0.26200
+- Python version: not applicable (C#, MSTest, net48)
+- Command/flags used: parallel regime `/Settings:TaskMaster.runsettings` (Workers 0, Scope ClassLevel)
+- Data source or fixture: files listed above, `main` at `177b6d78e`
+
+## Steps to Reproduce
+1. Inspect the cited `Task.Run` sites and confirm that each asserts a thread-identity property against the thread it obtained.
+2. Inspect `FileInfoWrapper_Tests.GetSolutionFile()` and confirm it resolves the repository's own solution file.
+
+## Expected Behavior
+- A test that needs a distinct thread uses a dedicated `Thread` that is joined, and asserts inside that thread that it is distinct (for example `CheckAccess() == false`) before exercising the guard. This is the #900 pattern.
+- A file-handle test uses a stream the test owns, supplied through the wrapper's seam or an in-memory stream. It never uses a repository file.
+- **Temporary files are prohibited by the unit-test policy and must not be used.**
+
+## Actual Behavior
+The guard under test can pass without being exercised, and the file-open test can fail or pass depending on MSBuild worker residency.
+
+## Logs / Screenshots
+- [ ] Attached minimal logs or screenshot
+- Snippet: none (static findings, verified present on 2026-09-28)
+
+## Impact / Severity
+- [ ] Blocker
+- [ ] High
+- [x] Medium
+- [ ] Low
+
+## Suspected Cause / Notes
+Both patterns were copied from earlier tests and survived because they usually pass. Tests must run in parallel. Do not fix either defect with `Workers=1`, `[DoNotParallelize]` or retries.
+
+## Proposed Fix / Validation Ideas
+- [ ] Apply the #900 dedicated-thread pattern at every triaged site. Split `ItemViewerBreadcrumbThreadAffinityTests.cs` so it stays at or under 500 lines, and register any new file in `QuickFiler.Test.csproj`.
+- [ ] Replace the `TaskMaster.sln` fixture with a test-owned stream or an injected seam. Add a seam to the wrapper only if one does not already exist.
+- [ ] Each rewritten test must be shown to fail against a deliberately broken guard, so the test demonstrably exercises the guard.
+- [ ] Run the full `QuickFiler.Test` and `UtilitiesCS.Test` suites in the parallel regime.
+
+## Next Step
+- [x] Promote to GitHub issue (bug-report template)
+- [ ] Move to active fix folder / branch
+
+Consolidates: #905, #906.
\ No newline at end of file