rouse-context/gradle
monkopedia-coder 0c9f67a606
#376: drain leaked test coroutines + route all setMain through MainDispatcherRule (#478)
* #376: drain leaked test coroutines + route all setMain through MainDispatcherRule

The TestMainDispatcher "Dispatchers.Main is used concurrently with setting
it" flake recurs because some test in the shared single-JVM unit-test run
leaves a coroutine alive that resumes a continuation onto Dispatchers.Main
during a later test's setMain/resetMain. CI run 27505633656 caught
AuditHistoryViewModelRoomTest as the victim: its MainDispatcherRule.resetMain()
raced a "reader" still dispatching on Dispatchers.IO (LimitedDispatcher$Worker
in the captured stack), so the leaker is another class, not the victim.

This closes the fire-and-forget coroutine leaks that were auditable and makes
every setMain deterministic:

- AppIntegrationTestHarness now cancelAndJoins its injected appScope
  (drainHarnessScope) instead of fire-and-forget cancel(), at stop() and the
  two mid-test reboot paths. This drains every harness-based integration test
  at once.
- The observer-family integration tests (Suppressed, PerCall, Runtime,
  MultiTool, ToolCallHappyPath, AuditRace) cancelAndJoin their Dispatchers.IO
  observer job before Main is reset.
- ToolCallViaSniPassthroughTest cancelAndJoins its tunnel scope.
- All 26 setMain-using test classes install the shared MainDispatcherRule, so
  setMain is paired with resetMain in a finally (no ad-hoc @Before/@After) and
  pairing is trivially auditable.

Verification of full flake elimination, plus the forkEvery structural-backstop
trade-off, are discussed in the PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add Gradle test-retry as the cheap CI backstop for the #376 flake

Per Jason: ship the #376 correctness fix (drain leaked coroutines + route
setMain through the rule) WITHOUT the ~7x forkEvery=1 tax, and add the
test-retry plugin instead. It retries only FAILED tests in a fresh JVM, so an
intermittent leak-induced failure passes on the isolated retry while a real
deterministic failure still fails every attempt (no masking). Only acts on
failure -> the green path is unaffected. Applied to all Test tasks (also
neutralizes the known OAuth integration flake).

---------

Co-authored-by: Monkopedia <monkopedia@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:02:04 -04:00
..
wrapper Fix #145 (phase 1): Bump Gradle wrapper 8.14 -> 8.14.4 2026-04-14 22:56:19 -04:00
libs.versions.toml #376: drain leaked test coroutines + route all setMain through MainDispatcherRule (#478) 2026-06-14 18:02:04 -04:00