@@ -94,6 +94,16 @@ function injectShadowCleanupBusyFailures(
9494 } ;
9595}
9696
97+ function injectShadowPreflightReadBusyFailures (
98+ failuresBeforeSuccess = 2 ,
99+ ) : NodeJS . ProcessEnv {
100+ return {
101+ CODEX_MULTI_AUTH_TEST_SHADOW_PREFLIGHT_READ_BUSY_FAILURES : String (
102+ failuresBeforeSuccess ,
103+ ) ,
104+ } ;
105+ }
106+
97107function createFakeGlobalCodexInstall ( rootDir : string ) : string {
98108 const fakeBin = join ( rootDir , "@openai" , "codex" , "bin" , "codex.js" ) ;
99109 mkdirSync ( dirname ( fakeBin ) , { recursive : true } ) ;
@@ -600,6 +610,47 @@ describe("codex bin wrapper", () => {
600610 ) . toBe ( '{"last":"external-during-retry"}' ) ;
601611 } ) ;
602612
613+ it ( "retries preflight destination reads when the sync-back target is transiently locked" , ( ) => {
614+ const fixtureRoot = createWrapperFixture ( ) ;
615+ const fakeBin = createCustomFakeCodexBin ( fixtureRoot , [
616+ "#!/usr/bin/env node" ,
617+ 'const fs = require("node:fs");' ,
618+ 'const path = require("node:path");' ,
619+ 'const home = process.env.CODEX_HOME ?? "";' ,
620+ 'fs.writeFileSync(path.join(home, "auth.json"), \'{"token":"shadow"}\\n\', "utf8");' ,
621+ 'fs.writeFileSync(path.join(home, "accounts.json"), \'{"accounts":["shadow"]}\\n\', "utf8");' ,
622+ 'fs.writeFileSync(path.join(home, ".codex-global-state.json"), \'{"last":"shadow"}\\n\', "utf8");' ,
623+ "process.exit(0);" ,
624+ ] ) ;
625+ const originalHome = join ( fixtureRoot , "codex-home" ) ;
626+ const controlledTmp = join ( fixtureRoot , "tmp" ) ;
627+ mkdirSync ( originalHome , { recursive : true } ) ;
628+ mkdirSync ( controlledTmp , { recursive : true } ) ;
629+ writeFileSync ( join ( originalHome , "auth.json" ) , '{"token":"original"}\n' , "utf8" ) ;
630+ writeFileSync ( join ( originalHome , "accounts.json" ) , '{"accounts":["original"]}\n' , "utf8" ) ;
631+ writeFileSync ( join ( originalHome , ".codex-global-state.json" ) , '{"last":"original"}\n' , "utf8" ) ;
632+ writeFileSync ( join ( originalHome , "config.toml" ) , 'model_reasoning_effort = "xhigh"\n' , "utf8" ) ;
633+
634+ const result = runWrapper (
635+ fixtureRoot ,
636+ [ "exec" , "status" , "--model" , "gpt-5.1" ] ,
637+ {
638+ CODEX_MULTI_AUTH_REAL_CODEX_BIN : fakeBin ,
639+ CODEX_HOME : originalHome ,
640+ TMP : controlledTmp ,
641+ TEMP : controlledTmp ,
642+ TMPDIR : controlledTmp ,
643+ ...injectShadowCleanupBusyFailures ( 1 ) ,
644+ ...injectShadowPreflightReadBusyFailures ( 2 ) ,
645+ } ,
646+ ) ;
647+
648+ expect ( result . status ) . toBe ( 0 ) ;
649+ expect ( readFileSync ( join ( originalHome , "auth.json" ) , "utf8" ) . trim ( ) ) . toBe ( '{"token":"shadow"}' ) ;
650+ expect ( readFileSync ( join ( originalHome , "accounts.json" ) , "utf8" ) . trim ( ) ) . toBe ( '{"accounts":["shadow"]}' ) ;
651+ expect ( readFileSync ( join ( originalHome , ".codex-global-state.json" ) , "utf8" ) . trim ( ) ) . toBe ( '{"last":"shadow"}' ) ;
652+ } ) ;
653+
603654 it ( "rewrites unquoted config reasoning effort values for mini compatibility models" , ( ) => {
604655 const fixtureRoot = createWrapperFixture ( ) ;
605656 const fakeBin = createCustomFakeCodexBin ( fixtureRoot , [
0 commit comments