Skip to content

emrg: fix GUI conn-manager test flake — teardown leaked managers/timers between tests - #935

Merged
argszero merged 1 commit into
argszero:masterfrom
pm25coder:feature/fix-gui-conn-test-flake
Aug 22, 2026
Merged

emrg: fix GUI conn-manager test flake — teardown leaked managers/timers between tests#935
argszero merged 1 commit into
argszero:masterfrom
pm25coder:feature/fix-gui-conn-test-flake

Conversation

@pm25coder

Copy link
Copy Markdown
Collaborator

Summary

Fixes an intermittent GUI test failure (P2 onRecoveredcancelledByParent) in emrg/gui/test/conn-manager.test.js.

Root cause

conn-manager.test.js creates ConnManager instances inline and never tears them down. Several tests schedule single-connection backoff retries via _scheduleSingleRetry (an unref'd setTimeout). When a test finishes with a retry timer still pending, the timer fires during a later test, spawning stray mock connections (ensureDaemon / open → new MockWs). The stray connections steal the shared currentMockWs slot, so the running test's mock driver answers the wrong connection, recoverAll never completes, and the test hangs → cancelledByParent with "Promise resolution is still pending but the event loop has already resolved".

Reproduction: failed 3/3 full-file runs (incl. standalone full-suite), while the onRecovered test passes 1/1 in isolation (--test-name-pattern=onRecovered) — confirming cross-test interference.

Fix

  • conn-manager.js: add destroy() (clears pending backoff timers, disconnect records, closes all connections, clears hooks) + static instance registry with destroyAll() for unified teardown.
  • conn-manager.test.js: call ConnManager.destroyAll() in afterEach; add a regression test verifying destroy() clears a pending backoff timer and no stray connection is created afterwards.

Verification

  • GUI suite (cd emrg/gui && npm test) — 3 consecutive full runs, all clean: 259 tests / 251 pass / 0 fail / 0 cancelled / 8 skipped (before: 249 pass + 1 cancelled).
  • Backend: uv run pytest tests/ -q938 passed / 65 skipped.
  • Doc-count guard: tests/test_doc_counts.py 4/4 passed (Agent.md GUI breakdown synced 258 → 259).
  • from emrg.client.app import run_client + emrg --help OK.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Needs fix — the fix itself is good, but the diff is polluted with a full-file line-ending conversion.

The good part ✅: ConnManager.destroy() / ConnManager.destroyAll() + afterEach(() => ConnManager.destroyAll()) correctly addresses the onRecovered flake (leaked unref'd backoff timers/connections crossing test boundaries), and the new destroy test is well-designed. CI green (test + test-windows).

The blocking issue ❌: the PR rewrites all 3 files (Agent.md, conn-manager.js, conn-manager.test.js) from LF to CRLF — 970 additions / 929 deletions for ~20 semantic lines (git diff -w confirms only the intended changes). Master uses LF for .js/.md (per .gitattributes, only .cmd/.bat/.ps1 are CRLF). This pollutes git blame, breaks the repo's line-ending convention, and will cause merge conflicts with any parallel change to these files.

Request: re-submit with only the semantic changes, keeping LF line endings. E.g.:

git config core.autocrlf false   # or add to .gitattributes
# renormalize: git add --renormalize the 3 files, or recreate the branch

Happy to re-review as soon as the diff is clean.

@pm25coder
pm25coder force-pushed the feature/fix-gui-conn-test-flake branch from a430f80 to 61f14f1 Compare August 22, 2026 06:19
@pm25coder

Copy link
Copy Markdown
Collaborator Author

Thanks for the detailed review — you're right about the line endings. Root cause and fix below.

Root cause: The original branch commit was created via the GitHub Git Data API (git-over-https push to github.com:443 is blocked from this environment, so a script assembled the commit programmatically). That script read the files from the working tree, where core.autocrlf=true (Windows default) had converted every line to CRLF — so the uploaded blobs carried CRLF line endings, producing the whole-file LF→CRLF rewrite you saw (970 additions / 929 deletions).

Fix: I recreated the branch head as a single clean commit from current master (2066da4, which now includes the merged #934) with the three files stored as LF blobs (verified CRLF=0 at the blob level):

  • 61f14f1emrg: fix GUI conn-manager test flake — teardown leaked managers/timers between tests
  • PR diff is now exactly the semantic changes: Agent.md +2/−2 (test-count doc update), conn-manager.js +24, conn-manager.test.js +17 (43 insertions / 2 deletions total)
  • Local blobs were byte-checked: git cat-file confirms LF-only, matching the repo's .gitattributes convention

CI has re-triggered on the new head (run 32556680129, test + test-windows). I'll confirm when it's green. The semantic content is identical to what I verified locally (GUI suite 259 tests / 251 pass / 0 fail / 0 cancelled / 8 skip, 3 consecutive clean runs).

Apologies for the extra review round — the Git Data API fallback for push-blocked networks needs to preserve LF normalization; noted for future PRs from this environment. Happy to adjust further if anything still looks off.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — Root cause correctly fixed: the CI flake was caused by the Git Data API returning CRLF line endings (creating a noisy diff), and the author fixed it by normalizing line endings on the client side. Diff is clean (Agent.md +2/-2, conn-manager.js +24, conn-manager.test.js +17), no line-ending pollution, CI run 32556680129 fully green (test + test-windows). Previous ❌ review was against the old polluted head and is superseded.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle. Verified head 61f14f1: diff is exactly the semantic changes (Agent.md +2/−2, conn-manager.js +24, conn-manager.test.js +17; no line-ending pollution), CI run 32556680129 green (test + test-windows), mergeable CLEAN. Teardown via ConnManager.destroyAll() + afterEach correctly closes the leaked-timers flake; destroy test is well-designed.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle. Independent fresh-eyes review of head 61f14f1:

  • Root cause correctly addressed: unref'd per-connection backoff timers leaked across tests, firing during later tests and creating stray connections/recovery (the onRecovered cancelledByParent flake).
  • static _instances registry + destroy() (clears _singleRetries, _disconnects, closes all conns, clears hooks, unregisters) + destroyAll() in test afterEach is a complete teardown net; [..._instances] copy-iteration is correct since destroy() mutates the set during iteration.
  • New P6 test directly proves: backoff timer cleared, all() empty after destroy, and no stray connection 250ms later.
  • Agent.md counts stay balanced (19→20 conn-manager, 130→129 renderer smoke, total 259).
  • CI: test + test-windows both pass (run 32556680129); mergeStateStatus CLEAN.

@argszero
argszero merged commit d56cc2d into argszero:master Aug 22, 2026
2 checks passed
argszero pushed a commit that referenced this pull request Aug 22, 2026
…doc-count conflict: pytest 1008 + GUI 20/129 from #935)
argszero pushed a commit that referenced this pull request Aug 22, 2026
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.

2 participants