Skip to content

Stop the tests losing entries to a race in their own harness - #50

Merged
amrali-eg merged 1 commit into
masterfrom
fix/entry-sink-on-master
Aug 27, 2026
Merged

Stop the tests losing entries to a race in their own harness#50
amrali-eg merged 1 commit into
masterfrom
fix/entry-sink-on-master

Conversation

@amrali-eg

Copy link
Copy Markdown
Owner

The CI flake was the test harness losing entries

AFileChangedAfterTheConfirmation_StopsTheWholeRun failed on master reporting Converted where it expected PlanWentStale.

The staleness check did not fail. The file modified after planning had never reached the plan, and FindStaleFiles only inspects files a plan actually schedules — so there was nothing to find stale.

Cause

ScanDirectory and ConvertFiles invoke onEntry concurrently from worker threads, and say so:

onEntry is invoked concurrently from worker threads; callers must synchronise.

Both production callers use a ConcurrentBag. The tests passed List<T>.Add.

Measured, not assumed

A throwaway probe scanning 200 files, 40 times, comparing a List collector against a ConcurrentBag over identical scans:

attempts with a wrong count: 3/40; deltas: 1,1,2

Entries were being dropped. It did not reproduce in 12 local runs of the failing test, which is what a narrow race looks like.

Why this is worse in a test than in the product

A dropped entry does not throw. It quietly removes a file from what the test then asserts about, so the test still passes while asserting less than it claims. Any of these could have been silently weakened; only the one whose subject was the dropped file failed visibly.

Fix

EntrySink collects into a ConcurrentBag and enumerates in path order, so tests reading several entries also get a fixed order. Every onEntry callback in the suite now uses it — 20 test files.

The stale-plan test now asserts that both files reached the plan. That is the assertion that should fail if this regresses, rather than the outcome quietly changing.

No production code is touched. The concurrent onEntry contract is correct and documented; the tests were not honouring it.

422 passing.

🤖 Generated with Claude Code

ScanDirectory and ConvertFiles invoke onEntry concurrently from worker threads
and document that the caller must synchronise. Both production callers use a
ConcurrentBag. The tests passed List<T>.Add.

Measured before fixing: over 200 files, 3 runs in 40 collected fewer entries than
were reported, losing one or two each time.

In a test that is worse than in the product. A dropped entry does not throw; it
quietly removes a file from what the test then asserts about, so the test still
passes while asserting less than it claims. It surfaced in CI as
AFileChangedAfterTheConfirmation_StopsTheWholeRun reporting Converted instead of
PlanWentStale - not because the staleness check failed, but because the file
modified after planning had never reached the plan, and FindStaleFiles only looks
at files a plan schedules.

EntrySink collects into a ConcurrentBag and enumerates in path order, so tests
reading several entries also get a fixed order. Every onEntry callback in the
suite now uses it.

The stale-plan test now states that both files reached the plan. That assertion
is the one that should fail if this ever regresses, rather than the outcome
quietly changing.

432 passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@amrali-eg
amrali-eg merged commit a201a08 into master Aug 27, 2026
1 check passed
@amrali-eg
amrali-eg deleted the fix/entry-sink-on-master branch August 27, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant