Summary
UtilitiesCS.Test/Threading/TimeOutTask_AdditionalTests.cs contains tests that race a real 100 ms wall-clock deadline against exception propagation. On a loaded CI runner the deadline can win, so the test observes TimeoutException instead of the expected InvalidOperationException and fails. This is a flaky test, not a product defect, and it fails CI on unrelated pull requests.
Environment
- OS/version:
windows-latest GitHub Actions runner (also reproducible in principle on any loaded machine)
- Runtime: .NET Framework 4.8.1, MSTest via
vstest.console.exe
- Command/flags used: the CI job "Format, build, analyze, and test", which runs the MSTest suite with coverage
- Data source or fixture:
UtilitiesCS.Test/Threading/TimeOutTask_AdditionalTests.cs
Steps to Reproduce
- Open any pull request that does not touch
UtilitiesCS.Test/Threading/ and let CI run the full MSTest suite.
- Observe the run under runner load.
- Compare against a local run of the same test on an idle machine.
Observed on 2026-08-08 in run 31275248280 for PR #514 (issue #438): 6350 total, 6349 passed, 1 failed.
Expected Behavior
The test deterministically asserts that a source exception set after TimeoutAfter is attached propagates as InvalidOperationException, independent of machine load. Per .claude/rules/general-unit-test.md, tests must be deterministic and real wall-clock waits are a banned API in test code.
Actual Behavior
TimeoutAfter_GenericTask_ShouldPropagateFaultedSourceException_WhenSourceFaultsLater failed on the CI runner with:
System.TimeoutException: The operation has timed out.
The same test passes locally on an idle machine on the same commit, confirming a load-dependent race rather than a functional regression.
The test attaches a 100 ms timeout and then sets the exception:
var source = new TaskCompletionSource<int>();
var proxy = source.Task.TimeoutAfter(100);
source.SetException(new InvalidOperationException("boom"));
Func<Task> act = async () => await proxy;
await act.Should().ThrowAsync<InvalidOperationException>().WithMessage("boom");
If the runner does not schedule the continuation within 100 ms, the timeout path completes first and the assertion sees the wrong exception type.
Logs / Screenshots
Note the reported duration of 174 ms against a 100 ms deadline, which is the race made visible.
Impact / Severity
This fails CI on pull requests that have nothing to do with the code under test, and the only available remedy today is to re-run the job. That is corrosive in two ways: it blocks unrelated work, and it normalizes re-running a red suite until it turns green - the exact habit that allows a genuine regression to be waved through. The sibling test TimeoutAfter_NonGenericTask_ShouldPropagateFaultedSourceException_WhenSourceFaultsLater has the identical structure and the same latent failure mode.
Source
From: docs/features/potential/2026-08-08-timeoutafter-tests-race-real-wall-clock-deadline.md
Summary
UtilitiesCS.Test/Threading/TimeOutTask_AdditionalTests.cscontains tests that race a real 100 ms wall-clock deadline against exception propagation. On a loaded CI runner the deadline can win, so the test observesTimeoutExceptioninstead of the expectedInvalidOperationExceptionand fails. This is a flaky test, not a product defect, and it fails CI on unrelated pull requests.Environment
windows-latestGitHub Actions runner (also reproducible in principle on any loaded machine)vstest.console.exeUtilitiesCS.Test/Threading/TimeOutTask_AdditionalTests.csSteps to Reproduce
UtilitiesCS.Test/Threading/and let CI run the full MSTest suite.Observed on 2026-08-08 in run
31275248280for PR #514 (issue #438):6350total,6349passed,1failed.Expected Behavior
The test deterministically asserts that a source exception set after
TimeoutAfteris attached propagates asInvalidOperationException, independent of machine load. Per.claude/rules/general-unit-test.md, tests must be deterministic and real wall-clock waits are a banned API in test code.Actual Behavior
TimeoutAfter_GenericTask_ShouldPropagateFaultedSourceException_WhenSourceFaultsLaterfailed on the CI runner with:The same test passes locally on an idle machine on the same commit, confirming a load-dependent race rather than a functional regression.
The test attaches a 100 ms timeout and then sets the exception:
If the runner does not schedule the continuation within 100 ms, the timeout path completes first and the assertion sees the wrong exception type.
Logs / Screenshots
Failed TimeoutAfter_GenericTask_ShouldPropagateFaultedSourceException_WhenSourceFaultsLater [174 ms]/Error Message: System.TimeoutException: The operation has timed out.Full log: https://github.com/drmoisan/TaskMaster/actions/runs/31275248280/job/93147648583Note the reported duration of 174 ms against a 100 ms deadline, which is the race made visible.
Impact / Severity
This fails CI on pull requests that have nothing to do with the code under test, and the only available remedy today is to re-run the job. That is corrosive in two ways: it blocks unrelated work, and it normalizes re-running a red suite until it turns green - the exact habit that allows a genuine regression to be waved through. The sibling test
TimeoutAfter_NonGenericTask_ShouldPropagateFaultedSourceException_WhenSourceFaultsLaterhas the identical structure and the same latent failure mode.Source
From: docs/features/potential/2026-08-08-timeoutafter-tests-race-real-wall-clock-deadline.md