Observed
Taskdeck.Application.Tests.Services.ArtefactExtractionGateTests.Saturated_RejectsExcess_NeverQueues_AndNeverExceedsCap failed once on Backend Unit / Backend Unit (windows-latest) for the documentation-only PR #2947 at head 5eb4d7b9561f1d528524eb6827c778b50634b16f (run 34624770817, job 103347294234, 2026-09-11T16:58Z). The Ubuntu leg and every other Application test passed (4342/4343). The diff touches only docs/STATUS.md and docs/releases/V0_3_0_READINESS.md, so the change cannot have caused it.
The failing assertion is the entry latch at backend/tests/Taskdeck.Application.Tests/Services/ArtefactExtractionGateTests.cs:128:
allEntered.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue("exactly the cap should enter the extractor");
Why it is timing-sensitive
The test submits 8 ExtractAsync calls and expects exactly cap = 2 workers to enter a LatchedCountingExtractor that blocks synchronously on a ManualResetEventSlim. Entry depends on the thread pool scheduling the two admitted continuations within 10 s while the other 6 submissions are also being processed. On a starved hosted Windows runner (thread-pool injection is roughly one thread per 500 ms once the minimum is exhausted, and the admitted workers block pool threads), the latch can miss the 10 s window without any product defect. The assertion is a liveness bound on the test harness, not on the gate.
Acceptance
- Make the entry wait independent of thread-pool injection rate: for example, make
LatchedCountingExtractor await an async signal (TaskCompletionSource) instead of blocking a pool thread, or raise ThreadPool.SetMinThreads for the test, or widen the entry budget with a rationale comment.
- Keep the assertions that matter (max concurrency equals the cap, no queueing, every permit returned).
- Do not weaken the gate semantics under test.
Disposition
Non-blocking for #2947 (docs-only). The failed job is being re-run once as the single permitted re-measure; the outcome will be recorded on the PR thread. Filed under global law 2c as a tracked finding rather than a fix-commit on an unrelated PR.
Observed
Taskdeck.Application.Tests.Services.ArtefactExtractionGateTests.Saturated_RejectsExcess_NeverQueues_AndNeverExceedsCapfailed once onBackend Unit / Backend Unit (windows-latest)for the documentation-only PR #2947 at head5eb4d7b9561f1d528524eb6827c778b50634b16f(run 34624770817, job 103347294234, 2026-09-11T16:58Z). The Ubuntu leg and every other Application test passed (4342/4343). The diff touches onlydocs/STATUS.mdanddocs/releases/V0_3_0_READINESS.md, so the change cannot have caused it.The failing assertion is the entry latch at
backend/tests/Taskdeck.Application.Tests/Services/ArtefactExtractionGateTests.cs:128:Why it is timing-sensitive
The test submits 8
ExtractAsynccalls and expects exactlycap = 2workers to enter aLatchedCountingExtractorthat blocks synchronously on aManualResetEventSlim. Entry depends on the thread pool scheduling the two admitted continuations within 10 s while the other 6 submissions are also being processed. On a starved hosted Windows runner (thread-pool injection is roughly one thread per 500 ms once the minimum is exhausted, and the admitted workers block pool threads), the latch can miss the 10 s window without any product defect. The assertion is a liveness bound on the test harness, not on the gate.Acceptance
LatchedCountingExtractorawait an async signal (TaskCompletionSource) instead of blocking a pool thread, or raiseThreadPool.SetMinThreadsfor the test, or widen the entry budget with a rationale comment.Disposition
Non-blocking for #2947 (docs-only). The failed job is being re-run once as the single permitted re-measure; the outcome will be recorded on the PR thread. Filed under global law 2c as a tracked finding rather than a fix-commit on an unrelated PR.