I1: give hermetic Codex runs a temp dir beside CODEX_HOME so Codex can create its PATH aliases

Codex 0.160 refuses to create its helper aliases (codex-linux-sandbox among
them) when CODEX_HOME sits under its temp dir, and the runner put the
hermetic home under the shard TMPDIR. Periodic run 37151477069 shows the
warning and 'Unable to spawn codex-linux-sandbox ... No viable candidates
found in PATH' for every exec_command in codex-sol-scope-termination.
This commit is contained in:
garrytan committed 2026-10-03 22:23:57 +00:00
1 parent aab8009b68
commit ed5260a254
2 files changed
+23 -1

No files matched your search

+17
View File
@@ -121,6 +121,23 @@ process.stdout.write(JSON.stringify({ type: 'turn.completed', usage: { input_tok
});
});
test('CODEX_HOME is never under the child temp dir, so Codex can create its PATH aliases', async () => {
const record = path.join(os.tmpdir(), `codex-lifecycle-child-env-${process.pid}.json`);
try {
await withFakeCodex(`
fs.writeFileSync(${JSON.stringify(record)}, JSON.stringify({ tmp: process.env.TMPDIR, codexHome: process.env.CODEX_HOME }));
`, async ({ skillDir }) => {
const result = await runCodexSkill({ skillDir, prompt: 'fixture', timeoutMs: 2_000 });
expect(result.exitCode).toBe(0);
const child = JSON.parse(fs.readFileSync(record, 'utf8')) as { tmp: string; codexHome: string };
expect(child.tmp).toBeTruthy();
expect(path.relative(child.tmp, child.codexHome).startsWith('..')).toBe(true);
});
} finally {
fs.rmSync(record, { force: true });
}
});
for (const exitCode of [2, 137]) {
test(`retains process exit ${exitCode}`, async () => {
await withFakeCodex(`process.exit(${exitCode});`, async ({ skillDir }) => {
+6 -1
View File
@@ -270,12 +270,17 @@ export async function runCodexSkill(opts: {
// itself; command subprocesses codex spawned survived as orphans holding
// our pipes open (the same blocked-drain hang the claude runner fixed —
// this copy never inherited that fix until now).
// Codex refuses to create its PATH aliases (codex-linux-sandbox among
// them) when CODEX_HOME sits under its temp dir, so the child gets a temp
// dir beside CODEX_HOME instead of the shard TMPDIR that holds tempHome.
const tempTmpDir = path.join(tempHome, 'tmp');
fs.mkdirSync(tempTmpDir, { recursive: true });
const proc = spawn('codex', args, {
cwd: cwd || skillDir,
stdio: ['ignore', 'pipe', 'pipe'],
detached: process.platform !== 'win32',
env: hermeticChildEnv(
{ HOME: tempHome, CODEX_HOME: tempCodexDir },
{ HOME: tempHome, CODEX_HOME: tempCodexDir, TMPDIR: tempTmpDir },
{ extraAllow: ['OPENAI_API_KEY', 'CODEX_*'] },
),
});