From 88696b7f7ed473863e18156977357bc7507c780f Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 07:19:51 -0400 Subject: [PATCH 01/24] wip(950): checkpoint preparation artifacts on quota hold Parent parallel-orchestrator committed the child preparation work in its current state on a coordinator COMMIT NOW order. The commit holds the promoted entry, issue.md, spec.md and the plan file as the child left them. The plan may still be a scaffold. Preflight has not cleared. The parent authored no plan or specification content. Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../issue.md | 66 +++++++++ .../plan.2026-10-01T07-11.md | 44 ++++++ .../spec.md | 128 ++++++++++++++++++ ...filer-tests-depend-on-wall-clock-timing.md | 64 +++++++++ 4 files changed, 302 insertions(+) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/issue.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md create mode 100644 docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/issue.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/issue.md new file mode 100644 index 000000000..5b14ba96b --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/issue.md @@ -0,0 +1,66 @@ +# quickfiler-tests-depend-on-wall-clock-timing (Issue #950) + +- Date captured: 2026-09-30 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/quickfiler-tests-depend-on-wall-clock-timing/ (Issue #950) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #950 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/950 +- Last Updated: 2026-09-30 +- Work Mode: full-bug + +## Summary + +Several QuickFiler.Test tests fail intermittently under load because they wait on real wall-clock time. Seen during parallel run bugs-2026-09-28: +- `QfcDatamodelLivenessTests`, including `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. Two failures stopped #944's P3-T8 gate, and one failure occurred during #929's local run. +- `QfcItemController.UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`, which failed once in CI on an earlier #929 head. + +## Environment + +- OS/version: Windows 11 (local, with concurrent coverage runs) and windows-latest (CI) +- Python version: n/a (C# / MSTest, parallel Workers=0, Scope=ClassLevel) +- Command/flags used: `Invoke-MSTestWithCoverage.ps1`, and the CI MSTest-with-coverage required check +- Data source or fixture: n/a + +## Steps to Reproduce + +1. Run QuickFiler.Test under the parallel regime while the machine is loaded, for example with a second coverage run. +2. Observe intermittent failures in the tests named above. + +## Expected Behavior + +The tests are deterministic regardless of machine load. + +## Actual Behavior + +- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` uses `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` (line ~56) and `.Task.Wait(TimeSpan.FromSeconds(5))` (lines ~103 and ~173). A slow scheduler turns these into assertion failures. +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs:206` fails intermittently. Its cause is not yet established. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: #944 evidence for P3-T8, first run; #929 PR #949 CI history. + +## Impact / Severity + +- [ ] Blocker +- [x] High +- [ ] Medium +- [ ] Low + +## Suspected Cause / Notes + +Real-time bounded waits violate the determinism rules: no wall-clock waits in tests, and use of `FakeTimeProvider` or a controllable scheduler. They hit a required check. The transaction test may be a different root cause. Triage it first, and split it into its own issue if its cause is not timing. + +## Proposed Fix / Validation Ideas + +- [ ] Replace the bounded waits with deterministic completion signals (`TaskCompletionSource` or awaited handles) or an injected `TimeProvider`. +- [ ] Use no retries, `[DoNotParallelize]`, Workers=1 or longer timeouts, and find any raced static state. +- [ ] Negative control: show that each rewritten test fails when the awaited signal is never set. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md new file mode 100644 index 000000000..21bd9dc06 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -0,0 +1,44 @@ +# 2026-09-30-quickfiler-tests-depend-on-wall-clock-timing (Plan) + +- **Issue:** #950 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Last Updated:** 2026-10-01T07-11 +- **Status:** Draft +- **Version:** 0.1 + +**Fail-closed evidence rule:** Include explicit baseline artifact tasks, final-QA artifact tasks, and coverage-comparison tasks for each in-scope language when policy requires coverage. If any required baseline artifact, QA artifact, or coverage-comparison artifact is missing, the audit verdict must be BLOCKED or INCOMPLETE, never PASS. + +**Evidence accounting rule:** Record the expected artifact path or location in each evidence-producing task. Do not mark evidence-backed work complete without the artifact. + + +**Phase 0 — Context & Inputs** +- [ ] [P0-T1] Link approved spec: +- [ ] [P0-T2] Record branch/commit baseline: +- [ ] [P0-T3] List required environment/fixtures/data: + +**Phase 1 — Preparation** +- [ ] [P1-T1] Confirm scope is locked for this fix (no open spec gaps) +- [ ] [P1-T2] Sync workspace to target branch and ensure tooling is available + +**Phase 2 — Regression Test (must fail first)** +- [ ] [P2-T1] [expect-fail] Add a small, deterministic regression test in the standard module file (use `tests/bugs//#950-.py` only if no clear home exists) +- [ ] [P2-T2] [expect-fail] Run the regression to confirm it fails and captures the repro + +**Phase 3 — Minimal Fix** +- [ ] [P3-T1] Apply the smallest change needed to make the regression test pass; avoid opportunistic refactors + +**Phase 4 — Verification Loop** +- [ ] [P4-T1] Re-run repro and regression test to confirm expected behavior +- [ ] [P4-T2] Run formatter → linter → type checker → tests; restart loop if any step changes files or fails +- [ ] [P4-T3] Record baseline, post-change, and comparison artifact paths for each in-scope language where coverage is required + +**Phase 5 — Documentation & Status** +- [ ] [P5-T1] Update spec/issue with outcomes, decisions, and any deviations from scope + +**Phase 6 — PR & Handoff** +- [ ] [P6-T1] Prepare PR notes (summary, risks, validation performed, links to tests) and request review + +**Phase 7 — Rollout / Follow-up** +- [ ] [P7-T1] Capture deployment/rollout notes and post-fix monitoring items +- [ ] [P7-T2] Record links (issue, PRs, related docs) for traceability diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md new file mode 100644 index 000000000..a39ca1f78 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md @@ -0,0 +1,128 @@ +# 2026-09-30-quickfiler-tests-depend-on-wall-clock-timing (Spec) + +- **Issue:** #950 +- **Parent (optional):** none +- **Owner:** drmoisan +- **Last Updated:** 2026-10-01T07-11 +- **Status:** Draft +- **Version:** 0.1 + +## Context +Several QuickFiler.Test tests fail intermittently under load because they wait on real wall-clock time. Seen during parallel run bugs-2026-09-28: +- `QfcDatamodelLivenessTests`, including `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. Two failures stopped #944's P3-T8 gate, and one failure occurred during #929's local run. +- `QfcItemController.UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`, which failed once in CI on an earlier #929 head. + +Environment: +- OS/version: Windows 11 (local, with concurrent coverage runs) and windows-latest (CI) +- Python version: n/a (C# / MSTest, parallel Workers=0, Scope=ClassLevel) +- Command/flags used: `Invoke-MSTestWithCoverage.ps1`, and the CI MSTest-with-coverage required check +- Data source or fixture: n/a + +Impact / Severity: +- [ ] Blocker +- [x] High +- [ ] Medium +- [ ] Low + + +## Repro & Evidence +Steps to Reproduce: +1. Run QuickFiler.Test under the parallel regime while the machine is loaded, for example with a second coverage run. +2. Observe intermittent failures in the tests named above. + +Expected: +The tests are deterministic regardless of machine load. + +Actual: +- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` uses `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` (line ~56) and `.Task.Wait(TimeSpan.FromSeconds(5))` (lines ~103 and ~173). A slow scheduler turns these into assertion failures. +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs:206` fails intermittently. Its cause is not yet established. + +Logs / Screenshots: +- [ ] Attached minimal logs or screenshot +- Snippet: #944 evidence for P3-T8, first run; #929 PR #949 CI history. + + +## Scope & Non-Goals +- In scope: +- Out of scope / non-goals: +- Explicitly excluded systems, integrations, or datasets: + +## Root Cause Analysis +Real-time bounded waits violate the determinism rules: no wall-clock waits in tests, and use of `FakeTimeProvider` or a controllable scheduler. They hit a required check. The transaction test may be a different root cause. Triage it first, and split it into its own issue if its cause is not timing. + + +## Proposed Fix + +### Design summary (what changes where): + +### Boundaries and invariants to preserve: + +### Dependencies or blocked work: + +### Implementation strategy (what changes, not sequencing): + +#### Files/modules to change: + +#### Functions/classes/CLI commands impacted: + +#### Data flow and validation changes: + +#### Error handling and logging updates: + +#### Rollback/feature-flag considerations (if applicable): + +### Technical specifications (interfaces/contracts): + +#### Inputs/outputs and formats: + +#### Required configuration keys and defaults: + +#### Backward-compatibility expectations: + +#### Performance constraints (latency/throughput/memory): + +## Assumptions, Constraints, Dependencies +- Assumptions (environment, data, access): +- Constraints (budget, performance, compatibility): +- External dependencies (services, libraries, releases): + +## Data / API / Config Impact +- User-facing or API changes: +- Data or migration considerations: +- Logging/telemetry updates (if any): +- Compatibility notes (CLI flags, config schemas, versioning): + +## Test Strategy +Seeded from issue: + +- [ ] Replace the bounded waits with deterministic completion signals (`TaskCompletionSource` or awaited handles) or an injected `TimeProvider`. +- [ ] Use no retries, `[DoNotParallelize]`, Workers=1 or longer timeouts, and find any raced static state. +- [ ] Negative control: show that each rewritten test fails when the awaited signal is never set. + +- Regression tests to add or update: +- Unit tests (pytest) for the fixed behavior and boundaries: +- Edge cases and negative scenarios (invalid inputs, missing data, boundary values): +- Error handling and logging verification: +- Coverage impact and targets for changed lines/modules: +- Toolchain commands to run (format → lint → type-check → test): +- Manual validation steps (if required): + + +## Acceptance Criteria +- [ ] Repro steps now produce the expected behavior in all documented environments. +- [ ] Regression test(s) added and passing (list file path and test name). +- [ ] Edge cases and invalid inputs are handled with correct errors or fallbacks. +- [ ] No unintended behavior changes outside the defined scope. +- [ ] Required logs/telemetry updated and validated (if applicable). +- [ ] Performance constraints met or explicitly waived with rationale. +- [ ] Full toolchain pass completed (format → lint → type-check → test). +- [ ] Docs/config references updated to match the new behavior. + +## Risks & Mitigations +- Technical or operational risks: +- Mitigations and rollbacks: + +## Rollout & Follow-up +- Release/rollout steps: +- Post-fix monitoring or clean-up tasks: +- Links: issue, PRs, related docs diff --git a/docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md b/docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md new file mode 100644 index 000000000..a9da5f04e --- /dev/null +++ b/docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md @@ -0,0 +1,64 @@ +# quickfiler-tests-depend-on-wall-clock-timing (Issue #950) + +- Date captured: 2026-09-30 +- Author: Dan Moisan +- Status: Promoted -> docs/features/active/quickfiler-tests-depend-on-wall-clock-timing/ (Issue #950) + +> Automation note: Keep the section headings below unchanged; the promotion tooling maps each of them into the GitHub bug issue template. + +- Issue: #950 +- Issue URL: https://github.com/drmoisan/TaskMaster/issues/950 +- Last Updated: 2026-09-30 +## Summary + +Several QuickFiler.Test tests fail intermittently under load because they wait on real wall-clock time. Seen during parallel run bugs-2026-09-28: +- `QfcDatamodelLivenessTests`, including `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. Two failures stopped #944's P3-T8 gate, and one failure occurred during #929's local run. +- `QfcItemController.UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`, which failed once in CI on an earlier #929 head. + +## Environment + +- OS/version: Windows 11 (local, with concurrent coverage runs) and windows-latest (CI) +- Python version: n/a (C# / MSTest, parallel Workers=0, Scope=ClassLevel) +- Command/flags used: `Invoke-MSTestWithCoverage.ps1`, and the CI MSTest-with-coverage required check +- Data source or fixture: n/a + +## Steps to Reproduce + +1. Run QuickFiler.Test under the parallel regime while the machine is loaded, for example with a second coverage run. +2. Observe intermittent failures in the tests named above. + +## Expected Behavior + +The tests are deterministic regardless of machine load. + +## Actual Behavior + +- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` uses `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` (line ~56) and `.Task.Wait(TimeSpan.FromSeconds(5))` (lines ~103 and ~173). A slow scheduler turns these into assertion failures. +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs:206` fails intermittently. Its cause is not yet established. + +## Logs / Screenshots + +- [ ] Attached minimal logs or screenshot +- Snippet: #944 evidence for P3-T8, first run; #929 PR #949 CI history. + +## Impact / Severity + +- [ ] Blocker +- [x] High +- [ ] Medium +- [ ] Low + +## Suspected Cause / Notes + +Real-time bounded waits violate the determinism rules: no wall-clock waits in tests, and use of `FakeTimeProvider` or a controllable scheduler. They hit a required check. The transaction test may be a different root cause. Triage it first, and split it into its own issue if its cause is not timing. + +## Proposed Fix / Validation Ideas + +- [ ] Replace the bounded waits with deterministic completion signals (`TaskCompletionSource` or awaited handles) or an injected `TimeProvider`. +- [ ] Use no retries, `[DoNotParallelize]`, Workers=1 or longer timeouts, and find any raced static state. +- [ ] Negative control: show that each rewritten test fails when the awaited signal is never set. + +## Next Step + +- [x] Promote to GitHub issue (bug-report template) +- [ ] Move to active fix folder / branch From dd6acfbc2a146aaac070134328189d5f278a32bd Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 07:24:31 -0400 Subject: [PATCH 02/24] wip(950): add incomplete root-cause research on quota hold Research artifact stopped early for quota. Establishes defect A (BackgroundWorker pool-thread start forces wall-clock waits) and defect B (gate-free EnsureDispatcher seeding races the transaction test across parallel classes). Spec, plan and preflight remain. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NcieP6KJzhzgHkRh4F21Po --- ...6-10-01T00-00-wall-clock-waits-research.md | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md new file mode 100644 index 000000000..18c9d4b69 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md @@ -0,0 +1,163 @@ +# Research: QuickFiler.Test wall-clock waits and the R4 transaction flake (Issue #950) + +> **INCOMPLETE: stopped for quota.** The coordinator ended the session before every item in the delegation was closed. Findings below are verified by reading the named files in the item worktree unless marked `[UNVERIFIED]`. The "Not completed" section at the end lists what remains. + +- Issue: #950 — Bug: quickfiler-tests-depend-on-wall-clock-timing +- Branch: `bug/quickfiler-tests-depend-on-wall-clock-timing-950` (based on origin/main `9b3eea58`) +- Date: 2026-10-01 +- Tooling note: the Bash tool was disabled in this session, so `git show`/`git diff` against CI head `b9692658` could not be run. The CI failure text quoted in the delegation matches the current file verbatim (assertion at `QfcItemController.UiThreadDispatcherFixtureTests.cs:244-250`, test declared at `:206`), so the current worktree copy is treated as the text under analysis. + +--- + +## 1. Constraints the fix must honor (recorded) + +| Constraint | Source | +|---|---| +| No `Thread.Sleep`, `Task.Delay`, real wall-clock waits in tests; use `FakeTimeProvider` / controllable scheduler | `.claude/rules/general-unit-test.md` "Determinism Infrastructure"; `BannedSymbols.txt` via `.claude/rules/csharp.md` | +| Tests keep running in parallel: `Workers=0`, `Scope=ClassLevel` | `scripts/vscode/TaskMaster.cli.runsettings:4-7` (used by `Invoke-MSTestWithCoverage.ps1:33,89`, which CI runs verbatim per `.github/workflows/_mstest-coverage.yml:83`); `TaskMaster.runsettings:4-7` carries the same values | +| Fix must never be `Workers=1`, `[DoNotParallelize]`, retries, or longer timeouts | issue.md "Proposed Fix"; user memory `feedback_tests_must_run_parallel_serial_masks_isolation_violation` | +| No temporary files in tests | CLAUDE.md UT4 | +| MSTest 4.4.1 + Moq 4.21.0 + FluentAssertions 8.11.0 | `QuickFiler.Test/packages.config:8,42-45` | +| net481: no `init`, no `record` | user memory `reference_net48_no_init_record_struct` | +| 500-line file cap | CLAUDE.md §4 | +| `QfcDatamodel` is COM-bound and carries a type-level `[ExcludeFromCodeCoverage]` (`QuickFiler/Controllers/QfcDatamodel.cs:25`), so any production seam added there is coverage-exempt and must stay minimal | verified | +| CI parallelism: `_mstest-coverage.yml` runs `Invoke-MSTestWithCoverage.ps1`, which passes `/Settings:TaskMaster.cli.runsettings /InIsolation /TestCaseFilter:TestCategory!=LiveOutlook` (`:89-91`). CI therefore runs ClassLevel-parallel, which supersedes the older memory note that CI ran sequentially. | verified | + +--- + +## 2. Defect A — `QfcDatamodelLivenessTests.cs` (255 lines) + +### 2.1 Every wall-clock wait in the file + +| # | Line | Call | Reached from | Event awaited | Producer (production code) | +|---|---|---|---|---|---| +| A1 | 56 | `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` inside `WaitForState` | 106-110 (test 1), 176-179 (helper used by tests 2-4), 223-226 (test 3), 249-252 (test 4) | 106/176: `!worker.IsBusy`; 223/249: `_remainingLoadActive == false` | `IsBusy` clears when `BackgroundWorker.WorkerThreadStart` finishes and posts `AsyncOperationCompleted` (framework, posted to the captured/default SynchronizationContext — not synchronously observable). The flag clears in the `finally` at `QfcDatamodel.cs:209-216` after `await loaderTask` (`:207`) resumes. | +| A2 | 103 | `loaderEntered.Task.Wait(TimeSpan.FromSeconds(5))` | test 1 | test-owned TCS set inside the injected `RemainingEmailLoader` | `Worker_DoWork` (`QfcDatamodel.cs:185-229`) calls `RemainingEmailLoader(_token)` at `:205`; it runs on the BackgroundWorker thread because `InitEmailQueue(0, worker)` (`:259-275`) calls `worker.RunWorkerAsync()` at `:273`. | +| A3 | 173 | `entered.Task.Wait(TimeSpan.FromSeconds(5))` | `StartHeldOpenLoader` (tests 2, 3, 4) | same as A2 | same as A2 | + +Non-wall-clock but scheduling-dependent residual in the same file (not in the issue's scope, recorded for completeness): test 1 lines 113-116 and 128-132 use `fake.Advance(...)` + `await Task.Yield()` loops (bounded to 20 iterations). They do not read a clock, but they rely on thread-pool scheduling of the gate's continuation between yields. Left as-is in the recommendation; see §2.4. + +### 2.2 Why the waits exist + +All three waits exist because `InitEmailQueue` starts the worker through `BackgroundWorker.RunWorkerAsync()` (`QfcDatamodel.cs:273`, `:300`), which runs `Worker_DoWork` on a thread-pool thread via delegate `BeginInvoke`. Nothing in the test controls that thread, so the test can only poll for the effects. There is no production seam for the *start* of the worker; the only existing seam is the worker *body* (`RemainingEmailLoader`, `QfcDatamodel.cs:140`, assigned in both constructors `:40`, `:51`, `null` on `GetUninitializedObject` instances). + +### 2.3 Recommended replacement (one approach) + +**Drive `Worker_DoWork` synchronously on the test thread under a test-owned, drainable `SynchronizationContext`; replace every wait with a synchronous assertion.** + +Mechanics (verified from the production code and the .NET await contract): + +1. `Worker_DoWork` is `async void`. Invoked synchronously it runs to its first incomplete await (`:207`, `await loaderTask` with no `ConfigureAwait(false)`) and returns. At that point `RemainingEmailLoader` has already been called (`:205`), `_remainingLoadTask` is set (`:206`), and `_remainingLoadActive` is whatever `InitEmailQueue` set (`true`, `:272`). So "entered" (A2/A3) becomes `entered.Task.IsCompleted.Should().BeTrue()` immediately after the call, and "flag stays true across the async-void boundary" (test 2) becomes a synchronous read. No wait. +2. The continuation of `await loaderTask` captures `SynchronizationContext.Current` at the await point. If the test installs a queueing context before invoking `Worker_DoWork`, then after `release.SetResult(true)` the continuation (the `finally` that clears the flag) is either posted to that queue or inlined; either way, after the test drains the queue the `finally` has run and `ReadLivenessFlag(model)` is `false` synchronously. Tests 3 and 4 (A1 at 223 and 249) become: `release.SetResult(true); pump.Drain(); ReadLivenessFlag(model).Should().BeFalse(...)`. For test 4 the throwing lambda's own continuation is drained the same way, which faults `loaderTask` and resumes `Worker_DoWork` into the same `finally` and then the `catch` at `:225-228` (log4net `logger.Error`, no-op without configuration). +3. Negative control (how the rewritten tests FAIL instead of hanging): if `release` is never set, `pump.Drain()` runs zero callbacks and `ReadLivenessFlag(model).Should().BeFalse()` fails immediately. If the production `finally` were removed, the same assertion fails immediately. If `Worker_DoWork` never reached the loader, `entered.Task.IsCompleted.Should().BeTrue()` fails immediately. No path blocks on anything with an unbounded wait: `Drain` loops only over already-queued callbacks. +4. The `!worker.IsBusy` waits (A1 at 106 and 176) disappear: the worker is never started asynchronously, so `IsBusy` is trivially `false`, and the gate never consulted `IsBusy` anyway (`_worker` has no `IsBusy` reader; its only uses are `QfcDatamodel.cs:78,100,118,261,316`). The #424 claim test 1 proves — the dequeue keeps polling on the flag, not on `IsBusy` — is preserved because `DequeueWithHighConfidenceGateWithOutcomeAsync` reads `() => _remainingLoadActive` (`QfcDatamodel.QueueProcessing.cs:305`) and `WaitForQueue` reads the flag at `:406`. + +How to invoke `Worker_DoWork` synchronously (two sub-options; pick one in the plan): + +- **D1 (recommended): minimal production seam for the worker start.** Add `internal Action WorkerStarter { get; set; }` to `QfcDatamodel`, assigned in both constructors (`:41`, `:52`) to `worker => worker.RunWorkerAsync()`, and replace the two `worker.RunWorkerAsync()` calls (`:273`, `:300`) with `WorkerStarter(worker)`. This mirrors the existing `RemainingEmailLoader` convention exactly (constructor-assigned, `null` on uninitialized instances, test-assigned). The test assigns `model.WorkerStarter = w => ((SynchronousBackgroundWorker)w).RaiseDoWork();` where `SynchronousBackgroundWorker : BackgroundWorker` is a test-side subclass exposing the `protected virtual OnDoWork(DoWorkEventArgs)` method (which raises the `DoWork` event synchronously on the calling thread and thereby invokes the privately-subscribed `Worker_DoWork` without reflection). `InitEmailQueue(0, worker)` then runs fully synchronously, keeping lines 267-274 under test. Production file growth: ~8 lines; `QfcDatamodel.cs` is 483 lines (cap 500). +- **D2 (no production change):** call the public `SetupWorker(worker)` (`:176-183`), set `_remainingLoadActive = true` via the file's existing `SetPrivateField`, then `RaiseDoWork()`. This skips `InitEmailQueue`'s flag-set lines in this file (they remain exercised by `QfcInitEmailQueueZeroBatchTests`), and no longer proves `InitEmailQueue` sets the flag before start. Rejected in favor of D1 because D1 keeps the tests' stated #424 claim intact. + +Pump helper reuse: QuickFiler.Test already contains several private nested drainable contexts — `DrainableSynchronizationContext` (`Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:246-265` and `Viewers/ItemViewerBreadcrumbLifecycleRegressionTests.cs:339`), `PumpSynchronizationContext` (`Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:299-358`, `Viewers/BreadcrumbDropDownReadinessTests.cs:420`), `QueuedCreatorThreadSynchronizationContext` (two files). None is shared; the assembly's shared folder is `QuickFiler.Test/TestSupport/` (`DedicatedWorkerThread.cs`, `WinFormsPumpHost.cs`). The `DrainableSynchronizationContext` shape (queue on `Post`, `Drain()` loops until empty, asserts creator thread) is the right one here: it never blocks. Either duplicate it privately in `QfcDatamodelLivenessTests.cs` (the file's own doc comment at `:19-23` records a duplication convention) or promote one copy to `TestSupport/` (new file requires a `` in `QuickFiler.Test.csproj`, which lists every file explicitly, e.g. `:157`, `:201`, `:203`). `FakeTimeProvider` (`Microsoft.Extensions.TimeProvider.Testing 10.10.0`, `packages.config:31`) is already referenced and already used by test 1; no new package. + +`[UNVERIFIED]` assumption to confirm during execution: that the MSTest 4.4.1 worker thread has no ambient `SynchronizationContext` that would intercept the continuation. The pump design is robust either way because the test installs its own context around the invocation and restores the previous one (precedent: `ViewerScope` at `ItemViewerBreadcrumbThreadAffinityTests.cs:271-292`). + +### 2.4 Out-of-scope residual wall-clock waits elsewhere in QuickFiler.Test (candidates, not in #950's two files) + +| File:line | Call | Note | +|---|---|---| +| `Controllers/QfcDatamodelTeardownTests.cs:67` | `SpinWait.SpinUntil(..., 5 s)` (`WaitForState`, used at `:225`) | same mechanism as A1; same D1 fix applies | +| `Controllers/QfcDatamodelTeardownTests.cs:220` | `loaderEntered.Task.Wait(5 s)` | same as A2 | +| `Controllers/QfcInitEmailQueueZeroBatchTests.cs:161` | `loaderInvokedTcs.Task.Wait(5 s)` | same as A2 | +| `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:397` | `_available.AvailableWaitHandle.WaitOne()` (unbounded) | not timed, but an unbounded block; hang risk rather than wall-clock bound | +| `Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:345`, `BreadcrumbSelectorToggleUiBoundaryTests.cs:419`, `BreadcrumbUiThreadDispatchTests.cs:410`, `BreadcrumbCoordinatorLifecycleTests.cs:57` | `Wait(0)` / `WaitOne(0)` | zero-bound probes; non-blocking, not wall-clock | +| `Controllers/QfcFormControllerCleanupTests.cs:380` | string literals in a banned-literal scan | not a wait | + +No `Thread.Sleep` or `Task.Delay` call exists in QuickFiler.Test (only comment mentions at `KaKeyTests.cs:104`, `KaCharTests.cs:113`, `QfcCollectionControllerDefects468Tests.cs:349-350`, `QfcDatamodelTests.cs:215`). + +--- + +## 3. Defect B — `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` (file: 458 lines) + +### 3.1 What `observedByB` reads + +`observedByB = UiThreadDispatcherFixture.Current` (`:230`) → `(Dispatcher)DispatcherField.GetValue(null)` under `FieldLock` (`QfcItemController.UiThreadDispatcherFixture.cs:62-71`), where `DispatcherField` is the private static `UtilitiesCS.UiThread._dispatcher` (`UtilitiesCS/Threading/UiThread.cs:285`; resolved at fixture `:197-205`). It is a plain process-wide static field — not `[ThreadStatic]`, not `AsyncLocal`, not `Dispatcher.CurrentDispatcher`. + +`original = UiThreadDispatcherFixture.Current` is read at `:215` in a separate lock acquisition from `transactionA.Install(liveA)` at `:216` (`Exchange`, fixture `:77-85`). + +### 3.2 The failure signature decoded + +FluentAssertions message: "Expected observedByB to refer to `` … but found … Dispatcher { Thread = … Name = "UiThreadDispatcherFixture.ParkedDispatcher" }". Therefore in the failing run `original == null` and `observedByB` was the fixture's singleton parked dispatcher (`_parkedDispatcher`, created only at fixture `:213-241` with that exact thread name at `:231`). + +### 3.3 Every writer of `UiThread._dispatcher` in the repository + +| # | Writer | Value written | Gate-aware? | Reachable from QuickFiler.Test concurrently? | +|---|---|---|---|---| +| W1 | `UiThreadDispatcherFixture.EnsureDispatcher()` — `QfcItemController.UiThreadDispatcherFixture.cs:132` | parked singleton, only when the field is `null` | **No** — by design never takes `TransactionGate` (`:26-30`, `:116-121`) | Yes: via `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` (`QfcItemController.TestSupport.cs:238-239`) from **`QfcItemController_FocusAndThemeTests.SetThemeDark_FromNormal_SelectsDarkNormalTheme` (`QfcItemController.FocusAndThemeTests.cs:452`) and `SetThemeLight_FromNormal_SelectsLightNormalTheme` (`:468`)** — both discard the returned scope, so they never revert; and from R1/R2/R3 in the same class as R4 (`:60`, `:119`, `:166`), which run serially with R4 and do revert. | +| W2 | `EnsureScope.Dispose()` — fixture `:271` via `CompareExchange(_installed, null)` | `null`, only when the field still holds the parked instance that scope installed | No | Only same-class R2/R3 dispose scopes today; no other class disposes one. | +| W3 | `UiThreadDispatcherTransaction.Install` — fixture `:316` (`Exchange`) | arbitrary | Yes (holder of `TransactionGate`) | callers: FixtureTests `:56,116,165,216,282,331`; `WpfUiDispatcherTests.cs:63`; `QfcFormControllerUndoHandoffTests.cs:236,287,343`; `QfcItemController.InitializationTests.Part2.cs:132`; `QfcHomeControllerRunAsyncTests.cs:355` — all gated. | +| W4 | `UiThreadDispatcherTransaction.Dispose` — fixture `:336` (`CompareExchange(_installedValue, _previous)`) | captured previous | Yes | gated | +| W5 | `UiThread.Initialize()` — `UiThread.cs:82` (`Dispatcher = _syncContextForm.UiDispatcher`), reached from `UiThread.Init()` `:57`, which the lazy getters `UiSyncContext` (`:224`) and `AutoScaleFactor` (`:298`) call when their fields are null; requires an STA caller (`:30-34`) | the `SyncContextForm`'s dispatcher (thread name differs from "ParkedDispatcher") | No | Not observed in this failure (wrong thread name). No QuickFiler.Test call to `UiThread.Init(` was found (only a commented one at `QfcHomeControllerTests.cs:240`); production reads of `UiThread.Dispatcher` (e.g. `ItemViewerQueue.cs:21,27`, `EfcViewerQueue.cs:20`) go through the throwing getter at `:266-284`, which never writes. `[UNVERIFIED]` whether any QuickFiler.Test path reads `UiThread.UiSyncContext`/`AutoScaleFactor` on an STA thread with `_initialized == false`. | +| W6 | `UiThread.ResetForTesting()` — `UiThread.cs:126` | `null` | No | Called only from `UtilitiesCS.Test/TestHelpers/UiThreadStateScope.cs:89` (different assembly). | +| W7 | `UtilitiesCS.Test/TestHelpers/UiThreadDispatcherScope.cs:78,109` and `UiThreadStateScope.cs:162,183` | their own values | No | Different assembly; those install `Dispatcher.CurrentDispatcher` of their own threads, never the QuickFiler.Test parked instance. `[UNVERIFIED]` whether vstest hosts UtilitiesCS.Test and QuickFiler.Test in the same process concurrently under the CI command. | + +Observed value = parked singleton, prior value = `null`. Only **W1** writes the parked instance, and only into a `null` field. W1 is reachable concurrently from exactly one other class: `QfcItemController_FocusAndThemeTests`. + +### 3.4 Established root cause (one) + +**Raced process-wide static state across parallel test classes, not timing inside the test.** `UiThreadDispatcherFixture.EnsureDispatcher()` is gate-free by design and seeds the parked dispatcher whenever it observes `UiThread._dispatcher == null`. Under `Workers=0 / ClassLevel`, `QfcItemController_FocusAndThemeTests` (two tests calling `EnsureUiThreadDispatcher()` and discarding the scope) can run concurrently with R4. R4 begins with a `null` baseline (`original == null`), and the seeding can land in either of two windows the test leaves open: + +- Window 1: between the `original` read (`:215`) and `Install(liveA)` (`:216`) — then `_previous` (parked) differs from `original` (`null`), `Dispose` restores parked, and B observes parked. +- Window 2: between `transactionA.Dispose()`'s `CompareExchange(liveA, null)` (`:240` → fixture `:336`) and B's `Current` read (`:230`), which runs on a thread-pool continuation after `WaitAsync` completes — under load this gap is long enough for another worker's `EnsureDispatcher` to see `null` and write parked. + +Either window produces exactly the observed message. The `NotBeSameAs(liveA)` assertion (`:251-257`, the real #230 lost-update check) was not what failed; the over-strong `BeSameAs(original)` assertion (`:244-250`) encodes a property the fixture does not guarantee when the baseline is `null`. The 2-second duration and the 60 s `[Timeout]` are not involved (no bound expired; the failure is an assertion). + +Discriminating observation (not needed for the conclusion, but confirms it): in the CI TRX for run 36722780748, `QfcItemController_FocusAndThemeTests.SetThemeDark_FromNormal_SelectsDarkNormalTheme` or `SetThemeLight_FromNormal_SelectsLightNormalTheme` has a `startTime`/`endTime` window overlapping the failing test's window. (Not fetched; the artifact requires authenticated access.) + +Historical corroboration: the #823 flake-watch log (`docs/features/active/2026-09-08-quickfiler-teardown-review-residuals-823/evidence/other/flake-watch-uithread-dispatcher-transaction.2026-09-09T00-15.md`) records 1 failure in 4 runs, all under `Workers 0 / ClassLevel`, with no failure text captured — consistent with a class-interleaving race. + +### 3.5 Recommended fix (test-only; keep in #950, no split needed) + +Pin a non-null baseline for the whole R4 body so W1 cannot write: wrap the test body in `using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher())` before `BeginTransactionAsync`. With the field non-null, `EnsureDispatcher` from any class installs nothing (fixture `:130-137`), closing both windows; `original` then equals the parked (or pre-existing) value and B observes it. Keep `NotBeSameAs(liveA)`. Residual exposure: a foreign `EnsureScope.Dispose()` holding the same parked singleton (W2) could null the field during the test; no such disposer exists today (`FocusAndThemeTests` discards its scopes), and `CompareExchange` compares identity against the singleton, so this must be recorded as an invariant in the R4 doc comment. Update the R4 `` (`:196-202`) to replace the #823 flake-watch instruction with the established cause and #950. + +Negative control for B: under a fixture that releases the gate before restoring (the #230 defect), B observes `liveA` and `NotBeSameAs(liveA)` fails — probabilistically, as the class doc at `:14-22` already records; this research does not change that limitation. + +Alternatives rejected: (i) making `EnsureDispatcher` take the gate — rejected by the fixture's own design note (`:26-30`; callers without `[Timeout]` would hang unboundedly); (ii) exposing `UiThreadDispatcherTransaction._previous` and asserting against it — closes window 1 only; (iii) weakening the assertion to "original or parked" — asserts a disjunction instead of a contract. + +--- + +## 4. Files a fix would touch + +| File | Kind | Lines now | csproj | Change | +|---|---|---|---|---| +| `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` | test | 255 | `QuickFiler.Test.csproj:157` (explicit ``) | remove A1-A3; synchronous driving via D1; pump helper (private or shared) | +| `QuickFiler/Controllers/QfcDatamodel.cs` | production (COM-bound, `[ExcludeFromCodeCoverage]` at `:25`) | 483 | `QuickFiler.csproj` (explicit items, not re-verified here `[UNVERIFIED]`) | D1 seam: `WorkerStarter` property + 2 ctor assignments + 2 call sites (`:273`, `:300`) | +| `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` | test | 458 | `QuickFiler.Test.csproj:203` | R4 baseline scope + doc comment | +| optional `QuickFiler.Test/TestSupport/.cs` | test support (new) | — | needs a new `` in `QuickFiler.Test.csproj` | only if the pump is shared rather than duplicated | +| optional `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs`, `QfcInitEmailQueueZeroBatchTests.cs` | test | not counted | existing | same D1 rewrite if the plan widens scope to the residuals in §2.4 | + +Acceptance-gate test names (fully qualified): +- `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` +- `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` +- `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` +- `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` +- `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` +- Concurrent-writer class for B: `QuickFiler.Controllers.Tests.QfcItemController_FocusAndThemeTests` (`SetThemeDark_FromNormal_SelectsDarkNormalTheme`, `SetThemeLight_FromNormal_SelectsLightNormalTheme`) + +--- + +## 5. Testing implications + +- A: each rewritten test gets an explicit negative-control step in the plan (run once with `release` never set, or with the production `finally` commented out locally) and must FAIL immediately, not hang. A regression test for the B race cannot rely on MSTest scheduling two classes concurrently (memory `uithread-dispatcher-restore-scope-493`, finding 4); if a regression test is wanted, it must create its own thread calling `EnsureDispatcher()` between A's dispose and B's read on the pre-fix test shape — otherwise rely on the code trace plus the baseline pin. +- No `[DoNotParallelize]`, no `Workers=1`, no retries, no timeout changes. + +## 6. Automation Feasibility + +No human interaction is required. All steps (edit test/production files, run the four-step toolchain, run the named tests by fully qualified name with `/Settings:scripts/vscode/TaskMaster.cli.runsettings`) are scriptable. No live Outlook, no UI pump host, no manual gate. + +## 7. Not completed (stopped for quota) + +1. `git show`/`git diff` of CI head `b9692658` vs `origin/main` for the four fixture/test files (Bash disabled; assumed unchanged at the assertion site from the verbatim message match). +2. Confirmation that `QuickFiler.csproj` uses explicit `` items (expected, legacy csproj). +3. Verification of W5 reachability (`UiThread.UiSyncContext`/`AutoScaleFactor` getters from QuickFiler.Test on an STA thread) and of whether UtilitiesCS.Test shares the test host process under the CI command (W7). +4. Confirmation that the MSTest 4.4.1 worker thread has no ambient SynchronizationContext (pump design is robust regardless). +5. Numeric derivation evidence section: no numeric acceptance criterion is proposed, so none is required. From a4d89a8f4b4d5c3ac6bb9aaeca2dc274924986ef Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 21:56:00 -0400 Subject: [PATCH 03/24] docs(950): close research section 7 and extend to teardown and zero-batch tests --- ...6-10-01T00-00-wall-clock-waits-research.md | 64 ++++++++++++++----- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md index 18c9d4b69..afd8caa53 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md @@ -1,11 +1,11 @@ # Research: QuickFiler.Test wall-clock waits and the R4 transaction flake (Issue #950) -> **INCOMPLETE: stopped for quota.** The coordinator ended the session before every item in the delegation was closed. Findings below are verified by reading the named files in the item worktree unless marked `[UNVERIFIED]`. The "Not completed" section at the end lists what remains. +> **Status: complete.** The first pass stopped for quota with the items in section 7 open; the orchestrator closed them on 2026-10-01 by direct reads in the item worktree. Section 7 now records how each item was closed. Findings are verified by reading the named files unless marked `[UNVERIFIED]` or recorded as an execution-time assumption. - Issue: #950 — Bug: quickfiler-tests-depend-on-wall-clock-timing - Branch: `bug/quickfiler-tests-depend-on-wall-clock-timing-950` (based on origin/main `9b3eea58`) - Date: 2026-10-01 -- Tooling note: the Bash tool was disabled in this session, so `git show`/`git diff` against CI head `b9692658` could not be run. The CI failure text quoted in the delegation matches the current file verbatim (assertion at `QfcItemController.UiThreadDispatcherFixtureTests.cs:244-250`, test declared at `:206`), so the current worktree copy is treated as the text under analysis. +- Tooling note: the Bash tool was disabled in this session, so `git show`/`git diff` against CI head `b9692658` could not be run. The CI failure text quoted in the delegation matches the current file verbatim (assertion at `QfcItemController.UiThreadDispatcherFixtureTests.cs:244-250`, test declared at `:206`), so the current worktree copy is treated as the text under analysis. Closure (section 7, item 1): `git diff --stat b9692658 HEAD -- QuickFiler.Test/Controllers QuickFiler.Test/TestSupport QuickFiler/Controllers/QfcDatamodel.cs UtilitiesCS/Threading/UiThread.cs` printed nothing, so every target file is byte-identical between CI head `b9692658` and the branch head. --- @@ -59,21 +59,49 @@ How to invoke `Worker_DoWork` synchronously (two sub-options; pick one in the pl Pump helper reuse: QuickFiler.Test already contains several private nested drainable contexts — `DrainableSynchronizationContext` (`Viewers/ItemViewerBreadcrumbThreadAffinityTests.cs:246-265` and `Viewers/ItemViewerBreadcrumbLifecycleRegressionTests.cs:339`), `PumpSynchronizationContext` (`Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:299-358`, `Viewers/BreadcrumbDropDownReadinessTests.cs:420`), `QueuedCreatorThreadSynchronizationContext` (two files). None is shared; the assembly's shared folder is `QuickFiler.Test/TestSupport/` (`DedicatedWorkerThread.cs`, `WinFormsPumpHost.cs`). The `DrainableSynchronizationContext` shape (queue on `Post`, `Drain()` loops until empty, asserts creator thread) is the right one here: it never blocks. Either duplicate it privately in `QfcDatamodelLivenessTests.cs` (the file's own doc comment at `:19-23` records a duplication convention) or promote one copy to `TestSupport/` (new file requires a `` in `QuickFiler.Test.csproj`, which lists every file explicitly, e.g. `:157`, `:201`, `:203`). `FakeTimeProvider` (`Microsoft.Extensions.TimeProvider.Testing 10.10.0`, `packages.config:31`) is already referenced and already used by test 1; no new package. -`[UNVERIFIED]` assumption to confirm during execution: that the MSTest 4.4.1 worker thread has no ambient `SynchronizationContext` that would intercept the continuation. The pump design is robust either way because the test installs its own context around the invocation and restores the previous one (precedent: `ViewerScope` at `ItemViewerBreadcrumbThreadAffinityTests.cs:271-292`). +Execution-time assumption (section 7, item 4; not statically decidable from the repository): the MSTest 4.4.1 worker thread has no ambient `SynchronizationContext` that would intercept the continuation. The plan must record the observed `SynchronizationContext.Current` value on the test thread once during execution. The pump design is robust either way because the test installs its own context around the invocation and restores the previous one (precedent: `ViewerScope` at `ItemViewerBreadcrumbThreadAffinityTests.cs:271-292`). -### 2.4 Out-of-scope residual wall-clock waits elsewhere in QuickFiler.Test (candidates, not in #950's two files) +### 2.4 Other wall-clock waits in QuickFiler.Test + +The first three rows are now IN SCOPE by maintainer decision (same root cause, same seam); section 2.5 analyses them. The remaining rows stay out of scope. | File:line | Call | Note | |---|---|---| -| `Controllers/QfcDatamodelTeardownTests.cs:67` | `SpinWait.SpinUntil(..., 5 s)` (`WaitForState`, used at `:225`) | same mechanism as A1; same D1 fix applies | -| `Controllers/QfcDatamodelTeardownTests.cs:220` | `loaderEntered.Task.Wait(5 s)` | same as A2 | -| `Controllers/QfcInitEmailQueueZeroBatchTests.cs:161` | `loaderInvokedTcs.Task.Wait(5 s)` | same as A2 | +| `Controllers/QfcDatamodelTeardownTests.cs:67` | `SpinWait.SpinUntil(..., 5 s)` (`WaitForState`, used at `:225`) | IN SCOPE (T1 in 2.5) | +| `Controllers/QfcDatamodelTeardownTests.cs:220` | `loaderEntered.Task.Wait(5 s)` | IN SCOPE (T2 in 2.5) | +| `Controllers/QfcInitEmailQueueZeroBatchTests.cs:161` | `loaderInvokedTcs.Task.Wait(5 s)` | IN SCOPE (Z1 in 2.5) | | `Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs:397` | `_available.AvailableWaitHandle.WaitOne()` (unbounded) | not timed, but an unbounded block; hang risk rather than wall-clock bound | | `Viewers/BreadcrumbPopupBoundaryCoverageTests.cs:345`, `BreadcrumbSelectorToggleUiBoundaryTests.cs:419`, `BreadcrumbUiThreadDispatchTests.cs:410`, `BreadcrumbCoordinatorLifecycleTests.cs:57` | `Wait(0)` / `WaitOne(0)` | zero-bound probes; non-blocking, not wall-clock | | `Controllers/QfcFormControllerCleanupTests.cs:380` | string literals in a banned-literal scan | not a wait | No `Thread.Sleep` or `Task.Delay` call exists in QuickFiler.Test (only comment mentions at `KaKeyTests.cs:104`, `KaCharTests.cs:113`, `QfcCollectionControllerDefects468Tests.cs:349-350`, `QfcDatamodelTests.cs:215`). +### 2.5 In-scope extension: `QfcDatamodelTeardownTests.cs` (235 lines) and `QfcInitEmailQueueZeroBatchTests.cs` (212 lines) + +Both files start a real `BackgroundWorker` through `QfcDatamodel.InitEmailQueue`, so they share the section 2.2 cause and take the same D1 seam. + +Complete caller inventory of `QfcDatamodel.InitEmailQueue` on a real (non-mock) instance, from `Grep` over `QuickFiler.Test` and `QuickFiler`: + +| Caller | Line | Batch | Worker start reached | Wall-clock wait | +|---|---|---|---|---| +| `QfcDatamodelLivenessTests` test 1 | `:100` | 0 | `QfcDatamodel.cs:273` | A1 (`:106`), A2 (`:103`) | +| `QfcDatamodelLivenessTests.StartHeldOpenLoader` | `:170` | 0 | `:273` | A1 (`:176`), A3 (`:173`) | +| `QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask` | `:218` | 0 | `:273` | T1 (`:225` via `WaitForState` `:67`), T2 (`:220`) | +| `QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` | `:127` | 0 | `:273` | none, but starts an unobserved thread-pool worker | +| `QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` | `:156` | 0 | `:273` | Z1 (`:161`) | +| `QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` | `:201` | 2 | `:300` | none, but starts an unobserved thread-pool worker | +| `QfcHomeController.cs:252` (production) | — | — | via `IQfcDatamodel` | n/a | + +`QfcHomeControllerRunAsyncTests.cs:123,191,218` call `InitEmailQueue` on a `Mock`, which never reaches `QfcDatamodel`; they are unaffected. No other test file reaches either `worker.RunWorkerAsync()` site. Therefore, after the three in-scope files assign the seam, no QuickFiler.Test test starts a `QfcDatamodel` worker on the thread pool. + +Per-test replacement under D1 (test assigns `model.WorkerStarter` to a synchronous starter that raises `DoWork` on the calling thread through a `BackgroundWorker` subclass exposing `OnDoWork`): + +- **T1/T2 (`Worker_DoWork_CapturesRemainingLoadTask`).** `Worker_DoWork` assigns `_remainingLoadTask` at `QfcDatamodel.cs:206`, before its first await at `:207`. Under the synchronous starter, `InitEmailQueue` returns only after that line ran, so `loaderEntered.Task.IsCompleted.Should().BeTrue()` and `GetPrivateField(model, "_remainingLoadTask").Should().NotBeNull()` are both synchronous reads. The file's own doc comment (`:24-25`) already claims "no wall-clock waits"; the rewrite makes that claim true. Negative control: a starter that does not raise `DoWork` leaves both values unset and the assertions fail at once. +- **Z1 (`InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`).** The inert loader (`:97-108`) completes its TCS synchronously when invoked, so under the synchronous starter `loaderInvokedTcs.Task.IsCompleted.Should().BeTrue()` holds as soon as `InitEmailQueue` returns. The doc comment at `:136-145` that justifies the bounded wait must be rewritten. Negative control: a no-op starter leaves the TCS incomplete and the assertion fails at once. +- **Z0/Z2 (`..._ReturnsEmptyListWithoutThrowing`, `..._PositiveBatchSize_...`).** No wait, but each currently starts a real thread-pool worker whose body runs after the test returns. They must assign the same synchronous starter (or a recording starter) so no worker escapes the test. Under D1 a test that leaves `WorkerStarter` unassigned on an uninitialized instance receives `null` and `InitEmailQueue` raises `NullReferenceException` at the start site; this is the same convention `RemainingEmailLoader` already has (`QfcDatamodel.cs:135-140`) and it fails fast rather than silently starting a thread. + +Inline-continuation note for Z1/Z0/Z2 and T1/T2: the inert loader returns `Task.FromResult(true)` (Z-tests), so `await loaderTask` at `:207` completes synchronously and the `finally` at `:209-216` runs before `InitEmailQueue` returns; no pump is needed for those. The T-test loader awaits a test-owned TCS, so its continuation follows the same rules as section 2.3 point 2: the test either drains a test-owned context after `loaderRelease.TrySetResult(true)` or, if it asserts nothing after release, leaves the continuation to run inline on the releasing thread. + --- ## 3. Defect B — `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` (file: 458 lines) @@ -96,9 +124,9 @@ FluentAssertions message: "Expected observedByB to refer to `` … but fou | W2 | `EnsureScope.Dispose()` — fixture `:271` via `CompareExchange(_installed, null)` | `null`, only when the field still holds the parked instance that scope installed | No | Only same-class R2/R3 dispose scopes today; no other class disposes one. | | W3 | `UiThreadDispatcherTransaction.Install` — fixture `:316` (`Exchange`) | arbitrary | Yes (holder of `TransactionGate`) | callers: FixtureTests `:56,116,165,216,282,331`; `WpfUiDispatcherTests.cs:63`; `QfcFormControllerUndoHandoffTests.cs:236,287,343`; `QfcItemController.InitializationTests.Part2.cs:132`; `QfcHomeControllerRunAsyncTests.cs:355` — all gated. | | W4 | `UiThreadDispatcherTransaction.Dispose` — fixture `:336` (`CompareExchange(_installedValue, _previous)`) | captured previous | Yes | gated | -| W5 | `UiThread.Initialize()` — `UiThread.cs:82` (`Dispatcher = _syncContextForm.UiDispatcher`), reached from `UiThread.Init()` `:57`, which the lazy getters `UiSyncContext` (`:224`) and `AutoScaleFactor` (`:298`) call when their fields are null; requires an STA caller (`:30-34`) | the `SyncContextForm`'s dispatcher (thread name differs from "ParkedDispatcher") | No | Not observed in this failure (wrong thread name). No QuickFiler.Test call to `UiThread.Init(` was found (only a commented one at `QfcHomeControllerTests.cs:240`); production reads of `UiThread.Dispatcher` (e.g. `ItemViewerQueue.cs:21,27`, `EfcViewerQueue.cs:20`) go through the throwing getter at `:266-284`, which never writes. `[UNVERIFIED]` whether any QuickFiler.Test path reads `UiThread.UiSyncContext`/`AutoScaleFactor` on an STA thread with `_initialized == false`. | +| W5 | `UiThread.Initialize()` — `UiThread.cs:82` (`Dispatcher = _syncContextForm.UiDispatcher`), reached from `UiThread.Init()` `:57`, which the lazy getters `UiSyncContext` (`:224`) and `AutoScaleFactor` (`:298`) call when their fields are null; requires an STA caller (`:30-34`) | the `SyncContextForm`'s dispatcher (thread name differs from "ParkedDispatcher") | No | Not observed in this failure (wrong thread name). No QuickFiler.Test call to `UiThread.Init(` was found (only a commented one at `QfcHomeControllerTests.cs:240`); production reads of `UiThread.Dispatcher` (e.g. `ItemViewerQueue.cs:21,27`, `EfcViewerQueue.cs:20`) go through the throwing getter at `:266-284`, which never writes. Closure (section 7, item 3): QuickFiler.Test contains no call to `UiThread.Init(`, `UiThread.UiSyncContext` or `UiThread.AutoScaleFactor` (only comment mentions at `QfcCollectionControllerDefects468Tests.cs:106`, `QfcHomeControllerRunAsyncTests.cs:328`, `QfcHomeControllerTests.cs:240`), while 23 QuickFiler production files reference `UiSyncContext`/`AutoScaleFactor`, so static reachability through production code cannot be ruled out. W5 is nevertheless excluded as the producer of this failure by value identity: `Init()` throws before `Initialize()` on a non-STA caller (`UiThread.cs:30-34`), and when it does run it writes the `SyncContextForm`'s dispatcher, never the parked singleton. W5 remains a residual writer for the pinned-baseline fix: it is latched once per process (`:51-59`), and a W5 write during R4 would make B observe a value other than `original`. Recorded as a residual risk, not a cause. | | W6 | `UiThread.ResetForTesting()` — `UiThread.cs:126` | `null` | No | Called only from `UtilitiesCS.Test/TestHelpers/UiThreadStateScope.cs:89` (different assembly). | -| W7 | `UtilitiesCS.Test/TestHelpers/UiThreadDispatcherScope.cs:78,109` and `UiThreadStateScope.cs:162,183` | their own values | No | Different assembly; those install `Dispatcher.CurrentDispatcher` of their own threads, never the QuickFiler.Test parked instance. `[UNVERIFIED]` whether vstest hosts UtilitiesCS.Test and QuickFiler.Test in the same process concurrently under the CI command. | +| W7 | `UtilitiesCS.Test/TestHelpers/UiThreadDispatcherScope.cs:78,109` and `UiThreadStateScope.cs:162,183` | their own values | No | Different assembly; those install `Dispatcher.CurrentDispatcher` of their own threads, never the QuickFiler.Test parked instance. Closure (section 7, item 3): `Invoke-MSTestWithCoverage.ps1` passes every `*.Test.dll` to one vstest invocation (`:117`, `:348`) with `/InIsolation` (`:90`), and neither runsettings file sets `DisableAppDomain`, so on .NET Framework the MSTest adapter loads each test assembly in its own AppDomain and the `UiThread` statics are per-AppDomain. Whether vstest uses one or several testhost processes is therefore not load-bearing. Independently of hosting, W7 is excluded by value identity: it never writes the QuickFiler.Test parked singleton. | Observed value = parked singleton, prior value = `null`. Only **W1** writes the parked instance, and only into a `null` field. W1 is reachable concurrently from exactly one other class: `QfcItemController_FocusAndThemeTests`. @@ -130,10 +158,11 @@ Alternatives rejected: (i) making `EnsureDispatcher` take the gate — rejected | File | Kind | Lines now | csproj | Change | |---|---|---|---|---| | `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` | test | 255 | `QuickFiler.Test.csproj:157` (explicit ``) | remove A1-A3; synchronous driving via D1; pump helper (private or shared) | -| `QuickFiler/Controllers/QfcDatamodel.cs` | production (COM-bound, `[ExcludeFromCodeCoverage]` at `:25`) | 483 | `QuickFiler.csproj` (explicit items, not re-verified here `[UNVERIFIED]`) | D1 seam: `WorkerStarter` property + 2 ctor assignments + 2 call sites (`:273`, `:300`) | +| `QuickFiler/Controllers/QfcDatamodel.cs` | production (COM-bound, `[ExcludeFromCodeCoverage]` at `:25`) | 483 | `QuickFiler.csproj:325` (explicit ``; legacy csproj, explicit items, verified) | D1 seam: `WorkerStarter` property + 2 ctor assignments + 2 call sites (`:273`, `:300`) | | `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` | test | 458 | `QuickFiler.Test.csproj:203` | R4 baseline scope + doc comment | | optional `QuickFiler.Test/TestSupport/.cs` | test support (new) | — | needs a new `` in `QuickFiler.Test.csproj` | only if the pump is shared rather than duplicated | -| optional `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs`, `QfcInitEmailQueueZeroBatchTests.cs` | test | not counted | existing | same D1 rewrite if the plan widens scope to the residuals in §2.4 | +| `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` | test | 235 | existing | IN SCOPE: remove T1/T2; assign the D1 starter (section 2.5) | +| `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` | test | 212 | existing | IN SCOPE: remove Z1; assign the D1 starter in all three tests (section 2.5) | Acceptance-gate test names (fully qualified): - `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` @@ -154,10 +183,11 @@ Acceptance-gate test names (fully qualified): No human interaction is required. All steps (edit test/production files, run the four-step toolchain, run the named tests by fully qualified name with `/Settings:scripts/vscode/TaskMaster.cli.runsettings`) are scriptable. No live Outlook, no UI pump host, no manual gate. -## 7. Not completed (stopped for quota) +## 7. Items left open by the first pass, and how each was closed -1. `git show`/`git diff` of CI head `b9692658` vs `origin/main` for the four fixture/test files (Bash disabled; assumed unchanged at the assertion site from the verbatim message match). -2. Confirmation that `QuickFiler.csproj` uses explicit `` items (expected, legacy csproj). -3. Verification of W5 reachability (`UiThread.UiSyncContext`/`AutoScaleFactor` getters from QuickFiler.Test on an STA thread) and of whether UtilitiesCS.Test shares the test host process under the CI command (W7). -4. Confirmation that the MSTest 4.4.1 worker thread has no ambient SynchronizationContext (pump design is robust regardless). -5. Numeric derivation evidence section: no numeric acceptance criterion is proposed, so none is required. +1. CI head `b9692658` versus the branch for the target files — CLOSED. The diff is empty (see the tooling note at the top). Main has not changed any target file since merge-base `9b3eea584` either, so citations against the branch tree are current. +2. `QuickFiler.csproj` compile-item style — CLOSED. Explicit legacy items; `QfcDatamodel.cs` is listed at `QuickFiler.csproj:325`. Editing an existing file needs no csproj change. +3. W5 reachability and W7 process sharing — CLOSED (section 3.3 rows W5 and W7). Neither can write the parked singleton, so neither changes the established cause. W5 is recorded as a residual writer for the pinned-baseline fix. +4. Ambient `SynchronizationContext` on the MSTest worker thread — RECORDED AS AN EXECUTION-TIME ASSUMPTION (section 2.3). The test-installed context makes the design independent of the answer; the plan records the observed value once. +5. Section 2 extended to `QfcDatamodelTeardownTests.cs` and `QfcInitEmailQueueZeroBatchTests.cs` — CLOSED (section 2.5), including the complete caller inventory of `InitEmailQueue`. +6. Numeric derivation evidence: no numeric acceptance criterion is proposed, so none is required. From 8bbd48f68f9631247ccc3c58fa232ff333d4fe56 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 22:02:16 -0400 Subject: [PATCH 04/24] docs(950): rewrite spec from completed research --- .../spec.md | 277 ++++++++++++++---- 1 file changed, 225 insertions(+), 52 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md index a39ca1f78..446b8237f 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md @@ -3,126 +3,299 @@ - **Issue:** #950 - **Parent (optional):** none - **Owner:** drmoisan -- **Last Updated:** 2026-10-01T07-11 -- **Status:** Draft -- **Version:** 0.1 +- **Last Updated:** 2026-10-01 +- **Status:** Ready for planning +- **Version:** 1.0 +- **Work Mode:** full-bug (this file is the only acceptance-criteria source; no user-story.md is produced) +- **Research:** research/2026-10-01T00-00-wall-clock-waits-research.md in this feature folder (read-only input; not a write target) + +> Formatting convention (do not change): a backticked repository path in this document is a write target. Every file this fix creates or modifies is backticked at least once and is listed under "Files/modules to change". Files that are cited but not modified (comparison files, the fixture, the concurrent-writer test class, the runsettings files, `UiThread`) are written as plain prose without backticks. Line citations such as QfcDatamodel.cs:273 are plain prose for the same reason. ## Context -Several QuickFiler.Test tests fail intermittently under load because they wait on real wall-clock time. Seen during parallel run bugs-2026-09-28: -- `QfcDatamodelLivenessTests`, including `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. Two failures stopped #944's P3-T8 gate, and one failure occurred during #929's local run. -- `QfcItemController.UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`, which failed once in CI on an earlier #929 head. + +Several QuickFiler.Test tests fail intermittently under load. They were observed during parallel run bugs-2026-09-28: + +- `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests`, including `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. Two failures stopped the P3-T8 gate of #944, and one failure occurred during the local run of #929. +- `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` (referred to below as R4), which failed once in CI on an earlier #929 head. + +Research (linked above) established two distinct root causes, both resolved in this issue: + +- **Defect A (timing).** The datamodel tests block on real-time bounded waits because the production code starts its background worker on a thread-pool thread that the test cannot control. +- **Defect B (raced static state).** R4 does not wait on time. It fails because a gate-free writer in another test class races the process-wide `UiThread._dispatcher` static under class-level parallelism. Environment: -- OS/version: Windows 11 (local, with concurrent coverage runs) and windows-latest (CI) -- Python version: n/a (C# / MSTest, parallel Workers=0, Scope=ClassLevel) -- Command/flags used: `Invoke-MSTestWithCoverage.ps1`, and the CI MSTest-with-coverage required check -- Data source or fixture: n/a + +- OS/version: Windows 11 (local, with concurrent coverage runs) and windows-latest (CI). +- Language/framework: C# on .NET Framework 4.8.1; MSTest 4.4.1, Moq 4.21.0, FluentAssertions 8.11.0. +- Test regime: the repository runsettings (scripts/vscode/TaskMaster.cli.runsettings, used by Invoke-MSTestWithCoverage.ps1 locally and verbatim by the CI MSTest-with-coverage required check) sets Workers=0 and Scope=ClassLevel. CI therefore runs test classes in parallel, the same as local runs. +- Command: Invoke-MSTestWithCoverage.ps1 under scripts/vscode, and the CI MSTest-with-coverage required check. Impact / Severity: + - [ ] Blocker - [x] High - [ ] Medium - [ ] Low +The affected tests sit on a required CI check and on executor toolchain gates; an intermittent failure stops unrelated work. ## Repro & Evidence + Steps to Reproduce: -1. Run QuickFiler.Test under the parallel regime while the machine is loaded, for example with a second coverage run. -2. Observe intermittent failures in the tests named above. + +1. Run QuickFiler.Test under the repository runsettings (Workers=0, Scope=ClassLevel) while the machine is loaded, for example with a second coverage run in progress. +2. Observe intermittent failures in the tests named in Context. Expected: -The tests are deterministic regardless of machine load. + +The tests are deterministic regardless of machine load and regardless of which other test classes run concurrently. Actual: -- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` uses `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` (line ~56) and `.Task.Wait(TimeSpan.FromSeconds(5))` (lines ~103 and ~173). A slow scheduler turns these into assertion failures. -- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs:206` fails intermittently. Its cause is not yet established. -Logs / Screenshots: -- [ ] Attached minimal logs or screenshot -- Snippet: #944 evidence for P3-T8, first run; #929 PR #949 CI history. +- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs uses `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` at line 56 (inside its `WaitForState` helper, reached from lines 106, 176, 223 and 249) and `Task.Wait(TimeSpan.FromSeconds(5))` at lines 103 and 173. A slow scheduler turns these into assertion failures. +- QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs has the same pattern: `SpinWait.SpinUntil` at line 67 (`WaitForState`, used at line 225) and `Task.Wait(TimeSpan.FromSeconds(5))` at line 220. +- QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs has `Task.Wait(TimeSpan.FromSeconds(5))` at line 161. +- R4 (declared at line 206 of QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs) fails at the `BeSameAs(original)` assertion (lines 244-250) with the message "Expected observedByB to refer to `` ... but found ... Dispatcher { ... Name = "UiThreadDispatcherFixture.ParkedDispatcher" }". +Logs / Evidence: + +- #944 evidence for P3-T8 (first run); #929 PR #949 CI history (run 36722780748). +- #823 flake-watch log (docs/features/active/2026-09-08-quickfiler-teardown-review-residuals-823/evidence/other/flake-watch-uithread-dispatcher-transaction.2026-09-09T00-15.md) records one R4 failure in four runs, all under Workers=0 / ClassLevel, consistent with a class-interleaving race. +- Research verified that every target file is byte-identical between CI head b9692658 and the branch head, so the line citations in this spec describe the code that failed. ## Scope & Non-Goals -- In scope: -- Out of scope / non-goals: -- Explicitly excluded systems, integrations, or datasets: + +Maintainer-approved scope (binding): + +1. **Defect A.** The three five-second waits in `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` (`SpinWait.SpinUntil` at line 56 via `WaitForState`, `Task.Wait` at lines 103 and 173) exist because `QfcDatamodel.InitEmailQueue` starts its worker through `BackgroundWorker.RunWorkerAsync()` (QfcDatamodel.cs lines 273 and 300) on an uncontrollable thread-pool thread. Fix with research option D1: an `internal Action WorkerStarter { get; set; }` seam on `QfcDatamodel`, assigned in both constructors to `worker => worker.RunWorkerAsync()`, mirroring the `RemainingEmailLoader` convention (null on `GetUninitializedObject` instances), replacing both `RunWorkerAsync` call sites. Tests assign a synchronous starter that raises `DoWork` on the calling thread through a test-side `BackgroundWorker` subclass exposing the protected `OnDoWork`, plus a test-owned drainable `SynchronizationContext` where a continuation must be observed after release. +2. **Same root cause, same seam, in scope.** `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` (waits at line 67 via `WaitForState` used at line 225, and line 220) and `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` (wait at line 161; the two other tests in that file start an unobserved thread-pool worker and must also assign the starter). Research section 2.5 holds the complete `InitEmailQueue` caller inventory. +3. **Defect B.** `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` (declared at line 206, failing assertion at lines 244-250). Root cause: `UiThreadDispatcherFixture.EnsureDispatcher()` writes the shared `UiThread._dispatcher` static without taking the transaction gate, racing `QfcItemController_FocusAndThemeTests` (`SetThemeDark_FromNormal_SelectsDarkNormalTheme` and `SetThemeLight_FromNormal_SelectsLightNormalTheme` call `EnsureUiThreadDispatcher` and discard the scope) under Workers=0 / ClassLevel. Test-only fix: pin a non-null baseline for the whole R4 body with `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()`, keep `NotBeSameAs(liveA)`, and update the R4 doc comment (lines 196-202) to state the cause, #950, and the W2/W5 residual-writer invariant. +4. **Prohibited.** Retries, `[DoNotParallelize]`, Workers=1, and longer timeouts. No `Thread.Sleep`, `Task.Delay`, or other wall-clock waits. No temporary files. +5. **Negative controls.** Each rewritten test needs a negative control that fails immediately rather than hanging. The mechanism for each test is stated in Test Strategy. +6. **Ambient context.** The ambient `SynchronizationContext` on the MSTest worker thread is an execution-time assumption to be recorded once during execution, not a blocker. +7. **Production footprint.** The production change is confined to `QuickFiler/Controllers/QfcDatamodel.cs` (483 total lines now; must stay at or below 500 total lines; the type carries `[ExcludeFromCodeCoverage]` at line 25, so the seam is coverage-exempt and must stay minimal). An optional shared test-support pump under QuickFiler.Test/TestSupport/ would need an explicit `` in QuickFiler.Test.csproj; a private nested helper per file is the alternative (the affected files document a duplication convention). + +Out of scope / non-goals (paths deliberately unbackticked because they are not modified): + +- QuickFiler.Test/Controllers/QfcItemController.FocusAndThemeTests.cs. Its discarded scopes are the concurrent writer, but R4's baseline pin makes them harmless, and changing them would not close the race against any future gate-free caller. +- QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs. Making `EnsureDispatcher` take the gate is rejected by the fixture's own design note (lines 26-30): callers without `[Timeout]` would block without bound. +- QuickFiler.Test/Controllers/QfcItemController.TestSupport.cs, UtilitiesCS/Threading/UiThread.cs, and both runsettings files. +- The `fake.Advance(...)` plus `await Task.Yield()` loops in the first liveness test (lines 113-116 and 128-132). They read no clock; they are scheduling-dependent but bounded to twenty iterations and are left as they are (research section 2.1). +- Other waits in QuickFiler.Test: the unbounded `WaitOne()` in Viewers/BreadcrumbSelectorToggleUiBoundaryTests.cs line 397 (a hang risk, not a wall-clock bound) and the zero-bound `Wait(0)` / `WaitOne(0)` probes in four Viewers files. None is a wall-clock wait. +- `QfcHomeControllerRunAsyncTests` calls `InitEmailQueue` on a `Mock` and never reaches `QfcDatamodel`; it is unaffected. +- A dedicated regression test for the Defect B race. MSTest cannot be made to schedule two classes concurrently on demand; the deterministic negative control in Test Strategy demonstrates the failure mode instead. ## Root Cause Analysis -Real-time bounded waits violate the determinism rules: no wall-clock waits in tests, and use of `FakeTimeProvider` or a controllable scheduler. They hit a required check. The transaction test may be a different root cause. Triage it first, and split it into its own issue if its cause is not timing. +### Defect A: worker start runs on an uncontrolled thread + +`QfcDatamodel.InitEmailQueue(0, worker)` (QfcDatamodel.cs lines 259-275) sets `_remainingLoadActive = true` (line 272) and then calls `worker.RunWorkerAsync()` (line 273; the positive-batch path calls it at line 300). `RunWorkerAsync` runs the subscribed `Worker_DoWork` handler (lines 185-229) on a thread-pool thread through delegate `BeginInvoke`. `Worker_DoWork` is `async void`: it calls `RemainingEmailLoader(_token)` at line 205, stores the result in `_remainingLoadTask` at line 206, awaits it at line 207 without `ConfigureAwait(false)`, and clears `_remainingLoadActive` in the `finally` at lines 209-216. + +Nothing in the test controls the thread on which that body runs, so each test can only poll for its effects: + +| Wait | File and line | Event awaited | +|---|---|---| +| A1 | QfcDatamodelLivenessTests.cs line 56 (`WaitForState`, reached from lines 106, 176, 223, 249) | `!worker.IsBusy` (106, 176) or `_remainingLoadActive == false` (223, 249) | +| A2 | QfcDatamodelLivenessTests.cs line 103 | test-owned TCS set inside the injected `RemainingEmailLoader` | +| A3 | QfcDatamodelLivenessTests.cs line 173 (`StartHeldOpenLoader`, used by tests 2-4) | same as A2 | +| T1 | QfcDatamodelTeardownTests.cs line 67 (`WaitForState`, used at line 225) | `_remainingLoadTask != null` | +| T2 | QfcDatamodelTeardownTests.cs line 220 | loader-entered TCS | +| Z1 | QfcInitEmailQueueZeroBatchTests.cs line 161 | loader-invoked TCS | + +The only existing seam is the worker body (`RemainingEmailLoader`, line 140, assigned in both constructors at lines 40 and 51). There is no seam for the worker start, which is the cause of every wait above. + +Two further tests in the zero-batch file (`InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` at line 127 and `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` at line 201) do not wait, but each starts a real thread-pool worker whose body runs after the test returns. They share the cause and take the same seam. + +### Defect B: gate-free writer races R4's null baseline + +R4 reads `original = UiThreadDispatcherFixture.Current` (line 215), installs `liveA` under transaction A (line 216), starts transaction B on a thread-pool continuation, disposes A (line 240), and asserts that B observed `original` (lines 244-250) and did not observe `liveA` (lines 251-257). + +The failure message shows `original == null` and B observed the fixture's parked singleton (thread name "UiThreadDispatcherFixture.ParkedDispatcher"). Research enumerated every writer of `UiThread._dispatcher` (W1-W7). Only W1, `UiThreadDispatcherFixture.EnsureDispatcher()`, writes the parked singleton, and only into a null field. W1 is gate-free by design. It is reachable concurrently from exactly one other class, `QfcItemController_FocusAndThemeTests`, whose two theme tests call `EnsureUiThreadDispatcher()` and discard the returned scope. + +Under Workers=0 / ClassLevel the seeding can land in either of two windows R4 leaves open: + +- Window 1: between the `original` read (line 215) and `Install(liveA)` (line 216). A captures the parked value as its previous value, restores it on dispose, and B observes parked. +- Window 2: between A's restore-to-null on dispose and B's `Current` read (line 230), which runs on a thread-pool continuation. Under load the gap admits a foreign `EnsureDispatcher` call that sees null and writes parked. + +Either window produces exactly the observed message. The `NotBeSameAs(liveA)` assertion (the #230 lost-update check) did not fail; `BeSameAs(original)` encodes a property the fixture does not guarantee when the baseline is null. The two-second duration and the sixty-second `[Timeout]` are not involved: no bound expired. + +Residual writers after the fix: W2 (`EnsureScope.Dispose()` holding the same parked singleton could null the field; no such foreign disposer exists today) and W5 (`UiThread.Initialize()`, latched once per process, writes the `SyncContextForm` dispatcher, never the parked singleton). Both are recorded as an invariant in the R4 doc comment rather than closed. ## Proposed Fix ### Design summary (what changes where): +- `QuickFiler/Controllers/QfcDatamodel.cs`: add the D1 worker-start seam. The property is `internal Action WorkerStarter { get; set; }`, assigned in both constructors to `worker => worker.RunWorkerAsync()`. Both call sites in `InitEmailQueue` (lines 273 and 300) call `WorkerStarter(worker)` instead of `worker.RunWorkerAsync()`. Production behavior is unchanged. +- The three datamodel test files assign `model.WorkerStarter` to a synchronous starter. The starter casts the worker to a test-side `SynchronousBackgroundWorker : BackgroundWorker` that exposes the protected virtual `OnDoWork(DoWorkEventArgs)` through a public `RaiseDoWork()` method. `OnDoWork` raises `DoWork` synchronously on the calling thread and so invokes the privately subscribed `Worker_DoWork` without reflection. `InitEmailQueue` then runs to `Worker_DoWork`'s first incomplete await before it returns. Every former wait becomes a synchronous assertion. +- Where a test must observe the continuation after the loader is released (liveness tests 3 and 4), the test installs a test-owned drainable `SynchronizationContext` before invoking `InitEmailQueue`, releases the loader, drains the queue, and asserts synchronously. The context is restored in a `finally` (precedent: `ViewerScope` in ItemViewerBreadcrumbThreadAffinityTests.cs lines 271-292). +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`: R4 wraps its whole body in `using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher())` before `BeginTransactionAsync`. The R4 `` (lines 196-202) is rewritten. + ### Boundaries and invariants to preserve: +- **Seam contract (one sentence).** `WorkerStarter` is the only path by which `InitEmailQueue` starts a worker; constructed instances start it with `RunWorkerAsync`, and `GetUninitializedObject` instances that do not assign it fail fast with `NullReferenceException` at the start site rather than silently starting a thread. +- **Trace of one value through the seam.** A zero-batch call on a constructed instance: `InitEmailQueue(0, worker)` sets `_remainingLoadActive = true`, calls `WorkerStarter(worker)`, which calls `worker.RunWorkerAsync()`, so `Worker_DoWork` runs on a thread-pool thread exactly as before. The same call on a test instance with the synchronous starter runs `Worker_DoWork` on the test thread up to `await loaderTask` and returns. The same call on an uninitialized instance with no starter assigned throws `NullReferenceException` from `InitEmailQueue`; no caller absorbs it, so the test fails at once. `QfcHomeController` (line 252) only reaches `InitEmailQueue` on constructed instances, so the throw path is unreachable in production. +- The #424 claim of liveness test 1 is preserved: the dequeue keeps polling on `_remainingLoadActive` (QfcDatamodel.QueueProcessing.cs lines 305 and 406), not on `worker.IsBusy`. `_worker` has no `IsBusy` reader, so removing the `IsBusy` waits removes no assertion about production behavior. +- D1 keeps `InitEmailQueue`'s flag-set lines (267-274) under test, which D2 (calling `SetupWorker` directly) would not. D2 is rejected for that reason. +- R4 keeps both assertions. `BeSameAs(original)` stays; with the pin, `original` is the non-null pinned value. `NotBeSameAs(liveA)` stays unchanged. +- Test classes keep running in parallel under Workers=0 / ClassLevel. + ### Dependencies or blocked work: +None. The fix is self-contained in QuickFiler and QuickFiler.Test. No package is added: `FakeTimeProvider` (Microsoft.Extensions.TimeProvider.Testing 10.10.0) is already referenced and used. + ### Implementation strategy (what changes, not sequencing): - + #### Files/modules to change: +- `QuickFiler/Controllers/QfcDatamodel.cs` (production; legacy csproj lists it explicitly, so no project-file change) +- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` +- `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` +- `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` +- `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` (acceptance-criteria check-off) + +Helper placement decision: the synchronous worker subclass and the drainable context are private nested helpers in each test file that needs them, following the duplication convention those files already document (QfcDatamodelLivenessTests.cs lines 19-23). This keeps the change footprint to the files above. If the plan instead promotes a shared helper to QuickFiler.Test/TestSupport/, it must add the new file and an explicit `` in QuickFiler.Test.csproj (every file is listed explicitly), and this list must be amended to name both before execution. + #### Functions/classes/CLI commands impacted: +- `QfcDatamodel`: new internal property `WorkerStarter`; both constructors; `InitEmailQueue` (both overload paths that start the worker). +- `QfcDatamodelLivenessTests`: `WaitForState` removed; `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle`, `StartHeldOpenLoader`, `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`, `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse`, `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` rewritten. +- `QfcDatamodelTeardownTests`: `WaitForState` removed; `Worker_DoWork_CapturesRemainingLoadTask` rewritten. The other four tests in the file are unchanged. Their `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` arguments and `fake.Advance(TimeSpan.FromSeconds(6))` are production arguments on a `FakeTimeProvider`, not wall-clock waits, and stay. +- `QfcInitEmailQueueZeroBatchTests`: all three tests assign the starter; `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` loses its bounded wait and its justifying doc comment (lines 136-145) is rewritten. +- `QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`: baseline pin and doc comment. + #### Data flow and validation changes: +Per-test replacement: + +- **Liveness test 1 (A1 at 106, A2).** Synchronous starter; `entered.Task.IsCompleted.Should().BeTrue()` immediately after `InitEmailQueue`; the `!IsBusy` wait is deleted because the worker never starts asynchronously. +- **`StartHeldOpenLoader` (A1 at 176, A3) and liveness test 2.** Same; "flag stays true across the async-void boundary" becomes a synchronous read of `_remainingLoadActive` after `InitEmailQueue` returns. +- **Liveness tests 3 and 4 (A1 at 223 and 249).** Drainable context installed before `InitEmailQueue`; then `release.SetResult(true)`, `pump.Drain()`, and `ReadLivenessFlag(model).Should().BeFalse(...)`. For test 4 the throwing loader's continuation is drained the same way, which faults `loaderTask` and resumes `Worker_DoWork` into the `finally` and then the `catch` at lines 225-228. +- **Teardown T1/T2.** Synchronous starter; `loaderEntered.Task.IsCompleted.Should().BeTrue()` and `GetPrivateField(model, "_remainingLoadTask").Should().NotBeNull()` as synchronous reads. The test asserts nothing after `loaderRelease.TrySetResult(true)`, so no pump is required; the continuation runs inline on the releasing thread. +- **Zero-batch Z1.** The inert loader returns `Task.FromResult(true)` and completes its TCS synchronously, so `loaderInvokedTcs.Task.IsCompleted.Should().BeTrue()` holds when `InitEmailQueue` returns. No pump is needed: `await loaderTask` completes synchronously and the `finally` runs before `InitEmailQueue` returns. +- **Zero-batch Z0/Z2.** Assign the synchronous starter so no worker outlives the test. +- **R4.** Pin as described; with the field non-null, `EnsureDispatcher` from any class installs nothing (fixture lines 130-137), which closes both windows. + #### Error handling and logging updates: +None in production. `Worker_DoWork`'s existing `catch` logs through log4net `logger.Error`, which is a no-op without configuration in tests. The `NullReferenceException` on an unassigned `WorkerStarter` is the intended fail-fast behavior for uninitialized test instances, matching `RemainingEmailLoader`. + #### Rollback/feature-flag considerations (if applicable): +No flag. Rollback is a revert of the branch. The production change is a behavior-preserving indirection. + ### Technical specifications (interfaces/contracts): #### Inputs/outputs and formats: +- `internal Action WorkerStarter { get; set; }`: input is the worker passed to `InitEmailQueue`; no return value; side effect is starting the worker. Default: `worker => worker.RunWorkerAsync()`. XML doc comment states the test-seam purpose, the constructor default, and the null-on-uninitialized behavior. +- Test-side `SynchronousBackgroundWorker.RaiseDoWork()`: calls `OnDoWork(new DoWorkEventArgs(null))` on the calling thread. +- Test-side drainable context: `Post` enqueues; `Send` throws or runs inline per the existing `DrainableSynchronizationContext` shape; `Drain()` loops only over already-queued callbacks until the queue is empty and never blocks. + #### Required configuration keys and defaults: +None. Runsettings are unchanged. + #### Backward-compatibility expectations: +`WorkerStarter` is internal (QuickFiler.Test already has `InternalsVisibleTo` access, as `RemainingEmailLoader` shows). `IQfcDatamodel` and every public member are unchanged. Production callers observe identical behavior. + #### Performance constraints (latency/throughput/memory): +The rewritten tests complete without blocking on any wait; their duration no longer depends on scheduler latency. Production adds one delegate invocation per `InitEmailQueue` call. + ## Assumptions, Constraints, Dependencies + - Assumptions (environment, data, access): + - The ambient `SynchronizationContext` on the MSTest 4.4.1 worker thread is not statically decidable from the repository. It is an execution-time assumption: the executor records the observed `SynchronizationContext.Current` value on the test thread once during execution, in the feature folder's evidence/other directory. It is not a blocker, because each test that observes a continuation installs and restores its own context. + - `BackgroundWorker.OnDoWork` is `protected virtual` in .NET Framework 4.8.1 and raises `DoWork` synchronously on the calling thread (research section 2.3). - Constraints (budget, performance, compatibility): -- External dependencies (services, libraries, releases): + - `QuickFiler/Controllers/QfcDatamodel.cs` is 483 total lines and must stay at or below 500 total lines (estimated growth about eight lines). Test files: QfcDatamodelLivenessTests.cs 255, QfcDatamodelTeardownTests.cs 235, QfcInitEmailQueueZeroBatchTests.cs 212, QfcItemController.UiThreadDispatcherFixtureTests.cs 458 total lines; each must stay at or below 500. + - net481: no `init`, no `record`. + - No retries, `[DoNotParallelize]`, Workers=1, longer timeouts, `Thread.Sleep`, `Task.Delay`, or temporary files. + - `QfcDatamodel` carries a type-level `[ExcludeFromCodeCoverage]` (line 25), so the seam adds no coverage denominator and must stay minimal. +- External dependencies (services, libraries, releases): none new. ## Data / API / Config Impact -- User-facing or API changes: -- Data or migration considerations: -- Logging/telemetry updates (if any): -- Compatibility notes (CLI flags, config schemas, versioning): -## Test Strategy -Seeded from issue: - -- [ ] Replace the bounded waits with deterministic completion signals (`TaskCompletionSource` or awaited handles) or an injected `TimeProvider`. -- [ ] Use no retries, `[DoNotParallelize]`, Workers=1 or longer timeouts, and find any raced static state. -- [ ] Negative control: show that each rewritten test fails when the awaited signal is never set. +- User-facing or API changes: none. One internal property is added to `QfcDatamodel`. +- Data or migration considerations: none. +- Logging/telemetry updates (if any): none. +- Compatibility notes (CLI flags, config schemas, versioning): none; runsettings and CI workflow files are unchanged. -- Regression tests to add or update: -- Unit tests (pytest) for the fixed behavior and boundaries: -- Edge cases and negative scenarios (invalid inputs, missing data, boundary values): -- Error handling and logging verification: -- Coverage impact and targets for changed lines/modules: -- Toolchain commands to run (format → lint → type-check → test): -- Manual validation steps (if required): +## Test Strategy +Seeded from issue (retained): + +- Replace the bounded waits with deterministic completion signals or a controllable scheduler (here: synchronous worker start plus a drainable context). +- Use no retries, `[DoNotParallelize]`, Workers=1 or longer timeouts, and find the raced static state (found: `UiThread._dispatcher` via W1). +- Negative control: show that each rewritten test fails when the awaited signal is never set. + +Regression tests to update (all in namespace `QuickFiler.Controllers.Tests`): + +| Test | Negative-control mechanism (temporary local edit, never committed) | Expected control outcome | +|---|---|---| +| `QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` | assign a no-op `WorkerStarter` | the loader-entered assertion fails immediately | +| `QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` | no-op `WorkerStarter` | the entered assertion in `StartHeldOpenLoader` fails immediately | +| `QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` | never set `release`, then `Drain()` | `Drain()` runs zero callbacks; `BeFalse` on the flag fails immediately | +| `QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` | never set `release`, then `Drain()` | the flag stays true; `BeFalse` fails immediately | +| `QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask` | no-op `WorkerStarter` | loader-entered and `_remainingLoadTask` assertions fail immediately | +| `QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` | no-op `WorkerStarter` | the loader-invoked TCS is incomplete; the assertion fails immediately | +| `QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` | remove the `WorkerStarter` assignment | `InitEmailQueue` throws `NullReferenceException` at the start site; the test fails immediately | +| `QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` | remove the `WorkerStarter` assignment | same as above | +| `QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` | on the pre-fix shape (pin removed), insert one discarded `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` call between the `original` read and `Install(liveA)`, and run the test alone by fully-qualified name so the baseline is null | `BeSameAs(original)` fails immediately with the CI message (parked singleton versus null). With the pin restored and the same injected call kept, the test passes, which shows the pin neutralizes the gate-free writer | + +No control blocks: `Drain()` loops only over already-queued callbacks, the synchronous starter returns at the first incomplete await, and R4's existing `[Timeout]` is unchanged. A control that hangs instead of failing is itself a defect in the rewrite. The executor records each control's outcome (test name, edit applied, observed failure message, duration) in the feature folder's evidence/other directory as a Markdown summary. + +The #230 lost-update control for R4 (a fixture that releases the gate before restoring) remains probabilistic, as the class doc at lines 14-22 already records. This issue does not change that limitation. + +- Unit tests (MSTest, Moq, FluentAssertions) for the fixed behavior and boundaries: the nine tests above. `WorkerStarter` itself is exercised by every zero-batch and liveness test through `InitEmailQueue`. +- Edge cases and negative scenarios: unassigned starter on an uninitialized instance (fail fast); loader that throws (test 4); loader never released (negative controls). +- Error handling and logging verification: test 4 covers the `finally`-then-`catch` path. +- Coverage impact and targets for changed lines/modules: `QfcDatamodel` is type-level `[ExcludeFromCodeCoverage]`, so the changed production lines carry no coverage obligation. Test files are excluded from the coverage denominator. No coverage figure changes. +- Toolchain commands to run (format, lint, type-check, test), in order, restarting from step 1 on any failure or auto-fix: + 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. Invoke-MSTestWithCoverage.ps1 under scripts/vscode (the `test: MSTest with Coverage (Koverage)` task), which uses the repository runsettings with Workers=0 / ClassLevel. +- Committed evidence: Markdown projections only (test-result summary derived from the TRX, coverage summary), per CLAUDE.md "Committed Test Evidence Format". No raw TRX or coverage XML. +- Manual validation steps: none. No live Outlook, UI host, or manual gate is required. ## Acceptance Criteria -- [ ] Repro steps now produce the expected behavior in all documented environments. -- [ ] Regression test(s) added and passing (list file path and test name). -- [ ] Edge cases and invalid inputs are handled with correct errors or fallbacks. -- [ ] No unintended behavior changes outside the defined scope. -- [ ] Required logs/telemetry updated and validated (if applicable). -- [ ] Performance constraints met or explicitly waived with rationale. -- [ ] Full toolchain pass completed (format → lint → type-check → test). -- [ ] Docs/config references updated to match the new behavior. + +- [ ] AC1: `QfcDatamodel` declares an internal `WorkerStarter` property whose type is an Action delegate over `BackgroundWorker`; both constructors assign it a default that calls `RunWorkerAsync` on the supplied worker, and it carries an XML doc comment stating the seam purpose and the null-on-uninitialized behavior. +- [ ] AC2: `InitEmailQueue` contains no direct `RunWorkerAsync` call; both of its worker-start sites (the zero-batch path and the positive-batch path) call `WorkerStarter` instead. +- [ ] AC3: `QuickFiler/Controllers/QfcDatamodel.cs` stays at or below five hundred total lines after the change, measured as total lines rather than non-blank lines. +- [ ] AC4: No `SpinWait.SpinUntil` call, no `Task.Wait` call, and no `WaitForState` helper remains in any of the three datamodel test files (`QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`, `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs`, `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs`); any surviving `TimeSpan` value in those files is a production argument or a `FakeTimeProvider` advance, not a blocking wait. +- [ ] AC5: No `Thread.Sleep`, `Task.Delay`, `[DoNotParallelize]`, retry construct, or `[Timeout]` value change is introduced by this branch, and neither runsettings file changes. +- [ ] AC6: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` passes under the repository runsettings, driving the worker through a synchronous `WorkerStarter`. +- [ ] AC7: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` passes under the repository runsettings, asserting the liveness flag synchronously after `InitEmailQueue` returns. +- [ ] AC8: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` passes under the repository runsettings, observing the cleared flag after releasing the loader and draining a test-owned synchronization context. +- [ ] AC9: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` passes under the repository runsettings, observing the cleared flag after releasing the throwing loader and draining a test-owned synchronization context. +- [ ] AC10: `QuickFiler.Controllers.Tests.QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask` passes under the repository runsettings, reading the loader-entered signal and the captured `_remainingLoadTask` synchronously. +- [ ] AC11: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` passes under the repository runsettings, reading the loader-invoked signal synchronously. +- [ ] AC12: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` both assign a synchronous `WorkerStarter` and pass under the repository runsettings. +- [ ] AC13: `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` wraps its whole body in a `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()`, keeps both its `BeSameAs(original)` and `NotBeSameAs(liveA)` assertions, and passes under the repository runsettings. +- [ ] AC14: The R4 doc comment no longer carries the flake-watch instruction and instead names the gate-free `EnsureDispatcher` writer as the cause, cites this issue, and states the W2/W5 residual-writer invariant. +- [ ] AC15: Each test named in AC6 through AC13 has a recorded negative control, using the mechanism the Test Strategy table assigns to it, that fails immediately with an assertion or exception rather than hanging; each outcome is recorded as a Markdown summary in the feature folder's evidence/other directory. +- [ ] AC16: The observed ambient `SynchronizationContext.Current` value on the MSTest worker thread is recorded once in the feature folder's evidence/other directory. +- [ ] AC17: The full C# toolchain passes in a single pass in order: csharpier check, the analyzers rebuild, the TreatWarningsAsErrors rebuild, and Invoke-MSTestWithCoverage with the repository runsettings unchanged. ## Risks & Mitigations + - Technical or operational risks: + - A future test class could dispose a scope holding the parked singleton (W2) during R4, or a production path could latch `UiThread.Initialize()` (W5) during R4. Either would make B observe a value other than `original`. Neither exists today. + - A future test reaching `InitEmailQueue` on an uninitialized instance without assigning `WorkerStarter` throws `NullReferenceException`. + - If the drainable context is not restored in a `finally`, it leaks to later tests on the same worker thread. + - The scheduling-dependent `Task.Yield` loops in liveness test 1 remain (out of scope). - Mitigations and rollbacks: + - The W2/W5 invariant is written into the R4 doc comment (AC14) so a future editor sees it. + - The `NullReferenceException` fails the test at once and matches the existing `RemainingEmailLoader` convention; the `WorkerStarter` XML doc states it. + - Context install/restore follows the `ViewerScope` try/finally precedent. + - Rollback is a branch revert; production behavior is unchanged. ## Rollout & Follow-up -- Release/rollout steps: -- Post-fix monitoring or clean-up tasks: -- Links: issue, PRs, related docs + +- Release/rollout steps: merge through the normal PR flow once the required checks pass. No deployment step. +- Post-fix monitoring or clean-up tasks: the #823 flake-watch log for R4 is superseded by this root cause; no further observations are needed. If the out-of-scope `Task.Yield` loops in liveness test 1 later prove flaky, open a separate issue. +- Links: issue #950 (https://github.com/drmoisan/TaskMaster/issues/950); research document linked in the header; related #424 (liveness gate), #230 (lost update), #823 (flake-watch), #929 and #944 (where the failures were observed). From c16cfea0d60ebc0dba73ad9ce682619dda83e6d2 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 22:32:58 -0400 Subject: [PATCH 05/24] docs(950): author atomic plan in place --- .../plan.2026-10-01T07-11.md | 1005 ++++++++++++++++- 1 file changed, 978 insertions(+), 27 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 21bd9dc06..fd802e9fc 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -3,42 +3,993 @@ - **Issue:** #950 - **Parent (optional):** none - **Owner:** drmoisan +- **Work Mode:** full-bug (acceptance criteria come from `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` only; no user story exists for this item and none is to be authored) - **Last Updated:** 2026-10-01T07-11 -- **Status:** Draft -- **Version:** 0.1 +- **Status:** Ready for preflight +- **Version:** 1.0 +- **Plan path continuity:** this file is updated in place for every preflight revision round. No timestamped sibling plan file is created for this cycle. -**Fail-closed evidence rule:** Include explicit baseline artifact tasks, final-QA artifact tasks, and coverage-comparison tasks for each in-scope language when policy requires coverage. If any required baseline artifact, QA artifact, or coverage-comparison artifact is missing, the audit verdict must be BLOCKED or INCOMPLETE, never PASS. +**Fail-closed evidence rule:** every command-bearing task writes one evidence artifact carrying `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`. A task whose artifact is missing or incomplete stays unchecked, and the plan outcome is BLOCKED or INCOMPLETE, never PASS. -**Evidence accounting rule:** Record the expected artifact path or location in each evidence-producing task. Do not mark evidence-backed work complete without the artifact. +**Evidence accounting rule:** the artifact path is named in the task text. Do not mark an evidence-bearing task complete without the artifact on disk at that exact path. +**Evidence location:** every artifact lives under `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/` in the canonical sub-kinds `baseline/`, `regression-testing/`, `qa-gates/` and `other/`. EVIDENCE_LOCATION_OVERRIDE_REJECTED: none supplied; no artifacts-tree evidence path appears in this plan. In task text the token FEATURE abbreviates `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; the Write Set spells every path in full. -**Phase 0 — Context & Inputs** -- [ ] [P0-T1] Link approved spec: -- [ ] [P0-T2] Record branch/commit baseline: -- [ ] [P0-T3] List required environment/fixtures/data: +## Caller instructions applied with a recorded adjustment -**Phase 1 — Preparation** -- [ ] [P1-T1] Confirm scope is locked for this fix (no open spec gaps) -- [ ] [P1-T2] Sync workspace to target branch and ensure tooling is available +Three delegation instructions are applied in an adjusted form. Each adjustment keeps the instruction's purpose and is stated here so a reviewer does not read it as an omission. -**Phase 2 — Regression Test (must fail first)** -- [ ] [P2-T1] [expect-fail] Add a small, deterministic regression test in the standard module file (use `tests/bugs//#950-.py` only if no clear home exists) -- [ ] [P2-T2] [expect-fail] Run the regression to confirm it fails and captures the repro +1. **Absolute worktree path in `Command:` rows.** The caller asked that each artifact's `Command:` row record the full prefixed payload. The prefix carries the absolute worktree path, which contains the account's profile directory. The repository hygiene guard (`scripts/hygiene/Test-RepositoryHygiene.Rules.ps1`, function `Get-UserProfilePathPattern`, line 21) rejects any committed line matching a drive-letter user-profile path, and both this plan and every evidence artifact are committed. Every `Command:` row therefore records the full payload with the literal token `WORKTREE` in place of the absolute path, and every payload prints `WORKTREE-LEAF:` followed by the leaf name of its working directory. The acceptance condition for the prefix is that the recorded payload begins with the PREFIX lines of the Command Reference and that `WORKTREE-LEAF: agent-a7805823735145ca4` is recorded; a payload that ran in the session checkout prints a different leaf and fails that condition. +2. **Quote character of the prefix.** The caller's prefix uses single quotes around the path. Payloads containing `$` are passed in outer single quotes (`pwsh -NoProfile -Command '...'`), and the Bash channel cannot carry a single quote inside a single-quoted argument. The prefix is therefore written `Set-Location -LiteralPath "WORKTREE"`; PowerShell treats a double-quoted literal path that contains no `$` and no backtick identically to the single-quoted form. +3. **Location of negative-control records.** The caller places negative-control runs under `evidence/regression-testing/`; spec AC15 requires the outcomes as a Markdown summary under `evidence/other/`. Both are produced: one run artifact per control batch under `regression-testing/`, and the AC15 summary under `other/`. -**Phase 3 — Minimal Fix** -- [ ] [P3-T1] Apply the smallest change needed to make the regression test pass; avoid opportunistic refactors +## Requirement sources -**Phase 4 — Verification Loop** -- [ ] [P4-T1] Re-run repro and regression test to confirm expected behavior -- [ ] [P4-T2] Run formatter → linter → type checker → tests; restart loop if any step changes files or fails -- [ ] [P4-T3] Record baseline, post-change, and comparison artifact paths for each in-scope language where coverage is required +- Acceptance criteria: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, section `## Acceptance Criteria`, lines 266 to 282: seventeen checkbox lines `- [ ] AC1:` through `- [ ] AC17:`, each on one line. The check-off edit changes only `- [ ] ACn:` to `- [x] ACn:`. +- Design record: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md` (sections 2.3, 2.5, 3.3, 3.4 and 3.5 govern this plan; read-only). +- Issue metadata: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/issue.md` carries `- Work Mode: full-bug` at line 12 and no acceptance-criteria section. It is not an acceptance-criteria source for this cycle. -**Phase 5 — Documentation & Status** -- [ ] [P5-T1] Update spec/issue with outcomes, decisions, and any deviations from scope +## Write Set (every file this plan creates or modifies) -**Phase 6 — PR & Handoff** -- [ ] [P6-T1] Prepare PR notes (summary, risks, validation performed, links to tests) and request review +Code files (the only paths outside the feature folder this plan may change; spec "Files/modules to change"): -**Phase 7 — Rollout / Follow-up** -- [ ] [P7-T1] Capture deployment/rollout notes and post-fix monitoring items -- [ ] [P7-T2] Record links (issue, PRs, related docs) for traceability +- `QuickFiler/Controllers/QfcDatamodel.cs` +- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` +- `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` +- `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` + +No project file changes: every edited file is already listed in its project file (fact 9), and no new source file is created. The helpers are private nested types in each test file that needs them (spec helper placement decision), so the spec's file list needs no amendment. + +Feature documents: + +- `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` (acceptance-criteria check-off edits only) +- `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md` (task check-off edits only) + +Evidence files, all new. Every name is fixed except the fail-before exception dossier, which carries the conventional timestamped name; the write time is the `Timestamp:` field (yyyy-MM-ddTHH-mm): + +- `FEATURE/evidence/baseline/`: `phase0-instructions-read.md`, `scope-and-anchor.md`, `bootstrap-sdk.md`, `bootstrap-tool-restore.md`, `bootstrap-nuget-restore.md`, `analyzer-alignment.md`, `bootstrap-dotnet-coverage.md`, `csharpier-check-baseline.md`, `msbuild-analyzer-baseline.md`, `msbuild-nullable-baseline.md`, `census-baseline.md`, `targets-baseline.md`, `concurrent-classes-baseline.md`, `stall-probe.md`, `coverage-baseline.md`, `phase0-commit.md` +- `FEATURE/evidence/regression-testing/`: `phase1-temporary-edits.md`, `phase1-build.md`, `r4-fail-before.md`, `phase1-revert.md`, `fail-before-exception..md` (exactly one), `pass-after-build.md`, `targets-pass-after.md`, `concurrent-classes-pass-after.md`, `controls-a-edits.md`, `controls-a-build.md`, `negative-controls-batch-a.md`, `controls-a-revert.md`, `controls-b-edits.md`, `controls-b-build.md`, `negative-controls-batch-b.md`, `controls-b-revert.md` +- `FEATURE/evidence/qa-gates/`: `production-seam-census.md`, `test-rewrite-census.md`, `scoped-format.md`, `post-format-census.md`, `implementation-commit.md`, `csharpier-format.md`, `csharpier-check-final.md`, `msbuild-analyzer-final.md`, `msbuild-nullable-final.md`, `coverage-post-change.md`, `coverage-comparison.md`, `toolchain-final-pass.md`, `wall-clock-tokens.md`, `prohibited-constructs.md`, `footprint-scope.md`, `evidence-hygiene.md`, `final-commit.md` +- `FEATURE/evidence/other/`: `ambient-synchronization-context.md`, `negative-controls-summary.md`, `ac-status-summary.md` + +Files this plan must not touch, stated so the executor fails closed rather than infers: QuickFiler.Test/QuickFiler.Test.csproj, QuickFiler/QuickFiler.csproj, QuickFiler.Test/Controllers/QfcItemController.FocusAndThemeTests.cs, QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs, QuickFiler.Test/Controllers/QfcItemController.TestSupport.cs, UtilitiesCS/Threading/UiThread.cs, TaskMaster.runsettings, scripts/vscode/TaskMaster.cli.runsettings, every file under scripts/, every file under .github/, every file under .claude/ (uncommitted .claude/agent-memory/ files are session memory and are never staged by this plan), every file under docs/features/potential/, and the research document. No raw test-result document (trx), raw coverage document (cobertura, coverage, coveragexml) or msbuild log is copied into the feature folder under any name; raw documents stay under the repository coverage directory, which .gitignore line 150 ignores. + +## AC identity table + +Each ID names one checkbox in the spec's `## Acceptance Criteria` section, in document order. + +| ID | Spec line | Opening words of the criterion | Evidence read by its check-off task | +|---|---|---|---| +| AC1 | 266 | QfcDatamodel declares an internal WorkerStarter property | `qa-gates/post-format-census.md` | +| AC2 | 267 | InitEmailQueue contains no direct RunWorkerAsync call | `qa-gates/post-format-census.md` | +| AC3 | 268 | QuickFiler/Controllers/QfcDatamodel.cs stays at or below five hundred total lines | `qa-gates/post-format-census.md` | +| AC4 | 269 | No SpinWait.SpinUntil call, no Task.Wait call, and no WaitForState helper | `qa-gates/wall-clock-tokens.md` | +| AC5 | 270 | No Thread.Sleep, Task.Delay, [DoNotParallelize], retry construct, or [Timeout] value change | `qa-gates/prohibited-constructs.md` | +| AC6 | 271 | DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle passes | `regression-testing/targets-pass-after.md`, `qa-gates/coverage-post-change.md`, `qa-gates/post-format-census.md` | +| AC7 | 272 | RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces passes | same three artifacts | +| AC8 | 273 | RemainingLoadActive_AfterLoaderCompletes_BecomesFalse passes | same three artifacts | +| AC9 | 274 | RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally passes | same three artifacts | +| AC10 | 275 | Worker_DoWork_CapturesRemainingLoadTask passes | same three artifacts | +| AC11 | 276 | InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker passes | same three artifacts | +| AC12 | 277 | the two other zero-batch-file tests both assign a synchronous WorkerStarter and pass | same three artifacts | +| AC13 | 278 | Transaction_SecondCallerCannotInstallUntilTheFirstRestores wraps its whole body in a using | same three artifacts plus `regression-testing/concurrent-classes-pass-after.md` | +| AC14 | 279 | The R4 doc comment no longer carries the flake-watch instruction | `qa-gates/post-format-census.md` | +| AC15 | 280 | Each test named in AC6 through AC13 has a recorded negative control | `other/negative-controls-summary.md` | +| AC16 | 281 | The observed ambient SynchronizationContext.Current value is recorded once | `other/ambient-synchronization-context.md` | +| AC17 | 282 | The full C# toolchain passes in a single pass in order | `qa-gates/toolchain-final-pass.md` | + +## Verified tree facts (re-derived against this worktree while authoring) + +Line totals below are content-line counts (the count a line-oriented reader returns for a file ending in a newline). + +1. `QuickFiler/Controllers/QfcDatamodel.cs` is 483 lines. `[ExcludeFromCodeCoverage]` at 25 on `public partial class QfcDatamodel` at 26. The private constructor 34 to 41 assigns `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` at 40; the public constructor 43 to 52 assigns it at 51; that literal occurs exactly twice. `RemainingEmailLoader` property documentation 126 to 139, declaration `internal Func> RemainingEmailLoader { get; set; }` at 140, blank 141, `#endregion Private Variables` at 142. `SetupWorker` 176 to 183 subscribes `Worker_DoWork` at 181. `Worker_DoWork` 185 to 229 (`async void`): loader call 205, capture 206, `e.Result = await loaderTask;` 207, `finally` 209 to 216 clears `_remainingLoadActive` at 215, `catch` 225 to 228. `InitEmailQueue` 259 to 303: zero-batch branch 267 to 275 with `_remainingLoadActive = true;` 272 and `worker.RunWorkerAsync();` 273; positive-batch `_remainingLoadActive = true;` 299 and `worker.RunWorkerAsync();` 300. `InitEmailQueueAsync` begins at 305. The file has exactly two lines whose trimmed text is `worker.RunWorkerAsync();` and no occurrence of `WorkerStarter`. Usings include System, System.ComponentModel (5) and Microsoft.Office.Interop.Outlook (14); the Outlook namespace declares a non-generic `Action`, which does not collide with the generic `Action` because C# name lookup matches type arity. +2. `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` is 255 lines, namespace `QuickFiler.Controllers.Tests`, class `QfcDatamodelLivenessTests` at 26. `WaitForState` documentation and body 47 to 57 with `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` at 56. Test 1 `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` 79 to 138: `new BackgroundWorker()` 97, `InitEmailQueue` 100, `.Task.Wait(TimeSpan.FromSeconds(5))` 103, `WaitForState` 106 to 110, `fake.Advance` 113, 115 and 130. `ReadLivenessFlag` 140 to 146. `StartHeldOpenLoader` documentation 148 to 152, method 153 to 181 (`new BackgroundWorker()` 169, `InitEmailQueue` 170, wait 173, `WaitForState` 176 to 179). Test 2 183 to 205 (`release.SetResult(true);` 204). Test 3 207 to 227 (`release.SetResult(true);` 220, `WaitForState` 223 to 226). Test 4 229 to 253 (`release.SetResult(true);` 246, `WaitForState` 249 to 252). Class closes at 254. Counts: `SpinWait` 1, `.Wait(` 2, `WaitForState` 5, `new BackgroundWorker()` 2. +3. `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` is 235 lines. `WaitForState` documentation and expression body 59 to 67 (`SpinWait.SpinUntil` at 67). `Cleanup_CalledTwice_DoesNotThrow` uses `new BackgroundWorker { WorkerSupportsCancellation = true }` at 174 and starts no worker. `Worker_DoWork_CapturesRemainingLoadTask` 196 to 233: the `using (var worker = new BackgroundWorker())` block 214 to 232 with `InitEmailQueue` 218, `.Task.Wait(TimeSpan.FromSeconds(5))` 220, `WaitForState` 225 to 229, `loaderRelease.TrySetResult(true);` 231. `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` at 118 and 149, `fake.Advance(TimeSpan.FromSeconds(6));` at 154. Counts: `SpinWait` 1, `.Wait(` 1, `WaitForState` 2, `new BackgroundWorker()` 1. +4. `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` is 212 lines. `CreateInertRemainingEmailLoader` 89 to 108 (completes its source and returns `Task.FromResult(true)`). Z0 `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` 117 to 133 (loader assignment 123, `new BackgroundWorker()` in the act lambda 127). Z1 `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` documentation 135 to 145, method 146 to 164 (`new BackgroundWorker()` 153, `.Task.Wait(TimeSpan.FromSeconds(5))` 161). Z2 `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` 176 to 210 (loader assignment 182, `new BackgroundWorker()` 201). Counts: `SpinWait` 0, `.Wait(` 1, `WaitForState` 0, `new BackgroundWorker()` 3. +5. `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` is 458 lines. `private const int GateTimeoutMs = 60000;` at 33; `[Timeout(GateTimeoutMs)]` occurs 8 times. R4 documentation 192 to 203 with the flake-watch `` 196 to 202 (`flake-watch` at 199, `Append an observation` at 200). R4 `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` declared at 206; body 207 to 264: `// Arrange` 208, `Dispatcher liveA = ...` 209, `try` 210, `Dispatcher original = UiThreadDispatcherFixture.Current;` 215, `transactionA.Install(liveA);` 216 (the only occurrence in the file), `BeSameAs(original)` 244 to 250, `NotBeSameAs(liveA)` 251 to 257, `finally` 260 to 263. R5 `Transaction_DisposedTwice_DoesNotOverReleaseTheGate` is declared at 273. The literal `Dispatcher original = UiThreadDispatcherFixture.Current;` also occurs at 55 and 115, so it is not a unique anchor. +6. `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs`: `EnsureDispatcher` 122 to 138 seeds the parked dispatcher only into a null field and never takes the transaction gate (design note 26 to 30); the parked thread is named `UiThreadDispatcherFixture.ParkedDispatcher` (231). `QfcItemController.TestSupport.cs` 238 to 239: `EnsureUiThreadDispatcher()` wraps it. `QfcItemController.FocusAndThemeTests.cs` declares class `QfcItemController_FocusAndThemeTests` (27) and discards the scope at 452 and 468. None is modified. +7. `QuickFiler.Test/SetupAssemblyInitializer.cs` `[AssemblyInitialize]` (14 to 25) installs an assembly resolver and WinForms rendering defaults and does not write `UiThread._dispatcher`, so R4 run alone by fully qualified name starts with a null baseline. +8. `scripts/vscode/TaskMaster.cli.runsettings` lines 4 to 7 set Workers 0 and Scope ClassLevel. `scripts/vscode/Invoke-MSTestWithCoverage.ps1` (461 lines): `Get-DotnetCoverageArgumentList` appends `/InIsolation`, `/TestCaseFilter:TestCategory!=LiveOutlook`, the results directory and the trx logger at 89 to 93 with no extension point; `Invoke-DotnetCoverageCollection` throws `MSTest with coverage failed with exit code` at 262 after the collector exits non-zero, before post-processing; defaults `coverage\test-results` and `mstest-coverage-run.trx` at 297 to 298; discovery 348 to 355 filters `bin\Debug` and a `.claude` segment relative to the search root; post-processing 399 to 402; threshold gate 406 to 409; `First-party coverage:` line printed at 410; JaCoCo projection 415 to 423; trx summary 430 to 447; raw document retained when written to the repository coverage directory 449 to 453; entry guard 459 to 461, so dot-sourcing is safe. `Invoke-MSTest.TrxSummary.ps1`: `Get-TrxRunSummary` 12 to 101, `Format-TrxRunSummary` 103 to 150 (its last line is `Failed tests: ` followed by names or `none`). `Invoke-MSTestWithCoverage.FirstParty.ps1` 117 to 120 renders `First-party coverage: lines a/b (p%), branches c/d (q%)`. +9. `QuickFiler.Test/QuickFiler.Test.csproj` lists the four test files explicitly (157, 161, 183, 203), `OutputPath` `bin\Debug\` (35), analyzer items 530 to 561 including `Meziantou.Analyzer.3.0.290` (554) and `Roslynator.Analyzers.5.0.0` (555 to 558); `QuickFiler.Test/packages.config` pins Meziantou.Analyzer 3.0.290 (11) and Roslynator.Analyzers 5.0.0 (52). `QuickFiler/QuickFiler.csproj` lists `Controllers\QfcDatamodel.cs` at 325, `OutputPath` `bin\Debug\` (24). +10. `.gitignore`: `*.trx` at 146, `coverage/*` at 150, `!coverage/.gitkeep` at 151. `.csharpierignore` excludes `**/evidence/**` (4) and `*.csproj` (12); there is no `.csharpierrc`, so the default print width of 100 applies. `global.json` pins SDK 8.0.205 under `.dotnet-sdk` with `latestFeature` roll-forward (3 to 8); `dotnet-tools.json` pins csharpier 1.2.6 (6). `scripts/vscode/Install-RepoDotNetSdk.ps1` defaults to version 8.0.205; `scripts/vscode/Invoke-Restore.ps1` takes SolutionPath, Configuration and Platform. +11. Branch `bug/quickfiler-tests-depend-on-wall-clock-timing-950`, cut at `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` (worktree reflog line 1); at authoring, HEAD is `8bbd48f68f9631247ccc3c58fa232ff333d4fe56` after four documentation commits. HEAD is recorded as an observation in P0-T3, never as an expectation. `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` exists on the tree. The `.dotnet-sdk`, `packages` and `bin` trees are absent (orchestrator environment fact 1), so every bootstrap task is guarded and gated on its post-task marker. +12. Host constraint carried from sibling items in this parallel run: four UtilitiesCS.Test shell-icon classes (`HelperClasses.ShellUtilities_Tests`, `HelperClasses.ShellUtilitiesStatic_Tests`, `HelperClasses.SysImageListHelperTests`, `EmailIntelligence.OSBrowser_Tests`) have stalled vstest on this workstation, and CI executes them. Whether the stall reproduces today is unknown, so P0-T15 measures it and the result selects the coverage route (D-6). + +## Design decisions (do not redesign) + +- **D-1 Production seam (research option D1, spec Proposed Fix).** `QfcDatamodel` gains `internal Action WorkerStarter { get; set; }` with the XML documentation in Delivered Source S1, inserted after line 140. Both constructors assign `WorkerStarter = worker => worker.RunWorkerAsync();` immediately after their `RemainingEmailLoader` assignment (S2). Both start sites in `InitEmailQueue` become `WorkerStarter(worker);` (S3). Nothing else in the file changes. Net growth is 12 lines (483 to 495); the diff against the anchor is 14 added and 2 deleted lines. +- **D-2 Test helpers are private nested types per file.** `SynchronousBackgroundWorker : BackgroundWorker` exposes `RaiseDoWork()`, which calls the protected `OnDoWork(new DoWorkEventArgs(null))`, and the static method `StartSynchronously(BackgroundWorker worker)` casts and raises. Both live in each of the three datamodel test files. `DrainableSynchronizationContext` lives only in the liveness file (only tests 3 and 4 observe a continuation after release). No file is added and no project file changes. +- **D-3 Fail-before evidence.** Defect B: P1-T6 runs the deterministic R4 repro from the spec's Test Strategy table on the unmodified test (one discarded `QfcItemControllerTestSupport.EnsureUiThreadDispatcher();` inserted immediately before `transactionA.Install(liveA);`, test run alone by fully qualified name), tagged `[expect-fail]`; P4-T6 pairs it with the pass-after results. Defect A: the failure needs the thread pool to delay the worker body past a five-second bound, which no policy-compliant test can force, so P1-T8 writes a fail-before exception dossier and P5-T18 appends the post-fix deterministic controls to it. +- **D-4 AC16 observation.** A temporary probe test (Delivered Source P-PROBE) is added to the unmodified liveness file in Phase 1, run alone under the repository runsettings, and reverted. The probe always fails by construction, so its exit code is deterministic (1) and its failure message carries the observed value after `AMBIENT-SYNC-CONTEXT=`. The probe is never committed. +- **D-5 Temporary edits and byte-level reverts.** Phase 1 edits are applied to the tree as committed at P0-T17; Phase 5 edits are applied to the tree as committed at P4-T7. Each batch is reverted with `git restore --source=HEAD --worktree` and verified by `git diff --exit-code HEAD -- ` exiting 0 and `git status --porcelain -- ` printing nothing. A control that hangs instead of failing is a defect in the rewrite (spec Test Strategy): every direct vstest run carries the hang-dump blame switch, and a `Sequence_*.xml` file or a Timeout or Aborted outcome is a stop. +- **D-6 Coverage route is selected by a recorded observation.** P0-T15 runs the four shell-icon classes alone and records `STALL-PROBE: CLEAR` or `REPRODUCES`. `COVERAGE-ROUTE: RUNNER` (CLEAR) runs `scripts/vscode/Invoke-MSTestWithCoverage.ps1` verbatim (CLAUDE.md step 4). `COVERAGE-ROUTE: DIRECT` (REPRODUCES) issues the runner's own inner collector invocation with the four-class exclusion appended and post-processes with the runner's own helpers, because the runner hard-codes its filter (fact 8). Both routes yield the same committed forms: the `First-party coverage:` line, the JaCoCo package projection text and the trx-derived summary, transcribed into Markdown. Under DIRECT, AC17 cannot be met as worded (the runner was not run) and its check-off records `AC17: NOT MET (ENVIRONMENTAL: COVERAGE-ROUTE DIRECT)`. +- **D-7 Coverage obligations.** `QfcDatamodel` is type-level `[ExcludeFromCodeCoverage]` (fact 1), so its file has no class element in the Cobertura document and no per-file coverage judgment is available in either direction; P0-T16 and P6-T5 record `QFCDATAMODEL-CLASS-NODES:` and the gate is that it is 0 at both stages. Test assemblies are excluded from instrumentation by the runner's derived settings. The first-party line and the root counters are recorded at both stages; the merged repository-wide rate is compared in two branches (denominators within 1 percent: tolerance 0.5 percentage points; otherwise recorded, not gated), because that rate is not reproducible across runs of an identical tree. The 80 percent line and 75 percent branch floors are applied by the runner (or by the runner's own threshold functions under DIRECT). +- **D-8 Baseline-relative test outcomes.** The baseline full run (P0-T16) may contain pre-existing failures; they are recorded as `BASELINE-FAILED-SET:` and do not stop Phase 0. The final run passes only when its failed set contains no name absent from the baseline failed set (`NEW-FAILURES: NONE`) and all nine target tests are `Passed`. AC17 additionally requires the runner to exit 0; a final run whose only failures are baseline failures completes P6-T5 but records `AC17: NOT MET (PRE-EXISTING FAILURES)` at P6-T28, and the orchestrator decides. +- **D-9 Anchor.** Every diff gate uses `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` as its ref operand (`BASE` in prose). P0-T3 verifies it is an ancestor of HEAD and equals `git merge-base origin/main HEAD`; a mismatch is `BASE-SHA MISMATCH`: stop and report (the orchestrator re-anchors). Paths changed between BASE and HEAD at P0-T3 form the inherited set `INHERITED-COMMITTED:`; each must be under FEATURE or be exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md`, otherwise `INHERITED SET OUT OF SCOPE`: stop. +- **D-10 Commits.** Three commits: P0-T17 (FEATURE only), P4-T7 (the five code files plus FEATURE, staged after the P4-T1 scoped format so the committed text is formatter-stable), P6-T32 (FEATURE). Each is a `git -C WORKTREE add -- ` invocation followed by a separate `git -C WORKTREE commit -m ""` invocation, one command per call, never chained, never `git add -A`. No commit message contains an angle bracket, a dollar sign or a backtick. A PreToolUse refusal of any `git add`, `git commit`, `.cs` edit or spec edit is recorded verbatim as `PRE-IMPLEMENTATION GATE BLOCKED` and stops the run; the executor does not modify hooks, checkpoints or permission configuration. +- **D-11 Git gates are pathspec-scoped and anchored.** Every `git diff` names BASE or HEAD as its ref operand; every name-listing diff is paired with a porcelain span in the same task; no gate asserts an unscoped empty porcelain. Uncommitted `.claude/agent-memory/` paths and this plan file may appear in porcelain output and are admitted by every scope gate. +- **D-12 Check-offs follow the loop.** Every check-off task sits in Phase 6 after the final toolchain pass and reads an artifact that survived it. Each flips exactly one checkbox and, when its evidence does not hold, completes with the box unchecked and records `ACn: NOT MET` with the reason in `FEATURE/evidence/other/ac-status-summary.md`. +- **D-13 Restart rules.** Phase 4: if P4-T4 or P4-T5 shows a target test not `Passed`, the executor corrects the Write Set file at fault (within the delivered design; no prohibited construct) and restarts at P4-T1, recording `P4-RESTART: n` in the restarted artifacts. Phase 6: `ITERATION` starts at 1. If P6-T1 rewrites any Write Set file, the executor commits exactly the rewritten Write Set files (`style(950): apply csharpier output`), increments ITERATION and restarts at P6-T1. If P6-T2, P6-T3, P6-T4 or P6-T5 fails because of a Write Set file, the executor corrects it, restarts at P4-T1 (scoped format, build, pass-after runs, commit), re-runs Phase 5 in full, increments ITERATION and resumes at P6-T1. A rewrite of a file outside the Write Set, or a failure not attributable to the Write Set beyond the D-8 baseline rule, is a stop with the failing artifact. P6-T7 records the final iteration only. + +## Delivered source (the executor writes these texts; CSharpier output wins on any layout difference) + +Every block below except R-INJECT is shown at its in-file indentation (eight, twelve or sixteen leading spaces), so it is written exactly as shown; R-INJECT states its indentation with it. Prose-quoted tokens used by later gates are each confined to one physical line of the delivered text. + +**S1 — `QuickFiler/Controllers/QfcDatamodel.cs`, inserted after line 140** (one blank line, then nine lines): + + /// + /// Injectable worker-start seam for + /// (issue #950). Both instance constructors assign a starter that calls + /// , so production behavior is unchanged; + /// tests assign a starter that raises DoWork synchronously on the calling thread. The + /// property stays null on instances built by GetUninitializedObject, so InitEmailQueue + /// fails fast with a NullReferenceException there instead of starting a thread. + /// + internal Action WorkerStarter { get; set; } + +Gate tokens quoted from S1: Injectable worker-start seam; null on instances built by GetUninitializedObject; internal Action WorkerStarter { get; set; }. + +**S2 — the same file, one line inserted after line 40 and one after line 51** (twelve-space indent): + + WorkerStarter = worker => worker.RunWorkerAsync(); + +**S3 — the same file, line 273 and line 300** (each line's whole text `worker.RunWorkerAsync();` is replaced, keeping that line's indentation): + + WorkerStarter(worker); + +**L1 — `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`, replaces lines 47 to 57** (the `WaitForState` documentation and method): + + /// + /// Test-side worker whose raises DoWork synchronously on + /// the calling thread through the protected OnDoWork, so the privately subscribed + /// Worker_DoWork runs to its first incomplete await before InitEmailQueue + /// returns. Issue #950: this replaces the bounded waits on a thread-pool worker. + /// + private sealed class SynchronousBackgroundWorker : BackgroundWorker + { + public void RaiseDoWork() => OnDoWork(new DoWorkEventArgs(null)); + } + + /// The synchronous starter assigned to QfcDatamodel.WorkerStarter. + private static void StartSynchronously(BackgroundWorker worker) => + ((SynchronousBackgroundWorker)worker).RaiseDoWork(); + + /// + /// Queues posted continuations and runs them only on an explicit call, + /// on the creating thread. Drain runs only work already queued, plus work that work queues, + /// and never blocks. Installed around InitEmailQueue by the tests that observe the + /// loader's continuation, and restored in a finally. + /// + private sealed class DrainableSynchronizationContext : SynchronizationContext + { + private readonly Queue> _callbacks = + new Queue>(); + private readonly int _creatorThreadId = Environment.CurrentManagedThreadId; + + public override void Post(SendOrPostCallback d, object state) => + _callbacks.Enqueue(Tuple.Create(d, state)); + + /// Runs every queued callback, including work queued while draining. + internal void Drain() + { + Environment.CurrentManagedThreadId.Should().Be(_creatorThreadId); + while (_callbacks.Count > 0) + { + Tuple callback = _callbacks.Dequeue(); + callback.Item1(callback.Item2); + } + } + } + +**L2 — the same file, replaces lines 97 to 110 of test 1** (from `var worker = new BackgroundWorker();` through the closing `);` of the `WaitForState` call; lines 111 onward are unchanged): + + var worker = new SynchronousBackgroundWorker(); + model.WorkerStarter = StartSynchronously; + + // Act — the issue #244 zero-batch short-circuit is COM-free and starts the worker + // through the issue #950 seam, which raises DoWork on this thread. + model.InitEmailQueue(0, worker); + + loaderEntered + .Task.IsCompleted.Should() + .BeTrue("the synchronous starter must reach the injected RemainingEmailLoader"); + +**L3 — the same file, replaces lines 148 to 181** (`StartHeldOpenLoader` documentation and method): + + /// + /// Starts the worker with a RemainingEmailLoader held open by + /// . The issue #950 synchronous starter raises DoWork on + /// this thread, so by the time InitEmailQueue returns the async void + /// Worker_DoWork has entered the loader and returned at its first incomplete await. + /// + private static QfcDatamodel StartHeldOpenLoader( + Func, Task> loaderBody, + out TaskCompletionSource release + ) + { + var model = CreateUninitializedDatamodel(); + var entered = new TaskCompletionSource(); + var localRelease = new TaskCompletionSource(); + release = localRelease; + + model.RemainingEmailLoader = _ => + { + entered.TrySetResult(true); + return loaderBody(localRelease); + }; + + var worker = new SynchronousBackgroundWorker(); + model.WorkerStarter = StartSynchronously; + model.InitEmailQueue(0, worker); + + entered + .Task.IsCompleted.Should() + .BeTrue("the synchronous starter must reach the injected loader before returning"); + return model; + } + +**L4 — the same file, replaces lines 207 to 227** (test 3 documentation and method): + + /// + /// AC 7: the flag becomes false only after the loader completes — never before. Issue + /// #950: the continuation that clears the flag is drained from a test-owned context. + /// + [TestMethod] + public void RemainingLoadActive_AfterLoaderCompletes_BecomesFalse() + { + // Arrange + SynchronizationContext previous = SynchronizationContext.Current; + var pump = new DrainableSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(pump); + try + { + QfcDatamodel model = StartHeldOpenLoader( + signal => signal.Task, + out TaskCompletionSource release + ); + ReadLivenessFlag(model).Should().BeTrue("the loader has not completed yet"); + + // Act + release.SetResult(true); + pump.Drain(); + + // Assert + ReadLivenessFlag(model) + .Should() + .BeFalse( + "the finally around the awaited loader must clear the flag once it completes" + ); + } + finally + { + SynchronizationContext.SetSynchronizationContext(previous); + } + } + +**L5 — the same file, replaces lines 229 to 253** (test 4 documentation and method): + + /// + /// AC 7: the finally clears the flag even when the loader throws. Issue #950: the + /// faulted loader's continuation is drained from a test-owned context. + /// + [TestMethod] + public void RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally() + { + // Arrange + SynchronizationContext previous = SynchronizationContext.Current; + var pump = new DrainableSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(pump); + try + { + QfcDatamodel model = StartHeldOpenLoader( + async signal => + { + await signal.Task; + throw new InvalidOperationException("loader failed"); + }, + out TaskCompletionSource release + ); + ReadLivenessFlag(model).Should().BeTrue("the loader has not failed yet"); + + // Act + release.SetResult(true); + pump.Drain(); + + // Assert + ReadLivenessFlag(model) + .Should() + .BeFalse( + "the finally must clear the flag on the throwing path too, or the gate would poll forever" + ); + } + finally + { + SynchronizationContext.SetSynchronizationContext(previous); + } + } + +**T1 — `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs`, replaces lines 59 to 67** (the `WaitForState` documentation and method): + + /// + /// Test-side worker whose raises DoWork synchronously on + /// the calling thread through the protected OnDoWork, so the privately subscribed + /// Worker_DoWork runs to its first incomplete await before InitEmailQueue + /// returns (issue #950). Duplicated per file, following the convention documented on + /// QfcDatamodelLivenessTests. + /// + private sealed class SynchronousBackgroundWorker : BackgroundWorker + { + public void RaiseDoWork() => OnDoWork(new DoWorkEventArgs(null)); + } + + /// The synchronous starter assigned to QfcDatamodel.WorkerStarter. + private static void StartSynchronously(BackgroundWorker worker) => + ((SynchronousBackgroundWorker)worker).RaiseDoWork(); + +**T2 — the same file, replaces lines 214 to 232** (the `using` block of `Worker_DoWork_CapturesRemainingLoadTask`): + + using (var worker = new SynchronousBackgroundWorker()) + { + model.WorkerStarter = StartSynchronously; + + // Act — the issue #244 zero-batch short-circuit is COM-free and starts the worker + // through the issue #950 seam, which raises DoWork on this thread, so + // Worker_DoWork has captured the loader task before InitEmailQueue returns. + model.InitEmailQueue(0, worker); + + // Assert + loaderEntered + .Task.IsCompleted.Should() + .BeTrue("the synchronous starter must reach the injected RemainingEmailLoader"); + GetPrivateField(model, "_remainingLoadTask") + .Should() + .NotBeNull( + "the loader task must be captured before it is awaited, so the Cancel path has " + + "a handle to quiesce" + ); + + loaderRelease.TrySetResult(true); + } + +**Z-H — `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs`, inserted after line 108** (one blank line, then the block): + + /// + /// Test-side worker whose raises DoWork synchronously on + /// the calling thread through the protected OnDoWork, so no worker started by + /// InitEmailQueue outlives the test (issue #950). Duplicated per file, following + /// the convention documented on QfcDatamodelLivenessTests. + /// + private sealed class SynchronousBackgroundWorker : BackgroundWorker + { + public void RaiseDoWork() => OnDoWork(new DoWorkEventArgs(null)); + } + + /// The synchronous starter assigned to QfcDatamodel.WorkerStarter. + private static void StartSynchronously(BackgroundWorker worker) => + ((SynchronousBackgroundWorker)worker).RaiseDoWork(); + +**Z0 — the same file, test `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing`:** insert ` model.WorkerStarter = StartSynchronously;` immediately after the line ` model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out _);` of this test (pre-edit line 123), and in the act lambda (pre-edit line 127) replace `new BackgroundWorker()` with `new SynchronousBackgroundWorker()`. + +**Z1 — the same file, replaces the documentation and method of `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`** (pre-edit lines 135 to 164): + + /// + /// Issue #244 AC2: a zero batch size must still set up and start the background worker so + /// remaining emails continue to load into the master queue. + /// (set synchronously by ) proves the worker was set up. + /// Issue #950: the worker is started through the WorkerStarter seam with a starter + /// that raises DoWork on this thread, and the inert loader completes its + /// synchronously, so the loader-invoked signal + /// is read without any wait as soon as InitEmailQueue returns. + /// + [TestMethod] + public void InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker() + { + // Arrange + var model = CreateUninitializedDatamodel(); + SetPrivateField(model, "_frame", CreateTwoRowEmailFrame()); + model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out var loaderInvokedTcs); + model.WorkerStarter = StartSynchronously; + var worker = new SynchronousBackgroundWorker(); + + // Act + model.InitEmailQueue(0, worker); + + // Assert + worker.WorkerSupportsCancellation.Should().BeTrue(); + loaderInvokedTcs + .Task.IsCompleted.Should() + .BeTrue("the injected RemainingEmailLoader must be invoked by the started worker"); + } + +**Z2 — the same file, test `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`:** insert ` model.WorkerStarter = StartSynchronously;` immediately after this test's line ` model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out _);` (pre-edit line 182), and on the act line (pre-edit line 201) replace `new BackgroundWorker()` with `new SynchronousBackgroundWorker()`. + +**R-DOC — `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`, replaces lines 196 to 202** (the R4 ``): + + /// + /// Issue #950: the earlier intermittent failure was a race on the shared static, not a + /// timing defect. The gate-free fixture method EnsureDispatcher seeds the parked dispatcher + /// whenever the field is null, so a concurrently running class that calls it could write + /// between the baseline read and the install, or between the restore and the second + /// caller's read. The test therefore pins a non-null baseline for its whole body. Invariant + /// for future editors: no other class may dispose an ensure scope holding the parked + /// dispatcher (W2), and UiThread.Initialize (W5) must not latch during this test; either + /// would change the value the second caller observes. + /// + +Gate tokens quoted from R-DOC: Issue #950: the earlier intermittent failure was a race on the shared static; The gate-free fixture method EnsureDispatcher seeds the parked dispatcher; (W2), and UiThread.Initialize (W5) must not latch. + +**R-BODY — the same file, R4 body.** Line 208 (` // Arrange`) is replaced by the three lines below; lines 209 to 263 (from `Dispatcher liveA = ...` through the closing brace of the `finally` block) are re-indented by four spaces with no other character changed; and one line ` }` is inserted after line 263, so the method's closing brace (pre-edit 264) closes the method as before: + + // Arrange — issue #950: with the field non-null for the whole body, the gate-free + // EnsureDispatcher in any concurrently running class installs nothing. + using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()) + { + +The two assertions at pre-edit lines 244 to 257 are retained unchanged apart from indentation. Gate token quoted from R-BODY: using (IDisposable baseline =. + +**P-PROBE — temporary (Phase 1 only, never committed). The unmodified liveness file, inserted after line 253** (the closing brace of `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally`), one blank line then: + + [TestMethod] + public void Issue950_AmbientSynchronizationContextProbe() + { + SynchronizationContext observed = SynchronizationContext.Current; + string observedName = observed == null ? "null" : observed.GetType().FullName; + string report = + "AMBIENT-SYNC-CONTEXT=" + + observedName + + " THREAD-POOL=" + + Thread.CurrentThread.IsThreadPoolThread + + " APARTMENT=" + + Thread.CurrentThread.GetApartmentState(); + report.Should().Be("PROBE-ALWAYS-FAILS", report); + } + +**R-INJECT — temporary (Phase 1 on the unmodified file, Phase 5 on the fixed file; never committed).** One line inserted immediately before the unique line whose trimmed text is `transactionA.Install(liveA);`, at that line's indentation: + + QfcItemControllerTestSupport.EnsureUiThreadDispatcher(); + +## Execution conventions + +- **Tokens.** `WORKTREE` denotes the absolute path of the item worktree supplied in the delegation prompt; the executor substitutes it into every payload and every `git -C` argument at run time and writes the token, never the path, into artifacts. `FEATURE` abbreviates the feature folder path. `BASE` denotes `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f`, which is written in full in every command. No artifact, and no line of this plan, carries an absolute host path, an account name or a machine name. +- **Payload channel.** Each indented payload in the Command Reference runs as one Bash tool call of the form `pwsh -NoProfile -Command ''`, newlines included, with the substitutions applied. Payloads use double quotes only, so the outer single quotes never conflict. Git commands run as single Bash calls of the form `git -C WORKTREE `, never chained. No `cd`, no `&&`, `;` or `|` between Bash commands. +- **Command rows.** The `Command:` field of a payload artifact records the full payload as executed, with `WORKTREE` in place of the path, followed on the next line by the canonical command it implements (for example `msbuild TaskMaster.sln /t:Rebuild ...`). Every payload artifact records `WORKTREE-LEAF: agent-a7805823735145ca4`; any other value means the payload ran in the wrong tree, and the task fails. +- **Exit codes.** `EXIT_CODE:` records the payload's principal exit value as named in each Command Reference entry. Deliberately failing runs carry `ExpectedExitCode:` equal to the deterministic value the task states. A recorded observation whose exit value is not gated carries `ExpectedExitCode:` equal to the observed value and says so. +- **Transcription.** Any absolute path inside a transcribed line is replaced by `REDACTED-PATH`. Trx and coverage documents are never copied into FEATURE. +- **Long-running payloads.** `CMD-COVERAGE-RUNNER` and `CMD-COVERAGE-DIRECT` are started as background processes with their standard output redirected to `coverage\logs\STAGE-950.result.log`; completion is detected when that log's final line is `PAYLOAD-COMPLETE`. A run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report. Before any re-invocation after a timed-out foreground call, the executor runs `pwsh -NoProfile -Command '"STRAY_TEST_PROCESSES: " + @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -like "vstest*" -or $_.ProcessName -like "testhost*" -or $_.ProcessName -like "dotnet-coverage*" }).Count'` and proceeds only at `STRAY_TEST_PROCESSES: 0`; two collections never run at once. +- **No wall-clock construct anywhere.** No payload sleeps; no test edit adds a sleep, delay, retry, timeout change, parallelism attribute or temporary file. + +## Command reference + +**PREFIX** (the first three lines of every payload): + + Set-Location -LiteralPath "WORKTREE" + [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path) + Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)) + +**TOOLS** (the next lines of every build and test payload): + + $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" + $msbuild = & $vswhere -latest -products * -find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1 + $vstest = & $vswhere -latest -products * -find "Common7\IDE\Extensions\TestPlatform\vstest.console.exe" | Select-Object -First 1 + New-Item -ItemType Directory -Path "coverage\logs" -Force | Out-Null + +**CODE5** (the five Write Set code paths, in this order): `"QuickFiler\Controllers\QfcDatamodel.cs", "QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs", "QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs", "QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs", "QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs"`. **THREE** is the second, third and fourth entries. **CODE5-GIT** is the same five paths with forward slashes, space-separated, for git pathspecs. + +**CMD-REBUILD** (`GATEARGS` is either `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` or `/p:TreatWarningsAsErrors=true`; `TASKID` substituted; `EXIT_CODE:` is `MSBUILD_EXIT_CODE:`; canonical command `msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS`, resolved through vswhere, plus /nodeReuse:false and a normal-verbosity file logger under the ignored coverage directory): + + PREFIX + TOOLS + $log = "coverage\logs\TASKID.msbuild.log" + if (Test-Path -LiteralPath $log) { Remove-Item -LiteralPath $log -Force } + $global:LASTEXITCODE = 0 + & $msbuild TaskMaster.sln /t:Rebuild /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" GATEARGS "/flp:LogFile=$log;Verbosity=normal" | Out-Null + Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE) + $lines = Get-Content -LiteralPath $log -Encoding UTF8 + Write-Output ("ERRORS: " + (($lines | Select-String -Pattern "^\s*(\d+) Error\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + Write-Output ("WARNINGS: " + (($lines | Select-String -Pattern "^\s*(\d+) Warning\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + Write-Output ("SKIP_CORECOMPILE_LINES: " + @($lines | Where-Object { $_.Contains("Skipping target ""CoreCompile""") }).Count) + Write-Output ("CSC_OUT_QUICKFILER: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\QuickFiler.dll") }).Count) + Write-Output ("CSC_OUT_QUICKFILER_TEST: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\QuickFiler.Test.dll") }).Count) + $files = @("QfcDatamodel.cs(", "QfcDatamodelLivenessTests.cs(", "QfcDatamodelTeardownTests.cs(", "QfcInitEmailQueueZeroBatchTests.cs(", "QfcItemController.UiThreadDispatcherFixtureTests.cs(") + $diag = @($lines | Where-Object { $l = $_; (@($files | Where-Object { $l.Contains($_) }).Count -gt 0) -and ($l -match "(error|warning) [A-Z]+[0-9]+") }) + Write-Output ("WRITESET_DIAGNOSTIC_LINES: " + $diag.Count) + Write-Output ("WRITESET_DIAGNOSTIC_CODES: " + ((@($diag | ForEach-Object { [regex]::Match($_, "(error|warning) ([A-Z]+[0-9]+)").Groups[2].Value }) | Sort-Object -Unique) -join ",")) + Write-Output ("TEST_DLL_EXISTS: " + (Test-Path -LiteralPath "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll")) + Write-Output ("UCS_TEST_DLL_EXISTS: " + (Test-Path -LiteralPath "UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll")) + +`ERRORS:` is read from the build summary line, so `0 Error(s)` is not mistaken for a substring of a larger count. Under /t:Rebuild `SKIP_CORECOMPILE_LINES` is 0 by construction; the two `CSC_OUT_` counts show the compiler ran for both Write Set projects (MSBuild echoes each csc command line at normal verbosity). + +**CMD-BUILD** (incremental build so a scoped test run observes a fresh assembly; `TASKID` substituted; `EXIT_CODE:` is `MSBUILD_EXIT_CODE:`; canonical command `msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU"`): + + PREFIX + TOOLS + $log = "coverage\logs\TASKID.msbuild.log" + if (Test-Path -LiteralPath $log) { Remove-Item -LiteralPath $log -Force } + $before = (Get-Item -LiteralPath "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll" -ErrorAction SilentlyContinue).LastWriteTimeUtc + $global:LASTEXITCODE = 0 + & $msbuild TaskMaster.sln /t:Build /m /nodeReuse:false /p:Configuration=Debug "/p:Platform=Any CPU" "/flp:LogFile=$log;Verbosity=normal" | Out-Null + Write-Output ("MSBUILD_EXIT_CODE: " + $LASTEXITCODE) + $lines = Get-Content -LiteralPath $log -Encoding UTF8 + Write-Output ("ERRORS: " + (($lines | Select-String -Pattern "^\s*(\d+) Error\(s\)" | Select-Object -Last 1).Matches[0].Groups[1].Value)) + $after = (Get-Item -LiteralPath "QuickFiler.Test\bin\Debug\QuickFiler.Test.dll").LastWriteTimeUtc + Write-Output ("TEST_DLL_ADVANCED: " + ($null -eq $before -or $after -gt $before)) + Write-Output ("CSC_OUT_QUICKFILER_TEST: " + @($lines | Where-Object { $_.Contains("/out:obj\Debug\QuickFiler.Test.dll") }).Count) + +**CMD-VSTEST** (`ASSEMBLY`, `FILTER`, `TASKID` and `NAMES` substituted; an empty `NAMES` prints every result; `EXIT_CODE:` is `VSTEST_EXIT_CODE:`, or 3 when the trx is absent; canonical command `vstest.console.exe ASSEMBLY /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER" "/ResultsDirectory:coverage\test-results\950\TASKID" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"`, resolved through vswhere): + + PREFIX + TOOLS + $results = "coverage\test-results\950\TASKID" + if (Test-Path -LiteralPath $results) { Remove-Item -LiteralPath $results -Recurse -Force } + $names = @(NAMES) + $global:LASTEXITCODE = 0 + & $vstest "ASSEMBLY" /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER" "/ResultsDirectory:$results" "/Logger:trx;LogFileName=TASKID.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" 2>&1 | Tee-Object -FilePath "coverage\logs\TASKID.vstest.log" | Out-Null + 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) + if (-not (Test-Path -LiteralPath $trxPath)) { 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.Count -eq 0 -or $names -contains $r.GetAttribute("testName")) { Write-Output ("RESULT " + $r.GetAttribute("testName") + " = " + $r.GetAttribute("outcome") + " duration=" + $r.GetAttribute("duration")) } } + foreach ($r in $all) { if ($r.GetAttribute("outcome") -ne "Passed") { $msg = $r.SelectSingleNode("t:Output/t:ErrorInfo/t:Message", $ns); Write-Output ("MESSAGE " + $r.GetAttribute("testName") + " :: " + $(if ($msg) { $msg.InnerText -replace "\s+", " " } else { "(no message)" })) } } + +The trx stays under the ignored coverage directory; the artifact transcribes the `COUNTERS`, `RESULT_COUNT:`, `RESULT` and `MESSAGE` lines. + +Substitutions: `ASSEMBLY-QF` is `QuickFiler.Test\bin\Debug\QuickFiler.Test.dll`; `ASSEMBLY-UCS` is `UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll`. The fully qualified prefix `QuickFiler.Controllers.Tests.` is written `QCT.` below for brevity and is expanded in full in every executed filter. + +- `FILTER-TARGETS`: the nine expressions `FullyQualifiedName=QCT.QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle`, `...=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`, `...=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse`, `...=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally`, `...=QCT.QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask`, `...=QCT.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`, `...=QCT.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing`, `...=QCT.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`, `...=QCT.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`, joined with `|` (vstest rejects `OR`). +- `NAMES-TARGETS`: the nine method names above, each double-quoted, comma-separated. +- `FILTER-CONCURRENT`: `FullyQualifiedName~QCT.QfcDatamodelLivenessTests.|FullyQualifiedName~QCT.QfcDatamodelTeardownTests.|FullyQualifiedName~QCT.QfcInitEmailQueueZeroBatchTests.|FullyQualifiedName~QCT.QfcItemController_UiThreadDispatcherFixtureTests.|FullyQualifiedName~QCT.QfcItemController_FocusAndThemeTests.` (all four target classes with the concurrent-writer class in one invocation under ClassLevel parallelism). +- `FILTER-PROBE`: `FullyQualifiedName=QCT.QfcDatamodelLivenessTests.Issue950_AmbientSynchronizationContextProbe`. +- `FILTER-R4`: `FullyQualifiedName=QCT.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`. +- `FILTER-CONTROLS-A`: the `FILTER-TARGETS` expressions for test 1, test 3, test 4, the teardown test, the three zero-batch tests and R4 (eight expressions; test 2 excluded). +- `FILTER-CONTROLS-B`: `FullyQualifiedName=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. +- `FILTER-STALL`: `FullyQualifiedName~HelperClasses.ShellUtilities_Tests|FullyQualifiedName~HelperClasses.ShellUtilitiesStatic_Tests|FullyQualifiedName~HelperClasses.SysImageListHelperTests|FullyQualifiedName~EmailIntelligence.OSBrowser_Tests`. + +**CMD-COVERAGE-RUNNER** (CLAUDE.md step 4 route; `STAGE` is `baseline` or `final`; `EXIT_CODE:` is `RUNNER_EXIT_CODE:`; canonical command `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`): + + PREFIX + 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", "coverage\STAGE-950.cobertura.xml", "coverage\STAGE-950.trx")) { 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-950.runner.log" | Out-Null + Write-Output ("RUNNER_EXIT_CODE: " + $LASTEXITCODE) + $log = Get-Content -LiteralPath "coverage\logs\STAGE-950.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 ("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-950.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-950.trx" -Force } + Write-Output "PAYLOAD-COMPLETE" + +The stale-output removal makes every `_PRESENT` value an observation of this run. Runner lines naming resolved paths stay in the ignored log. + +**CMD-COVERAGE-DIRECT** (the runner's inner invocation issued directly with the four-class exclusion; `STAGE` substituted; `EXIT_CODE:` is `COLLECT_EXIT_CODE:`; canonical command `dotnet-coverage collect --output coverage\STAGE-950.cobertura.xml --output-format cobertura --settings coverage\effective-coverage-950.config -- vstest.console.exe /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:" "/ResultsDirectory:coverage\test-results\950\STAGE" "/Logger:trx;LogFileName=STAGE-950.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None"`): + + PREFIX + . (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-950.cobertura.xml", "coverage\STAGE-950.trx")) { 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-950.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-950.cobertura.xml" + $settings = Join-Path $repo "scripts\vscode\TaskMaster.cli.runsettings" + $results = Join-Path $repo "coverage\test-results\950\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-950.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" 2>&1 | Tee-Object -FilePath "coverage\logs\STAGE-950.collect.log" | Out-Null + 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-950.trx")) { Copy-Item -LiteralPath (Join-Path $results "STAGE-950.trx") -Destination "coverage\STAGE-950.trx" -Force } + Write-Output ("TRX_PRESENT: " + (Test-Path -LiteralPath "coverage\STAGE-950.trx")) + Write-Output ("DOCUMENT_PRESENT: " + (Test-Path -LiteralPath $output)) + Write-Output "PAYLOAD-COMPLETE" + +**CMD-COVERAGE-POST** (summarise the trx, post-process if raw, apply the floors, print the committed forms and the figures; `STAGE` substituted; `RAW` is `True` under DIRECT and under a RUNNER run whose `COLLECT_FAILURE_MESSAGE:` is non-empty, otherwise `False`, because a completed runner run has already post-processed the document in place; `EXIT_CODE:` is the payload's own exit status, 0 when every line printed): + + PREFIX + . (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 + $trxText = Get-Content -LiteralPath "coverage\STAGE-950.trx" -Raw -Encoding UTF8 + $summary = Get-TrxRunSummary -TrxContent $trxText + Write-Output "SUMMARY-BEGIN" + Write-Output (Format-TrxRunSummary -Summary $summary) + Write-Output "SUMMARY-END" + Write-Output ("FAILED-SET: " + ((@($summary.FailedTestName) | Sort-Object -Unique) -join ", ")) + [xml]$trx = $trxText + $ns = New-Object System.Xml.XmlNamespaceManager($trx.NameTable) + $ns.AddNamespace("t", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010") + $names = @(NAMES-TARGETS) + foreach ($r in @($trx.SelectNodes("//t:UnitTestResult", $ns))) { if ($names -contains $r.GetAttribute("testName")) { Write-Output ("RESULT " + $r.GetAttribute("testName") + " = " + $r.GetAttribute("outcome")) } } + $doc = Get-Content -LiteralPath "coverage\STAGE-950.cobertura.xml" -Raw -Encoding UTF8 + if ("RAW" -eq "True") { $doc = ConvertTo-KoverageCoberturaXml -XmlContent $doc -RepoRoot $repo; Set-Content -LiteralPath "coverage\STAGE-950.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 + $root = $xml.SelectSingleNode("/coverage") + Write-Output ("ROOT line-rate=" + $root.GetAttribute("line-rate") + " branch-rate=" + $root.GetAttribute("branch-rate") + " lines-covered=" + $root.GetAttribute("lines-covered") + " lines-valid=" + $root.GetAttribute("lines-valid") + " branches-covered=" + $root.GetAttribute("branches-covered") + " branches-valid=" + $root.GetAttribute("branches-valid")) + $projection = ConvertTo-JacocoPackageProjection -XmlDocument $xml + Assert-JacocoProjectionReconciliation -XmlDocument $xml -ProjectionXml $projection + Write-Output "PROJECTION-BEGIN" + Write-Output $projection + Write-Output "PROJECTION-END" + $qd = @($xml.SelectNodes("//class[@filename]") | Where-Object { $_.GetAttribute("filename").Replace([string][char]92, "/") -eq "QuickFiler/Controllers/QfcDatamodel.cs" }) + Write-Output ("QFCDATAMODEL-CLASS-NODES: " + $qd.Count) + +The projection and the summary block are the two committed forms (CLAUDE.md "Committed Test Evidence Format"); the remaining lines are figures. The filename comparison is an exact equality on the repository-relative path, so the separately instrumented `QuickFiler/Interfaces/IQfcDatamodel.cs` cannot be counted. + +**CMD-HASH** (SHA-256 of CODE5; hashes only): + + PREFIX + foreach ($p in @(CODE5)) { Write-Output ("HASH " + $p + " = " + (Get-FileHash -Algorithm SHA256 -LiteralPath $p).Hash) } + +**CMD-LINECOUNT** (content line counts of CODE5): + + PREFIX + foreach ($p in @(CODE5)) { Write-Output ("LINES " + $p + " = " + @(Get-Content -LiteralPath $p -Encoding UTF8).Count) } + +**CMD-TOKEN-COUNT** (`FILE` and the `TOKENS` list substituted; ordinal, case-sensitive substring counts per physical line, so a token wrapped across two lines reads 0): + + PREFIX + $src = Get-Content -LiteralPath "FILE" -Encoding UTF8 + foreach ($t in @(TOKENS)) { Write-Output ("TOKEN [" + $t + "] = " + @($src | Where-Object { $_.Contains($t) }).Count) } + Write-Output ("TRIMMED-EQUAL [worker.RunWorkerAsync();] = " + @($src | Where-Object { $_.Trim() -eq "worker.RunWorkerAsync();" }).Count) + +**CMD-SPAN-TOKEN-COUNT** (`FILE`, `START`, `END` and `TOKENS` substituted; the span runs from the first line containing START up to, not including, the next line containing END; exit 4 when either anchor is missing): + + PREFIX + $src = Get-Content -LiteralPath "FILE" -Encoding UTF8 + $s = -1; for ($i = 0; $i -lt $src.Count; $i++) { if ($src[$i].Contains("START")) { $s = $i; break } } + $e = -1; if ($s -ge 0) { for ($i = $s + 1; $i -lt $src.Count; $i++) { if ($src[$i].Contains("END")) { $e = $i; break } } } + Write-Output ("SPAN: " + ($s + 1) + "-" + $e) + if ($s -lt 0 -or $e -lt 0) { exit 4 } + $span = $src[$s..($e - 1)] + foreach ($t in @(TOKENS)) { Write-Output ("SPAN-TOKEN [" + $t + "] = " + @($span | Where-Object { $_.Contains($t) }).Count) } + +Span anchors used by this plan: `INITQ` is START `public IList InitEmailQueue(` and END `public async Task> InitEmailQueueAsync(` in `QuickFiler\Controllers\QfcDatamodel.cs`; `R4SPAN` is START `public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores()` and END `public async Task Transaction_DisposedTwice_DoesNotOverReleaseTheGate()` in `QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs`. + +**CMD-TIMESPAN** (classifies every line of THREE that contains `TimeSpan.`): + + PREFIX + foreach ($p in @(THREE)) { $src = Get-Content -LiteralPath $p -Encoding UTF8; for ($i = 0; $i -lt $src.Count; $i++) { if ($src[$i].Contains("TimeSpan.")) { $ok = $src[$i].Contains("QuiesceLoaderAsync(") -or $src[$i].Contains("fake.Advance("); Write-Output ("TIMESPAN " + $p + ":" + ($i + 1) + " " + $(if ($ok) { "CLASSIFIED" } else { "UNCLASSIFIED" }) + " :: " + $src[$i].Trim()) } } } + $n = 0; foreach ($p in @(THREE)) { $n += @(Get-Content -LiteralPath $p -Encoding UTF8 | Where-Object { $_.Contains("TimeSpan.") -and -not ($_.Contains("QuiesceLoaderAsync(") -or $_.Contains("fake.Advance(")) }).Count } + Write-Output ("TIMESPAN-UNCLASSIFIED: " + $n) + +**CMD-ADDED-SCAN** (added lines of the anchored diff over the five code files): + + PREFIX + $diff = @(git diff 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- CODE5-GIT) + Write-Output ("GIT_DIFF_EXIT_CODE: " + $LASTEXITCODE) + $added = @($diff | Where-Object { $_.StartsWith("+") -and -not $_.StartsWith("+++") }) + Write-Output ("ADDED_LINES: " + $added.Count) + foreach ($t in @("Thread.Sleep", "Task.Delay", "DoNotParallelize", "Retry(", "Timeout(", "WorkerStarter")) { Write-Output ("ADDED-TOKEN [" + $t + "] = " + @($added | Where-Object { $_.Contains($t) }).Count) } + +`WorkerStarter` is the positive control: it is added by this plan, so a scan that cannot see added lines reports 0 for it and the gate fails. + +### Phase 0 — Policy Reads, Anchor, Bootstrap and Baseline Capture + +- [ ] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/plan-acceptance-gates.md, .claude/rules/tonality.md, .claude/skills/evidence-and-timestamp-conventions/SKILL.md and .claude/skills/acceptance-criteria-tracking/SKILL.md, and record the read in FEATURE/evidence/baseline/phase0-instructions-read.md. + - Acceptance: the artifact carries `Timestamp:`, a `Policy Order:` line naming the four mandatory documents in that order, and one line per document read. No policy document is modified. +- [ ] [P0-T2] Read FEATURE/spec.md, FEATURE/issue.md and FEATURE/research/2026-10-01T00-00-wall-clock-waits-research.md in full and record the Write Set, the prohibited paths and the acceptance-criteria inventory in FEATURE/evidence/baseline/scope-and-anchor.md (this task creates the file; P0-T3 appends to it). + - Acceptance: the artifact lists the five code paths of the Write Set verbatim; names the prohibited paths from the Write Set section; records that issue.md line 12 reads `- Work Mode: full-bug`; and records, counted from the file, that spec.md holds exactly 17 lines beginning `- [ ] AC` and 0 lines beginning `- [x] AC`. +- [ ] [P0-T3] Record the anchor and the pre-change tree state by appending to FEATURE/evidence/baseline/scope-and-anchor.md. + - Commands, each a separate Bash call: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse --abbrev-ref HEAD`; `git -C WORKTREE merge-base --is-ancestor 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE merge-base origin/main HEAD`; `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE diff --exit-code 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD -- QuickFiler QuickFiler.Test UtilitiesCS/Threading/UiThread.cs scripts/vscode TaskMaster.runsettings`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance, all required: `HEAD-SHA:` records the first output as an observation; `BRANCH:` reads `bug/quickfiler-tests-depend-on-wall-clock-timing-950`; the ancestor check exits 0 and the merge-base command prints exactly `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` (otherwise `BASE-SHA MISMATCH`: record both values and stop); `INHERITED-COMMITTED:` lists every name-status line or `NONE`, and every listed path is under FEATURE or is exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` (otherwise `INHERITED SET OUT OF SCOPE`: stop); the scoped `--exit-code` diff exits 0, recorded as `CODE-TREE-AT-BASE: UNCHANGED` (otherwise `CITED TREE ADVANCED`: stop, because every line citation in this plan is against BASE); `PRE-EXISTING-WORKTREE-PATHS:` lists every porcelain line verbatim or `NONE`, and no line names a path under QuickFiler/ or QuickFiler.Test/ (otherwise `CODE TREE DIRTY AT ANCHOR`: stop). The porcelain output is not asserted empty. +- [ ] [P0-T4] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record it in FEATURE/evidence/baseline/bootstrap-sdk.md. + - Command: `pwsh -NoProfile -Command 'PREFIX; if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version; "DOTNET_EXIT=$LASTEXITCODE"'` (PREFIX expanded to its three lines, joined with semicolons). + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `SDK_MARKER=True`, `DOTNET_EXIT=0`, and the version line is a version string rather than the global.json `errorMessage` text. The installer's filesystem marker is the gate; version equality is not asserted because global.json rolls forward within the feature band. +- [ ] [P0-T5] Restore the manifest tools with `dotnet tool restore` at the worktree root (manifest dotnet-tools.json) and record it in FEATURE/evidence/baseline/bootstrap-tool-restore.md. + - Command: `pwsh -NoProfile -Command 'PREFIX; dotnet tool restore; "RESTORE_EXIT=$LASTEXITCODE"; dotnet tool list --local; dotnet tool run csharpier check --help | Out-Null; "CHECK_HELP_EXIT=$LASTEXITCODE"'`. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `RESTORE_EXIT=0`, a local tool row with Package Id `csharpier` and Version `1.2.6`, and `CHECK_HELP_EXIT=0`. Only the Package Id and Version columns are transcribed (the Manifest column carries an absolute path). +- [ ] [P0-T6] Restore NuGet packages with scripts/vscode/Invoke-Restore.ps1 and record it in FEATURE/evidence/baseline/bootstrap-nuget-restore.md. + - Command: `pwsh -NoProfile -Command 'PREFIX; $env:MSBUILDDISABLENODEREUSE = "1"; & (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1"); "RESTORE_EXIT=$LASTEXITCODE"; "PACKAGE_DIRS=$(@(Get-ChildItem -LiteralPath packages -Directory -ErrorAction SilentlyContinue).Count)"'`. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `RESTORE_EXIT=0` and `PACKAGE_DIRS=` at least 1. +- [ ] [P0-T7] Verify analyzer-path alignment across every first-party project file (every `*.csproj` outside `packages\` and `.claude\`) and record it in FEATURE/evidence/baseline/analyzer-alignment.md. + - Command: `pwsh -NoProfile -Command 'PREFIX; $root = (Get-Location).Path; $rootLen = $root.TrimEnd([char]92).Length; $projs = @(Get-ChildItem -Path $root -Recurse -Filter "*.csproj" | Where-Object { $rel = $_.FullName.Substring($rootLen); $rel -notlike "\packages\*" -and $rel -notlike "\.claude\*" }); "PROJECTS=$($projs.Count)"; $missing = 0; $skew = 0; foreach ($p in $projs) { $dir = $p.DirectoryName; [xml]$x = Get-Content -LiteralPath $p.FullName -Raw; foreach ($a in @($x.SelectNodes("//*[local-name()=""Analyzer""]"))) { $inc = $a.GetAttribute("Include"); if (-not (Test-Path -LiteralPath (Join-Path $dir $inc))) { $missing++; "MISSING " + $p.FullName.Substring($rootLen) + " :: " + $inc } }; $pc = Join-Path $dir "packages.config"; if (Test-Path -LiteralPath $pc) { [xml]$c = Get-Content -LiteralPath $pc -Raw; foreach ($id in @("Meziantou.Analyzer", "Roslynator.Analyzers")) { $pin = @($c.SelectNodes("//package[@id=""$id""]") | ForEach-Object { $_.GetAttribute("version") }); $inc = @($x.SelectNodes("//*[local-name()=""Analyzer""]") | ForEach-Object { $_.GetAttribute("Include") } | Where-Object { $_.Contains("\$id.") }); foreach ($i in $inc) { if ($pin.Count -eq 0 -or -not $i.Contains("\$id." + $pin[0] + "\")) { $skew++; "SKEW " + $p.FullName.Substring($rootLen) + " :: " + $i } } } } }; "ANALYZER_MISSING=$missing"; "VERSION_SKEW=$skew"'`. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `PROJECTS=` at least 1, `ANALYZER_MISSING=0` and `VERSION_SKEW=0`. A non-zero value is `ANALYZER PATH SKEW`: record every `MISSING` and `SKEW` line and stop; it is an environment defect, not a plan defect, and no version number is asserted here because pins move. +- [ ] [P0-T8] Provision the dotnet-coverage global tool (guarded) and record it in FEATURE/evidence/baseline/bootstrap-dotnet-coverage.md. + - Command: `pwsh -NoProfile -Command 'if (-not (Get-Command dotnet-coverage -ErrorAction SilentlyContinue)) { dotnet tool install --global dotnet-coverage }; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"; dotnet-coverage --version'` (no PREFIX: the command touches no repository path; `WORKTREE-LEAF:` is recorded as `not applicable`). + - Acceptance: `DOTNET_COVERAGE_RESOLVED=True`, a version line is printed, `EXIT_CODE: 0`. +- [ ] [P0-T9] Capture the read-only formatter baseline with `dotnet tool run csharpier check .` and record it in FEATURE/evidence/baseline/csharpier-check-baseline.md. + - Command: `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE:` is the printed `CSHARPIER_EXIT_CODE:` value; the success-case line beginning `Checked ` and ending `ms.` is transcribed; `EXIT_CODE: 0` is the gate. A non-zero value lists every reported path and is `FORMAT BASELINE NOT CLEAN`: stop, because the CLAUDE.md format step (`csharpier format .`) would then rewrite files outside the Write Set and the decision belongs to the orchestrator. +- [ ] [P0-T10] Capture the analyzer baseline with `CMD-REBUILD` (analyzer GATEARGS, `TASKID` p0-t10) and record it in FEATURE/evidence/baseline/msbuild-analyzer-baseline.md. + - Acceptance, all required: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; `CSC_OUT_QUICKFILER:` and `CSC_OUT_QUICKFILER_TEST:` each at least 1; `TEST_DLL_EXISTS: True`; `UCS_TEST_DLL_EXISTS: True`; `WARNINGS:` recorded as `ANALYZER-BASELINE-WARNINGS:`; `WRITESET_DIAGNOSTIC_LINES:` and `WRITESET_DIAGNOSTIC_CODES:` recorded as `ANALYZER-BASELINE-WRITESET-LINES:` and `ANALYZER-BASELINE-WRITESET-CODES:` (the comparison basis for P6-T3). A non-zero exit is `ANALYZER BASELINE NOT CLEAN`: stop. +- [ ] [P0-T11] Capture the nullable baseline with `CMD-REBUILD` (nullable GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable property override, `TASKID` p0-t11) and record it in FEATURE/evidence/baseline/msbuild-nullable-baseline.md. + - Acceptance, all required: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. A non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop. +- [ ] [P0-T12] Record the pre-change census of the five code files in FEATURE/evidence/baseline/census-baseline.md, using `CMD-LINECOUNT`, `CMD-HASH`, `CMD-TIMESPAN`, `CMD-TOKEN-COUNT` once per code file and `CMD-SPAN-TOKEN-COUNT` for `INITQ` and `R4SPAN`. + - Token lists: for each of THREE, `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;"`; for QfcDatamodel.cs, `"WorkerStarter", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`; for the R4 file, `"flake-watch", "Append an observation", "Issue #950:", "[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`; for `INITQ`, `"RunWorkerAsync", "WorkerStarter(worker);"`; for `R4SPAN`, `"using (IDisposable baseline =", "EnsureUiThreadDispatcher()", ".BeSameAs(", ".NotBeSameAs("`. + - Acceptance (each value is derived in facts 1 to 5 and is a falsifiable pre-change observation): `WORKTREE-LEAF: agent-a7805823735145ca4`; LINES 483, 255, 235, 212, 458 in CODE5 order; five HASH values recorded as `BASE-HASH:` lines; liveness tokens 1, 2, 5, 2, 0, 0; teardown tokens 1, 1, 2, 1, 0, 0; zero-batch tokens 0, 1, 0, 3, 0, 0; QfcDatamodel.cs `WorkerStarter` 0, the loader-assignment literal 2, `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2`; R4 file tokens 1, 1, 0, 8, 1; `INITQ` `RunWorkerAsync` 2 and `WorkerStarter(worker);` 0; `R4SPAN` tokens 0, 0, 1, 1; `TIMESPAN-UNCLASSIFIED: 6` (liveness 56, 103, 173; teardown 67, 220; zero-batch 161). Any differing value is `CENSUS MISMATCH`: record and stop, because a later gate is defined against these values. The non-zero wall-clock counts are the positive control for the zero gates of P6-T8. +- [ ] [P0-T13] Capture the pre-change run of the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p0-t13, `NAMES-TARGETS`) and record it in FEATURE/evidence/baseline/targets-baseline.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 9` with one `RESULT` line per target name and its outcome; `BASELINE-TARGETS-NOT-PASSED:` lists every non-Passed name with its `MESSAGE` line, or `NONE`. Outcomes are observations of the pre-fix, load-dependent tests and are not gated; `ExpectedExitCode:` carries the observed value when non-zero. +- [ ] [P0-T14] Capture the pre-change concurrent run of the four target classes with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p0-t14, empty `NAMES`) and record it in FEATURE/evidence/baseline/concurrent-classes-baseline.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; the `COUNTERS` line recorded as `BASELINE-CONCURRENT-COUNTERS:`; every `RESULT` line transcribed; `BASELINE-CONCURRENT-FAILED:` lists every non-Passed name or `NONE` (the comparison basis for P4-T5). `ExpectedExitCode:` carries the observed value when non-zero; nothing here is gated beyond trx presence and no hang. +- [ ] [P0-T15] Run the stall probe with `CMD-VSTEST` (`ASSEMBLY-UCS`, `FILTER-STALL`, `TASKID` p0-t15, empty `NAMES`) and record FEATURE/evidence/baseline/stall-probe.md. + - Acceptance: the artifact records `WORKTREE-LEAF:`, `EXIT_CODE:`, `ExpectedExitCode:` equal to the observed value when non-zero (presentational), `TRX_PRESENT:`, `SEQUENCE_FILES:`, the `COUNTERS` line when present and every `MESSAGE` line; then exactly one `STALL-PROBE:` line — `CLEAR` when `EXIT_CODE: 0`, `failed=0` and `SEQUENCE_FILES: 0`, otherwise `REPRODUCES` — and exactly one `COVERAGE-ROUTE:` line — `RUNNER` under CLEAR, `DIRECT` under REPRODUCES. The probe runs once and is never re-run. Both values complete this task. +- [ ] [P0-T16] Capture the baseline repository-wide test-and-coverage run by the route P0-T15 fixed and record FEATURE/evidence/baseline/coverage-baseline.md: under RUNNER run `CMD-COVERAGE-RUNNER` with `STAGE` baseline, under DIRECT run `CMD-COVERAGE-DIRECT` with `STAGE` baseline; then, unless branch (d) applies, run `CMD-COVERAGE-POST` with `STAGE` baseline and `RAW` per its rule. + - Artifact: `Timestamp:`, `Command:` (both payloads, with the route's canonical command), `EXIT_CODE:` (`RUNNER_EXIT_CODE:` or `COLLECT_EXIT_CODE:`), `ExpectedExitCode:` equal to the observed value when non-zero (a baseline observation), and an `Output Summary:` recording `WORKTREE-LEAF:`, `COVERAGE-ROUTE:`, `RAW:`, `DISCOVERED_LINE:` or `ASSEMBLY_COUNT:` with every `ASSEMBLY:` line, `TRX_PRESENT:`, `SEQUENCE_FILES:` (DIRECT), `THRESHOLD_MESSAGE:` and `COLLECT_FAILURE_MESSAGE:` (RUNNER), `LINE-FLOOR:`, `BRANCH-FLOOR:`, the `First-party coverage:` line (the numeric baseline headline: lines covered over valid with percentage, branches likewise), the `ROOT` line, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the five summary lines verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, `FAILED-SET:` recorded as `BASELINE-FAILED-SET:`, the nine `RESULT` lines and `QFCDATAMODEL-CLASS-NODES:`. + - Branches, checked in order: (d) `TRX_PRESENT: False`, `SEQUENCE_FILES:` greater than 0, or a non-zero exit with an empty `FAILED-SET:` and no floor message, is `COVERAGE RUN ABORTED`: stop, report the last lines of the log with paths redacted, do not re-run. (c) `QFCDATAMODEL-CLASS-NODES:` other than 0 is `QFCDATAMODEL INSTRUMENTED`: stop, because D-7 rests on fact 1. (b) a non-zero exit with a non-empty `FAILED-SET:` or a floor `NOT MET` is recorded as `BASELINE-STATE: PRE-EXISTING FAILURES` (with `BASELINE-FLOOR:` naming any floor not met) and completes this task (D-8). (a) exit 0 with both floors met is `BASELINE-STATE: GREEN` and completes this task. +- [ ] [P0-T17] Commit the Phase 0 evidence (FEATURE only) and record it in FEATURE/evidence/baseline/phase0-commit.md. + - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record phase 0 baseline evidence"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance: both git commands exit 0; `PHASE0-COMMIT:` records the new HEAD as an observation; no porcelain line names a path under FEATURE other than this plan file (whose check-off mark is written after the commit) and FEATURE/evidence/baseline/phase0-commit.md (written after the commit), and no porcelain line names a path under QuickFiler/ or QuickFiler.Test/. This artifact itself is committed in P4-T7. + +### Phase 1 — Execution-Time Observation and Fail-Before Evidence (temporary edits on the unmodified tree) + +- [ ] [P1-T1] Insert Delivered Source P-PROBE into QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs after line 253 (temporary; reverted by P1-T7). + - Acceptance (recorded by P1-T3): the file contains exactly one line containing `public void Issue950_AmbientSynchronizationContextProbe()` and exactly one line containing `PROBE-ALWAYS-FAILS`; no other line changed. +- [ ] [P1-T2] Insert Delivered Source R-INJECT into QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs immediately before line 216 `transactionA.Install(liveA);` (temporary; reverted by P1-T7). + - Acceptance (recorded by P1-T3): `R4SPAN` `EnsureUiThreadDispatcher()` count is 1 and the inserted line sits between the line containing `Dispatcher original = UiThreadDispatcherFixture.Current;` (pre-edit 215) and the line containing `transactionA.Install(liveA);`; no other line changed. +- [ ] [P1-T3] Record the temporary-edit census in FEATURE/evidence/regression-testing/phase1-temporary-edits.md using `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"public void Issue950_AmbientSynchronizationContextProbe()", "PROBE-ALWAYS-FAILS"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: both liveness tokens 1; the R4 span token 1; numstat `15 0` for the liveness file and `1 0` for the R4 file; the porcelain span lists exactly those two paths with status ` M`. +- [ ] [P1-T4] Build the temporarily edited tree with `CMD-BUILD` (`TASKID` p1-t4) and record it in FEATURE/evidence/regression-testing/phase1-build.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_QUICKFILER_TEST:` at least 1. +- [ ] [P1-T5] [expect-fail] Run the AC16 probe alone with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-PROBE`, `TASKID` p1-t5, `NAMES` `"Issue950_AmbientSynchronizationContextProbe"`) under the repository runsettings and record FEATURE/evidence/other/ambient-synchronization-context.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1` (the probe fails by construction, D-4); `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 1`; the `RESULT` line reads `Failed`; the `MESSAGE` line contains `AMBIENT-SYNC-CONTEXT=`; and the artifact carries exactly one line `AMBIENT-SYNCHRONIZATION-CONTEXT:` followed by the text the message carries after `AMBIENT-SYNC-CONTEXT=` up to the next space, plus `THREAD-POOL:` and `APARTMENT:` lines copied the same way. A `RESULT` of `Passed`, or a message without `AMBIENT-SYNC-CONTEXT=`, means the probe did not run as written: stop. The recorded value is an observation (spec Assumptions); no value is required, because tests 3 and 4 install their own context. +- [ ] [P1-T6] [expect-fail] Run the deterministic R4 fail-before repro alone with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-R4`, `TASKID` p1-t6, `NAMES` `"Transaction_SecondCallerCannotInstallUntilTheFirstRestores"`) and record FEATURE/evidence/regression-testing/r4-fail-before.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 1`; the `RESULT` line reads `Failed` with its duration recorded; the `MESSAGE` line contains `to refer to` and `ParkedDispatcher` (the CI failure signature of spec Repro and Evidence: the second caller observed the parked dispatcher where the null baseline was expected). The artifact states the edit applied (R-INJECT on the unmodified file), that the test ran alone so the baseline was null (fact 7), and that this run is both the Defect B fail-before evidence and the R4 negative control of the spec Test Strategy table. A `Passed` result is `R4 REPRO DID NOT FAIL`: stop and report, because the root-cause claim rests on it. +- [ ] [P1-T7] Revert both temporary edits to HEAD and verify the revert byte-for-byte, recording FEATURE/evidence/regression-testing/phase1-revert.md. + - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`; then `CMD-HASH`. + - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing; the five HASH values equal the P0-T12 `BASE-HASH:` values. +- [ ] [P1-T8] Author the Defect A fail-before exception dossier FEATURE/evidence/regression-testing/fail-before-exception..md (timestamp yyyy-MM-ddTHH-mm at authoring). + - Required content: `Timestamp:`; `Defect: A (wall-clock waits on a thread-pool worker)`; `WhyFailingRunImpossible:` stating in one to three sentences that the Defect A failure occurs only when the thread pool delays the BackgroundWorker body past a five-second bound, and that no deterministic, policy-compliant test can force that delay (sleeps, delays and wall-clock waits are prohibited, and starving the thread pool changes process-wide state that other parallel classes share); and an `## Alternative proof` section citing (1) the P0-T12 census (`SpinWait` 1 and 1, `.Wait(` 2, 1 and 1, `WaitForState` 5 and 2, `TIMESPAN-UNCLASSIFIED: 6`), (2) the two direct start sites QfcDatamodel.cs lines 273 and 300 with `WorkerStarter` absent (P0-T12), (3) the recorded failure history from spec Context (two failures at the #944 P3-T8 gate, one in the #929 local run), and (4) a forward statement that P5-T18 appends the post-fix deterministic negative controls. + - Acceptance: exactly one file matching `fail-before-exception.*.md` exists under FEATURE/evidence/regression-testing/, and it carries `Timestamp:`, `WhyFailingRunImpossible:` and the `## Alternative proof` heading with items (1) to (4). + +### Phase 2 — Production Seam in QfcDatamodel.cs + +- [ ] [P2-T1] Insert Delivered Source S1 (the `WorkerStarter` property and its XML documentation) into QuickFiler/Controllers/QfcDatamodel.cs after line 140. + - Acceptance (recorded by P2-T4): the file contains exactly one line containing `internal Action WorkerStarter { get; set; }`, one containing `Injectable worker-start seam` and one containing `null on instances built by GetUninitializedObject`; the property sits before `#endregion Private Variables`. +- [ ] [P2-T2] Insert Delivered Source S2 into both constructors of QuickFiler/Controllers/QfcDatamodel.cs, each immediately after that constructor's `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` line (pre-edit 40 and 51). + - Acceptance (recorded by P2-T4): exactly two lines contain `WorkerStarter = worker => worker.RunWorkerAsync();`, each directly below one of the two loader-assignment lines. +- [ ] [P2-T3] Replace both start sites in `InitEmailQueue` of QuickFiler/Controllers/QfcDatamodel.cs (pre-edit lines 273 and 300) with Delivered Source S3, keeping each line's indentation. + - Acceptance (recorded by P2-T4): `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; within `INITQ`, `RunWorkerAsync` 0 and `WorkerStarter(worker);` 2. +- [ ] [P2-T4] Record the production seam census in FEATURE/evidence/qa-gates/production-seam-census.md with `CMD-TOKEN-COUNT` on QuickFiler\Controllers\QfcDatamodel.cs (TOKENS `"internal Action WorkerStarter { get; set; }", "WorkerStarter = worker => worker.RunWorkerAsync();", "Injectable worker-start seam", "null on instances built by GetUninitializedObject", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`), `CMD-SPAN-TOKEN-COUNT` on `INITQ` (TOKENS `"RunWorkerAsync", "WorkerStarter(worker);"`) and `CMD-LINECOUNT`. + - Acceptance: tokens 1, 2, 1, 1, 2; `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; `INITQ` 0 and 2; QfcDatamodel.cs LINES 495. Any other value: correct the edit and re-run this task. + +### Phase 3 — Test Rewrites + +- [ ] [P3-T1] Replace lines 47 to 57 of QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (the `WaitForState` documentation and method) with Delivered Source L1. + - Acceptance (recorded by P3-T14): `WaitForState` occurs only at the four call sites still awaiting P3-T2 to P3-T5; one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private sealed class DrainableSynchronizationContext : SynchronizationContext`. +- [ ] [P3-T2] Replace the arrange-act-wait block of test 1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 97 to 110) with Delivered Source L2; lines from `Task> pending = ...` onward are unchanged. + - Acceptance (recorded by P3-T14): the test contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected RemainingEmailLoader`, and no `.Wait(` or `WaitForState`. +- [ ] [P3-T3] Replace `StartHeldOpenLoader` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 148 to 181) with Delivered Source L3. + - Acceptance (recorded by P3-T14): the method contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected loader before returning`. +- [ ] [P3-T4] Replace test 3 `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 207 to 227) with Delivered Source L4. + - Acceptance (recorded by P3-T14): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally around the awaited loader must clear the flag once it completes`. +- [ ] [P3-T5] Replace test 4 `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 229 to 253) with Delivered Source L5. + - Acceptance (recorded by P3-T14): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally must clear the flag on the throwing path too`. +- [ ] [P3-T6] Replace lines 59 to 67 of QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (the `WaitForState` documentation and method) with Delivered Source T1. + - Acceptance (recorded by P3-T14): one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker`; `WaitForState` remains only at the call site that P3-T7 removes. +- [ ] [P3-T7] Replace the `using` block of `Worker_DoWork_CapturesRemainingLoadTask` in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (pre-edit lines 214 to 232) with Delivered Source T2. + - Acceptance (recorded by P3-T14): the teardown file has `SpinWait` 0, `.Wait(` 0, `WaitForState` 0, `new SynchronousBackgroundWorker()` 1 and `WorkerStarter = StartSynchronously;` 1; the four other tests in the file are unchanged (their `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` and `fake.Advance(TimeSpan.FromSeconds(6))` arguments stay). +- [ ] [P3-T8] Insert Delivered Source Z-H into QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs after line 108. + - Acceptance (recorded by P3-T14): one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private static void StartSynchronously(BackgroundWorker worker) =>`. +- [ ] [P3-T9] Apply Delivered Source Z0 to `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. + - Acceptance (recorded by P3-T14): the test assigns `model.WorkerStarter = StartSynchronously;` and its act lambda constructs `new SynchronousBackgroundWorker()`. +- [ ] [P3-T10] Replace the documentation and method of `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z1. + - Acceptance (recorded by P3-T14): the test reads `loaderInvokedTcs.Task.IsCompleted` with the reason literal `the injected RemainingEmailLoader must be invoked by the started worker`; the documentation no longer mentions a bounded timeout. +- [ ] [P3-T11] Apply Delivered Source Z2 to `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. + - Acceptance (recorded by P3-T14): the zero-batch file has `.Wait(` 0, `new BackgroundWorker()` 0, `new SynchronousBackgroundWorker()` 3 and `WorkerStarter = StartSynchronously;` 3. +- [ ] [P3-T12] Apply Delivered Source R-BODY (the baseline pin) to `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. + - Acceptance (recorded by P3-T14): within `R4SPAN`, `using (IDisposable baseline =` 1, `EnsureUiThreadDispatcher()` 1, `.BeSameAs(` 1 and `.NotBeSameAs(` 1; the `[Timeout(GateTimeoutMs)]` attribute and `private const int GateTimeoutMs = 60000;` are unchanged. +- [ ] [P3-T13] Replace the R4 documentation paragraph (pre-edit lines 196 to 202) of QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs with Delivered Source R-DOC. + - Acceptance (recorded by P3-T14): `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, and one line each containing `The gate-free fixture method EnsureDispatcher seeds the parked dispatcher` and `(W2), and UiThread.Initialize (W5) must not latch`. +- [ ] [P3-T14] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (the P0-T12 tokens) and `CMD-TIMESPAN`. + - Acceptance: liveness 0, 0, 0, 0, 2, 2, 1, 1, 2, 2; teardown 0, 0, 0, 0, 1, 1, 1, 0, 0, 0; zero-batch 0, 0, 0, 0, 3, 3, 1, 0, 0, 0; R4 file `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, `[Timeout(GateTimeoutMs)]` 8, the constant 1, the two R-DOC tokens 1 each; `R4SPAN` 1, 1, 1, 1; `TIMESPAN-UNCLASSIFIED: 0`, with every `TIMESPAN` line `CLASSIFIED`. Any other value: correct the edit and re-run this task. + +### Phase 4 — Scoped Format, Pass-After Runs and Implementation Commit + +- [ ] [P4-T1] Format the five Write Set code files with a scoped CSharpier pass and record the before-and-after hashes in FEATURE/evidence/qa-gates/scoped-format.md. + - Command: `CMD-HASH`; then `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier format QuickFiler\Controllers\QfcDatamodel.cs QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH` again. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `CSHARPIER_EXIT_CODE: 0`; the line beginning `Formatted ` is transcribed and labelled as a processed-file count, not a rewrite count; `REWRITTEN:` lists every CODE5 path whose hash differs between the two captures, or `NONE` (the rewrite observation is the hash difference, not the console line). Either value completes this task: the pass exists so the committed text is formatter-stable. +- [ ] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T14 commands, plus `git -C WORKTREE diff --numstat 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: every P2-T4 and P3-T14 value holds after formatting; CMD-LINECOUNT reports at most 500 for each CODE5 file (expected 495 for QfcDatamodel.cs; the others are recorded); numstat for QfcDatamodel.cs reads `14 2`, and the two deleted lines in the anchored diff both have the trimmed text `worker.RunWorkerAsync();`; the porcelain span lists exactly the five CODE5 paths with status ` M`. This artifact is the evidence for AC1, AC2, AC3 and AC14 and the census half of AC6 to AC13. +- [ ] [P4-T3] Build the fixed tree with `CMD-BUILD` (`TASKID` p4-t3) and record it in FEATURE/evidence/regression-testing/pass-after-build.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_QUICKFILER_TEST:` at least 1. +- [ ] [P4-T4] Run the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p4-t4, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/targets-pass-after.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 9`; nine `RESULT` lines, one per target name, each `Passed`, with durations recorded. Any target not `Passed` invokes the D-13 Phase 4 restart rule. +- [ ] [P4-T5] Run the four target classes concurrently with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p4-t5, empty `NAMES`) and record FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; every nine-target `RESULT` line `Passed`; `CONCURRENT-NOT-PASSED:` lists every non-Passed name or `NONE`, and every listed name is in P0-T14 `BASELINE-CONCURRENT-FAILED:` (`CONCURRENT-NEW-FAILURES: NONE`). `EXIT_CODE: 0` when the list is `NONE`; otherwise `ExpectedExitCode:` equals the observed value and the artifact names the baseline entries that account for it. A new failure invokes the D-13 Phase 4 restart rule. +- [ ] [P4-T6] Confirm the R4 pass-after condition from FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/regression-testing/targets-pass-after.md by appending a `R4-PASS-AFTER:` section to FEATURE/evidence/regression-testing/r4-fail-before.md. + - Acceptance: the section quotes the R4 `RESULT` line from both pass-after artifacts (`Passed` in each) beside the P1-T6 `Failed` line, completing the fail-before and pass-after pair for Defect B. +- [ ] [P4-T7] Commit the implementation (the five CODE5 files and FEATURE) and record FEATURE/evidence/qa-gates/implementation-commit.md. + - Commands, separate Bash calls: `git -C WORKTREE add -- QuickFiler/Controllers/QfcDatamodel.cs QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance: both git writes exit 0; `IMPLEMENTATION-COMMIT:` records the new HEAD as an observation; the name-status diff lists the five CODE5 paths with status `M`, FEATURE paths, and at most the inherited promotion record, nothing else; no porcelain line names a path under QuickFiler/ or QuickFiler.Test/. This artifact is committed in P6-T32. + +### Phase 5 — Negative Controls (temporary edits against the implementation commit) + +- [ ] [P5-T1] Control edit A1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: replace the first occurrence in file order of `model.WorkerStarter = StartSynchronously;` (test 1) with `model.WorkerStarter = _ => { };`. + - Acceptance (recorded by P5-T9): the liveness file has `WorkerStarter = StartSynchronously;` 1 and `WorkerStarter = _ => { };` 1, the latter inside test 1. +- [ ] [P5-T2] Control edit A2 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `release.SetResult(true);` inside `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` (test 3), so the loader is never released before `pump.Drain();`. + - Acceptance (recorded by P5-T9): test 3 no longer contains `release.SetResult(true);` and still contains `pump.Drain();`. +- [ ] [P5-T3] Control edit A3 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `release.SetResult(true);` inside `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (test 4). + - Acceptance (recorded by P5-T9): the liveness file has `release.SetResult(true);` 1 (test 2 only) and `pump.Drain();` 2. +- [ ] [P5-T4] Control edit A4 in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs: replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. + - Acceptance (recorded by P5-T9): the teardown file has `WorkerStarter = StartSynchronously;` 0 and `WorkerStarter = _ => { };` 1. +- [ ] [P5-T5] Control edit A5 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`, replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. + - Acceptance (recorded by P5-T9): `WorkerStarter = _ => { };` 1 in the zero-batch file, inside that test. +- [ ] [P5-T6] Control edit A6 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing`, delete the line `model.WorkerStarter = StartSynchronously;`. + - Acceptance (recorded by P5-T9): that test contains no `WorkerStarter` assignment. +- [ ] [P5-T7] Control edit A7 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`, delete the line `model.WorkerStarter = StartSynchronously;`. + - Acceptance (recorded by P5-T9): the zero-batch file has `WorkerStarter = StartSynchronously;` 0 and `WorkerStarter = _ => { };` 1. +- [ ] [P5-T8] Control edit A8 in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs: insert Delivered Source R-INJECT immediately before `transactionA.Install(liveA);` in the pinned R4 body. + - Acceptance (recorded by P5-T9): within `R4SPAN`, `EnsureUiThreadDispatcher()` 2 and `using (IDisposable baseline =` 1. +- [ ] [P5-T9] Record the batch A edit census in FEATURE/evidence/regression-testing/controls-a-edits.md with `CMD-TOKEN-COUNT` on THREE (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };", "release.SetResult(true);", "pump.Drain();"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()", "using (IDisposable baseline ="`), and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: liveness 1, 1, 1, 2; teardown 0, 1, 0, 0; zero-batch 0, 1, 0, 0; `R4SPAN` 2 and 1; numstat lists exactly the four test files (liveness `1 3`, teardown `1 1`, zero-batch `1 3`, R4 `1 0`); porcelain lists exactly those four paths with ` M`. The artifact names each edit A1 to A8 with its test. +- [ ] [P5-T10] Build the batch A tree with `CMD-BUILD` (`TASKID` p5-t10) and record FEATURE/evidence/regression-testing/controls-a-build.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. +- [ ] [P5-T11] [expect-fail] Run batch A with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-A`, `TASKID` p5-t11, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/negative-controls-batch-a.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 8`; and per test, with duration recorded: test 1 `Failed` with a message containing `the synchronous starter must reach the injected RemainingEmailLoader`; test 3 `Failed` with `the finally around the awaited loader must clear the flag once it completes`; test 4 `Failed` with `the finally must clear the flag on the throwing path too`; teardown `Failed` with `the synchronous starter must reach the injected RemainingEmailLoader`; Z1 `Failed` with `the injected RemainingEmailLoader must be invoked by the started worker`; Z0 `Failed` with `NullReferenceException`; Z2 `Failed` with `NullReferenceException`; R4 `Passed` (the pin neutralises the injected gate-free writer). Any `Timeout`, `Aborted` or `NotExecuted` outcome, any Sequence file, or any control that passes is `CONTROL DEFECT`: stop and report, because a control that does not fail immediately is a defect in the rewrite (spec Test Strategy). +- [ ] [P5-T12] Revert batch A to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-a-revert.md. + - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. +- [ ] [P5-T13] Control edit B1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: inside `StartHeldOpenLoader`, replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. + - Acceptance (recorded by P5-T14): the liveness file has `WorkerStarter = StartSynchronously;` 1 (test 1) and `WorkerStarter = _ => { };` 1 (inside `StartHeldOpenLoader`). +- [ ] [P5-T14] Record the batch B edit census in FEATURE/evidence/regression-testing/controls-b-edits.md with `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: 1 and 1; numstat lists only the liveness file with `1 1`; porcelain lists only that path with ` M`. +- [ ] [P5-T15] Build the batch B tree with `CMD-BUILD` (`TASKID` p5-t15) and record FEATURE/evidence/regression-testing/controls-b-build.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. +- [ ] [P5-T16] [expect-fail] Run batch B with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-B`, `TASKID` p5-t16, `NAMES` `"RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces"`) and record FEATURE/evidence/regression-testing/negative-controls-batch-b.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 1`; the `RESULT` line `Failed` with duration recorded; the `MESSAGE` line contains `the synchronous starter must reach the injected loader before returning`. Any other outcome is `CONTROL DEFECT`: stop. +- [ ] [P5-T17] Revert batch B to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-b-revert.md. + - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. +- [ ] [P5-T18] Append a `## Post-fix deterministic controls` section to the FEATURE/evidence/regression-testing/fail-before-exception.*.md dossier citing FEATURE/evidence/regression-testing/negative-controls-batch-a.md and FEATURE/evidence/regression-testing/negative-controls-batch-b.md. + - Acceptance: the section names the eight Defect A controls (A1 to A7 from batch A and B1 from batch B) with their outcome `Failed` and duration copied from the two batch artifacts, and states that each rewritten test fails at once when its signal is withheld, which the pre-fix tests could show only by waiting out a five-second bound. +- [ ] [P5-T19] Write the AC15 negative-control summary FEATURE/evidence/other/negative-controls-summary.md. + - Acceptance: a Markdown table with exactly nine rows, one per test named in AC6 to AC13 in that order, with columns Test, Mechanism (the spec Test Strategy row), Edit applied (A1 to A8, B1, or R-INJECT on the unmodified file), Observed outcome, Failure message fragment, Duration and Source artifact. The R4 row cites P1-T6 (`Failed`, pre-fix shape with the injected writer) and P5-T11 (`Passed`, pinned shape with the same injected writer), matching the spec's two-part R4 mechanism. Every row's values are copied from the cited artifacts; the artifact carries `Timestamp:`. + +### Phase 6 — Final QA Loop, Static Gates, Check-offs and Final Commit + +- [ ] [P6-T1] Run the CLAUDE.md formatting step `dotnet tool run csharpier format .` at the worktree root with a tree observation before and after, and record FEATURE/evidence/qa-gates/csharpier-format.md. + - Commands: `git -C WORKTREE status --porcelain --untracked-files=all`; `CMD-HASH`; `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier format .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; `CMD-HASH`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `CSHARPIER_EXIT_CODE: 0`; the `Formatted ` line transcribed and labelled as a processed-file count; `REWRITTEN-WRITESET:` lists every CODE5 path whose hash changed, or `NONE`; `REWRITTEN-OTHER:` lists every porcelain path present after and absent before, or `NONE`. Clean pass: both `NONE`. A non-empty `REWRITTEN-WRITESET:` with `REWRITTEN-OTHER: NONE` invokes the D-13 format restart; a non-empty `REWRITTEN-OTHER:` is `FORMAT TOUCHED OUT-OF-SCOPE FILE`: stop. +- [ ] [P6-T2] Run the read-only formatter gate `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/csharpier-check-final.md. + - Command: `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`. + - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; the success-case line beginning `Checked ` and ending `ms.` transcribed verbatim. +- [ ] [P6-T3] Run the analyzer rebuild with `CMD-REBUILD` (analyzer GATEARGS, `TASKID` p6-t3) and record FEATURE/evidence/qa-gates/msbuild-analyzer-final.md. + - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES:` not greater than `ANALYZER-BASELINE-WRITESET-LINES:` and every code in `WRITESET_DIAGNOSTIC_CODES:` present in `ANALYZER-BASELINE-WRITESET-CODES:` (no new analyzer diagnostic in a Write Set file); `WARNINGS:` recorded beside `ANALYZER-BASELINE-WARNINGS:`. +- [ ] [P6-T4] Run the type-check rebuild with `CMD-REBUILD` (GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable override, `TASKID` p6-t4) and record FEATURE/evidence/qa-gates/msbuild-nullable-final.md. + - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. +- [ ] [P6-T5] Run the final repository-wide test-and-coverage run by the P0-T15 route (`CMD-COVERAGE-RUNNER` or `CMD-COVERAGE-DIRECT` with `STAGE` final, then `CMD-COVERAGE-POST` with `STAGE` final) and record FEATURE/evidence/qa-gates/coverage-post-change.md. + - Artifact: the same fields as P0-T16, with `ITERATION:` and `FAILED-SET:` recorded as `FINAL-FAILED-SET:`. + - Acceptance, all required: the route equals P0-T15's; `TRX_PRESENT: True`; `SEQUENCE_FILES:` 0 under DIRECT; `QFCDATAMODEL-CLASS-NODES: 0`; the nine `RESULT` lines all `Passed`; `NEW-FAILURES:` (names in `FINAL-FAILED-SET:` absent from `BASELINE-FAILED-SET:`) is `NONE`; `LINE-FLOOR:` and `BRANCH-FLOOR:` each `MET`, or `NOT MET` only where P0-T16 recorded the same floor as not met; the `First-party coverage:` line is recorded as the numeric post-change headline. `RUNNER-GREEN: YES` is recorded when the route is RUNNER and `RUNNER_EXIT_CODE: 0`, otherwise `RUNNER-GREEN: NO` with the reason (`COVERAGE-ROUTE DIRECT` or `PRE-EXISTING FAILURES`). A failure of any target test or a new failure attributable to the Write Set invokes the D-13 failure restart; any other new failure is `NEW FAILURE OUTSIDE SCOPE`: stop and report, without re-running. +- [ ] [P6-T6] Compare baseline and post-change coverage and test outcomes and record FEATURE/evidence/qa-gates/coverage-comparison.md from FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-post-change.md. + - Acceptance: the artifact records the baseline and post-change `First-party coverage:` lines (lines and branches, numeric), the two `ROOT` lines, and the repository-wide comparison in exactly one named branch: `BRANCH A` when the two `lines-valid` figures differ by at most 1 percent of the baseline figure (the post-change line rate must not be lower than baseline by more than 0.5 percentage points), otherwise `BRANCH B` (recorded and not gated, with one sentence stating the denominators are not comparable). New and changed code: `CHANGED-PRODUCTION-COVERAGE: NOT MEASURED` with the reason `[ExcludeFromCodeCoverage]` at QuickFiler/Controllers/QfcDatamodel.cs line 25 and `QFCDATAMODEL-CLASS-NODES: 0` at both stages; the changed test files are outside the instrumented set. The artifact also restates `BASELINE-FAILED-SET:`, `FINAL-FAILED-SET:` and `NEW-FAILURES: NONE`. A Branch A breach is `COVERAGE REGRESSION`: stop. +- [ ] [P6-T7] Record the final toolchain pass in FEATURE/evidence/qa-gates/toolchain-final-pass.md from the P6-T1 to P6-T5 artifacts of the final iteration. + - Acceptance: one row per step, in order — csharpier format (`REWRITTEN-WRITESET: NONE`, `REWRITTEN-OTHER: NONE`), csharpier check (exit 0), analyzer rebuild (exit 0), TreatWarningsAsErrors rebuild (exit 0), coverage run (route, exit code, `RUNNER-GREEN:`) — each with its exact command, `EXIT_CODE:` and the same `ITERATION:` value; `SINGLE-PASS: YES` when all five rows come from one iteration with no restart after P6-T1; `AC17-STATUS: MET` only when `SINGLE-PASS: YES` and `RUNNER-GREEN: YES`, otherwise `AC17-STATUS: NOT MET` with the reason. +- [ ] [P6-T8] Record the AC4 wall-clock census in FEATURE/evidence/qa-gates/wall-clock-tokens.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "Task.Wait("`) and `CMD-TIMESPAN`. + - Acceptance: every count 0 in all three files; `TIMESPAN-UNCLASSIFIED: 0` with every `TIMESPAN` line `CLASSIFIED` (the surviving lines are the teardown file's two `QuiesceLoaderAsync(` production arguments and one `fake.Advance(` and the liveness file's three `fake.Advance(` lines); the artifact cites the non-zero P0-T12 counts as the positive control. +- [ ] [P6-T9] Record the AC5 prohibited-construct gate in FEATURE/evidence/qa-gates/prohibited-constructs.md with `CMD-ADDED-SCAN`, `git -C WORKTREE diff --exit-code 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, `git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, and `CMD-TOKEN-COUNT` on the R4 file (TOKENS `"[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`). + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `GIT_DIFF_EXIT_CODE: 0`; `ADDED_LINES:` greater than 0; `ADDED-TOKEN` counts 0 for `Thread.Sleep`, `Task.Delay`, `DoNotParallelize`, `Retry(` and `Timeout(`, and at least 1 for `WorkerStarter` (positive control); the runsettings diff exits 0 and the porcelain span prints nothing; the R4 file reads 8 and 1, equal to P0-T12. +- [ ] [P6-T10] Record the footprint gate in FEATURE/evidence/qa-gates/footprint-scope.md with `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD` paired with `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance: `THIS-ITEM-FOOTPRINT:` (name-status paths outside FEATURE) is exactly the five CODE5 paths with status `M`, plus the promotion record only if P0-T3 listed it as inherited (recorded as `INHERITED-AND-EXCLUDED:`); no path ends in `.csproj`; no status `A` outside FEATURE; no porcelain line names a path under QuickFiler/, QuickFiler.Test/ or scripts/. +- [ ] [P6-T11] Record the evidence hygiene gate in FEATURE/evidence/qa-gates/evidence-hygiene.md by scanning every file under FEATURE/evidence/. + - Command: `pwsh -NoProfile -Command 'PREFIX; $b = [char]92; $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950\evidence" -Recurse -File); "EVIDENCE_FILES=$($files.Count)"; "RAW_DOCUMENTS=$(@($files | Where-Object { $_.Extension -in @(".trx", ".xml", ".coverage", ".coveragexml", ".log") }).Count)"; $pattern = "[a-z]:[" + $b + $b + "/]+users[" + $b + $b + "/]+[a-z0-9_.~-]"; "PROFILE_PATH_LINES=$(@($files | Select-String -Pattern $pattern).Count)"'`. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EVIDENCE_FILES=` at least 1; `RAW_DOCUMENTS=0`; `PROFILE_PATH_LINES=0` (the pattern is the repository hygiene rule's, built from `[char]92` so the Bash channel cannot collapse its backslashes). A non-zero value names the offending files; the executor redacts them and re-runs this task. +- [ ] [P6-T12] Check off AC1 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows the property line 1, the constructor default 2 and both documentation tokens 1. + - Acceptance: only `- [ ] AC1:` changes to `- [x] AC1:`; otherwise the box stays unchecked and `AC1: NOT MET` with the reason is recorded for P6-T29. +- [ ] [P6-T13] Check off AC2 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `INITQ` `RunWorkerAsync` 0 and `WorkerStarter(worker);` 2. + - Acceptance: only `- [ ] AC2:` changes; otherwise unchecked with `AC2: NOT MET`. +- [ ] [P6-T14] Check off AC3 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows QfcDatamodel.cs LINES at most 500. + - Acceptance: only `- [ ] AC3:` changes; otherwise unchecked with `AC3: NOT MET`. +- [ ] [P6-T15] Check off AC4 in FEATURE/spec.md when FEATURE/evidence/qa-gates/wall-clock-tokens.md holds every P6-T8 condition. + - Acceptance: only `- [ ] AC4:` changes; otherwise unchecked with `AC4: NOT MET`. +- [ ] [P6-T16] Check off AC5 in FEATURE/spec.md when FEATURE/evidence/qa-gates/prohibited-constructs.md holds every P6-T9 condition. + - Acceptance: only `- [ ] AC5:` changes; otherwise unchecked with `AC5: NOT MET`. +- [ ] [P6-T17] Check off AC6 in FEATURE/spec.md when `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the liveness `WorkerStarter = StartSynchronously;` count 2. + - Acceptance: only `- [ ] AC6:` changes; otherwise unchecked with `AC6: NOT MET`. +- [ ] [P6-T18] Check off AC7 in FEATURE/spec.md when `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md. + - Acceptance: only `- [ ] AC7:` changes; otherwise unchecked with `AC7: NOT MET`. +- [ ] [P6-T19] Check off AC8 in FEATURE/spec.md when `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `pump.Drain();` 2 in the liveness file. + - Acceptance: only `- [ ] AC8:` changes; otherwise unchecked with `AC8: NOT MET`. +- [ ] [P6-T20] Check off AC9 in FEATURE/spec.md when `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md. + - Acceptance: only `- [ ] AC9:` changes; otherwise unchecked with `AC9: NOT MET`. +- [ ] [P6-T21] Check off AC10 in FEATURE/spec.md when `Worker_DoWork_CapturesRemainingLoadTask` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the teardown `WaitForState` 0 and `.Wait(` 0. + - Acceptance: only `- [ ] AC10:` changes; otherwise unchecked with `AC10: NOT MET`. +- [ ] [P6-T22] Check off AC11 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `.Wait(` 0. + - Acceptance: only `- [ ] AC11:` changes; otherwise unchecked with `AC11: NOT MET`. +- [ ] [P6-T23] Check off AC12 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` are both `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `WorkerStarter = StartSynchronously;` 3. + - Acceptance: only `- [ ] AC12:` changes; otherwise unchecked with `AC12: NOT MET`. +- [ ] [P6-T24] Check off AC13 in FEATURE/spec.md when `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md, FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `R4SPAN` 1, 1, 1, 1. + - Acceptance: only `- [ ] AC13:` changes; otherwise unchecked with `AC13: NOT MET`. +- [ ] [P6-T25] Check off AC14 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1 and both R-DOC tokens 1. + - Acceptance: only `- [ ] AC14:` changes; otherwise unchecked with `AC14: NOT MET`. +- [ ] [P6-T26] Check off AC15 in FEATURE/spec.md when FEATURE/evidence/other/negative-controls-summary.md holds nine rows whose outcomes match P5-T19's acceptance. + - Acceptance: only `- [ ] AC15:` changes; otherwise unchecked with `AC15: NOT MET`. +- [ ] [P6-T27] Check off AC16 in FEATURE/spec.md when FEATURE/evidence/other/ambient-synchronization-context.md carries exactly one `AMBIENT-SYNCHRONIZATION-CONTEXT:` line. + - Acceptance: only `- [ ] AC16:` changes; otherwise unchecked with `AC16: NOT MET`. +- [ ] [P6-T28] Check off AC17 in FEATURE/spec.md when FEATURE/evidence/qa-gates/toolchain-final-pass.md reads `AC17-STATUS: MET`. + - Acceptance: only `- [ ] AC17:` changes; otherwise the box stays unchecked and the recorded reason (`COVERAGE-ROUTE DIRECT`, `PRE-EXISTING FAILURES` or a restart after P6-T1) is carried to P6-T29 as `AC17: NOT MET`. +- [ ] [P6-T29] Write the acceptance-criteria status summary FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: the artifact carries `Timestamp:` and the acceptance-criteria-tracking status block (Source: the spec path; Total AC items: 17; Checked off; Remaining; Items remaining with each `ACn: NOT MET` reason), with the counts read from FEATURE/spec.md after P6-T28 (lines beginning `- [x] AC` and `- [ ] AC`, summing to 17). +- [ ] [P6-T30] Re-run the P6-T11 hygiene command over FEATURE/evidence/ after P6-T29 and append the result to FEATURE/evidence/qa-gates/evidence-hygiene.md as a `## Re-run after check-offs` section. + - Acceptance: `RAW_DOCUMENTS=0` and `PROFILE_PATH_LINES=0` for the evidence tree as it will be committed. +- [ ] [P6-T31] Verify that FEATURE/spec.md changed only in its checkbox lines by recording `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` and `git -C WORKTREE diff HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, paired with `git -C WORKTREE status --porcelain -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, in a `## Spec check-off diff` section appended to FEATURE/evidence/other/ac-status-summary.md. + - Acceptance: added and deleted line counts are equal and equal the checked-off count; every deleted line begins `- [ ] AC` and every added line begins `- [x] AC` with identical remaining text. +- [ ] [P6-T32] Commit the remaining feature evidence and check-offs (FEATURE only) and record FEATURE/evidence/qa-gates/final-commit.md. + - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record final QA evidence and acceptance check-offs"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance: both git writes exit 0; `FINAL-COMMIT:` records HEAD as an observation; the name-status paths outside FEATURE are exactly the P6-T10 footprint; the porcelain output names no path under FEATURE other than this plan file (whose final check-off mark follows the commit) and FEATURE/evidence/qa-gates/final-commit.md (written after the commit), and no path under QuickFiler/, QuickFiler.Test/ or scripts/. The final-commit artifact and this plan's check-off marks are committed by the orchestrator with the plan file. + +## Planner self-review and internal review record + +SELF-REVIEW: RE-DERIVED THIS PASS + +Citations re-derived in this pass against the item worktree tree at HEAD 8bbd48f68f9631247ccc3c58fa232ff333d4fe56 (content-line counts by line-oriented count): + +1. QuickFiler/Controllers/QfcDatamodel.cs — 483 lines; `[ExcludeFromCodeCoverage]` 25; loader assignments 40 and 51 (exactly two); `RemainingEmailLoader` declaration 140 followed by blank 141 and `#endregion Private Variables` 142; `Worker_DoWork` 185 to 229 with 205, 206, 207 and finally 209 to 216; `InitEmailQueue` 259 to 303 with start sites 273 and 300; `InitEmailQueueAsync` at 305; `WorkerStarter` absent. +2. QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs — 255 lines; `WaitForState` 47 to 57 (SpinWait at 56); test 1 97 to 110 block, waits 103 and 106; `StartHeldOpenLoader` 148 to 181; `release.SetResult(true);` 204, 220, 246; test 3 207 to 227; test 4 229 to 253; tokens `SpinWait` 1, `.Wait(` 2, `WaitForState` 5, `new BackgroundWorker()` 2. +3. QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs — 235 lines; `WaitForState` 59 to 67; `using` block 214 to 232 with waits 220 and 225; `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` 118 and 149; `fake.Advance(TimeSpan.FromSeconds(6))` 154; `new BackgroundWorker { ... }` at 174 does not match the `new BackgroundWorker()` token. +4. QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs — 212 lines; inert loader 97 to 108; Z0 117 to 133 (123, 127); Z1 doc 135 to 145 and method 146 to 164 (153, 161); Z2 176 to 210 (182, 201). +5. QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs — 458 lines; `GateTimeoutMs` constant 33; `[Timeout(GateTimeoutMs)]` 8 occurrences; R4 doc 192 to 203 with flake-watch para 196 to 202; R4 declared 206; 208 to 216 arrange block; `transactionA.Install(liveA);` unique at 216; `Dispatcher original = ...` also at 55 and 115; assertions 244 to 257; R5 declared 273. +6. QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs — `EnsureDispatcher` 122 to 138; design note 26 to 30; parked thread name 231. +7. QuickFiler.Test/Controllers/QfcItemController.TestSupport.cs 238 to 239; QuickFiler.Test/Controllers/QfcItemController.FocusAndThemeTests.cs class at 27, discards at 452 and 468; QuickFiler.Test/SetupAssemblyInitializer.cs 14 to 25 (no dispatcher write). +8. scripts/vscode/Invoke-MSTestWithCoverage.ps1 — 89 to 93, 262, 297 to 298, 348 to 355, 399 to 402, 406 to 410, 415 to 423, 430 to 453, 459 to 461; scripts/vscode/Invoke-MSTest.TrxSummary.ps1 12 to 150; scripts/vscode/Invoke-MSTestWithCoverage.FirstParty.ps1 117 to 120; scripts/vscode/TaskMaster.cli.runsettings 4 to 7; scripts/vscode/Install-RepoDotNetSdk.ps1 default version; scripts/vscode/Invoke-Restore.ps1 parameters. +9. QuickFiler.Test/QuickFiler.Test.csproj 35, 157, 161, 183, 203, 554 to 558; QuickFiler.Test/packages.config 11 and 52; QuickFiler/QuickFiler.csproj 24 and 325. +10. .gitignore 146, 150, 151; .csharpierignore 4 and 12; global.json 3 to 8; dotnet-tools.json 6; scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 line 21. +11. spec.md acceptance lines 266 to 282 (17 lines `- [ ] AC`); issue.md line 12; worktree reflog for the BASE cut and HEAD. + +Sibling-region re-checks: the three `release.SetResult(true);` lines in the liveness file (control edits A2 and A3 target the second and third only); the three `Dispatcher original = ...` lines in the R4 file (R-INJECT anchors on the unique `transactionA.Install(liveA);`); the teardown file's `Cleanup_CalledTwice_DoesNotThrow` worker at 174 (not a start site, not edited); the teardown `TimeSpan` lines that must survive (118, 149, 154); `QuickFiler/Interfaces/IQfcDatamodel.cs` (instrumented separately, excluded by the exact filename equality in CMD-COVERAGE-POST); the R4 `[Timeout]` attribute line, which R-BODY does not re-indent, so the added-line scan for `Timeout(` reads 0. + +PLANNER-INTERNAL-REVIEW: PASS +CITATION-TO-TREE: PASS +AC-TRACEABILITY: PASS +SCOPE-BOUNDARY: PASS +CITATION: QuickFiler/Controllers/QfcDatamodel.cs | lines 25, 40, 51, 140, 185-229, 259-303 (start sites 273 and 300) +CITATION: QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs | lines 47-57, 97-110, 148-181, 207-227, 229-253 +CITATION: QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs | lines 59-67, 214-232 +CITATION: QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs | lines 97-108, 117-133, 135-164, 176-210 +CITATION: QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs | lines 33, 192-203, 206-264, 216, 273 +CITATION: QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs | lines 26-30, 122-138, 231 +CITATION: QuickFiler.Test/SetupAssemblyInitializer.cs | lines 14-25 +CITATION: scripts/vscode/Invoke-MSTestWithCoverage.ps1 | lines 89-93, 262, 348-355, 410, 415-423, 449-453 +CITATION: scripts/vscode/TaskMaster.cli.runsettings | lines 4-7 +CITATION: scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 | line 21 +CITATION: docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md | lines 266-282 +AC-INVENTORY: AC1, AC2, AC3, AC4, AC5, AC6, AC7, AC8, AC9, AC10, AC11, AC12, AC13, AC14, AC15, AC16, AC17 +AC-MAPPING: AC1 | IMPLEMENTATION: P2-T1, P2-T2 | TESTS: P2-T4, P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md +AC-MAPPING: AC2 | IMPLEMENTATION: P2-T3 | TESTS: P2-T4, P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md +AC-MAPPING: AC3 | IMPLEMENTATION: P2-T1, P2-T2, P2-T3, P4-T1 | TESTS: P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md +AC-MAPPING: AC4 | IMPLEMENTATION: P3-T1 to P3-T11 | TESTS: P6-T8 | EVIDENCE: FEATURE/evidence/qa-gates/wall-clock-tokens.md +AC-MAPPING: AC5 | IMPLEMENTATION: P2-T1 to P3-T13 | TESTS: P6-T9 | EVIDENCE: FEATURE/evidence/qa-gates/prohibited-constructs.md +AC-MAPPING: AC6 | IMPLEMENTATION: P3-T1, P3-T2 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC7 | IMPLEMENTATION: P3-T1, P3-T3 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC8 | IMPLEMENTATION: P3-T1, P3-T3, P3-T4 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC9 | IMPLEMENTATION: P3-T1, P3-T3, P3-T5 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC10 | IMPLEMENTATION: P3-T6, P3-T7 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC11 | IMPLEMENTATION: P3-T8, P3-T10 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC12 | IMPLEMENTATION: P3-T8, P3-T9, P3-T11 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md +AC-MAPPING: AC13 | IMPLEMENTATION: P3-T12 | TESTS: P1-T6, P4-T4, P4-T5, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md +AC-MAPPING: AC14 | IMPLEMENTATION: P3-T13 | TESTS: P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md +AC-MAPPING: AC15 | IMPLEMENTATION: P5-T1 to P5-T17 | TESTS: P1-T6, P5-T11, P5-T16 | EVIDENCE: FEATURE/evidence/other/negative-controls-summary.md +AC-MAPPING: AC16 | IMPLEMENTATION: P1-T1 | TESTS: P1-T5 | EVIDENCE: FEATURE/evidence/other/ambient-synchronization-context.md +AC-MAPPING: AC17 | IMPLEMENTATION: P6-T1 to P6-T5 | TESTS: P6-T5 | EVIDENCE: FEATURE/evidence/qa-gates/toolchain-final-pass.md +UNRESOLVED-GAPS: NONE + +DIRECTIVE: PREFLIGHT VALIDATION ONLY +Executor preflight has not yet run on this plan; the signal below requests it and does not report a discovered defect. +PREFLIGHT: REVISIONS REQUIRED From e3827fb2c6c273099db62db3c59f10362a2d82c8 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 22:58:13 -0400 Subject: [PATCH 06/24] docs(950): take the R4 baseline pin inside the gate (preflight round 1) --- .../spec.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md index 446b8237f..bebcb312f 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md @@ -69,7 +69,7 @@ Maintainer-approved scope (binding): 1. **Defect A.** The three five-second waits in `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` (`SpinWait.SpinUntil` at line 56 via `WaitForState`, `Task.Wait` at lines 103 and 173) exist because `QfcDatamodel.InitEmailQueue` starts its worker through `BackgroundWorker.RunWorkerAsync()` (QfcDatamodel.cs lines 273 and 300) on an uncontrollable thread-pool thread. Fix with research option D1: an `internal Action WorkerStarter { get; set; }` seam on `QfcDatamodel`, assigned in both constructors to `worker => worker.RunWorkerAsync()`, mirroring the `RemainingEmailLoader` convention (null on `GetUninitializedObject` instances), replacing both `RunWorkerAsync` call sites. Tests assign a synchronous starter that raises `DoWork` on the calling thread through a test-side `BackgroundWorker` subclass exposing the protected `OnDoWork`, plus a test-owned drainable `SynchronizationContext` where a continuation must be observed after release. 2. **Same root cause, same seam, in scope.** `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` (waits at line 67 via `WaitForState` used at line 225, and line 220) and `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` (wait at line 161; the two other tests in that file start an unobserved thread-pool worker and must also assign the starter). Research section 2.5 holds the complete `InitEmailQueue` caller inventory. -3. **Defect B.** `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` (declared at line 206, failing assertion at lines 244-250). Root cause: `UiThreadDispatcherFixture.EnsureDispatcher()` writes the shared `UiThread._dispatcher` static without taking the transaction gate, racing `QfcItemController_FocusAndThemeTests` (`SetThemeDark_FromNormal_SelectsDarkNormalTheme` and `SetThemeLight_FromNormal_SelectsLightNormalTheme` call `EnsureUiThreadDispatcher` and discard the scope) under Workers=0 / ClassLevel. Test-only fix: pin a non-null baseline for the whole R4 body with `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()`, keep `NotBeSameAs(liveA)`, and update the R4 doc comment (lines 196-202) to state the cause, #950, and the W2/W5 residual-writer invariant. +3. **Defect B.** `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` (declared at line 206, failing assertion at lines 244-250). Root cause: `UiThreadDispatcherFixture.EnsureDispatcher()` writes the shared `UiThread._dispatcher` static without taking the transaction gate, racing `QfcItemController_FocusAndThemeTests` (`SetThemeDark_FromNormal_SelectsDarkNormalTheme` and `SetThemeLight_FromNormal_SelectsLightNormalTheme` call `EnsureUiThreadDispatcher` and discard the scope) under Workers=0 / ClassLevel. Test-only fix: pin a non-null baseline with `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()`, taken immediately after transaction A has acquired the gate (after `BeginTransactionAsync` returns, before the `original` read) and held through the end of the test, keep `NotBeSameAs(liveA)`, and update the R4 doc comment (lines 196-202) to state the cause, #950, and the W2/W5 residual-writer invariant. The pin is taken inside the gate rather than before `BeginTransactionAsync` so that a gated transaction from another class (W3/W4) cannot restore a null previous value between the pin and R4's acquisition; `EnsureDispatcher` is gate-free, so calling it while R4 holds the gate cannot deadlock (orchestrator decision 2026-10-02 on preflight round 1, defect 3). 4. **Prohibited.** Retries, `[DoNotParallelize]`, Workers=1, and longer timeouts. No `Thread.Sleep`, `Task.Delay`, or other wall-clock waits. No temporary files. 5. **Negative controls.** Each rewritten test needs a negative control that fails immediately rather than hanging. The mechanism for each test is stated in Test Strategy. 6. **Ambient context.** The ambient `SynchronizationContext` on the MSTest worker thread is an execution-time assumption to be recorded once during execution, not a blocker. @@ -128,7 +128,7 @@ Residual writers after the fix: W2 (`EnsureScope.Dispose()` holding the same par - `QuickFiler/Controllers/QfcDatamodel.cs`: add the D1 worker-start seam. The property is `internal Action WorkerStarter { get; set; }`, assigned in both constructors to `worker => worker.RunWorkerAsync()`. Both call sites in `InitEmailQueue` (lines 273 and 300) call `WorkerStarter(worker)` instead of `worker.RunWorkerAsync()`. Production behavior is unchanged. - The three datamodel test files assign `model.WorkerStarter` to a synchronous starter. The starter casts the worker to a test-side `SynchronousBackgroundWorker : BackgroundWorker` that exposes the protected virtual `OnDoWork(DoWorkEventArgs)` through a public `RaiseDoWork()` method. `OnDoWork` raises `DoWork` synchronously on the calling thread and so invokes the privately subscribed `Worker_DoWork` without reflection. `InitEmailQueue` then runs to `Worker_DoWork`'s first incomplete await before it returns. Every former wait becomes a synchronous assertion. - Where a test must observe the continuation after the loader is released (liveness tests 3 and 4), the test installs a test-owned drainable `SynchronizationContext` before invoking `InitEmailQueue`, releases the loader, drains the queue, and asserts synchronously. The context is restored in a `finally` (precedent: `ViewerScope` in ItemViewerBreadcrumbThreadAffinityTests.cs lines 271-292). -- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`: R4 wraps its whole body in `using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher())` before `BeginTransactionAsync`. The R4 `` (lines 196-202) is rewritten. +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`: R4 opens `using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher())` immediately after `BeginTransactionAsync` returns transaction A (before the `original` read at line 215) and holds it through the assertions. The R4 `` (lines 196-202) is rewritten. ### Boundaries and invariants to preserve: @@ -275,7 +275,7 @@ The #230 lost-update control for R4 (a fixture that releases the gate before res - [ ] AC10: `QuickFiler.Controllers.Tests.QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask` passes under the repository runsettings, reading the loader-entered signal and the captured `_remainingLoadTask` synchronously. - [ ] AC11: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` passes under the repository runsettings, reading the loader-invoked signal synchronously. - [ ] AC12: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` both assign a synchronous `WorkerStarter` and pass under the repository runsettings. -- [ ] AC13: `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` wraps its whole body in a `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()`, keeps both its `BeSameAs(original)` and `NotBeSameAs(liveA)` assertions, and passes under the repository runsettings. +- [ ] AC13: `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` opens a `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` after transaction A acquires the gate and before the `original` read, keeps both its `BeSameAs(original)` and `NotBeSameAs(liveA)` assertions, and passes under the repository runsettings. - [ ] AC14: The R4 doc comment no longer carries the flake-watch instruction and instead names the gate-free `EnsureDispatcher` writer as the cause, cites this issue, and states the W2/W5 residual-writer invariant. - [ ] AC15: Each test named in AC6 through AC13 has a recorded negative control, using the mechanism the Test Strategy table assigns to it, that fails immediately with an assertion or exception rather than hanging; each outcome is recorded as a Markdown summary in the feature folder's evidence/other directory. - [ ] AC16: The observed ambient `SynchronizationContext.Current` value on the MSTest worker thread is recorded once in the feature folder's evidence/other directory. From 8e8961f001c9ed9b6276595b6ad1837b2cc6a03c Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 23:18:39 -0400 Subject: [PATCH 07/24] docs(950): revise plan after preflight round 1 --- .../plan.2026-10-01T07-11.md | 234 ++++++++++++------ 1 file changed, 159 insertions(+), 75 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index fd802e9fc..d05d8372b 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -5,8 +5,9 @@ - **Owner:** drmoisan - **Work Mode:** full-bug (acceptance criteria come from `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` only; no user story exists for this item and none is to be authored) - **Last Updated:** 2026-10-01T07-11 -- **Status:** Ready for preflight -- **Version:** 1.0 +- **Status:** Ready for preflight (revision round 1 applied) +- **Version:** 1.1 +- **Revision R1:** applies executor preflight round 1 defects 1, 4, 5+6, 7 and 8 and the orchestrator's spec change committed at e3827fb2c (the R4 baseline pin is taken inside the gate, which supersedes preflight defects 2 and 3). Section "Revision R1 change log" below lists every change. - **Plan path continuity:** this file is updated in place for every preflight revision round. No timestamped sibling plan file is created for this cycle. **Fail-closed evidence rule:** every command-bearing task writes one evidence artifact carrying `Timestamp:`, `Command:`, `EXIT_CODE:` and `Output Summary:`. A task whose artifact is missing or incomplete stays unchecked, and the plan outcome is BLOCKED or INCOMPLETE, never PASS. @@ -23,6 +24,15 @@ Three delegation instructions are applied in an adjusted form. Each adjustment k 2. **Quote character of the prefix.** The caller's prefix uses single quotes around the path. Payloads containing `$` are passed in outer single quotes (`pwsh -NoProfile -Command '...'`), and the Bash channel cannot carry a single quote inside a single-quoted argument. The prefix is therefore written `Set-Location -LiteralPath "WORKTREE"`; PowerShell treats a double-quoted literal path that contains no `$` and no backtick identically to the single-quoted form. 3. **Location of negative-control records.** The caller places negative-control runs under `evidence/regression-testing/`; spec AC15 requires the outcomes as a Markdown summary under `evidence/other/`. Both are produced: one run artifact per control batch under `regression-testing/`, and the AC15 summary under `other/`. +## Revision R1 change log + +- Spec change (R4 pin inside the gate): Delivered Source R-BODY re-authored (four lines inserted after pre-edit line 214, pre-edit lines 215 to 258 re-indented, one closing line inserted after pre-edit line 258); R-DOC re-authored (cause, inside-the-gate rationale with W3/W4, W2/W5 invariant; `Issue #950:` once); the R-BODY gate token changed to a form that holds whichever way CSharpier lays out the `using` header; three new span anchors (`R4PRE`, `R4HEAD`, `R4TAIL`) prove the placement; fact 5, P0-T12, P3-T12, P3-T13, P3-T15, P5-T8, P5-T9 and P6-T24 updated; new section "Risks". +- Defect 1: L3 replaced with the executor's text (release source created with `TaskCreationOptions.RunContinuationsAsynchronously`), extended to also replace the stale test 2 summary (defect 8); D-2 appended; P3-T3 acceptance appended; census token list and rows updated; new negative-control batch C (P5-T18 to P5-T23: delete `pump.Drain();` in tests 3 and 4) proves the two tests depend on `Drain()`; former P5-T18 and P5-T19 are now P5-T24 and P5-T25. +- Defect 4: P6-T10 acceptance replaced. +- Defects 5 and 6: the "Long-running payloads" convention replaced; both coverage payloads end with `PAYLOAD-COMPLETE` (verified in their text). +- Defect 7: P3-T1 and P3-T6 acceptance replaced. +- Defect 8: liveness test 2 summary rewritten inside L3; zero-batch class remarks rewritten by new task P3-T14 (Delivered Source Z-R); the census formerly numbered P3-T14 is now P3-T15, and every reference to it is renumbered. + ## Requirement sources - Acceptance criteria: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, section `## Acceptance Criteria`, lines 266 to 282: seventeen checkbox lines `- [ ] AC1:` through `- [ ] AC17:`, each on one line. The check-off edit changes only `- [ ] ACn:` to `- [x] ACn:`. @@ -49,7 +59,7 @@ Feature documents: Evidence files, all new. Every name is fixed except the fail-before exception dossier, which carries the conventional timestamped name; the write time is the `Timestamp:` field (yyyy-MM-ddTHH-mm): - `FEATURE/evidence/baseline/`: `phase0-instructions-read.md`, `scope-and-anchor.md`, `bootstrap-sdk.md`, `bootstrap-tool-restore.md`, `bootstrap-nuget-restore.md`, `analyzer-alignment.md`, `bootstrap-dotnet-coverage.md`, `csharpier-check-baseline.md`, `msbuild-analyzer-baseline.md`, `msbuild-nullable-baseline.md`, `census-baseline.md`, `targets-baseline.md`, `concurrent-classes-baseline.md`, `stall-probe.md`, `coverage-baseline.md`, `phase0-commit.md` -- `FEATURE/evidence/regression-testing/`: `phase1-temporary-edits.md`, `phase1-build.md`, `r4-fail-before.md`, `phase1-revert.md`, `fail-before-exception..md` (exactly one), `pass-after-build.md`, `targets-pass-after.md`, `concurrent-classes-pass-after.md`, `controls-a-edits.md`, `controls-a-build.md`, `negative-controls-batch-a.md`, `controls-a-revert.md`, `controls-b-edits.md`, `controls-b-build.md`, `negative-controls-batch-b.md`, `controls-b-revert.md` +- `FEATURE/evidence/regression-testing/`: `phase1-temporary-edits.md`, `phase1-build.md`, `r4-fail-before.md`, `phase1-revert.md`, `fail-before-exception..md` (exactly one), `pass-after-build.md`, `targets-pass-after.md`, `concurrent-classes-pass-after.md`, `controls-a-edits.md`, `controls-a-build.md`, `negative-controls-batch-a.md`, `controls-a-revert.md`, `controls-b-edits.md`, `controls-b-build.md`, `negative-controls-batch-b.md`, `controls-b-revert.md`, `controls-c-edits.md`, `controls-c-build.md`, `negative-controls-batch-c.md`, `controls-c-revert.md` - `FEATURE/evidence/qa-gates/`: `production-seam-census.md`, `test-rewrite-census.md`, `scoped-format.md`, `post-format-census.md`, `implementation-commit.md`, `csharpier-format.md`, `csharpier-check-final.md`, `msbuild-analyzer-final.md`, `msbuild-nullable-final.md`, `coverage-post-change.md`, `coverage-comparison.md`, `toolchain-final-pass.md`, `wall-clock-tokens.md`, `prohibited-constructs.md`, `footprint-scope.md`, `evidence-hygiene.md`, `final-commit.md` - `FEATURE/evidence/other/`: `ambient-synchronization-context.md`, `negative-controls-summary.md`, `ac-status-summary.md` @@ -73,7 +83,7 @@ Each ID names one checkbox in the spec's `## Acceptance Criteria` section, in do | AC10 | 275 | Worker_DoWork_CapturesRemainingLoadTask passes | same three artifacts | | AC11 | 276 | InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker passes | same three artifacts | | AC12 | 277 | the two other zero-batch-file tests both assign a synchronous WorkerStarter and pass | same three artifacts | -| AC13 | 278 | Transaction_SecondCallerCannotInstallUntilTheFirstRestores wraps its whole body in a using | same three artifacts plus `regression-testing/concurrent-classes-pass-after.md` | +| AC13 | 278 | Transaction_SecondCallerCannotInstallUntilTheFirstRestores opens a using over EnsureUiThreadDispatcher after transaction A acquires the gate | same three artifacts plus `regression-testing/concurrent-classes-pass-after.md` | | AC14 | 279 | The R4 doc comment no longer carries the flake-watch instruction | `qa-gates/post-format-census.md` | | AC15 | 280 | Each test named in AC6 through AC13 has a recorded negative control | `other/negative-controls-summary.md` | | AC16 | 281 | The observed ambient SynchronizationContext.Current value is recorded once | `other/ambient-synchronization-context.md` | @@ -84,23 +94,23 @@ Each ID names one checkbox in the spec's `## Acceptance Criteria` section, in do Line totals below are content-line counts (the count a line-oriented reader returns for a file ending in a newline). 1. `QuickFiler/Controllers/QfcDatamodel.cs` is 483 lines. `[ExcludeFromCodeCoverage]` at 25 on `public partial class QfcDatamodel` at 26. The private constructor 34 to 41 assigns `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` at 40; the public constructor 43 to 52 assigns it at 51; that literal occurs exactly twice. `RemainingEmailLoader` property documentation 126 to 139, declaration `internal Func> RemainingEmailLoader { get; set; }` at 140, blank 141, `#endregion Private Variables` at 142. `SetupWorker` 176 to 183 subscribes `Worker_DoWork` at 181. `Worker_DoWork` 185 to 229 (`async void`): loader call 205, capture 206, `e.Result = await loaderTask;` 207, `finally` 209 to 216 clears `_remainingLoadActive` at 215, `catch` 225 to 228. `InitEmailQueue` 259 to 303: zero-batch branch 267 to 275 with `_remainingLoadActive = true;` 272 and `worker.RunWorkerAsync();` 273; positive-batch `_remainingLoadActive = true;` 299 and `worker.RunWorkerAsync();` 300. `InitEmailQueueAsync` begins at 305. The file has exactly two lines whose trimmed text is `worker.RunWorkerAsync();` and no occurrence of `WorkerStarter`. Usings include System, System.ComponentModel (5) and Microsoft.Office.Interop.Outlook (14); the Outlook namespace declares a non-generic `Action`, which does not collide with the generic `Action` because C# name lookup matches type arity. -2. `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` is 255 lines, namespace `QuickFiler.Controllers.Tests`, class `QfcDatamodelLivenessTests` at 26. `WaitForState` documentation and body 47 to 57 with `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` at 56. Test 1 `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` 79 to 138: `new BackgroundWorker()` 97, `InitEmailQueue` 100, `.Task.Wait(TimeSpan.FromSeconds(5))` 103, `WaitForState` 106 to 110, `fake.Advance` 113, 115 and 130. `ReadLivenessFlag` 140 to 146. `StartHeldOpenLoader` documentation 148 to 152, method 153 to 181 (`new BackgroundWorker()` 169, `InitEmailQueue` 170, wait 173, `WaitForState` 176 to 179). Test 2 183 to 205 (`release.SetResult(true);` 204). Test 3 207 to 227 (`release.SetResult(true);` 220, `WaitForState` 223 to 226). Test 4 229 to 253 (`release.SetResult(true);` 246, `WaitForState` 249 to 252). Class closes at 254. Counts: `SpinWait` 1, `.Wait(` 2, `WaitForState` 5, `new BackgroundWorker()` 2. +2. `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` is 255 lines, namespace `QuickFiler.Controllers.Tests`, class `QfcDatamodelLivenessTests` at 26. `WaitForState` documentation and body 47 to 57 with `SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5))` at 56. Test 1 `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` 79 to 138: `new BackgroundWorker()` 97, `InitEmailQueue` 100, `.Task.Wait(TimeSpan.FromSeconds(5))` 103, `WaitForState` 106 to 110, `fake.Advance` 113, 115 and 130. `ReadLivenessFlag` 140 to 146. `StartHeldOpenLoader` documentation 148 to 152, method 153 to 181 (`new BackgroundWorker()` 169, `InitEmailQueue` 170, wait 173, `WaitForState` 176 to 179). Blank line 182. Test 2 183 to 205: summary 183 to 187 (stale after the rewrite: line 185 says `BackgroundWorker.IsBusy` "has already gone false"), `release.SetResult(true);` 204. Test 3 207 to 227 (`release.SetResult(true);` 220, `WaitForState` 223 to 226). Test 4 229 to 253 (`release.SetResult(true);` 246, `WaitForState` 249 to 252). Class closes at 254. Counts: `SpinWait` 1, `.Wait(` 2, `WaitForState` 5, `new BackgroundWorker()` 2, `IsBusy` 6 (73, 107, 108, 151, 177, 185; line 73 is test 1's issue #424 description of production behavior and stays). No occurrence of `TaskCreationOptions.RunContinuationsAsynchronously`, `pump.Drain();` or `SynchronizationContext.SetSynchronizationContext(previous);`. 3. `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` is 235 lines. `WaitForState` documentation and expression body 59 to 67 (`SpinWait.SpinUntil` at 67). `Cleanup_CalledTwice_DoesNotThrow` uses `new BackgroundWorker { WorkerSupportsCancellation = true }` at 174 and starts no worker. `Worker_DoWork_CapturesRemainingLoadTask` 196 to 233: the `using (var worker = new BackgroundWorker())` block 214 to 232 with `InitEmailQueue` 218, `.Task.Wait(TimeSpan.FromSeconds(5))` 220, `WaitForState` 225 to 229, `loaderRelease.TrySetResult(true);` 231. `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` at 118 and 149, `fake.Advance(TimeSpan.FromSeconds(6));` at 154. Counts: `SpinWait` 1, `.Wait(` 1, `WaitForState` 2, `new BackgroundWorker()` 1. -4. `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` is 212 lines. `CreateInertRemainingEmailLoader` 89 to 108 (completes its source and returns `Task.FromResult(true)`). Z0 `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` 117 to 133 (loader assignment 123, `new BackgroundWorker()` in the act lambda 127). Z1 `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` documentation 135 to 145, method 146 to 164 (`new BackgroundWorker()` 153, `.Task.Wait(TimeSpan.FromSeconds(5))` 161). Z2 `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` 176 to 210 (loader assignment 182, `new BackgroundWorker()` 201). Counts: `SpinWait` 0, `.Wait(` 1, `WaitForState` 0, `new BackgroundWorker()` 3. -5. `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` is 458 lines. `private const int GateTimeoutMs = 60000;` at 33; `[Timeout(GateTimeoutMs)]` occurs 8 times. R4 documentation 192 to 203 with the flake-watch `` 196 to 202 (`flake-watch` at 199, `Append an observation` at 200). R4 `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` declared at 206; body 207 to 264: `// Arrange` 208, `Dispatcher liveA = ...` 209, `try` 210, `Dispatcher original = UiThreadDispatcherFixture.Current;` 215, `transactionA.Install(liveA);` 216 (the only occurrence in the file), `BeSameAs(original)` 244 to 250, `NotBeSameAs(liveA)` 251 to 257, `finally` 260 to 263. R5 `Transaction_DisposedTwice_DoesNotOverReleaseTheGate` is declared at 273. The literal `Dispatcher original = UiThreadDispatcherFixture.Current;` also occurs at 55 and 115, so it is not a unique anchor. +4. `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` is 212 lines. Class remarks 23 to 31 (`/// ` 23, `/// ` 31); line 24 says every test "starts a real" `BackgroundWorker`, which is stale after the rewrite (the token `starts a real` occurs once, at 24). `IsBusy` occurs twice (139, 141, both inside the Z1 documentation that Z1 replaces). `CreateInertRemainingEmailLoader` 89 to 108 (completes its source and returns `Task.FromResult(true)`). Z0 `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` 117 to 133 (loader assignment 123, `new BackgroundWorker()` in the act lambda 127). Z1 `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` documentation 135 to 145, method 146 to 164 (`new BackgroundWorker()` 153, `.Task.Wait(TimeSpan.FromSeconds(5))` 161). Z2 `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` 176 to 210 (loader assignment 182, `new BackgroundWorker()` 201). Counts: `SpinWait` 0, `.Wait(` 1, `WaitForState` 0, `new BackgroundWorker()` 3. +5. `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` is 458 lines. `private const int GateTimeoutMs = 60000;` at 33; `[Timeout(GateTimeoutMs)]` occurs 8 times. R4 documentation 192 to 203 with the flake-watch `` 196 to 202 (`flake-watch` at 199, `Append an observation` at 200). R4 `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` attributes `[TestMethod]` 204 and `[Timeout(GateTimeoutMs)]` 205, declared at 206; body 207 to 264: `{` 207, `// Arrange` 208, `Dispatcher liveA = ...` 209, `try` 210, `{` 211, transaction A acquisition 212 to 214 (`UiThreadDispatcherTransaction transactionA = await UiThreadDispatcherFixture` 212, `.BeginTransactionAsync()` 213, `.ConfigureAwait(false);` 214, all at sixteen spaces except the two chained lines at twenty), `Dispatcher original = UiThreadDispatcherFixture.Current;` 215, `transactionA.Install(liveA);` 216 (the only occurrence in the file), blank 217, `using (var secondCallerStarted = new ManualResetEventSlim(false))` 218 (the only `using (` between 206 and 273), blank lines 221, 237 and 242, transaction B acquisition 225 to 227, `BeSameAs(original)` 244 to 250, `NotBeSameAs(liveA)` 251 to 257 with `+ "issue #230 lost update"` at 256 (the only occurrence of `issue #230 lost update` in the file), `}` 258 (closes the `secondCallerStarted` using, sixteen spaces), `}` 259 (closes the `try`, twelve spaces), `finally` 260, `{` 261, `QfcItemControllerTestSupport.ShutdownDispatcher(liveA);` 262, `}` 263, method close 264. Between 256 and 261, exactly two lines contain `}` (258 and 259). R5 `Transaction_DisposedTwice_DoesNotOverReleaseTheGate` is declared at 273. The literal `Dispatcher original = UiThreadDispatcherFixture.Current;` also occurs at 55 and 115, so it is not a unique anchor; the first occurrence after line 206 is 215. The first `.BeginTransactionAsync()` after 206 is 213. Precedent for an ensure scope taken while a transaction holds the gate: R2 and R3 call `IDisposable ensureScope = QfcItemControllerTestSupport.EnsureUiThreadDispatcher();` at 119 and 166, each after their own `BeginTransactionAsync` (111, 161). The file has no `Issue #950` text and no `W3` or `W4` text. 6. `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs`: `EnsureDispatcher` 122 to 138 seeds the parked dispatcher only into a null field and never takes the transaction gate (design note 26 to 30); the parked thread is named `UiThreadDispatcherFixture.ParkedDispatcher` (231). `QfcItemController.TestSupport.cs` 238 to 239: `EnsureUiThreadDispatcher()` wraps it. `QfcItemController.FocusAndThemeTests.cs` declares class `QfcItemController_FocusAndThemeTests` (27) and discards the scope at 452 and 468. None is modified. 7. `QuickFiler.Test/SetupAssemblyInitializer.cs` `[AssemblyInitialize]` (14 to 25) installs an assembly resolver and WinForms rendering defaults and does not write `UiThread._dispatcher`, so R4 run alone by fully qualified name starts with a null baseline. 8. `scripts/vscode/TaskMaster.cli.runsettings` lines 4 to 7 set Workers 0 and Scope ClassLevel. `scripts/vscode/Invoke-MSTestWithCoverage.ps1` (461 lines): `Get-DotnetCoverageArgumentList` appends `/InIsolation`, `/TestCaseFilter:TestCategory!=LiveOutlook`, the results directory and the trx logger at 89 to 93 with no extension point; `Invoke-DotnetCoverageCollection` throws `MSTest with coverage failed with exit code` at 262 after the collector exits non-zero, before post-processing; defaults `coverage\test-results` and `mstest-coverage-run.trx` at 297 to 298; discovery 348 to 355 filters `bin\Debug` and a `.claude` segment relative to the search root; post-processing 399 to 402; threshold gate 406 to 409; `First-party coverage:` line printed at 410; JaCoCo projection 415 to 423; trx summary 430 to 447; raw document retained when written to the repository coverage directory 449 to 453; entry guard 459 to 461, so dot-sourcing is safe. `Invoke-MSTest.TrxSummary.ps1`: `Get-TrxRunSummary` 12 to 101, `Format-TrxRunSummary` 103 to 150 (its last line is `Failed tests: ` followed by names or `none`). `Invoke-MSTestWithCoverage.FirstParty.ps1` 117 to 120 renders `First-party coverage: lines a/b (p%), branches c/d (q%)`. 9. `QuickFiler.Test/QuickFiler.Test.csproj` lists the four test files explicitly (157, 161, 183, 203), `OutputPath` `bin\Debug\` (35), analyzer items 530 to 561 including `Meziantou.Analyzer.3.0.290` (554) and `Roslynator.Analyzers.5.0.0` (555 to 558); `QuickFiler.Test/packages.config` pins Meziantou.Analyzer 3.0.290 (11) and Roslynator.Analyzers 5.0.0 (52). `QuickFiler/QuickFiler.csproj` lists `Controllers\QfcDatamodel.cs` at 325, `OutputPath` `bin\Debug\` (24). 10. `.gitignore`: `*.trx` at 146, `coverage/*` at 150, `!coverage/.gitkeep` at 151. `.csharpierignore` excludes `**/evidence/**` (4) and `*.csproj` (12); there is no `.csharpierrc`, so the default print width of 100 applies. `global.json` pins SDK 8.0.205 under `.dotnet-sdk` with `latestFeature` roll-forward (3 to 8); `dotnet-tools.json` pins csharpier 1.2.6 (6). `scripts/vscode/Install-RepoDotNetSdk.ps1` defaults to version 8.0.205; `scripts/vscode/Invoke-Restore.ps1` takes SolutionPath, Configuration and Platform. -11. Branch `bug/quickfiler-tests-depend-on-wall-clock-timing-950`, cut at `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` (worktree reflog line 1); at authoring, HEAD is `8bbd48f68f9631247ccc3c58fa232ff333d4fe56` after four documentation commits. HEAD is recorded as an observation in P0-T3, never as an expectation. `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` exists on the tree. The `.dotnet-sdk`, `packages` and `bin` trees are absent (orchestrator environment fact 1), so every bootstrap task is guarded and gated on its post-task marker. +11. Branch `bug/quickfiler-tests-depend-on-wall-clock-timing-950`, cut at `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` (worktree reflog line 1); at the original authoring, HEAD was `8bbd48f68f9631247ccc3c58fa232ff333d4fe56` after four documentation commits; for revision R1 the delegation states HEAD `e3827fb2c`, the orchestrator's spec-only commit (spec.md lines 72, 131 and AC13 at 278 re-read in this pass; the acceptance-criteria lines are still 266 to 282). HEAD is recorded as an observation in P0-T3, never as an expectation. `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` exists on the tree. The `.dotnet-sdk`, `packages` and `bin` trees are absent (orchestrator environment fact 1), so every bootstrap task is guarded and gated on its post-task marker. 12. Host constraint carried from sibling items in this parallel run: four UtilitiesCS.Test shell-icon classes (`HelperClasses.ShellUtilities_Tests`, `HelperClasses.ShellUtilitiesStatic_Tests`, `HelperClasses.SysImageListHelperTests`, `EmailIntelligence.OSBrowser_Tests`) have stalled vstest on this workstation, and CI executes them. Whether the stall reproduces today is unknown, so P0-T15 measures it and the result selects the coverage route (D-6). ## Design decisions (do not redesign) - **D-1 Production seam (research option D1, spec Proposed Fix).** `QfcDatamodel` gains `internal Action WorkerStarter { get; set; }` with the XML documentation in Delivered Source S1, inserted after line 140. Both constructors assign `WorkerStarter = worker => worker.RunWorkerAsync();` immediately after their `RemainingEmailLoader` assignment (S2). Both start sites in `InitEmailQueue` become `WorkerStarter(worker);` (S3). Nothing else in the file changes. Net growth is 12 lines (483 to 495); the diff against the anchor is 14 added and 2 deleted lines. -- **D-2 Test helpers are private nested types per file.** `SynchronousBackgroundWorker : BackgroundWorker` exposes `RaiseDoWork()`, which calls the protected `OnDoWork(new DoWorkEventArgs(null))`, and the static method `StartSynchronously(BackgroundWorker worker)` casts and raises. Both live in each of the three datamodel test files. `DrainableSynchronizationContext` lives only in the liveness file (only tests 3 and 4 observe a continuation after release). No file is added and no project file changes. -- **D-3 Fail-before evidence.** Defect B: P1-T6 runs the deterministic R4 repro from the spec's Test Strategy table on the unmodified test (one discarded `QfcItemControllerTestSupport.EnsureUiThreadDispatcher();` inserted immediately before `transactionA.Install(liveA);`, test run alone by fully qualified name), tagged `[expect-fail]`; P4-T6 pairs it with the pass-after results. Defect A: the failure needs the thread pool to delay the worker body past a five-second bound, which no policy-compliant test can force, so P1-T8 writes a fail-before exception dossier and P5-T18 appends the post-fix deterministic controls to it. +- **D-2 Test helpers are private nested types per file.** `SynchronousBackgroundWorker : BackgroundWorker` exposes `RaiseDoWork()`, which calls the protected `OnDoWork(new DoWorkEventArgs(null))`, and the static method `StartSynchronously(BackgroundWorker worker)` casts and raises. Both live in each of the three datamodel test files. `DrainableSynchronizationContext` lives only in the liveness file (only tests 3 and 4 observe a continuation after release). No file is added and no project file changes. `StartHeldOpenLoader` creates its release source with `TaskCreationOptions.RunContinuationsAsynchronously`, so the loader's continuation is posted to the installed context and runs only inside `Drain()`. With a default source the continuation runs inline inside `SetResult` and `Drain()` runs nothing (measured during preflight). Negative-control batch C (P5-T18 to P5-T23) deletes `pump.Drain();` from tests 3 and 4 and requires both to fail, so a regression to inline continuations is observable. +- **D-3 Fail-before evidence.** Defect B: P1-T6 runs the deterministic R4 repro from the spec's Test Strategy table on the unmodified test (one discarded `QfcItemControllerTestSupport.EnsureUiThreadDispatcher();` inserted immediately before `transactionA.Install(liveA);`, test run alone by fully qualified name), tagged `[expect-fail]`; P4-T6 pairs it with the pass-after results. Defect A: the failure needs the thread pool to delay the worker body past a five-second bound, which no policy-compliant test can force, so P1-T8 writes a fail-before exception dossier and P5-T24 appends the post-fix deterministic controls to it. - **D-4 AC16 observation.** A temporary probe test (Delivered Source P-PROBE) is added to the unmodified liveness file in Phase 1, run alone under the repository runsettings, and reverted. The probe always fails by construction, so its exit code is deterministic (1) and its failure message carries the observed value after `AMBIENT-SYNC-CONTEXT=`. The probe is never committed. - **D-5 Temporary edits and byte-level reverts.** Phase 1 edits are applied to the tree as committed at P0-T17; Phase 5 edits are applied to the tree as committed at P4-T7. Each batch is reverted with `git restore --source=HEAD --worktree` and verified by `git diff --exit-code HEAD -- ` exiting 0 and `git status --porcelain -- ` printing nothing. A control that hangs instead of failing is a defect in the rewrite (spec Test Strategy): every direct vstest run carries the hang-dump blame switch, and a `Sequence_*.xml` file or a Timeout or Aborted outcome is a stop. - **D-6 Coverage route is selected by a recorded observation.** P0-T15 runs the four shell-icon classes alone and records `STALL-PROBE: CLEAR` or `REPRODUCES`. `COVERAGE-ROUTE: RUNNER` (CLEAR) runs `scripts/vscode/Invoke-MSTestWithCoverage.ps1` verbatim (CLAUDE.md step 4). `COVERAGE-ROUTE: DIRECT` (REPRODUCES) issues the runner's own inner collector invocation with the four-class exclusion appended and post-processes with the runner's own helpers, because the runner hard-codes its filter (fact 8). Both routes yield the same committed forms: the `First-party coverage:` line, the JaCoCo package projection text and the trx-derived summary, transcribed into Markdown. Under DIRECT, AC17 cannot be met as worded (the runner was not run) and its check-off records `AC17: NOT MET (ENVIRONMENTAL: COVERAGE-ROUTE DIRECT)`. @@ -112,9 +122,14 @@ Line totals below are content-line counts (the count a line-oriented reader retu - **D-12 Check-offs follow the loop.** Every check-off task sits in Phase 6 after the final toolchain pass and reads an artifact that survived it. Each flips exactly one checkbox and, when its evidence does not hold, completes with the box unchecked and records `ACn: NOT MET` with the reason in `FEATURE/evidence/other/ac-status-summary.md`. - **D-13 Restart rules.** Phase 4: if P4-T4 or P4-T5 shows a target test not `Passed`, the executor corrects the Write Set file at fault (within the delivered design; no prohibited construct) and restarts at P4-T1, recording `P4-RESTART: n` in the restarted artifacts. Phase 6: `ITERATION` starts at 1. If P6-T1 rewrites any Write Set file, the executor commits exactly the rewritten Write Set files (`style(950): apply csharpier output`), increments ITERATION and restarts at P6-T1. If P6-T2, P6-T3, P6-T4 or P6-T5 fails because of a Write Set file, the executor corrects it, restarts at P4-T1 (scoped format, build, pass-after runs, commit), re-runs Phase 5 in full, increments ITERATION and resumes at P6-T1. A rewrite of a file outside the Write Set, or a failure not attributable to the Write Set beyond the D-8 baseline rule, is a stop with the failing artifact. P6-T7 records the final iteration only. +## Risks (recorded; no mitigation is in scope) + +- **R4 pin-scope disposal can null the field under a concurrent theme test.** When R4's `baseline` scope disposes at the end of R4 it can reset `UiThread._dispatcher` to null (it does so whenever the pin seeded the parked dispatcher, which is the case whenever the field was null at the pin). A concurrently running `QfcItemController_FocusAndThemeTests` theme test (`SetThemeDark_FromNormal_SelectsDarkNormalTheme` or `SetThemeLight_FromNormal_SelectsLightNormalTheme`) whose discarded `EnsureUiThreadDispatcher()` ran during R4 installed nothing (the field was non-null), so after R4's disposal it can throw from `UiThread.Dispatcher`. This exposure pre-exists through the R2 and R3 scope disposals and through transaction restores, which reset the field the same way; it is out of scope for this issue. If it is observed in P4-T5 or P6-T5 (either theme test not `Passed` and absent from the corresponding baseline failed set, P0-T14 `BASELINE-CONCURRENT-FAILED:` or P0-T16 `BASELINE-FAILED-SET:`), the run stops and reports it with the failing artifact and the test's `MESSAGE` line as `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED`. This stop takes precedence over the D-13 restart rule; the Write Set is not expanded and FocusAndThemeTests.cs is not edited. +- **CSharpier layout of the re-indented R4 body.** The four-space re-indent pushes some R4 lines past the default width of 100 (for example the transaction B acquisition, pre-edit 225 to 227). The P4-T1 scoped format may reflow them; its output wins. No gate depends on that layout: every R4 gate token is confined to a line whose text CSharpier does not split (see R-BODY), and the R4 file's line count is recorded, not fixed. + ## Delivered source (the executor writes these texts; CSharpier output wins on any layout difference) -Every block below except R-INJECT is shown at its in-file indentation (eight, twelve or sixteen leading spaces), so it is written exactly as shown; R-INJECT states its indentation with it. Prose-quoted tokens used by later gates are each confined to one physical line of the delivered text. +Every block below except R-INJECT is shown at its in-file indentation (four, eight, twelve, sixteen or twenty leading spaces), so it is written exactly as shown; R-INJECT states its indentation with it. Prose-quoted tokens used by later gates are each confined to one physical line of the delivered text. **S1 — `QuickFiler/Controllers/QfcDatamodel.cs`, inserted after line 140** (one blank line, then nine lines): @@ -195,13 +210,16 @@ Gate tokens quoted from S1: Injectable worker-start seam; null on instances buil .Task.IsCompleted.Should() .BeTrue("the synchronous starter must reach the injected RemainingEmailLoader"); -**L3 — the same file, replaces lines 148 to 181** (`StartHeldOpenLoader` documentation and method): +**L3 — the same file, replaces lines 148 to 187** (`StartHeldOpenLoader` documentation and method 148 to 181, blank line 182, and the stale test 2 summary 183 to 187; test 2's `[TestMethod]` at 188 and everything after it up to test 3 is unchanged). The block ends with test 2's rewritten summary: /// /// Starts the worker with a RemainingEmailLoader held open by /// . The issue #950 synchronous starter raises DoWork on /// this thread, so by the time InitEmailQueue returns the async void /// Worker_DoWork has entered the loader and returned at its first incomplete await. + /// runs its continuations asynchronously, so a test that has + /// installed DrainableSynchronizationContext observes the resumed loader only + /// through Drain, never inline inside SetResult. /// private static QfcDatamodel StartHeldOpenLoader( Func, Task> loaderBody, @@ -210,7 +228,9 @@ Gate tokens quoted from S1: Injectable worker-start seam; null on instances buil { var model = CreateUninitializedDatamodel(); var entered = new TaskCompletionSource(); - var localRelease = new TaskCompletionSource(); + var localRelease = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously + ); release = localRelease; model.RemainingEmailLoader = _ => @@ -229,6 +249,15 @@ Gate tokens quoted from S1: Injectable worker-start seam; null on instances buil return model; } + /// + /// AC 7: the flag stays true across the async void first-await boundary while the + /// loader is still producing. Issue #950: the synchronous starter has already run + /// Worker_DoWork to that boundary when InitEmailQueue returns, so the flag + /// is read with no wait. + /// + +The block above is forty-three lines replacing forty (net +3). No line of it contains `IsBusy`, `.Wait(`, `WaitForState` or `pump.Drain();`. Gate tokens quoted from L3: TaskCreationOptions.RunContinuationsAsynchronously; the synchronous starter must reach the injected loader before returning. + **L4 — the same file, replaces lines 207 to 227** (test 3 documentation and method): /// @@ -404,29 +433,57 @@ Gate tokens quoted from S1: Injectable worker-start seam; null on instances buil **Z2 — the same file, test `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`:** insert ` model.WorkerStarter = StartSynchronously;` immediately after this test's line ` model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out _);` (pre-edit line 182), and on the act line (pre-edit line 201) replace `new BackgroundWorker()` with `new SynchronousBackgroundWorker()`. -**R-DOC — `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`, replaces lines 196 to 202** (the R4 ``): +**Z-R — the same file, replaces the class remarks, lines 23 to 31** (four-space indent; thirteen lines replacing nine, net +4; applied by P3-T14 after every other zero-batch edit, and lines 23 to 31 lie above every other zero-batch edit site, so their numbering is the pre-edit numbering): + + /// + /// v1.1 revision (issue #244): every test below assigns an inert, recording + /// delegate via the internal seam BEFORE + /// calling . Without this, + /// the started worker's Worker_DoWork reaches the real + /// LoadRemainingEmailsToQueueAsync, which pops a live + /// dialog and touches Outlook COM + /// (_olApp.GetNamespace("MAPI")) — this is the maintainer-reported defect in the v1.0 + /// revision of these tests, and this file must never reproduce it. Issue #950: every test also + /// assigns the WorkerStarter seam a starter that raises DoWork synchronously on + /// the test thread through the nested SynchronousBackgroundWorker, so no test starts a + /// thread-pool worker and none outlives the test. + /// + +No line of Z-R contains `starts a real`, `IsBusy`, `new BackgroundWorker()`, `new SynchronousBackgroundWorker()` or `WorkerStarter = StartSynchronously;`. Gate token quoted from Z-R: so no test starts a. + +**R-DOC — `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`, replaces lines 196 to 202** (the R4 ``; thirteen lines replacing seven, net +6). P3-T13 applies it after P3-T12, and R-BODY's first change is at line 214, below this region, so these are still the pre-edit line numbers when P3-T13 runs: /// /// Issue #950: the earlier intermittent failure was a race on the shared static, not a /// timing defect. The gate-free fixture method EnsureDispatcher seeds the parked dispatcher /// whenever the field is null, so a concurrently running class that calls it could write /// between the baseline read and the install, or between the restore and the second - /// caller's read. The test therefore pins a non-null baseline for its whole body. Invariant - /// for future editors: no other class may dispose an ensure scope holding the parked - /// dispatcher (W2), and UiThread.Initialize (W5) must not latch during this test; either - /// would change the value the second caller observes. + /// caller's read. The test therefore pins a non-null baseline with an ensure scope that it + /// opens only after transaction A has acquired the gate and holds through both assertions. + /// Taking the pin inside the gate means that a gated transaction from another class (W3/W4) + /// cannot restore a null previous value between the pin and this test's acquisition. + /// Invariant for future editors: no other class may dispose an ensure scope holding the + /// parked dispatcher (W2), and UiThread.Initialize (W5) must not latch during this test; + /// either would change the value the second caller observes. /// -Gate tokens quoted from R-DOC: Issue #950: the earlier intermittent failure was a race on the shared static; The gate-free fixture method EnsureDispatcher seeds the parked dispatcher; (W2), and UiThread.Initialize (W5) must not latch. +`Issue #950:` occurs once in R-DOC and nowhere else in the file after the change (R-BODY adds no comment). Gate tokens quoted from R-DOC, each confined to one line: Issue #950: the earlier intermittent failure was a race on the shared static; The gate-free fixture method EnsureDispatcher seeds the parked dispatcher; cannot restore a null previous value between the pin; (W2), and UiThread.Initialize (W5) must not latch. -**R-BODY — the same file, R4 body.** Line 208 (` // Arrange`) is replaced by the three lines below; lines 209 to 263 (from `Dispatcher liveA = ...` through the closing brace of the `finally` block) are re-indented by four spaces with no other character changed; and one line ` }` is inserted after line 263, so the method's closing brace (pre-edit 264) closes the method as before: +**R-BODY — the same file, R4 body (applied by P3-T12, before R-DOC, against the pre-edit numbering).** Three changes, nothing else: - // Arrange — issue #950: with the field non-null for the whole body, the gate-free - // EnsureDispatcher in any concurrently running class installs nothing. - using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()) - { +1. Insert these four lines immediately after pre-edit line 214 (`.ConfigureAwait(false);`, the line that completes transaction A's acquisition) and before pre-edit line 215 (`Dispatcher original = UiThreadDispatcherFixture.Current;`). The `using (`, `)` and `{` lines are at sixteen spaces and the declaration at twenty: + + using ( + IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher() + ) + { + +2. Re-indent pre-edit lines 215 to 258 inclusive (forty-four lines: from the `original` read through the `}` that closes the `secondCallerStarted` using block) by four additional leading spaces, with no other character changed; the blank lines among them (pre-edit 217, 221, 237 and 242) stay empty. +3. Insert one line consisting of sixteen spaces and `}` immediately after pre-edit line 258, closing the `baseline` using block before the `try` block's closing brace (pre-edit 259). + +Pre-edit lines 207 to 214 (including `// Arrange` at 208 and the transaction A acquisition) and 259 to 264 (the `try` close, the `finally` block and the method close) are unchanged. Net growth five lines (458 plus 5 plus R-DOC's 6 is 469 before formatting). The two assertions (pre-edit 244 to 257) are retained apart from indentation, so B's read (pre-edit 230) and both assertions run under the pin, and the pin is taken after transaction A holds the gate. -The two assertions at pre-edit lines 244 to 257 are retained unchanged apart from indentation. Gate token quoted from R-BODY: using (IDisposable baseline =. +The `using` header is shown split because the one-line form `using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher())` at sixteen spaces is 102 characters, over the default width of 100; the split form is the expected CSharpier layout, and the declaration line is 98 characters. If CSharpier lays the header out differently, its output wins: the gate token below is the declaration text, which is a substring of the header in the one-line form and the whole declaration line in the split form, so it holds in either layout. Gate token quoted from R-BODY: IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher(). **P-PROBE — temporary (Phase 1 only, never committed). The unmodified liveness file, inserted after line 253** (the closing brace of `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally`), one blank line then: @@ -445,7 +502,7 @@ The two assertions at pre-edit lines 244 to 257 are retained unchanged apart fro report.Should().Be("PROBE-ALWAYS-FAILS", report); } -**R-INJECT — temporary (Phase 1 on the unmodified file, Phase 5 on the fixed file; never committed).** One line inserted immediately before the unique line whose trimmed text is `transactionA.Install(liveA);`, at that line's indentation: +**R-INJECT — temporary (Phase 1 on the unmodified file, Phase 5 on the fixed file; never committed).** One line inserted immediately before the unique line whose trimmed text is `transactionA.Install(liveA);`, at that line's indentation (sixteen spaces on the unmodified file; twenty on the pinned file, where the inserted line sits inside the `baseline` using block, after the `original` read): QfcItemControllerTestSupport.EnsureUiThreadDispatcher(); @@ -456,7 +513,7 @@ The two assertions at pre-edit lines 244 to 257 are retained unchanged apart fro - **Command rows.** The `Command:` field of a payload artifact records the full payload as executed, with `WORKTREE` in place of the path, followed on the next line by the canonical command it implements (for example `msbuild TaskMaster.sln /t:Rebuild ...`). Every payload artifact records `WORKTREE-LEAF: agent-a7805823735145ca4`; any other value means the payload ran in the wrong tree, and the task fails. - **Exit codes.** `EXIT_CODE:` records the payload's principal exit value as named in each Command Reference entry. Deliberately failing runs carry `ExpectedExitCode:` equal to the deterministic value the task states. A recorded observation whose exit value is not gated carries `ExpectedExitCode:` equal to the observed value and says so. - **Transcription.** Any absolute path inside a transcribed line is replaced by `REDACTED-PATH`. Trx and coverage documents are never copied into FEATURE. -- **Long-running payloads.** `CMD-COVERAGE-RUNNER` and `CMD-COVERAGE-DIRECT` are started as background processes with their standard output redirected to `coverage\logs\STAGE-950.result.log`; completion is detected when that log's final line is `PAYLOAD-COMPLETE`. A run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report. Before any re-invocation after a timed-out foreground call, the executor runs `pwsh -NoProfile -Command '"STRAY_TEST_PROCESSES: " + @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -like "vstest*" -or $_.ProcessName -like "testhost*" -or $_.ProcessName -like "dotnet-coverage*" }).Count'` and proceeds only at `STRAY_TEST_PROCESSES: 0`; two collections never run at once. +- **Long-running payloads.** `CMD-COVERAGE-RUNNER` and `CMD-COVERAGE-DIRECT` are started with the Bash tool's `run_in_background` option and no shell redirection (a Bash `>` target resolves against the session checkout, not WORKTREE); the payload's own output, captured by the tool, is the record, and completion is the background-task notification together with a final output line `PAYLOAD-COMPLETE`. A run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report. Before any re-invocation after a timed-out or interrupted call, the executor runs `pwsh -NoProfile -Command '"STRAY_TEST_PROCESSES: " + @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { ($_.Name -like "vstest*" -or $_.Name -like "dotnet-coverage*") -and $null -ne $_.CommandLine -and $_.CommandLine.Contains("agent-a7805823735145ca4") }).Count'` and proceeds only at `STRAY_TEST_PROCESSES: 0`; the count is scoped to this worktree's leaf so test runs in sibling worktrees cannot hold the gate open, and two collections from this worktree never run at once. Both payloads, as written in the Command Reference, end with the unconditional line `Write-Output "PAYLOAD-COMPLETE"`, which runs on success and on a non-zero collector exit alike (a native command's exit code does not stop the payload); a run that ends without that line is incomplete and is treated as `COVERAGE RUN ABORTED`. - **No wall-clock construct anywhere.** No payload sleeps; no test edit adds a sleep, delay, retry, timeout change, parallelism attribute or temporary file. ## Command reference @@ -551,6 +608,7 @@ Substitutions: `ASSEMBLY-QF` is `QuickFiler.Test\bin\Debug\QuickFiler.Test.dll`; - `FILTER-R4`: `FullyQualifiedName=QCT.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores`. - `FILTER-CONTROLS-A`: the `FILTER-TARGETS` expressions for test 1, test 3, test 4, the teardown test, the three zero-batch tests and R4 (eight expressions; test 2 excluded). - `FILTER-CONTROLS-B`: `FullyQualifiedName=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces`. +- `FILTER-CONTROLS-C`: `FullyQualifiedName=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse|FullyQualifiedName=QCT.QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (tests 3 and 4). - `FILTER-STALL`: `FullyQualifiedName~HelperClasses.ShellUtilities_Tests|FullyQualifiedName~HelperClasses.ShellUtilitiesStatic_Tests|FullyQualifiedName~HelperClasses.SysImageListHelperTests|FullyQualifiedName~EmailIntelligence.OSBrowser_Tests`. **CMD-COVERAGE-RUNNER** (CLAUDE.md step 4 route; `STAGE` is `baseline` or `final`; `EXIT_CODE:` is `RUNNER_EXIT_CODE:`; canonical command `pwsh -NoProfile -File scripts\vscode\Invoke-MSTestWithCoverage.ps1`): @@ -671,7 +729,7 @@ The projection and the summary block are the two committed forms (CLAUDE.md "Com $span = $src[$s..($e - 1)] foreach ($t in @(TOKENS)) { Write-Output ("SPAN-TOKEN [" + $t + "] = " + @($span | Where-Object { $_.Contains($t) }).Count) } -Span anchors used by this plan: `INITQ` is START `public IList InitEmailQueue(` and END `public async Task> InitEmailQueueAsync(` in `QuickFiler\Controllers\QfcDatamodel.cs`; `R4SPAN` is START `public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores()` and END `public async Task Transaction_DisposedTwice_DoesNotOverReleaseTheGate()` in `QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs`. +Span anchors used by this plan: `INITQ` is START `public IList InitEmailQueue(` and END `public async Task> InitEmailQueueAsync(` in `QuickFiler\Controllers\QfcDatamodel.cs`; `R4SPAN` is START `public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores()` and END `public async Task Transaction_DisposedTwice_DoesNotOverReleaseTheGate()` in `QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs`. Three further anchors in the same file locate the R4 pin (fact 5): `R4PRE` is START the R4SPAN START and END `.BeginTransactionAsync()` (the span runs from the R4 declaration up to, not including, the first acquisition line of transaction A); `R4HEAD` is START the R4SPAN START and END `Dispatcher original = UiThreadDispatcherFixture.Current;` (from the R4 declaration up to, not including, the `original` read); `R4TAIL` is START `issue #230 lost update` and END `QfcItemControllerTestSupport.ShutdownDispatcher(liveA);` (from R4's last assertion argument up to, not including, the `finally` body). A pin token that is 0 in `R4PRE` and 1 in `R4HEAD` lies after transaction A's acquisition begins and before the `original` read; one more line containing `}` in `R4TAIL` than at BASE places the pin's closing brace after the last assertion and before `finally`. **CMD-TIMESPAN** (classifies every line of THREE that contains `TimeSpan.`): @@ -722,9 +780,9 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance, all required: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; `CSC_OUT_QUICKFILER:` and `CSC_OUT_QUICKFILER_TEST:` each at least 1; `TEST_DLL_EXISTS: True`; `UCS_TEST_DLL_EXISTS: True`; `WARNINGS:` recorded as `ANALYZER-BASELINE-WARNINGS:`; `WRITESET_DIAGNOSTIC_LINES:` and `WRITESET_DIAGNOSTIC_CODES:` recorded as `ANALYZER-BASELINE-WRITESET-LINES:` and `ANALYZER-BASELINE-WRITESET-CODES:` (the comparison basis for P6-T3). A non-zero exit is `ANALYZER BASELINE NOT CLEAN`: stop. - [ ] [P0-T11] Capture the nullable baseline with `CMD-REBUILD` (nullable GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable property override, `TASKID` p0-t11) and record it in FEATURE/evidence/baseline/msbuild-nullable-baseline.md. - Acceptance, all required: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. A non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop. -- [ ] [P0-T12] Record the pre-change census of the five code files in FEATURE/evidence/baseline/census-baseline.md, using `CMD-LINECOUNT`, `CMD-HASH`, `CMD-TIMESPAN`, `CMD-TOKEN-COUNT` once per code file and `CMD-SPAN-TOKEN-COUNT` for `INITQ` and `R4SPAN`. - - Token lists: for each of THREE, `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;"`; for QfcDatamodel.cs, `"WorkerStarter", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`; for the R4 file, `"flake-watch", "Append an observation", "Issue #950:", "[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`; for `INITQ`, `"RunWorkerAsync", "WorkerStarter(worker);"`; for `R4SPAN`, `"using (IDisposable baseline =", "EnsureUiThreadDispatcher()", ".BeSameAs(", ".NotBeSameAs("`. - - Acceptance (each value is derived in facts 1 to 5 and is a falsifiable pre-change observation): `WORKTREE-LEAF: agent-a7805823735145ca4`; LINES 483, 255, 235, 212, 458 in CODE5 order; five HASH values recorded as `BASE-HASH:` lines; liveness tokens 1, 2, 5, 2, 0, 0; teardown tokens 1, 1, 2, 1, 0, 0; zero-batch tokens 0, 1, 0, 3, 0, 0; QfcDatamodel.cs `WorkerStarter` 0, the loader-assignment literal 2, `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2`; R4 file tokens 1, 1, 0, 8, 1; `INITQ` `RunWorkerAsync` 2 and `WorkerStarter(worker);` 0; `R4SPAN` tokens 0, 0, 1, 1; `TIMESPAN-UNCLASSIFIED: 6` (liveness 56, 103, 173; teardown 67, 220; zero-batch 161). Any differing value is `CENSUS MISMATCH`: record and stop, because a later gate is defined against these values. The non-zero wall-clock counts are the positive control for the zero gates of P6-T8. +- [ ] [P0-T12] Record the pre-change census of the five code files in FEATURE/evidence/baseline/census-baseline.md, using `CMD-LINECOUNT`, `CMD-HASH`, `CMD-TIMESPAN`, `CMD-TOKEN-COUNT` once per code file and `CMD-SPAN-TOKEN-COUNT` for `INITQ`, `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL`. + - Token lists: for each of THREE, `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "IsBusy", "starts a real"`; for QfcDatamodel.cs, `"WorkerStarter", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`; for the R4 file, `"flake-watch", "Append an observation", "Issue #950:", "[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`; for `INITQ`, `"RunWorkerAsync", "WorkerStarter(worker);"`; for `R4SPAN`, `"IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()", "EnsureUiThreadDispatcher()", "using (", ".BeSameAs(", ".NotBeSameAs("`; for `R4PRE` and `R4HEAD`, `"EnsureUiThreadDispatcher()", "using ("`; for `R4TAIL`, `"}"`. + - Acceptance (each value is derived in facts 1 to 5 and is a falsifiable pre-change observation): `WORKTREE-LEAF: agent-a7805823735145ca4`; LINES 483, 255, 235, 212, 458 in CODE5 order; five HASH values recorded as `BASE-HASH:` lines; liveness tokens 1, 2, 5, 2, 0, 0, 6, 0; teardown tokens 1, 1, 2, 1, 0, 0, 0, 0; zero-batch tokens 0, 1, 0, 3, 0, 0, 2, 1; QfcDatamodel.cs `WorkerStarter` 0, the loader-assignment literal 2, `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2`; R4 file tokens 1, 1, 0, 8, 1; `INITQ` `RunWorkerAsync` 2 and `WorkerStarter(worker);` 0; `R4SPAN` tokens 0, 0, 1, 1, 1 with `SPAN: 206-272`; `R4PRE` 0 and 0 with `SPAN: 206-212`; `R4HEAD` 0 and 0 with `SPAN: 206-214`; `R4TAIL` 2 with `SPAN: 256-261`; `TIMESPAN-UNCLASSIFIED: 6` (liveness 56, 103, 173; teardown 67, 220; zero-batch 161). Any differing value is `CENSUS MISMATCH`: record and stop, because a later gate is defined against these values. The non-zero wall-clock counts are the positive control for the zero gates of P6-T8. - [ ] [P0-T13] Capture the pre-change run of the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p0-t13, `NAMES-TARGETS`) and record it in FEATURE/evidence/baseline/targets-baseline.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 9` with one `RESULT` line per target name and its outcome; `BASELINE-TARGETS-NOT-PASSED:` lists every non-Passed name with its `MESSAGE` line, or `NONE`. Outcomes are observations of the pre-fix, load-dependent tests and are not gated; `ExpectedExitCode:` carries the observed value when non-zero. - [ ] [P0-T14] Capture the pre-change concurrent run of the four target classes with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p0-t14, empty `NAMES`) and record it in FEATURE/evidence/baseline/concurrent-classes-baseline.md. @@ -756,7 +814,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`; then `CMD-HASH`. - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing; the five HASH values equal the P0-T12 `BASE-HASH:` values. - [ ] [P1-T8] Author the Defect A fail-before exception dossier FEATURE/evidence/regression-testing/fail-before-exception..md (timestamp yyyy-MM-ddTHH-mm at authoring). - - Required content: `Timestamp:`; `Defect: A (wall-clock waits on a thread-pool worker)`; `WhyFailingRunImpossible:` stating in one to three sentences that the Defect A failure occurs only when the thread pool delays the BackgroundWorker body past a five-second bound, and that no deterministic, policy-compliant test can force that delay (sleeps, delays and wall-clock waits are prohibited, and starving the thread pool changes process-wide state that other parallel classes share); and an `## Alternative proof` section citing (1) the P0-T12 census (`SpinWait` 1 and 1, `.Wait(` 2, 1 and 1, `WaitForState` 5 and 2, `TIMESPAN-UNCLASSIFIED: 6`), (2) the two direct start sites QfcDatamodel.cs lines 273 and 300 with `WorkerStarter` absent (P0-T12), (3) the recorded failure history from spec Context (two failures at the #944 P3-T8 gate, one in the #929 local run), and (4) a forward statement that P5-T18 appends the post-fix deterministic negative controls. + - Required content: `Timestamp:`; `Defect: A (wall-clock waits on a thread-pool worker)`; `WhyFailingRunImpossible:` stating in one to three sentences that the Defect A failure occurs only when the thread pool delays the BackgroundWorker body past a five-second bound, and that no deterministic, policy-compliant test can force that delay (sleeps, delays and wall-clock waits are prohibited, and starving the thread pool changes process-wide state that other parallel classes share); and an `## Alternative proof` section citing (1) the P0-T12 census (`SpinWait` 1 and 1, `.Wait(` 2, 1 and 1, `WaitForState` 5 and 2, `TIMESPAN-UNCLASSIFIED: 6`), (2) the two direct start sites QfcDatamodel.cs lines 273 and 300 with `WorkerStarter` absent (P0-T12), (3) the recorded failure history from spec Context (two failures at the #944 P3-T8 gate, one in the #929 local run), and (4) a forward statement that P5-T24 appends the post-fix deterministic negative controls. - Acceptance: exactly one file matching `fail-before-exception.*.md` exists under FEATURE/evidence/regression-testing/, and it carries `Timestamp:`, `WhyFailingRunImpossible:` and the `## Alternative proof` heading with items (1) to (4). ### Phase 2 — Production Seam in QfcDatamodel.cs @@ -773,47 +831,49 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 3 — Test Rewrites - [ ] [P3-T1] Replace lines 47 to 57 of QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (the `WaitForState` documentation and method) with Delivered Source L1. - - Acceptance (recorded by P3-T14): `WaitForState` occurs only at the four call sites still awaiting P3-T2 to P3-T5; one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private sealed class DrainableSynchronizationContext : SynchronizationContext`. + - Acceptance: (recorded by P3-T15) one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private sealed class DrainableSynchronizationContext : SynchronizationContext`. - [ ] [P3-T2] Replace the arrange-act-wait block of test 1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 97 to 110) with Delivered Source L2; lines from `Task> pending = ...` onward are unchanged. - - Acceptance (recorded by P3-T14): the test contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected RemainingEmailLoader`, and no `.Wait(` or `WaitForState`. -- [ ] [P3-T3] Replace `StartHeldOpenLoader` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 148 to 181) with Delivered Source L3. - - Acceptance (recorded by P3-T14): the method contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected loader before returning`. + - Acceptance (recorded by P3-T15): the test contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected RemainingEmailLoader`, and no `.Wait(` or `WaitForState`. +- [ ] [P3-T3] Replace `StartHeldOpenLoader` and its documentation, the following blank line and the summary of test 2 `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 148 to 187) with Delivered Source L3. + - Acceptance (recorded by P3-T15): the method contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected loader before returning`, and the method creates `localRelease` with `TaskCreationOptions.RunContinuationsAsynchronously`; test 2's summary no longer mentions `IsBusy` (the liveness `IsBusy` count is 1, the issue #424 line of test 1's documentation). - [ ] [P3-T4] Replace test 3 `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 207 to 227) with Delivered Source L4. - - Acceptance (recorded by P3-T14): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally around the awaited loader must clear the flag once it completes`. + - Acceptance (recorded by P3-T15): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally around the awaited loader must clear the flag once it completes`. - [ ] [P3-T5] Replace test 4 `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 229 to 253) with Delivered Source L5. - - Acceptance (recorded by P3-T14): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally must clear the flag on the throwing path too`. + - Acceptance (recorded by P3-T15): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally must clear the flag on the throwing path too`. - [ ] [P3-T6] Replace lines 59 to 67 of QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (the `WaitForState` documentation and method) with Delivered Source T1. - - Acceptance (recorded by P3-T14): one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker`; `WaitForState` remains only at the call site that P3-T7 removes. + - Acceptance: (recorded by P3-T15) one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker`. - [ ] [P3-T7] Replace the `using` block of `Worker_DoWork_CapturesRemainingLoadTask` in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (pre-edit lines 214 to 232) with Delivered Source T2. - - Acceptance (recorded by P3-T14): the teardown file has `SpinWait` 0, `.Wait(` 0, `WaitForState` 0, `new SynchronousBackgroundWorker()` 1 and `WorkerStarter = StartSynchronously;` 1; the four other tests in the file are unchanged (their `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` and `fake.Advance(TimeSpan.FromSeconds(6))` arguments stay). + - Acceptance (recorded by P3-T15): the teardown file has `SpinWait` 0, `.Wait(` 0, `WaitForState` 0, `new SynchronousBackgroundWorker()` 1 and `WorkerStarter = StartSynchronously;` 1; the four other tests in the file are unchanged (their `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` and `fake.Advance(TimeSpan.FromSeconds(6))` arguments stay). - [ ] [P3-T8] Insert Delivered Source Z-H into QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs after line 108. - - Acceptance (recorded by P3-T14): one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private static void StartSynchronously(BackgroundWorker worker) =>`. + - Acceptance (recorded by P3-T15): one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private static void StartSynchronously(BackgroundWorker worker) =>`. - [ ] [P3-T9] Apply Delivered Source Z0 to `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. - - Acceptance (recorded by P3-T14): the test assigns `model.WorkerStarter = StartSynchronously;` and its act lambda constructs `new SynchronousBackgroundWorker()`. + - Acceptance (recorded by P3-T15): the test assigns `model.WorkerStarter = StartSynchronously;` and its act lambda constructs `new SynchronousBackgroundWorker()`. - [ ] [P3-T10] Replace the documentation and method of `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z1. - - Acceptance (recorded by P3-T14): the test reads `loaderInvokedTcs.Task.IsCompleted` with the reason literal `the injected RemainingEmailLoader must be invoked by the started worker`; the documentation no longer mentions a bounded timeout. + - Acceptance (recorded by P3-T15): the test reads `loaderInvokedTcs.Task.IsCompleted` with the reason literal `the injected RemainingEmailLoader must be invoked by the started worker`; the documentation no longer mentions a bounded timeout. - [ ] [P3-T11] Apply Delivered Source Z2 to `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. - - Acceptance (recorded by P3-T14): the zero-batch file has `.Wait(` 0, `new BackgroundWorker()` 0, `new SynchronousBackgroundWorker()` 3 and `WorkerStarter = StartSynchronously;` 3. -- [ ] [P3-T12] Apply Delivered Source R-BODY (the baseline pin) to `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. - - Acceptance (recorded by P3-T14): within `R4SPAN`, `using (IDisposable baseline =` 1, `EnsureUiThreadDispatcher()` 1, `.BeSameAs(` 1 and `.NotBeSameAs(` 1; the `[Timeout(GateTimeoutMs)]` attribute and `private const int GateTimeoutMs = 60000;` are unchanged. -- [ ] [P3-T13] Replace the R4 documentation paragraph (pre-edit lines 196 to 202) of QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs with Delivered Source R-DOC. - - Acceptance (recorded by P3-T14): `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, and one line each containing `The gate-free fixture method EnsureDispatcher seeds the parked dispatcher` and `(W2), and UiThread.Initialize (W5) must not latch`. -- [ ] [P3-T14] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (the P0-T12 tokens) and `CMD-TIMESPAN`. - - Acceptance: liveness 0, 0, 0, 0, 2, 2, 1, 1, 2, 2; teardown 0, 0, 0, 0, 1, 1, 1, 0, 0, 0; zero-batch 0, 0, 0, 0, 3, 3, 1, 0, 0, 0; R4 file `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, `[Timeout(GateTimeoutMs)]` 8, the constant 1, the two R-DOC tokens 1 each; `R4SPAN` 1, 1, 1, 1; `TIMESPAN-UNCLASSIFIED: 0`, with every `TIMESPAN` line `CLASSIFIED`. Any other value: correct the edit and re-run this task. + - Acceptance (recorded by P3-T15): the zero-batch file has `.Wait(` 0, `new BackgroundWorker()` 0, `new SynchronousBackgroundWorker()` 3 and `WorkerStarter = StartSynchronously;` 3. +- [ ] [P3-T12] Apply Delivered Source R-BODY (the baseline pin taken inside the gate: four lines inserted after pre-edit line 214, pre-edit lines 215 to 258 re-indented by four spaces, one closing line inserted after pre-edit line 258) to `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. This task runs before P3-T13, so the pre-edit line numbers are current. + - Acceptance (recorded by P3-T15): within `R4SPAN`, `IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` 1, `EnsureUiThreadDispatcher()` 1, `using (` 2, `.BeSameAs(` 1 and `.NotBeSameAs(` 1; within `R4PRE`, `EnsureUiThreadDispatcher()` 0 and `using (` 0; within `R4HEAD`, `EnsureUiThreadDispatcher()` 1 and `using (` 1; within `R4TAIL`, `}` 3; the `[Timeout(GateTimeoutMs)]` attribute and `private const int GateTimeoutMs = 60000;` are unchanged. +- [ ] [P3-T13] Replace the R4 documentation paragraph (pre-edit lines 196 to 202, unchanged by P3-T12) of QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs with Delivered Source R-DOC. + - Acceptance (recorded by P3-T15): `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, and one line each containing `The gate-free fixture method EnsureDispatcher seeds the parked dispatcher`, `cannot restore a null previous value between the pin` and `(W2), and UiThread.Initialize (W5) must not latch`. +- [ ] [P3-T14] Replace the class remarks (pre-edit lines 23 to 31) of QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z-R. + - Acceptance (recorded by P3-T15): the zero-batch file has `starts a real` 0 and one line containing `so no test starts a`. +- [ ] [P3-T15] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);", "TaskCreationOptions.RunContinuationsAsynchronously", "IsBusy", "starts a real", "so no test starts a"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "cannot restore a null previous value between the pin", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL` (the P0-T12 token list of each) and `CMD-TIMESPAN`. + - Acceptance (fourteen THREE tokens in the order listed): liveness 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0; teardown 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0; zero-batch 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1; R4 file `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, `[Timeout(GateTimeoutMs)]` 8, the constant 1, the three R-DOC tokens 1 each; `R4SPAN` 1, 1, 2, 1, 1; `R4PRE` 0, 0; `R4HEAD` 1, 1; `R4TAIL` 3; `TIMESPAN-UNCLASSIFIED: 0`, with every `TIMESPAN` line `CLASSIFIED`. Any other value: correct the edit and re-run this task. (The first eleven values of each THREE row are the executor's round 1 rows; the last three columns are added by revision R1 for the stale-comment edits.) ### Phase 4 — Scoped Format, Pass-After Runs and Implementation Commit - [ ] [P4-T1] Format the five Write Set code files with a scoped CSharpier pass and record the before-and-after hashes in FEATURE/evidence/qa-gates/scoped-format.md. - Command: `CMD-HASH`; then `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier format QuickFiler\Controllers\QfcDatamodel.cs QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH` again. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `CSHARPIER_EXIT_CODE: 0`; the line beginning `Formatted ` is transcribed and labelled as a processed-file count, not a rewrite count; `REWRITTEN:` lists every CODE5 path whose hash differs between the two captures, or `NONE` (the rewrite observation is the hash difference, not the console line). Either value completes this task: the pass exists so the committed text is formatter-stable. -- [ ] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T14 commands, plus `git -C WORKTREE diff --numstat 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - - Acceptance: every P2-T4 and P3-T14 value holds after formatting; CMD-LINECOUNT reports at most 500 for each CODE5 file (expected 495 for QfcDatamodel.cs; the others are recorded); numstat for QfcDatamodel.cs reads `14 2`, and the two deleted lines in the anchored diff both have the trimmed text `worker.RunWorkerAsync();`; the porcelain span lists exactly the five CODE5 paths with status ` M`. This artifact is the evidence for AC1, AC2, AC3 and AC14 and the census half of AC6 to AC13. +- [ ] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T15 commands, plus `git -C WORKTREE diff --numstat 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: every P2-T4 and P3-T15 value holds after formatting; CMD-LINECOUNT reports at most 500 for each CODE5 file (expected 495 for QfcDatamodel.cs; the others are recorded); numstat for QfcDatamodel.cs reads `14 2`, and the two deleted lines in the anchored diff both have the trimmed text `worker.RunWorkerAsync();`; the porcelain span lists exactly the five CODE5 paths with status ` M`. This artifact is the evidence for AC1, AC2, AC3 and AC14 and the census half of AC6 to AC13. - [ ] [P4-T3] Build the fixed tree with `CMD-BUILD` (`TASKID` p4-t3) and record it in FEATURE/evidence/regression-testing/pass-after-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_QUICKFILER_TEST:` at least 1. - [ ] [P4-T4] Run the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p4-t4, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/targets-pass-after.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 9`; nine `RESULT` lines, one per target name, each `Passed`, with durations recorded. Any target not `Passed` invokes the D-13 Phase 4 restart rule. - [ ] [P4-T5] Run the four target classes concurrently with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p4-t5, empty `NAMES`) and record FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md. - - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; every nine-target `RESULT` line `Passed`; `CONCURRENT-NOT-PASSED:` lists every non-Passed name or `NONE`, and every listed name is in P0-T14 `BASELINE-CONCURRENT-FAILED:` (`CONCURRENT-NEW-FAILURES: NONE`). `EXIT_CODE: 0` when the list is `NONE`; otherwise `ExpectedExitCode:` equals the observed value and the artifact names the baseline entries that account for it. A new failure invokes the D-13 Phase 4 restart rule. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; every nine-target `RESULT` line `Passed`; `CONCURRENT-NOT-PASSED:` lists every non-Passed name or `NONE`, and every listed name is in P0-T14 `BASELINE-CONCURRENT-FAILED:` (`CONCURRENT-NEW-FAILURES: NONE`). `EXIT_CODE: 0` when the list is `NONE`; otherwise `ExpectedExitCode:` equals the observed value and the artifact names the baseline entries that account for it. A new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"): stop and report; any other new failure invokes the D-13 Phase 4 restart rule. - [ ] [P4-T6] Confirm the R4 pass-after condition from FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/regression-testing/targets-pass-after.md by appending a `R4-PASS-AFTER:` section to FEATURE/evidence/regression-testing/r4-fail-before.md. - Acceptance: the section quotes the R4 `RESULT` line from both pass-after artifacts (`Passed` in each) beside the P1-T6 `Failed` line, completing the fail-before and pass-after pair for Defect B. - [ ] [P4-T7] Commit the implementation (the five CODE5 files and FEATURE) and record FEATURE/evidence/qa-gates/implementation-commit.md. @@ -836,9 +896,9 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance (recorded by P5-T9): that test contains no `WorkerStarter` assignment. - [ ] [P5-T7] Control edit A7 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`, delete the line `model.WorkerStarter = StartSynchronously;`. - Acceptance (recorded by P5-T9): the zero-batch file has `WorkerStarter = StartSynchronously;` 0 and `WorkerStarter = _ => { };` 1. -- [ ] [P5-T8] Control edit A8 in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs: insert Delivered Source R-INJECT immediately before `transactionA.Install(liveA);` in the pinned R4 body. - - Acceptance (recorded by P5-T9): within `R4SPAN`, `EnsureUiThreadDispatcher()` 2 and `using (IDisposable baseline =` 1. -- [ ] [P5-T9] Record the batch A edit census in FEATURE/evidence/regression-testing/controls-a-edits.md with `CMD-TOKEN-COUNT` on THREE (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };", "release.SetResult(true);", "pump.Drain();"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()", "using (IDisposable baseline ="`), and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [ ] [P5-T8] Control edit A8 in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs: insert Delivered Source R-INJECT immediately before `transactionA.Install(liveA);` in the pinned R4 body (inside the `baseline` using block, after the `original` read, at twenty spaces). + - Acceptance (recorded by P5-T9): within `R4SPAN`, `EnsureUiThreadDispatcher()` 2 and `IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` 1. +- [ ] [P5-T9] Record the batch A edit census in FEATURE/evidence/regression-testing/controls-a-edits.md with `CMD-TOKEN-COUNT` on THREE (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };", "release.SetResult(true);", "pump.Drain();"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()", "IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()"`), and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: liveness 1, 1, 1, 2; teardown 0, 1, 0, 0; zero-batch 0, 1, 0, 0; `R4SPAN` 2 and 1; numstat lists exactly the four test files (liveness `1 3`, teardown `1 1`, zero-batch `1 3`, R4 `1 0`); porcelain lists exactly those four paths with ` M`. The artifact names each edit A1 to A8 with its test. - [ ] [P5-T10] Build the batch A tree with `CMD-BUILD` (`TASKID` p5-t10) and record FEATURE/evidence/regression-testing/controls-a-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. @@ -858,10 +918,23 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P5-T17] Revert batch B to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-b-revert.md. - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. -- [ ] [P5-T18] Append a `## Post-fix deterministic controls` section to the FEATURE/evidence/regression-testing/fail-before-exception.*.md dossier citing FEATURE/evidence/regression-testing/negative-controls-batch-a.md and FEATURE/evidence/regression-testing/negative-controls-batch-b.md. - - Acceptance: the section names the eight Defect A controls (A1 to A7 from batch A and B1 from batch B) with their outcome `Failed` and duration copied from the two batch artifacts, and states that each rewritten test fails at once when its signal is withheld, which the pre-fix tests could show only by waiting out a five-second bound. -- [ ] [P5-T19] Write the AC15 negative-control summary FEATURE/evidence/other/negative-controls-summary.md. - - Acceptance: a Markdown table with exactly nine rows, one per test named in AC6 to AC13 in that order, with columns Test, Mechanism (the spec Test Strategy row), Edit applied (A1 to A8, B1, or R-INJECT on the unmodified file), Observed outcome, Failure message fragment, Duration and Source artifact. The R4 row cites P1-T6 (`Failed`, pre-fix shape with the injected writer) and P5-T11 (`Passed`, pinned shape with the same injected writer), matching the spec's two-part R4 mechanism. Every row's values are copied from the cited artifacts; the artifact carries `Timestamp:`. +- [ ] [P5-T18] Control edit C1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `pump.Drain();` inside `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` (test 3), keeping `release.SetResult(true);`, so the released loader's continuation stays queued in the installed context. + - Acceptance (recorded by P5-T20): test 3 still contains `release.SetResult(true);` and no longer contains `pump.Drain();`. +- [ ] [P5-T19] Control edit C2 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `pump.Drain();` inside `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (test 4), keeping `release.SetResult(true);`. + - Acceptance (recorded by P5-T20): the liveness file has `pump.Drain();` 0 and `release.SetResult(true);` 3. +- [ ] [P5-T20] Record the batch C edit census in FEATURE/evidence/regression-testing/controls-c-edits.md with `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"pump.Drain();", "release.SetResult(true);", "TaskCreationOptions.RunContinuationsAsynchronously"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: 0, 3 and 1; numstat lists only the liveness file with `0 2`; porcelain lists only that path with ` M`. The artifact names C1 and C2 with their tests. +- [ ] [P5-T21] Build the batch C tree with `CMD-BUILD` (`TASKID` p5-t21) and record FEATURE/evidence/regression-testing/controls-c-build.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. +- [ ] [P5-T22] [expect-fail] Run batch C with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-C`, `TASKID` p5-t22, `NAMES` `"RemainingLoadActive_AfterLoaderCompletes_BecomesFalse", "RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally"`) and record FEATURE/evidence/regression-testing/negative-controls-batch-c.md. + - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 2`; with duration recorded, test 3 `Failed` with a message containing `the finally around the awaited loader must clear the flag once it completes` and test 4 `Failed` with a message containing `the finally must clear the flag on the throwing path too`. This shows that, with the loader released, the flag clears only through `Drain()`: the continuation is posted to the installed context rather than run inline inside `SetResult` (D-2). Either test `Passed` is `DRAIN NOT LOAD-BEARING`, and any `Timeout`, `Aborted` or `NotExecuted` outcome or any Sequence file is `CONTROL DEFECT`: stop and report in both cases. +- [ ] [P5-T23] Revert batch C to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-c-revert.md. + - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. + - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. +- [ ] [P5-T24] Append a `## Post-fix deterministic controls` section to the FEATURE/evidence/regression-testing/fail-before-exception.*.md dossier citing FEATURE/evidence/regression-testing/negative-controls-batch-a.md, FEATURE/evidence/regression-testing/negative-controls-batch-b.md and FEATURE/evidence/regression-testing/negative-controls-batch-c.md. + - Acceptance: the section names the ten Defect A controls (A1 to A7 from batch A, B1 from batch B, C1 and C2 from batch C) with their outcome `Failed` and duration copied from the three batch artifacts, and states that each rewritten test fails at once when its signal is withheld, which the pre-fix tests could show only by waiting out a five-second bound. +- [ ] [P5-T25] Write the AC15 negative-control summary FEATURE/evidence/other/negative-controls-summary.md. + - Acceptance: a Markdown table with exactly nine rows, one per test named in AC6 to AC13 in that order, with columns Test, Mechanism (the spec Test Strategy row), Edit applied (A1 to A8, B1, or R-INJECT on the unmodified file), Observed outcome, Failure message fragment, Duration and Source artifact. The R4 row cites P1-T6 (`Failed`, pre-fix shape with the injected writer) and P5-T11 (`Passed`, pinned shape with the same injected writer), matching the spec's two-part R4 mechanism. A second table headed `Drain-dependency controls` has exactly two rows, tests 3 and 4, with the same columns, Edit applied C1 and C2, and values copied from P5-T22; it supplements, and does not replace, the Test Strategy mechanism in the first table. Every row's values are copied from the cited artifacts; the artifact carries `Timestamp:`. ### Phase 6 — Final QA Loop, Static Gates, Check-offs and Final Commit @@ -877,7 +950,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. - [ ] [P6-T5] Run the final repository-wide test-and-coverage run by the P0-T15 route (`CMD-COVERAGE-RUNNER` or `CMD-COVERAGE-DIRECT` with `STAGE` final, then `CMD-COVERAGE-POST` with `STAGE` final) and record FEATURE/evidence/qa-gates/coverage-post-change.md. - Artifact: the same fields as P0-T16, with `ITERATION:` and `FAILED-SET:` recorded as `FINAL-FAILED-SET:`. - - Acceptance, all required: the route equals P0-T15's; `TRX_PRESENT: True`; `SEQUENCE_FILES:` 0 under DIRECT; `QFCDATAMODEL-CLASS-NODES: 0`; the nine `RESULT` lines all `Passed`; `NEW-FAILURES:` (names in `FINAL-FAILED-SET:` absent from `BASELINE-FAILED-SET:`) is `NONE`; `LINE-FLOOR:` and `BRANCH-FLOOR:` each `MET`, or `NOT MET` only where P0-T16 recorded the same floor as not met; the `First-party coverage:` line is recorded as the numeric post-change headline. `RUNNER-GREEN: YES` is recorded when the route is RUNNER and `RUNNER_EXIT_CODE: 0`, otherwise `RUNNER-GREEN: NO` with the reason (`COVERAGE-ROUTE DIRECT` or `PRE-EXISTING FAILURES`). A failure of any target test or a new failure attributable to the Write Set invokes the D-13 failure restart; any other new failure is `NEW FAILURE OUTSIDE SCOPE`: stop and report, without re-running. + - Acceptance, all required: the route equals P0-T15's; `TRX_PRESENT: True`; `SEQUENCE_FILES:` 0 under DIRECT; `QFCDATAMODEL-CLASS-NODES: 0`; the nine `RESULT` lines all `Passed`; `NEW-FAILURES:` (names in `FINAL-FAILED-SET:` absent from `BASELINE-FAILED-SET:`) is `NONE`; `LINE-FLOOR:` and `BRANCH-FLOOR:` each `MET`, or `NOT MET` only where P0-T16 recorded the same floor as not met; the `First-party coverage:` line is recorded as the numeric post-change headline. `RUNNER-GREEN: YES` is recorded when the route is RUNNER and `RUNNER_EXIT_CODE: 0`, otherwise `RUNNER-GREEN: NO` with the reason (`COVERAGE-ROUTE DIRECT` or `PRE-EXISTING FAILURES`). A failure of any target test or a new failure attributable to the Write Set invokes the D-13 failure restart; a new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"); any other new failure is `NEW FAILURE OUTSIDE SCOPE`; both are a stop and report, without re-running. - [ ] [P6-T6] Compare baseline and post-change coverage and test outcomes and record FEATURE/evidence/qa-gates/coverage-comparison.md from FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-post-change.md. - Acceptance: the artifact records the baseline and post-change `First-party coverage:` lines (lines and branches, numeric), the two `ROOT` lines, and the repository-wide comparison in exactly one named branch: `BRANCH A` when the two `lines-valid` figures differ by at most 1 percent of the baseline figure (the post-change line rate must not be lower than baseline by more than 0.5 percentage points), otherwise `BRANCH B` (recorded and not gated, with one sentence stating the denominators are not comparable). New and changed code: `CHANGED-PRODUCTION-COVERAGE: NOT MEASURED` with the reason `[ExcludeFromCodeCoverage]` at QuickFiler/Controllers/QfcDatamodel.cs line 25 and `QFCDATAMODEL-CLASS-NODES: 0` at both stages; the changed test files are outside the instrumented set. The artifact also restates `BASELINE-FAILED-SET:`, `FINAL-FAILED-SET:` and `NEW-FAILURES: NONE`. A Branch A breach is `COVERAGE REGRESSION`: stop. - [ ] [P6-T7] Record the final toolchain pass in FEATURE/evidence/qa-gates/toolchain-final-pass.md from the P6-T1 to P6-T5 artifacts of the final iteration. @@ -887,7 +960,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P6-T9] Record the AC5 prohibited-construct gate in FEATURE/evidence/qa-gates/prohibited-constructs.md with `CMD-ADDED-SCAN`, `git -C WORKTREE diff --exit-code 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, `git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, and `CMD-TOKEN-COUNT` on the R4 file (TOKENS `"[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`). - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `GIT_DIFF_EXIT_CODE: 0`; `ADDED_LINES:` greater than 0; `ADDED-TOKEN` counts 0 for `Thread.Sleep`, `Task.Delay`, `DoNotParallelize`, `Retry(` and `Timeout(`, and at least 1 for `WorkerStarter` (positive control); the runsettings diff exits 0 and the porcelain span prints nothing; the R4 file reads 8 and 1, equal to P0-T12. - [ ] [P6-T10] Record the footprint gate in FEATURE/evidence/qa-gates/footprint-scope.md with `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD` paired with `git -C WORKTREE status --porcelain --untracked-files=all`. - - Acceptance: `THIS-ITEM-FOOTPRINT:` (name-status paths outside FEATURE) is exactly the five CODE5 paths with status `M`, plus the promotion record only if P0-T3 listed it as inherited (recorded as `INHERITED-AND-EXCLUDED:`); no path ends in `.csproj`; no status `A` outside FEATURE; no porcelain line names a path under QuickFiler/, QuickFiler.Test/ or scripts/. + - Acceptance: `THIS-ITEM-FOOTPRINT:` (name-status paths outside FEATURE, excluding any path P0-T3 listed in `INHERITED-COMMITTED:`) is exactly the five CODE5 paths with status `M`; the excluded inherited paths are recorded as `INHERITED-AND-EXCLUDED:` (at most `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md`, status `A`); no path ends in `.csproj`; no status `A` outside FEATURE other than an `INHERITED-AND-EXCLUDED:` path; no porcelain line names a path under QuickFiler/, QuickFiler.Test/ or scripts/. - [ ] [P6-T11] Record the evidence hygiene gate in FEATURE/evidence/qa-gates/evidence-hygiene.md by scanning every file under FEATURE/evidence/. - Command: `pwsh -NoProfile -Command 'PREFIX; $b = [char]92; $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950\evidence" -Recurse -File); "EVIDENCE_FILES=$($files.Count)"; "RAW_DOCUMENTS=$(@($files | Where-Object { $_.Extension -in @(".trx", ".xml", ".coverage", ".coveragexml", ".log") }).Count)"; $pattern = "[a-z]:[" + $b + $b + "/]+users[" + $b + $b + "/]+[a-z0-9_.~-]"; "PROFILE_PATH_LINES=$(@($files | Select-String -Pattern $pattern).Count)"'`. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EVIDENCE_FILES=` at least 1; `RAW_DOCUMENTS=0`; `PROFILE_PATH_LINES=0` (the pattern is the repository hygiene rule's, built from `[char]92` so the Bash channel cannot collapse its backslashes). A non-zero value names the offending files; the executor redacts them and re-runs this task. @@ -915,11 +988,11 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance: only `- [ ] AC11:` changes; otherwise unchecked with `AC11: NOT MET`. - [ ] [P6-T23] Check off AC12 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` are both `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `WorkerStarter = StartSynchronously;` 3. - Acceptance: only `- [ ] AC12:` changes; otherwise unchecked with `AC12: NOT MET`. -- [ ] [P6-T24] Check off AC13 in FEATURE/spec.md when `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md, FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `R4SPAN` 1, 1, 1, 1. +- [ ] [P6-T24] Check off AC13 in FEATURE/spec.md when `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md, FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `R4SPAN` 1, 1, 2, 1, 1, `R4PRE` 0, 0, `R4HEAD` 1, 1 and `R4TAIL` 3 (the pin opens after transaction A's acquisition and before the `original` read, closes after both assertions, and both assertions remain). - Acceptance: only `- [ ] AC13:` changes; otherwise unchecked with `AC13: NOT MET`. -- [ ] [P6-T25] Check off AC14 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1 and both R-DOC tokens 1. +- [ ] [P6-T25] Check off AC14 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1 and the three R-DOC tokens 1 each. - Acceptance: only `- [ ] AC14:` changes; otherwise unchecked with `AC14: NOT MET`. -- [ ] [P6-T26] Check off AC15 in FEATURE/spec.md when FEATURE/evidence/other/negative-controls-summary.md holds nine rows whose outcomes match P5-T19's acceptance. +- [ ] [P6-T26] Check off AC15 in FEATURE/spec.md when FEATURE/evidence/other/negative-controls-summary.md holds nine rows in its AC15 table and two rows in its Drain-dependency table whose outcomes match P5-T25's acceptance. - Acceptance: only `- [ ] AC15:` changes; otherwise unchecked with `AC15: NOT MET`. - [ ] [P6-T27] Check off AC16 in FEATURE/spec.md when FEATURE/evidence/other/ambient-synchronization-context.md carries exactly one `AMBIENT-SYNCHRONIZATION-CONTEXT:` line. - Acceptance: only `- [ ] AC16:` changes; otherwise unchecked with `AC16: NOT MET`. @@ -939,7 +1012,17 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma SELF-REVIEW: RE-DERIVED THIS PASS -Citations re-derived in this pass against the item worktree tree at HEAD 8bbd48f68f9631247ccc3c58fa232ff333d4fe56 (content-line counts by line-oriented count): +Revision R1 pass (this pass): every citation the R1 edits touched, and the sibling lines around them, was re-read directly from the item worktree files (HEAD `e3827fb2c` as stated by the delegation; this planning session has no shell, so HEAD itself was not re-queried and P0-T3 records it): + +- R1-a. QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs — lines 185 to 279 read in full: para 196 to 202; attributes 204 and 205; declaration 206; 207 to 214 (acquisition 212 to 214); `original` read 215; `Install` 216; blank 217, 221, 237, 242; `using (` 218 (the only one in 206 to 272); transaction B 225 to 227 and read 230; assertions 244 to 257; `issue #230 lost update` at 256 (unique in the file); braces 258 and 259; `finally` 260 to 263; method close 264; R5 at 273. File-wide search: `.BeginTransactionAsync()` at 51, 111, 161, 213, 226 and later; `Dispatcher original = ` at 55, 115, 215; `EnsureUiThreadDispatcher` at 60, 119, 166; `ShutdownDispatcher(liveA)` at 96, 262, 310, 351; no `Issue #950`, no `W3`/`W4`; 11 lines carry `GateTimeoutMs`, `flake-watch` or `Append an observation` (9, 1 and 1). Test names: R1 44, R2 107, R3 157, R4 206, R5 273. Derived: `R4SPAN` 206-272, `R4PRE` 206-212, `R4HEAD` 206-214, `R4TAIL` 256-261 (two `}` lines). +- R1-b. QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs — `EnsureDispatcher` 116 to 138 (seeds only a null field, line 130; never takes the gate); design note 25 to 30. QuickFiler.Test/Controllers/QfcItemController.TestSupport.cs 238 to 239 (`EnsureUiThreadDispatcher` returns `IDisposable`). +- R1-c. QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs — read in full (255 lines): L1 region 47 to 57; test 1 70 to 138 (IsBusy 73, 107, 108); `StartHeldOpenLoader` 148 to 181 (IsBusy 151, 177); blank 182; test 2 summary 183 to 187 (IsBusy 185), `[TestMethod]` 188; test 3 207 to 227; test 4 229 to 253. Post-edit census rows re-derived from the final prescribed source text (L1 to L5 including the extended L3): 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0; batch C census 0, 3, 1. +- R1-d. QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs — read in full (212 lines): remarks 23 to 31 (`starts a real` at 24 only); inert loader 89 to 108; Z0 110 to 133; Z1 135 to 164 (IsBusy 139, 141); Z2 166 to 210. Post-edit row 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1. +- R1-e. QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs — token search for every census token: `WaitForState` 66 and 225, `SpinWait` 67, `new BackgroundWorker()` 214, `.Wait(` 220; no `IsBusy`, `starts a real`, `RunContinuationsAsynchronously`, `SetSynchronizationContext` or `Drain`. Post-edit row 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0. +- R1-f. docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md — lines 60 to 284 read: scope item 72 (pin inside the gate, W3/W4 rationale), design summary 131, invariants 139, Test Strategy table 236 to 246, acceptance lines 266 to 282 (AC13 at 278). +- R1-g. This plan — the Command Reference payloads `CMD-COVERAGE-RUNNER` and `CMD-COVERAGE-DIRECT` each end with `Write-Output "PAYLOAD-COMPLETE"`; no remaining reference to `STAGE-950.result.log`; every former `P3-T14`, `P5-T18` and `P5-T19` reference renumbered. + +Original authoring pass (HEAD 8bbd48f68f9631247ccc3c58fa232ff333d4fe56; items not touched by R1 are carried from it): 1. QuickFiler/Controllers/QfcDatamodel.cs — 483 lines; `[ExcludeFromCodeCoverage]` 25; loader assignments 40 and 51 (exactly two); `RemainingEmailLoader` declaration 140 followed by blank 141 and `#endregion Private Variables` 142; `Worker_DoWork` 185 to 229 with 205, 206, 207 and finally 209 to 216; `InitEmailQueue` 259 to 303 with start sites 273 and 300; `InitEmailQueueAsync` at 305; `WorkerStarter` absent. 2. QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs — 255 lines; `WaitForState` 47 to 57 (SpinWait at 56); test 1 97 to 110 block, waits 103 and 106; `StartHeldOpenLoader` 148 to 181; `release.SetResult(true);` 204, 220, 246; test 3 207 to 227; test 4 229 to 253; tokens `SpinWait` 1, `.Wait(` 2, `WaitForState` 5, `new BackgroundWorker()` 2. @@ -953,29 +1036,30 @@ Citations re-derived in this pass against the item worktree tree at HEAD 8bbd48f 10. .gitignore 146, 150, 151; .csharpierignore 4 and 12; global.json 3 to 8; dotnet-tools.json 6; scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 line 21. 11. spec.md acceptance lines 266 to 282 (17 lines `- [ ] AC`); issue.md line 12; worktree reflog for the BASE cut and HEAD. -Sibling-region re-checks: the three `release.SetResult(true);` lines in the liveness file (control edits A2 and A3 target the second and third only); the three `Dispatcher original = ...` lines in the R4 file (R-INJECT anchors on the unique `transactionA.Install(liveA);`); the teardown file's `Cleanup_CalledTwice_DoesNotThrow` worker at 174 (not a start site, not edited); the teardown `TimeSpan` lines that must survive (118, 149, 154); `QuickFiler/Interfaces/IQfcDatamodel.cs` (instrumented separately, excluded by the exact filename equality in CMD-COVERAGE-POST); the R4 `[Timeout]` attribute line, which R-BODY does not re-indent, so the added-line scan for `Timeout(` reads 0. +Sibling-region re-checks: the three `release.SetResult(true);` lines in the liveness file (control edits A2 and A3 target the second and third only); the three `Dispatcher original = ...` lines in the R4 file (R-INJECT anchors on the unique `transactionA.Install(liveA);`); the teardown file's `Cleanup_CalledTwice_DoesNotThrow` worker at 174 (not a start site, not edited); the teardown `TimeSpan` lines that must survive (118, 149, 154); `QuickFiler/Interfaces/IQfcDatamodel.cs` (instrumented separately, excluded by the exact filename equality in CMD-COVERAGE-POST); the R4 `[Timeout]` attribute line, which R-BODY does not re-indent, so the added-line scan for `Timeout(` reads 0. Revision R1 sibling re-checks: R1, R2 and R3 in the R4 file also call the ensure wrapper (60, 119, 166) but lie outside every R4 span; the R4 acquisition lines 212 to 214 stay outside the re-indented range; the `secondCallerStarted.Wait();` line (239) is re-indented and so appears as an added line, which the added-line scan tolerates because it does not scan `.Wait(`; the liveness test 1 documentation (73) keeps its one `IsBusy` (production behavior, issue #424); test 2's `release.SetResult(true);` (204) is untouched by batch C; the zero-batch inert-loader documentation (93, "starting a real") does not contain `starts a real` and is left unchanged. PLANNER-INTERNAL-REVIEW: PASS CITATION-TO-TREE: PASS AC-TRACEABILITY: PASS SCOPE-BOUNDARY: PASS CITATION: QuickFiler/Controllers/QfcDatamodel.cs | lines 25, 40, 51, 140, 185-229, 259-303 (start sites 273 and 300) -CITATION: QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs | lines 47-57, 97-110, 148-181, 207-227, 229-253 +CITATION: QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs | lines 47-57, 73, 97-110, 148-187, 207-227, 229-253 CITATION: QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs | lines 59-67, 214-232 -CITATION: QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs | lines 97-108, 117-133, 135-164, 176-210 -CITATION: QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs | lines 33, 192-203, 206-264, 216, 273 -CITATION: QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs | lines 26-30, 122-138, 231 +CITATION: QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs | lines 23-31, 97-108, 117-133, 135-164, 176-210 +CITATION: QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs | lines 33, 192-203, 206-264 (acquisition 212-214, re-indent range 215-258, tail 256-261), 216, 273 +CITATION: QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs | lines 26-30, 116-138, 231 +CITATION: QuickFiler.Test/Controllers/QfcItemController.TestSupport.cs | lines 238-239 CITATION: QuickFiler.Test/SetupAssemblyInitializer.cs | lines 14-25 CITATION: scripts/vscode/Invoke-MSTestWithCoverage.ps1 | lines 89-93, 262, 348-355, 410, 415-423, 449-453 CITATION: scripts/vscode/TaskMaster.cli.runsettings | lines 4-7 CITATION: scripts/hygiene/Test-RepositoryHygiene.Rules.ps1 | line 21 -CITATION: docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md | lines 266-282 +CITATION: docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md | lines 72, 131, 236-246, 266-282 AC-INVENTORY: AC1, AC2, AC3, AC4, AC5, AC6, AC7, AC8, AC9, AC10, AC11, AC12, AC13, AC14, AC15, AC16, AC17 AC-MAPPING: AC1 | IMPLEMENTATION: P2-T1, P2-T2 | TESTS: P2-T4, P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md AC-MAPPING: AC2 | IMPLEMENTATION: P2-T3 | TESTS: P2-T4, P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md AC-MAPPING: AC3 | IMPLEMENTATION: P2-T1, P2-T2, P2-T3, P4-T1 | TESTS: P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md AC-MAPPING: AC4 | IMPLEMENTATION: P3-T1 to P3-T11 | TESTS: P6-T8 | EVIDENCE: FEATURE/evidence/qa-gates/wall-clock-tokens.md -AC-MAPPING: AC5 | IMPLEMENTATION: P2-T1 to P3-T13 | TESTS: P6-T9 | EVIDENCE: FEATURE/evidence/qa-gates/prohibited-constructs.md +AC-MAPPING: AC5 | IMPLEMENTATION: P2-T1 to P3-T14 | TESTS: P6-T9 | EVIDENCE: FEATURE/evidence/qa-gates/prohibited-constructs.md AC-MAPPING: AC6 | IMPLEMENTATION: P3-T1, P3-T2 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md AC-MAPPING: AC7 | IMPLEMENTATION: P3-T1, P3-T3 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md AC-MAPPING: AC8 | IMPLEMENTATION: P3-T1, P3-T3, P3-T4 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md @@ -985,11 +1069,11 @@ AC-MAPPING: AC11 | IMPLEMENTATION: P3-T8, P3-T10 | TESTS: P4-T4, P6-T5 | EVIDENC AC-MAPPING: AC12 | IMPLEMENTATION: P3-T8, P3-T9, P3-T11 | TESTS: P4-T4, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/targets-pass-after.md AC-MAPPING: AC13 | IMPLEMENTATION: P3-T12 | TESTS: P1-T6, P4-T4, P4-T5, P6-T5 | EVIDENCE: FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md AC-MAPPING: AC14 | IMPLEMENTATION: P3-T13 | TESTS: P4-T2 | EVIDENCE: FEATURE/evidence/qa-gates/post-format-census.md -AC-MAPPING: AC15 | IMPLEMENTATION: P5-T1 to P5-T17 | TESTS: P1-T6, P5-T11, P5-T16 | EVIDENCE: FEATURE/evidence/other/negative-controls-summary.md +AC-MAPPING: AC15 | IMPLEMENTATION: P5-T1 to P5-T23, P5-T25 | TESTS: P1-T6, P5-T11, P5-T16, P5-T22 | EVIDENCE: FEATURE/evidence/other/negative-controls-summary.md AC-MAPPING: AC16 | IMPLEMENTATION: P1-T1 | TESTS: P1-T5 | EVIDENCE: FEATURE/evidence/other/ambient-synchronization-context.md AC-MAPPING: AC17 | IMPLEMENTATION: P6-T1 to P6-T5 | TESTS: P6-T5 | EVIDENCE: FEATURE/evidence/qa-gates/toolchain-final-pass.md UNRESOLVED-GAPS: NONE DIRECTIVE: PREFLIGHT VALIDATION ONLY -Executor preflight has not yet run on this plan; the signal below requests it and does not report a discovered defect. +Executor preflight round 1 returned REVISIONS REQUIRED; revision R1 applies every delta, and executor preflight round 2 is requested. The signal below is the planner's request line, not a self-approval and not a newly discovered defect. PREFLIGHT: REVISIONS REQUIRED From 71669a78d9def6dbfb8799c27ad315e336005076 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Thu, 1 Oct 2026 23:58:26 -0400 Subject: [PATCH 08/24] docs(950): revise plan after preflight round 2 --- .../plan.2026-10-01T07-11.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index d05d8372b..9e9f5186d 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -513,7 +513,7 @@ The `using` header is shown split because the one-line form `using (IDisposable - **Command rows.** The `Command:` field of a payload artifact records the full payload as executed, with `WORKTREE` in place of the path, followed on the next line by the canonical command it implements (for example `msbuild TaskMaster.sln /t:Rebuild ...`). Every payload artifact records `WORKTREE-LEAF: agent-a7805823735145ca4`; any other value means the payload ran in the wrong tree, and the task fails. - **Exit codes.** `EXIT_CODE:` records the payload's principal exit value as named in each Command Reference entry. Deliberately failing runs carry `ExpectedExitCode:` equal to the deterministic value the task states. A recorded observation whose exit value is not gated carries `ExpectedExitCode:` equal to the observed value and says so. - **Transcription.** Any absolute path inside a transcribed line is replaced by `REDACTED-PATH`. Trx and coverage documents are never copied into FEATURE. -- **Long-running payloads.** `CMD-COVERAGE-RUNNER` and `CMD-COVERAGE-DIRECT` are started with the Bash tool's `run_in_background` option and no shell redirection (a Bash `>` target resolves against the session checkout, not WORKTREE); the payload's own output, captured by the tool, is the record, and completion is the background-task notification together with a final output line `PAYLOAD-COMPLETE`. A run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report. Before any re-invocation after a timed-out or interrupted call, the executor runs `pwsh -NoProfile -Command '"STRAY_TEST_PROCESSES: " + @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { ($_.Name -like "vstest*" -or $_.Name -like "dotnet-coverage*") -and $null -ne $_.CommandLine -and $_.CommandLine.Contains("agent-a7805823735145ca4") }).Count'` and proceeds only at `STRAY_TEST_PROCESSES: 0`; the count is scoped to this worktree's leaf so test runs in sibling worktrees cannot hold the gate open, and two collections from this worktree never run at once. Both payloads, as written in the Command Reference, end with the unconditional line `Write-Output "PAYLOAD-COMPLETE"`, which runs on success and on a non-zero collector exit alike (a native command's exit code does not stop the payload); a run that ends without that line is incomplete and is treated as `COVERAGE RUN ABORTED`. +- **Long-running payloads.** `CMD-COVERAGE-RUNNER` and `CMD-COVERAGE-DIRECT` are started with the Bash tool's `run_in_background` option and no shell redirection (a Bash `>` target resolves against the session checkout, not WORKTREE); the payload's own output, captured by the tool, is the record, and completion is the background-task notification together with a final output line `PAYLOAD-COMPLETE`. A run still in progress after 120 minutes is `COVERAGE RUN STALLED`: stop and report. Before any re-invocation after a timed-out or interrupted call, the executor runs `pwsh -NoProfile -Command '$leaf = "agent-a7805823735145ca4"; $runner = "Invoke-MSTest" + "WithCoverage"; "STRAY_TEST_PROCESSES: " + @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessId -ne $PID -and $null -ne $_.CommandLine -and $_.CommandLine.Contains($leaf) -and ($_.Name -like "vstest*" -or $_.Name -like "testhost*" -or $_.Name -like "dotnet-coverage*" -or ($_.Name -like "pwsh*" -and $_.CommandLine.Contains($runner))) }).Count'` and proceeds only at `STRAY_TEST_PROCESSES: 0`; the count covers the collector, the console, any test host whose command line carries the leaf, and a surviving pwsh runner (which respawns test hosts), each scoped to this worktree's leaf so test runs in sibling worktrees cannot hold the gate open; the runner token is assembled from two literals and the checking process excludes itself by PID, so the check never counts its own command line (preflight round 2 observed 0 with this form on an idle machine, and 1 with the joined literal and no PID exclusion), and two collections from this worktree never run at once. Both payloads, as written in the Command Reference, end with the unconditional line `Write-Output "PAYLOAD-COMPLETE"`, which runs on success and on a non-zero collector exit alike (a native command's exit code does not stop the payload); a run that ends without that line is incomplete and is treated as `COVERAGE RUN ABORTED`. - **No wall-clock construct anywhere.** No payload sleeps; no test edit adds a sleep, delay, retry, timeout change, parallelism attribute or temporary file. ## Command reference @@ -729,7 +729,7 @@ The projection and the summary block are the two committed forms (CLAUDE.md "Com $span = $src[$s..($e - 1)] foreach ($t in @(TOKENS)) { Write-Output ("SPAN-TOKEN [" + $t + "] = " + @($span | Where-Object { $_.Contains($t) }).Count) } -Span anchors used by this plan: `INITQ` is START `public IList InitEmailQueue(` and END `public async Task> InitEmailQueueAsync(` in `QuickFiler\Controllers\QfcDatamodel.cs`; `R4SPAN` is START `public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores()` and END `public async Task Transaction_DisposedTwice_DoesNotOverReleaseTheGate()` in `QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs`. Three further anchors in the same file locate the R4 pin (fact 5): `R4PRE` is START the R4SPAN START and END `.BeginTransactionAsync()` (the span runs from the R4 declaration up to, not including, the first acquisition line of transaction A); `R4HEAD` is START the R4SPAN START and END `Dispatcher original = UiThreadDispatcherFixture.Current;` (from the R4 declaration up to, not including, the `original` read); `R4TAIL` is START `issue #230 lost update` and END `QfcItemControllerTestSupport.ShutdownDispatcher(liveA);` (from R4's last assertion argument up to, not including, the `finally` body). A pin token that is 0 in `R4PRE` and 1 in `R4HEAD` lies after transaction A's acquisition begins and before the `original` read; one more line containing `}` in `R4TAIL` than at BASE places the pin's closing brace after the last assertion and before `finally`. +Span anchors used by this plan: `INITQ` is START `public IList InitEmailQueue(` and END `public async Task> InitEmailQueueAsync(` in `QuickFiler\Controllers\QfcDatamodel.cs`; `R4SPAN` is START `public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores()` and END `public async Task Transaction_DisposedTwice_DoesNotOverReleaseTheGate()` in `QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs`. Three further anchors in the same file locate the R4 pin (fact 5): `R4PRE` is START the R4SPAN START and END `.BeginTransactionAsync()` (the span runs from the R4 declaration up to, not including, the `.BeginTransactionAsync()` line of transaction A's acquisition; it includes that statement's first line, `UiThreadDispatcherTransaction transactionA = await UiThreadDispatcherFixture`, so a pin placed before the acquisition statement is counted in it); `R4HEAD` is START the R4SPAN START and END `Dispatcher original = UiThreadDispatcherFixture.Current;` (from the R4 declaration up to, not including, the `original` read); `R4TAIL` is START `issue #230 lost update` and END `QfcItemControllerTestSupport.ShutdownDispatcher(liveA);` (from R4's last assertion argument up to, not including, the `finally` body). A pin token that is 0 in `R4PRE` and 1 in `R4HEAD` lies after transaction A's acquisition begins and before the `original` read; one more line containing `}` in `R4TAIL` than at BASE places the pin's closing brace after the last assertion and before `finally`. **CMD-TIMESPAN** (classifies every line of THREE that contains `TimeSpan.`): @@ -819,14 +819,14 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 2 — Production Seam in QfcDatamodel.cs -- [ ] [P2-T1] Insert Delivered Source S1 (the `WorkerStarter` property and its XML documentation) into QuickFiler/Controllers/QfcDatamodel.cs after line 140. +- [ ] [P2-T1] Insert Delivered Source S1 (the `WorkerStarter` property and its XML documentation) into QuickFiler/Controllers/QfcDatamodel.cs after line 140. This task, P2-T2 and P2-T3 apply S1 to S3 as in-place edits that preserve the file's leading UTF-8 byte-order mark (gated by `QFCDATAMODEL-BOM: True` in P2-T4); the file is never rewritten whole. - Acceptance (recorded by P2-T4): the file contains exactly one line containing `internal Action WorkerStarter { get; set; }`, one containing `Injectable worker-start seam` and one containing `null on instances built by GetUninitializedObject`; the property sits before `#endregion Private Variables`. - [ ] [P2-T2] Insert Delivered Source S2 into both constructors of QuickFiler/Controllers/QfcDatamodel.cs, each immediately after that constructor's `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` line (pre-edit 40 and 51). - Acceptance (recorded by P2-T4): exactly two lines contain `WorkerStarter = worker => worker.RunWorkerAsync();`, each directly below one of the two loader-assignment lines. - [ ] [P2-T3] Replace both start sites in `InitEmailQueue` of QuickFiler/Controllers/QfcDatamodel.cs (pre-edit lines 273 and 300) with Delivered Source S3, keeping each line's indentation. - Acceptance (recorded by P2-T4): `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; within `INITQ`, `RunWorkerAsync` 0 and `WorkerStarter(worker);` 2. -- [ ] [P2-T4] Record the production seam census in FEATURE/evidence/qa-gates/production-seam-census.md with `CMD-TOKEN-COUNT` on QuickFiler\Controllers\QfcDatamodel.cs (TOKENS `"internal Action WorkerStarter { get; set; }", "WorkerStarter = worker => worker.RunWorkerAsync();", "Injectable worker-start seam", "null on instances built by GetUninitializedObject", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`), `CMD-SPAN-TOKEN-COUNT` on `INITQ` (TOKENS `"RunWorkerAsync", "WorkerStarter(worker);"`) and `CMD-LINECOUNT`. - - Acceptance: tokens 1, 2, 1, 1, 2; `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; `INITQ` 0 and 2; QfcDatamodel.cs LINES 495. Any other value: correct the edit and re-run this task. +- [ ] [P2-T4] Record the production seam census in FEATURE/evidence/qa-gates/production-seam-census.md with `CMD-TOKEN-COUNT` on QuickFiler\Controllers\QfcDatamodel.cs (TOKENS `"internal Action WorkerStarter { get; set; }", "WorkerStarter = worker => worker.RunWorkerAsync();", "Injectable worker-start seam", "null on instances built by GetUninitializedObject", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`), `CMD-SPAN-TOKEN-COUNT` on `INITQ` (TOKENS `"RunWorkerAsync", "WorkerStarter(worker);"`) and `CMD-LINECOUNT`, and `pwsh -NoProfile -Command 'PREFIX; $b = [System.IO.File]::ReadAllBytes((Join-Path (Get-Location).Path "QuickFiler\Controllers\QfcDatamodel.cs")); "QFCDATAMODEL-BOM: " + ($b.Length -ge 3 -and $b[0] -eq 239 -and $b[1] -eq 187 -and $b[2] -eq 191)'`. + - Acceptance: tokens 1, 2, 1, 1, 2; `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; `INITQ` 0 and 2; QfcDatamodel.cs LINES 495; `QFCDATAMODEL-BOM: True` (the file carries a UTF-8 byte-order mark at BASE; S1 to S3 are applied as in-place edits that keep it, because an edit that drops it rewrites line 1 and P4-T2's numstat then reads `15 3`). Any other value: correct the edit and re-run this task. ### Phase 3 — Test Rewrites @@ -849,7 +849,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P3-T9] Apply Delivered Source Z0 to `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. - Acceptance (recorded by P3-T15): the test assigns `model.WorkerStarter = StartSynchronously;` and its act lambda constructs `new SynchronousBackgroundWorker()`. - [ ] [P3-T10] Replace the documentation and method of `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z1. - - Acceptance (recorded by P3-T15): the test reads `loaderInvokedTcs.Task.IsCompleted` with the reason literal `the injected RemainingEmailLoader must be invoked by the started worker`; the documentation no longer mentions a bounded timeout. + - Acceptance (recorded by P3-T15): the test asserts the loader-invoked signal with the reason literal `the injected RemainingEmailLoader must be invoked by the started worker`; the documentation no longer mentions a bounded timeout. - [ ] [P3-T11] Apply Delivered Source Z2 to `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. - Acceptance (recorded by P3-T15): the zero-batch file has `.Wait(` 0, `new BackgroundWorker()` 0, `new SynchronousBackgroundWorker()` 3 and `WorkerStarter = StartSynchronously;` 3. - [ ] [P3-T12] Apply Delivered Source R-BODY (the baseline pin taken inside the gate: four lines inserted after pre-edit line 214, pre-edit lines 215 to 258 re-indented by four spaces, one closing line inserted after pre-edit line 258) to `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. This task runs before P3-T13, so the pre-edit line numbers are current. @@ -858,8 +858,8 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance (recorded by P3-T15): `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, and one line each containing `The gate-free fixture method EnsureDispatcher seeds the parked dispatcher`, `cannot restore a null previous value between the pin` and `(W2), and UiThread.Initialize (W5) must not latch`. - [ ] [P3-T14] Replace the class remarks (pre-edit lines 23 to 31) of QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z-R. - Acceptance (recorded by P3-T15): the zero-batch file has `starts a real` 0 and one line containing `so no test starts a`. -- [ ] [P3-T15] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);", "TaskCreationOptions.RunContinuationsAsynchronously", "IsBusy", "starts a real", "so no test starts a"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "cannot restore a null previous value between the pin", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL` (the P0-T12 token list of each) and `CMD-TIMESPAN`. - - Acceptance (fourteen THREE tokens in the order listed): liveness 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0; teardown 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0; zero-batch 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1; R4 file `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, `[Timeout(GateTimeoutMs)]` 8, the constant 1, the three R-DOC tokens 1 each; `R4SPAN` 1, 1, 2, 1, 1; `R4PRE` 0, 0; `R4HEAD` 1, 1; `R4TAIL` 3; `TIMESPAN-UNCLASSIFIED: 0`, with every `TIMESPAN` line `CLASSIFIED`. Any other value: correct the edit and re-run this task. (The first eleven values of each THREE row are the executor's round 1 rows; the last three columns are added by revision R1 for the stale-comment edits.) +- [ ] [P3-T15] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);", "TaskCreationOptions.RunContinuationsAsynchronously", "IsBusy", "starts a real", "so no test starts a"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "cannot restore a null previous value between the pin", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL` (the P0-T12 token list of each) and `CMD-TIMESPAN`, plus a second `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"the synchronous starter must reach the injected RemainingEmailLoader", "the synchronous starter must reach the injected loader before returning", "the finally around the awaited loader must clear the flag once it completes", "the finally must clear the flag on the throwing path too", "private static void StartSynchronously(BackgroundWorker worker)", "the injected RemainingEmailLoader must be invoked by the started worker", "bounded timeout"`). + - Acceptance (fourteen THREE tokens in the order listed): liveness 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0; teardown 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0; zero-batch 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1; reason-literal rows (seven tokens in the order listed): liveness 1, 1, 1, 1, 1, 0, 0; teardown 1, 0, 0, 0, 1, 0, 0; zero-batch 0, 0, 0, 0, 1, 1, 0; R4 file `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, `[Timeout(GateTimeoutMs)]` 8, the constant 1, the three R-DOC tokens 1 each; `R4SPAN` 1, 1, 2, 1, 1; `R4PRE` 0, 0; `R4HEAD` 1, 1; `R4TAIL` 3; `TIMESPAN-UNCLASSIFIED: 0`, with every `TIMESPAN` line `CLASSIFIED`. Any other value: correct the edit and re-run this task. (The first eleven values of each THREE row are the executor's round 1 rows; the last three columns are added by revision R1 for the stale-comment edits.) ### Phase 4 — Scoped Format, Pass-After Runs and Implementation Commit @@ -950,7 +950,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. - [ ] [P6-T5] Run the final repository-wide test-and-coverage run by the P0-T15 route (`CMD-COVERAGE-RUNNER` or `CMD-COVERAGE-DIRECT` with `STAGE` final, then `CMD-COVERAGE-POST` with `STAGE` final) and record FEATURE/evidence/qa-gates/coverage-post-change.md. - Artifact: the same fields as P0-T16, with `ITERATION:` and `FAILED-SET:` recorded as `FINAL-FAILED-SET:`. - - Acceptance, all required: the route equals P0-T15's; `TRX_PRESENT: True`; `SEQUENCE_FILES:` 0 under DIRECT; `QFCDATAMODEL-CLASS-NODES: 0`; the nine `RESULT` lines all `Passed`; `NEW-FAILURES:` (names in `FINAL-FAILED-SET:` absent from `BASELINE-FAILED-SET:`) is `NONE`; `LINE-FLOOR:` and `BRANCH-FLOOR:` each `MET`, or `NOT MET` only where P0-T16 recorded the same floor as not met; the `First-party coverage:` line is recorded as the numeric post-change headline. `RUNNER-GREEN: YES` is recorded when the route is RUNNER and `RUNNER_EXIT_CODE: 0`, otherwise `RUNNER-GREEN: NO` with the reason (`COVERAGE-ROUTE DIRECT` or `PRE-EXISTING FAILURES`). A failure of any target test or a new failure attributable to the Write Set invokes the D-13 failure restart; a new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"); any other new failure is `NEW FAILURE OUTSIDE SCOPE`; both are a stop and report, without re-running. + - Acceptance, all required: the route equals P0-T15's; `TRX_PRESENT: True`; `SEQUENCE_FILES:` 0 under DIRECT; `QFCDATAMODEL-CLASS-NODES: 0`; the nine `RESULT` lines all `Passed`; `NEW-FAILURES:` (names in `FINAL-FAILED-SET:` absent from `BASELINE-FAILED-SET:`) is `NONE`; `FIGURES-COMPARED:` restates the `Total`, `executed`, `error`, `timeout`, `aborted` and `notExecuted` figures from the P0-T16 summary block and from this run's summary block, and this run holds `Total` equal to the P0-T16 value (this plan adds and removes no test method), `executed` not less than the P0-T16 value, and each of `error`, `timeout`, `aborted` and `notExecuted` not greater than its P0-T16 value, because the failed set lists only results whose outcome is `Failed` and cannot show an aborted or unexecuted test; `LINE-FLOOR:` and `BRANCH-FLOOR:` each `MET`, or `NOT MET` only where P0-T16 recorded the same floor as not met; the `First-party coverage:` line is recorded as the numeric post-change headline. `RUNNER-GREEN: YES` is recorded when the route is RUNNER and `RUNNER_EXIT_CODE: 0`, otherwise `RUNNER-GREEN: NO` with the reason (`COVERAGE-ROUTE DIRECT` or `PRE-EXISTING FAILURES`). A failure of any target test or a new failure attributable to the Write Set invokes the D-13 failure restart; a new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"); any other new failure, and any `FIGURES-COMPARED:` breach, is `NEW FAILURE OUTSIDE SCOPE`; both are a stop and report, without re-running. - [ ] [P6-T6] Compare baseline and post-change coverage and test outcomes and record FEATURE/evidence/qa-gates/coverage-comparison.md from FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-post-change.md. - Acceptance: the artifact records the baseline and post-change `First-party coverage:` lines (lines and branches, numeric), the two `ROOT` lines, and the repository-wide comparison in exactly one named branch: `BRANCH A` when the two `lines-valid` figures differ by at most 1 percent of the baseline figure (the post-change line rate must not be lower than baseline by more than 0.5 percentage points), otherwise `BRANCH B` (recorded and not gated, with one sentence stating the denominators are not comparable). New and changed code: `CHANGED-PRODUCTION-COVERAGE: NOT MEASURED` with the reason `[ExcludeFromCodeCoverage]` at QuickFiler/Controllers/QfcDatamodel.cs line 25 and `QFCDATAMODEL-CLASS-NODES: 0` at both stages; the changed test files are outside the instrumented set. The artifact also restates `BASELINE-FAILED-SET:`, `FINAL-FAILED-SET:` and `NEW-FAILURES: NONE`. A Branch A breach is `COVERAGE REGRESSION`: stop. - [ ] [P6-T7] Record the final toolchain pass in FEATURE/evidence/qa-gates/toolchain-final-pass.md from the P6-T1 to P6-T5 artifacts of the final iteration. From 24494464c24ff903840f03906da6f1d429f6e230 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:09:11 -0400 Subject: [PATCH 09/24] docs(950): record preflight clearance after three rounds --- .../preflight-clearance.2026-10-02T04-30.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance.2026-10-02T04-30.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance.2026-10-02T04-30.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance.2026-10-02T04-30.md new file mode 100644 index 000000000..24bbc78dc --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance.2026-10-02T04-30.md @@ -0,0 +1,34 @@ +# Preflight Clearance — Issue #950 + +Timestamp: 2026-10-02T04-30 +Command: atomic-executor delegation under `DIRECTIVE: PREFLIGHT VALIDATION ONLY` (non-isolated, read-only probes in the item worktree and the session scratchpad) +EXIT_CODE: 0 +Output Summary: The plan cleared executor preflight on round 3 with no remaining defects. + +PREFLIGHT: ALL CLEAR + +CONVERGENCE: NO FURTHER ROUNDS EXPECTED + +- Plan: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md` (1079 lines) +- Plan blob SHA cleared: `9e9f5186d7ff8424764ef7cf2f0180f2d12e6b23` +- Branch head at clearance: `71669a78d9def6dbfb8799c27ad315e336005076` (branch `bug/quickfiler-tests-depend-on-wall-clock-timing-950`) +- Preflight rounds: 3 +- MCP plan validator (`validate_orchestration_artifacts`, artifact_type `plan`): ok on the authored plan and after each revision. + +## Round history + +| Round | Plan commit | Signal | Defects | Resolution | +|---|---|---|---|---| +| 1 | `c16cfea0d` | PREFLIGHT: REVISIONS REQUIRED | 7 | Deltas applied at `8e8961f00`. The orchestrator moved the R4 baseline pin inside the transaction gate (spec commit `e3827fb2c`) to close the W4 path; this superseded defects 2 and 3 as written. | +| 2 | `8e8961f00` | PREFLIGHT: REVISIONS REQUIRED | 5 | Verbatim deltas applied at `71669a78d`. | +| 3 | `71669a78d` | PREFLIGHT: ALL CLEAR | 0 | None required. | + +## Non-blocking residuals reported by round 3 + +The plan header (line 8, line 10, change log), the self-review record (lines 1013-1023) and the trailer (line 1078) still describe round 1 or request round 2. No task reads that text. It was left unchanged so that the cleared blob is the blob executed. + +## Execution notes carried from preflight + +- Execution must run non-isolated: pwsh, dotnet and msbuild are refused under worktree isolation. +- The item worktree has no `.dotnet-sdk` and no `packages/`; Phase 0 bootstraps both before first use. +- The plan's Risks section records a pre-existing exposure: disposal of the R4 pin scope can reset the shared dispatcher field to null while a concurrently running FocusAndTheme theme test relies on a discarded ensure scope. If observed, the run stops with `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED`. From 5387d9b6d44df72470608892463fa1ce02138925 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:39:14 -0400 Subject: [PATCH 10/24] docs(950): re-anchor plan diff gates to the post-merge main base (revision R2) Co-Authored-By: Claude Opus 5.5 --- .../plan.2026-10-01T07-11.md | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 9e9f5186d..8acff8c6f 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -33,6 +33,13 @@ Three delegation instructions are applied in an adjusted form. Each adjustment k - Defect 7: P3-T1 and P3-T6 acceptance replaced. - Defect 8: liveness test 2 summary rewritten inside L3; zero-batch class remarks rewritten by new task P3-T14 (Delivered Source Z-R); the census formerly numbered P3-T14 is now P3-T15, and every reference to it is renumbered. +## Revision R2 change log (orchestrator re-anchor at execution start, standing authority) + +- Cause: the execution run merged origin/main into the branch as its first action (merge commit `b8fcc0f8a`). After that merge, `git merge-base origin/main HEAD` prints `34c2ed88cbb009f2f231453db87bc64d45a9bd51`, so the P0-T3 checks against the original anchor would stop with `BASE-SHA MISMATCH`, `INHERITED SET OUT OF SCOPE` and `CITED TREE ADVANCED` although no cited file changed. +- Change: every occurrence of the original anchor in a command, in D-9 and in the Execution conventions `BASE` token (thirteen occurrences) is replaced by `34c2ed88cbb009f2f231453db87bc64d45a9bd51`. Fact 11 keeps the original cut point as history in its abbreviated form. No task, acceptance criterion, Delivered Source block or line citation changes. +- Citation validity: `git diff --stat 9b3eea584 34c2ed88c` over QuickFiler/Controllers, QuickFiler.Test/Controllers, QuickFiler.Test/SetupAssemblyInitializer.cs, both QuickFiler project files, QuickFiler.Test/packages.config, UtilitiesCS/Threading, scripts, TaskMaster.runsettings, .csharpierignore, global.json and dotnet-tools.json prints nothing, so every line citation made against the original anchor holds at the new one. The only .gitignore change on main is one line appended at 258, below the cited lines 146, 150 and 151. +- Negative control: the same P0-T3 commands run against the original anchor fail (merge-base differs; the name-status list contains main's paths outside FEATURE), so the re-anchored checks still discriminate. Against the new anchor, the name-status list before execution is exactly the five FEATURE files plus the promoted record. + ## Requirement sources - Acceptance criteria: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, section `## Acceptance Criteria`, lines 266 to 282: seventeen checkbox lines `- [ ] AC1:` through `- [ ] AC17:`, each on one line. The check-off edit changes only `- [ ] ACn:` to `- [x] ACn:`. @@ -103,7 +110,7 @@ Line totals below are content-line counts (the count a line-oriented reader retu 8. `scripts/vscode/TaskMaster.cli.runsettings` lines 4 to 7 set Workers 0 and Scope ClassLevel. `scripts/vscode/Invoke-MSTestWithCoverage.ps1` (461 lines): `Get-DotnetCoverageArgumentList` appends `/InIsolation`, `/TestCaseFilter:TestCategory!=LiveOutlook`, the results directory and the trx logger at 89 to 93 with no extension point; `Invoke-DotnetCoverageCollection` throws `MSTest with coverage failed with exit code` at 262 after the collector exits non-zero, before post-processing; defaults `coverage\test-results` and `mstest-coverage-run.trx` at 297 to 298; discovery 348 to 355 filters `bin\Debug` and a `.claude` segment relative to the search root; post-processing 399 to 402; threshold gate 406 to 409; `First-party coverage:` line printed at 410; JaCoCo projection 415 to 423; trx summary 430 to 447; raw document retained when written to the repository coverage directory 449 to 453; entry guard 459 to 461, so dot-sourcing is safe. `Invoke-MSTest.TrxSummary.ps1`: `Get-TrxRunSummary` 12 to 101, `Format-TrxRunSummary` 103 to 150 (its last line is `Failed tests: ` followed by names or `none`). `Invoke-MSTestWithCoverage.FirstParty.ps1` 117 to 120 renders `First-party coverage: lines a/b (p%), branches c/d (q%)`. 9. `QuickFiler.Test/QuickFiler.Test.csproj` lists the four test files explicitly (157, 161, 183, 203), `OutputPath` `bin\Debug\` (35), analyzer items 530 to 561 including `Meziantou.Analyzer.3.0.290` (554) and `Roslynator.Analyzers.5.0.0` (555 to 558); `QuickFiler.Test/packages.config` pins Meziantou.Analyzer 3.0.290 (11) and Roslynator.Analyzers 5.0.0 (52). `QuickFiler/QuickFiler.csproj` lists `Controllers\QfcDatamodel.cs` at 325, `OutputPath` `bin\Debug\` (24). 10. `.gitignore`: `*.trx` at 146, `coverage/*` at 150, `!coverage/.gitkeep` at 151. `.csharpierignore` excludes `**/evidence/**` (4) and `*.csproj` (12); there is no `.csharpierrc`, so the default print width of 100 applies. `global.json` pins SDK 8.0.205 under `.dotnet-sdk` with `latestFeature` roll-forward (3 to 8); `dotnet-tools.json` pins csharpier 1.2.6 (6). `scripts/vscode/Install-RepoDotNetSdk.ps1` defaults to version 8.0.205; `scripts/vscode/Invoke-Restore.ps1` takes SolutionPath, Configuration and Platform. -11. Branch `bug/quickfiler-tests-depend-on-wall-clock-timing-950`, cut at `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` (worktree reflog line 1); at the original authoring, HEAD was `8bbd48f68f9631247ccc3c58fa232ff333d4fe56` after four documentation commits; for revision R1 the delegation states HEAD `e3827fb2c`, the orchestrator's spec-only commit (spec.md lines 72, 131 and AC13 at 278 re-read in this pass; the acceptance-criteria lines are still 266 to 282). HEAD is recorded as an observation in P0-T3, never as an expectation. `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` exists on the tree. The `.dotnet-sdk`, `packages` and `bin` trees are absent (orchestrator environment fact 1), so every bootstrap task is guarded and gated on its post-task marker. +11. Branch `bug/quickfiler-tests-depend-on-wall-clock-timing-950`, originally cut at `9b3eea584` (worktree reflog line 1; the original anchor, superseded by Revision R2); at the original authoring, HEAD was `8bbd48f68f9631247ccc3c58fa232ff333d4fe56` after four documentation commits; for revision R1 the delegation states HEAD `e3827fb2c`, the orchestrator's spec-only commit (spec.md lines 72, 131 and AC13 at 278 re-read in this pass; the acceptance-criteria lines are still 266 to 282). HEAD is recorded as an observation in P0-T3, never as an expectation. `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` exists on the tree. The `.dotnet-sdk`, `packages` and `bin` trees are absent (orchestrator environment fact 1), so every bootstrap task is guarded and gated on its post-task marker. 12. Host constraint carried from sibling items in this parallel run: four UtilitiesCS.Test shell-icon classes (`HelperClasses.ShellUtilities_Tests`, `HelperClasses.ShellUtilitiesStatic_Tests`, `HelperClasses.SysImageListHelperTests`, `EmailIntelligence.OSBrowser_Tests`) have stalled vstest on this workstation, and CI executes them. Whether the stall reproduces today is unknown, so P0-T15 measures it and the result selects the coverage route (D-6). ## Design decisions (do not redesign) @@ -116,7 +123,7 @@ Line totals below are content-line counts (the count a line-oriented reader retu - **D-6 Coverage route is selected by a recorded observation.** P0-T15 runs the four shell-icon classes alone and records `STALL-PROBE: CLEAR` or `REPRODUCES`. `COVERAGE-ROUTE: RUNNER` (CLEAR) runs `scripts/vscode/Invoke-MSTestWithCoverage.ps1` verbatim (CLAUDE.md step 4). `COVERAGE-ROUTE: DIRECT` (REPRODUCES) issues the runner's own inner collector invocation with the four-class exclusion appended and post-processes with the runner's own helpers, because the runner hard-codes its filter (fact 8). Both routes yield the same committed forms: the `First-party coverage:` line, the JaCoCo package projection text and the trx-derived summary, transcribed into Markdown. Under DIRECT, AC17 cannot be met as worded (the runner was not run) and its check-off records `AC17: NOT MET (ENVIRONMENTAL: COVERAGE-ROUTE DIRECT)`. - **D-7 Coverage obligations.** `QfcDatamodel` is type-level `[ExcludeFromCodeCoverage]` (fact 1), so its file has no class element in the Cobertura document and no per-file coverage judgment is available in either direction; P0-T16 and P6-T5 record `QFCDATAMODEL-CLASS-NODES:` and the gate is that it is 0 at both stages. Test assemblies are excluded from instrumentation by the runner's derived settings. The first-party line and the root counters are recorded at both stages; the merged repository-wide rate is compared in two branches (denominators within 1 percent: tolerance 0.5 percentage points; otherwise recorded, not gated), because that rate is not reproducible across runs of an identical tree. The 80 percent line and 75 percent branch floors are applied by the runner (or by the runner's own threshold functions under DIRECT). - **D-8 Baseline-relative test outcomes.** The baseline full run (P0-T16) may contain pre-existing failures; they are recorded as `BASELINE-FAILED-SET:` and do not stop Phase 0. The final run passes only when its failed set contains no name absent from the baseline failed set (`NEW-FAILURES: NONE`) and all nine target tests are `Passed`. AC17 additionally requires the runner to exit 0; a final run whose only failures are baseline failures completes P6-T5 but records `AC17: NOT MET (PRE-EXISTING FAILURES)` at P6-T28, and the orchestrator decides. -- **D-9 Anchor.** Every diff gate uses `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` as its ref operand (`BASE` in prose). P0-T3 verifies it is an ancestor of HEAD and equals `git merge-base origin/main HEAD`; a mismatch is `BASE-SHA MISMATCH`: stop and report (the orchestrator re-anchors). Paths changed between BASE and HEAD at P0-T3 form the inherited set `INHERITED-COMMITTED:`; each must be under FEATURE or be exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md`, otherwise `INHERITED SET OUT OF SCOPE`: stop. +- **D-9 Anchor.** Every diff gate uses `34c2ed88cbb009f2f231453db87bc64d45a9bd51` as its ref operand (`BASE` in prose). P0-T3 verifies it is an ancestor of HEAD and equals `git merge-base origin/main HEAD`; a mismatch is `BASE-SHA MISMATCH`: stop and report (the orchestrator re-anchors). Paths changed between BASE and HEAD at P0-T3 form the inherited set `INHERITED-COMMITTED:`; each must be under FEATURE or be exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md`, otherwise `INHERITED SET OUT OF SCOPE`: stop. - **D-10 Commits.** Three commits: P0-T17 (FEATURE only), P4-T7 (the five code files plus FEATURE, staged after the P4-T1 scoped format so the committed text is formatter-stable), P6-T32 (FEATURE). Each is a `git -C WORKTREE add -- ` invocation followed by a separate `git -C WORKTREE commit -m ""` invocation, one command per call, never chained, never `git add -A`. No commit message contains an angle bracket, a dollar sign or a backtick. A PreToolUse refusal of any `git add`, `git commit`, `.cs` edit or spec edit is recorded verbatim as `PRE-IMPLEMENTATION GATE BLOCKED` and stops the run; the executor does not modify hooks, checkpoints or permission configuration. - **D-11 Git gates are pathspec-scoped and anchored.** Every `git diff` names BASE or HEAD as its ref operand; every name-listing diff is paired with a porcelain span in the same task; no gate asserts an unscoped empty porcelain. Uncommitted `.claude/agent-memory/` paths and this plan file may appear in porcelain output and are admitted by every scope gate. - **D-12 Check-offs follow the loop.** Every check-off task sits in Phase 6 after the final toolchain pass and reads an artifact that survived it. Each flips exactly one checkbox and, when its evidence does not hold, completes with the box unchecked and records `ACn: NOT MET` with the reason in `FEATURE/evidence/other/ac-status-summary.md`. @@ -508,7 +515,7 @@ The `using` header is shown split because the one-line form `using (IDisposable ## Execution conventions -- **Tokens.** `WORKTREE` denotes the absolute path of the item worktree supplied in the delegation prompt; the executor substitutes it into every payload and every `git -C` argument at run time and writes the token, never the path, into artifacts. `FEATURE` abbreviates the feature folder path. `BASE` denotes `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f`, which is written in full in every command. No artifact, and no line of this plan, carries an absolute host path, an account name or a machine name. +- **Tokens.** `WORKTREE` denotes the absolute path of the item worktree supplied in the delegation prompt; the executor substitutes it into every payload and every `git -C` argument at run time and writes the token, never the path, into artifacts. `FEATURE` abbreviates the feature folder path. `BASE` denotes `34c2ed88cbb009f2f231453db87bc64d45a9bd51`, which is written in full in every command. No artifact, and no line of this plan, carries an absolute host path, an account name or a machine name. - **Payload channel.** Each indented payload in the Command Reference runs as one Bash tool call of the form `pwsh -NoProfile -Command ''`, newlines included, with the substitutions applied. Payloads use double quotes only, so the outer single quotes never conflict. Git commands run as single Bash calls of the form `git -C WORKTREE `, never chained. No `cd`, no `&&`, `;` or `|` between Bash commands. - **Command rows.** The `Command:` field of a payload artifact records the full payload as executed, with `WORKTREE` in place of the path, followed on the next line by the canonical command it implements (for example `msbuild TaskMaster.sln /t:Rebuild ...`). Every payload artifact records `WORKTREE-LEAF: agent-a7805823735145ca4`; any other value means the payload ran in the wrong tree, and the task fails. - **Exit codes.** `EXIT_CODE:` records the payload's principal exit value as named in each Command Reference entry. Deliberately failing runs carry `ExpectedExitCode:` equal to the deterministic value the task states. A recorded observation whose exit value is not gated carries `ExpectedExitCode:` equal to the observed value and says so. @@ -741,7 +748,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma **CMD-ADDED-SCAN** (added lines of the anchored diff over the five code files): PREFIX - $diff = @(git diff 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- CODE5-GIT) + $diff = @(git diff 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- CODE5-GIT) Write-Output ("GIT_DIFF_EXIT_CODE: " + $LASTEXITCODE) $added = @($diff | Where-Object { $_.StartsWith("+") -and -not $_.StartsWith("+++") }) Write-Output ("ADDED_LINES: " + $added.Count) @@ -756,8 +763,8 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P0-T2] Read FEATURE/spec.md, FEATURE/issue.md and FEATURE/research/2026-10-01T00-00-wall-clock-waits-research.md in full and record the Write Set, the prohibited paths and the acceptance-criteria inventory in FEATURE/evidence/baseline/scope-and-anchor.md (this task creates the file; P0-T3 appends to it). - Acceptance: the artifact lists the five code paths of the Write Set verbatim; names the prohibited paths from the Write Set section; records that issue.md line 12 reads `- Work Mode: full-bug`; and records, counted from the file, that spec.md holds exactly 17 lines beginning `- [ ] AC` and 0 lines beginning `- [x] AC`. - [ ] [P0-T3] Record the anchor and the pre-change tree state by appending to FEATURE/evidence/baseline/scope-and-anchor.md. - - Commands, each a separate Bash call: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse --abbrev-ref HEAD`; `git -C WORKTREE merge-base --is-ancestor 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE merge-base origin/main HEAD`; `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE diff --exit-code 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD -- QuickFiler QuickFiler.Test UtilitiesCS/Threading/UiThread.cs scripts/vscode TaskMaster.runsettings`; `git -C WORKTREE status --porcelain --untracked-files=all`. - - Acceptance, all required: `HEAD-SHA:` records the first output as an observation; `BRANCH:` reads `bug/quickfiler-tests-depend-on-wall-clock-timing-950`; the ancestor check exits 0 and the merge-base command prints exactly `9b3eea58447c264eae6f95a4bfee3bfcec7fb17f` (otherwise `BASE-SHA MISMATCH`: record both values and stop); `INHERITED-COMMITTED:` lists every name-status line or `NONE`, and every listed path is under FEATURE or is exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` (otherwise `INHERITED SET OUT OF SCOPE`: stop); the scoped `--exit-code` diff exits 0, recorded as `CODE-TREE-AT-BASE: UNCHANGED` (otherwise `CITED TREE ADVANCED`: stop, because every line citation in this plan is against BASE); `PRE-EXISTING-WORKTREE-PATHS:` lists every porcelain line verbatim or `NONE`, and no line names a path under QuickFiler/ or QuickFiler.Test/ (otherwise `CODE TREE DIRTY AT ANCHOR`: stop). The porcelain output is not asserted empty. + - Commands, each a separate Bash call: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse --abbrev-ref HEAD`; `git -C WORKTREE merge-base --is-ancestor 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE merge-base origin/main HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- QuickFiler QuickFiler.Test UtilitiesCS/Threading/UiThread.cs scripts/vscode TaskMaster.runsettings`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Acceptance, all required: `HEAD-SHA:` records the first output as an observation; `BRANCH:` reads `bug/quickfiler-tests-depend-on-wall-clock-timing-950`; the ancestor check exits 0 and the merge-base command prints exactly `34c2ed88cbb009f2f231453db87bc64d45a9bd51` (otherwise `BASE-SHA MISMATCH`: record both values and stop); `INHERITED-COMMITTED:` lists every name-status line or `NONE`, and every listed path is under FEATURE or is exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` (otherwise `INHERITED SET OUT OF SCOPE`: stop); the scoped `--exit-code` diff exits 0, recorded as `CODE-TREE-AT-BASE: UNCHANGED` (otherwise `CITED TREE ADVANCED`: stop, because every line citation in this plan is against BASE); `PRE-EXISTING-WORKTREE-PATHS:` lists every porcelain line verbatim or `NONE`, and no line names a path under QuickFiler/ or QuickFiler.Test/ (otherwise `CODE TREE DIRTY AT ANCHOR`: stop). The porcelain output is not asserted empty. - [ ] [P0-T4] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record it in FEATURE/evidence/baseline/bootstrap-sdk.md. - Command: `pwsh -NoProfile -Command 'PREFIX; if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version; "DOTNET_EXIT=$LASTEXITCODE"'` (PREFIX expanded to its three lines, joined with semicolons). - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `SDK_MARKER=True`, `DOTNET_EXIT=0`, and the version line is a version string rather than the global.json `errorMessage` text. The installer's filesystem marker is the gate; version equality is not asserted because global.json rolls forward within the feature band. @@ -866,7 +873,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P4-T1] Format the five Write Set code files with a scoped CSharpier pass and record the before-and-after hashes in FEATURE/evidence/qa-gates/scoped-format.md. - Command: `CMD-HASH`; then `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier format QuickFiler\Controllers\QfcDatamodel.cs QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH` again. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `CSHARPIER_EXIT_CODE: 0`; the line beginning `Formatted ` is transcribed and labelled as a processed-file count, not a rewrite count; `REWRITTEN:` lists every CODE5 path whose hash differs between the two captures, or `NONE` (the rewrite observation is the hash difference, not the console line). Either value completes this task: the pass exists so the committed text is formatter-stable. -- [ ] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T15 commands, plus `git -C WORKTREE diff --numstat 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [ ] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T15 commands, plus `git -C WORKTREE diff --numstat 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: every P2-T4 and P3-T15 value holds after formatting; CMD-LINECOUNT reports at most 500 for each CODE5 file (expected 495 for QfcDatamodel.cs; the others are recorded); numstat for QfcDatamodel.cs reads `14 2`, and the two deleted lines in the anchored diff both have the trimmed text `worker.RunWorkerAsync();`; the porcelain span lists exactly the five CODE5 paths with status ` M`. This artifact is the evidence for AC1, AC2, AC3 and AC14 and the census half of AC6 to AC13. - [ ] [P4-T3] Build the fixed tree with `CMD-BUILD` (`TASKID` p4-t3) and record it in FEATURE/evidence/regression-testing/pass-after-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_QUICKFILER_TEST:` at least 1. @@ -877,7 +884,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P4-T6] Confirm the R4 pass-after condition from FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/regression-testing/targets-pass-after.md by appending a `R4-PASS-AFTER:` section to FEATURE/evidence/regression-testing/r4-fail-before.md. - Acceptance: the section quotes the R4 `RESULT` line from both pass-after artifacts (`Passed` in each) beside the P1-T6 `Failed` line, completing the fail-before and pass-after pair for Defect B. - [ ] [P4-T7] Commit the implementation (the five CODE5 files and FEATURE) and record FEATURE/evidence/qa-gates/implementation-commit.md. - - Commands, separate Bash calls: `git -C WORKTREE add -- QuickFiler/Controllers/QfcDatamodel.cs QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Commands, separate Bash calls: `git -C WORKTREE add -- QuickFiler/Controllers/QfcDatamodel.cs QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: both git writes exit 0; `IMPLEMENTATION-COMMIT:` records the new HEAD as an observation; the name-status diff lists the five CODE5 paths with status `M`, FEATURE paths, and at most the inherited promotion record, nothing else; no porcelain line names a path under QuickFiler/ or QuickFiler.Test/. This artifact is committed in P6-T32. ### Phase 5 — Negative Controls (temporary edits against the implementation commit) @@ -957,9 +964,9 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance: one row per step, in order — csharpier format (`REWRITTEN-WRITESET: NONE`, `REWRITTEN-OTHER: NONE`), csharpier check (exit 0), analyzer rebuild (exit 0), TreatWarningsAsErrors rebuild (exit 0), coverage run (route, exit code, `RUNNER-GREEN:`) — each with its exact command, `EXIT_CODE:` and the same `ITERATION:` value; `SINGLE-PASS: YES` when all five rows come from one iteration with no restart after P6-T1; `AC17-STATUS: MET` only when `SINGLE-PASS: YES` and `RUNNER-GREEN: YES`, otherwise `AC17-STATUS: NOT MET` with the reason. - [ ] [P6-T8] Record the AC4 wall-clock census in FEATURE/evidence/qa-gates/wall-clock-tokens.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "Task.Wait("`) and `CMD-TIMESPAN`. - Acceptance: every count 0 in all three files; `TIMESPAN-UNCLASSIFIED: 0` with every `TIMESPAN` line `CLASSIFIED` (the surviving lines are the teardown file's two `QuiesceLoaderAsync(` production arguments and one `fake.Advance(` and the liveness file's three `fake.Advance(` lines); the artifact cites the non-zero P0-T12 counts as the positive control. -- [ ] [P6-T9] Record the AC5 prohibited-construct gate in FEATURE/evidence/qa-gates/prohibited-constructs.md with `CMD-ADDED-SCAN`, `git -C WORKTREE diff --exit-code 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, `git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, and `CMD-TOKEN-COUNT` on the R4 file (TOKENS `"[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`). +- [ ] [P6-T9] Record the AC5 prohibited-construct gate in FEATURE/evidence/qa-gates/prohibited-constructs.md with `CMD-ADDED-SCAN`, `git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, `git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, and `CMD-TOKEN-COUNT` on the R4 file (TOKENS `"[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`). - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `GIT_DIFF_EXIT_CODE: 0`; `ADDED_LINES:` greater than 0; `ADDED-TOKEN` counts 0 for `Thread.Sleep`, `Task.Delay`, `DoNotParallelize`, `Retry(` and `Timeout(`, and at least 1 for `WorkerStarter` (positive control); the runsettings diff exits 0 and the porcelain span prints nothing; the R4 file reads 8 and 1, equal to P0-T12. -- [ ] [P6-T10] Record the footprint gate in FEATURE/evidence/qa-gates/footprint-scope.md with `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD` paired with `git -C WORKTREE status --porcelain --untracked-files=all`. +- [ ] [P6-T10] Record the footprint gate in FEATURE/evidence/qa-gates/footprint-scope.md with `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD` paired with `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: `THIS-ITEM-FOOTPRINT:` (name-status paths outside FEATURE, excluding any path P0-T3 listed in `INHERITED-COMMITTED:`) is exactly the five CODE5 paths with status `M`; the excluded inherited paths are recorded as `INHERITED-AND-EXCLUDED:` (at most `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md`, status `A`); no path ends in `.csproj`; no status `A` outside FEATURE other than an `INHERITED-AND-EXCLUDED:` path; no porcelain line names a path under QuickFiler/, QuickFiler.Test/ or scripts/. - [ ] [P6-T11] Record the evidence hygiene gate in FEATURE/evidence/qa-gates/evidence-hygiene.md by scanning every file under FEATURE/evidence/. - Command: `pwsh -NoProfile -Command 'PREFIX; $b = [char]92; $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950\evidence" -Recurse -File); "EVIDENCE_FILES=$($files.Count)"; "RAW_DOCUMENTS=$(@($files | Where-Object { $_.Extension -in @(".trx", ".xml", ".coverage", ".coveragexml", ".log") }).Count)"; $pattern = "[a-z]:[" + $b + $b + "/]+users[" + $b + $b + "/]+[a-z0-9_.~-]"; "PROFILE_PATH_LINES=$(@($files | Select-String -Pattern $pattern).Count)"'`. @@ -1005,7 +1012,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [ ] [P6-T31] Verify that FEATURE/spec.md changed only in its checkbox lines by recording `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` and `git -C WORKTREE diff HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, paired with `git -C WORKTREE status --porcelain -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, in a `## Spec check-off diff` section appended to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: added and deleted line counts are equal and equal the checked-off count; every deleted line begins `- [ ] AC` and every added line begins `- [x] AC` with identical remaining text. - [ ] [P6-T32] Commit the remaining feature evidence and check-offs (FEATURE only) and record FEATURE/evidence/qa-gates/final-commit.md. - - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record final QA evidence and acceptance check-offs"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 9b3eea58447c264eae6f95a4bfee3bfcec7fb17f HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. + - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record final QA evidence and acceptance check-offs"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: both git writes exit 0; `FINAL-COMMIT:` records HEAD as an observation; the name-status paths outside FEATURE are exactly the P6-T10 footprint; the porcelain output names no path under FEATURE other than this plan file (whose final check-off mark follows the commit) and FEATURE/evidence/qa-gates/final-commit.md (written after the commit), and no path under QuickFiler/, QuickFiler.Test/ or scripts/. The final-commit artifact and this plan's check-off marks are committed by the orchestrator with the plan file. ## Planner self-review and internal review record From 03c0d01eb517c7b4fe3a837bfede5a4fcf16669f Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:44:57 -0400 Subject: [PATCH 11/24] docs(950): record the confirming preflight for plan revision R2 Co-Authored-By: Claude Opus 5.5 --- ...preflight-clearance-r2.2026-10-02T12-10.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance-r2.2026-10-02T12-10.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance-r2.2026-10-02T12-10.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance-r2.2026-10-02T12-10.md new file mode 100644 index 000000000..93798a1f0 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance-r2.2026-10-02T12-10.md @@ -0,0 +1,26 @@ +# Confirming preflight for plan Revision R2 (issue 950) + +Timestamp: 2026-10-02T12-10 +Command: atomic-executor delegation, DIRECTIVE: PREFLIGHT VALIDATION ONLY, scoped to the R2 delta (git diff 71669a78d 5387d9b6d on the plan file) and the re-anchored checks +EXIT_CODE: 0 +Output Summary: PREFLIGHT: ALL CLEAR; CONVERGENCE: NO FURTHER ROUNDS EXPECTED; 0 defects; plan blob 8acff8c6ff2efff55994124e1da9e617b5928270 at commit 5387d9b6d. + +## Correction under review + +- Revision R2 replaces the original anchor `9b3eea584` with `34c2ed88cbb009f2f231453db87bc64d45a9bd51` in thirteen command-bearing places (P0-T3 four, D-9, the Execution conventions BASE token, CMD-ADDED-SCAN, P4-T2 two, P4-T7, P6-T9, P6-T10, P6-T32). Fact 11 keeps the original cut point as history. +- Trigger: origin/main was merged into the branch at `b8fcc0f8a` (clean merge; no Write Set file touched), which moved `git merge-base origin/main HEAD` to `34c2ed88c`. +- No task, acceptance criterion, Delivered Source block or line citation changed. The plan validator passes on the revised blob. + +## Reviewer observations recorded + +- `git merge-base origin/main HEAD` prints the new anchor; `git merge-base --is-ancestor` exits 0. +- `git diff --name-status` from the new anchor to HEAD lists six entries, all status A: five FEATURE files and the promoted record. This matches the INHERITED-COMMITTED expectation in P0-T3, P4-T7, P6-T10 and P6-T32. +- The P0-T3 scoped `--exit-code` diff against the new anchor exits 0. +- Negative control: the same name-status diff against the original anchor lists main's paths outside FEATURE, so the re-anchored gate still distinguishes a wrong anchor. +- Verified tree facts 1 to 5 and 8 to 10 were spot-checked at their cited lines and hold; the only .gitignore change on main is at line 258, below cited lines 146, 150 and 151. +- Tonality and host-path checks on the R2 prose: no findings. + +## Non-blocking observations (no delta) + +- The plan header Status, Version and Last Updated fields were already stale at the round-3 clearance and were not updated for R2. +- The R2 log path list omits QuickFiler/Interfaces/IQfcDatamodel.cs; a broader diff over all of QuickFiler shows only Viewers and .csproj.bak changes, so the claim holds. From b6be10a2d1245ca8cbccf05e427c56f2cca877f0 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 00:57:33 -0400 Subject: [PATCH 12/24] docs(950): record phase 0 baseline evidence Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../evidence/baseline/analyzer-alignment.md | 14 +++ .../baseline/bootstrap-dotnet-coverage.md | 12 +++ .../baseline/bootstrap-nuget-restore.md | 15 +++ .../evidence/baseline/bootstrap-sdk.md | 16 +++ .../baseline/bootstrap-tool-restore.md | 14 +++ .../evidence/baseline/census-baseline.md | 63 ++++++++++++ .../baseline/concurrent-classes-baseline.md | 53 ++++++++++ .../evidence/baseline/coverage-baseline.md | 97 +++++++++++++++++++ .../baseline/csharpier-check-baseline.md | 12 +++ .../baseline/msbuild-analyzer-baseline.md | 23 +++++ .../baseline/msbuild-nullable-baseline.md | 21 ++++ .../baseline/phase0-instructions-read.md | 18 ++++ .../evidence/baseline/scope-and-anchor.md | 66 +++++++++++++ .../evidence/baseline/stall-probe.md | 21 ++++ .../evidence/baseline/targets-baseline.md | 27 ++++++ .../plan.2026-10-01T07-11.md | 32 +++--- 16 files changed, 488 insertions(+), 16 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/analyzer-alignment.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-dotnet-coverage.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-nuget-restore.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-sdk.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-tool-restore.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/census-baseline.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/concurrent-classes-baseline.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/coverage-baseline.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/csharpier-check-baseline.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-analyzer-baseline.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-nullable-baseline.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-instructions-read.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/scope-and-anchor.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/stall-probe.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/targets-baseline.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/analyzer-alignment.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/analyzer-alignment.md new file mode 100644 index 000000000..7efbc3533 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/analyzer-alignment.md @@ -0,0 +1,14 @@ +# Analyzer-path alignment (P0-T7) + +Timestamp: 2026-10-02T00-49 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); $root = (Get-Location).Path; $rootLen = $root.TrimEnd([char]92).Length; $projs = @(Get-ChildItem -Path $root -Recurse -Filter "*.csproj" | Where-Object { $rel = $_.FullName.Substring($rootLen); $rel -notlike "\packages\*" -and $rel -notlike "\.claude\*" }); "PROJECTS=$($projs.Count)"; $missing = 0; $skew = 0; foreach ($p in $projs) { ... MISSING and SKEW checks exactly as plan P0-T7 ... }; "ANALYZER_MISSING=$missing"; "VERSION_SKEW=$skew"' +(The payload executed is the plan P0-T7 command verbatim with PREFIX expanded; the loop body is elided here only for length.) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +PROJECTS=18 +ANALYZER_MISSING=0 +VERSION_SKEW=0 + +No MISSING or SKEW lines were printed. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-dotnet-coverage.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-dotnet-coverage.md new file mode 100644 index 000000000..1cebc9160 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-dotnet-coverage.md @@ -0,0 +1,12 @@ +# Bootstrap: dotnet-coverage global tool (P0-T8) + +Timestamp: 2026-10-02T00-49 +Command: pwsh -NoProfile -Command 'if (-not (Get-Command dotnet-coverage -ErrorAction SilentlyContinue)) { dotnet tool install --global dotnet-coverage }; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"; dotnet-coverage --version; "EXIT=$LASTEXITCODE"' +(The trailing EXIT echo was added to observe the exit value; the rest is the plan P0-T8 command verbatim.) +WORKTREE-LEAF: not applicable (the command touches no repository path) +EXIT_CODE: 0 + +Output Summary: +DOTNET_COVERAGE_RESOLVED=True +18.10.0+f4cc39224845ffa74bf246c9da2399d50e5d6342 +The tool was already installed; no install ran. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-nuget-restore.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-nuget-restore.md new file mode 100644 index 000000000..c39b875d8 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-nuget-restore.md @@ -0,0 +1,15 @@ +# Bootstrap: NuGet restore (P0-T6) + +Timestamp: 2026-10-02T00-49 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); $env:MSBUILDDISABLENODEREUSE = "1"; & (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1"); "RESTORE_EXIT=$LASTEXITCODE"; "PACKAGE_DIRS=$(@(Get-ChildItem -LiteralPath packages -Directory -ErrorAction SilentlyContinue).Count)"' +Canonical command: scripts\vscode\Invoke-Restore.ps1 +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Installed: 172 package(s) to packages.config projects +Build succeeded. 0 Warning(s), 0 Error(s) +RESTORE_EXIT=0 +PACKAGE_DIRS=172 + +Transcription note: the console tail was trimmed for display by a second pwsh process reading the first one's output; the payload itself ran unchanged. Lines naming NuGet config files and feeds carried absolute paths and are omitted. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-sdk.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-sdk.md new file mode 100644 index 000000000..9f7d0b562 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-sdk.md @@ -0,0 +1,16 @@ +# Bootstrap: repository .NET SDK (P0-T4) + +Timestamp: 2026-10-02T00-48 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version; "DOTNET_EXIT=$LASTEXITCODE"' +Canonical command: scripts\vscode\Install-RepoDotNetSdk.ps1 (guarded on the .dotnet-sdk\sdk\8.0.205 marker) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Downloading .NET SDK 8.0.205 from https://builds.dotnet.microsoft.com/dotnet/Sdk/8.0.205/dotnet-sdk-8.0.205-win-x64.zip... +Installed repo-local .NET SDK 8.0.205 to REDACTED-PATH. +SDK_MARKER=True +8.0.205 +DOTNET_EXIT=0 + +The marker was absent, so the installer ran; the version line is a version string, not the global.json errorMessage text. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-tool-restore.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-tool-restore.md new file mode 100644 index 000000000..e27cf9f2c --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/bootstrap-tool-restore.md @@ -0,0 +1,14 @@ +# Bootstrap: manifest tool restore (P0-T5) + +Timestamp: 2026-10-02T00-49 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); dotnet tool restore; "RESTORE_EXIT=$LASTEXITCODE"; dotnet tool list --local; dotnet tool run csharpier check --help | Out-Null; "CHECK_HELP_EXIT=$LASTEXITCODE"' +Canonical command: dotnet tool restore +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Tool 'csharpier' (version '1.2.6') was restored. Available commands: csharpier +Restore was successful. +RESTORE_EXIT=0 +Local tool row (Package Id, Version): csharpier, 1.2.6 +CHECK_HELP_EXIT=0 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/census-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/census-baseline.md new file mode 100644 index 000000000..d151a8476 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/census-baseline.md @@ -0,0 +1,63 @@ +# Pre-change census of the five code files (P0-T12) + +Timestamp: 2026-10-02T00-51 +Command: one pwsh -NoProfile -Command payload: PREFIX (Set-Location -LiteralPath "WORKTREE"; SetCurrentDirectory; WORKTREE-LEAF echo); then CMD-LINECOUNT and CMD-HASH over CODE5; CMD-TIMESPAN over THREE; CMD-TOKEN-COUNT once per code file (bodies verbatim, wrapped in a local function `Tok FILE TOKENS`); CMD-SPAN-TOKEN-COUNT for INITQ, R4SPAN, R4PRE, R4HEAD and R4TAIL (body verbatim, wrapped in a local function `Span LABEL FILE START END TOKENS`; a missing anchor sets a flag and the payload exits 4 at the end instead of mid-payload). Token lists exactly as plan P0-T12. +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 + +## CMD-LINECOUNT +LINES QuickFiler\Controllers\QfcDatamodel.cs = 483 +LINES QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 255 +LINES QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 235 +LINES QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 212 +LINES QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 458 + +## CMD-HASH +BASE-HASH: QuickFiler\Controllers\QfcDatamodel.cs = 0C8F7E7DDEB0F1E52843DF18244A8E792CD6127B419737839F748976EEB12B94 +BASE-HASH: QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 390B28D669815DE30C1D0724938FA2E58C889F18AF8DFF62090DD5F2FACA2FDF +BASE-HASH: QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 44A1952093125CB42891B01DC7FAD1A4C2B379E88D34082DFE040DD6BC74FAE6 +BASE-HASH: QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 4F350522F071BBF1B9890D570DC143027C979CA2CFC051054DE681E8A55EBB07 +BASE-HASH: QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 40EE455C7D2ACF6ADA5D01B8880B37A7BCBAF320CEC9F83A8B497015EAE56F52 + +## CMD-TIMESPAN +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:56 UNCLASSIFIED :: SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5)).Should().BeTrue(because); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:103 UNCLASSIFIED :: .Task.Wait(TimeSpan.FromSeconds(5)) +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:113 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:115 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:130 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:173 UNCLASSIFIED :: .Task.Wait(TimeSpan.FromSeconds(5)) +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:67 UNCLASSIFIED :: SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5)).Should().BeTrue(because); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:118 CLASSIFIED :: Task pending = model.QuiesceLoaderAsync(TimeSpan.FromSeconds(5)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:149 CLASSIFIED :: Task pending = model.QuiesceLoaderAsync(TimeSpan.FromSeconds(5)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:154 CLASSIFIED :: fake.Advance(TimeSpan.FromSeconds(6)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:220 UNCLASSIFIED :: .Task.Wait(TimeSpan.FromSeconds(5)) +TIMESPAN QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs:161 UNCLASSIFIED :: .Task.Wait(TimeSpan.FromSeconds(5)) +TIMESPAN-UNCLASSIFIED: 6 + +## CMD-TOKEN-COUNT (THREE; tokens SpinWait, .Wait(, WaitForState, new BackgroundWorker(), new SynchronousBackgroundWorker(), WorkerStarter = StartSynchronously;, IsBusy, starts a real) +Liveness: 1, 2, 5, 2, 0, 0, 6, 0 +Teardown: 1, 1, 2, 1, 0, 0, 0, 0 +Zero-batch: 0, 1, 0, 3, 0, 0, 2, 1 + +## CMD-TOKEN-COUNT QfcDatamodel.cs +TOKEN [WorkerStarter] = 0 +TOKEN [RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;] = 2 +TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2 + +## CMD-TOKEN-COUNT R4 file +TOKEN [flake-watch] = 1 +TOKEN [Append an observation] = 1 +TOKEN [Issue #950:] = 0 +TOKEN [[Timeout(GateTimeoutMs)]] = 8 +TOKEN [private const int GateTimeoutMs = 60000;] = 1 + +## CMD-SPAN-TOKEN-COUNT +INITQ: SPAN: 259-304; RunWorkerAsync 2; WorkerStarter(worker); 0 +R4SPAN: SPAN: 206-272; IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher() 0; EnsureUiThreadDispatcher() 0; using ( 1; .BeSameAs( 1; .NotBeSameAs( 1 +R4PRE: SPAN: 206-212; EnsureUiThreadDispatcher() 0; using ( 0 +R4HEAD: SPAN: 206-214; EnsureUiThreadDispatcher() 0; using ( 0 +R4TAIL: SPAN: 256-261; } 2 + +Verdict: every value equals the plan's expected pre-change value (facts 1 to 5). No CENSUS MISMATCH. The non-zero wall-clock counts are the positive control for the P6-T8 zero gates. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/concurrent-classes-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/concurrent-classes-baseline.md new file mode 100644 index 000000000..1c2a19e7a --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/concurrent-classes-baseline.md @@ -0,0 +1,53 @@ +# Pre-change concurrent run of the four target classes with QfcItemController_FocusAndThemeTests (P0-T14) + +Timestamp: 2026-10-02T00-53 +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-CONCURRENT (FullyQualifiedName~ expressions for QfcDatamodelLivenessTests., QfcDatamodelTeardownTests., QfcInitEmailQueueZeroBatchTests., QfcItemController_UiThreadDispatcherFixtureTests. and QfcItemController_FocusAndThemeTests., QCT. expanded, joined with |), TASKID p0-t14 and empty NAMES, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-CONCURRENT" "/ResultsDirectory:coverage\test-results\950\p0-t14" "/Logger:trx;LogFileName=p0-t14.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +BASELINE-CONCURRENT-COUNTERS: COUNTERS total=37 executed=37 passed=37 failed=0 +RESULT_COUNT: 37 +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Passed duration=00:00:00.1249035 +RESULT Install_CalledTwiceOnTheSameTransaction_ThrowsInvalidOperationException = Passed duration=00:00:00.0062973 +RESULT ToggleTipsAsync_WithEmptyCollections_Completes = Passed duration=00:00:00.0004655 +RESULT ToggleFocus_ParameterlessOverload_FromActive_DeactivatesUiAndSwitchesToNormalTheme = Passed duration=00:00:00.0012099 +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Passed duration=00:00:00.1703879 +RESULT HtmlDarkConverter_WhenWebViewNotInitialized_DoesNotNavigate = Passed duration=00:00:00.0005955 +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Passed duration=00:00:00.0007540 +RESULT ToggleFocusOnAsync_ActivatesUiAndSwitchesToActiveTheme = Passed duration=00:00:00.0023641 +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0033943 +RESULT SetThemeLight_FromNormal_SelectsLightNormalTheme = Passed duration=00:00:00.0002659 +RESULT ToggleTips_Synchronous_DispatchesAndExecutesDelegate = Passed duration=00:00:00.0005281 +RESULT ToggleSaveAttachments_DoesNotThrow = Passed duration=00:00:00.0002516 +RESULT InvokeBeginInvoke_WhenAsync_UsesBeginInvoke = Passed duration=00:00:00.0013229 +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Passed duration=00:00:00.0038194 +RESULT QuiesceLoaderAsync_LoaderHangs_ReturnsAtBoundAndLogs = Passed duration=00:00:00.0066437 +RESULT ToggleFocus_StateOverload_Off_FromActive_DeactivatesUiAndSwitchesToNormalTheme = Passed duration=00:00:00.0025793 +RESULT Transaction_DisposedTwice_DoesNotOverReleaseTheGate = Passed duration=00:00:00.0029089 +RESULT SetThemeDark_FromNormal_SelectsDarkNormalTheme = Passed duration=00:00:00.0004719 +RESULT ToggleNavigationAsync_AwaitsPositionTipsToggleAsync = Passed duration=00:00:00.0013005 +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Passed duration=00:00:00.1930378 +RESULT TransactionGate_WhileThisTestHoldsATransaction_HasExactlyOneUnreleasedAcquisition = Passed duration=00:00:00.0022432 +RESULT EnsureDispatcher_WhenTheFieldIsNull_InstallsAndRestoresOnDispose = Passed duration=00:00:00.0012895 +RESULT ToggleFocusOffAsync_DeactivatesUiAndSwitchesToNormalTheme = Passed duration=00:00:00.0008572 +RESULT ToggleFocus_ParameterlessOverload_MarshalsThroughItemViewerInvoke = Passed duration=00:00:00.0009876 +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Passed duration=00:00:00.0155993 +RESULT ToggleNavigation_Synchronous_TogglesPositionTips = Passed duration=00:00:00.0040634 +RESULT InvokeBeginInvoke_WhenSynchronous_UsesInvoke = Passed duration=00:00:00.0004919 +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Passed duration=00:00:00.0038885 +RESULT ToggleNavigation_WithState_TogglesPositionTipsWithState = Passed duration=00:00:00.0008005 +RESULT BeginTransactionAsync_ZeroBoundWhileThisTestHoldsThePermit_ThrowsTimeoutExceptionAndReleasesNothing = Passed duration=00:00:00.0500834 +RESULT Cleanup_CalledTwice_DoesNotThrow = Passed duration=00:00:00.0012892 +RESULT TryQueueRemainingMailItemAsync_AfterCleanupNulledFields_ReturnsFalseWithoutThrowing = Passed duration=00:00:00.1356806 +RESULT QuiesceLoaderAsync_LoaderCompletes_ReturnsBeforeTimeout = Passed duration=00:00:00.0077365 +RESULT EnsureDispatcher_WhileATransactionHoldsALiveDispatcher_DoesNotReplaceIt = Passed duration=00:00:00.0556902 +RESULT ToggleFocus_StateOverload_MarshalsThroughItemViewerInvoke = Passed duration=00:00:00.3730804 +RESULT EnsureDispatcher_ScopeDisposedTwice_IsIdempotent = Passed duration=00:00:00.0039765 +RESULT Worker_DoWork_CapturesRemainingLoadTask = Passed duration=00:00:00.0012639 + +BASELINE-CONCURRENT-FAILED: NONE diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/coverage-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/coverage-baseline.md new file mode 100644 index 000000000..791e670f8 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/coverage-baseline.md @@ -0,0 +1,97 @@ +# Baseline repository-wide test-and-coverage run (P0-T16) + +Timestamp: 2026-10-02T00-56 +Command: (1) CMD-COVERAGE-DIRECT with STAGE baseline, executed as one pwsh -NoProfile -Command payload started with run_in_background: PREFIX (Set-Location -LiteralPath "WORKTREE"; SetCurrentDirectory; WORKTREE-LEAF echo), then the CMD-COVERAGE-DIRECT body verbatim (dot-source scripts\vscode\Invoke-MSTestWithCoverage.ps1; derive coverage\effective-coverage-950.config from coverage.config with ConvertTo-DerivedCoverageSettingsXml; discover *.Test.dll under bin\Debug excluding obj, ref and .claude; invoke dotnet-coverage collect with the four-class exclusion filter; end with PAYLOAD-COMPLETE). CLOCK and CLOCK-END echo lines were added. (2) CMD-COVERAGE-POST with STAGE baseline and RAW True (DIRECT route), PREFIX then the body verbatim with NAMES-TARGETS substituted. +Canonical command: dotnet-coverage collect --output coverage\baseline-950.cobertura.xml --output-format cobertura --settings coverage\effective-coverage-950.config -- vstest.console.exe /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:TestCategory!=LiveOutlook&FullyQualifiedName!~HelperClasses.ShellUtilities_Tests&FullyQualifiedName!~HelperClasses.ShellUtilitiesStatic_Tests&FullyQualifiedName!~HelperClasses.SysImageListHelperTests&FullyQualifiedName!~EmailIntelligence.OSBrowser_Tests" "/ResultsDirectory:coverage\test-results\950\baseline" "/Logger:trx;LogFileName=baseline-950.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +COVERAGE-ROUTE: DIRECT (from P0-T15 STALL-PROBE: REPRODUCES) +RAW: True +COLLECT_EXIT_CODE: 0 +ASSEMBLY_COUNT: 9 +ASSEMBLY: \QuickFiler.Test\bin\Debug\QuickFiler.Test.dll +ASSEMBLY: \SVGControl.Test\bin\Debug\SVGControl.Test.dll +ASSEMBLY: \Tags.Test\bin\Debug\Tags.Test.dll +ASSEMBLY: \TaskMaster.Test\bin\Debug\TaskMaster.Test.dll +ASSEMBLY: \TaskTree.Test\bin\Debug\TaskTree.Test.dll +ASSEMBLY: \TaskVisualization.Test\bin\Debug\TaskVisualization.Test.dll +ASSEMBLY: \ToDoModel.Test\bin\Debug\ToDoModel.Test.dll +ASSEMBLY: \UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll +ASSEMBLY: \VBFunctions.Test\bin\Debug\VBFunctions.Test.dll +TRX_PRESENT: True +DOCUMENT_PRESENT: True +SEQUENCE_FILES: 0 +PAYLOAD-COMPLETE observed (collection payload ran from 2026-10-02T00-54 to 2026-10-02T00-55) + +LINE-FLOOR: MET +BRANCH-FLOOR: MET +First-party coverage: lines 56204/65855 (85.35%), branches 13618/17078 (79.74%) +ROOT line-rate=0.853451 branch-rate=0.7974 lines-covered=56204 lines-valid=65855 branches-covered=13618 branches-valid=17078 + +PROJECTION-BEGIN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +PROJECTION-END + +SUMMARY-BEGIN +Test run outcome: Completed +Total 7361, executed 7361, passed 7361, failed 0. +Skipped 0, derived as total minus executed rather than reported by the test platform. +Figures reported verbatim by the test platform: error 0, timeout 0, aborted 0, notExecuted 0, inconclusive 0. +Failed tests: none +SUMMARY-END + +BASELINE-FAILED-SET: (empty) +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Passed +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Passed +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Passed +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Passed +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Passed +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Passed +RESULT Worker_DoWork_CapturesRemainingLoadTask = Passed +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Passed +QFCDATAMODEL-CLASS-NODES: 0 + +Branch evaluation: (d) not met (trx present, no Sequence file, exit 0); (c) not met (class nodes 0); (b) not met; (a) exit 0 with both floors met. +BASELINE-STATE: GREEN + +The raw collector document and the trx stay under the ignored coverage directory; only the projection, the first-party line and the trx-derived summary are committed here. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/csharpier-check-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/csharpier-check-baseline.md new file mode 100644 index 000000000..cbb77b6c0 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/csharpier-check-baseline.md @@ -0,0 +1,12 @@ +# Formatter baseline (P0-T9) + +Timestamp: 2026-10-02T00-49 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"' +Canonical command: dotnet tool run csharpier check . +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Checked 1637 files in 5251ms. +CSHARPIER_EXIT_CODE: 0 +The baseline is clean; no path was reported. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-analyzer-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-analyzer-baseline.md new file mode 100644 index 000000000..776a787db --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-analyzer-baseline.md @@ -0,0 +1,23 @@ +# Analyzer baseline (P0-T10) + +Timestamp: 2026-10-02T00-49 +Command: CMD-REBUILD with GATEARGS `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` and TASKID p0-t10, executed as one pwsh -NoProfile -Command payload: PREFIX (Set-Location -LiteralPath "WORKTREE"; SetCurrentDirectory; WORKTREE-LEAF echo), TOOLS (vswhere resolution of MSBuild.exe and vstest.console.exe; coverage\logs created), then the CMD-REBUILD body verbatim from the plan Command Reference with $log = "coverage\logs\p0-t10.msbuild.log". +Canonical command: 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 a normal-verbosity file logger under the ignored coverage directory) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 +SKIP_CORECOMPILE_LINES: 0 +CSC_OUT_QUICKFILER: 2 +CSC_OUT_QUICKFILER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +WRITESET_DIAGNOSTIC_CODES: (empty) +TEST_DLL_EXISTS: True +UCS_TEST_DLL_EXISTS: True + +ANALYZER-BASELINE-WARNINGS: 0 +ANALYZER-BASELINE-WRITESET-LINES: 0 +ANALYZER-BASELINE-WRITESET-CODES: (empty) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-nullable-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-nullable-baseline.md new file mode 100644 index 000000000..0d0452bbf --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/msbuild-nullable-baseline.md @@ -0,0 +1,21 @@ +# Nullable (TreatWarningsAsErrors) baseline (P0-T11) + +Timestamp: 2026-10-02T00-50 +Command: CMD-REBUILD with GATEARGS `/p:TreatWarningsAsErrors=true` (no Nullable property override) and TASKID p0-t11, executed as one pwsh -NoProfile -Command payload: PREFIX (Set-Location -LiteralPath "WORKTREE"; SetCurrentDirectory; WORKTREE-LEAF echo), TOOLS, then the CMD-REBUILD body verbatim from the plan Command Reference with $log = "coverage\logs\p0-t11.msbuild.log". Two START and END clock echo lines were added around the body to timestamp the run. +Canonical command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true (resolved through vswhere, plus /nodeReuse:false and a normal-verbosity file logger under the ignored coverage directory) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +START: 2026-10-02T00-50 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 +SKIP_CORECOMPILE_LINES: 0 +CSC_OUT_QUICKFILER: 2 +CSC_OUT_QUICKFILER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +WRITESET_DIAGNOSTIC_CODES: (empty) +TEST_DLL_EXISTS: True +UCS_TEST_DLL_EXISTS: True +END: 2026-10-02T00-50 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-instructions-read.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-instructions-read.md new file mode 100644 index 000000000..f1098e61d --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-instructions-read.md @@ -0,0 +1,18 @@ +# Phase 0 policy reads (P0-T1) + +Timestamp: 2026-10-02T00-46 +Task: P0-T1 +Policy Order: CLAUDE.md -> .claude/rules/general-code-change.md -> .claude/rules/general-unit-test.md -> .claude/rules/csharp.md + +Files read (all from the item worktree WORKTREE): + +- CLAUDE.md (mandatory 1) +- .claude/rules/general-code-change.md (mandatory 2) +- .claude/rules/general-unit-test.md (mandatory 3) +- .claude/rules/csharp.md (mandatory 4) +- .claude/rules/plan-acceptance-gates.md +- .claude/rules/tonality.md +- .claude/skills/evidence-and-timestamp-conventions/SKILL.md +- .claude/skills/acceptance-criteria-tracking/SKILL.md + +Output Summary: all eight documents read in the stated order; no policy document was modified. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/scope-and-anchor.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/scope-and-anchor.md new file mode 100644 index 000000000..e7c2f2654 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/scope-and-anchor.md @@ -0,0 +1,66 @@ +# Scope and anchor (P0-T2, P0-T3) + +Timestamp: 2026-10-02T00-46 +Task: P0-T2 + +## Documents read in full + +- FEATURE/spec.md (302 lines) +- FEATURE/issue.md (67 lines) +- FEATURE/research/2026-10-01T00-00-wall-clock-waits-research.md (194 lines) + +## Write Set (code paths, verbatim) + +- `QuickFiler/Controllers/QfcDatamodel.cs` +- `QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs` +- `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs` +- `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs` +- `QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` + +Feature documents in the Write Set: FEATURE/spec.md (check-off edits only) and FEATURE/plan.2026-10-01T07-11.md (task check-off edits only); evidence files under FEATURE/evidence/. + +## Prohibited paths (from the plan Write Set section) + +QuickFiler.Test/QuickFiler.Test.csproj, QuickFiler/QuickFiler.csproj, QuickFiler.Test/Controllers/QfcItemController.FocusAndThemeTests.cs, QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs, QuickFiler.Test/Controllers/QfcItemController.TestSupport.cs, UtilitiesCS/Threading/UiThread.cs, TaskMaster.runsettings, scripts/vscode/TaskMaster.cli.runsettings, every file under scripts/, every file under .github/, every file under .claude/ (uncommitted .claude/agent-memory/ files are never staged), every file under docs/features/potential/, and the research document. No raw trx, coverage document or msbuild log is copied into FEATURE. + +## Work mode and acceptance-criteria inventory + +- issue.md line 12 reads: `- Work Mode: full-bug` +- spec.md lines beginning `- [ ] AC`: 17 (counted with a line-anchored search) +- spec.md lines beginning `- [x] AC`: 0 +- Inventory: AC1 to AC17, spec.md lines 266 to 282. + +Output Summary: Write Set and prohibited paths recorded; Work Mode full-bug confirmed at issue.md line 12; 17 unchecked and 0 checked acceptance-criteria lines in spec.md. + +## Anchor and pre-change tree state (P0-T3) + +Timestamp: 2026-10-02T00-47 +Command: git -C WORKTREE rev-parse HEAD; git -C WORKTREE rev-parse --abbrev-ref HEAD; git -C WORKTREE merge-base --is-ancestor 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD; git -C WORKTREE merge-base origin/main HEAD; git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD; git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- QuickFiler QuickFiler.Test UtilitiesCS/Threading/UiThread.cs scripts/vscode TaskMaster.runsettings; git -C WORKTREE status --porcelain --untracked-files=all (each a separate call) +EXIT_CODE: 0 + +HEAD-SHA: 03c0d01eb517c7b4fe3a837bfede5a4fcf16669f +BRANCH: bug/quickfiler-tests-depend-on-wall-clock-timing-950 +ANCESTOR-CHECK: exit 0 (BASE 34c2ed88cbb009f2f231453db87bc64d45a9bd51 is an ancestor of HEAD) +MERGE-BASE: 34c2ed88cbb009f2f231453db87bc64d45a9bd51 (equals BASE) + +INHERITED-COMMITTED: +- A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance-r2.2026-10-02T12-10.md +- A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance.2026-10-02T04-30.md +- A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/issue.md +- A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +- A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/research/2026-10-01T00-00-wall-clock-waits-research.md +- A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +- A docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md + +Every inherited path is under FEATURE or is exactly the promoted record: in scope. + +CODE-TREE-AT-BASE: UNCHANGED (scoped --exit-code diff exited 0 and printed nothing) + +PRE-EXISTING-WORKTREE-PATHS: +- M docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +- ?? docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-instructions-read.md +- ?? docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/scope-and-anchor.md + +No porcelain line names a path under QuickFiler/ or QuickFiler.Test/. + +Output Summary: anchor verified (merge-base equals BASE, ancestor check exit 0); inherited set is FEATURE plus the promoted record; cited code tree unchanged since BASE; code tree clean at anchor. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/stall-probe.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/stall-probe.md new file mode 100644 index 000000000..7ada53b0a --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/stall-probe.md @@ -0,0 +1,21 @@ +# Stall probe: UtilitiesCS.Test shell-icon classes (P0-T15) + +Timestamp: 2026-10-02T00-53 +Command: CMD-VSTEST with ASSEMBLY-UCS (UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll), FILTER-STALL, TASKID p0-t15 and empty NAMES, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body, run in the background and polled. Deviations from the body, recorded for audit: the per-result `RESULT` loop was omitted (P0-T15's acceptance reads only COUNTERS and MESSAGE lines), CLOCK, CLOCK-END and PROBE-END echo lines were added, and PROBE-END is also echoed before the exit 3 branch. The probe ran once and is not re-run. +Canonical command: vstest.console.exe UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-STALL" "/ResultsDirectory:coverage\test-results\950\p0-t15" "/Logger:trx;LogFileName=p0-t15.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 1 +ExpectedExitCode: 1 (presentational: the observed value, recorded per the P0-T15 acceptance; the probe outcome is not gated) + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 1 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=23 executed=23 passed=22 failed=1 +RESULT_COUNT: 23 +MESSAGE GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension :: Test method UtilitiesCS.Test.HelperClasses.ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension threw exception: System.ArgumentException: Win32 handle that was passed to Icon is not valid or is the wrong type. + +The run completed within the same minute (no hang, no Sequence file); it failed one test rather than stalling. The plan's rule is CLEAR only when EXIT_CODE is 0, failed=0 and SEQUENCE_FILES is 0, so the result is REPRODUCES. + +STALL-PROBE: REPRODUCES +COVERAGE-ROUTE: DIRECT diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/targets-baseline.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/targets-baseline.md new file mode 100644 index 000000000..62f058503 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/targets-baseline.md @@ -0,0 +1,27 @@ +# Pre-change run of the nine target tests (P0-T13) + +Timestamp: 2026-10-02T00-52 +Command: CMD-VSTEST with ASSEMBLY-QF (QuickFiler.Test\bin\Debug\QuickFiler.Test.dll), FILTER-TARGETS (the nine FullyQualifiedName= expressions, QCT. expanded to QuickFiler.Controllers.Tests., joined with |), TASKID p0-t13 and NAMES-TARGETS, executed as one pwsh -NoProfile -Command payload: PREFIX (Set-Location -LiteralPath "WORKTREE"; SetCurrentDirectory; WORKTREE-LEAF echo), TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-TARGETS" "/ResultsDirectory:coverage\test-results\950\p0-t13" "/Logger:trx;LogFileName=p0-t13.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=9 executed=9 passed=9 failed=0 +RESULT_COUNT: 9 +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Passed duration=00:00:00.0003198 +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Passed duration=00:00:00.1369667 +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Passed duration=00:00:00.0007117 +RESULT Worker_DoWork_CapturesRemainingLoadTask = Passed duration=00:00:00.0605081 +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Passed duration=00:00:00.2066649 +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0722791 +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Passed duration=00:00:00.0029094 +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Passed duration=00:00:00.1560974 +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Passed duration=00:00:00.0133551 + +BASELINE-TARGETS-NOT-PASSED: NONE + +Outcomes are observations of the pre-fix, load-dependent tests on an idle run and are not gated. The trx stays under the ignored coverage directory. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 8acff8c6f..0978c530e 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -758,45 +758,45 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 0 — Policy Reads, Anchor, Bootstrap and Baseline Capture -- [ ] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/plan-acceptance-gates.md, .claude/rules/tonality.md, .claude/skills/evidence-and-timestamp-conventions/SKILL.md and .claude/skills/acceptance-criteria-tracking/SKILL.md, and record the read in FEATURE/evidence/baseline/phase0-instructions-read.md. +- [x] [P0-T1] Read the policy documents in the mandatory order — CLAUDE.md, then .claude/rules/general-code-change.md, then .claude/rules/general-unit-test.md, then .claude/rules/csharp.md — plus .claude/rules/plan-acceptance-gates.md, .claude/rules/tonality.md, .claude/skills/evidence-and-timestamp-conventions/SKILL.md and .claude/skills/acceptance-criteria-tracking/SKILL.md, and record the read in FEATURE/evidence/baseline/phase0-instructions-read.md. - Acceptance: the artifact carries `Timestamp:`, a `Policy Order:` line naming the four mandatory documents in that order, and one line per document read. No policy document is modified. -- [ ] [P0-T2] Read FEATURE/spec.md, FEATURE/issue.md and FEATURE/research/2026-10-01T00-00-wall-clock-waits-research.md in full and record the Write Set, the prohibited paths and the acceptance-criteria inventory in FEATURE/evidence/baseline/scope-and-anchor.md (this task creates the file; P0-T3 appends to it). +- [x] [P0-T2] Read FEATURE/spec.md, FEATURE/issue.md and FEATURE/research/2026-10-01T00-00-wall-clock-waits-research.md in full and record the Write Set, the prohibited paths and the acceptance-criteria inventory in FEATURE/evidence/baseline/scope-and-anchor.md (this task creates the file; P0-T3 appends to it). - Acceptance: the artifact lists the five code paths of the Write Set verbatim; names the prohibited paths from the Write Set section; records that issue.md line 12 reads `- Work Mode: full-bug`; and records, counted from the file, that spec.md holds exactly 17 lines beginning `- [ ] AC` and 0 lines beginning `- [x] AC`. -- [ ] [P0-T3] Record the anchor and the pre-change tree state by appending to FEATURE/evidence/baseline/scope-and-anchor.md. +- [x] [P0-T3] Record the anchor and the pre-change tree state by appending to FEATURE/evidence/baseline/scope-and-anchor.md. - Commands, each a separate Bash call: `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE rev-parse --abbrev-ref HEAD`; `git -C WORKTREE merge-base --is-ancestor 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE merge-base origin/main HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- QuickFiler QuickFiler.Test UtilitiesCS/Threading/UiThread.cs scripts/vscode TaskMaster.runsettings`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance, all required: `HEAD-SHA:` records the first output as an observation; `BRANCH:` reads `bug/quickfiler-tests-depend-on-wall-clock-timing-950`; the ancestor check exits 0 and the merge-base command prints exactly `34c2ed88cbb009f2f231453db87bc64d45a9bd51` (otherwise `BASE-SHA MISMATCH`: record both values and stop); `INHERITED-COMMITTED:` lists every name-status line or `NONE`, and every listed path is under FEATURE or is exactly `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md` (otherwise `INHERITED SET OUT OF SCOPE`: stop); the scoped `--exit-code` diff exits 0, recorded as `CODE-TREE-AT-BASE: UNCHANGED` (otherwise `CITED TREE ADVANCED`: stop, because every line citation in this plan is against BASE); `PRE-EXISTING-WORKTREE-PATHS:` lists every porcelain line verbatim or `NONE`, and no line names a path under QuickFiler/ or QuickFiler.Test/ (otherwise `CODE TREE DIRTY AT ANCHOR`: stop). The porcelain output is not asserted empty. -- [ ] [P0-T4] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record it in FEATURE/evidence/baseline/bootstrap-sdk.md. +- [x] [P0-T4] Provision the repository .NET SDK with scripts/vscode/Install-RepoDotNetSdk.ps1 (guarded) and record it in FEATURE/evidence/baseline/bootstrap-sdk.md. - Command: `pwsh -NoProfile -Command 'PREFIX; if (-not (Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")) { & (Join-Path (Get-Location).Path "scripts\vscode\Install-RepoDotNetSdk.ps1") }; "SDK_MARKER=$(Test-Path -LiteralPath ".dotnet-sdk\sdk\8.0.205")"; dotnet --version; "DOTNET_EXIT=$LASTEXITCODE"'` (PREFIX expanded to its three lines, joined with semicolons). - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `SDK_MARKER=True`, `DOTNET_EXIT=0`, and the version line is a version string rather than the global.json `errorMessage` text. The installer's filesystem marker is the gate; version equality is not asserted because global.json rolls forward within the feature band. -- [ ] [P0-T5] Restore the manifest tools with `dotnet tool restore` at the worktree root (manifest dotnet-tools.json) and record it in FEATURE/evidence/baseline/bootstrap-tool-restore.md. +- [x] [P0-T5] Restore the manifest tools with `dotnet tool restore` at the worktree root (manifest dotnet-tools.json) and record it in FEATURE/evidence/baseline/bootstrap-tool-restore.md. - Command: `pwsh -NoProfile -Command 'PREFIX; dotnet tool restore; "RESTORE_EXIT=$LASTEXITCODE"; dotnet tool list --local; dotnet tool run csharpier check --help | Out-Null; "CHECK_HELP_EXIT=$LASTEXITCODE"'`. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `RESTORE_EXIT=0`, a local tool row with Package Id `csharpier` and Version `1.2.6`, and `CHECK_HELP_EXIT=0`. Only the Package Id and Version columns are transcribed (the Manifest column carries an absolute path). -- [ ] [P0-T6] Restore NuGet packages with scripts/vscode/Invoke-Restore.ps1 and record it in FEATURE/evidence/baseline/bootstrap-nuget-restore.md. +- [x] [P0-T6] Restore NuGet packages with scripts/vscode/Invoke-Restore.ps1 and record it in FEATURE/evidence/baseline/bootstrap-nuget-restore.md. - Command: `pwsh -NoProfile -Command 'PREFIX; $env:MSBUILDDISABLENODEREUSE = "1"; & (Join-Path (Get-Location).Path "scripts\vscode\Invoke-Restore.ps1"); "RESTORE_EXIT=$LASTEXITCODE"; "PACKAGE_DIRS=$(@(Get-ChildItem -LiteralPath packages -Directory -ErrorAction SilentlyContinue).Count)"'`. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `RESTORE_EXIT=0` and `PACKAGE_DIRS=` at least 1. -- [ ] [P0-T7] Verify analyzer-path alignment across every first-party project file (every `*.csproj` outside `packages\` and `.claude\`) and record it in FEATURE/evidence/baseline/analyzer-alignment.md. +- [x] [P0-T7] Verify analyzer-path alignment across every first-party project file (every `*.csproj` outside `packages\` and `.claude\`) and record it in FEATURE/evidence/baseline/analyzer-alignment.md. - Command: `pwsh -NoProfile -Command 'PREFIX; $root = (Get-Location).Path; $rootLen = $root.TrimEnd([char]92).Length; $projs = @(Get-ChildItem -Path $root -Recurse -Filter "*.csproj" | Where-Object { $rel = $_.FullName.Substring($rootLen); $rel -notlike "\packages\*" -and $rel -notlike "\.claude\*" }); "PROJECTS=$($projs.Count)"; $missing = 0; $skew = 0; foreach ($p in $projs) { $dir = $p.DirectoryName; [xml]$x = Get-Content -LiteralPath $p.FullName -Raw; foreach ($a in @($x.SelectNodes("//*[local-name()=""Analyzer""]"))) { $inc = $a.GetAttribute("Include"); if (-not (Test-Path -LiteralPath (Join-Path $dir $inc))) { $missing++; "MISSING " + $p.FullName.Substring($rootLen) + " :: " + $inc } }; $pc = Join-Path $dir "packages.config"; if (Test-Path -LiteralPath $pc) { [xml]$c = Get-Content -LiteralPath $pc -Raw; foreach ($id in @("Meziantou.Analyzer", "Roslynator.Analyzers")) { $pin = @($c.SelectNodes("//package[@id=""$id""]") | ForEach-Object { $_.GetAttribute("version") }); $inc = @($x.SelectNodes("//*[local-name()=""Analyzer""]") | ForEach-Object { $_.GetAttribute("Include") } | Where-Object { $_.Contains("\$id.") }); foreach ($i in $inc) { if ($pin.Count -eq 0 -or -not $i.Contains("\$id." + $pin[0] + "\")) { $skew++; "SKEW " + $p.FullName.Substring($rootLen) + " :: " + $i } } } } }; "ANALYZER_MISSING=$missing"; "VERSION_SKEW=$skew"'`. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `PROJECTS=` at least 1, `ANALYZER_MISSING=0` and `VERSION_SKEW=0`. A non-zero value is `ANALYZER PATH SKEW`: record every `MISSING` and `SKEW` line and stop; it is an environment defect, not a plan defect, and no version number is asserted here because pins move. -- [ ] [P0-T8] Provision the dotnet-coverage global tool (guarded) and record it in FEATURE/evidence/baseline/bootstrap-dotnet-coverage.md. +- [x] [P0-T8] Provision the dotnet-coverage global tool (guarded) and record it in FEATURE/evidence/baseline/bootstrap-dotnet-coverage.md. - Command: `pwsh -NoProfile -Command 'if (-not (Get-Command dotnet-coverage -ErrorAction SilentlyContinue)) { dotnet tool install --global dotnet-coverage }; "DOTNET_COVERAGE_RESOLVED=$($null -ne (Get-Command dotnet-coverage -ErrorAction SilentlyContinue))"; dotnet-coverage --version'` (no PREFIX: the command touches no repository path; `WORKTREE-LEAF:` is recorded as `not applicable`). - Acceptance: `DOTNET_COVERAGE_RESOLVED=True`, a version line is printed, `EXIT_CODE: 0`. -- [ ] [P0-T9] Capture the read-only formatter baseline with `dotnet tool run csharpier check .` and record it in FEATURE/evidence/baseline/csharpier-check-baseline.md. +- [x] [P0-T9] Capture the read-only formatter baseline with `dotnet tool run csharpier check .` and record it in FEATURE/evidence/baseline/csharpier-check-baseline.md. - Command: `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE:` is the printed `CSHARPIER_EXIT_CODE:` value; the success-case line beginning `Checked ` and ending `ms.` is transcribed; `EXIT_CODE: 0` is the gate. A non-zero value lists every reported path and is `FORMAT BASELINE NOT CLEAN`: stop, because the CLAUDE.md format step (`csharpier format .`) would then rewrite files outside the Write Set and the decision belongs to the orchestrator. -- [ ] [P0-T10] Capture the analyzer baseline with `CMD-REBUILD` (analyzer GATEARGS, `TASKID` p0-t10) and record it in FEATURE/evidence/baseline/msbuild-analyzer-baseline.md. +- [x] [P0-T10] Capture the analyzer baseline with `CMD-REBUILD` (analyzer GATEARGS, `TASKID` p0-t10) and record it in FEATURE/evidence/baseline/msbuild-analyzer-baseline.md. - Acceptance, all required: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; `CSC_OUT_QUICKFILER:` and `CSC_OUT_QUICKFILER_TEST:` each at least 1; `TEST_DLL_EXISTS: True`; `UCS_TEST_DLL_EXISTS: True`; `WARNINGS:` recorded as `ANALYZER-BASELINE-WARNINGS:`; `WRITESET_DIAGNOSTIC_LINES:` and `WRITESET_DIAGNOSTIC_CODES:` recorded as `ANALYZER-BASELINE-WRITESET-LINES:` and `ANALYZER-BASELINE-WRITESET-CODES:` (the comparison basis for P6-T3). A non-zero exit is `ANALYZER BASELINE NOT CLEAN`: stop. -- [ ] [P0-T11] Capture the nullable baseline with `CMD-REBUILD` (nullable GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable property override, `TASKID` p0-t11) and record it in FEATURE/evidence/baseline/msbuild-nullable-baseline.md. +- [x] [P0-T11] Capture the nullable baseline with `CMD-REBUILD` (nullable GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable property override, `TASKID` p0-t11) and record it in FEATURE/evidence/baseline/msbuild-nullable-baseline.md. - Acceptance, all required: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. A non-zero exit is `NULLABLE BASELINE NOT CLEAN`: stop. -- [ ] [P0-T12] Record the pre-change census of the five code files in FEATURE/evidence/baseline/census-baseline.md, using `CMD-LINECOUNT`, `CMD-HASH`, `CMD-TIMESPAN`, `CMD-TOKEN-COUNT` once per code file and `CMD-SPAN-TOKEN-COUNT` for `INITQ`, `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL`. +- [x] [P0-T12] Record the pre-change census of the five code files in FEATURE/evidence/baseline/census-baseline.md, using `CMD-LINECOUNT`, `CMD-HASH`, `CMD-TIMESPAN`, `CMD-TOKEN-COUNT` once per code file and `CMD-SPAN-TOKEN-COUNT` for `INITQ`, `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL`. - Token lists: for each of THREE, `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "IsBusy", "starts a real"`; for QfcDatamodel.cs, `"WorkerStarter", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`; for the R4 file, `"flake-watch", "Append an observation", "Issue #950:", "[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`; for `INITQ`, `"RunWorkerAsync", "WorkerStarter(worker);"`; for `R4SPAN`, `"IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()", "EnsureUiThreadDispatcher()", "using (", ".BeSameAs(", ".NotBeSameAs("`; for `R4PRE` and `R4HEAD`, `"EnsureUiThreadDispatcher()", "using ("`; for `R4TAIL`, `"}"`. - Acceptance (each value is derived in facts 1 to 5 and is a falsifiable pre-change observation): `WORKTREE-LEAF: agent-a7805823735145ca4`; LINES 483, 255, 235, 212, 458 in CODE5 order; five HASH values recorded as `BASE-HASH:` lines; liveness tokens 1, 2, 5, 2, 0, 0, 6, 0; teardown tokens 1, 1, 2, 1, 0, 0, 0, 0; zero-batch tokens 0, 1, 0, 3, 0, 0, 2, 1; QfcDatamodel.cs `WorkerStarter` 0, the loader-assignment literal 2, `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2`; R4 file tokens 1, 1, 0, 8, 1; `INITQ` `RunWorkerAsync` 2 and `WorkerStarter(worker);` 0; `R4SPAN` tokens 0, 0, 1, 1, 1 with `SPAN: 206-272`; `R4PRE` 0 and 0 with `SPAN: 206-212`; `R4HEAD` 0 and 0 with `SPAN: 206-214`; `R4TAIL` 2 with `SPAN: 256-261`; `TIMESPAN-UNCLASSIFIED: 6` (liveness 56, 103, 173; teardown 67, 220; zero-batch 161). Any differing value is `CENSUS MISMATCH`: record and stop, because a later gate is defined against these values. The non-zero wall-clock counts are the positive control for the zero gates of P6-T8. -- [ ] [P0-T13] Capture the pre-change run of the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p0-t13, `NAMES-TARGETS`) and record it in FEATURE/evidence/baseline/targets-baseline.md. +- [x] [P0-T13] Capture the pre-change run of the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p0-t13, `NAMES-TARGETS`) and record it in FEATURE/evidence/baseline/targets-baseline.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 9` with one `RESULT` line per target name and its outcome; `BASELINE-TARGETS-NOT-PASSED:` lists every non-Passed name with its `MESSAGE` line, or `NONE`. Outcomes are observations of the pre-fix, load-dependent tests and are not gated; `ExpectedExitCode:` carries the observed value when non-zero. -- [ ] [P0-T14] Capture the pre-change concurrent run of the four target classes with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p0-t14, empty `NAMES`) and record it in FEATURE/evidence/baseline/concurrent-classes-baseline.md. +- [x] [P0-T14] Capture the pre-change concurrent run of the four target classes with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p0-t14, empty `NAMES`) and record it in FEATURE/evidence/baseline/concurrent-classes-baseline.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; the `COUNTERS` line recorded as `BASELINE-CONCURRENT-COUNTERS:`; every `RESULT` line transcribed; `BASELINE-CONCURRENT-FAILED:` lists every non-Passed name or `NONE` (the comparison basis for P4-T5). `ExpectedExitCode:` carries the observed value when non-zero; nothing here is gated beyond trx presence and no hang. -- [ ] [P0-T15] Run the stall probe with `CMD-VSTEST` (`ASSEMBLY-UCS`, `FILTER-STALL`, `TASKID` p0-t15, empty `NAMES`) and record FEATURE/evidence/baseline/stall-probe.md. +- [x] [P0-T15] Run the stall probe with `CMD-VSTEST` (`ASSEMBLY-UCS`, `FILTER-STALL`, `TASKID` p0-t15, empty `NAMES`) and record FEATURE/evidence/baseline/stall-probe.md. - Acceptance: the artifact records `WORKTREE-LEAF:`, `EXIT_CODE:`, `ExpectedExitCode:` equal to the observed value when non-zero (presentational), `TRX_PRESENT:`, `SEQUENCE_FILES:`, the `COUNTERS` line when present and every `MESSAGE` line; then exactly one `STALL-PROBE:` line — `CLEAR` when `EXIT_CODE: 0`, `failed=0` and `SEQUENCE_FILES: 0`, otherwise `REPRODUCES` — and exactly one `COVERAGE-ROUTE:` line — `RUNNER` under CLEAR, `DIRECT` under REPRODUCES. The probe runs once and is never re-run. Both values complete this task. -- [ ] [P0-T16] Capture the baseline repository-wide test-and-coverage run by the route P0-T15 fixed and record FEATURE/evidence/baseline/coverage-baseline.md: under RUNNER run `CMD-COVERAGE-RUNNER` with `STAGE` baseline, under DIRECT run `CMD-COVERAGE-DIRECT` with `STAGE` baseline; then, unless branch (d) applies, run `CMD-COVERAGE-POST` with `STAGE` baseline and `RAW` per its rule. +- [x] [P0-T16] Capture the baseline repository-wide test-and-coverage run by the route P0-T15 fixed and record FEATURE/evidence/baseline/coverage-baseline.md: under RUNNER run `CMD-COVERAGE-RUNNER` with `STAGE` baseline, under DIRECT run `CMD-COVERAGE-DIRECT` with `STAGE` baseline; then, unless branch (d) applies, run `CMD-COVERAGE-POST` with `STAGE` baseline and `RAW` per its rule. - Artifact: `Timestamp:`, `Command:` (both payloads, with the route's canonical command), `EXIT_CODE:` (`RUNNER_EXIT_CODE:` or `COLLECT_EXIT_CODE:`), `ExpectedExitCode:` equal to the observed value when non-zero (a baseline observation), and an `Output Summary:` recording `WORKTREE-LEAF:`, `COVERAGE-ROUTE:`, `RAW:`, `DISCOVERED_LINE:` or `ASSEMBLY_COUNT:` with every `ASSEMBLY:` line, `TRX_PRESENT:`, `SEQUENCE_FILES:` (DIRECT), `THRESHOLD_MESSAGE:` and `COLLECT_FAILURE_MESSAGE:` (RUNNER), `LINE-FLOOR:`, `BRANCH-FLOOR:`, the `First-party coverage:` line (the numeric baseline headline: lines covered over valid with percentage, branches likewise), the `ROOT` line, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the five summary lines verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, `FAILED-SET:` recorded as `BASELINE-FAILED-SET:`, the nine `RESULT` lines and `QFCDATAMODEL-CLASS-NODES:`. - Branches, checked in order: (d) `TRX_PRESENT: False`, `SEQUENCE_FILES:` greater than 0, or a non-zero exit with an empty `FAILED-SET:` and no floor message, is `COVERAGE RUN ABORTED`: stop, report the last lines of the log with paths redacted, do not re-run. (c) `QFCDATAMODEL-CLASS-NODES:` other than 0 is `QFCDATAMODEL INSTRUMENTED`: stop, because D-7 rests on fact 1. (b) a non-zero exit with a non-empty `FAILED-SET:` or a floor `NOT MET` is recorded as `BASELINE-STATE: PRE-EXISTING FAILURES` (with `BASELINE-FLOOR:` naming any floor not met) and completes this task (D-8). (a) exit 0 with both floors met is `BASELINE-STATE: GREEN` and completes this task. - [ ] [P0-T17] Commit the Phase 0 evidence (FEATURE only) and record it in FEATURE/evidence/baseline/phase0-commit.md. From 3c466ba776d4d1c9ed0c4b969287c57d9ab29e3a Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:00:46 -0400 Subject: [PATCH 13/24] docs(950): record phase 1 fail-before evidence and ambient context observation Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../evidence/baseline/phase0-commit.md | 14 ++++++++++ .../other/ambient-synchronization-context.md | 26 +++++++++++++++++++ .../fail-before-exception.2026-10-02T01-00.md | 13 ++++++++++ .../regression-testing/phase1-build.md | 13 ++++++++++ .../regression-testing/phase1-revert.md | 18 +++++++++++++ .../phase1-temporary-edits.md | 25 ++++++++++++++++++ .../regression-testing/r4-fail-before.md | 22 ++++++++++++++++ .../plan.2026-10-01T07-11.md | 18 ++++++------- 8 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-commit.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ambient-synchronization-context.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-build.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-revert.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-temporary-edits.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-commit.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-commit.md new file mode 100644 index 000000000..fcfaf19f0 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/phase0-commit.md @@ -0,0 +1,14 @@ +# Phase 0 commit (P0-T17) + +Timestamp: 2026-10-02T00-57 +Command: git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950; git -C WORKTREE commit -m "docs(950): record phase 0 baseline evidence" -m "Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com"; git -C WORKTREE rev-parse HEAD; git -C WORKTREE status --porcelain --untracked-files=all (separate calls); then git -C WORKTREE push origin bug/quickfiler-tests-depend-on-wall-clock-timing-950 (phase-boundary push required by the delegation) +EXIT_CODE: 0 + +Output Summary: +git add: exit 0 (CRLF normalisation warnings only) +git commit: exit 0; 16 files changed, 488 insertions(+), 16 deletions(-) (the 16 deletions are plan check-off lines) +PHASE0-COMMIT: b6be10a2d1245ca8cbccf05e427c56f2cca877f0 +Porcelain after the commit: empty (no path under FEATURE, QuickFiler/ or QuickFiler.Test/) +Push: 03c0d01eb..b6be10a2d accepted by origin + +The commit message carries a second -m paragraph with the attribution trailer written without angle brackets, per the delegation rule that no commit message contains an angle bracket. This artifact and the plan check-off mark for P0-T17 are written after the commit and are committed in P4-T7. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ambient-synchronization-context.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ambient-synchronization-context.md new file mode 100644 index 000000000..9630d7883 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ambient-synchronization-context.md @@ -0,0 +1,26 @@ +# Ambient SynchronizationContext on the MSTest worker thread (P1-T5, AC16) + +Timestamp: 2026-10-02T00-59 +Task: P1-T5 [expect-fail] +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-PROBE (FullyQualifiedName=QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.Issue950_AmbientSynchronizationContextProbe), TASKID p1-t5 and NAMES "Issue950_AmbientSynchronizationContextProbe", executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-PROBE" "/ResultsDirectory:coverage\test-results\950\p1-t5" "/Logger:trx;LogFileName=p1-t5.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 1 +ExpectedExitCode: 1 + +Setup: Delivered Source P-PROBE was inserted temporarily into the unmodified QfcDatamodelLivenessTests.cs (P1-T1), the tree was built (P1-T4), and the probe ran alone under the repository runsettings (Workers=0, Scope=ClassLevel). The probe always fails by construction (D-4), so its failure message carries the observed values. The probe is reverted in P1-T7 and is never committed. + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 1 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=1 executed=1 passed=0 failed=1 +RESULT_COUNT: 1 +RESULT Issue950_AmbientSynchronizationContextProbe = Failed duration=00:00:00.1313302 +MESSAGE Issue950_AmbientSynchronizationContextProbe :: Expected report to be a match with the expectation because AMBIENT-SYNC-CONTEXT=null THREAD-POOL=True APARTMENT=MTA, but it differs at index 0: (actual) "AMBIENT-SYNC-CONTEXT=null THREAD-POOL=True APARTMENT=MTA" "PROBE-ALWAYS-FAILS" (expected) + +AMBIENT-SYNCHRONIZATION-CONTEXT: null +THREAD-POOL: True +APARTMENT: MTA + +The recorded value is an observation (spec Assumptions), not a requirement: liveness tests 3 and 4 install and restore their own DrainableSynchronizationContext, so the design does not depend on it. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md new file mode 100644 index 000000000..4beb21f14 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md @@ -0,0 +1,13 @@ +# Fail-before exception dossier: Defect A (P1-T8) + +Timestamp: 2026-10-02T01-00 +Defect: A (wall-clock waits on a thread-pool worker) + +WhyFailingRunImpossible: The Defect A failure occurs only when the thread pool delays the BackgroundWorker body (Worker_DoWork, started by RunWorkerAsync) past the five-second bound of SpinWait.SpinUntil or Task.Wait in the pre-fix tests. No deterministic, policy-compliant test can force that delay: sleeps, delays and wall-clock waits are prohibited, and starving the thread pool would change process-wide state that other test classes running in parallel under Workers=0 / ClassLevel share. + +## Alternative proof + +1. Pre-change census (FEATURE/evidence/baseline/census-baseline.md, P0-T12): `SpinWait` 1 (liveness) and 1 (teardown); `.Wait(` 2 (liveness), 1 (teardown) and 1 (zero-batch); `WaitForState` 5 (liveness) and 2 (teardown); `TIMESPAN-UNCLASSIFIED: 6` (liveness 56, 103, 173; teardown 67, 220; zero-batch 161). Every one of these is a bounded real-time wait on the thread-pool worker. +2. The two direct start sites: QuickFiler/Controllers/QfcDatamodel.cs lines 273 and 300 each call `worker.RunWorkerAsync();` (`TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2`, `INITQ` `RunWorkerAsync` 2), and `WorkerStarter` is absent (`TOKEN [WorkerStarter] = 0`), so no test can control the thread on which the worker body runs (P0-T12). +3. Recorded failure history (spec Context): two failures stopped the #944 P3-T8 gate, and one failure occurred during the #929 local run, both in QfcDatamodelLivenessTests under the repository runsettings on a loaded machine. +4. Forward statement: P5-T24 appends a "Post-fix deterministic controls" section to this dossier, recording the ten post-fix negative controls (A1 to A7, B1, C1, C2), each of which fails at once when its signal is withheld. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-build.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-build.md new file mode 100644 index 000000000..183aa7fbb --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-build.md @@ -0,0 +1,13 @@ +# Phase 1 build of the temporarily edited tree (P1-T4) + +Timestamp: 2026-10-02T00-58 +Command: CMD-BUILD with TASKID p1-t4, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-BUILD body verbatim ($log = "coverage\logs\p1-t4.msbuild.log"). One CLOCK echo line was added after PREFIX. +Canonical command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" (resolved through vswhere, plus /nodeReuse:false and a normal-verbosity file logger) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +TEST_DLL_ADVANCED: True +CSC_OUT_QUICKFILER_TEST: 2 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-revert.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-revert.md new file mode 100644 index 000000000..fc1ff951e --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-revert.md @@ -0,0 +1,18 @@ +# Phase 1 revert (P1-T7) + +Timestamp: 2026-10-02T01-00 +Command: git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs; git -C WORKTREE diff --exit-code HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs; git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls); then CMD-HASH (pwsh payload with PREFIX) +EXIT_CODE: 0 + +Output Summary: +restore: exit 0 +anchored diff: exit 0, printed nothing +porcelain span: printed nothing +WORKTREE-LEAF: agent-a7805823735145ca4 +HASH QuickFiler\Controllers\QfcDatamodel.cs = 0C8F7E7DDEB0F1E52843DF18244A8E792CD6127B419737839F748976EEB12B94 +HASH QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 390B28D669815DE30C1D0724938FA2E58C889F18AF8DFF62090DD5F2FACA2FDF +HASH QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 44A1952093125CB42891B01DC7FAD1A4C2B379E88D34082DFE040DD6BC74FAE6 +HASH QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 4F350522F071BBF1B9890D570DC143027C979CA2CFC051054DE681E8A55EBB07 +HASH QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 40EE455C7D2ACF6ADA5D01B8880B37A7BCBAF320CEC9F83A8B497015EAE56F52 + +All five HASH values equal the P0-T12 BASE-HASH values. The probe and the injected writer are gone; neither was committed. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-temporary-edits.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-temporary-edits.md new file mode 100644 index 000000000..7bd328d14 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/phase1-temporary-edits.md @@ -0,0 +1,25 @@ +# Phase 1 temporary-edit census (P1-T1, P1-T2, P1-T3) + +Timestamp: 2026-10-02T00-58 +Command: CMD-TOKEN-COUNT on QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs (TOKENS "public void Issue950_AmbientSynchronizationContextProbe()", "PROBE-ALWAYS-FAILS"); CMD-SPAN-TOKEN-COUNT on R4SPAN (TOKENS "EnsureUiThreadDispatcher()"), plus a read-only echo of lines 213 to 218 to show placement; both in one pwsh -NoProfile -Command payload after PREFIX. Then git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs and git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls). +EXIT_CODE: 0 + +Edits applied (temporary; reverted by P1-T7): +- P1-T1: Delivered Source P-PROBE inserted after liveness line 253 (one blank line, then the probe test). +- P1-T2: Delivered Source R-INJECT (`QfcItemControllerTestSupport.EnsureUiThreadDispatcher();`, sixteen spaces) inserted immediately before `transactionA.Install(liveA);` on the unmodified R4 file. + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +TOKEN [public void Issue950_AmbientSynchronizationContextProbe()] = 1 +TOKEN [PROBE-ALWAYS-FAILS] = 1 +R4SPAN SPAN: 206-273 +SPAN-TOKEN [EnsureUiThreadDispatcher()] = 1 +Placement: line 215 `Dispatcher original = UiThreadDispatcherFixture.Current;`, line 216 `QfcItemControllerTestSupport.EnsureUiThreadDispatcher();`, line 217 `transactionA.Install(liveA);` +numstat: +15 0 QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +1 0 QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs +porcelain: + M QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs + M QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs + +All P1-T1, P1-T2 and P1-T3 acceptance values hold. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md new file mode 100644 index 000000000..0745711df --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md @@ -0,0 +1,22 @@ +# R4 deterministic fail-before repro (P1-T6, Defect B) + +Timestamp: 2026-10-02T00-59 +Task: P1-T6 [expect-fail] +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-R4 (FullyQualifiedName=QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores), TASKID p1-t6 and NAMES "Transaction_SecondCallerCannotInstallUntilTheFirstRestores", executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-R4" "/ResultsDirectory:coverage\test-results\950\p1-t6" "/Logger:trx;LogFileName=p1-t6.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 1 +ExpectedExitCode: 1 + +Edit applied: Delivered Source R-INJECT on the unmodified R4 file (one discarded `QfcItemControllerTestSupport.EnsureUiThreadDispatcher();` inserted between the `original` read and `transactionA.Install(liveA);`, P1-T2). The test ran alone by fully qualified name, so `UiThread._dispatcher` started null (fact 7: the assembly initializer does not write it) and `original` was null. The injected gate-free writer then seeded the parked dispatcher inside window 1. This run is both the Defect B fail-before evidence and the R4 negative control of the spec Test Strategy table (pre-fix shape with the injected writer). + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 1 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=1 executed=1 passed=0 failed=1 +RESULT_COUNT: 1 +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Failed duration=00:00:00.1933723 +MESSAGE Transaction_SecondCallerCannotInstallUntilTheFirstRestores :: Expected observedByB to refer to because the first transaction restores before it releases the gate, so the waiter cannot observe the pre-restore value, but found System.Windows.Threading.Dispatcher { HasShutdownFinished = False, HasShutdownStarted = False, Hooks = System.Windows.Threading.DispatcherHooks{ }, Thread = System.Threading.Thread { ApartmentState = ApartmentState.STA {value: 0}, CurrentCulture = en-US, CurrentUICulture = en-US, ExecutionContext = System.Threading.ExecutionContext{ }, IsAlive = True, IsBackground = True, IsThreadPoolThread = False, ManagedThreadId = 37, Name = "UiThreadDispatcherFixture.ParkedDispatcher", Priority = ThreadPriority.Normal {value: 2}, ThreadState = ThreadState.Background|WaitSleepJoin {value: 36} } }. + +The message contains `to refer to` and `ParkedDispatcher`: the CI failure signature of spec Repro and Evidence (the second caller observed the parked dispatcher where the null baseline was expected). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 0978c530e..789a993ea 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -799,28 +799,28 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - [x] [P0-T16] Capture the baseline repository-wide test-and-coverage run by the route P0-T15 fixed and record FEATURE/evidence/baseline/coverage-baseline.md: under RUNNER run `CMD-COVERAGE-RUNNER` with `STAGE` baseline, under DIRECT run `CMD-COVERAGE-DIRECT` with `STAGE` baseline; then, unless branch (d) applies, run `CMD-COVERAGE-POST` with `STAGE` baseline and `RAW` per its rule. - Artifact: `Timestamp:`, `Command:` (both payloads, with the route's canonical command), `EXIT_CODE:` (`RUNNER_EXIT_CODE:` or `COLLECT_EXIT_CODE:`), `ExpectedExitCode:` equal to the observed value when non-zero (a baseline observation), and an `Output Summary:` recording `WORKTREE-LEAF:`, `COVERAGE-ROUTE:`, `RAW:`, `DISCOVERED_LINE:` or `ASSEMBLY_COUNT:` with every `ASSEMBLY:` line, `TRX_PRESENT:`, `SEQUENCE_FILES:` (DIRECT), `THRESHOLD_MESSAGE:` and `COLLECT_FAILURE_MESSAGE:` (RUNNER), `LINE-FLOOR:`, `BRANCH-FLOOR:`, the `First-party coverage:` line (the numeric baseline headline: lines covered over valid with percentage, branches likewise), the `ROOT` line, the projection verbatim between `PROJECTION-BEGIN` and `PROJECTION-END`, the five summary lines verbatim between `SUMMARY-BEGIN` and `SUMMARY-END`, `FAILED-SET:` recorded as `BASELINE-FAILED-SET:`, the nine `RESULT` lines and `QFCDATAMODEL-CLASS-NODES:`. - Branches, checked in order: (d) `TRX_PRESENT: False`, `SEQUENCE_FILES:` greater than 0, or a non-zero exit with an empty `FAILED-SET:` and no floor message, is `COVERAGE RUN ABORTED`: stop, report the last lines of the log with paths redacted, do not re-run. (c) `QFCDATAMODEL-CLASS-NODES:` other than 0 is `QFCDATAMODEL INSTRUMENTED`: stop, because D-7 rests on fact 1. (b) a non-zero exit with a non-empty `FAILED-SET:` or a floor `NOT MET` is recorded as `BASELINE-STATE: PRE-EXISTING FAILURES` (with `BASELINE-FLOOR:` naming any floor not met) and completes this task (D-8). (a) exit 0 with both floors met is `BASELINE-STATE: GREEN` and completes this task. -- [ ] [P0-T17] Commit the Phase 0 evidence (FEATURE only) and record it in FEATURE/evidence/baseline/phase0-commit.md. +- [x] [P0-T17] Commit the Phase 0 evidence (FEATURE only) and record it in FEATURE/evidence/baseline/phase0-commit.md. - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record phase 0 baseline evidence"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: both git commands exit 0; `PHASE0-COMMIT:` records the new HEAD as an observation; no porcelain line names a path under FEATURE other than this plan file (whose check-off mark is written after the commit) and FEATURE/evidence/baseline/phase0-commit.md (written after the commit), and no porcelain line names a path under QuickFiler/ or QuickFiler.Test/. This artifact itself is committed in P4-T7. ### Phase 1 — Execution-Time Observation and Fail-Before Evidence (temporary edits on the unmodified tree) -- [ ] [P1-T1] Insert Delivered Source P-PROBE into QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs after line 253 (temporary; reverted by P1-T7). +- [x] [P1-T1] Insert Delivered Source P-PROBE into QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs after line 253 (temporary; reverted by P1-T7). - Acceptance (recorded by P1-T3): the file contains exactly one line containing `public void Issue950_AmbientSynchronizationContextProbe()` and exactly one line containing `PROBE-ALWAYS-FAILS`; no other line changed. -- [ ] [P1-T2] Insert Delivered Source R-INJECT into QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs immediately before line 216 `transactionA.Install(liveA);` (temporary; reverted by P1-T7). +- [x] [P1-T2] Insert Delivered Source R-INJECT into QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs immediately before line 216 `transactionA.Install(liveA);` (temporary; reverted by P1-T7). - Acceptance (recorded by P1-T3): `R4SPAN` `EnsureUiThreadDispatcher()` count is 1 and the inserted line sits between the line containing `Dispatcher original = UiThreadDispatcherFixture.Current;` (pre-edit 215) and the line containing `transactionA.Install(liveA);`; no other line changed. -- [ ] [P1-T3] Record the temporary-edit census in FEATURE/evidence/regression-testing/phase1-temporary-edits.md using `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"public void Issue950_AmbientSynchronizationContextProbe()", "PROBE-ALWAYS-FAILS"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [x] [P1-T3] Record the temporary-edit census in FEATURE/evidence/regression-testing/phase1-temporary-edits.md using `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"public void Issue950_AmbientSynchronizationContextProbe()", "PROBE-ALWAYS-FAILS"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: both liveness tokens 1; the R4 span token 1; numstat `15 0` for the liveness file and `1 0` for the R4 file; the porcelain span lists exactly those two paths with status ` M`. -- [ ] [P1-T4] Build the temporarily edited tree with `CMD-BUILD` (`TASKID` p1-t4) and record it in FEATURE/evidence/regression-testing/phase1-build.md. +- [x] [P1-T4] Build the temporarily edited tree with `CMD-BUILD` (`TASKID` p1-t4) and record it in FEATURE/evidence/regression-testing/phase1-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_QUICKFILER_TEST:` at least 1. -- [ ] [P1-T5] [expect-fail] Run the AC16 probe alone with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-PROBE`, `TASKID` p1-t5, `NAMES` `"Issue950_AmbientSynchronizationContextProbe"`) under the repository runsettings and record FEATURE/evidence/other/ambient-synchronization-context.md. +- [x] [P1-T5] [expect-fail] Run the AC16 probe alone with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-PROBE`, `TASKID` p1-t5, `NAMES` `"Issue950_AmbientSynchronizationContextProbe"`) under the repository runsettings and record FEATURE/evidence/other/ambient-synchronization-context.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1` (the probe fails by construction, D-4); `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 1`; the `RESULT` line reads `Failed`; the `MESSAGE` line contains `AMBIENT-SYNC-CONTEXT=`; and the artifact carries exactly one line `AMBIENT-SYNCHRONIZATION-CONTEXT:` followed by the text the message carries after `AMBIENT-SYNC-CONTEXT=` up to the next space, plus `THREAD-POOL:` and `APARTMENT:` lines copied the same way. A `RESULT` of `Passed`, or a message without `AMBIENT-SYNC-CONTEXT=`, means the probe did not run as written: stop. The recorded value is an observation (spec Assumptions); no value is required, because tests 3 and 4 install their own context. -- [ ] [P1-T6] [expect-fail] Run the deterministic R4 fail-before repro alone with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-R4`, `TASKID` p1-t6, `NAMES` `"Transaction_SecondCallerCannotInstallUntilTheFirstRestores"`) and record FEATURE/evidence/regression-testing/r4-fail-before.md. +- [x] [P1-T6] [expect-fail] Run the deterministic R4 fail-before repro alone with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-R4`, `TASKID` p1-t6, `NAMES` `"Transaction_SecondCallerCannotInstallUntilTheFirstRestores"`) and record FEATURE/evidence/regression-testing/r4-fail-before.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 1`; the `RESULT` line reads `Failed` with its duration recorded; the `MESSAGE` line contains `to refer to` and `ParkedDispatcher` (the CI failure signature of spec Repro and Evidence: the second caller observed the parked dispatcher where the null baseline was expected). The artifact states the edit applied (R-INJECT on the unmodified file), that the test ran alone so the baseline was null (fact 7), and that this run is both the Defect B fail-before evidence and the R4 negative control of the spec Test Strategy table. A `Passed` result is `R4 REPRO DID NOT FAIL`: stop and report, because the root-cause claim rests on it. -- [ ] [P1-T7] Revert both temporary edits to HEAD and verify the revert byte-for-byte, recording FEATURE/evidence/regression-testing/phase1-revert.md. +- [x] [P1-T7] Revert both temporary edits to HEAD and verify the revert byte-for-byte, recording FEATURE/evidence/regression-testing/phase1-revert.md. - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`; then `CMD-HASH`. - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing; the five HASH values equal the P0-T12 `BASE-HASH:` values. -- [ ] [P1-T8] Author the Defect A fail-before exception dossier FEATURE/evidence/regression-testing/fail-before-exception..md (timestamp yyyy-MM-ddTHH-mm at authoring). +- [x] [P1-T8] Author the Defect A fail-before exception dossier FEATURE/evidence/regression-testing/fail-before-exception..md (timestamp yyyy-MM-ddTHH-mm at authoring). - Required content: `Timestamp:`; `Defect: A (wall-clock waits on a thread-pool worker)`; `WhyFailingRunImpossible:` stating in one to three sentences that the Defect A failure occurs only when the thread pool delays the BackgroundWorker body past a five-second bound, and that no deterministic, policy-compliant test can force that delay (sleeps, delays and wall-clock waits are prohibited, and starving the thread pool changes process-wide state that other parallel classes share); and an `## Alternative proof` section citing (1) the P0-T12 census (`SpinWait` 1 and 1, `.Wait(` 2, 1 and 1, `WaitForState` 5 and 2, `TIMESPAN-UNCLASSIFIED: 6`), (2) the two direct start sites QfcDatamodel.cs lines 273 and 300 with `WorkerStarter` absent (P0-T12), (3) the recorded failure history from spec Context (two failures at the #944 P3-T8 gate, one in the #929 local run), and (4) a forward statement that P5-T24 appends the post-fix deterministic negative controls. - Acceptance: exactly one file matching `fail-before-exception.*.md` exists under FEATURE/evidence/regression-testing/, and it carries `Timestamp:`, `WhyFailingRunImpossible:` and the `## Alternative proof` heading with items (1) to (4). From 0700ee1e30e8d32145c6c0aa755cf3233f23165a Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:01:54 -0400 Subject: [PATCH 14/24] fix(950): add the QfcDatamodel WorkerStarter seam Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- QuickFiler/Controllers/QfcDatamodel.cs | 16 ++++++++++++-- .../qa-gates/production-seam-census.md | 22 +++++++++++++++++++ .../plan.2026-10-01T07-11.md | 8 +++---- 3 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/production-seam-census.md diff --git a/QuickFiler/Controllers/QfcDatamodel.cs b/QuickFiler/Controllers/QfcDatamodel.cs index 87a17a31e..f8847a6e3 100644 --- a/QuickFiler/Controllers/QfcDatamodel.cs +++ b/QuickFiler/Controllers/QfcDatamodel.cs @@ -38,6 +38,7 @@ private QfcDatamodel(IApplicationGlobals appGlobals) _activeExplorer = _olApp.ActiveExplorer(); _globals.Ol.App.NewMailEx += Application_NewMailEx; RemainingEmailLoader = LoadRemainingEmailsToQueueAsync; + WorkerStarter = worker => worker.RunWorkerAsync(); } public QfcDatamodel(IApplicationGlobals appGlobals, CancellationToken token) @@ -49,6 +50,7 @@ public QfcDatamodel(IApplicationGlobals appGlobals, CancellationToken token) _frame = InitDf(_activeExplorer); _globals.Ol.App.NewMailEx += Application_NewMailEx; RemainingEmailLoader = LoadRemainingEmailsToQueueAsync; + WorkerStarter = worker => worker.RunWorkerAsync(); } public static async Task LoadAsync( @@ -139,6 +141,16 @@ public void Cleanup() /// internal Func> RemainingEmailLoader { get; set; } + /// + /// Injectable worker-start seam for + /// (issue #950). Both instance constructors assign a starter that calls + /// , so production behavior is unchanged; + /// tests assign a starter that raises DoWork synchronously on the calling thread. The + /// property stays null on instances built by GetUninitializedObject, so InitEmailQueue + /// fails fast with a NullReferenceException there instead of starting a thread. + /// + internal Action WorkerStarter { get; set; } + #endregion Private Variables #region Public Properties @@ -270,7 +282,7 @@ public IList InitEmailQueue(int batchSize, BackgroundWorker worker) // Issue #424: mark the producer live before starting it, so a dequeue that runs // before Worker_DoWork's first await cannot mistake an empty queue for exhaustion. _remainingLoadActive = true; - worker.RunWorkerAsync(); + WorkerStarter(worker); return new List(); } @@ -297,7 +309,7 @@ public IList InitEmailQueue(int batchSize, BackgroundWorker worker) // Issue #424: see the zero-batch path above — the flag is the honest producer-liveness // signal and must be set before the worker starts. _remainingLoadActive = true; - worker.RunWorkerAsync(); + WorkerStarter(worker); return emailList; } diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/production-seam-census.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/production-seam-census.md new file mode 100644 index 000000000..0c6e07ffc --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/production-seam-census.md @@ -0,0 +1,22 @@ +# Production seam census (P2-T1 to P2-T4) + +Timestamp: 2026-10-02T01-01 +Command: one pwsh -NoProfile -Command payload after PREFIX: CMD-TOKEN-COUNT on QuickFiler\Controllers\QfcDatamodel.cs (TOKENS "internal Action WorkerStarter { get; set; }", "WorkerStarter = worker => worker.RunWorkerAsync();", "Injectable worker-start seam", "null on instances built by GetUninitializedObject", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"); CMD-SPAN-TOKEN-COUNT on INITQ (TOKENS "RunWorkerAsync", "WorkerStarter(worker);"); CMD-LINECOUNT; the plan's QFCDATAMODEL-BOM byte check; plus read-only placement echoes (property line against the #endregion line, and the line above each constructor default). +EXIT_CODE: 0 + +Edits applied (in place, BOM preserved): S1 inserted after line 140 (one blank line plus nine lines); S2 inserted after each constructor's `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` line; S3 replaced `worker.RunWorkerAsync();` at the two InitEmailQueue start sites (pre-edit lines 273 and 300) with `WorkerStarter(worker);`, keeping each line's indentation. + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +TOKEN [internal Action WorkerStarter { get; set; }] = 1 +TOKEN [WorkerStarter = worker => worker.RunWorkerAsync();] = 2 +TOKEN [Injectable worker-start seam] = 1 +TOKEN [null on instances built by GetUninitializedObject] = 1 +TOKEN [RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;] = 2 +TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0 +INITQ SPAN: 271-316; SPAN-TOKEN [RunWorkerAsync] = 0; SPAN-TOKEN [WorkerStarter(worker);] = 2 +LINES QuickFiler\Controllers\QfcDatamodel.cs = 495 (other CODE5 files unchanged: 255, 235, 212, 458) +QFCDATAMODEL-BOM: True +Placement: property at line 152, before `#endregion Private Variables` at line 154; constructor defaults at lines 41 and 53, each directly below a loader-assignment line. + +All P2-T1 to P2-T4 acceptance values hold (tokens 1, 2, 1, 1, 2; TRIMMED-EQUAL 0; INITQ 0 and 2; LINES 495; BOM True). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 789a993ea..a6ca09589 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -826,13 +826,13 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 2 — Production Seam in QfcDatamodel.cs -- [ ] [P2-T1] Insert Delivered Source S1 (the `WorkerStarter` property and its XML documentation) into QuickFiler/Controllers/QfcDatamodel.cs after line 140. This task, P2-T2 and P2-T3 apply S1 to S3 as in-place edits that preserve the file's leading UTF-8 byte-order mark (gated by `QFCDATAMODEL-BOM: True` in P2-T4); the file is never rewritten whole. +- [x] [P2-T1] Insert Delivered Source S1 (the `WorkerStarter` property and its XML documentation) into QuickFiler/Controllers/QfcDatamodel.cs after line 140. This task, P2-T2 and P2-T3 apply S1 to S3 as in-place edits that preserve the file's leading UTF-8 byte-order mark (gated by `QFCDATAMODEL-BOM: True` in P2-T4); the file is never rewritten whole. - Acceptance (recorded by P2-T4): the file contains exactly one line containing `internal Action WorkerStarter { get; set; }`, one containing `Injectable worker-start seam` and one containing `null on instances built by GetUninitializedObject`; the property sits before `#endregion Private Variables`. -- [ ] [P2-T2] Insert Delivered Source S2 into both constructors of QuickFiler/Controllers/QfcDatamodel.cs, each immediately after that constructor's `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` line (pre-edit 40 and 51). +- [x] [P2-T2] Insert Delivered Source S2 into both constructors of QuickFiler/Controllers/QfcDatamodel.cs, each immediately after that constructor's `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;` line (pre-edit 40 and 51). - Acceptance (recorded by P2-T4): exactly two lines contain `WorkerStarter = worker => worker.RunWorkerAsync();`, each directly below one of the two loader-assignment lines. -- [ ] [P2-T3] Replace both start sites in `InitEmailQueue` of QuickFiler/Controllers/QfcDatamodel.cs (pre-edit lines 273 and 300) with Delivered Source S3, keeping each line's indentation. +- [x] [P2-T3] Replace both start sites in `InitEmailQueue` of QuickFiler/Controllers/QfcDatamodel.cs (pre-edit lines 273 and 300) with Delivered Source S3, keeping each line's indentation. - Acceptance (recorded by P2-T4): `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; within `INITQ`, `RunWorkerAsync` 0 and `WorkerStarter(worker);` 2. -- [ ] [P2-T4] Record the production seam census in FEATURE/evidence/qa-gates/production-seam-census.md with `CMD-TOKEN-COUNT` on QuickFiler\Controllers\QfcDatamodel.cs (TOKENS `"internal Action WorkerStarter { get; set; }", "WorkerStarter = worker => worker.RunWorkerAsync();", "Injectable worker-start seam", "null on instances built by GetUninitializedObject", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`), `CMD-SPAN-TOKEN-COUNT` on `INITQ` (TOKENS `"RunWorkerAsync", "WorkerStarter(worker);"`) and `CMD-LINECOUNT`, and `pwsh -NoProfile -Command 'PREFIX; $b = [System.IO.File]::ReadAllBytes((Join-Path (Get-Location).Path "QuickFiler\Controllers\QfcDatamodel.cs")); "QFCDATAMODEL-BOM: " + ($b.Length -ge 3 -and $b[0] -eq 239 -and $b[1] -eq 187 -and $b[2] -eq 191)'`. +- [x] [P2-T4] Record the production seam census in FEATURE/evidence/qa-gates/production-seam-census.md with `CMD-TOKEN-COUNT` on QuickFiler\Controllers\QfcDatamodel.cs (TOKENS `"internal Action WorkerStarter { get; set; }", "WorkerStarter = worker => worker.RunWorkerAsync();", "Injectable worker-start seam", "null on instances built by GetUninitializedObject", "RemainingEmailLoader = LoadRemainingEmailsToQueueAsync;"`), `CMD-SPAN-TOKEN-COUNT` on `INITQ` (TOKENS `"RunWorkerAsync", "WorkerStarter(worker);"`) and `CMD-LINECOUNT`, and `pwsh -NoProfile -Command 'PREFIX; $b = [System.IO.File]::ReadAllBytes((Join-Path (Get-Location).Path "QuickFiler\Controllers\QfcDatamodel.cs")); "QFCDATAMODEL-BOM: " + ($b.Length -ge 3 -and $b[0] -eq 239 -and $b[1] -eq 187 -and $b[2] -eq 191)'`. - Acceptance: tokens 1, 2, 1, 1, 2; `TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0`; `INITQ` 0 and 2; QfcDatamodel.cs LINES 495; `QFCDATAMODEL-BOM: True` (the file carries a UTF-8 byte-order mark at BASE; S1 to S3 are applied as in-place edits that keep it, because an edit that drops it rewrites line 1 and P4-T2's numstat then reads `15 3`). Any other value: correct the edit and re-run this task. ### Phase 3 — Test Rewrites From 709123349b832644ab4a1089101767a8d09cdb6c Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:05:27 -0400 Subject: [PATCH 15/24] test(950): drive the QfcDatamodel worker synchronously and pin the R4 baseline Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../Controllers/QfcDatamodelLivenessTests.cs | 177 ++++++++++++------ .../Controllers/QfcDatamodelTeardownTests.cs | 47 +++-- .../QfcInitEmailQueueZeroBatchTests.cs | 55 ++++-- ...ntroller.UiThreadDispatcherFixtureTests.cs | 95 +++++----- .../evidence/qa-gates/test-rewrite-census.md | 39 ++++ .../plan.2026-10-01T07-11.md | 30 +-- 6 files changed, 289 insertions(+), 154 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/test-rewrite-census.md diff --git a/QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs b/QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs index 4fb636e76..92f9bfc91 100644 --- a/QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +++ b/QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs @@ -45,15 +45,45 @@ private static void SetPrivateField(object target, string name, object value) } /// - /// Bounded, event-driven wait for a state transition. This is not a fixed sleep: it returns - /// as soon as the condition holds, and fails the test with a clear message if it never does. - /// Required because BackgroundWorker clears isRunning from an asynchronously - /// posted completion, so the transition is not observable synchronously (the same race the - /// remarks on document). + /// Test-side worker whose raises DoWork synchronously on + /// the calling thread through the protected OnDoWork, so the privately subscribed + /// Worker_DoWork runs to its first incomplete await before InitEmailQueue + /// returns. Issue #950: this replaces the bounded waits on a thread-pool worker. /// - private static void WaitForState(Func condition, string because) + private sealed class SynchronousBackgroundWorker : BackgroundWorker { - SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5)).Should().BeTrue(because); + public void RaiseDoWork() => OnDoWork(new DoWorkEventArgs(null)); + } + + /// The synchronous starter assigned to QfcDatamodel.WorkerStarter. + private static void StartSynchronously(BackgroundWorker worker) => + ((SynchronousBackgroundWorker)worker).RaiseDoWork(); + + /// + /// Queues posted continuations and runs them only on an explicit call, + /// on the creating thread. Drain runs only work already queued, plus work that work queues, + /// and never blocks. Installed around InitEmailQueue by the tests that observe the + /// loader's continuation, and restored in a finally. + /// + private sealed class DrainableSynchronizationContext : SynchronizationContext + { + private readonly Queue> _callbacks = + new Queue>(); + private readonly int _creatorThreadId = Environment.CurrentManagedThreadId; + + public override void Post(SendOrPostCallback d, object state) => + _callbacks.Enqueue(Tuple.Create(d, state)); + + /// Runs every queued callback, including work queued while draining. + internal void Drain() + { + Environment.CurrentManagedThreadId.Should().Be(_creatorThreadId); + while (_callbacks.Count > 0) + { + Tuple callback = _callbacks.Dequeue(); + callback.Item1(callback.Item2); + } + } } /// Globals wired for the high-confidence dequeue path. @@ -94,20 +124,16 @@ public async Task DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPolli return await loaderRelease.Task; }; - var worker = new BackgroundWorker(); + var worker = new SynchronousBackgroundWorker(); + model.WorkerStarter = StartSynchronously; - // Act — the issue #244 zero-batch short-circuit is COM-free and still starts the worker. + // Act — the issue #244 zero-batch short-circuit is COM-free and starts the worker + // through the issue #950 seam, which raises DoWork on this thread. model.InitEmailQueue(0, worker); loaderEntered - .Task.Wait(TimeSpan.FromSeconds(5)) - .Should() - .BeTrue("the started worker must reach the injected RemainingEmailLoader"); - WaitForState( - () => !worker.IsBusy, - "the async void Worker_DoWork returns at its first await, so IsBusy goes false " - + "while the loader is still producing" - ); + .Task.IsCompleted.Should() + .BeTrue("the synchronous starter must reach the injected RemainingEmailLoader"); Task> pending = model.DequeueNextItemGroupAsync(1, 200); fake.Advance(TimeSpan.FromMilliseconds(200)); @@ -147,8 +173,12 @@ private static bool ReadLivenessFlag(QfcDatamodel model) /// /// Starts the worker with a RemainingEmailLoader held open by - /// , and returns once the worker has entered the loader and - /// BackgroundWorker.IsBusy has gone false at the async-void first-await boundary. + /// . The issue #950 synchronous starter raises DoWork on + /// this thread, so by the time InitEmailQueue returns the async void + /// Worker_DoWork has entered the loader and returned at its first incomplete await. + /// runs its continuations asynchronously, so a test that has + /// installed DrainableSynchronizationContext observes the resumed loader only + /// through Drain, never inline inside SetResult. /// private static QfcDatamodel StartHeldOpenLoader( Func, Task> loaderBody, @@ -157,7 +187,9 @@ out TaskCompletionSource release { var model = CreateUninitializedDatamodel(); var entered = new TaskCompletionSource(); - var localRelease = new TaskCompletionSource(); + var localRelease = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously + ); release = localRelease; model.RemainingEmailLoader = _ => @@ -166,24 +198,21 @@ out TaskCompletionSource release return loaderBody(localRelease); }; - var worker = new BackgroundWorker(); + var worker = new SynchronousBackgroundWorker(); + model.WorkerStarter = StartSynchronously; model.InitEmailQueue(0, worker); entered - .Task.Wait(TimeSpan.FromSeconds(5)) - .Should() - .BeTrue("the started worker must reach the injected loader"); - WaitForState( - () => !worker.IsBusy, - "async void Worker_DoWork returns at its first await" - ); + .Task.IsCompleted.Should() + .BeTrue("the synchronous starter must reach the injected loader before returning"); return model; } /// /// AC 7: the flag stays true across the async void first-await boundary while the - /// loader is still producing — precisely where BackgroundWorker.IsBusy has already - /// gone false. + /// loader is still producing. Issue #950: the synchronous starter has already run + /// Worker_DoWork to that boundary when InitEmailQueue returns, so the flag + /// is read with no wait. /// [TestMethod] public void RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces() @@ -205,51 +234,79 @@ out TaskCompletionSource release } /// - /// AC 7: the flag becomes false only after the loader completes — never before. + /// AC 7: the flag becomes false only after the loader completes — never before. Issue + /// #950: the continuation that clears the flag is drained from a test-owned context. /// [TestMethod] public void RemainingLoadActive_AfterLoaderCompletes_BecomesFalse() { - // Arrange / Act - QfcDatamodel model = StartHeldOpenLoader( - signal => signal.Task, - out TaskCompletionSource release - ); - ReadLivenessFlag(model).Should().BeTrue("the loader has not completed yet"); + // Arrange + SynchronizationContext previous = SynchronizationContext.Current; + var pump = new DrainableSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(pump); + try + { + QfcDatamodel model = StartHeldOpenLoader( + signal => signal.Task, + out TaskCompletionSource release + ); + ReadLivenessFlag(model).Should().BeTrue("the loader has not completed yet"); - release.SetResult(true); + // Act + release.SetResult(true); + pump.Drain(); - // Assert - WaitForState( - () => !ReadLivenessFlag(model), - "the finally around the awaited loader must clear the flag once it completes" - ); + // Assert + ReadLivenessFlag(model) + .Should() + .BeFalse( + "the finally around the awaited loader must clear the flag once it completes" + ); + } + finally + { + SynchronizationContext.SetSynchronizationContext(previous); + } } /// - /// AC 7: the finally clears the flag even when the loader throws. + /// AC 7: the finally clears the flag even when the loader throws. Issue #950: the + /// faulted loader's continuation is drained from a test-owned context. /// [TestMethod] public void RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally() { - // Arrange / Act - QfcDatamodel model = StartHeldOpenLoader( - async signal => - { - await signal.Task; - throw new InvalidOperationException("loader failed"); - }, - out TaskCompletionSource release - ); - ReadLivenessFlag(model).Should().BeTrue("the loader has not failed yet"); + // Arrange + SynchronizationContext previous = SynchronizationContext.Current; + var pump = new DrainableSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(pump); + try + { + QfcDatamodel model = StartHeldOpenLoader( + async signal => + { + await signal.Task; + throw new InvalidOperationException("loader failed"); + }, + out TaskCompletionSource release + ); + ReadLivenessFlag(model).Should().BeTrue("the loader has not failed yet"); - release.SetResult(true); + // Act + release.SetResult(true); + pump.Drain(); - // Assert - WaitForState( - () => !ReadLivenessFlag(model), - "the finally must clear the flag on the throwing path too, or the gate would poll forever" - ); + // Assert + ReadLivenessFlag(model) + .Should() + .BeFalse( + "the finally must clear the flag on the throwing path too, or the gate would poll forever" + ); + } + finally + { + SynchronizationContext.SetSynchronizationContext(previous); + } } } } diff --git a/QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs b/QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs index b2b6d0514..3f12c756f 100644 --- a/QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs +++ b/QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs @@ -57,14 +57,20 @@ private static object GetPrivateField(object target, string name) } /// - /// Bounded, event-driven wait for a state transition. This is not a fixed sleep: it returns - /// as soon as the condition holds and fails the test with a clear message if it never does. - /// Required because Worker_DoWork is async void and runs on the - /// thread, so the field assignment it performs is not - /// observable synchronously from the calling thread. + /// Test-side worker whose raises DoWork synchronously on + /// the calling thread through the protected OnDoWork, so the privately subscribed + /// Worker_DoWork runs to its first incomplete await before InitEmailQueue + /// returns (issue #950). Duplicated per file, following the convention documented on + /// QfcDatamodelLivenessTests. /// - private static void WaitForState(Func condition, string because) => - SpinWait.SpinUntil(condition, TimeSpan.FromSeconds(5)).Should().BeTrue(because); + private sealed class SynchronousBackgroundWorker : BackgroundWorker + { + public void RaiseDoWork() => OnDoWork(new DoWorkEventArgs(null)); + } + + /// The synchronous starter assigned to QfcDatamodel.WorkerStarter. + private static void StartSynchronously(BackgroundWorker worker) => + ((SynchronousBackgroundWorker)worker).RaiseDoWork(); /// /// AC2, the reported crash. Once Cleanup() has nulled _masterQueue and @@ -211,22 +217,25 @@ public void Worker_DoWork_CapturesRemainingLoadTask() return await loaderRelease.Task; }; - using (var worker = new BackgroundWorker()) + using (var worker = new SynchronousBackgroundWorker()) { - // Act — the issue #244 zero-batch short-circuit is COM-free and still starts the - // worker, which is the only path that reaches Worker_DoWork without live Outlook. + model.WorkerStarter = StartSynchronously; + + // Act — the issue #244 zero-batch short-circuit is COM-free and starts the worker + // through the issue #950 seam, which raises DoWork on this thread, so + // Worker_DoWork has captured the loader task before InitEmailQueue returns. model.InitEmailQueue(0, worker); - loaderEntered - .Task.Wait(TimeSpan.FromSeconds(5)) - .Should() - .BeTrue("the started worker must reach the injected RemainingEmailLoader"); // Assert - WaitForState( - () => GetPrivateField(model, "_remainingLoadTask") != null, - "the loader task must be captured before it is awaited, so the Cancel path has " - + "a handle to quiesce" - ); + loaderEntered + .Task.IsCompleted.Should() + .BeTrue("the synchronous starter must reach the injected RemainingEmailLoader"); + GetPrivateField(model, "_remainingLoadTask") + .Should() + .NotBeNull( + "the loader task must be captured before it is awaited, so the Cancel path has " + + "a handle to quiesce" + ); loaderRelease.TrySetResult(true); } diff --git a/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs b/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs index f9251a3a5..f0c093dfe 100644 --- a/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs +++ b/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs @@ -21,13 +21,17 @@ namespace QuickFiler.Controllers.Tests /// as QfcDatamodelTests to exercise the method without a live Outlook process. /// /// - /// v1.1 revision (issue #244): every test below that starts a real - /// via assigns an inert, recording - /// delegate via the internal seam BEFORE calling - /// InitEmailQueue. Without this, the started worker's Worker_DoWork reaches the real - /// LoadRemainingEmailsToQueueAsync, which pops a live - /// dialog and touches Outlook COM (_olApp.GetNamespace("MAPI")) — this is the maintainer-reported - /// defect in the v1.0 revision of these tests, and this file must never reproduce it. + /// v1.1 revision (issue #244): every test below assigns an inert, recording + /// delegate via the internal seam BEFORE + /// calling . Without this, + /// the started worker's Worker_DoWork reaches the real + /// LoadRemainingEmailsToQueueAsync, which pops a live + /// dialog and touches Outlook COM + /// (_olApp.GetNamespace("MAPI")) — this is the maintainer-reported defect in the v1.0 + /// revision of these tests, and this file must never reproduce it. Issue #950: every test also + /// assigns the WorkerStarter seam a starter that raises DoWork synchronously on + /// the test thread through the nested SynchronousBackgroundWorker, so no test starts a + /// thread-pool worker and none outlives the test. /// [TestClass] public class QfcInitEmailQueueZeroBatchTests @@ -107,6 +111,21 @@ out TaskCompletionSource invoked }; } + /// + /// Test-side worker whose raises DoWork synchronously on + /// the calling thread through the protected OnDoWork, so no worker started by + /// InitEmailQueue outlives the test (issue #950). Duplicated per file, following + /// the convention documented on QfcDatamodelLivenessTests. + /// + private sealed class SynchronousBackgroundWorker : BackgroundWorker + { + public void RaiseDoWork() => OnDoWork(new DoWorkEventArgs(null)); + } + + /// The synchronous starter assigned to QfcDatamodel.WorkerStarter. + private static void StartSynchronously(BackgroundWorker worker) => + ((SynchronousBackgroundWorker)worker).RaiseDoWork(); + /// /// Issue #244 AC1: a zero batch size must not throw the Deedle "The interface member /// 'EntryId' does not exist in the column index." exception, and must return an empty, @@ -121,10 +140,11 @@ public void InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing() var model = CreateUninitializedDatamodel(); SetPrivateField(model, "_frame", CreateTwoRowEmailFrame()); model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out _); + model.WorkerStarter = StartSynchronously; IList result = null; // Act - System.Action act = () => result = model.InitEmailQueue(0, new BackgroundWorker()); + System.Action act = () => result = model.InitEmailQueue(0, new SynchronousBackgroundWorker()); // Assert act.Should().NotThrow(); @@ -136,12 +156,10 @@ public void InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing() /// Issue #244 AC2: a zero batch size must still set up and start the background worker so /// remaining emails continue to load into the master queue. /// (set synchronously by ) proves the worker was set up. - /// Because Worker_DoWork is async void, can - /// flip back to almost immediately and a synchronous post-call check on - /// it races the worker thread, so this test does not assert IsBusy. Instead, it proves the - /// worker actually started and reached the injected - /// by waiting (with a bounded timeout, not a fixed sleep) on a - /// that the inert loader completes. + /// Issue #950: the worker is started through the WorkerStarter seam with a starter + /// that raises DoWork on this thread, and the inert loader completes its + /// synchronously, so the loader-invoked signal + /// is read without any wait as soon as InitEmailQueue returns. /// [TestMethod] public void InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker() @@ -150,7 +168,8 @@ public void InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker() var model = CreateUninitializedDatamodel(); SetPrivateField(model, "_frame", CreateTwoRowEmailFrame()); model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out var loaderInvokedTcs); - var worker = new BackgroundWorker(); + model.WorkerStarter = StartSynchronously; + var worker = new SynchronousBackgroundWorker(); // Act model.InitEmailQueue(0, worker); @@ -158,8 +177,7 @@ public void InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker() // Assert worker.WorkerSupportsCancellation.Should().BeTrue(); loaderInvokedTcs - .Task.Wait(TimeSpan.FromSeconds(5)) - .Should() + .Task.IsCompleted.Should() .BeTrue("the injected RemainingEmailLoader must be invoked by the started worker"); } @@ -180,6 +198,7 @@ public void InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDr var model = CreateUninitializedDatamodel(); SetPrivateField(model, "_frame", CreateTwoRowEmailFrame()); model.RemainingEmailLoader = CreateInertRemainingEmailLoader(out _); + model.WorkerStarter = StartSynchronously; var mailItemsByEntryId = new Dictionary { @@ -198,7 +217,7 @@ public void InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDr SetPrivateField(model, "_olApp", application.Object); // Act - var result = model.InitEmailQueue(2, new BackgroundWorker()); + var result = model.InitEmailQueue(2, new SynchronousBackgroundWorker()); // Assert result.Should().HaveCount(2); diff --git a/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs b/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs index 757494ae7..136926666 100644 --- a/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs +++ b/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs @@ -194,11 +194,17 @@ public async Task EnsureDispatcher_ScopeDisposedTwice_IsIdempotent() /// observes the pre-install value on acquisition, never the first transaction's installed /// value, because restore strictly precedes gate release. /// - /// Issue #823 (R5): this test fails intermittently. Observations of its outcomes are - /// collected in the append-only log at - /// docs/features/active/2026-09-08-quickfiler-teardown-review-residuals-823/evidence/other/flake-watch-uithread-dispatcher-transaction.2026-09-09T00-15.md. - /// Append an observation there rather than stabilising the test with a sleep, a retry or a - /// timing tolerance, none of which this repository permits. + /// Issue #950: the earlier intermittent failure was a race on the shared static, not a + /// timing defect. The gate-free fixture method EnsureDispatcher seeds the parked dispatcher + /// whenever the field is null, so a concurrently running class that calls it could write + /// between the baseline read and the install, or between the restore and the second + /// caller's read. The test therefore pins a non-null baseline with an ensure scope that it + /// opens only after transaction A has acquired the gate and holds through both assertions. + /// Taking the pin inside the gate means that a gated transaction from another class (W3/W4) + /// cannot restore a null previous value between the pin and this test's acquisition. + /// Invariant for future editors: no other class may dispose an ensure scope holding the + /// parked dispatcher (W2), and UiThread.Initialize (W5) must not latch during this test; + /// either would change the value the second caller observes. /// /// [TestMethod] @@ -212,49 +218,54 @@ public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores() UiThreadDispatcherTransaction transactionA = await UiThreadDispatcherFixture .BeginTransactionAsync() .ConfigureAwait(false); - Dispatcher original = UiThreadDispatcherFixture.Current; - transactionA.Install(liveA); - - using (var secondCallerStarted = new ManualResetEventSlim(false)) + using ( + IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher() + ) { - Dispatcher observedByB = null; + Dispatcher original = UiThreadDispatcherFixture.Current; + transactionA.Install(liveA); - Task waiter = Task.Run(async () => + using (var secondCallerStarted = new ManualResetEventSlim(false)) { - secondCallerStarted.Set(); - UiThreadDispatcherTransaction transactionB = await UiThreadDispatcherFixture - .BeginTransactionAsync() - .ConfigureAwait(false); - try - { - observedByB = UiThreadDispatcherFixture.Current; - } - finally + Dispatcher observedByB = null; + + Task waiter = Task.Run(async () => { - transactionB.Dispose(); - } - }); + secondCallerStarted.Set(); + UiThreadDispatcherTransaction transactionB = await UiThreadDispatcherFixture + .BeginTransactionAsync() + .ConfigureAwait(false); + try + { + observedByB = UiThreadDispatcherFixture.Current; + } + finally + { + transactionB.Dispose(); + } + }); - // Act - secondCallerStarted.Wait(); - transactionA.Dispose(); - await waiter.ConfigureAwait(false); + // Act + secondCallerStarted.Wait(); + transactionA.Dispose(); + await waiter.ConfigureAwait(false); - // Assert - observedByB - .Should() - .BeSameAs( - original, - because: "the first transaction restores before it releases the gate, so " - + "the waiter cannot observe the pre-restore value" - ); - observedByB - .Should() - .NotBeSameAs( - liveA, - because: "observing the first transaction's installed value would be the " - + "issue #230 lost update" - ); + // Assert + observedByB + .Should() + .BeSameAs( + original, + because: "the first transaction restores before it releases the gate, so " + + "the waiter cannot observe the pre-restore value" + ); + observedByB + .Should() + .NotBeSameAs( + liveA, + because: "observing the first transaction's installed value would be the " + + "issue #230 lost update" + ); + } } } finally diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/test-rewrite-census.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/test-rewrite-census.md new file mode 100644 index 000000000..f54636d94 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/test-rewrite-census.md @@ -0,0 +1,39 @@ +# Test-rewrite census (P3-T1 to P3-T15) + +Timestamp: 2026-10-02T01-04 +Command: one pwsh -NoProfile -Command payload after PREFIX: CMD-TOKEN-COUNT on each of THREE with the fourteen P3-T15 tokens; a second CMD-TOKEN-COUNT on each of THREE with the seven reason-literal tokens; CMD-TOKEN-COUNT on the R4 file (the P0-T12 R4 tokens plus the three R-DOC tokens); CMD-SPAN-TOKEN-COUNT on R4SPAN, R4PRE, R4HEAD and R4TAIL (the P0-T12 token list of each); CMD-TIMESPAN. Bodies verbatim, wrapped in local functions `Tok` and `Span` (each Tok call also prints a ROW line joining its counts); a missing span anchor exits 4 at the end. +EXIT_CODE: 0 + +Edits applied: L1, L2, L3, L4, L5 (liveness); T1, T2 (teardown); Z-H, Z0, Z1, Z2, Z-R (zero-batch); R-BODY then R-DOC (R4 file). + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 + +THREE rows, fourteen tokens in the order SpinWait, .Wait(, WaitForState, new BackgroundWorker(), new SynchronousBackgroundWorker(), WorkerStarter = StartSynchronously;, private sealed class SynchronousBackgroundWorker : BackgroundWorker, private sealed class DrainableSynchronizationContext : SynchronizationContext, pump.Drain();, SynchronizationContext.SetSynchronizationContext(previous);, TaskCreationOptions.RunContinuationsAsynchronously, IsBusy, starts a real, so no test starts a: +- Liveness: 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0 +- Teardown: 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +- Zero-batch: 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1 + +Reason-literal rows, seven tokens in the order "the synchronous starter must reach the injected RemainingEmailLoader", "the synchronous starter must reach the injected loader before returning", "the finally around the awaited loader must clear the flag once it completes", "the finally must clear the flag on the throwing path too", "private static void StartSynchronously(BackgroundWorker worker)", "the injected RemainingEmailLoader must be invoked by the started worker", "bounded timeout": +- Liveness: 1, 1, 1, 1, 1, 0, 0 +- Teardown: 1, 0, 0, 0, 1, 0, 0 +- Zero-batch: 0, 0, 0, 0, 1, 1, 0 + +R4 file: flake-watch 0; Append an observation 0; Issue #950: 1; [Timeout(GateTimeoutMs)] 8; private const int GateTimeoutMs = 60000; 1; "The gate-free fixture method EnsureDispatcher seeds the parked dispatcher" 1; "cannot restore a null previous value between the pin" 1; "(W2), and UiThread.Initialize (W5) must not latch" 1 + +Spans: +- R4SPAN SPAN: 212-283; IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher() 1; EnsureUiThreadDispatcher() 1; using ( 2; .BeSameAs( 1; .NotBeSameAs( 1 +- R4PRE SPAN: 212-218; EnsureUiThreadDispatcher() 0; using ( 0 +- R4HEAD SPAN: 212-224; EnsureUiThreadDispatcher() 1; using ( 1 +- R4TAIL SPAN: 266-272; } 3 + +CMD-TIMESPAN: +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:139 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:141 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:156 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:124 CLASSIFIED :: Task pending = model.QuiesceLoaderAsync(TimeSpan.FromSeconds(5)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:155 CLASSIFIED :: Task pending = model.QuiesceLoaderAsync(TimeSpan.FromSeconds(5)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:160 CLASSIFIED :: fake.Advance(TimeSpan.FromSeconds(6)); +TIMESPAN-UNCLASSIFIED: 0 + +Verdict: every value equals the P3-T15 acceptance values. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index a6ca09589..3cfccb275 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -837,35 +837,35 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 3 — Test Rewrites -- [ ] [P3-T1] Replace lines 47 to 57 of QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (the `WaitForState` documentation and method) with Delivered Source L1. +- [x] [P3-T1] Replace lines 47 to 57 of QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (the `WaitForState` documentation and method) with Delivered Source L1. - Acceptance: (recorded by P3-T15) one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private sealed class DrainableSynchronizationContext : SynchronizationContext`. -- [ ] [P3-T2] Replace the arrange-act-wait block of test 1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 97 to 110) with Delivered Source L2; lines from `Task> pending = ...` onward are unchanged. +- [x] [P3-T2] Replace the arrange-act-wait block of test 1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 97 to 110) with Delivered Source L2; lines from `Task> pending = ...` onward are unchanged. - Acceptance (recorded by P3-T15): the test contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected RemainingEmailLoader`, and no `.Wait(` or `WaitForState`. -- [ ] [P3-T3] Replace `StartHeldOpenLoader` and its documentation, the following blank line and the summary of test 2 `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 148 to 187) with Delivered Source L3. +- [x] [P3-T3] Replace `StartHeldOpenLoader` and its documentation, the following blank line and the summary of test 2 `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 148 to 187) with Delivered Source L3. - Acceptance (recorded by P3-T15): the method contains `new SynchronousBackgroundWorker()`, `model.WorkerStarter = StartSynchronously;` and the reason literal `the synchronous starter must reach the injected loader before returning`, and the method creates `localRelease` with `TaskCreationOptions.RunContinuationsAsynchronously`; test 2's summary no longer mentions `IsBusy` (the liveness `IsBusy` count is 1, the issue #424 line of test 1's documentation). -- [ ] [P3-T4] Replace test 3 `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 207 to 227) with Delivered Source L4. +- [x] [P3-T4] Replace test 3 `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 207 to 227) with Delivered Source L4. - Acceptance (recorded by P3-T15): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally around the awaited loader must clear the flag once it completes`. -- [ ] [P3-T5] Replace test 4 `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 229 to 253) with Delivered Source L5. +- [x] [P3-T5] Replace test 4 `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` and its documentation in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (pre-edit lines 229 to 253) with Delivered Source L5. - Acceptance (recorded by P3-T15): the test installs `DrainableSynchronizationContext`, calls `pump.Drain();` after `release.SetResult(true);`, restores the previous context in a `finally`, and keeps the reason literal `the finally must clear the flag on the throwing path too`. -- [ ] [P3-T6] Replace lines 59 to 67 of QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (the `WaitForState` documentation and method) with Delivered Source T1. +- [x] [P3-T6] Replace lines 59 to 67 of QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (the `WaitForState` documentation and method) with Delivered Source T1. - Acceptance: (recorded by P3-T15) one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker`. -- [ ] [P3-T7] Replace the `using` block of `Worker_DoWork_CapturesRemainingLoadTask` in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (pre-edit lines 214 to 232) with Delivered Source T2. +- [x] [P3-T7] Replace the `using` block of `Worker_DoWork_CapturesRemainingLoadTask` in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs (pre-edit lines 214 to 232) with Delivered Source T2. - Acceptance (recorded by P3-T15): the teardown file has `SpinWait` 0, `.Wait(` 0, `WaitForState` 0, `new SynchronousBackgroundWorker()` 1 and `WorkerStarter = StartSynchronously;` 1; the four other tests in the file are unchanged (their `QuiesceLoaderAsync(TimeSpan.FromSeconds(5))` and `fake.Advance(TimeSpan.FromSeconds(6))` arguments stay). -- [ ] [P3-T8] Insert Delivered Source Z-H into QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs after line 108. +- [x] [P3-T8] Insert Delivered Source Z-H into QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs after line 108. - Acceptance (recorded by P3-T15): one line contains `private sealed class SynchronousBackgroundWorker : BackgroundWorker` and one contains `private static void StartSynchronously(BackgroundWorker worker) =>`. -- [ ] [P3-T9] Apply Delivered Source Z0 to `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. +- [x] [P3-T9] Apply Delivered Source Z0 to `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. - Acceptance (recorded by P3-T15): the test assigns `model.WorkerStarter = StartSynchronously;` and its act lambda constructs `new SynchronousBackgroundWorker()`. -- [ ] [P3-T10] Replace the documentation and method of `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z1. +- [x] [P3-T10] Replace the documentation and method of `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z1. - Acceptance (recorded by P3-T15): the test asserts the loader-invoked signal with the reason literal `the injected RemainingEmailLoader must be invoked by the started worker`; the documentation no longer mentions a bounded timeout. -- [ ] [P3-T11] Apply Delivered Source Z2 to `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. +- [x] [P3-T11] Apply Delivered Source Z2 to `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs. - Acceptance (recorded by P3-T15): the zero-batch file has `.Wait(` 0, `new BackgroundWorker()` 0, `new SynchronousBackgroundWorker()` 3 and `WorkerStarter = StartSynchronously;` 3. -- [ ] [P3-T12] Apply Delivered Source R-BODY (the baseline pin taken inside the gate: four lines inserted after pre-edit line 214, pre-edit lines 215 to 258 re-indented by four spaces, one closing line inserted after pre-edit line 258) to `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. This task runs before P3-T13, so the pre-edit line numbers are current. +- [x] [P3-T12] Apply Delivered Source R-BODY (the baseline pin taken inside the gate: four lines inserted after pre-edit line 214, pre-edit lines 215 to 258 re-indented by four spaces, one closing line inserted after pre-edit line 258) to `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. This task runs before P3-T13, so the pre-edit line numbers are current. - Acceptance (recorded by P3-T15): within `R4SPAN`, `IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` 1, `EnsureUiThreadDispatcher()` 1, `using (` 2, `.BeSameAs(` 1 and `.NotBeSameAs(` 1; within `R4PRE`, `EnsureUiThreadDispatcher()` 0 and `using (` 0; within `R4HEAD`, `EnsureUiThreadDispatcher()` 1 and `using (` 1; within `R4TAIL`, `}` 3; the `[Timeout(GateTimeoutMs)]` attribute and `private const int GateTimeoutMs = 60000;` are unchanged. -- [ ] [P3-T13] Replace the R4 documentation paragraph (pre-edit lines 196 to 202, unchanged by P3-T12) of QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs with Delivered Source R-DOC. +- [x] [P3-T13] Replace the R4 documentation paragraph (pre-edit lines 196 to 202, unchanged by P3-T12) of QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs with Delivered Source R-DOC. - Acceptance (recorded by P3-T15): `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, and one line each containing `The gate-free fixture method EnsureDispatcher seeds the parked dispatcher`, `cannot restore a null previous value between the pin` and `(W2), and UiThread.Initialize (W5) must not latch`. -- [ ] [P3-T14] Replace the class remarks (pre-edit lines 23 to 31) of QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z-R. +- [x] [P3-T14] Replace the class remarks (pre-edit lines 23 to 31) of QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs with Delivered Source Z-R. - Acceptance (recorded by P3-T15): the zero-batch file has `starts a real` 0 and one line containing `so no test starts a`. -- [ ] [P3-T15] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);", "TaskCreationOptions.RunContinuationsAsynchronously", "IsBusy", "starts a real", "so no test starts a"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "cannot restore a null previous value between the pin", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL` (the P0-T12 token list of each) and `CMD-TIMESPAN`, plus a second `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"the synchronous starter must reach the injected RemainingEmailLoader", "the synchronous starter must reach the injected loader before returning", "the finally around the awaited loader must clear the flag once it completes", "the finally must clear the flag on the throwing path too", "private static void StartSynchronously(BackgroundWorker worker)", "the injected RemainingEmailLoader must be invoked by the started worker", "bounded timeout"`). +- [x] [P3-T15] Record the test-rewrite census in FEATURE/evidence/qa-gates/test-rewrite-census.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "new BackgroundWorker()", "new SynchronousBackgroundWorker()", "WorkerStarter = StartSynchronously;", "private sealed class SynchronousBackgroundWorker : BackgroundWorker", "private sealed class DrainableSynchronizationContext : SynchronizationContext", "pump.Drain();", "SynchronizationContext.SetSynchronizationContext(previous);", "TaskCreationOptions.RunContinuationsAsynchronously", "IsBusy", "starts a real", "so no test starts a"`), `CMD-TOKEN-COUNT` on the R4 file (the P0-T12 R4 tokens plus `"The gate-free fixture method EnsureDispatcher seeds the parked dispatcher", "cannot restore a null previous value between the pin", "(W2), and UiThread.Initialize (W5) must not latch"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN`, `R4PRE`, `R4HEAD` and `R4TAIL` (the P0-T12 token list of each) and `CMD-TIMESPAN`, plus a second `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"the synchronous starter must reach the injected RemainingEmailLoader", "the synchronous starter must reach the injected loader before returning", "the finally around the awaited loader must clear the flag once it completes", "the finally must clear the flag on the throwing path too", "private static void StartSynchronously(BackgroundWorker worker)", "the injected RemainingEmailLoader must be invoked by the started worker", "bounded timeout"`). - Acceptance (fourteen THREE tokens in the order listed): liveness 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0; teardown 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0; zero-batch 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1; reason-literal rows (seven tokens in the order listed): liveness 1, 1, 1, 1, 1, 0, 0; teardown 1, 0, 0, 0, 1, 0, 0; zero-batch 0, 0, 0, 0, 1, 1, 0; R4 file `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1, `[Timeout(GateTimeoutMs)]` 8, the constant 1, the three R-DOC tokens 1 each; `R4SPAN` 1, 1, 2, 1, 1; `R4PRE` 0, 0; `R4HEAD` 1, 1; `R4TAIL` 3; `TIMESPAN-UNCLASSIFIED: 0`, with every `TIMESPAN` line `CLASSIFIED`. Any other value: correct the edit and re-run this task. (The first eleven values of each THREE row are the executor's round 1 rows; the last three columns are added by revision R1 for the stale-comment edits.) ### Phase 4 — Scoped Format, Pass-After Runs and Implementation Commit From fe2f80f65e730d3a7b7d0ca44c3d73b90244b6f4 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:09:13 -0400 Subject: [PATCH 16/24] fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../QfcInitEmailQueueZeroBatchTests.cs | 3 +- ...ntroller.UiThreadDispatcherFixtureTests.cs | 7 ++- .../evidence/qa-gates/post-format-census.md | 58 +++++++++++++++++++ .../evidence/qa-gates/scoped-format.md | 28 +++++++++ .../concurrent-classes-pass-after.md | 56 ++++++++++++++++++ .../regression-testing/pass-after-build.md | 15 +++++ .../regression-testing/r4-fail-before.md | 14 +++++ .../regression-testing/targets-pass-after.md | 26 +++++++++ .../plan.2026-10-01T07-11.md | 12 ++-- 9 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/post-format-census.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/scoped-format.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/concurrent-classes-pass-after.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/pass-after-build.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/targets-pass-after.md diff --git a/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs b/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs index f0c093dfe..d87ce6ec8 100644 --- a/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs +++ b/QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs @@ -144,7 +144,8 @@ public void InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing() IList result = null; // Act - System.Action act = () => result = model.InitEmailQueue(0, new SynchronousBackgroundWorker()); + System.Action act = () => + result = model.InitEmailQueue(0, new SynchronousBackgroundWorker()); // Assert act.Should().NotThrow(); diff --git a/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs b/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs index 136926666..8a19b1645 100644 --- a/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs +++ b/QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs @@ -232,9 +232,10 @@ public async Task Transaction_SecondCallerCannotInstallUntilTheFirstRestores() Task waiter = Task.Run(async () => { secondCallerStarted.Set(); - UiThreadDispatcherTransaction transactionB = await UiThreadDispatcherFixture - .BeginTransactionAsync() - .ConfigureAwait(false); + UiThreadDispatcherTransaction transactionB = + await UiThreadDispatcherFixture + .BeginTransactionAsync() + .ConfigureAwait(false); try { observedByB = UiThreadDispatcherFixture.Current; diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/post-format-census.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/post-format-census.md new file mode 100644 index 000000000..a953f44c9 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/post-format-census.md @@ -0,0 +1,58 @@ +# Post-format census (P4-T2) + +Timestamp: 2026-10-02T01-06 +P4-RESTART: 0 +Command: (1) one pwsh -NoProfile -Command payload after PREFIX re-running the P2-T4 commands (CMD-TOKEN-COUNT on QfcDatamodel.cs with the five P2-T4 tokens, CMD-SPAN-TOKEN-COUNT on INITQ, CMD-LINECOUNT over CODE5, the QFCDATAMODEL-BOM check) and the P3-T15 commands (the fourteen THREE tokens, the seven reason-literal tokens, the R4 file tokens, the four R4 spans, CMD-TIMESPAN); each Tok call prints its counts as one ROW line in token order. (2) git -C WORKTREE diff --numstat 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs. (3) git -C WORKTREE diff 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs. (4) git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test. (5) Re-anchoring companion, see below: git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler QuickFiler.Test. +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 + +## P2-T4 values (QfcDatamodel.cs) +Tokens (property line, constructor default, "Injectable worker-start seam", "null on instances built by GetUninitializedObject", loader-assignment literal): 1, 2, 1, 1, 2 +TRIMMED-EQUAL [worker.RunWorkerAsync();] = 0 +INITQ: 271-316; RunWorkerAsync 0; WorkerStarter(worker); 2 +QFCDATAMODEL-BOM: True + +## CMD-LINECOUNT (each at most 500) +LINES QuickFiler\Controllers\QfcDatamodel.cs = 495 +LINES QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 312 +LINES QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 244 +LINES QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 232 +LINES QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 470 + +## P3-T15 values +THREE rows (fourteen tokens, P3-T15 order): +- Liveness: 0, 0, 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 0, 0 +- Teardown: 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +- Zero-batch: 0, 0, 0, 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 1 +Reason-literal rows (seven tokens, P3-T15 order): +- Liveness: 1, 1, 1, 1, 1, 0, 0 +- Teardown: 1, 0, 0, 0, 1, 0, 0 +- Zero-batch: 0, 0, 0, 0, 1, 1, 0 +R4 file (flake-watch, Append an observation, Issue #950:, [Timeout(GateTimeoutMs)], the constant, three R-DOC tokens): 0, 0, 1, 8, 1, 1, 1, 1 +R4SPAN: 212-284; 1, 1, 2, 1, 1 +R4PRE: 212-218; 0, 0 +R4HEAD: 212-224; 1, 1 +R4TAIL: 267-273; 3 +TIMESPAN lines: liveness 139, 141, 156 (fake.Advance) and teardown 124, 155 (QuiesceLoaderAsync), 160 (fake.Advance), all CLASSIFIED +TIMESPAN-UNCLASSIFIED: 0 + +## Anchored diff of QfcDatamodel.cs +numstat: 14 2 QuickFiler/Controllers/QfcDatamodel.cs +Added lines: two `WorkerStarter = worker => worker.RunWorkerAsync();` constructor defaults, the blank line and nine S1 lines, and two `WorkerStarter(worker);` start sites (14). Deleted lines (2): both have the trimmed text `worker.RunWorkerAsync();` (hunks at BASE lines 273 and 300). + +## Porcelain span and re-anchoring note +Porcelain (git status --porcelain -- QuickFiler QuickFiler.Test): + M QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs + M QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs + +PLAN-CORRECTION (re-anchoring only): the P4-T2 acceptance expects the porcelain span to list all five CODE5 paths with ` M`. The delegation required a commit at every phase boundary, so the Phase 2 commit (0700ee1e3) already holds QfcDatamodel.cs and the Phase 3 commit (709123349) holds the four test files as written. The working tree therefore differs from HEAD only in the two files the P4-T1 formatter rewrote. The purpose of the check (all five Write Set files changed, nothing else under QuickFiler/ or QuickFiler.Test/) is re-anchored to BASE with the companion name-status, which covers committed plus working-tree changes: +M QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +M QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs +M QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs +M QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs +M QuickFiler/Controllers/QfcDatamodel.cs +Exactly the five CODE5 paths, status M, and no other path under the code trees. Every porcelain path is a CODE5 path. + +This artifact is the evidence for AC1, AC2, AC3 and AC14 and the census half of AC6 to AC13. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/scoped-format.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/scoped-format.md new file mode 100644 index 000000000..7e0bd9336 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/scoped-format.md @@ -0,0 +1,28 @@ +# Scoped CSharpier format of the Write Set (P4-T1) + +Timestamp: 2026-10-02T01-05 +P4-RESTART: 0 +Command: one pwsh -NoProfile -Command payload after PREFIX: CMD-HASH (before); `dotnet tool run csharpier format QuickFiler\Controllers\QfcDatamodel.cs QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"`; CMD-HASH (after), with the REWRITTEN list computed from the hash difference. +Canonical command: dotnet tool run csharpier format +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Before: +HASH QuickFiler\Controllers\QfcDatamodel.cs = B06004A654EB1630B4759D378271187BA252F1FCC44486B9DFF9B4B08C24491F +HASH QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 04A1963C8D577FB6FD43079DD05446600399832EC3438971D08503CB2B11870A +HASH QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 0F076832B8ACEC32BA61D822D19397E7ADF9FECE4424C2EC3F73B4D7D277D3F1 +HASH QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 306D98796ABB7E80A12BF5707D17CF684E648918CEE5B9EA4DE2DE705FBE8C6A +HASH QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 15BDA8A55D70B7B651579DC8058B7315C2BC62E60E4E33403A4CF75682F83039 +Formatted 5 files in 4480ms. (processed-file count, not a rewrite count) +CSHARPIER_EXIT_CODE: 0 +After: +HASH QuickFiler\Controllers\QfcDatamodel.cs = B06004A654EB1630B4759D378271187BA252F1FCC44486B9DFF9B4B08C24491F +HASH QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 04A1963C8D577FB6FD43079DD05446600399832EC3438971D08503CB2B11870A +HASH QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 0F076832B8ACEC32BA61D822D19397E7ADF9FECE4424C2EC3F73B4D7D277D3F1 +HASH QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 4B3D6D3FD67ABC2F583EEABB6FAFEC22D3561A7B72AEC553556F31C2A30B32EE +HASH QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 05638767D69C12FE98B28DCE78B6827AD2C1EC64221F1E445862087668BF5DCA + +REWRITTEN: QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs, QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs + +The two rewrites are the expected layout reflows (the over-width Z0 act lambda and the re-indented R4 body); formatter output wins. The committed text is therefore formatter-stable. P4-T2 re-checks every census value after this pass. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/concurrent-classes-pass-after.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/concurrent-classes-pass-after.md new file mode 100644 index 000000000..160edb782 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/concurrent-classes-pass-after.md @@ -0,0 +1,56 @@ +# Pass-after concurrent run of the four target classes with QfcItemController_FocusAndThemeTests (P4-T5) + +Timestamp: 2026-10-02T01-08 +P4-RESTART: 0 +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-CONCURRENT (QCT. expanded), TASKID p4-t5 and empty NAMES, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-CONCURRENT" "/ResultsDirectory:coverage\test-results\950\p4-t5" "/Logger:trx;LogFileName=p4-t5.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=37 executed=37 passed=37 failed=0 +RESULT_COUNT: 37 +RESULT TryQueueRemainingMailItemAsync_AfterCleanupNulledFields_ReturnsFalseWithoutThrowing = Passed duration=00:00:00.1351500 +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Passed duration=00:00:00.1243627 +RESULT ToggleNavigation_Synchronous_TogglesPositionTips = Passed duration=00:00:00.0042419 +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Passed duration=00:00:00.0034524 +RESULT SetThemeLight_FromNormal_SelectsLightNormalTheme = Passed duration=00:00:00.0002900 +RESULT ToggleFocus_ParameterlessOverload_MarshalsThroughItemViewerInvoke = Passed duration=00:00:00.0061414 +RESULT ToggleSaveAttachments_DoesNotThrow = Passed duration=00:00:00.0002462 +RESULT ToggleFocus_StateOverload_MarshalsThroughItemViewerInvoke = Passed duration=00:00:00.3645132 +RESULT SetThemeDark_FromNormal_SelectsDarkNormalTheme = Passed duration=00:00:00.0004669 +RESULT ToggleFocusOffAsync_DeactivatesUiAndSwitchesToNormalTheme = Passed duration=00:00:00.0007734 +RESULT InvokeBeginInvoke_WhenSynchronous_UsesInvoke = Passed duration=00:00:00.0004538 +RESULT ToggleFocus_ParameterlessOverload_FromActive_DeactivatesUiAndSwitchesToNormalTheme = Passed duration=00:00:00.0012065 +RESULT BeginTransactionAsync_ZeroBoundWhileThisTestHoldsThePermit_ThrowsTimeoutExceptionAndReleasesNothing = Passed duration=00:00:00.0513738 +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Passed duration=00:00:00.0005949 +RESULT Worker_DoWork_CapturesRemainingLoadTask = Passed duration=00:00:00.0008942 +RESULT ToggleTipsAsync_WithEmptyCollections_Completes = Passed duration=00:00:00.0004007 +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Passed duration=00:00:00.0005800 +RESULT QuiesceLoaderAsync_LoaderHangs_ReturnsAtBoundAndLogs = Passed duration=00:00:00.0067577 +RESULT EnsureDispatcher_WhileATransactionHoldsALiveDispatcher_DoesNotReplaceIt = Passed duration=00:00:00.0558074 +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Passed duration=00:00:00.0005510 +RESULT InvokeBeginInvoke_WhenAsync_UsesBeginInvoke = Passed duration=00:00:00.0012755 +RESULT QuiesceLoaderAsync_LoaderCompletes_ReturnsBeforeTimeout = Passed duration=00:00:00.0073498 +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Passed duration=00:00:00.1602821 +RESULT ToggleFocus_StateOverload_Off_FromActive_DeactivatesUiAndSwitchesToNormalTheme = Passed duration=00:00:00.0026943 +RESULT Cleanup_CalledTwice_DoesNotThrow = Passed duration=00:00:00.0012682 +RESULT HtmlDarkConverter_WhenWebViewNotInitialized_DoesNotNavigate = Passed duration=00:00:00.0005992 +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0035572 +RESULT ToggleTips_Synchronous_DispatchesAndExecutesDelegate = Passed duration=00:00:00.0004116 +RESULT ToggleFocusOnAsync_ActivatesUiAndSwitchesToActiveTheme = Passed duration=00:00:00.0020793 +RESULT Transaction_DisposedTwice_DoesNotOverReleaseTheGate = Passed duration=00:00:00.0034823 +RESULT EnsureDispatcher_ScopeDisposedTwice_IsIdempotent = Passed duration=00:00:00.0037047 +RESULT TransactionGate_WhileThisTestHoldsATransaction_HasExactlyOneUnreleasedAcquisition = Passed duration=00:00:00.0023954 +RESULT EnsureDispatcher_WhenTheFieldIsNull_InstallsAndRestoresOnDispose = Passed duration=00:00:00.0013583 +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Passed duration=00:00:00.1879890 +RESULT ToggleNavigationAsync_AwaitsPositionTipsToggleAsync = Passed duration=00:00:00.0012641 +RESULT ToggleNavigation_WithState_TogglesPositionTipsWithState = Passed duration=00:00:00.0008584 +RESULT Install_CalledTwiceOnTheSameTransaction_ThrowsInvalidOperationException = Passed duration=00:00:00.0062198 + +All nine target results Passed, including Transaction_SecondCallerCannotInstallUntilTheFirstRestores; both FocusAndThemeTests theme tests (SetThemeDark_FromNormal_SelectsDarkNormalTheme, SetThemeLight_FromNormal_SelectsLightNormalTheme) Passed, so no THEME TEST NULL-DISPATCHER EXPOSURE was observed. +CONCURRENT-NOT-PASSED: NONE +CONCURRENT-NEW-FAILURES: NONE diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/pass-after-build.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/pass-after-build.md new file mode 100644 index 000000000..53a485428 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/pass-after-build.md @@ -0,0 +1,15 @@ +# Pass-after build of the fixed tree (P4-T3) + +Timestamp: 2026-10-02T01-07 +P4-RESTART: 0 +Command: CMD-BUILD with TASKID p4-t3, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-BUILD body verbatim ($log = "coverage\logs\p4-t3.msbuild.log"). A CLOCK echo and a read-only diagnostic echo (first 20 lines matching "(error|warning) CODE", paths redacted; none printed) were added. +Canonical command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" (resolved through vswhere, plus /nodeReuse:false and a normal-verbosity file logger) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +TEST_DLL_ADVANCED: True +CSC_OUT_QUICKFILER_TEST: 2 +No error or warning diagnostic line in the build log. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md index 0745711df..dafbf8a7d 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/r4-fail-before.md @@ -20,3 +20,17 @@ RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Failed durat MESSAGE Transaction_SecondCallerCannotInstallUntilTheFirstRestores :: Expected observedByB to refer to because the first transaction restores before it releases the gate, so the waiter cannot observe the pre-restore value, but found System.Windows.Threading.Dispatcher { HasShutdownFinished = False, HasShutdownStarted = False, Hooks = System.Windows.Threading.DispatcherHooks{ }, Thread = System.Threading.Thread { ApartmentState = ApartmentState.STA {value: 0}, CurrentCulture = en-US, CurrentUICulture = en-US, ExecutionContext = System.Threading.ExecutionContext{ }, IsAlive = True, IsBackground = True, IsThreadPoolThread = False, ManagedThreadId = 37, Name = "UiThreadDispatcherFixture.ParkedDispatcher", Priority = ThreadPriority.Normal {value: 2}, ThreadState = ThreadState.Background|WaitSleepJoin {value: 36} } }. The message contains `to refer to` and `ParkedDispatcher`: the CI failure signature of spec Repro and Evidence (the second caller observed the parked dispatcher where the null baseline was expected). + +## R4-PASS-AFTER: + +Timestamp: 2026-10-02T01-09 +Task: P4-T6 + +Fail-before (P1-T6, pre-fix shape with the injected gate-free writer, run alone): +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Failed duration=00:00:00.1933723 + +Pass-after, fixed tree with the baseline pin taken inside the gate (P3-T12), no injected writer: +- FEATURE/evidence/regression-testing/targets-pass-after.md (P4-T4, nine targets): RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0730800 +- FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md (P4-T5, concurrent with QfcItemController_FocusAndThemeTests under Workers=0 / ClassLevel): RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0035572 + +R4-PASS-AFTER: Passed in both pass-after artifacts. This completes the Defect B fail-before and pass-after pair. Phase 5 batch A (P5-T11) separately runs the pinned shape with the same injected writer. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/targets-pass-after.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/targets-pass-after.md new file mode 100644 index 000000000..0335e8b98 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/targets-pass-after.md @@ -0,0 +1,26 @@ +# Pass-after run of the nine target tests (P4-T4) + +Timestamp: 2026-10-02T01-08 +P4-RESTART: 0 +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-TARGETS (nine FullyQualifiedName= expressions, QCT. expanded, joined with |), TASKID p4-t4 and NAMES-TARGETS, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-TARGETS" "/ResultsDirectory:coverage\test-results\950\p4-t4" "/Logger:trx;LogFileName=p4-t4.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 0 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=9 executed=9 passed=9 failed=0 +RESULT_COUNT: 9 +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Passed duration=00:00:00.1525930 +RESULT Worker_DoWork_CapturesRemainingLoadTask = Passed duration=00:00:00.0573582 +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Passed duration=00:00:00.0013640 +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Passed duration=00:00:00.0028547 +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Passed duration=00:00:00.2647588 +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0730800 +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Passed duration=00:00:00.0031888 +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Passed duration=00:00:00.1367933 +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Passed duration=00:00:00.0028913 + +All nine target tests Passed under the repository runsettings (Workers=0, Scope=ClassLevel). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 3cfccb275..7deee83b3 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -870,18 +870,18 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 4 — Scoped Format, Pass-After Runs and Implementation Commit -- [ ] [P4-T1] Format the five Write Set code files with a scoped CSharpier pass and record the before-and-after hashes in FEATURE/evidence/qa-gates/scoped-format.md. +- [x] [P4-T1] Format the five Write Set code files with a scoped CSharpier pass and record the before-and-after hashes in FEATURE/evidence/qa-gates/scoped-format.md. - Command: `CMD-HASH`; then `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier format QuickFiler\Controllers\QfcDatamodel.cs QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; then `CMD-HASH` again. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `CSHARPIER_EXIT_CODE: 0`; the line beginning `Formatted ` is transcribed and labelled as a processed-file count, not a rewrite count; `REWRITTEN:` lists every CODE5 path whose hash differs between the two captures, or `NONE` (the rewrite observation is the hash difference, not the console line). Either value completes this task: the pass exists so the committed text is formatter-stable. -- [ ] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T15 commands, plus `git -C WORKTREE diff --numstat 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [x] [P4-T2] Record the post-format census in FEATURE/evidence/qa-gates/post-format-census.md by re-running the P2-T4 commands and the P3-T15 commands, plus `git -C WORKTREE diff --numstat 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs` and `git -C WORKTREE diff 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- QuickFiler/Controllers/QfcDatamodel.cs`, paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: every P2-T4 and P3-T15 value holds after formatting; CMD-LINECOUNT reports at most 500 for each CODE5 file (expected 495 for QfcDatamodel.cs; the others are recorded); numstat for QfcDatamodel.cs reads `14 2`, and the two deleted lines in the anchored diff both have the trimmed text `worker.RunWorkerAsync();`; the porcelain span lists exactly the five CODE5 paths with status ` M`. This artifact is the evidence for AC1, AC2, AC3 and AC14 and the census half of AC6 to AC13. -- [ ] [P4-T3] Build the fixed tree with `CMD-BUILD` (`TASKID` p4-t3) and record it in FEATURE/evidence/regression-testing/pass-after-build.md. +- [x] [P4-T3] Build the fixed tree with `CMD-BUILD` (`TASKID` p4-t3) and record it in FEATURE/evidence/regression-testing/pass-after-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`, `CSC_OUT_QUICKFILER_TEST:` at least 1. -- [ ] [P4-T4] Run the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p4-t4, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/targets-pass-after.md. +- [x] [P4-T4] Run the nine target tests with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-TARGETS`, `TASKID` p4-t4, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/targets-pass-after.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 9`; nine `RESULT` lines, one per target name, each `Passed`, with durations recorded. Any target not `Passed` invokes the D-13 Phase 4 restart rule. -- [ ] [P4-T5] Run the four target classes concurrently with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p4-t5, empty `NAMES`) and record FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md. +- [x] [P4-T5] Run the four target classes concurrently with QfcItemController_FocusAndThemeTests using `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONCURRENT`, `TASKID` p4-t5, empty `NAMES`) and record FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; every nine-target `RESULT` line `Passed`; `CONCURRENT-NOT-PASSED:` lists every non-Passed name or `NONE`, and every listed name is in P0-T14 `BASELINE-CONCURRENT-FAILED:` (`CONCURRENT-NEW-FAILURES: NONE`). `EXIT_CODE: 0` when the list is `NONE`; otherwise `ExpectedExitCode:` equals the observed value and the artifact names the baseline entries that account for it. A new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"): stop and report; any other new failure invokes the D-13 Phase 4 restart rule. -- [ ] [P4-T6] Confirm the R4 pass-after condition from FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/regression-testing/targets-pass-after.md by appending a `R4-PASS-AFTER:` section to FEATURE/evidence/regression-testing/r4-fail-before.md. +- [x] [P4-T6] Confirm the R4 pass-after condition from FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/regression-testing/targets-pass-after.md by appending a `R4-PASS-AFTER:` section to FEATURE/evidence/regression-testing/r4-fail-before.md. - Acceptance: the section quotes the R4 `RESULT` line from both pass-after artifacts (`Passed` in each) beside the P1-T6 `Failed` line, completing the fail-before and pass-after pair for Defect B. - [ ] [P4-T7] Commit the implementation (the five CODE5 files and FEATURE) and record FEATURE/evidence/qa-gates/implementation-commit.md. - Commands, separate Bash calls: `git -C WORKTREE add -- QuickFiler/Controllers/QfcDatamodel.cs QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. From bd99f92d64c20a0d8cfadbce8fbbaa6a1bac20c7 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:09:42 -0400 Subject: [PATCH 17/24] docs(950): record the implementation commit evidence Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../evidence/qa-gates/implementation-commit.md | 16 ++++++++++++++++ .../plan.2026-10-01T07-11.md | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/implementation-commit.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/implementation-commit.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/implementation-commit.md new file mode 100644 index 000000000..1e3faa1d1 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/implementation-commit.md @@ -0,0 +1,16 @@ +# Implementation commit (P4-T7) + +Timestamp: 2026-10-02T01-10 +P4-RESTART: 0 +Command: git -C WORKTREE add -- QuickFiler/Controllers/QfcDatamodel.cs QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950; git -C WORKTREE commit -m "fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline" -m "Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com"; git -C WORKTREE rev-parse HEAD; git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD; git -C WORKTREE status --porcelain --untracked-files=all (separate calls); then git -C WORKTREE push origin bug/quickfiler-tests-depend-on-wall-clock-timing-950 +EXIT_CODE: 0 + +Output Summary: +git add: exit 0 (CRLF normalisation warnings only) +git commit: exit 0; 9 files changed, 209 insertions(+), 10 deletions(-) (the two formatter-rewritten test files and FEATURE; the other three code files were committed at the Phase 2 and Phase 3 boundaries, 0700ee1e3 and 709123349) +IMPLEMENTATION-COMMIT: fe2f80f65e730d3a7b7d0ca44c3d73b90244b6f4 +Name-status BASE..HEAD: the five CODE5 paths with status M; every other path is under FEATURE (status A) or is the inherited promotion record docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md (status A); nothing else. +Porcelain after the commit: empty (no path under QuickFiler/ or QuickFiler.Test/) +Push: 709123349..fe2f80f65 accepted by origin + +This artifact is committed in P6-T32 (or at the Phase 4 boundary commit, whichever comes first). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 7deee83b3..aa05f2123 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -883,7 +883,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; every nine-target `RESULT` line `Passed`; `CONCURRENT-NOT-PASSED:` lists every non-Passed name or `NONE`, and every listed name is in P0-T14 `BASELINE-CONCURRENT-FAILED:` (`CONCURRENT-NEW-FAILURES: NONE`). `EXIT_CODE: 0` when the list is `NONE`; otherwise `ExpectedExitCode:` equals the observed value and the artifact names the baseline entries that account for it. A new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"): stop and report; any other new failure invokes the D-13 Phase 4 restart rule. - [x] [P4-T6] Confirm the R4 pass-after condition from FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/regression-testing/targets-pass-after.md by appending a `R4-PASS-AFTER:` section to FEATURE/evidence/regression-testing/r4-fail-before.md. - Acceptance: the section quotes the R4 `RESULT` line from both pass-after artifacts (`Passed` in each) beside the P1-T6 `Failed` line, completing the fail-before and pass-after pair for Defect B. -- [ ] [P4-T7] Commit the implementation (the five CODE5 files and FEATURE) and record FEATURE/evidence/qa-gates/implementation-commit.md. +- [x] [P4-T7] Commit the implementation (the five CODE5 files and FEATURE) and record FEATURE/evidence/qa-gates/implementation-commit.md. - Commands, separate Bash calls: `git -C WORKTREE add -- QuickFiler/Controllers/QfcDatamodel.cs QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "fix(950): start the QfcDatamodel worker through a seam and pin the R4 baseline"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: both git writes exit 0; `IMPLEMENTATION-COMMIT:` records the new HEAD as an observation; the name-status diff lists the five CODE5 paths with status `M`, FEATURE paths, and at most the inherited promotion record, nothing else; no porcelain line names a path under QuickFiler/ or QuickFiler.Test/. This artifact is committed in P6-T32. From 2f7602f6991cea547307eb14c266f15708faeab0 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:16:41 -0400 Subject: [PATCH 18/24] docs(950): record negative-control evidence Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../other/negative-controls-summary.md | 30 +++++++++++ .../regression-testing/controls-a-build.md | 13 +++++ .../regression-testing/controls-a-edits.md | 30 +++++++++++ .../regression-testing/controls-a-revert.md | 11 ++++ .../regression-testing/controls-b-build.md | 13 +++++ .../regression-testing/controls-b-edits.md | 15 ++++++ .../regression-testing/controls-b-revert.md | 10 ++++ .../regression-testing/controls-c-build.md | 13 +++++ .../regression-testing/controls-c-edits.md | 17 +++++++ .../regression-testing/controls-c-revert.md | 10 ++++ .../fail-before-exception.2026-10-02T01-00.md | 19 +++++++ .../negative-controls-batch-a.md | 42 ++++++++++++++++ .../negative-controls-batch-b.md | 22 ++++++++ .../negative-controls-batch-c.md | 24 +++++++++ .../plan.2026-10-01T07-11.md | 50 +++++++++---------- 15 files changed, 294 insertions(+), 25 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/negative-controls-summary.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-build.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-edits.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-revert.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-build.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-edits.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-revert.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-build.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-edits.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-revert.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-a.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-b.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-c.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/negative-controls-summary.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/negative-controls-summary.md new file mode 100644 index 000000000..dcde90be4 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/negative-controls-summary.md @@ -0,0 +1,30 @@ +# Negative-control summary (P5-T25, AC15) + +Timestamp: 2026-10-02T01-16 + +Every value below is copied from the cited artifact. All runs used the repository runsettings (scripts\vscode\TaskMaster.cli.runsettings, Workers=0, Scope=ClassLevel) with the hang-dump blame switch; no run produced a Sequence file, a Timeout, an Aborted or a NotExecuted outcome. + +## AC15 table (one row per test named in AC6 to AC13, in that order) + +| Test | Mechanism (spec Test Strategy) | Edit applied | Observed outcome | Failure message fragment | Duration | Source artifact | +|---|---|---|---|---|---|---| +| QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle (AC6) | assign a no-op WorkerStarter | A1 | Failed | the synchronous starter must reach the injected RemainingEmailLoader | 00:00:00.1859136 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces (AC7) | no-op WorkerStarter | B1 | Failed | the synchronous starter must reach the injected loader before returning | 00:00:00.2592311 | regression-testing/negative-controls-batch-b.md (P5-T16) | +| QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse (AC8) | never set release, then Drain() | A2 | Failed | the finally around the awaited loader must clear the flag once it completes | 00:00:00.0085546 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally (AC9) | never set release, then Drain() | A3 | Failed | the finally must clear the flag on the throwing path too | 00:00:00.0017485 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask (AC10) | no-op WorkerStarter | A4 | Failed | the synchronous starter must reach the injected RemainingEmailLoader | 00:00:00.1862274 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker (AC11) | no-op WorkerStarter | A5 | Failed | the injected RemainingEmailLoader must be invoked by the started worker | 00:00:00.0077444 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing (AC12) | remove the WorkerStarter assignment | A6 | Failed | NullReferenceException (at the start site in InitEmailQueue) | 00:00:00.1859851 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop (AC12) | remove the WorkerStarter assignment | A7 | Failed | NullReferenceException | 00:00:00.2862076 | regression-testing/negative-controls-batch-a.md (P5-T11) | +| QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores (AC13) | pre-fix shape plus one injected discarded EnsureUiThreadDispatcher() between the original read and Install(liveA), run alone; then the pinned shape with the same injected call | R-INJECT on the unmodified file (P1-T6); A8 on the pinned file (P5-T11) | Failed (pre-fix, P1-T6); Passed (pinned, P5-T11) | P1-T6: Expected observedByB to refer to null ... Name = "UiThreadDispatcherFixture.ParkedDispatcher"; P5-T11: none (passed) | 00:00:00.1933723 (P1-T6); 00:00:00.0825982 (P5-T11) | regression-testing/r4-fail-before.md (P1-T6); regression-testing/negative-controls-batch-a.md (P5-T11) | + +## Drain-dependency controls + +Supplements the AC15 table; it does not replace the Test Strategy mechanism above. + +| Test | Mechanism (spec Test Strategy) | Edit applied | Observed outcome | Failure message fragment | Duration | Source artifact | +|---|---|---|---|---|---|---| +| QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse | release set, Drain() removed (shows the flag clears only through Drain) | C1 | Failed | the finally around the awaited loader must clear the flag once it completes | 00:00:00.1620202 | regression-testing/negative-controls-batch-c.md (P5-T22) | +| QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally | release set, Drain() removed | C2 | Failed | the finally must clear the flag on the throwing path too | 00:00:00.0021400 | regression-testing/negative-controls-batch-c.md (P5-T22) | + +Every control that is meant to fail did so at once with an assertion or an exception; none hung. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-build.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-build.md new file mode 100644 index 000000000..5919c18c8 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-build.md @@ -0,0 +1,13 @@ +# Batch A control build (P5-T10) + +Timestamp: 2026-10-02T01-11 +Command: CMD-BUILD with TASKID p5-t10, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-BUILD body verbatim ($log = "coverage\logs\p5-t10.msbuild.log"). One CLOCK echo line was added after PREFIX. +Canonical command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +TEST_DLL_ADVANCED: True +CSC_OUT_QUICKFILER_TEST: 2 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-edits.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-edits.md new file mode 100644 index 000000000..09642f929 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-edits.md @@ -0,0 +1,30 @@ +# Negative-control batch A edit census (P5-T1 to P5-T9) + +Timestamp: 2026-10-02T01-10 +Command: one pwsh -NoProfile -Command payload after PREFIX: CMD-TOKEN-COUNT on THREE (TOKENS "WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };", "release.SetResult(true);", "pump.Drain();"; printed as one ROW per file) and CMD-SPAN-TOKEN-COUNT on R4SPAN (TOKENS "EnsureUiThreadDispatcher()", "IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()"); then git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test and git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls). +EXIT_CODE: 0 + +Edits applied (temporary, against the implementation commit; reverted by P5-T12): +- A1 (test 1, DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle): `model.WorkerStarter = StartSynchronously;` replaced with `model.WorkerStarter = _ => { };` +- A2 (test 3, RemainingLoadActive_AfterLoaderCompletes_BecomesFalse): `release.SetResult(true);` deleted; `pump.Drain();` kept +- A3 (test 4, RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally): `release.SetResult(true);` deleted; `pump.Drain();` kept +- A4 (Worker_DoWork_CapturesRemainingLoadTask): `model.WorkerStarter = StartSynchronously;` replaced with `model.WorkerStarter = _ => { };` +- A5 (Z1, InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker): `model.WorkerStarter = StartSynchronously;` replaced with `model.WorkerStarter = _ => { };` +- A6 (Z0, InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing): `model.WorkerStarter = StartSynchronously;` deleted +- A7 (Z2, InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop): `model.WorkerStarter = StartSynchronously;` deleted +- A8 (R4, Transaction_SecondCallerCannotInstallUntilTheFirstRestores): Delivered Source R-INJECT inserted at twenty spaces immediately before `transactionA.Install(liveA);`, inside the `baseline` using block, after the `original` read + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Liveness (StartSynchronously, no-op starter, release.SetResult, pump.Drain): 1, 1, 1, 2 +Teardown: 0, 1, 0, 0 +Zero-batch: 0, 1, 0, 0 +R4SPAN SPAN: 212-285; EnsureUiThreadDispatcher() 2; IDisposable baseline = ... 1 +numstat: +1 3 QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +1 1 QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs +1 3 QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs +1 0 QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs +porcelain: exactly those four paths with ` M` + +All P5-T9 acceptance values hold. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-revert.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-revert.md new file mode 100644 index 000000000..52b98eec5 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-a-revert.md @@ -0,0 +1,11 @@ +# Batch A revert (P5-T12) + +Timestamp: 2026-10-02T01-12 +Command: git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs; git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test; git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls) +EXIT_CODE: 0 + +Output Summary: +restore: exit 0 +anchored diff: exit 0, printed nothing +porcelain span: printed nothing +The four test files are byte-identical to HEAD (bd99f92d6, whose code files equal the implementation commit fe2f80f65). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-build.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-build.md new file mode 100644 index 000000000..404b99152 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-build.md @@ -0,0 +1,13 @@ +# Batch B control build (P5-T15) + +Timestamp: 2026-10-02T01-13 +Command: CMD-BUILD with TASKID p5-t15, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-BUILD body verbatim ($log = "coverage\logs\p5-t15.msbuild.log"). One CLOCK echo line was added after PREFIX. +Canonical command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +TEST_DLL_ADVANCED: True +CSC_OUT_QUICKFILER_TEST: 2 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-edits.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-edits.md new file mode 100644 index 000000000..f1c5a2f0e --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-edits.md @@ -0,0 +1,15 @@ +# Negative-control batch B edit census (P5-T13, P5-T14) + +Timestamp: 2026-10-02T01-12 +Command: CMD-TOKEN-COUNT on QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs (TOKENS "WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };") in one pwsh -NoProfile -Command payload after PREFIX, plus a read-only echo of the no-op line and the line after it; then git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test and git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls). +EXIT_CODE: 0 + +Edit applied (temporary; reverted by P5-T17): +- B1 (StartHeldOpenLoader, used by test 2 RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces): `model.WorkerStarter = StartSynchronously;` replaced with `model.WorkerStarter = _ => { };` + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +TOKEN [WorkerStarter = StartSynchronously;] = 1 (test 1) +TOKEN [WorkerStarter = _ => { };] = 1 (line 202, inside StartHeldOpenLoader, directly above `model.InitEmailQueue(0, worker);`) +numstat: 1 1 QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +porcelain: M QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (only that path) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-revert.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-revert.md new file mode 100644 index 000000000..1a7a9dbf5 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-b-revert.md @@ -0,0 +1,10 @@ +# Batch B revert (P5-T17) + +Timestamp: 2026-10-02T01-13 +Command: git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs; git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test; git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls) +EXIT_CODE: 0 + +Output Summary: +restore: exit 0 +anchored diff: exit 0, printed nothing +porcelain span: printed nothing diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-build.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-build.md new file mode 100644 index 000000000..e572be070 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-build.md @@ -0,0 +1,13 @@ +# Batch C control build (P5-T21) + +Timestamp: 2026-10-02T01-15 +Command: CMD-BUILD with TASKID p5-t21, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-BUILD body verbatim ($log = "coverage\logs\p5-t21.msbuild.log"). One CLOCK echo line was added after PREFIX. +Canonical command: msbuild TaskMaster.sln /t:Build /m /p:Configuration=Debug "/p:Platform=Any CPU" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +TEST_DLL_ADVANCED: True +CSC_OUT_QUICKFILER_TEST: 2 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-edits.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-edits.md new file mode 100644 index 000000000..7d8a017f3 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-edits.md @@ -0,0 +1,17 @@ +# Negative-control batch C edit census (P5-T18 to P5-T20) + +Timestamp: 2026-10-02T01-14 +Command: CMD-TOKEN-COUNT on QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs (TOKENS "pump.Drain();", "release.SetResult(true);", "TaskCreationOptions.RunContinuationsAsynchronously") in one pwsh -NoProfile -Command payload after PREFIX; then git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test and git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls). +EXIT_CODE: 0 + +Edits applied (temporary; reverted by P5-T23): +- C1 (test 3, RemainingLoadActive_AfterLoaderCompletes_BecomesFalse): `pump.Drain();` deleted; `release.SetResult(true);` kept +- C2 (test 4, RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally): `pump.Drain();` deleted; `release.SetResult(true);` kept + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +TOKEN [pump.Drain();] = 0 +TOKEN [release.SetResult(true);] = 3 +TOKEN [TaskCreationOptions.RunContinuationsAsynchronously] = 1 +numstat: 0 2 QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +porcelain: M QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs (only that path) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-revert.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-revert.md new file mode 100644 index 000000000..f4c455708 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/controls-c-revert.md @@ -0,0 +1,10 @@ +# Batch C revert (P5-T23) + +Timestamp: 2026-10-02T01-15 +Command: git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs; git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test; git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test (separate calls) +EXIT_CODE: 0 + +Output Summary: +restore: exit 0 +anchored diff: exit 0, printed nothing +porcelain span: printed nothing diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md index 4beb21f14..f872afcac 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md @@ -11,3 +11,22 @@ WhyFailingRunImpossible: The Defect A failure occurs only when the thread pool d 2. The two direct start sites: QuickFiler/Controllers/QfcDatamodel.cs lines 273 and 300 each call `worker.RunWorkerAsync();` (`TRIMMED-EQUAL [worker.RunWorkerAsync();] = 2`, `INITQ` `RunWorkerAsync` 2), and `WorkerStarter` is absent (`TOKEN [WorkerStarter] = 0`), so no test can control the thread on which the worker body runs (P0-T12). 3. Recorded failure history (spec Context): two failures stopped the #944 P3-T8 gate, and one failure occurred during the #929 local run, both in QfcDatamodelLivenessTests under the repository runsettings on a loaded machine. 4. Forward statement: P5-T24 appends a "Post-fix deterministic controls" section to this dossier, recording the ten post-fix negative controls (A1 to A7, B1, C1, C2), each of which fails at once when its signal is withheld. + +## Post-fix deterministic controls + +Appended by P5-T24 at 2026-10-02T01-16. Sources: FEATURE/evidence/regression-testing/negative-controls-batch-a.md (P5-T11), FEATURE/evidence/regression-testing/negative-controls-batch-b.md (P5-T16), FEATURE/evidence/regression-testing/negative-controls-batch-c.md (P5-T22). + +| Control | Test | Signal withheld | Outcome | Duration | Source | +|---|---|---|---|---|---| +| A1 | DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle | no-op WorkerStarter | Failed | 00:00:00.1859136 | negative-controls-batch-a.md | +| A2 | RemainingLoadActive_AfterLoaderCompletes_BecomesFalse | release never set, then Drain() | Failed | 00:00:00.0085546 | negative-controls-batch-a.md | +| A3 | RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally | release never set, then Drain() | Failed | 00:00:00.0017485 | negative-controls-batch-a.md | +| A4 | Worker_DoWork_CapturesRemainingLoadTask | no-op WorkerStarter | Failed | 00:00:00.1862274 | negative-controls-batch-a.md | +| A5 | InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker | no-op WorkerStarter | Failed | 00:00:00.0077444 | negative-controls-batch-a.md | +| A6 | InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing | WorkerStarter assignment removed | Failed | 00:00:00.1859851 | negative-controls-batch-a.md | +| A7 | InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop | WorkerStarter assignment removed | Failed | 00:00:00.2862076 | negative-controls-batch-a.md | +| B1 | RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces | no-op WorkerStarter in StartHeldOpenLoader | Failed | 00:00:00.2592311 | negative-controls-batch-b.md | +| C1 | RemainingLoadActive_AfterLoaderCompletes_BecomesFalse | Drain() removed (loader released) | Failed | 00:00:00.1620202 | negative-controls-batch-c.md | +| C2 | RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally | Drain() removed (loader released) | Failed | 00:00:00.0021400 | negative-controls-batch-c.md | + +Each rewritten test fails at once (every duration is under 0.3 seconds) when its signal is withheld, with an assertion or a NullReferenceException and no hang. The pre-fix tests could show the same failure only by waiting out a five-second bound on a thread-pool worker. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-a.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-a.md new file mode 100644 index 000000000..d53848f46 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-a.md @@ -0,0 +1,42 @@ +# Negative-control batch A run (P5-T11) + +Timestamp: 2026-10-02T01-11 +Task: P5-T11 [expect-fail] +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-CONTROLS-A (the eight FILTER-TARGETS expressions for test 1, test 3, test 4, the teardown test, the three zero-batch tests and R4; test 2 excluded; QCT. expanded), TASKID p5-t11 and NAMES-TARGETS, executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-CONTROLS-A" "/ResultsDirectory:coverage\test-results\950\p5-t11" "/Logger:trx;LogFileName=p5-t11.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 1 +ExpectedExitCode: 1 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 1 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=8 executed=8 passed=1 failed=7 +RESULT_COUNT: 8 +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Failed duration=00:00:00.1859851 +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Failed duration=00:00:00.0017485 +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Failed duration=00:00:00.0077444 +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Failed duration=00:00:00.2862076 +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Failed duration=00:00:00.1859136 +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Failed duration=00:00:00.0085546 +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed duration=00:00:00.0825982 +RESULT Worker_DoWork_CapturesRemainingLoadTask = Failed duration=00:00:00.1862274 +MESSAGE InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing :: Did not expect any exception, but found System.NullReferenceException: Object reference not set to an instance of an object. at QuickFiler.Controllers.QfcDatamodel.InitEmailQueue(Int32 batchSize, BackgroundWorker worker) in REDACTED-PATH\QuickFiler\Controllers\QfcDatamodel.cs:line 285 at QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.<>c__DisplayClass7_0.b__0() in REDACTED-PATH\QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs:line 147 at FluentAssertions.Specialized.DelegateAssertions`2.InvokeSubjectWithInterception() in /_/Src/FluentAssertions/Specialized/DelegateAssertions.cs:line 174. +MESSAGE RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally :: Expected ReadLivenessFlag(model) to be False because the finally must clear the flag on the throwing path too, or the gate would poll forever, but found True. +MESSAGE InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker :: Expected loaderInvokedTcs.Task.IsCompleted to be True because the injected RemainingEmailLoader must be invoked by the started worker, but found False. +MESSAGE InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop :: Test method QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop threw exception: System.NullReferenceException: Object reference not set to an instance of an object. +MESSAGE DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle :: Expected loaderEntered.Task.IsCompleted to be True because the synchronous starter must reach the injected RemainingEmailLoader, but found False. +MESSAGE RemainingLoadActive_AfterLoaderCompletes_BecomesFalse :: Expected ReadLivenessFlag(model) to be False because the finally around the awaited loader must clear the flag once it completes, but found True. +MESSAGE Worker_DoWork_CapturesRemainingLoadTask :: Expected loaderEntered.Task.IsCompleted to be True because the synchronous starter must reach the injected RemainingEmailLoader, but found False. + +Per-control verdict against the P5-T11 acceptance: +- A1 test 1: Failed; message contains "the synchronous starter must reach the injected RemainingEmailLoader" (0.186 s) +- A2 test 3: Failed; message contains "the finally around the awaited loader must clear the flag once it completes" (0.009 s) +- A3 test 4: Failed; message contains "the finally must clear the flag on the throwing path too" (0.002 s) +- A4 teardown: Failed; message contains "the synchronous starter must reach the injected RemainingEmailLoader" (0.186 s) +- A5 Z1: Failed; message contains "the injected RemainingEmailLoader must be invoked by the started worker" (0.008 s) +- A6 Z0: Failed; message contains "NullReferenceException" (0.186 s) +- A7 Z2: Failed; message contains "NullReferenceException" (0.286 s) +- A8 R4: Passed (0.083 s); the pin neutralises the injected gate-free writer +No Timeout, Aborted or NotExecuted outcome; no Sequence file; no control passed that should fail. No CONTROL DEFECT. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-b.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-b.md new file mode 100644 index 000000000..4340d481c --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-b.md @@ -0,0 +1,22 @@ +# Negative-control batch B run (P5-T16) + +Timestamp: 2026-10-02T01-13 +Task: P5-T16 [expect-fail] +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-CONTROLS-B (FullyQualifiedName=QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces), TASKID p5-t16 and NAMES "RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces", executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-CONTROLS-B" "/ResultsDirectory:coverage\test-results\950\p5-t16" "/Logger:trx;LogFileName=p5-t16.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 1 +ExpectedExitCode: 1 + +Edit under test: B1 (no-op starter in StartHeldOpenLoader). + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 1 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=1 executed=1 passed=0 failed=1 +RESULT_COUNT: 1 +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Failed duration=00:00:00.2592311 +MESSAGE RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces :: Expected entered.Task.IsCompleted to be True because the synchronous starter must reach the injected loader before returning, but found False. + +Verdict: Failed at once with the message "the synchronous starter must reach the injected loader before returning". No CONTROL DEFECT. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-c.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-c.md new file mode 100644 index 000000000..1275e4512 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/regression-testing/negative-controls-batch-c.md @@ -0,0 +1,24 @@ +# Negative-control batch C run (P5-T22) + +Timestamp: 2026-10-02T01-15 +Task: P5-T22 [expect-fail] +Command: CMD-VSTEST with ASSEMBLY-QF, FILTER-CONTROLS-C (tests 3 and 4 by FullyQualifiedName=, QCT. expanded), TASKID p5-t22 and NAMES "RemainingLoadActive_AfterLoaderCompletes_BecomesFalse", "RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally", executed as one pwsh -NoProfile -Command payload: PREFIX, TOOLS, then the CMD-VSTEST body verbatim. One CLOCK echo line was added after PREFIX. +Canonical command: vstest.console.exe QuickFiler.Test\bin\Debug\QuickFiler.Test.dll /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:FILTER-CONTROLS-C" "/ResultsDirectory:coverage\test-results\950\p5-t22" "/Logger:trx;LogFileName=p5-t22.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 1 +ExpectedExitCode: 1 + +Edits under test: C1 and C2 (`pump.Drain();` deleted from tests 3 and 4; the loader is still released). + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +VSTEST_EXIT_CODE: 1 +TRX_PRESENT: True +SEQUENCE_FILES: 0 +COUNTERS total=2 executed=2 passed=0 failed=2 +RESULT_COUNT: 2 +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Failed duration=00:00:00.0021400 +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Failed duration=00:00:00.1620202 +MESSAGE RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally :: Expected ReadLivenessFlag(model) to be False because the finally must clear the flag on the throwing path too, or the gate would poll forever, but found True. +MESSAGE RemainingLoadActive_AfterLoaderCompletes_BecomesFalse :: Expected ReadLivenessFlag(model) to be False because the finally around the awaited loader must clear the flag once it completes, but found True. + +Verdict: with the loader released but Drain() removed, both tests fail at once, so the flag clears only through Drain(). The loader's continuation is posted to the installed DrainableSynchronizationContext rather than run inline inside SetResult (D-2; RunContinuationsAsynchronously on the release source). Neither test passed (no DRAIN NOT LOAD-BEARING); no Timeout, Aborted or NotExecuted outcome and no Sequence file (no CONTROL DEFECT). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index aa05f2123..8376ae420 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -889,58 +889,58 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 5 — Negative Controls (temporary edits against the implementation commit) -- [ ] [P5-T1] Control edit A1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: replace the first occurrence in file order of `model.WorkerStarter = StartSynchronously;` (test 1) with `model.WorkerStarter = _ => { };`. +- [x] [P5-T1] Control edit A1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: replace the first occurrence in file order of `model.WorkerStarter = StartSynchronously;` (test 1) with `model.WorkerStarter = _ => { };`. - Acceptance (recorded by P5-T9): the liveness file has `WorkerStarter = StartSynchronously;` 1 and `WorkerStarter = _ => { };` 1, the latter inside test 1. -- [ ] [P5-T2] Control edit A2 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `release.SetResult(true);` inside `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` (test 3), so the loader is never released before `pump.Drain();`. +- [x] [P5-T2] Control edit A2 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `release.SetResult(true);` inside `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` (test 3), so the loader is never released before `pump.Drain();`. - Acceptance (recorded by P5-T9): test 3 no longer contains `release.SetResult(true);` and still contains `pump.Drain();`. -- [ ] [P5-T3] Control edit A3 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `release.SetResult(true);` inside `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (test 4). +- [x] [P5-T3] Control edit A3 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `release.SetResult(true);` inside `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (test 4). - Acceptance (recorded by P5-T9): the liveness file has `release.SetResult(true);` 1 (test 2 only) and `pump.Drain();` 2. -- [ ] [P5-T4] Control edit A4 in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs: replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. +- [x] [P5-T4] Control edit A4 in QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs: replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. - Acceptance (recorded by P5-T9): the teardown file has `WorkerStarter = StartSynchronously;` 0 and `WorkerStarter = _ => { };` 1. -- [ ] [P5-T5] Control edit A5 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`, replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. +- [x] [P5-T5] Control edit A5 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker`, replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. - Acceptance (recorded by P5-T9): `WorkerStarter = _ => { };` 1 in the zero-batch file, inside that test. -- [ ] [P5-T6] Control edit A6 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing`, delete the line `model.WorkerStarter = StartSynchronously;`. +- [x] [P5-T6] Control edit A6 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing`, delete the line `model.WorkerStarter = StartSynchronously;`. - Acceptance (recorded by P5-T9): that test contains no `WorkerStarter` assignment. -- [ ] [P5-T7] Control edit A7 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`, delete the line `model.WorkerStarter = StartSynchronously;`. +- [x] [P5-T7] Control edit A7 in QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs: inside `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop`, delete the line `model.WorkerStarter = StartSynchronously;`. - Acceptance (recorded by P5-T9): the zero-batch file has `WorkerStarter = StartSynchronously;` 0 and `WorkerStarter = _ => { };` 1. -- [ ] [P5-T8] Control edit A8 in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs: insert Delivered Source R-INJECT immediately before `transactionA.Install(liveA);` in the pinned R4 body (inside the `baseline` using block, after the `original` read, at twenty spaces). +- [x] [P5-T8] Control edit A8 in QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs: insert Delivered Source R-INJECT immediately before `transactionA.Install(liveA);` in the pinned R4 body (inside the `baseline` using block, after the `original` read, at twenty spaces). - Acceptance (recorded by P5-T9): within `R4SPAN`, `EnsureUiThreadDispatcher()` 2 and `IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` 1. -- [ ] [P5-T9] Record the batch A edit census in FEATURE/evidence/regression-testing/controls-a-edits.md with `CMD-TOKEN-COUNT` on THREE (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };", "release.SetResult(true);", "pump.Drain();"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()", "IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()"`), and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [x] [P5-T9] Record the batch A edit census in FEATURE/evidence/regression-testing/controls-a-edits.md with `CMD-TOKEN-COUNT` on THREE (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };", "release.SetResult(true);", "pump.Drain();"`), `CMD-SPAN-TOKEN-COUNT` on `R4SPAN` (TOKENS `"EnsureUiThreadDispatcher()", "IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher()"`), and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: liveness 1, 1, 1, 2; teardown 0, 1, 0, 0; zero-batch 0, 1, 0, 0; `R4SPAN` 2 and 1; numstat lists exactly the four test files (liveness `1 3`, teardown `1 1`, zero-batch `1 3`, R4 `1 0`); porcelain lists exactly those four paths with ` M`. The artifact names each edit A1 to A8 with its test. -- [ ] [P5-T10] Build the batch A tree with `CMD-BUILD` (`TASKID` p5-t10) and record FEATURE/evidence/regression-testing/controls-a-build.md. +- [x] [P5-T10] Build the batch A tree with `CMD-BUILD` (`TASKID` p5-t10) and record FEATURE/evidence/regression-testing/controls-a-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. -- [ ] [P5-T11] [expect-fail] Run batch A with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-A`, `TASKID` p5-t11, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/negative-controls-batch-a.md. +- [x] [P5-T11] [expect-fail] Run batch A with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-A`, `TASKID` p5-t11, `NAMES-TARGETS`) and record FEATURE/evidence/regression-testing/negative-controls-batch-a.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 8`; and per test, with duration recorded: test 1 `Failed` with a message containing `the synchronous starter must reach the injected RemainingEmailLoader`; test 3 `Failed` with `the finally around the awaited loader must clear the flag once it completes`; test 4 `Failed` with `the finally must clear the flag on the throwing path too`; teardown `Failed` with `the synchronous starter must reach the injected RemainingEmailLoader`; Z1 `Failed` with `the injected RemainingEmailLoader must be invoked by the started worker`; Z0 `Failed` with `NullReferenceException`; Z2 `Failed` with `NullReferenceException`; R4 `Passed` (the pin neutralises the injected gate-free writer). Any `Timeout`, `Aborted` or `NotExecuted` outcome, any Sequence file, or any control that passes is `CONTROL DEFECT`: stop and report, because a control that does not fail immediately is a defect in the rewrite (spec Test Strategy). -- [ ] [P5-T12] Revert batch A to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-a-revert.md. +- [x] [P5-T12] Revert batch A to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-a-revert.md. - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. -- [ ] [P5-T13] Control edit B1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: inside `StartHeldOpenLoader`, replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. +- [x] [P5-T13] Control edit B1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: inside `StartHeldOpenLoader`, replace `model.WorkerStarter = StartSynchronously;` with `model.WorkerStarter = _ => { };`. - Acceptance (recorded by P5-T14): the liveness file has `WorkerStarter = StartSynchronously;` 1 (test 1) and `WorkerStarter = _ => { };` 1 (inside `StartHeldOpenLoader`). -- [ ] [P5-T14] Record the batch B edit census in FEATURE/evidence/regression-testing/controls-b-edits.md with `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [x] [P5-T14] Record the batch B edit census in FEATURE/evidence/regression-testing/controls-b-edits.md with `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"WorkerStarter = StartSynchronously;", "WorkerStarter = _ => { };"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: 1 and 1; numstat lists only the liveness file with `1 1`; porcelain lists only that path with ` M`. -- [ ] [P5-T15] Build the batch B tree with `CMD-BUILD` (`TASKID` p5-t15) and record FEATURE/evidence/regression-testing/controls-b-build.md. +- [x] [P5-T15] Build the batch B tree with `CMD-BUILD` (`TASKID` p5-t15) and record FEATURE/evidence/regression-testing/controls-b-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. -- [ ] [P5-T16] [expect-fail] Run batch B with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-B`, `TASKID` p5-t16, `NAMES` `"RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces"`) and record FEATURE/evidence/regression-testing/negative-controls-batch-b.md. +- [x] [P5-T16] [expect-fail] Run batch B with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-B`, `TASKID` p5-t16, `NAMES` `"RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces"`) and record FEATURE/evidence/regression-testing/negative-controls-batch-b.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 1`; the `RESULT` line `Failed` with duration recorded; the `MESSAGE` line contains `the synchronous starter must reach the injected loader before returning`. Any other outcome is `CONTROL DEFECT`: stop. -- [ ] [P5-T17] Revert batch B to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-b-revert.md. +- [x] [P5-T17] Revert batch B to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-b-revert.md. - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. -- [ ] [P5-T18] Control edit C1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `pump.Drain();` inside `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` (test 3), keeping `release.SetResult(true);`, so the released loader's continuation stays queued in the installed context. +- [x] [P5-T18] Control edit C1 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `pump.Drain();` inside `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` (test 3), keeping `release.SetResult(true);`, so the released loader's continuation stays queued in the installed context. - Acceptance (recorded by P5-T20): test 3 still contains `release.SetResult(true);` and no longer contains `pump.Drain();`. -- [ ] [P5-T19] Control edit C2 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `pump.Drain();` inside `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (test 4), keeping `release.SetResult(true);`. +- [x] [P5-T19] Control edit C2 in QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs: delete the line `pump.Drain();` inside `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` (test 4), keeping `release.SetResult(true);`. - Acceptance (recorded by P5-T20): the liveness file has `pump.Drain();` 0 and `release.SetResult(true);` 3. -- [ ] [P5-T20] Record the batch C edit census in FEATURE/evidence/regression-testing/controls-c-edits.md with `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"pump.Drain();", "release.SetResult(true);", "TaskCreationOptions.RunContinuationsAsynchronously"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. +- [x] [P5-T20] Record the batch C edit census in FEATURE/evidence/regression-testing/controls-c-edits.md with `CMD-TOKEN-COUNT` on the liveness file (TOKENS `"pump.Drain();", "release.SetResult(true);", "TaskCreationOptions.RunContinuationsAsynchronously"`) and `git -C WORKTREE diff --numstat HEAD -- QuickFiler.Test` paired with `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: 0, 3 and 1; numstat lists only the liveness file with `0 2`; porcelain lists only that path with ` M`. The artifact names C1 and C2 with their tests. -- [ ] [P5-T21] Build the batch C tree with `CMD-BUILD` (`TASKID` p5-t21) and record FEATURE/evidence/regression-testing/controls-c-build.md. +- [x] [P5-T21] Build the batch C tree with `CMD-BUILD` (`TASKID` p5-t21) and record FEATURE/evidence/regression-testing/controls-c-build.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`, `EXIT_CODE: 0`, `ERRORS: 0`, `TEST_DLL_ADVANCED: True`. -- [ ] [P5-T22] [expect-fail] Run batch C with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-C`, `TASKID` p5-t22, `NAMES` `"RemainingLoadActive_AfterLoaderCompletes_BecomesFalse", "RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally"`) and record FEATURE/evidence/regression-testing/negative-controls-batch-c.md. +- [x] [P5-T22] [expect-fail] Run batch C with `CMD-VSTEST` (`ASSEMBLY-QF`, `FILTER-CONTROLS-C`, `TASKID` p5-t22, `NAMES` `"RemainingLoadActive_AfterLoaderCompletes_BecomesFalse", "RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally"`) and record FEATURE/evidence/regression-testing/negative-controls-batch-c.md. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 1`; `ExpectedExitCode: 1`; `TRX_PRESENT: True`; `SEQUENCE_FILES: 0`; `RESULT_COUNT: 2`; with duration recorded, test 3 `Failed` with a message containing `the finally around the awaited loader must clear the flag once it completes` and test 4 `Failed` with a message containing `the finally must clear the flag on the throwing path too`. This shows that, with the loader released, the flag clears only through `Drain()`: the continuation is posted to the installed context rather than run inline inside `SetResult` (D-2). Either test `Passed` is `DRAIN NOT LOAD-BEARING`, and any `Timeout`, `Aborted` or `NotExecuted` outcome or any Sequence file is `CONTROL DEFECT`: stop and report in both cases. -- [ ] [P5-T23] Revert batch C to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-c-revert.md. +- [x] [P5-T23] Revert batch C to HEAD and verify byte-for-byte, recording FEATURE/evidence/regression-testing/controls-c-revert.md. - Commands, separate Bash calls: `git -C WORKTREE restore --source=HEAD --worktree -- QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`; `git -C WORKTREE diff --exit-code HEAD -- QuickFiler QuickFiler.Test`; `git -C WORKTREE status --porcelain -- QuickFiler QuickFiler.Test`. - Acceptance: the restore exits 0; the anchored diff exits 0 and prints nothing; the porcelain span prints nothing. -- [ ] [P5-T24] Append a `## Post-fix deterministic controls` section to the FEATURE/evidence/regression-testing/fail-before-exception.*.md dossier citing FEATURE/evidence/regression-testing/negative-controls-batch-a.md, FEATURE/evidence/regression-testing/negative-controls-batch-b.md and FEATURE/evidence/regression-testing/negative-controls-batch-c.md. +- [x] [P5-T24] Append a `## Post-fix deterministic controls` section to the FEATURE/evidence/regression-testing/fail-before-exception.*.md dossier citing FEATURE/evidence/regression-testing/negative-controls-batch-a.md, FEATURE/evidence/regression-testing/negative-controls-batch-b.md and FEATURE/evidence/regression-testing/negative-controls-batch-c.md. - Acceptance: the section names the ten Defect A controls (A1 to A7 from batch A, B1 from batch B, C1 and C2 from batch C) with their outcome `Failed` and duration copied from the three batch artifacts, and states that each rewritten test fails at once when its signal is withheld, which the pre-fix tests could show only by waiting out a five-second bound. -- [ ] [P5-T25] Write the AC15 negative-control summary FEATURE/evidence/other/negative-controls-summary.md. +- [x] [P5-T25] Write the AC15 negative-control summary FEATURE/evidence/other/negative-controls-summary.md. - Acceptance: a Markdown table with exactly nine rows, one per test named in AC6 to AC13 in that order, with columns Test, Mechanism (the spec Test Strategy row), Edit applied (A1 to A8, B1, or R-INJECT on the unmodified file), Observed outcome, Failure message fragment, Duration and Source artifact. The R4 row cites P1-T6 (`Failed`, pre-fix shape with the injected writer) and P5-T11 (`Passed`, pinned shape with the same injected writer), matching the spec's two-part R4 mechanism. A second table headed `Drain-dependency controls` has exactly two rows, tests 3 and 4, with the same columns, Edit applied C1 and C2, and values copied from P5-T22; it supplements, and does not replace, the Test Strategy mechanism in the first table. Every row's values are copied from the cited artifacts; the artifact carries `Timestamp:`. ### Phase 6 — Final QA Loop, Static Gates, Check-offs and Final Commit From ec937a928f342a1cc55d8f9d45e861d5ab089f7b Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:27:04 -0400 Subject: [PATCH 19/24] docs(950): record final QA evidence and acceptance check-offs Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../evidence/other/ac-status-summary.md | 46 ++++++++ .../evidence/qa-gates/coverage-comparison.md | 28 +++++ .../evidence/qa-gates/coverage-post-change.md | 107 ++++++++++++++++++ .../qa-gates/csharpier-check-final.md | 12 ++ .../evidence/qa-gates/csharpier-format.md | 24 ++++ .../evidence/qa-gates/evidence-hygiene.md | 28 +++++ .../evidence/qa-gates/footprint-scope.md | 23 ++++ .../qa-gates/msbuild-analyzer-final.md | 22 ++++ .../qa-gates/msbuild-nullable-final.md | 20 ++++ .../qa-gates/prohibited-constructs.md | 21 ++++ .../evidence/qa-gates/toolchain-final-pass.md | 22 ++++ .../evidence/qa-gates/wall-clock-tokens.md | 22 ++++ .../plan.2026-10-01T07-11.md | 62 +++++----- .../spec.md | 32 +++--- 14 files changed, 422 insertions(+), 47 deletions(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-comparison.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-post-change.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-check-final.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-format.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/evidence-hygiene.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/footprint-scope.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-analyzer-final.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-nullable-final.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/prohibited-constructs.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/toolchain-final-pass.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/wall-clock-tokens.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md new file mode 100644 index 000000000..3b799670a --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md @@ -0,0 +1,46 @@ +# Acceptance-criteria status summary (P6-T29) + +Timestamp: 2026-10-02T01-26 + +### Acceptance Criteria Status +- Source: docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md (Work Mode full-bug; spec.md is the only acceptance-criteria source) +- Total AC items: 17 +- Checked off (delivered): 16 (lines beginning `- [x] AC`, counted after P6-T28) +- Remaining (unchecked): 1 (lines beginning `- [ ] AC`) +- Items remaining: + - AC17: NOT MET (ENVIRONMENTAL: COVERAGE-ROUTE DIRECT). The P0-T15 stall probe recorded STALL-PROBE: REPRODUCES because UtilitiesCS.Test ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension failed on this workstation ("Win32 handle that was passed to Icon is not valid or is the wrong type"). Under D-6 the test step therefore ran the runner's inner dotnet-coverage collector with the four shell-icon classes excluded, plus the runner's own post-processing, rather than scripts\vscode\Invoke-MSTestWithCoverage.ps1 verbatim. Every other step of the final toolchain passed in one iteration (SINGLE-PASS: YES), and the DIRECT run passed 7361 of 7361 tests with both coverage floors met (FEATURE/evidence/qa-gates/toolchain-final-pass.md). The coordinator rules on AC17. + +## Per-criterion evidence +| AC | Status | Evidence | +|---|---|---| +| AC1 | MET | qa-gates/post-format-census.md (property 1, constructor default 2, documentation tokens 1 and 1) | +| AC2 | MET | qa-gates/post-format-census.md (INITQ RunWorkerAsync 0, WorkerStarter(worker); 2) | +| AC3 | MET | qa-gates/post-format-census.md (QfcDatamodel.cs 495 lines) | +| AC4 | MET | qa-gates/wall-clock-tokens.md (all counts 0, TIMESPAN-UNCLASSIFIED: 0) | +| AC5 | MET | qa-gates/prohibited-constructs.md (prohibited added tokens 0, runsettings unchanged, [Timeout] 8 and constant 1) | +| AC6 | MET | regression-testing/targets-pass-after.md and qa-gates/coverage-post-change.md (Passed); census StartSynchronously 2 | +| AC7 | MET | regression-testing/targets-pass-after.md and qa-gates/coverage-post-change.md (Passed) | +| AC8 | MET | same two artifacts (Passed); census pump.Drain(); 2 | +| AC9 | MET | same two artifacts (Passed) | +| AC10 | MET | same two artifacts (Passed); teardown WaitForState 0 and .Wait( 0 | +| AC11 | MET | same two artifacts (Passed); zero-batch .Wait( 0 | +| AC12 | MET | same two artifacts (both tests Passed); zero-batch StartSynchronously 3 | +| AC13 | MET | targets-pass-after.md, concurrent-classes-pass-after.md and coverage-post-change.md (Passed); R4 spans 1,1,2,1,1 / 0,0 / 1,1 / 3 | +| AC14 | MET | qa-gates/post-format-census.md (flake-watch 0, Append an observation 0, Issue #950: 1, three R-DOC tokens 1 each) | +| AC15 | MET | other/negative-controls-summary.md (nine AC15 rows, two Drain-dependency rows) | +| AC16 | MET | other/ambient-synchronization-context.md (AMBIENT-SYNCHRONIZATION-CONTEXT: null) | +| AC17 | NOT MET | qa-gates/toolchain-final-pass.md (AC17-STATUS: NOT MET, COVERAGE-ROUTE DIRECT) | + +## Spec check-off diff + +Timestamp: 2026-10-02T01-27 +Task: P6-T31 +Command: git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md; git -C WORKTREE diff HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md; git -C WORKTREE status --porcelain -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md (separate calls) +EXIT_CODE: 0 + +Output Summary: +numstat: 16 16 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md (CRLF normalisation warning only) +Diff: one hunk at spec.md lines 263 to 284. The sixteen deleted lines are `- [ ] AC1:` through `- [ ] AC16:` and the sixteen added lines are `- [x] AC1:` through `- [x] AC16:`, each with the remaining criterion text identical. `- [ ] AC17:` is unchanged context. +porcelain: M docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md + +Added and deleted counts are equal (16) and equal the checked-off count (16). spec.md changed only in its checkbox lines. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-comparison.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-comparison.md new file mode 100644 index 000000000..4b79e6604 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-comparison.md @@ -0,0 +1,28 @@ +# Coverage and outcome comparison (P6-T6) + +Timestamp: 2026-10-02T01-22 +Sources: FEATURE/evidence/baseline/coverage-baseline.md (P0-T16) and FEATURE/evidence/qa-gates/coverage-post-change.md (P6-T5). Both runs used the DIRECT route with the identical four-class exclusion filter. +Command: none (comparison of two recorded artifacts) +EXIT_CODE: 0 + +Output Summary: + +## First-party coverage +- Baseline: First-party coverage: lines 56204/65855 (85.35%), branches 13618/17078 (79.74%) +- Post-change: First-party coverage: lines 56212/65855 (85.36%), branches 13620/17078 (79.75%) + +## Root counters +- Baseline: ROOT line-rate=0.853451 branch-rate=0.7974 lines-covered=56204 lines-valid=65855 branches-covered=13618 branches-valid=17078 +- Post-change: ROOT line-rate=0.853572 branch-rate=0.797517 lines-covered=56212 lines-valid=65855 branches-covered=13620 branches-valid=17078 + +## Repository-wide comparison +BRANCH A: the two lines-valid figures are equal (65855 and 65855, a difference of 0, within 1 percent of the baseline). The post-change line rate (85.36%) is not lower than the baseline line rate (85.35%); the 0.5 percentage-point tolerance is not needed. Branch rate 79.74% to 79.75%. No COVERAGE REGRESSION. +The small rise (8 lines, 2 branches, all in the UtilitiesCS package) is in code this plan did not change; it is run-to-run variance of the merged rate, which D-7 records as not reproducible across runs of an identical tree. + +## New and changed code +CHANGED-PRODUCTION-COVERAGE: NOT MEASURED. Reason: QfcDatamodel carries a type-level [ExcludeFromCodeCoverage] at QuickFiler/Controllers/QfcDatamodel.cs line 25, so the file has no class element in the Cobertura document (QFCDATAMODEL-CLASS-NODES: 0 at both stages). The changed test files are outside the instrumented set (test assemblies are excluded by the derived coverage settings). + +## Test outcomes +BASELINE-FAILED-SET: (empty) +FINAL-FAILED-SET: (empty) +NEW-FAILURES: NONE diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-post-change.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-post-change.md new file mode 100644 index 000000000..afe44e1bc --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-post-change.md @@ -0,0 +1,107 @@ +# Final QA step 4: repository-wide test-and-coverage run (P6-T5) + +Timestamp: 2026-10-02T01-21 +ITERATION: 1 +Command: (1) CMD-COVERAGE-DIRECT with STAGE final (route fixed by P0-T15: DIRECT), executed as one pwsh -NoProfile -Command payload started with run_in_background after a STRAY_TEST_PROCESSES / sibling-process check (both 0): PREFIX, then the CMD-COVERAGE-DIRECT body verbatim, ending with PAYLOAD-COMPLETE; CLOCK and CLOCK-END echo lines were added. (2) CMD-COVERAGE-POST with STAGE final and RAW True, PREFIX then the body verbatim with NAMES-TARGETS substituted. +Canonical command: dotnet-coverage collect --output coverage\final-950.cobertura.xml --output-format cobertura --settings coverage\effective-coverage-950.config -- vstest.console.exe /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:TestCategory!=LiveOutlook&FullyQualifiedName!~HelperClasses.ShellUtilities_Tests&FullyQualifiedName!~HelperClasses.ShellUtilitiesStatic_Tests&FullyQualifiedName!~HelperClasses.SysImageListHelperTests&FullyQualifiedName!~EmailIntelligence.OSBrowser_Tests" "/ResultsDirectory:coverage\test-results\950\final" "/Logger:trx;LogFileName=final-950.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +COVERAGE-ROUTE: DIRECT (equals P0-T15) +RAW: True +COLLECT_EXIT_CODE: 0 +ASSEMBLY_COUNT: 9 +ASSEMBLY: \QuickFiler.Test\bin\Debug\QuickFiler.Test.dll +ASSEMBLY: \SVGControl.Test\bin\Debug\SVGControl.Test.dll +ASSEMBLY: \Tags.Test\bin\Debug\Tags.Test.dll +ASSEMBLY: \TaskMaster.Test\bin\Debug\TaskMaster.Test.dll +ASSEMBLY: \TaskTree.Test\bin\Debug\TaskTree.Test.dll +ASSEMBLY: \TaskVisualization.Test\bin\Debug\TaskVisualization.Test.dll +ASSEMBLY: \ToDoModel.Test\bin\Debug\ToDoModel.Test.dll +ASSEMBLY: \UtilitiesCS.Test\bin\Debug\UtilitiesCS.Test.dll +ASSEMBLY: \VBFunctions.Test\bin\Debug\VBFunctions.Test.dll +TRX_PRESENT: True +DOCUMENT_PRESENT: True +SEQUENCE_FILES: 0 +PAYLOAD-COMPLETE observed (collection payload ran from 2026-10-02T01-20 to 2026-10-02T01-21) + +LINE-FLOOR: MET +BRANCH-FLOOR: MET +First-party coverage: lines 56212/65855 (85.36%), branches 13620/17078 (79.75%) +ROOT line-rate=0.853572 branch-rate=0.797517 lines-covered=56212 lines-valid=65855 branches-covered=13620 branches-valid=17078 + +PROJECTION-BEGIN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +PROJECTION-END + +SUMMARY-BEGIN +Test run outcome: Completed +Total 7361, executed 7361, passed 7361, failed 0. +Skipped 0, derived as total minus executed rather than reported by the test platform. +Figures reported verbatim by the test platform: error 0, timeout 0, aborted 0, notExecuted 0, inconclusive 0. +Failed tests: none +SUMMARY-END + +FINAL-FAILED-SET: (empty) +RESULT RemainingLoadActive_AfterLoaderCompletes_BecomesFalse = Passed +RESULT InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing = Passed +RESULT InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop = Passed +RESULT RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally = Passed +RESULT InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker = Passed +RESULT Transaction_SecondCallerCannotInstallUntilTheFirstRestores = Passed +RESULT Worker_DoWork_CapturesRemainingLoadTask = Passed +RESULT RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces = Passed +RESULT DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle = Passed +QFCDATAMODEL-CLASS-NODES: 0 + +NEW-FAILURES: NONE (FINAL-FAILED-SET is empty; BASELINE-FAILED-SET was empty) +FIGURES-COMPARED: +- Total: baseline 7361, final 7361 (equal; this plan adds and removes no test method) +- executed: baseline 7361, final 7361 (not less) +- error: baseline 0, final 0 (not greater) +- timeout: baseline 0, final 0 (not greater) +- aborted: baseline 0, final 0 (not greater) +- notExecuted: baseline 0, final 0 (not greater) +No breach. + +RUNNER-GREEN: NO (reason: COVERAGE-ROUTE DIRECT; P0-T15 recorded STALL-PROBE: REPRODUCES, so the runner scripts\vscode\Invoke-MSTestWithCoverage.ps1 was not run verbatim. The collector itself exited 0 with no failed test.) + +All P6-T5 acceptance conditions hold: route equals P0-T15, trx present, no Sequence file, class nodes 0, all nine targets Passed, no new failure, figures not regressed, both floors MET. The raw collector document and trx remain under the ignored coverage directory. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-check-final.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-check-final.md new file mode 100644 index 000000000..3ee460d01 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-check-final.md @@ -0,0 +1,12 @@ +# Final QA step 1 verify: CSharpier check (P6-T2) + +Timestamp: 2026-10-02T01-17 +ITERATION: 1 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"' (plus a CLOCK echo) +Canonical command: dotnet tool run csharpier check . +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Checked 1637 files in 6256ms. +CSHARPIER_EXIT_CODE: 0 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-format.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-format.md new file mode 100644 index 000000000..26241f386 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/csharpier-format.md @@ -0,0 +1,24 @@ +# Final QA step 1: CSharpier format (P6-T1) + +Timestamp: 2026-10-02T01-16 +ITERATION: 1 +Command: git -C WORKTREE status --porcelain --untracked-files=all; then one pwsh -NoProfile -Command payload after PREFIX: CMD-HASH (before), `dotnet tool run csharpier format .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"`, CMD-HASH (after) with REWRITTEN-WRITESET computed from the hash difference; then git -C WORKTREE status --porcelain --untracked-files=all. +Canonical command: dotnet tool run csharpier format . +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +Porcelain before: empty +Formatted 1637 files in 7462ms. (processed-file count, not a rewrite count) +CSHARPIER_EXIT_CODE: 0 +Write Set hashes before and after are identical: +HASH QuickFiler\Controllers\QfcDatamodel.cs = B06004A654EB1630B4759D378271187BA252F1FCC44486B9DFF9B4B08C24491F +HASH QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs = 04A1963C8D577FB6FD43079DD05446600399832EC3438971D08503CB2B11870A +HASH QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs = 0F076832B8ACEC32BA61D822D19397E7ADF9FECE4424C2EC3F73B4D7D277D3F1 +HASH QuickFiler.Test\Controllers\QfcInitEmailQueueZeroBatchTests.cs = 4B3D6D3FD67ABC2F583EEABB6FAFEC22D3561A7B72AEC553556F31C2A30B32EE +HASH QuickFiler.Test\Controllers\QfcItemController.UiThreadDispatcherFixtureTests.cs = 05638767D69C12FE98B28DCE78B6827AD2C1EC64221F1E445862087668BF5DCA +Porcelain after: empty + +REWRITTEN-WRITESET: NONE +REWRITTEN-OTHER: NONE +Clean pass; no restart. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/evidence-hygiene.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/evidence-hygiene.md new file mode 100644 index 000000000..baaebfb52 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/evidence-hygiene.md @@ -0,0 +1,28 @@ +# Evidence hygiene gate (P6-T11) + +Timestamp: 2026-10-02T01-24 +Command: pwsh -NoProfile -Command 'Set-Location -LiteralPath "WORKTREE"; [System.IO.Directory]::SetCurrentDirectory((Get-Location).Path); Write-Output ("WORKTREE-LEAF: " + (Split-Path -Leaf (Get-Location).Path)); $b = [char]92; $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950\evidence" -Recurse -File); "EVIDENCE_FILES=$($files.Count)"; "RAW_DOCUMENTS=$(@($files | Where-Object { $_.Extension -in @(".trx", ".xml", ".coverage", ".coveragexml", ".log") }).Count)"; $pattern = "[a-z]:[" + $b + $b + "/]+users[" + $b + $b + "/]+[a-z0-9_.~-]"; "PROFILE_PATH_LINES=$(@($files | Select-String -Pattern $pattern).Count)"' (plus a CLOCK echo, a PATTERN_LENGTH echo and a per-hit listing, which printed nothing) +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +EVIDENCE_FILES=55 +RAW_DOCUMENTS=0 +PATTERN_LENGTH=35 (the pattern was built from [char]92, so the Bash channel could not collapse its backslashes) +PROFILE_PATH_LINES=0 + +Pattern self-test (a separate in-memory probe; no file written): a backslash-separated drive-letter user-profile sample matched (True), the forward-slash form matched (True), and a REDACTED-PATH sample did not match (False). The zero count is therefore a real observation, not a vacuous one. + +## Re-run after check-offs + +Timestamp: 2026-10-02T01-26 +Task: P6-T30 +Command: the P6-T11 command above, re-run after P6-T29, plus one additional line scanning the files at the FEATURE root (spec.md, issue.md, plan.2026-10-01T07-11.md) with the same pattern. +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +EVIDENCE_FILES=57 +RAW_DOCUMENTS=0 +PROFILE_PATH_LINES=0 +FEATURE_ROOT_PROFILE_PATH_LINES=0 diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/footprint-scope.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/footprint-scope.md new file mode 100644 index 000000000..c6a2de1c6 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/footprint-scope.md @@ -0,0 +1,23 @@ +# Footprint gate (P6-T10) + +Timestamp: 2026-10-02T01-24 +Command: git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- . ":(exclude)docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950"; git -C WORKTREE status --porcelain --untracked-files=all (separate calls). The exclude pathspec removes the FEATURE paths at source; this is the same set the plan derives by filtering the unscoped name-status (P4-T7's full list is recorded in FEATURE/evidence/qa-gates/implementation-commit.md). +EXIT_CODE: 0 + +Output Summary: +Name-status BASE..HEAD outside FEATURE: +M QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs +M QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs +M QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs +M QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs +M QuickFiler/Controllers/QfcDatamodel.cs +A docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md + +THIS-ITEM-FOOTPRINT: exactly the five CODE5 paths, status M +INHERITED-AND-EXCLUDED: docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md (status A; listed in P0-T3 INHERITED-COMMITTED) +No path ends in .csproj. No status A outside FEATURE other than the inherited promotion record. + +Porcelain (all paths under FEATURE: the plan file and the uncommitted Phase 6 qa-gates artifacts): + M docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +?? FEATURE/evidence/qa-gates/coverage-comparison.md, coverage-post-change.md, csharpier-check-final.md, csharpier-format.md, msbuild-analyzer-final.md, msbuild-nullable-final.md, prohibited-constructs.md, toolchain-final-pass.md, wall-clock-tokens.md +No porcelain line names a path under QuickFiler/, QuickFiler.Test/ or scripts/. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-analyzer-final.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-analyzer-final.md new file mode 100644 index 000000000..47b6f7d69 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-analyzer-final.md @@ -0,0 +1,22 @@ +# Final QA step 2: analyzer rebuild (P6-T3) + +Timestamp: 2026-10-02T01-18 +ITERATION: 1 +Command: CMD-REBUILD with GATEARGS `/p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true` and TASKID p6-t3, executed as one pwsh -NoProfile -Command payload started in the background and polled: PREFIX, TOOLS, then the CMD-REBUILD body verbatim ($log = "coverage\logs\p6-t3.msbuild.log"). START and END clock echo lines were added. +Canonical command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 (ANALYZER-BASELINE-WARNINGS: 0) +SKIP_CORECOMPILE_LINES: 0 +CSC_OUT_QUICKFILER: 2 +CSC_OUT_QUICKFILER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 (ANALYZER-BASELINE-WRITESET-LINES: 0; not greater) +WRITESET_DIAGNOSTIC_CODES: (empty) (ANALYZER-BASELINE-WRITESET-CODES: (empty); no new code) +TEST_DLL_EXISTS: True +UCS_TEST_DLL_EXISTS: True + +No new analyzer diagnostic in a Write Set file. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-nullable-final.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-nullable-final.md new file mode 100644 index 000000000..21c5ca373 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/msbuild-nullable-final.md @@ -0,0 +1,20 @@ +# Final QA step 3: TreatWarningsAsErrors rebuild (P6-T4) + +Timestamp: 2026-10-02T01-19 +ITERATION: 1 +Command: CMD-REBUILD with GATEARGS `/p:TreatWarningsAsErrors=true` (no Nullable property override) and TASKID p6-t4, executed as one pwsh -NoProfile -Command payload started in the background and polled: PREFIX, TOOLS, then the CMD-REBUILD body verbatim ($log = "coverage\logs\p6-t4.msbuild.log"). START and END clock echo lines were added. +Canonical command: msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +MSBUILD_EXIT_CODE: 0 +ERRORS: 0 +WARNINGS: 0 +SKIP_CORECOMPILE_LINES: 0 +CSC_OUT_QUICKFILER: 2 +CSC_OUT_QUICKFILER_TEST: 2 +WRITESET_DIAGNOSTIC_LINES: 0 +WRITESET_DIAGNOSTIC_CODES: (empty) +TEST_DLL_EXISTS: True +UCS_TEST_DLL_EXISTS: True diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/prohibited-constructs.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/prohibited-constructs.md new file mode 100644 index 000000000..c8975c20c --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/prohibited-constructs.md @@ -0,0 +1,21 @@ +# AC5 prohibited-construct gate (P6-T9) + +Timestamp: 2026-10-02T01-23 +Command: (1) CMD-ADDED-SCAN in one pwsh -NoProfile -Command payload after PREFIX (git diff 34c2ed88cbb009f2f231453db87bc64d45a9bd51 -- CODE5-GIT; added lines are those beginning "+" but not "+++"), followed in the same payload by CMD-TOKEN-COUNT on the R4 file (TOKENS "[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"). (2) git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings. (3) git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings. +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +GIT_DIFF_EXIT_CODE: 0 +ADDED_LINES: 251 +ADDED-TOKEN [Thread.Sleep] = 0 +ADDED-TOKEN [Task.Delay] = 0 +ADDED-TOKEN [DoNotParallelize] = 0 +ADDED-TOKEN [Retry(] = 0 +ADDED-TOKEN [Timeout(] = 0 +ADDED-TOKEN [WorkerStarter] = 16 (positive control: the scan sees added lines) +Runsettings anchored diff: exit 0, printed nothing +Runsettings porcelain span: printed nothing +R4 file: [Timeout(GateTimeoutMs)] 8; private const int GateTimeoutMs = 60000; 1 (equal to P0-T12) + +No Thread.Sleep, Task.Delay, [DoNotParallelize], retry construct or [Timeout] value change is introduced by this branch, and neither runsettings file changed. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/toolchain-final-pass.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/toolchain-final-pass.md new file mode 100644 index 000000000..ed5737f13 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/toolchain-final-pass.md @@ -0,0 +1,22 @@ +# Final toolchain pass (P6-T7) + +Timestamp: 2026-10-02T01-22 +Sources: the P6-T1 to P6-T5 artifacts of ITERATION 1 (the only iteration). +Command: none (summary of five recorded command artifacts) +EXIT_CODE: 0 + +Output Summary: + +| Step | Exact command | EXIT_CODE | ITERATION | Result | +|---|---|---|---|---| +| 1. CSharpier format (P6-T1) | dotnet tool run csharpier format . | 0 | 1 | REWRITTEN-WRITESET: NONE; REWRITTEN-OTHER: NONE | +| 1b. CSharpier check (P6-T2) | dotnet tool run csharpier check . | 0 | 1 | Checked 1637 files in 6256ms. | +| 2. Analyzer rebuild (P6-T3) | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true | 0 | 1 | 0 errors, 0 warnings, no Write Set diagnostic | +| 3. TreatWarningsAsErrors rebuild (P6-T4) | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true | 0 | 1 | 0 errors, 0 warnings | +| 4. Coverage run (P6-T5) | COVERAGE-ROUTE: DIRECT (dotnet-coverage collect over vstest.console with the four shell-icon classes excluded, then the runner's own post-processing functions) | 0 (COLLECT_EXIT_CODE) | 1 | 7361/7361 passed, both floors MET; RUNNER-GREEN: NO | + +SINGLE-PASS: YES (all five rows come from ITERATION 1 with no restart after P6-T1) +RUNNER-GREEN: NO (reason: COVERAGE-ROUTE DIRECT) + +AC17-STATUS: NOT MET +Reason: ENVIRONMENTAL: COVERAGE-ROUTE DIRECT. P0-T15 recorded STALL-PROBE: REPRODUCES (UtilitiesCS.Test ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension failed with "Win32 handle that was passed to Icon is not valid or is the wrong type" on this workstation), so under D-6 the test step ran the runner's inner collector invocation with the four-class exclusion rather than scripts\vscode\Invoke-MSTestWithCoverage.ps1 verbatim. AC17 requires the runner itself; the evidence shows every other step passed in a single pass and the excluded-class run is green. The coordinator rules on AC17. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/wall-clock-tokens.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/wall-clock-tokens.md new file mode 100644 index 000000000..fa17da643 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/wall-clock-tokens.md @@ -0,0 +1,22 @@ +# AC4 wall-clock census (P6-T8) + +Timestamp: 2026-10-02T01-23 +Command: one pwsh -NoProfile -Command payload after PREFIX: CMD-TOKEN-COUNT on each of THREE (TOKENS "SpinWait", ".Wait(", "WaitForState", "Task.Wait(") and CMD-TIMESPAN, bodies verbatim. +EXIT_CODE: 0 + +Output Summary: +WORKTREE-LEAF: agent-a7805823735145ca4 +QfcDatamodelLivenessTests.cs: SpinWait 0, .Wait( 0, WaitForState 0, Task.Wait( 0 +QfcDatamodelTeardownTests.cs: SpinWait 0, .Wait( 0, WaitForState 0, Task.Wait( 0 +QfcInitEmailQueueZeroBatchTests.cs: SpinWait 0, .Wait( 0, WaitForState 0, Task.Wait( 0 +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:139 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:141 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelLivenessTests.cs:156 CLASSIFIED :: fake.Advance(TimeSpan.FromMilliseconds(200)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:124 CLASSIFIED :: Task pending = model.QuiesceLoaderAsync(TimeSpan.FromSeconds(5)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:155 CLASSIFIED :: Task pending = model.QuiesceLoaderAsync(TimeSpan.FromSeconds(5)); +TIMESPAN QuickFiler.Test\Controllers\QfcDatamodelTeardownTests.cs:160 CLASSIFIED :: fake.Advance(TimeSpan.FromSeconds(6)); +TIMESPAN-UNCLASSIFIED: 0 + +Surviving TimeSpan lines: the teardown file's two QuiesceLoaderAsync( production arguments and one fake.Advance(, and the liveness file's three fake.Advance( lines; none is a blocking wait. + +Positive control (P0-T12, FEATURE/evidence/baseline/census-baseline.md): before the change the same census read SpinWait 1 (liveness) and 1 (teardown); .Wait( 2, 1 and 1; WaitForState 5 and 2; TIMESPAN-UNCLASSIFIED: 6. The census therefore detects these tokens when present. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 8376ae420..07c3b680f 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -945,71 +945,71 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma ### Phase 6 — Final QA Loop, Static Gates, Check-offs and Final Commit -- [ ] [P6-T1] Run the CLAUDE.md formatting step `dotnet tool run csharpier format .` at the worktree root with a tree observation before and after, and record FEATURE/evidence/qa-gates/csharpier-format.md. +- [x] [P6-T1] Run the CLAUDE.md formatting step `dotnet tool run csharpier format .` at the worktree root with a tree observation before and after, and record FEATURE/evidence/qa-gates/csharpier-format.md. - Commands: `git -C WORKTREE status --porcelain --untracked-files=all`; `CMD-HASH`; `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier format .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`; `CMD-HASH`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `CSHARPIER_EXIT_CODE: 0`; the `Formatted ` line transcribed and labelled as a processed-file count; `REWRITTEN-WRITESET:` lists every CODE5 path whose hash changed, or `NONE`; `REWRITTEN-OTHER:` lists every porcelain path present after and absent before, or `NONE`. Clean pass: both `NONE`. A non-empty `REWRITTEN-WRITESET:` with `REWRITTEN-OTHER: NONE` invokes the D-13 format restart; a non-empty `REWRITTEN-OTHER:` is `FORMAT TOUCHED OUT-OF-SCOPE FILE`: stop. -- [ ] [P6-T2] Run the read-only formatter gate `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/csharpier-check-final.md. +- [x] [P6-T2] Run the read-only formatter gate `dotnet tool run csharpier check .` and record FEATURE/evidence/qa-gates/csharpier-check-final.md. - Command: `pwsh -NoProfile -Command 'PREFIX; dotnet tool run csharpier check .; "CSHARPIER_EXIT_CODE: $LASTEXITCODE"'`. - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; the success-case line beginning `Checked ` and ending `ms.` transcribed verbatim. -- [ ] [P6-T3] Run the analyzer rebuild with `CMD-REBUILD` (analyzer GATEARGS, `TASKID` p6-t3) and record FEATURE/evidence/qa-gates/msbuild-analyzer-final.md. +- [x] [P6-T3] Run the analyzer rebuild with `CMD-REBUILD` (analyzer GATEARGS, `TASKID` p6-t3) and record FEATURE/evidence/qa-gates/msbuild-analyzer-final.md. - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; `WRITESET_DIAGNOSTIC_LINES:` not greater than `ANALYZER-BASELINE-WRITESET-LINES:` and every code in `WRITESET_DIAGNOSTIC_CODES:` present in `ANALYZER-BASELINE-WRITESET-CODES:` (no new analyzer diagnostic in a Write Set file); `WARNINGS:` recorded beside `ANALYZER-BASELINE-WARNINGS:`. -- [ ] [P6-T4] Run the type-check rebuild with `CMD-REBUILD` (GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable override, `TASKID` p6-t4) and record FEATURE/evidence/qa-gates/msbuild-nullable-final.md. +- [x] [P6-T4] Run the type-check rebuild with `CMD-REBUILD` (GATEARGS `/p:TreatWarningsAsErrors=true`, no Nullable override, `TASKID` p6-t4) and record FEATURE/evidence/qa-gates/msbuild-nullable-final.md. - Acceptance: `ITERATION:` recorded; `WORKTREE-LEAF: agent-a7805823735145ca4`; `EXIT_CODE: 0`; `ERRORS: 0`; `SKIP_CORECOMPILE_LINES: 0`; both `CSC_OUT_` counts at least 1; both `_DLL_EXISTS:` values `True`. -- [ ] [P6-T5] Run the final repository-wide test-and-coverage run by the P0-T15 route (`CMD-COVERAGE-RUNNER` or `CMD-COVERAGE-DIRECT` with `STAGE` final, then `CMD-COVERAGE-POST` with `STAGE` final) and record FEATURE/evidence/qa-gates/coverage-post-change.md. +- [x] [P6-T5] Run the final repository-wide test-and-coverage run by the P0-T15 route (`CMD-COVERAGE-RUNNER` or `CMD-COVERAGE-DIRECT` with `STAGE` final, then `CMD-COVERAGE-POST` with `STAGE` final) and record FEATURE/evidence/qa-gates/coverage-post-change.md. - Artifact: the same fields as P0-T16, with `ITERATION:` and `FAILED-SET:` recorded as `FINAL-FAILED-SET:`. - Acceptance, all required: the route equals P0-T15's; `TRX_PRESENT: True`; `SEQUENCE_FILES:` 0 under DIRECT; `QFCDATAMODEL-CLASS-NODES: 0`; the nine `RESULT` lines all `Passed`; `NEW-FAILURES:` (names in `FINAL-FAILED-SET:` absent from `BASELINE-FAILED-SET:`) is `NONE`; `FIGURES-COMPARED:` restates the `Total`, `executed`, `error`, `timeout`, `aborted` and `notExecuted` figures from the P0-T16 summary block and from this run's summary block, and this run holds `Total` equal to the P0-T16 value (this plan adds and removes no test method), `executed` not less than the P0-T16 value, and each of `error`, `timeout`, `aborted` and `notExecuted` not greater than its P0-T16 value, because the failed set lists only results whose outcome is `Failed` and cannot show an aborted or unexecuted test; `LINE-FLOOR:` and `BRANCH-FLOOR:` each `MET`, or `NOT MET` only where P0-T16 recorded the same floor as not met; the `First-party coverage:` line is recorded as the numeric post-change headline. `RUNNER-GREEN: YES` is recorded when the route is RUNNER and `RUNNER_EXIT_CODE: 0`, otherwise `RUNNER-GREEN: NO` with the reason (`COVERAGE-ROUTE DIRECT` or `PRE-EXISTING FAILURES`). A failure of any target test or a new failure attributable to the Write Set invokes the D-13 failure restart; a new failure of either FocusAndThemeTests theme test is `THEME TEST NULL-DISPATCHER EXPOSURE OBSERVED` (section "Risks"); any other new failure, and any `FIGURES-COMPARED:` breach, is `NEW FAILURE OUTSIDE SCOPE`; both are a stop and report, without re-running. -- [ ] [P6-T6] Compare baseline and post-change coverage and test outcomes and record FEATURE/evidence/qa-gates/coverage-comparison.md from FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-post-change.md. +- [x] [P6-T6] Compare baseline and post-change coverage and test outcomes and record FEATURE/evidence/qa-gates/coverage-comparison.md from FEATURE/evidence/baseline/coverage-baseline.md and FEATURE/evidence/qa-gates/coverage-post-change.md. - Acceptance: the artifact records the baseline and post-change `First-party coverage:` lines (lines and branches, numeric), the two `ROOT` lines, and the repository-wide comparison in exactly one named branch: `BRANCH A` when the two `lines-valid` figures differ by at most 1 percent of the baseline figure (the post-change line rate must not be lower than baseline by more than 0.5 percentage points), otherwise `BRANCH B` (recorded and not gated, with one sentence stating the denominators are not comparable). New and changed code: `CHANGED-PRODUCTION-COVERAGE: NOT MEASURED` with the reason `[ExcludeFromCodeCoverage]` at QuickFiler/Controllers/QfcDatamodel.cs line 25 and `QFCDATAMODEL-CLASS-NODES: 0` at both stages; the changed test files are outside the instrumented set. The artifact also restates `BASELINE-FAILED-SET:`, `FINAL-FAILED-SET:` and `NEW-FAILURES: NONE`. A Branch A breach is `COVERAGE REGRESSION`: stop. -- [ ] [P6-T7] Record the final toolchain pass in FEATURE/evidence/qa-gates/toolchain-final-pass.md from the P6-T1 to P6-T5 artifacts of the final iteration. +- [x] [P6-T7] Record the final toolchain pass in FEATURE/evidence/qa-gates/toolchain-final-pass.md from the P6-T1 to P6-T5 artifacts of the final iteration. - Acceptance: one row per step, in order — csharpier format (`REWRITTEN-WRITESET: NONE`, `REWRITTEN-OTHER: NONE`), csharpier check (exit 0), analyzer rebuild (exit 0), TreatWarningsAsErrors rebuild (exit 0), coverage run (route, exit code, `RUNNER-GREEN:`) — each with its exact command, `EXIT_CODE:` and the same `ITERATION:` value; `SINGLE-PASS: YES` when all five rows come from one iteration with no restart after P6-T1; `AC17-STATUS: MET` only when `SINGLE-PASS: YES` and `RUNNER-GREEN: YES`, otherwise `AC17-STATUS: NOT MET` with the reason. -- [ ] [P6-T8] Record the AC4 wall-clock census in FEATURE/evidence/qa-gates/wall-clock-tokens.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "Task.Wait("`) and `CMD-TIMESPAN`. +- [x] [P6-T8] Record the AC4 wall-clock census in FEATURE/evidence/qa-gates/wall-clock-tokens.md with `CMD-TOKEN-COUNT` on each of THREE (TOKENS `"SpinWait", ".Wait(", "WaitForState", "Task.Wait("`) and `CMD-TIMESPAN`. - Acceptance: every count 0 in all three files; `TIMESPAN-UNCLASSIFIED: 0` with every `TIMESPAN` line `CLASSIFIED` (the surviving lines are the teardown file's two `QuiesceLoaderAsync(` production arguments and one `fake.Advance(` and the liveness file's three `fake.Advance(` lines); the artifact cites the non-zero P0-T12 counts as the positive control. -- [ ] [P6-T9] Record the AC5 prohibited-construct gate in FEATURE/evidence/qa-gates/prohibited-constructs.md with `CMD-ADDED-SCAN`, `git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, `git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, and `CMD-TOKEN-COUNT` on the R4 file (TOKENS `"[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`). +- [x] [P6-T9] Record the AC5 prohibited-construct gate in FEATURE/evidence/qa-gates/prohibited-constructs.md with `CMD-ADDED-SCAN`, `git -C WORKTREE diff --exit-code 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, `git -C WORKTREE status --porcelain -- scripts/vscode/TaskMaster.cli.runsettings TaskMaster.runsettings`, and `CMD-TOKEN-COUNT` on the R4 file (TOKENS `"[Timeout(GateTimeoutMs)]", "private const int GateTimeoutMs = 60000;"`). - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `GIT_DIFF_EXIT_CODE: 0`; `ADDED_LINES:` greater than 0; `ADDED-TOKEN` counts 0 for `Thread.Sleep`, `Task.Delay`, `DoNotParallelize`, `Retry(` and `Timeout(`, and at least 1 for `WorkerStarter` (positive control); the runsettings diff exits 0 and the porcelain span prints nothing; the R4 file reads 8 and 1, equal to P0-T12. -- [ ] [P6-T10] Record the footprint gate in FEATURE/evidence/qa-gates/footprint-scope.md with `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD` paired with `git -C WORKTREE status --porcelain --untracked-files=all`. +- [x] [P6-T10] Record the footprint gate in FEATURE/evidence/qa-gates/footprint-scope.md with `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD` paired with `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: `THIS-ITEM-FOOTPRINT:` (name-status paths outside FEATURE, excluding any path P0-T3 listed in `INHERITED-COMMITTED:`) is exactly the five CODE5 paths with status `M`; the excluded inherited paths are recorded as `INHERITED-AND-EXCLUDED:` (at most `docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md`, status `A`); no path ends in `.csproj`; no status `A` outside FEATURE other than an `INHERITED-AND-EXCLUDED:` path; no porcelain line names a path under QuickFiler/, QuickFiler.Test/ or scripts/. -- [ ] [P6-T11] Record the evidence hygiene gate in FEATURE/evidence/qa-gates/evidence-hygiene.md by scanning every file under FEATURE/evidence/. +- [x] [P6-T11] Record the evidence hygiene gate in FEATURE/evidence/qa-gates/evidence-hygiene.md by scanning every file under FEATURE/evidence/. - Command: `pwsh -NoProfile -Command 'PREFIX; $b = [char]92; $files = @(Get-ChildItem -LiteralPath "docs\features\active\2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950\evidence" -Recurse -File); "EVIDENCE_FILES=$($files.Count)"; "RAW_DOCUMENTS=$(@($files | Where-Object { $_.Extension -in @(".trx", ".xml", ".coverage", ".coveragexml", ".log") }).Count)"; $pattern = "[a-z]:[" + $b + $b + "/]+users[" + $b + $b + "/]+[a-z0-9_.~-]"; "PROFILE_PATH_LINES=$(@($files | Select-String -Pattern $pattern).Count)"'`. - Acceptance: `WORKTREE-LEAF: agent-a7805823735145ca4`; `EVIDENCE_FILES=` at least 1; `RAW_DOCUMENTS=0`; `PROFILE_PATH_LINES=0` (the pattern is the repository hygiene rule's, built from `[char]92` so the Bash channel cannot collapse its backslashes). A non-zero value names the offending files; the executor redacts them and re-runs this task. -- [ ] [P6-T12] Check off AC1 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows the property line 1, the constructor default 2 and both documentation tokens 1. +- [x] [P6-T12] Check off AC1 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows the property line 1, the constructor default 2 and both documentation tokens 1. - Acceptance: only `- [ ] AC1:` changes to `- [x] AC1:`; otherwise the box stays unchecked and `AC1: NOT MET` with the reason is recorded for P6-T29. -- [ ] [P6-T13] Check off AC2 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `INITQ` `RunWorkerAsync` 0 and `WorkerStarter(worker);` 2. +- [x] [P6-T13] Check off AC2 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `INITQ` `RunWorkerAsync` 0 and `WorkerStarter(worker);` 2. - Acceptance: only `- [ ] AC2:` changes; otherwise unchecked with `AC2: NOT MET`. -- [ ] [P6-T14] Check off AC3 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows QfcDatamodel.cs LINES at most 500. +- [x] [P6-T14] Check off AC3 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows QfcDatamodel.cs LINES at most 500. - Acceptance: only `- [ ] AC3:` changes; otherwise unchecked with `AC3: NOT MET`. -- [ ] [P6-T15] Check off AC4 in FEATURE/spec.md when FEATURE/evidence/qa-gates/wall-clock-tokens.md holds every P6-T8 condition. +- [x] [P6-T15] Check off AC4 in FEATURE/spec.md when FEATURE/evidence/qa-gates/wall-clock-tokens.md holds every P6-T8 condition. - Acceptance: only `- [ ] AC4:` changes; otherwise unchecked with `AC4: NOT MET`. -- [ ] [P6-T16] Check off AC5 in FEATURE/spec.md when FEATURE/evidence/qa-gates/prohibited-constructs.md holds every P6-T9 condition. +- [x] [P6-T16] Check off AC5 in FEATURE/spec.md when FEATURE/evidence/qa-gates/prohibited-constructs.md holds every P6-T9 condition. - Acceptance: only `- [ ] AC5:` changes; otherwise unchecked with `AC5: NOT MET`. -- [ ] [P6-T17] Check off AC6 in FEATURE/spec.md when `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the liveness `WorkerStarter = StartSynchronously;` count 2. +- [x] [P6-T17] Check off AC6 in FEATURE/spec.md when `DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the liveness `WorkerStarter = StartSynchronously;` count 2. - Acceptance: only `- [ ] AC6:` changes; otherwise unchecked with `AC6: NOT MET`. -- [ ] [P6-T18] Check off AC7 in FEATURE/spec.md when `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md. +- [x] [P6-T18] Check off AC7 in FEATURE/spec.md when `RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md. - Acceptance: only `- [ ] AC7:` changes; otherwise unchecked with `AC7: NOT MET`. -- [ ] [P6-T19] Check off AC8 in FEATURE/spec.md when `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `pump.Drain();` 2 in the liveness file. +- [x] [P6-T19] Check off AC8 in FEATURE/spec.md when `RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `pump.Drain();` 2 in the liveness file. - Acceptance: only `- [ ] AC8:` changes; otherwise unchecked with `AC8: NOT MET`. -- [ ] [P6-T20] Check off AC9 in FEATURE/spec.md when `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md. +- [x] [P6-T20] Check off AC9 in FEATURE/spec.md when `RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md. - Acceptance: only `- [ ] AC9:` changes; otherwise unchecked with `AC9: NOT MET`. -- [ ] [P6-T21] Check off AC10 in FEATURE/spec.md when `Worker_DoWork_CapturesRemainingLoadTask` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the teardown `WaitForState` 0 and `.Wait(` 0. +- [x] [P6-T21] Check off AC10 in FEATURE/spec.md when `Worker_DoWork_CapturesRemainingLoadTask` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the teardown `WaitForState` 0 and `.Wait(` 0. - Acceptance: only `- [ ] AC10:` changes; otherwise unchecked with `AC10: NOT MET`. -- [ ] [P6-T22] Check off AC11 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `.Wait(` 0. +- [x] [P6-T22] Check off AC11 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `.Wait(` 0. - Acceptance: only `- [ ] AC11:` changes; otherwise unchecked with `AC11: NOT MET`. -- [ ] [P6-T23] Check off AC12 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` are both `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `WorkerStarter = StartSynchronously;` 3. +- [x] [P6-T23] Check off AC12 in FEATURE/spec.md when `InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` are both `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows the zero-batch `WorkerStarter = StartSynchronously;` 3. - Acceptance: only `- [ ] AC12:` changes; otherwise unchecked with `AC12: NOT MET`. -- [ ] [P6-T24] Check off AC13 in FEATURE/spec.md when `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md, FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `R4SPAN` 1, 1, 2, 1, 1, `R4PRE` 0, 0, `R4HEAD` 1, 1 and `R4TAIL` 3 (the pin opens after transaction A's acquisition and before the `original` read, closes after both assertions, and both assertions remain). +- [x] [P6-T24] Check off AC13 in FEATURE/spec.md when `Transaction_SecondCallerCannotInstallUntilTheFirstRestores` is `Passed` in FEATURE/evidence/regression-testing/targets-pass-after.md, FEATURE/evidence/regression-testing/concurrent-classes-pass-after.md and FEATURE/evidence/qa-gates/coverage-post-change.md, and post-format-census.md shows `R4SPAN` 1, 1, 2, 1, 1, `R4PRE` 0, 0, `R4HEAD` 1, 1 and `R4TAIL` 3 (the pin opens after transaction A's acquisition and before the `original` read, closes after both assertions, and both assertions remain). - Acceptance: only `- [ ] AC13:` changes; otherwise unchecked with `AC13: NOT MET`. -- [ ] [P6-T25] Check off AC14 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1 and the three R-DOC tokens 1 each. +- [x] [P6-T25] Check off AC14 in FEATURE/spec.md when FEATURE/evidence/qa-gates/post-format-census.md shows `flake-watch` 0, `Append an observation` 0, `Issue #950:` 1 and the three R-DOC tokens 1 each. - Acceptance: only `- [ ] AC14:` changes; otherwise unchecked with `AC14: NOT MET`. -- [ ] [P6-T26] Check off AC15 in FEATURE/spec.md when FEATURE/evidence/other/negative-controls-summary.md holds nine rows in its AC15 table and two rows in its Drain-dependency table whose outcomes match P5-T25's acceptance. +- [x] [P6-T26] Check off AC15 in FEATURE/spec.md when FEATURE/evidence/other/negative-controls-summary.md holds nine rows in its AC15 table and two rows in its Drain-dependency table whose outcomes match P5-T25's acceptance. - Acceptance: only `- [ ] AC15:` changes; otherwise unchecked with `AC15: NOT MET`. -- [ ] [P6-T27] Check off AC16 in FEATURE/spec.md when FEATURE/evidence/other/ambient-synchronization-context.md carries exactly one `AMBIENT-SYNCHRONIZATION-CONTEXT:` line. +- [x] [P6-T27] Check off AC16 in FEATURE/spec.md when FEATURE/evidence/other/ambient-synchronization-context.md carries exactly one `AMBIENT-SYNCHRONIZATION-CONTEXT:` line. - Acceptance: only `- [ ] AC16:` changes; otherwise unchecked with `AC16: NOT MET`. -- [ ] [P6-T28] Check off AC17 in FEATURE/spec.md when FEATURE/evidence/qa-gates/toolchain-final-pass.md reads `AC17-STATUS: MET`. +- [x] [P6-T28] Check off AC17 in FEATURE/spec.md when FEATURE/evidence/qa-gates/toolchain-final-pass.md reads `AC17-STATUS: MET`. - Acceptance: only `- [ ] AC17:` changes; otherwise the box stays unchecked and the recorded reason (`COVERAGE-ROUTE DIRECT`, `PRE-EXISTING FAILURES` or a restart after P6-T1) is carried to P6-T29 as `AC17: NOT MET`. -- [ ] [P6-T29] Write the acceptance-criteria status summary FEATURE/evidence/other/ac-status-summary.md. +- [x] [P6-T29] Write the acceptance-criteria status summary FEATURE/evidence/other/ac-status-summary.md. - Acceptance: the artifact carries `Timestamp:` and the acceptance-criteria-tracking status block (Source: the spec path; Total AC items: 17; Checked off; Remaining; Items remaining with each `ACn: NOT MET` reason), with the counts read from FEATURE/spec.md after P6-T28 (lines beginning `- [x] AC` and `- [ ] AC`, summing to 17). -- [ ] [P6-T30] Re-run the P6-T11 hygiene command over FEATURE/evidence/ after P6-T29 and append the result to FEATURE/evidence/qa-gates/evidence-hygiene.md as a `## Re-run after check-offs` section. +- [x] [P6-T30] Re-run the P6-T11 hygiene command over FEATURE/evidence/ after P6-T29 and append the result to FEATURE/evidence/qa-gates/evidence-hygiene.md as a `## Re-run after check-offs` section. - Acceptance: `RAW_DOCUMENTS=0` and `PROFILE_PATH_LINES=0` for the evidence tree as it will be committed. -- [ ] [P6-T31] Verify that FEATURE/spec.md changed only in its checkbox lines by recording `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` and `git -C WORKTREE diff HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, paired with `git -C WORKTREE status --porcelain -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, in a `## Spec check-off diff` section appended to FEATURE/evidence/other/ac-status-summary.md. +- [x] [P6-T31] Verify that FEATURE/spec.md changed only in its checkbox lines by recording `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` and `git -C WORKTREE diff HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, paired with `git -C WORKTREE status --porcelain -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, in a `## Spec check-off diff` section appended to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: added and deleted line counts are equal and equal the checked-off count; every deleted line begins `- [ ] AC` and every added line begins `- [x] AC` with identical remaining text. - [ ] [P6-T32] Commit the remaining feature evidence and check-offs (FEATURE only) and record FEATURE/evidence/qa-gates/final-commit.md. - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record final QA evidence and acceptance check-offs"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md index bebcb312f..6a2bac8f8 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md @@ -263,22 +263,22 @@ The #230 lost-update control for R4 (a fixture that releases the gate before res ## Acceptance Criteria -- [ ] AC1: `QfcDatamodel` declares an internal `WorkerStarter` property whose type is an Action delegate over `BackgroundWorker`; both constructors assign it a default that calls `RunWorkerAsync` on the supplied worker, and it carries an XML doc comment stating the seam purpose and the null-on-uninitialized behavior. -- [ ] AC2: `InitEmailQueue` contains no direct `RunWorkerAsync` call; both of its worker-start sites (the zero-batch path and the positive-batch path) call `WorkerStarter` instead. -- [ ] AC3: `QuickFiler/Controllers/QfcDatamodel.cs` stays at or below five hundred total lines after the change, measured as total lines rather than non-blank lines. -- [ ] AC4: No `SpinWait.SpinUntil` call, no `Task.Wait` call, and no `WaitForState` helper remains in any of the three datamodel test files (`QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`, `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs`, `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs`); any surviving `TimeSpan` value in those files is a production argument or a `FakeTimeProvider` advance, not a blocking wait. -- [ ] AC5: No `Thread.Sleep`, `Task.Delay`, `[DoNotParallelize]`, retry construct, or `[Timeout]` value change is introduced by this branch, and neither runsettings file changes. -- [ ] AC6: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` passes under the repository runsettings, driving the worker through a synchronous `WorkerStarter`. -- [ ] AC7: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` passes under the repository runsettings, asserting the liveness flag synchronously after `InitEmailQueue` returns. -- [ ] AC8: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` passes under the repository runsettings, observing the cleared flag after releasing the loader and draining a test-owned synchronization context. -- [ ] AC9: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` passes under the repository runsettings, observing the cleared flag after releasing the throwing loader and draining a test-owned synchronization context. -- [ ] AC10: `QuickFiler.Controllers.Tests.QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask` passes under the repository runsettings, reading the loader-entered signal and the captured `_remainingLoadTask` synchronously. -- [ ] AC11: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` passes under the repository runsettings, reading the loader-invoked signal synchronously. -- [ ] AC12: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` both assign a synchronous `WorkerStarter` and pass under the repository runsettings. -- [ ] AC13: `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` opens a `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` after transaction A acquires the gate and before the `original` read, keeps both its `BeSameAs(original)` and `NotBeSameAs(liveA)` assertions, and passes under the repository runsettings. -- [ ] AC14: The R4 doc comment no longer carries the flake-watch instruction and instead names the gate-free `EnsureDispatcher` writer as the cause, cites this issue, and states the W2/W5 residual-writer invariant. -- [ ] AC15: Each test named in AC6 through AC13 has a recorded negative control, using the mechanism the Test Strategy table assigns to it, that fails immediately with an assertion or exception rather than hanging; each outcome is recorded as a Markdown summary in the feature folder's evidence/other directory. -- [ ] AC16: The observed ambient `SynchronizationContext.Current` value on the MSTest worker thread is recorded once in the feature folder's evidence/other directory. +- [x] AC1: `QfcDatamodel` declares an internal `WorkerStarter` property whose type is an Action delegate over `BackgroundWorker`; both constructors assign it a default that calls `RunWorkerAsync` on the supplied worker, and it carries an XML doc comment stating the seam purpose and the null-on-uninitialized behavior. +- [x] AC2: `InitEmailQueue` contains no direct `RunWorkerAsync` call; both of its worker-start sites (the zero-batch path and the positive-batch path) call `WorkerStarter` instead. +- [x] AC3: `QuickFiler/Controllers/QfcDatamodel.cs` stays at or below five hundred total lines after the change, measured as total lines rather than non-blank lines. +- [x] AC4: No `SpinWait.SpinUntil` call, no `Task.Wait` call, and no `WaitForState` helper remains in any of the three datamodel test files (`QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs`, `QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs`, `QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs`); any surviving `TimeSpan` value in those files is a production argument or a `FakeTimeProvider` advance, not a blocking wait. +- [x] AC5: No `Thread.Sleep`, `Task.Delay`, `[DoNotParallelize]`, retry construct, or `[Timeout]` value change is introduced by this branch, and neither runsettings file changes. +- [x] AC6: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle` passes under the repository runsettings, driving the worker through a synchronous `WorkerStarter`. +- [x] AC7: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces` passes under the repository runsettings, asserting the liveness flag synchronously after `InitEmailQueue` returns. +- [x] AC8: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_AfterLoaderCompletes_BecomesFalse` passes under the repository runsettings, observing the cleared flag after releasing the loader and draining a test-owned synchronization context. +- [x] AC9: `QuickFiler.Controllers.Tests.QfcDatamodelLivenessTests.RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally` passes under the repository runsettings, observing the cleared flag after releasing the throwing loader and draining a test-owned synchronization context. +- [x] AC10: `QuickFiler.Controllers.Tests.QfcDatamodelTeardownTests.Worker_DoWork_CapturesRemainingLoadTask` passes under the repository runsettings, reading the loader-entered signal and the captured `_remainingLoadTask` synchronously. +- [x] AC11: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker` passes under the repository runsettings, reading the loader-invoked signal synchronously. +- [x] AC12: `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing` and `QuickFiler.Controllers.Tests.QfcInitEmailQueueZeroBatchTests.InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop` both assign a synchronous `WorkerStarter` and pass under the repository runsettings. +- [x] AC13: `QuickFiler.Controllers.Tests.QfcItemController_UiThreadDispatcherFixtureTests.Transaction_SecondCallerCannotInstallUntilTheFirstRestores` opens a `using` over `QfcItemControllerTestSupport.EnsureUiThreadDispatcher()` after transaction A acquires the gate and before the `original` read, keeps both its `BeSameAs(original)` and `NotBeSameAs(liveA)` assertions, and passes under the repository runsettings. +- [x] AC14: The R4 doc comment no longer carries the flake-watch instruction and instead names the gate-free `EnsureDispatcher` writer as the cause, cites this issue, and states the W2/W5 residual-writer invariant. +- [x] AC15: Each test named in AC6 through AC13 has a recorded negative control, using the mechanism the Test Strategy table assigns to it, that fails immediately with an assertion or exception rather than hanging; each outcome is recorded as a Markdown summary in the feature folder's evidence/other directory. +- [x] AC16: The observed ambient `SynchronizationContext.Current` value on the MSTest worker thread is recorded once in the feature folder's evidence/other directory. - [ ] AC17: The full C# toolchain passes in a single pass in order: csharpier check, the analyzers rebuild, the TreatWarningsAsErrors rebuild, and Invoke-MSTestWithCoverage with the repository runsettings unchanged. ## Risks & Mitigations From 11d6d7b81df97ca7ed91f398927de4b8a7b47da5 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:27:38 -0400 Subject: [PATCH 20/24] docs(950): record the final commit evidence and plan completion Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com --- .../evidence/qa-gates/final-commit.md | 15 +++++++++++++++ .../plan.2026-10-01T07-11.md | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/final-commit.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/final-commit.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/final-commit.md new file mode 100644 index 000000000..3d035c3d1 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/final-commit.md @@ -0,0 +1,15 @@ +# Final commit (P6-T32) + +Timestamp: 2026-10-02T01-28 +Command: git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950; git -C WORKTREE commit -m "docs(950): record final QA evidence and acceptance check-offs" -m "Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com"; git -C WORKTREE rev-parse HEAD; git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD -- . ":(exclude)docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950"; git -C WORKTREE status --porcelain --untracked-files=all (separate calls); then git -C WORKTREE push origin bug/quickfiler-tests-depend-on-wall-clock-timing-950 +EXIT_CODE: 0 + +Output Summary: +git add: exit 0 (CRLF normalisation warnings only) +git commit: exit 0; 14 files changed, 422 insertions(+), 47 deletions(-) +FINAL-COMMIT: ec937a928f342a1cc55d8f9d45e861d5ab089f7b +Name-status BASE..HEAD outside FEATURE: the five CODE5 paths (M) and the inherited promotion record (A), exactly the P6-T10 footprint plus its INHERITED-AND-EXCLUDED path. +Porcelain after the commit: empty (no path under FEATURE, QuickFiler/, QuickFiler.Test/ or scripts/) +Push: 2f7602f69..ec937a928 accepted by origin + +This artifact and the P6-T32 check-off mark are written after that commit. They are committed in one follow-up FEATURE-only commit so the worktree ends clean (the delegation requires every phase's evidence to be committed and pushed). diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md index 07c3b680f..63a2ae92a 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/plan.2026-10-01T07-11.md @@ -1011,7 +1011,7 @@ Span anchors used by this plan: `INITQ` is START `public IList InitEma - Acceptance: `RAW_DOCUMENTS=0` and `PROFILE_PATH_LINES=0` for the evidence tree as it will be committed. - [x] [P6-T31] Verify that FEATURE/spec.md changed only in its checkbox lines by recording `git -C WORKTREE diff --numstat HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md` and `git -C WORKTREE diff HEAD -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, paired with `git -C WORKTREE status --porcelain -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md`, in a `## Spec check-off diff` section appended to FEATURE/evidence/other/ac-status-summary.md. - Acceptance: added and deleted line counts are equal and equal the checked-off count; every deleted line begins `- [ ] AC` and every added line begins `- [x] AC` with identical remaining text. -- [ ] [P6-T32] Commit the remaining feature evidence and check-offs (FEATURE only) and record FEATURE/evidence/qa-gates/final-commit.md. +- [x] [P6-T32] Commit the remaining feature evidence and check-offs (FEATURE only) and record FEATURE/evidence/qa-gates/final-commit.md. - Commands, separate Bash calls: `git -C WORKTREE add -- docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950`; `git -C WORKTREE commit -m "docs(950): record final QA evidence and acceptance check-offs"`; `git -C WORKTREE rev-parse HEAD`; `git -C WORKTREE diff --name-status 34c2ed88cbb009f2f231453db87bc64d45a9bd51 HEAD`; `git -C WORKTREE status --porcelain --untracked-files=all`. - Acceptance: both git writes exit 0; `FINAL-COMMIT:` records HEAD as an observation; the name-status paths outside FEATURE are exactly the P6-T10 footprint; the porcelain output names no path under FEATURE other than this plan file (whose final check-off mark follows the commit) and FEATURE/evidence/qa-gates/final-commit.md (written after the commit), and no path under QuickFiler/, QuickFiler.Test/ or scripts/. The final-commit artifact and this plan's check-off marks are committed by the orchestrator with the plan file. From 523c93ee990aefa3e59877373e73fe3c4b1e4ac4 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 01:38:47 -0400 Subject: [PATCH 21/24] docs(950): record the coordinator ruling on the AC17 evidence source Co-Authored-By: Claude Opus 5.5 --- .../spec.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md index 6a2bac8f8..714f48d4c 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md @@ -280,6 +280,7 @@ The #230 lost-update control for R4 (a fixture that releases the gate before res - [x] AC15: Each test named in AC6 through AC13 has a recorded negative control, using the mechanism the Test Strategy table assigns to it, that fails immediately with an assertion or exception rather than hanging; each outcome is recorded as a Markdown summary in the feature folder's evidence/other directory. - [x] AC16: The observed ambient `SynchronizationContext.Current` value on the MSTest worker thread is recorded once in the feature folder's evidence/other directory. - [ ] AC17: The full C# toolchain passes in a single pass in order: csharpier check, the analyzers rebuild, the TreatWarningsAsErrors rebuild, and Invoke-MSTestWithCoverage with the repository runsettings unchanged. + - Note (2026-10-02, coordinator ruling, AC17 option (a) approved): the intent of AC17 (the full suite through the standard route) is unchanged; only the evidence source for the test stage changes. The local test stage cannot complete on this workstation because `ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension` fails deterministically with an invalid Win32 handle, a known environmental failure that reproduces on main; CI runs the shell-icon classes. The local fallback run passed 7361 of 7361 with coverage not below baseline (lines 85.35 to 85.36, branches 79.74 to 79.75). AC17 is checked off only from this pull request's own CI run on the final head, recording the run ID, the head SHA, the mstest-coverage job pass and fail counts and coverage figures, and the local fallback result. If CI fails any test, AC17 is not met. ## Risks & Mitigations From c1dc0f33c34bc194efccb338cc54c557eca41a27 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 02:00:44 -0400 Subject: [PATCH 22/24] docs(950): add the feature review audit artifacts Co-Authored-By: Claude Opus 5.5 --- .../code-review.2026-10-02T14-30.md | 59 ++++ .../feature-audit.2026-10-02T14-30.md | 111 ++++++++ .../policy-audit.2026-10-02T14-30.md | 261 ++++++++++++++++++ 3 files changed, 431 insertions(+) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/code-review.2026-10-02T14-30.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/feature-audit.2026-10-02T14-30.md create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/policy-audit.2026-10-02T14-30.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/code-review.2026-10-02T14-30.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/code-review.2026-10-02T14-30.md new file mode 100644 index 000000000..4607e8e97 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/code-review.2026-10-02T14-30.md @@ -0,0 +1,59 @@ +# Code Review: quickfiler-tests-depend-on-wall-clock-timing (Issue #950) + +- Timestamp: 2026-10-02T14-30 +- Branch: bug/quickfiler-tests-depend-on-wall-clock-timing-950, head 523c93ee9, base 34c2ed88c +- Files reviewed in full: QuickFiler/Controllers/QfcDatamodel.cs; QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs; QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs; QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs; QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs. Also read for context: QuickFiler/Controllers/QfcDatamodel.QueueProcessing.cs (header), QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixture.cs (EnsureDispatcher and EnsureScope), QuickFiler.Test/Controllers/QfcItemController.FocusAndThemeTests.cs (the two theme tests). +- Companion artifacts: policy-audit.2026-10-02T14-30.md, feature-audit.2026-10-02T14-30.md + +## Executive Summary + +Verdict: PASS. 0 Blocking findings, 6 Non-blocking findings. + +The production change is the smallest seam that removes the uncontrolled thread: an `internal Action WorkerStarter { get; set; }` on `QfcDatamodel`, defaulted in both instance constructors to `worker => worker.RunWorkerAsync()` and called at the two start sites of `InitEmailQueue` (lines 285 and 312). Production behavior is unchanged; the type stays at 495 of 500 lines. The test rewrite replaces every five-second bounded wait with a synchronous start (a test-side `BackgroundWorker` subclass exposing the protected `OnDoWork`) and, where a continuation must be observed, with a test-owned `DrainableSynchronizationContext` whose `Drain()` runs only already-queued work and never blocks. R4 pins a non-null baseline inside the gate with a `using` over the ensure scope and keeps both assertions. Each design choice is traceable to the spec and each is confirmed by a recorded negative control that fails immediately rather than hanging. + +Coordinator prohibitions checked directly in the source: no retry construct, no `[DoNotParallelize]`, no `Workers=1` or other serialization, no lengthened `[Timeout]` (eight `[Timeout(GateTimeoutMs)]` attributes and the 60000 ms constant are unchanged), no `Thread.Sleep` or `Task.Delay`, no temporary file. + +## Findings Table + +| Severity | File | Location | Finding | Recommendation | Rationale | Evidence | +|---|---|---|---|---|---|---| +| Non-blocking (CR-1) | QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs; QfcDatamodelTeardownTests.cs; QfcInitEmailQueueZeroBatchTests.cs | Liveness 53-60 and 68-87; Teardown 66-73; Zero-batch 120-127 | `SynchronousBackgroundWorker` and `StartSynchronously` are duplicated verbatim in three files, and `DrainableSynchronizationContext` repeats the shape of the `ViewerScope` context in ItemViewerBreadcrumbThreadAffinityTests | Promote the worker subclass, the starter and the drainable context to QuickFiler.Test/TestSupport/ with an explicit `` in QuickFiler.Test.csproj on the next change that already touches the project file | General Code Change Policy "Reusability: avoid copy-paste". The spec sanctions the per-file duplication (documented convention, footprint kept to the five files), so this is accepted for the fix, not a defect of it | Spec "Helper placement decision"; Read of the three files | +| Non-blocking (CR-2) | QuickFiler/Controllers/QfcDatamodel.QueueProcessing.cs | Line 17 | The `_remainingLoadActive` doc comment still says the flag is set "immediately before each `RunWorkerAsync()` call"; the start site now goes through `WorkerStarter` | Reword to "immediately before each `WorkerStarter` invocation (`RunWorkerAsync` in production)" | C#6.3 "Keep comments synchronized with behavior". The file is outside this issue's Write Set, so a follow-up edit is the correct vehicle | Grep for `RunWorkerAsync` under QuickFiler/ | +| Non-blocking (CR-3) | QuickFiler/Controllers/QfcDatamodel.cs | Whole file (495 lines); legacy region lines 246-265, 378-416, 418-465 | The file is five lines under the 500-line limit. It carries three apparently dead members: `Worker_RunWorkerCompleted` (subscription commented out at line 194), the synchronous `LoadRemainingEmailsToQueue(BackgroundWorker, CancellationToken)` and the two-argument `LoadRemainingEmailsToQueueAsync(BackgroundWorker, CancellationToken)`, neither referenced by the seam default or the worker body | Before the next edit to this file, either delete the dead members (after a reference census) or move the legacy loader variants to a partial, so the limit is not breached by the next change | General Code Change Policy 4.1 (500-line limit) and C#5.1 | Read of the file; Grep shows `RemainingEmailLoader = LoadRemainingEmailsToQueueAsync` binds the single-argument overload only | +| Non-blocking (CR-4) | QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs; QfcInitEmailQueueZeroBatchTests.cs | Liveness 127, 201; Zero-batch 148, 173, 221 | `SynchronousBackgroundWorker` instances (a `Component`) are not disposed; the teardown file wraps its instance in `using` (line 220) | Align the liveness and zero-batch files with the teardown file's `using` | C#2.4 "Prefer using for disposable resources". Low impact: `BackgroundWorker` holds no unmanaged resource and the pattern predates this change in these files | Read of the three files | +| Non-blocking (CR-5) | QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs | R4, lines 218-251 | `transactionA` is disposed explicitly at line 251 with no `try`/`finally`; if `Install(liveA)`, `EnsureUiThreadDispatcher()` or `secondCallerStarted.Wait()` threw, the gate permit would be held until the test ended and the #743 counter test or the 120 s acquisition bound would surface it. R1, R5 and R6 use `try`/`finally`. Pre-existing shape; the pin did not introduce it | Wrap the transaction in `try`/`finally` as the sibling tests do, keeping the explicit `Dispose()` at the Act step and letting the `finally` re-dispose idempotently (R5 proves double dispose is safe) | General Unit Test Policy "Independence": a leaked permit would block every later transaction-taking class | Read of lines 44-98 (R1) against 212-276 (R4) | +| Non-blocking (CR-6) | docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/preflight-clearance-r2.2026-10-02T12-10.md | Label and `Timestamp:` line | The label 2026-10-02T12-10 leads the commit that recorded it (03c0d01eb, reflog epoch 1790916297 = 2026-10-02T04:44:57Z, 00:44 at the recorded -0400 offset) by about 7.4 hours. The executor's Phase 0 to Phase 6 labels agree with the reflog to the minute, so the discrepancy is confined to the orchestrator-authored artifact | Derive evidence `Timestamp:` labels from the clock at write time; do not carry forward a planned or estimated time | Evidence labels are used by reviewers as a clock; a synthetic label hides ordering | Worktree reflog lines 15 and 23; label comparison | + +## Observations (not findings) + +- O-1, THEME TEST NULL-DISPATCHER EXPOSURE: not observed (both theme tests passed in the concurrent run, evidence/regression-testing/concurrent-classes-pass-after.md). Structural analysis: the theme tests call `EnsureUiThreadDispatcher()` and discard the scope (FocusAndThemeTests.cs lines 452 and 468), then read the process-wide dispatcher. They are exposed whenever any writer nulls the static between that call and that read. Before this change, R4 with a null baseline restored null once at `transactionA.Dispose()`. After this change, R4 with a null baseline restores the parked singleton at `transactionA.Dispose()` and nulls the field once at the end of the `using (baseline)` block through `EnsureScope.Dispose()` (fixture lines 269-272, `CompareExchange(parked, null)`). The number of null writes R4 performs is therefore unchanged (one per run with a null baseline, zero with a non-null baseline); the write moved later in the test. R2, R3 and R6 (`Install(null)` then restore) and any pre-fix transaction with a null baseline already carried the same exposure. The residual hazard belongs to the discarded-scope pattern in the theme tests, which the spec places out of scope (Non-goals, first bullet). Recorded as follow-up F-5 rather than as a finding of this change. +- O-2: The spec's W2 invariant ("no other class may dispose an ensure scope holding the parked dispatcher") is written from R4's point of view; R4's own baseline disposal is the only such disposal today and runs after both assertions, so it cannot affect R4's observation. The doc comment at lines 205-207 states the invariant for future editors as AC14 requires. +- O-3: The fail-fast on an unassigned seam is a `NullReferenceException` from the delegate invocation, not an explicit `InvalidOperationException`. The spec chooses this deliberately to match the `RemainingEmailLoader` convention, and the path is reachable only on `GetUninitializedObject` test instances (every production path runs a constructor). Accepted as specified; an explicit guard would add production lines to a file at 495/500 for a test-only path. +- O-4: `Action` resolves unambiguously in a file that imports both `System` and `Microsoft.Office.Interop.Outlook`, because `Outlook.Action` is non-generic. Both rebuild gates compiled with 0 errors. The test files use `System.Action` where the bare name would be ambiguous (Teardown line 190, Zero-batch line 147) and carry the explanatory comment. +- O-5: The `DrainableSynchronizationContext` does not override `Send`; the base implementation runs the callback inline, which matches the spec's "Send throws or runs inline" allowance. `Drain()` asserts the creator thread so an accidental cross-thread drain fails loudly. +- O-6: In `StartHeldOpenLoader` the release source is created with `TaskCreationOptions.RunContinuationsAsynchronously` so that `SetResult` never runs the loader continuation inline on the releasing thread; with the pump installed, the continuation is posted to the pump and observed only through `Drain()`. Controls C1 and C2 (Drain removed, release set, test fails) confirm this ordering empirically. +- O-7: The two `Task.Yield` scheduling loops in liveness test 1 (lines 140-142 and 154-158) are unchanged and remain scheduling-dependent but bounded; the spec records them as out of scope with a stated follow-up condition. +- O-8: The promoted record docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md states "Status: Promoted -> docs/features/active/quickfiler-tests-depend-on-wall-clock-timing/", a folder name without the date prefix and issue suffix of the real folder. This is promotion-tooling output inherited from the base; not a defect of this branch. + +## Prohibition checklist (coordinator) + +| Prohibition | Observed | Method | +|---|---|---| +| Test retries | None | Grep `Retry` over the four changed test files: 0; ADDED-TOKEN Retry( 0 over 251 added lines | +| [DoNotParallelize] | None | Grep: 0; ADDED-TOKEN 0 | +| Workers=1 or other serialization | None | Runsettings unchanged (anchored diff exit 0, prohibited-constructs.md); no runsettings path in the name-status | +| Lengthened timeouts | None | Eight `[Timeout(GateTimeoutMs)]`, `GateTimeoutMs = 60000` unchanged; ADDED-TOKEN Timeout( 0 | +| Thread.Sleep / Task.Delay / wall-clock waits | None in changed tests | Grep: 0 for Sleep/Delay/SpinWait/WaitForState; the only `.Wait(` is the pre-existing unbounded `ManualResetEventSlim.Wait()` in R4 (signal wait, no time bound) | +| Temporary files | None | Read of all four test files: no Path.GetTemp*, File.*, or StreamWriter | + +## Follow-ups (non-blocking residuals) + +- F-1 (CR-1): consolidate `SynchronousBackgroundWorker`, `StartSynchronously` and `DrainableSynchronizationContext` into QuickFiler.Test/TestSupport/ when the project file is next edited. +- F-2 (CR-2): reword the `_remainingLoadActive` doc comment in QfcDatamodel.QueueProcessing.cs to name `WorkerStarter`. +- F-3 (CR-3): remove or relocate the dead legacy loader variants and `Worker_RunWorkerCompleted` in QfcDatamodel.cs before the next change to that file (495/500 lines). +- F-4 (CR-5): add `try`/`finally` around `transactionA` in R4 to match R1/R5/R6. +- F-5 (O-1): hold the ensure scope in the two FocusAndThemeTests theme tests (or take a transaction) so a null-restoring writer in another class cannot expose them; spec out of scope for this issue. +- F-6: canonical C# coverage artifact artifacts/csharp/coverage.xml absent in the worktree (recurring across reviews); the committed projections served as the source. +- F-7 (CR-6): derive evidence labels from the clock for orchestrator-authored artifacts. +- F-8: quality-tiers.yml is absent at the repository root (pre-existing; already promoted to an issue by the #956 review), so tier-dependent gates cannot be evaluated for QuickFiler. +- F-9: AC17 closure: record this pull request's CI run ID, head SHA, mstest-coverage pass/fail counts and coverage figures in spec.md under AC17 and check it off, per the coordinator ruling. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/feature-audit.2026-10-02T14-30.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/feature-audit.2026-10-02T14-30.md new file mode 100644 index 000000000..3285c74f9 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/feature-audit.2026-10-02T14-30.md @@ -0,0 +1,111 @@ +# Feature Audit: quickfiler-tests-depend-on-wall-clock-timing (Issue #950) + +- Timestamp: 2026-10-02T14-30 +- Branch: bug/quickfiler-tests-depend-on-wall-clock-timing-950, head 523c93ee990aefa3e59877373e73fe3c4b1e4ac4 +- Base: 34c2ed88cbb009f2f231453db87bc64d45a9bd51 +- Work mode: full-bug (issue.md line 12). Acceptance-criteria source: spec.md only (AC1 to AC17, spec.md lines 266-283). +- Companion artifacts: policy-audit.2026-10-02T14-30.md, code-review.2026-10-02T14-30.md + +## Scope and Baseline + +- Changed code files (name-status BASE..HEAD, three agreeing sources: caller prompt, evidence/qa-gates/footprint-scope.md, evidence/qa-gates/final-commit.md): + - M QuickFiler/Controllers/QfcDatamodel.cs (+14 / -2) + - M QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs + - M QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs + - M QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs + - M QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs + - A docs/features/potential/promoted/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing.md (inherited promotion record) + - A docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/** (issue, spec, plan, research, 57 evidence files) +- Baseline state at the anchor: code tree unchanged relative to BASE (scoped --exit-code diff exit 0); baseline full-suite run 7361/7361 passed, first-party lines 85.35%, branches 79.74% (evidence/baseline/coverage-baseline.md). +- Post-change state: 7361/7361 passed, lines 85.36%, branches 79.75%; no new failure; no test method added or removed (evidence/qa-gates/coverage-post-change.md, coverage-comparison.md). +- Review method: no-Bash (caller directive); all five code files read in full from the worktree; evidence files read; Grep censuses re-run with the Grep tool; the worktree reflog used as the clock and head reference. +- Defects addressed: Defect A (datamodel tests blocked on real-time bounded waits because the worker started on an uncontrollable thread-pool thread); Defect B (R4 raced the gate-free `EnsureDispatcher` writer in another class under Workers=0 / ClassLevel). + +## Acceptance Criteria Inventory + +| AC | Criterion (abbreviated) | Spec state at review start | +|---|---|---| +| AC1 | `WorkerStarter` internal Action over BackgroundWorker; both constructors assign the RunWorkerAsync default; XML doc states purpose and null-on-uninitialized | [x] | +| AC2 | `InitEmailQueue` has no direct RunWorkerAsync; both start sites call `WorkerStarter` | [x] | +| AC3 | QfcDatamodel.cs at or below 500 total lines | [x] | +| AC4 | No SpinWait.SpinUntil, Task.Wait or WaitForState in the three datamodel test files; surviving TimeSpan values are production arguments or fake advances | [x] | +| AC5 | No Thread.Sleep, Task.Delay, [DoNotParallelize], retry, or [Timeout] value change; runsettings unchanged | [x] | +| AC6 | DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle passes via synchronous WorkerStarter | [x] | +| AC7 | RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces passes, flag read synchronously | [x] | +| AC8 | RemainingLoadActive_AfterLoaderCompletes_BecomesFalse passes via release + drain | [x] | +| AC9 | RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally passes via release + drain | [x] | +| AC10 | Worker_DoWork_CapturesRemainingLoadTask passes, signals read synchronously | [x] | +| AC11 | InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker passes, signal read synchronously | [x] | +| AC12 | The other two zero-batch tests assign a synchronous WorkerStarter and pass | [x] | +| AC13 | R4 opens a `using` over EnsureUiThreadDispatcher after transaction A acquires the gate and before the `original` read; keeps both assertions; passes | [x] | +| AC14 | R4 doc comment: no flake-watch instruction; names the gate-free EnsureDispatcher writer; cites #950; states the W2/W5 invariant | [x] | +| AC15 | Negative control recorded for each test in AC6-AC13, failing immediately, Markdown summary under evidence/other | [x] | +| AC16 | Ambient SynchronizationContext.Current recorded once under evidence/other | [x] | +| AC17 | Full C# toolchain single pass: csharpier check, analyzer rebuild, TreatWarningsAsErrors rebuild, Invoke-MSTestWithCoverage with runsettings unchanged | [ ] (dated coordinator note present) | + +## Acceptance Criteria Evaluation + +| AC | Verdict | Evidence (code and artifacts) | +|---|---|---| +| AC1 | PASS | QfcDatamodel.cs line 152 `internal Action WorkerStarter { get; set; }`; constructors lines 41 and 53 `WorkerStarter = worker => worker.RunWorkerAsync();`; doc comment lines 144-151 names the seam purpose ("Injectable worker-start seam"), the constructor default, the synchronous test use and "stays null on instances built by GetUninitializedObject, so InitEmailQueue fails fast with a NullReferenceException". Census agrees (post-format-census.md: property 1, default 2, doc tokens 1 and 1) | +| AC2 | PASS | Grep over QuickFiler/ for `RunWorkerAsync`: only the two constructor defaults (41, 53), the doc comment (147) and a QueueProcessing doc comment (17); `InitEmailQueue` (lines 271-315) calls `WorkerStarter(worker)` at 285 (zero-batch path) and 312 (positive-batch path) | +| AC3 | PASS | Read of the file: 495 total lines (last line 495 is the closing brace); census LINES 495 | +| AC4 | PASS | Grep over the three datamodel test files for `SpinWait`, `.Wait(`, `WaitForState`: 0 hits. Surviving TimeSpan values: Liveness 139, 141, 156 (`fake.Advance`), Teardown 124 and 155 (`QuiesceLoaderAsync(TimeSpan.FromSeconds(5))`, production argument on a FakeTimeProvider-driven path) and 160 (`fake.Advance`). Census TIMESPAN-UNCLASSIFIED 0; positive control recorded (pre-change census had SpinWait 1/1, .Wait( 2/1/1, WaitForState 5/2) | +| AC5 | PASS | Grep over the four changed test files for `Thread.Sleep`, `Task.Delay`, `DoNotParallelize`, `Retry`: 0. Eight `[Timeout(GateTimeoutMs)]` and `GateTimeoutMs = 60000` unchanged (Read lines 33, 43, 106, 156, 211, 284, 331, 376, 418). Runsettings: not in the name-status; anchored diff exit 0 (prohibited-constructs.md). ADDED-TOKEN counts over 251 added lines all 0 with WorkerStarter 16 as positive control | +| AC6 | PASS | Liveness lines 109-164: `model.WorkerStarter = StartSynchronously` (128), `InitEmailQueue(0, worker)` (132), immediate `loaderEntered.Task.IsCompleted.Should().BeTrue()` (134-136), no wait. Passed in targets-pass-after.md, concurrent-classes-pass-after.md and the final run. Control A1 (no-op starter) failed at the entered assertion in 0.186 s | +| AC7 | PASS | Liveness lines 217-234: `StartHeldOpenLoader` (183-209) asserts entered synchronously; the test reads `ReadLivenessFlag(model)` with no wait (227-231). Passed in all three runs. Control B1 failed at the StartHeldOpenLoader entered assertion in 0.259 s | +| AC8 | PASS | Liveness lines 240-270: pump installed before `StartHeldOpenLoader` (244-246), `release.SetResult(true)` then `pump.Drain()` (256-257), `BeFalse` (260-264), context restored in `finally` (266-269). Passed in all three runs. Controls A2 (release withheld) and C1 (Drain removed) both failed at the `BeFalse` in under 0.17 s | +| AC9 | PASS | Liveness lines 276-310: throwing loader body (286-290), release then Drain (296-297), `BeFalse` (300-304), `finally` restore (306-309). Passed in all three runs. Controls A3 and C2 failed at the `BeFalse` in under 0.003 s | +| AC10 | PASS | Teardown lines 207-242: synchronous starter (222), `InitEmailQueue(0, worker)` (227), `loaderEntered.Task.IsCompleted.Should().BeTrue()` (230-232) and `GetPrivateField(model, "_remainingLoadTask").Should().NotBeNull()` (233-238) read synchronously; `using` over the worker. Passed in all three runs. Control A4 failed in 0.186 s | +| AC11 | PASS | Zero-batch lines 165-183: starter assigned (172), `InitEmailQueue(0, worker)` (176), `loaderInvokedTcs.Task.IsCompleted.Should().BeTrue()` (180-182) with no wait; the inert loader completes its TCS synchronously (105-111). Passed in all three runs. Control A5 failed in 0.008 s | +| AC12 | PASS | Zero-batch lines 143 and 202 assign `StartSynchronously`; both tests pass `new SynchronousBackgroundWorker()` to `InitEmailQueue` (148, 221). Passed in all three runs. Controls A6 and A7 (assignment removed) failed with NullReferenceException at the start site | +| AC13 | PASS | R4 lines 218-223: `BeginTransactionAsync` returns `transactionA`, then `using (IDisposable baseline = QfcItemControllerTestSupport.EnsureUiThreadDispatcher())`, then `Dispatcher original = UiThreadDispatcherFixture.Current` (225) and `transactionA.Install(liveA)` (226). Assertions `BeSameAs(original)` (255-261) and `NotBeSameAs(liveA)` (262-268) both present. Passed alone (0.073 s), concurrently with QfcItemController_FocusAndThemeTests (0.0036 s) and in the final run. Fail-before: R-INJECT on the pre-fix shape reproduced the CI message ("Expected observedByB to refer to ... ParkedDispatcher"); the pinned shape with the same injected writer passed (A8). `EnsureDispatcher` takes only FieldLock (fixture lines 128-135), so the pin inside the gate cannot deadlock | +| AC14 | PASS | R4 doc comment lines 192-209: "Issue #950" (197), "gate-free fixture method EnsureDispatcher seeds the parked dispatcher whenever the field is null" (198-199), the two race windows (199-201), the pin rationale (201-204), and "Invariant for future editors: no other class may dispose an ensure scope holding the parked dispatcher (W2), and UiThread.Initialize (W5) must not latch during this test" (205-207). No flake-watch or "Append an observation" text remains (census 0 and 0) | +| AC15 | PASS | evidence/other/negative-controls-summary.md: nine AC15 rows, one per test named in AC6-AC13, each using the mechanism the spec Test Strategy table assigns (no-op starter; release withheld then Drain; assignment removed; R4 injected writer on pre-fix shape then on pinned shape), each failing with an assertion or exception in under 0.3 s, none hanging; two supplementary Drain-dependency rows. Source batches under evidence/regression-testing/ | +| AC16 | PASS | evidence/other/ambient-synchronization-context.md: AMBIENT-SYNCHRONIZATION-CONTEXT: null, THREAD-POOL: True, APARTMENT: MTA, observed once by a temporary always-failing probe under the repository runsettings and reverted (never committed). The design does not depend on the value because the two context-observing tests install their own | +| AC17 | PENDING (not checked; evaluated under the coordinator ruling) | Steps 1-3 passed in a single iteration with exit 0 (csharpier-check-final.md, msbuild-analyzer-final.md, msbuild-nullable-final.md; toolchain-final-pass.md SINGLE-PASS: YES). Step 4 could not run Invoke-MSTestWithCoverage.ps1 verbatim: the stall probe (evidence/baseline/stall-probe.md) shows ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension failing deterministically on this workstation ("Win32 handle that was passed to Icon is not valid or is the wrong type"), a known environmental failure that reproduces on main. The DIRECT fallback (collector over vstest with the four shell-icon classes excluded, repository runsettings unchanged) passed 7361/7361 with coverage not below baseline (lines 85.35 to 85.36, branches 79.74 to 79.75). The pull request does not exist yet, so no CI run on the final head exists; AC17 stays unchecked per the ruling below | + +Verification notes on AC6-AC13 beyond the recorded runs: the reviewer traced each synchronous mechanism in source. `SynchronousBackgroundWorker.RaiseDoWork()` calls the protected `OnDoWork(new DoWorkEventArgs(null))`, which raises `DoWork` on the calling thread, so the privately subscribed `Worker_DoWork` runs to its first incomplete await (`await loaderTask`, QfcDatamodel.cs line 219) before `InitEmailQueue` returns. With the pump installed, that await captures the pump as its SynchronizationContext; `release` is created with `RunContinuationsAsynchronously`, so `SetResult` posts the continuation rather than running it inline, and `Drain()` runs it on the test thread, clearing the flag in the `finally` (line 227). The negative controls C1/C2 (Drain removed) confirm empirically that the continuation is observed only through `Drain()`. + +## Coordinator Ruling (AC17), recorded verbatim + +"COORDINATOR RULING, AC17 OPTION (a) APPROVED. The only local blocker is ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension, which fails deterministically on this workstation (Win32 handle not valid); a known environmental failure that reproduces on main; CI runs the shell-icon classes. The local fallback run passed 7361/7361 with coverage not below baseline. AC17 is checked off ONLY from this pull request's own CI run on the FINAL head." + +Reviewer disposition: the ruling is applied. AC17 is left unchecked and evaluated as pending CI evidence. The dated note under AC17 in spec.md (2026-10-02, option (a) approved) is present and consistent with the ruling text. Closure requires recording the CI run ID, the head SHA, the mstest-coverage job pass and fail counts and coverage figures, and the local fallback result, then changing `- [ ] AC17` to `- [x] AC17`. If CI fails any test, AC17 is not met. + +## Acceptance Criteria Check-off + +- AC1 to AC16 were already checked `[x]` by the executor (evidence/other/ac-status-summary.md, spec check-off diff: sixteen `- [ ] AC` lines changed to `- [x] AC`, criterion text identical). Each was independently evaluated PASS above against the code and the evidence; no discrepancy found. +- Newly checked off by this review: none (no PASS item was unchecked). +- Left unchecked: AC17 (PENDING under the coordinator ruling; do not check off before this pull request's own CI run on the final head). +- spec.md was not modified by this review. + +### Acceptance Criteria Status +- Source: docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +- Total AC items: 17 +- Checked off (delivered): 16 +- Remaining (unchecked): 1 +- Items remaining: + - AC17: The full C# toolchain passes in a single pass in order: csharpier check, the analyzers rebuild, the TreatWarningsAsErrors rebuild, and Invoke-MSTestWithCoverage with the repository runsettings unchanged. (Pending this pull request's own CI run on the final head, per the coordinator ruling, option (a).) + +## Findings Summary + +- Blocking: 0. +- Non-blocking: 6 (CR-1 helper duplication across three test files, spec-sanctioned; CR-2 stale `RunWorkerAsync` wording in the QueueProcessing doc comment; CR-3 QfcDatamodel.cs at 495/500 lines with dead legacy members; CR-4 undisposed `SynchronousBackgroundWorker` instances in two files; CR-5 R4 `transactionA` without `try`/`finally`, pre-existing shape; CR-6 preflight-clearance-r2 label leads its commit clock by about 7.4 h). Details in code-review.2026-10-02T14-30.md. +- THEME TEST NULL-DISPATCHER EXPOSURE: not observed; structural count of R4 null writes unchanged by this branch; residual hazard belongs to the theme tests' discarded-scope pattern (spec out of scope). See code-review Observation O-1. + +## Follow-ups (non-blocking residuals) + +- F-1: consolidate `SynchronousBackgroundWorker`, `StartSynchronously` and `DrainableSynchronizationContext` into QuickFiler.Test/TestSupport/ with a `` when the project file is next edited (CR-1). +- F-2: reword the `_remainingLoadActive` doc comment in QfcDatamodel.QueueProcessing.cs line 17 to name `WorkerStarter` (CR-2). +- F-3: remove or relocate the dead legacy loader variants and `Worker_RunWorkerCompleted` in QfcDatamodel.cs before the next change to that file; it has five lines of headroom (CR-3). +- F-4: add `try`/`finally` around `transactionA` in R4 to match R1/R5/R6 (CR-5). +- F-5: hold the ensure scope (or take a transaction) in the two FocusAndThemeTests theme tests so a null-restoring writer in another class cannot expose them; the spec places this out of scope for #950 (O-1). +- F-6: canonical C# coverage artifact path artifacts/csharp/coverage.xml absent in the worktree; committed projections used as the source (recurring). +- F-7: derive evidence labels from the clock for orchestrator-authored artifacts (CR-6). +- F-8: quality-tiers.yml absent at the repository root (pre-existing; already promoted by the #956 review). +- F-9: close AC17 from this pull request's CI run on the final head as the ruling prescribes. + +## Summary + +Verdict: PASS. Both defects are resolved by the smallest change the spec allows: a documented, constructor-defaulted worker-start seam in `QfcDatamodel` and a baseline pin taken inside the gate in R4. Sixteen of seventeen acceptance criteria are verified PASS against the source and the committed evidence; AC17 is pending this pull request's own CI run under the approved coordinator ruling and remains unchecked. No Blocking finding. Six Non-blocking findings and nine follow-ups are recorded for the orchestrator; none requires a remediation cycle before the pull request is opened. diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/policy-audit.2026-10-02T14-30.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/policy-audit.2026-10-02T14-30.md new file mode 100644 index 000000000..f4d9d17a8 --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/policy-audit.2026-10-02T14-30.md @@ -0,0 +1,261 @@ +# Policy Audit: quickfiler-tests-depend-on-wall-clock-timing (Issue #950) + +- Timestamp: 2026-10-02T14-30 +- Branch: bug/quickfiler-tests-depend-on-wall-clock-timing-950 +- Head: 523c93ee990aefa3e59877373e73fe3c4b1e4ac4 (worktree reflog, last entry; commit "docs(950): record the coordinator ruling on the AC17 evidence source") +- Base: 34c2ed88cbb009f2f231453db87bc64d45a9bd51 (post-merge origin/main anchor; verified as merge-base in evidence/baseline/scope-and-anchor.md) +- Work mode: full-bug (issue.md line 12); acceptance-criteria source: spec.md only +- Reviewer: feature-review, no-Bash mode (caller directive). Every check was performed with Read, Grep and Glob against the item worktree plus the committed evidence under evidence/. Where a check needs a shell it is recorded as such with the reason. +- Timestamp derivation: the label above was chosen to be monotone after every label in the feature folder (latest 2026-10-02T12-10) and after the orchestrator checkpoint's latest recorded time (2026-10-02T14:05Z). No shell clock was available. The worktree reflog places the head commit at epoch 1790919527 (2026-10-02T05:38:47Z, 01:38 at the recorded -0400 offset). + +## Executive Summary + +Overall verdict: PASS. 0 Blocking findings, 6 Non-blocking findings (CR-1 to CR-6, detailed in code-review.2026-10-02T14-30.md). AC1 to AC16 verified PASS against code and evidence; AC17 remains unchecked under the coordinator ruling (option (a): checked off only from this pull request's own CI run on the final head). + +| Area | Verdict | Evidence summary | +|---|---|---| +| General Unit Test Policy | PASS | Nine rewritten or pinned tests are deterministic (synchronous worker start, drained test-owned SynchronizationContext, FakeTimeProvider); no Thread.Sleep, Task.Delay, retry, [DoNotParallelize] or temporary file; 7361/7361 pass at baseline and after the change | +| General Code Change Policy | PASS | Minimal fix: one delegate seam in one production file (14 added, 2 deleted lines); every file at or under 500 lines; toolchain evidence single pass | +| C# Code Change Policy | PASS | csharpier check exit 0 (1637 files); analyzer rebuild 0 errors 0 warnings; TreatWarningsAsErrors rebuild 0 errors 0 warnings; no /p:Nullable=enable; /t:Rebuild used | +| C# Unit Test Policy | PASS | MSTest, Moq, FluentAssertions throughout; first-party lines 85.36%, branches 79.75% (floors 80/75 per CLAUDE.md, 85/75 per rules, both met) | +| Coverage (C#) | PASS | Repo-wide not lower than baseline (85.35 to 85.36 lines, 79.74 to 79.75 branches); changed production file attribute-excluded (pre-existing), execution proven by tests and negative controls | +| Evidence hygiene | PASS | 0 host paths, 0 raw trx or coverage documents in the committed feature folder (reviewer sweep and evidence/qa-gates/evidence-hygiene.md agree) | +| Coordinator prohibitions | PASS | No retries, no [DoNotParallelize], no Workers=1, no lengthened timeout, no wall-clock wait, no temporary file (evidence/qa-gates/prohibited-constructs.md plus direct Grep) | + +## Rejected Scope Narrowing + +No scope narrowing was detected in the caller prompt. Two caller statements were evaluated and accepted as factual rather than narrowing: + +- "This item touches no PowerShell file." Confirmed by the caller-supplied name-status, evidence/qa-gates/footprint-scope.md and evidence/qa-gates/final-commit.md: the branch-vs-base diff outside the feature folder is exactly five .cs files plus the promoted record. Zero PowerShell, TypeScript or Python files changed. +- "No artifacts/csharp/coverage.xml exists in the worktree; use the committed projections and summaries as the coverage source and say so." This is an evidence-source instruction consistent with CLAUDE.md "Committed Test Evidence Format" and with the prior ruling that committed feature-folder coverage projections count as the coverage artifact. It does not narrow the audit scope, which remains the full branch diff against the base. + +## Evidence Location Compliance + +- Branch diff scan for files under artifacts/baselines/, artifacts/qa/, artifacts/evidence/ or artifacts/coverage/: none. The name-status BASE..HEAD recorded in evidence/qa-gates/footprint-scope.md and evidence/qa-gates/final-commit.md lists the five code files, the promoted record and paths under docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/ only. +- All executor evidence lives under docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/{baseline,qa-gates,regression-testing,other}/ (57 Markdown files; Glob listing in this review). +- validate_evidence_locations.py --root .: not run (Bash was forbidden for this review). The manual scan above substitutes; no violation observed. +- EVIDENCE_LOCATION_OVERRIDE_REJECTED: none required; the caller supplied no non-canonical evidence path. +- PR context artifacts (artifacts/pr_context.summary.txt, artifacts/pr_context.appendix.txt): absent in the review worktree (Grep over artifacts/ found only orchestration/orchestrator-state.json). Regeneration was not possible without a shell or the collection tool. Scope was derived from the caller-supplied name-status, the committed footprint-scope.md (git diff --name-status BASE..HEAD) and the worktree reflog head. Recorded as UNVERIFIED for the artifact pair only; the scope itself is verified by three agreeing sources. + +## 1. General Unit Test Policy Compliance + +### 1.1 Core principles + +| Principle | Verdict | Evidence | +|---|---|---| +| Independence | PASS | Each test builds its own uninitialized QfcDatamodel and its own worker; the two context-installing tests restore SynchronizationContext.Current in a finally (QfcDatamodelLivenessTests.cs lines 244-269 and 280-309). R4 pins and later releases its own baseline scope (lines 221-270). Concurrent run with QfcItemController_FocusAndThemeTests: 37/37 passed (evidence/regression-testing/concurrent-classes-pass-after.md) | +| Isolation | PASS | Each test targets one behavior of InitEmailQueue / Worker_DoWork or one fixture property; negative controls fail at exactly one named assertion each (evidence/other/negative-controls-summary.md) | +| Fast execution | PASS | Nine target durations between 0.0014 s and 0.265 s (evidence/regression-testing/targets-pass-after.md); the former five-second bounds are gone | +| Determinism | PASS | Synchronous worker start through WorkerStarter; posted continuations observed only through DrainableSynchronizationContext.Drain(); FakeTimeProvider for all time; R4 baseline pinned inside the gate. Negative controls fail immediately when the signal is withheld (ten rows, all under 0.3 s) | +| Readability | PASS | Descriptive names, XML doc comments on every helper, Arrange/Act/Assert markers, failure reasons on every assertion | + +### 1.2 Coverage + +**Coverage Metrics by Language:** + +| Language | Files Changed | Tests | Test Result | Baseline Coverage | Post-Change Coverage | New Code Coverage | +|---|---|---|---|---|---|---| +| C# | 5 | 7361 | 7361 passed, 0 failed | 85.35% lines / 79.74% branches | 85.36% lines / 79.75% branches | N/A (attribute-excluded production file; see the per-language comparison block) | +| TypeScript | 0 | N/A | N/A | N/A | N/A | N/A | +| PowerShell | 0 | N/A | N/A | N/A | N/A | N/A | +| Python | 0 | N/A | N/A | N/A | N/A | N/A | + +Coverage source statement: no artifacts/csharp/coverage.xml exists in the review worktree. The figures above are read from the committed projections and summaries, which are the forms CLAUDE.md "Committed Test Evidence Format" requires: evidence/baseline/coverage-baseline.md (P0-T16) and evidence/qa-gates/coverage-post-change.md (P6-T5), each carrying the first-party summary line, the root counters (lines-covered/lines-valid, branches-covered/branches-valid) and the package-level JaCoCo projection, plus evidence/qa-gates/coverage-comparison.md (P6-T6). Both runs used the DIRECT route with the identical four-class shell-icon exclusion, so they are comparable. + +Verdict lines: + +- C# coverage verdict: PASS (repo-wide first-party lines 85.36% and branches 79.75% from the committed post-change projection; above the CLAUDE.md floors of 80% lines and 75% branches, which the caller designates as governing, and also above the 85% / 75% floors in .claude/rules; no regression against the baseline 85.35% / 79.74%). +- C# changed-production-file coverage: PASS on the no-regression limb. The only changed production file, QuickFiler/Controllers/QfcDatamodel.cs, carries a pre-existing type-level ExcludeFromCodeCoverage attribute (line 25, introduced by the #197 exemption commit), so neither Cobertura document has a class node for it (QFCDATAMODEL-CLASS-NODES 0 at both stages) and no measured line of it can regress. Execution of the 14 added lines is proven independently: all nine target tests reach WorkerStarter(worker) through InitEmailQueue, and negative controls A6 and A7 (WorkerStarter assignment removed) fail with NullReferenceException at the start site, which shows the call is executed. The four changed test files are outside the coverage denominator by policy. +- C# package-level corroboration: the QuickFiler package counters are identical at both stages (LINE missed 2293 covered 10461; BRANCH missed 699 covered 2518), consistent with a change whose only production lines are in an attribute-excluded type. The +8 lines / +2 branches delta sits entirely in the UtilitiesCS package, which this branch does not touch; the comparison artifact records it as run-to-run variance. +- PowerShell coverage gate: PASS by vacuity (zero PowerShell files changed on this branch, so the changed-line no-regression requirement has no line to evaluate; no PoshQC format, analyze or test gate was owed or run; artifacts/pester/powershell-coverage.xml was not consulted). +- TypeScript and Python: zero files changed on this branch; no verdict is owed. + +### Coverage Evidence Checklist + +- C# baseline coverage artifact: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/baseline/coverage-baseline.md` (committed JaCoCo package projection + first-party summary + root counters; canonical artifacts/csharp/coverage.xml absent in the worktree) +- C# post-change coverage artifact: `docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/coverage-post-change.md` (same three forms; canonical artifacts/csharp/coverage.xml absent in the worktree) +- TypeScript baseline coverage artifact: none consulted (zero TypeScript files changed on this branch) +- TypeScript post-change coverage artifact: none consulted (zero TypeScript files changed on this branch) +- PowerShell baseline coverage artifact: none consulted (zero PowerShell files changed on this branch) +- PowerShell post-change coverage artifact: none consulted (zero PowerShell files changed on this branch) +- Python baseline coverage artifact: none consulted (zero Python files changed on this branch) +- Python post-change coverage artifact: none consulted (zero Python files changed on this branch) +- Per-language comparison summary: the per-language comparison block of this document + +### 1.2.1 Per-Language Coverage Comparison + +- C#: Baseline: 85.35% lines (56204/65855) / 79.74% branches (13618/17078). Post-change: 85.36% lines (56212/65855) / 79.75% branches (13620/17078). Change: +0.01% lines (+8 covered) / +0.01% branches (+2 covered), confined to the UtilitiesCS package which this branch does not change. Disposition: PASS. Evidence: evidence/baseline/coverage-baseline.md, evidence/qa-gates/coverage-post-change.md, evidence/qa-gates/coverage-comparison.md. +- 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 + +| Language | Artifact consulted | State | Disposition | +|---|---|---|---| +| C# | Committed projections and summaries under evidence/baseline and evidence/qa-gates | Present; canonical artifacts/csharp/coverage.xml absent in the worktree (recorded as follow-up F-6, recurring) | PASS | +| TypeScript | none | zero files changed | no verdict owed | +| PowerShell | none | zero files changed | no verdict owed | +| Python | none | zero files changed | no verdict owed | + +Coverage exclusion policy check (.claude/rules/general-unit-test.md): the branch adds no coverage-config `exclude` entry and no new ExcludeFromCodeCoverage attribute (the QfcDatamodel.cs attribute at line 25 predates the base). Under the standing ruling (CLAUDE.md UT2 COM/VSTO exemption is the more specific, higher-authority clause; the rules file's Blocking clause enumerates config `exclude` globs), the pre-existing attribute is Not Blocking for this change. Its material consequence (no coverage observation for the 14 changed lines) is recorded above rather than waived. + +### 1.3 Scenario completeness + +| Scenario | Verdict | Evidence | +|---|---|---| +| Positive flows | PASS | Zero-batch start (AC11), positive-batch projection (AC12), liveness flag true across the async-void boundary (AC7), flag cleared on completion (AC8), capture of _remainingLoadTask (AC10) | +| Negative flows | PASS | Unassigned WorkerStarter fails fast with NullReferenceException (controls A6, A7); loader never released leaves the flag true (A2, A3); no-op starter never reaches the loader (A1, A4, A5, B1) | +| Edge cases | PASS | Drain removed with loader released shows the continuation is observed only through Drain (C1, C2) | +| Error handling | PASS | Throwing loader still clears the flag through finally then catch (AC9, test 4) | +| Concurrency | PASS | R4 two-transaction ordering kept; baseline pin closes both race windows; concurrent run with the gate-free writer class passed 37/37 | +| State transitions | PASS | _remainingLoadActive true -> false observed synchronously after Drain | + +### 1.4 Arrange-Act-Assert + +PASS. Every rewritten test carries Arrange / Act / Assert comments or an "Arrange / Act" combined marker where the helper performs both (RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces, lines 220-233). Every assertion carries a because-reason string. + +### 1.5 External dependencies and temporary files + +PASS. Outlook COM is mocked (Mock, Mock, Mock); the loader is an injected delegate; no file system, network or process is touched; Grep over the four changed test files for Path.GetTempFileName, Path.GetTempPath, File.Create and StreamWriter: no hit (the test-file Grep for prohibited constructs in section 7 covers the wait tokens). + +### 1.6 Test file location + +PASS (repository convention). Tests live in QuickFiler.Test/Controllers/, mirroring QuickFiler/Controllers/. The rules file names a tests/ tree, but the per-project *.Test layout is the pre-existing repository convention for every C# project; no test file was created or moved by this branch. + +### 1.7 Determinism infrastructure + +PASS. FakeTimeProvider drives all time (QfcDatamodelLivenessTests.cs lines 114, 139, 141, 156; QfcDatamodelTeardownTests.cs lines 118, 146, 160). Banned APIs in test code (Thread.Sleep, Task.Delay, wall-clock waits): none in the four changed files (Grep: the only `.Wait(` hit is the pre-existing `secondCallerStarted.Wait()` on a ManualResetEventSlim in the R4 file at line 250, a signal wait without a time bound and not in the three datamodel files AC4 names). No seeded RNG is needed; no randomness is used. + +## 2. General Code Change Policy Compliance + +| Item | Verdict | Evidence | +|---|---|---| +| Before making changes (plan, spec) | PASS | spec.md (302 lines) and plan.2026-10-01T07-11.md exist; three preflight rounds plus a confirming R2 preflight recorded under evidence/other/ | +| Bugfix workflow step 1 (failing regression test first) | PASS | Defect B: deterministic fail-before via the R-INJECT edit on the unmodified file, run alone (evidence/regression-testing/r4-fail-before.md, CI failure signature reproduced), then pass-after in two runs. Defect A: a deterministic failing run is impossible without a wall-clock wait or thread-pool starvation; the dossier (evidence/regression-testing/fail-before-exception.2026-10-02T01-00.md) substitutes the pre-change wait census plus ten post-fix negative controls that each fail at once when the signal is withheld. The rewritten tests themselves are the regression tests; no test method was added or removed (7361 before and after) | +| Bugfix workflow step 2 (minimal targeted fix) | PASS | Production diff is 14 added / 2 deleted lines in one file (evidence/qa-gates/post-format-census.md numstat 14 2); the R4 change is a using pin plus a doc comment | +| Bugfix workflow step 3 (verify locally, toolchain in order) | PASS with ruling | Steps 1-3 single pass, exit 0 (evidence/qa-gates/toolchain-final-pass.md SINGLE-PASS: YES). Step 4 ran the DIRECT route (collector over vstest with the four shell-icon classes excluded) because the runner's full suite fails deterministically on this workstation in ShellUtilitiesStatic_Tests (Win32 handle not valid; reproduces on main; evidence/baseline/stall-probe.md). Coordinator ruling option (a) accepted; AC17 is pending this PR's CI run | +| Design principles (simplicity, reusability, extensibility, separation) | PASS | Smallest seam: one Action delegate with a constructor default, mirroring the existing RemainingEmailLoader convention; production path unchanged (one extra delegate invocation). Reusability: helper duplication across three test files is spec-sanctioned by the documented per-file convention (CR-1, non-blocking) | +| Classes, functions, APIs | PASS | Test helpers are small private nested types; the seam is an internal property with a documented contract | +| Error handling | PASS | No new catch; the unassigned-seam NullReferenceException is the intended fail-fast on test-only uninitialized instances (spec Boundaries), unreachable in production because QfcHomeController only reaches InitEmailQueue on constructed instances | +| Logging | PASS | No new logging; existing log4net path unchanged | +| File size limit (500 lines) | PASS | QfcDatamodel.cs 495; QfcDatamodelLivenessTests.cs 312; QfcDatamodelTeardownTests.cs 244; QfcInitEmailQueueZeroBatchTests.cs 232; QfcItemController.UiThreadDispatcherFixtureTests.cs 470 (reviewer Read line counts agree with evidence/qa-gates/post-format-census.md). QfcDatamodel.cs has five lines of headroom (CR-3) | +| Naming | PASS | PascalCase property WorkerStarter; camelCase locals; descriptive test and helper names | +| Public APIs and compatibility | PASS | IQfcDatamodel unchanged; the new member is internal; InternalsVisibleTo already in use | +| Dependencies | PASS | None added; FakeTimeProvider already referenced | +| I/O boundaries | PASS | No I/O introduced | + +## 3. Language-Specific Code Change Policy Compliance + +Language in scope: C# only. + +| Item | Verdict | Evidence | +|---|---|---| +| Formatting (csharpier via dotnet tool run) | PASS | evidence/qa-gates/csharpier-format.md (REWRITTEN-WRITESET: NONE after the Phase 4 scoped format) and csharpier-check-final.md: Checked 1637 files, CSHARPIER_EXIT_CODE 0 | +| Linting (analyzer rebuild, /t:Rebuild, EnableNETAnalyzers, EnforceCodeStyleInBuild) | PASS | evidence/qa-gates/msbuild-analyzer-final.md: MSBUILD_EXIT_CODE 0, ERRORS 0, WARNINGS 0, SKIP_CORECOMPILE_LINES 0, no Write Set diagnostic | +| Type checking (TreatWarningsAsErrors rebuild, no /p:Nullable=enable) | PASS | evidence/qa-gates/msbuild-nullable-final.md: exit 0, 0 errors, 0 warnings, SKIP_CORECOMPILE_LINES 0; command text matches CLAUDE.md character for character | +| Nullable annotations | PASS | QfcDatamodel.cs carries no #nullable enable directive, so the new property is outside nullable analysis by the per-file opt-in rule; the doc comment states the null-on-uninitialized behavior | +| DI seam preference (interface > delegate > adapter) | PASS | A narrow Action delegate for a single call path, with a safe deterministic default; an interface for one call would be excessive (.claude/rules/csharp.md DI Seams item 2) | +| XML docs on non-obvious contract | PASS | WorkerStarter doc comment lines 144-151 states purpose, default, null-on-uninitialized behavior and the issue number | +| Internal surface | PASS | internal property; QuickFiler.Test consumes it through the existing InternalsVisibleTo | +| Name resolution | PASS | QfcDatamodel.cs imports both System and Microsoft.Office.Interop.Outlook; the generic Action is unambiguous because Outlook.Action is non-generic, and both rebuilds compiled with 0 errors | +| Analyzer suppressions added | PASS | None (the pre-existing CS0618 pragma at lines 436/457 is untouched) | + +## 4. Language-Specific Unit Test Policy Compliance + +| Item | Verdict | Evidence | +|---|---|---| +| MSTest framework | PASS | [TestClass] / [TestMethod] in all four files | +| Moq for mocks | PASS | Mock, Mock, Mock, Mock, Mock | +| FluentAssertions | PASS | All assertions use Should(); no MSTest Assert calls introduced | +| Repo-wide coverage floors | PASS | 85.36% lines (floor 80% per CLAUDE.md, 85% per rules), 79.75% branches (floor 75%) | +| New module/class/method >= 90% | PASS on the execution limb | The new property sits in an attribute-excluded type and has no coverage observation; its execution on every test path is proven by the nine tests and controls A6/A7 (see section 1.2) | +| No regression on changed lines | PASS | No measured changed line exists; repo-wide and package-level figures are not lower | +| Prohibited behaviors (sleeps, retries, timing hacks, weakened assertions) | PASS | ADDED-TOKEN Thread.Sleep 0, Task.Delay 0, DoNotParallelize 0, Retry( 0, Timeout( 0 over 251 added lines (evidence/qa-gates/prohibited-constructs.md); R4 keeps both BeSameAs(original) and NotBeSameAs(liveA) (lines 255-268); the #424 claim of liveness test 1 is preserved (dequeue still polls _remainingLoadActive, QfcDatamodel.QueueProcessing.cs lines 305 and 406) | +| Test toolchain route | PASS with ruling | Step 4 used the DIRECT route for the environmental reason recorded in section 2; the inner vstest invocation used the repository runsettings (Workers=0, Scope=ClassLevel) unchanged | + +## 5. Test Coverage Detail + +| File | Change type | Coverage observation | Disposition | +|---|---|---|---| +| QuickFiler/Controllers/QfcDatamodel.cs | Modified production (+14 / -2) | No class node in either Cobertura document (type-level ExcludeFromCodeCoverage, pre-existing). Execution proven: nine tests reach WorkerStarter(worker); controls A6/A7 throw NullReferenceException at the start site when the seam is unassigned | PASS (no-regression limb; unmeasured by pre-existing exemption) | +| QuickFiler.Test/Controllers/QfcDatamodelLivenessTests.cs | Modified test | Outside the denominator by policy | Not applicable to the metric; four tests Passed | +| QuickFiler.Test/Controllers/QfcDatamodelTeardownTests.cs | Modified test | Outside the denominator by policy | Not applicable to the metric; five tests Passed | +| QuickFiler.Test/Controllers/QfcInitEmailQueueZeroBatchTests.cs | Modified test | Outside the denominator by policy | Not applicable to the metric; three tests Passed | +| QuickFiler.Test/Controllers/QfcItemController.UiThreadDispatcherFixtureTests.cs | Modified test | Outside the denominator by policy | Not applicable to the metric; eight tests Passed | + +Package-level projection (QuickFiler package, identical at both stages): LINE missed 2293 / covered 10461; BRANCH missed 699 / covered 2518. + +## 6. Test Execution Metrics + +| Run | Scope | Total | Passed | Failed | Source | +|---|---|---|---|---|---| +| Baseline (P0-T16, DIRECT route) | nine test assemblies | 7361 | 7361 | 0 | evidence/baseline/coverage-baseline.md | +| Final (P6-T5, DIRECT route) | nine test assemblies | 7361 | 7361 | 0 | evidence/qa-gates/coverage-post-change.md | +| Nine targets alone (P4-T4) | QuickFiler.Test | 9 | 9 | 0 | evidence/regression-testing/targets-pass-after.md | +| Four target classes with FocusAndThemeTests (P4-T5) | QuickFiler.Test | 37 | 37 | 0 | evidence/regression-testing/concurrent-classes-pass-after.md | +| R4 fail-before (P1-T6, R-INJECT, run alone) | one test | 1 | 0 | 1 (expected) | evidence/regression-testing/r4-fail-before.md | +| Stall probe (P0-T15, shell-icon classes) | 23 tests | 23 | 22 | 1 (environmental, reproduces on main) | evidence/baseline/stall-probe.md | + +Figures compared (evidence/qa-gates/coverage-post-change.md FIGURES-COMPARED): total, executed, error, timeout, aborted and notExecuted all equal between baseline and final; no Sequence file in any run; FINAL-FAILED-SET empty. + +Negative controls (AC15), one row per test named in AC6 to AC13; mechanism per the spec Test Strategy table: + +| Control | Test | Signal withheld | Outcome | Duration | +|---|---|---|---|---| +| A1 | DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle | no-op WorkerStarter | Failed | 0.186 s | +| B1 | RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces | no-op WorkerStarter | Failed | 0.259 s | +| A2 | RemainingLoadActive_AfterLoaderCompletes_BecomesFalse | release never set, then Drain() | Failed | 0.009 s | +| A3 | RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally | release never set, then Drain() | Failed | 0.002 s | +| A4 | Worker_DoWork_CapturesRemainingLoadTask | no-op WorkerStarter | Failed | 0.186 s | +| A5 | InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker | no-op WorkerStarter | Failed | 0.008 s | +| A6 | InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing | WorkerStarter assignment removed | Failed (NullReferenceException) | 0.186 s | +| A7 | InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop | WorkerStarter assignment removed | Failed (NullReferenceException) | 0.286 s | +| R-INJECT / A8 | Transaction_SecondCallerCannotInstallUntilTheFirstRestores | injected gate-free writer on the pre-fix shape, run alone / same writer on the pinned shape | Failed with the CI signature / Passed | 0.193 s / 0.083 s | +| C1, C2 | the two Drain-dependent liveness tests | Drain() removed with loader released | Failed | 0.162 s / 0.002 s | + +THEME TEST NULL-DISPATCHER EXPOSURE: not observed. Both FocusAndThemeTests theme tests passed in the concurrent run (P4-T5). Analysis of the structural exposure is in code-review.2026-10-02T14-30.md (Observation O-1): the count of null-restoring writes R4 performs is unchanged by this branch, and the residual hazard belongs to the theme tests' discarded-scope pattern, which the spec places out of scope. + +## 7. Code Quality Checks + +| Check | Command or method | Result | Verdict | +|---|---|---|---| +| Confidentiality masking scan | Grep over the feature folder for drive-letter paths, user-profile paths, account names and UNC prefixes | 0 hits (agrees with evidence/qa-gates/evidence-hygiene.md PROFILE_PATH_LINES 0 and FEATURE_ROOT_PROFILE_PATH_LINES 0) | PASS | +| Raw document scan | Glob over the feature folder for .trx, .xml, .coverage, .cobertura, .json | 0 files (RAW_DOCUMENTS 0 in the executor gate) | PASS | +| Suppression scan (added lines) | Read of all five changed files | No new #pragma, [SuppressMessage], or analyzer suppression | PASS | +| Workflow change scan | Name-status BASE..HEAD | No .github/, scripts/, runsettings or .csproj path changed | PASS | +| Prohibited-construct scan | Grep over the four changed test files for Thread.Sleep, Task.Delay, DoNotParallelize, SpinWait, .Wait(, WaitForState, Retry | One hit: pre-existing secondCallerStarted.Wait() (ManualResetEventSlim) in the R4 file; none in the three datamodel files | PASS | +| Call-site census | Grep for .InitEmailQueue( and WorkerStarter across the worktree | Production caller QfcHomeController.cs line 252 (constructed instance, seam assigned by both constructors); every test caller on an uninitialized instance assigns the seam (11 WorkerStarter occurrences across the three datamodel test files); QfcHomeControllerRunAsyncTests mocks IQfcDatamodel | PASS | +| Tonality scan | Read of spec.md, issue.md and the committed evidence | Neutral, factual wording; no humor, hyperbole or metaphor | PASS | + +## Appendix A: Test Inventory + +| Test class | Test | Status after change | AC | +|---|---|---|---| +| QfcDatamodelLivenessTests | DequeueNextItemGroupAsync_WhileLoaderStillProducing_KeepsPollingAfterWorkerIdle | Passed | AC6 | +| QfcDatamodelLivenessTests | RemainingLoadActive_AcrossAsyncVoidFirstAwait_StaysTrueWhileLoaderProduces | Passed | AC7 | +| QfcDatamodelLivenessTests | RemainingLoadActive_AfterLoaderCompletes_BecomesFalse | Passed | AC8 | +| QfcDatamodelLivenessTests | RemainingLoadActive_WhenLoaderThrows_IsStillClearedByFinally | Passed | AC9 | +| QfcDatamodelTeardownTests | Worker_DoWork_CapturesRemainingLoadTask | Passed | AC10 | +| QfcDatamodelTeardownTests | TryQueueRemainingMailItemAsync_AfterCleanupNulledFields_ReturnsFalseWithoutThrowing, QuiesceLoaderAsync_LoaderCompletes_ReturnsBeforeTimeout, QuiesceLoaderAsync_LoaderHangs_ReturnsAtBoundAndLogs, Cleanup_CalledTwice_DoesNotThrow | Passed, unchanged | regression | +| QfcInitEmailQueueZeroBatchTests | InitEmailQueue_ZeroBatchSize_StillStartsBackgroundWorker | Passed | AC11 | +| QfcInitEmailQueueZeroBatchTests | InitEmailQueue_ZeroBatchSize_ReturnsEmptyListWithoutThrowing, InitEmailQueue_PositiveBatchSize_RetainsExistingProjectionAndFrameDrop | Passed | AC12 | +| QfcItemController_UiThreadDispatcherFixtureTests | Transaction_SecondCallerCannotInstallUntilTheFirstRestores | Passed (alone and concurrent) | AC13, AC14 | +| QfcItemController_UiThreadDispatcherFixtureTests | R1, R2, R3, R5, R6, #743 counters, #882 zero-bound | Passed, unchanged | regression | +| QfcItemController_FocusAndThemeTests | SetThemeDark_FromNormal_SelectsDarkNormalTheme, SetThemeLight_FromNormal_SelectsLightNormalTheme | Passed concurrently with the target classes | exposure check | + +Test method count: 7361 at baseline and after the change; no method added or removed. + +## Appendix B: Toolchain Commands Reference + +| Step | Command (as recorded in evidence/qa-gates/toolchain-final-pass.md) | Exit | Iteration | +|---|---|---|---| +| 1 | dotnet tool run csharpier format . | 0 | 1 | +| 1b | dotnet tool run csharpier check . | 0 | 1 | +| 2 | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=true | 0 | 1 | +| 3 | msbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=true | 0 | 1 | +| 4 | dotnet-coverage collect --output coverage\final-950.cobertura.xml --output-format cobertura --settings coverage\effective-coverage-950.config -- vstest.console.exe /Settings:scripts\vscode\TaskMaster.cli.runsettings /InIsolation "/TestCaseFilter:TestCategory!=LiveOutlook&FullyQualifiedName!~HelperClasses.ShellUtilities_Tests&FullyQualifiedName!~HelperClasses.ShellUtilitiesStatic_Tests&FullyQualifiedName!~HelperClasses.SysImageListHelperTests&FullyQualifiedName!~EmailIntelligence.OSBrowser_Tests" "/ResultsDirectory:coverage\test-results\950\final" "/Logger:trx;LogFileName=final-950.trx" "/Blame:CollectHangDump;TestTimeout=4min;HangDumpType=None" (DIRECT route; the runner script Invoke-MSTestWithCoverage.ps1 was dot-sourced for its post-processing functions but not run verbatim, for the environmental reason in section 2) | 0 | 1 | + +PowerShell gates (PoshQC MCP format / analyze / test): not run; zero PowerShell files changed on this branch. + +Reviewer commands: none (Bash forbidden). All verification by Read, Grep and Glob against the item worktree and its reflog. From e591b11b72bf1c4682ee43171008935887f8c7e6 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 02:09:21 -0400 Subject: [PATCH 23/24] docs(950): check off AC17 from the pull request CI run Co-Authored-By: Claude Opus 5.5 --- .../evidence/other/ac-status-summary.md | 11 +++++ .../evidence/qa-gates/ac17-ci-evidence.md | 45 +++++++++++++++++++ .../spec.md | 2 +- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md index 3b799670a..4b7a0cf39 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/other/ac-status-summary.md @@ -44,3 +44,14 @@ Diff: one hunk at spec.md lines 263 to 284. The sixteen deleted lines are `- [ ] porcelain: M docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md Added and deleted counts are equal (16) and equal the checked-off count (16). spec.md changed only in its checkbox lines. + +## Addendum 2026-10-02: AC17 closed from pull request CI + +The table above records the state at the end of plan execution. Under the coordinator ruling (AC17 option (a), recorded in spec.md under AC17), AC17 was checked off from pull request 971's own CI run: run 36971702087 on head d46738a75, mstest-coverage 7384 passed of 7384, lines 85.96%, branches 80.11%. Evidence: evidence/qa-gates/ac17-ci-evidence.md. + +### Acceptance Criteria Status +- Source: docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +- Total AC items: 17 +- Checked off (delivered): 17 +- Remaining (unchecked): 0 +- Items remaining: none diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md new file mode 100644 index 000000000..429cc82bc --- /dev/null +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md @@ -0,0 +1,45 @@ +# AC17 evidence: full C# toolchain through the standard route (pull request CI) + +Timestamp: 2026-10-02T06-10 +Command: gh run view 36971702087 --repo drmoisan/TaskMaster --log --job 110726836511 +EXIT_CODE: 0 + +## Authority + +COORDINATOR RULING, AC17 OPTION (a) APPROVED (recorded verbatim from the relaunch directive): + +- Rationale: the only local blocker is ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension, which fails deterministically on this workstation ("Win32 handle ... not valid"); it is a known environmental failure that reproduces on main, and CI runs the shell-icon classes. The local fallback run already passed 7361/7361 with coverage not below baseline (lines 85.35 to 85.36, branches 79.74 to 79.75). +- Conditions: (a) AC17 is checked off ONLY from this pull request's own CI run on the FINAL head; record the run ID, the head SHA, the mstest-coverage job pass/fail counts and its coverage figures, and the local fallback result alongside them in the AC17 evidence; (b) add a dated note in spec.md under AC17 citing this coordinator ruling - AC17 intent (full suite through the standard route) is unchanged, only the evidence source changes; (c) if CI fails ANY test, AC17 is NOT met: do not check it off, commit and push, STOP and report. + +The dated spec.md note was committed at 523c93ee9. + +## Pull request CI run (head at check-off) + +- Pull request: 971 (drmoisan/TaskMaster), base main +- Head SHA: d46738a75347fff8ccd63571f424b077399c60cc +- Workflow run ID: 36971702087 (event pull_request), conclusion success +- Jobs, all success: format-check (csharpier verify), build-analyzers, build-nullable, mstest-coverage, pester, actionlint, hygiene +- mstest-coverage job ID: 110726836511 (5m6s) + +Output Summary: + +- Test Run Successful. +- Total tests: 7384 +- Passed: 7384 +- Failed: 0 (no Failed line printed; the console prints one only when a test fails) +- First-party coverage: lines 56612/65855 (85.96%), branches 13682/17078 (80.11%) +- The test named in the ruling, GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension, passed in CI (two passing rows, 5 ms and 2 ms). +- Transaction_SecondCallerCannotInstallUntilTheFirstRestores (R4) passed (7 ms); SetThemeDark_FromNormal_SelectsDarkNormalTheme and SetThemeLight_FromNormal_SelectsLightNormalTheme passed. No theme-test null-dispatcher exposure was observed. + +## Local fallback result (for comparison) + +- Route: Invoke-MSTestWithCoverage with the four shell-icon test classes excluded (environmental failure on this workstation; see evidence/baseline/stall-probe.md and evidence/qa-gates/toolchain-final-pass.md). +- Result: 7361 of 7361 passed. +- Coverage: lines 85.35% (baseline) to 85.36% (post-change); branches 79.74% to 79.75% (evidence/qa-gates/coverage-comparison.md). +- The CI total (7384) exceeds the local total (7361) by 23, consistent with the shell-icon classes running in CI and being excluded locally. + +## Final-head confirmation + +The commit that adds this file and checks off AC17 creates a new head whose diff against d46738a75 is documentation-only (this file, the AC17 checkbox in spec.md, and an addendum to evidence/other/ac-status-summary.md). The CI run on that final head, and its mstest-coverage figures, are recorded in the section below once available. + +Final head: pending diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md index 714f48d4c..bb5c40480 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/spec.md @@ -279,7 +279,7 @@ The #230 lost-update control for R4 (a fixture that releases the gate before res - [x] AC14: The R4 doc comment no longer carries the flake-watch instruction and instead names the gate-free `EnsureDispatcher` writer as the cause, cites this issue, and states the W2/W5 residual-writer invariant. - [x] AC15: Each test named in AC6 through AC13 has a recorded negative control, using the mechanism the Test Strategy table assigns to it, that fails immediately with an assertion or exception rather than hanging; each outcome is recorded as a Markdown summary in the feature folder's evidence/other directory. - [x] AC16: The observed ambient `SynchronizationContext.Current` value on the MSTest worker thread is recorded once in the feature folder's evidence/other directory. -- [ ] AC17: The full C# toolchain passes in a single pass in order: csharpier check, the analyzers rebuild, the TreatWarningsAsErrors rebuild, and Invoke-MSTestWithCoverage with the repository runsettings unchanged. +- [x] AC17: The full C# toolchain passes in a single pass in order: csharpier check, the analyzers rebuild, the TreatWarningsAsErrors rebuild, and Invoke-MSTestWithCoverage with the repository runsettings unchanged. - Note (2026-10-02, coordinator ruling, AC17 option (a) approved): the intent of AC17 (the full suite through the standard route) is unchanged; only the evidence source for the test stage changes. The local test stage cannot complete on this workstation because `ShellUtilitiesStatic_Tests.GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension` fails deterministically with an invalid Win32 handle, a known environmental failure that reproduces on main; CI runs the shell-icon classes. The local fallback run passed 7361 of 7361 with coverage not below baseline (lines 85.35 to 85.36, branches 79.74 to 79.75). AC17 is checked off only from this pull request's own CI run on the final head, recording the run ID, the head SHA, the mstest-coverage job pass and fail counts and coverage figures, and the local fallback result. If CI fails any test, AC17 is not met. ## Risks & Mitigations From dd367e220fd80a83dfc0e03f107f7719ca335709 Mon Sep 17 00:00:00 2001 From: Dan Moisan Date: Fri, 2 Oct 2026 02:13:39 -0400 Subject: [PATCH 24/24] docs(950): record the final-head CI run in the AC17 evidence Co-Authored-By: Claude Opus 5.5 --- .../evidence/qa-gates/ac17-ci-evidence.md | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md index 429cc82bc..05cdd55b8 100644 --- a/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md +++ b/docs/features/active/2026-09-30-quickfiler-tests-depend-on-wall-clock-timing-950/evidence/qa-gates/ac17-ci-evidence.md @@ -40,6 +40,23 @@ Output Summary: ## Final-head confirmation -The commit that adds this file and checks off AC17 creates a new head whose diff against d46738a75 is documentation-only (this file, the AC17 checkbox in spec.md, and an addendum to evidence/other/ac-status-summary.md). The CI run on that final head, and its mstest-coverage figures, are recorded in the section below once available. +The commit that adds this file and checks off AC17 creates a new head whose diff against d46738a75 is documentation-only (this file, the AC17 checkbox in spec.md, and an addendum to evidence/other/ac-status-summary.md). The CI run on that head and its mstest-coverage figures are recorded below. -Final head: pending +Timestamp: 2026-10-02T06-25 +Command: gh run view 36972215199 --repo drmoisan/TaskMaster --log --job 110728374213 +EXIT_CODE: 0 + +- Head SHA: e591b11b72bf1c4682ee43171008935887f8c7e6 (adds this file and the AC17 check-off; no code change against d46738a75) +- Workflow run ID: 36972215199 (event pull_request), conclusion success; all seven jobs success +- mstest-coverage job ID: 110728374213 + +Output Summary: + +- Test Run Successful. +- Total tests: 7384 +- Passed: 7384 +- Failed: 0 (no Failed line printed) +- First-party coverage: lines 56612/65855 (85.96%), branches 13682/17078 (80.11%) +- GetFileIcon_WithUseFileType_ShouldReturnIconsForDirectoryAndFileExtension passed (two rows); R4 passed; both theme tests passed. + +Recording this run creates one further documentation-only commit (this section). That commit cannot name its own run; its CI result is confirmed on the pull request and reported in the orchestration final report. Because every commit after d46738a75 changes only files under this feature folder, the test and coverage figures above apply to the code on the final head unchanged.