Skip to content

Branch preloads never outlive the codebase - #6337

Merged
pchiusano merged 4 commits into
trunkfrom
fix-flaky-mcp-cleanup
Nov 10, 2026
Merged

pchiusano merged 4 commits into
trunkfrom
fix-flaky-mcp-cleanup

Conversation

@pchiusano

Copy link
Copy Markdown
Member

Overview

Fixes a flaky Windows failure of the MCP runner test "a branch created by an earlier command can be named". It removed #6324 from the merge queue (run):

EXCEPTION!!!: ...\ucm-mcp-runner-test-31cceb5edf4b0451\.unison\v2\unison.sqlite3-wal:
removeDirectoryRecursive:...:DeleteFile "...\unison.sqlite3-wal": does not exist

The cause is in ucm, not the test:

  • Switching branches (Cli/Monad.hs) calls preloadProjectBranch, which called preloadBranch.
  • preloadBranch forked a thread with forkIO that nobody tracked. The thread opens its own SQLite connection to load the branch into the cache.
  • The test's transcript switches to a branch it just created. If the transcript ends while that thread is still loading, the codebase closes with the connection still open.
  • The test then deletes the codebase directory while that last connection closes. Closing the last connection makes SQLite delete the WAL file, so the file vanishes in the middle of the delete. On Windows that is an error.

Implementation approach and notes

  • sqliteCodebase opens a ki scope for the codebase's lifetime (withPreloads). The scope is inside the codebase lock.
  • preloadBranch forks into that scope. When the codebase closes, ki cancels any preload still running and waits for it to finish. No connection outlives the codebase.
  • A preload catches its own synchronous exceptions. A failure only means the branch isn't cached, as before, and doesn't propagate to the thread that opened the codebase.
  • parser-typechecker now depends on ki, which unison-cli already uses.

Test coverage

  • The existing test is the regression test. The race needs Windows to fail, so I couldn't reproduce it on macOS. The evidence for the fix is the explanation above.
  • The MCP runner tests pass on macOS, and so do the tests, transcripts, formatting and migration proofs.

Loose ends

None.

preloadBranch forked an untracked thread that opened its own connection. A
branch switch just before the codebase closed left that connection open (or
closing) after withCreatedCodebase returned, so deleting the codebase directory
raced SQLite deleting its WAL file: on Windows the MCP runner test 'a branch
created by an earlier command can be named' failed in the merge queue with
DeleteFile unison.sqlite3-wal: does not exist.

The preload threads are now children of a ki scope that sqliteCodebase opens
around the codebase's lifetime; closing it cancels any preload still running
and waits for it.
@pchiusano
pchiusano added this pull request to the merge queue Nov 10, 2026
@pchiusano

Copy link
Copy Markdown
Member Author

Cc @mitchellwrosen

Ki looks great btw I haven't really used it before.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Nov 10, 2026
# Conflicts:
#	.github/workflows/proofs/formatting/65c888d835d8453df021501c966d9f1850bc386c6a0f8c6e3ad7ef5c7df0f582.txt
#	.github/workflows/proofs/tests/f6c623e5124c43ae9fad43b4f432cefb5114650268d0b03464a3b484d82e588d.txt
#	.github/workflows/proofs/transcripts/94069090a42b3051b35c2dfd18d51af8f914dcf02bfd9833185020122964310f.txt
@pchiusano
pchiusano added this pull request to the merge queue Nov 10, 2026
Merged via the queue into trunk with commit 52469ef Nov 10, 2026
13 checks passed
@pchiusano
pchiusano deleted the fix-flaky-mcp-cleanup branch November 10, 2026 20:37
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