@@ -137,6 +137,25 @@ async function waitForPolicyJob(
137137 throw new Error ( "policy job did not finish" ) ;
138138}
139139
140+ /** Windows can keep SQLite/job handles briefly after stop; retry only transient cleanup codes. */
141+ function removeTree ( path : string ) : void {
142+ let lastError : unknown ;
143+ for ( let attempt = 0 ; attempt < 50 ; attempt += 1 ) {
144+ try {
145+ rmSync ( path , { recursive : true , force : true } ) ;
146+ return ;
147+ } catch ( error ) {
148+ const code = error && typeof error === "object" && "code" in error
149+ ? String ( error . code )
150+ : "" ;
151+ if ( ! new Set ( [ "EPERM" , "EBUSY" , "ENOTEMPTY" ] ) . has ( code ) ) throw error ;
152+ lastError = error ;
153+ Bun . sleepSync ( 50 ) ;
154+ }
155+ }
156+ throw lastError ;
157+ }
158+
140159beforeEach ( ( ) => {
141160 previousHome = process . env . OPENCODEX_HOME ;
142161 isolatedCodexHome = installIsolatedCodexHome ( "ocx-storage-mutation-race-codex-" ) ;
@@ -161,7 +180,7 @@ afterEach(() => {
161180 else process . env . OPENCODEX_HOME = previousHome ;
162181 isolatedCodexHome ?. restore ( ) ;
163182 isolatedCodexHome = null ;
164- if ( testDir ) rmSync ( testDir , { recursive : true , force : true } ) ;
183+ if ( testDir ) removeTree ( testDir ) ;
165184 testDir = "" ;
166185} ) ;
167186
@@ -231,7 +250,7 @@ describe("storage mutation coordinator", () => {
231250
232251 const server = startServer ( 0 ) ;
233252 try {
234- await enablePolicyAndRun ( server . url ) ;
253+ const { startedAt } = await enablePolicyAndRun ( server . url ) ;
235254 await Bun . sleep ( 80 ) ;
236255
237256 const preview = await previewDigest ( server . url , 50 ) ;
@@ -250,6 +269,10 @@ describe("storage mutation coordinator", () => {
250269 } ) ;
251270 expect ( restoreRes . status ) . toBe ( 409 ) ;
252271 expect ( ( await restoreRes . json ( ) ) . error ) . toBe ( "storage_mutation_busy" ) ;
272+
273+ // Drain the blocked policy job before stop/teardown — leaving it mid-block leaves
274+ // Windows holding OPENCODEX_HOME (SQLite/job handles) and afterEach rmSync fails EBUSY.
275+ await waitForPolicyJob ( server . url , startedAt ) ;
253276 } finally {
254277 await server . stop ( true ) ;
255278 }
0 commit comments