Skip to content

Residual flake: NativeDocking_DockContextHooksResolveOnRealMount fixture still occasionally fails under AOT after #442 #445

@codemonkeychris

Description

@codemonkeychris

Summary

PR #442 (fix(selftest): drain dispatcher after UpdateLayout in Harness.Render) cut the flake rate of the five NativeDocking_* fixtures dramatically — main has been green for 8 consecutive CI runs since the merge — but did not fully eliminate the underlying race. Hit again on PR #444's CI:

Job: AOT Selftests — run 26617293434

not ok DockHooks_Host_Resolved - assertion failed
not ok DockHooks_Pane_TitleResolved - assertion failed
not ok DockHooks_Pane_KeyResolved - assertion failed
not ok DockHooks_IsActivePane_TrueWhenActive - assertion failed
# Total failures: 4

PR #444's changes are doc-only (templates + doc apps under docs/) plus five <snippet:...> comments in src/Reactor/Core/V1Protocol/ — byte-equivalent to no-ops for the C# compiler, cannot change runtime behavior. The failure pattern is identical to the original Mode-B cascade I diagnosed in PR #442.

A re-run of just the failed AOT job is in flight; statistically it will pass.

Root cause (from PR #442 investigation)

The four DockHooks_* asserts probe text rendered inside a Memo component nested in a DockManager pane. Pane bodies render through a WinUI TabView, which realizes the selected tab's content presenter via Normal-priority dispatcher work scheduled by the layout pass. When the harness's post-UpdateLayout dispatcher drain doesn't cover that scheduled work in time, the probe runs against an empty subtree and every initial probe fails as a cascade.

PR #442's fix (tests/Reactor.AppTests.Host/SelfTest/Harness.cs):

  • await WaitForIdleAsync()UpdateLayout()Low-priority dispatcher yieldUpdateLayout()Task.Delay(16)

That sequence wins 99%+ of the time and is what flipped main green. The residual rate looks like a path where TabView's content realization is posted at higher than Normal priority on AOT (where the dispatcher is fast enough to drain Low without the realization being queued yet), or where the realization completes only after Loaded, which the Low yield doesn't gate.

Proposed fix — defense-in-depth

Restore the per-fixture pump-until guards I removed from the five NativeDocking_* fixtures in PR #442. The pattern is the team's established shape for deferred-event flakes (#139, #149, #152, fix/selftest-gotfocus-pump-until):

for (int i = 0; i < 10 && H.FindText(\"alpha-host:ok\") is null; i++)
    await Harness.Render();
H.Check(\"DockHooks_Host_Resolved\", H.FindText(\"alpha-host:ok\") is not null);

Fixtures that need the guard (sentinel probe):

Fixture (file) Sentinel probe
TabGroupRendersToTabView (NativeDockingSmokeFixture.cs:94) H.FindText(\"native-body-alpha\")
DockContextHooksResolveOnRealMount (NativeDockingSmokeFixture.cs:133) H.FindText(\"alpha-host:ok\")
Composition_ContentMutationFlowsToActivePane (NativeDockingCompositionFixture.cs:25) H.FindText(\"count=0\")
UseEffectCleanup_BodyRemovedOnPaneClose (NativeDockingReliabilityFixture.cs:195) H.FindText(\"effect-body-p1\")
DynamicallyDockedComponentPage_WithOuterShellState (NativeDockingDynamicContentFixture.cs:~624) FindControl<Button>(b => b.Name == \"PixDoc_ToolbarOpenWelcome\")
CompositionDrivenDocumentsRespectKeyedReconciliation H.FindText(\"<first body sentinel>\")

Belt + suspenders: the harness fix carries the 99% case, the per-fixture pumps catch the residual 1% AOT timing window. Combined fail rate should round to zero.

Validation plan

  1. Apply the per-fixture pump-until guards to the six fixtures above (last one was missed in PR fix(selftest): drain dispatcher after UpdateLayout to fix local TabView race #442's local sweep — surfaced only on the second 10x run).
  2. Re-run tools/flake-loop.ps1 -Iterations 30 -Project tests/Reactor.SelfTests locally; expect 30/30.
  3. Land via PR; let CI exercise the AOT path over several green runs to confirm.

Repro / observation surface

  • Job pattern: AOT Selftests
  • Affected fixtures: any NativeDocking_* whose initial probe targets text/control rendered through DockManagerTabViewTabViewItem.ContentPresenter
  • Symptom: every initial assert in the fixture fails as a single cascade (count=4 / 8 / 1 depending on fixture)
  • Frequency since fix(selftest): drain dispatcher after UpdateLayout to fix local TabView race #442 merged: 1 hit in 9 PR-attached runs (≤12%)

Related

🤖 Filed via Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions