fix(test): isolate provider homes outside native Codex e2e files (#1586)

The test setup left `CLAUDE_CONFIG_DIR` and the passwd home outside the fixture, and exporting `OPENRIG_E2E_REAL_CODEX=1` turned off home isolation for the whole run, so ordinary suites could read or write a developer's real provider homes.

Ordinary suites now stay fixture-scoped even with the real-Codex opt-in exported; the exception is limited to the native Codex end-to-end files. Inherited provider roots (`CODEX_HOME`, `CLAUDE_CONFIG_DIR`) are isolated, while a provider home a test passes explicitly in its `baseEnv` is kept, so scenario environments that set one on purpose still work. Default Codex thread discovery now follows `HOME` (`os.homedir()`) instead of the passwd entry, so it stays inside the fixture too. `docs/as-built/test-layers.md` describes the layers.

Thanks to @AdamWhitehurst for the report and to @Aschenbath for the fix.
This commit is contained in:
Nash Aschenbath authored and GitHub committed 2026-10-21 00:57:54 -07:00
1 parent d2a6704788
commit 8bad2f5a01
11 files changed
+168 -38

No files matched your search

+7 -2
View File
@@ -71,8 +71,13 @@ are not yet runnable checks.
database, workspace and topology roots, seat identity, bearer tokens), forces a fixture
`OPENRIG_HOME` and fails if the home is not fixture-scoped. It also wraps `fetch`, so a real
request to anything other than a loopback ephemeral-port fixture or a registered target fails.
Unless `OPENRIG_E2E_REAL_CODEX=1`, it points `HOME` and the XDG directories at a fixture user
home and clears `CODEX_HOME`; that opt-in keeps the real homes. A test can still set its own
It points `HOME` and the XDG directories at a fixture user home and clears `CODEX_HOME` and
`CLAUDE_CONFIG_DIR`. `OPENRIG_E2E_REAL_CODEX=1` keeps native homes and binaries only for
`seat-handover-model-fidelity-e2e.test.ts` and `seat-lifecycle-set-model-resume-e2e.test.ts`;
all other files remain isolated. The scenario scaffold always uses fixture homes and runtime stubs.
The two native files rely on Vitest's default per-file isolation; runs without it are unsupported.
Scenario scaffolds scrub inherited provider roots but preserve those passed in an explicit `baseEnv`.
A test can still set its own
overrides after setup. The UI suite (jsdom) uses its own `packages/ui/test/setup.ts`.
- **Stale vendored daemon.** `npm run build:package` leaves an assembled copy of the daemon at
+3 -11
View File
@@ -203,7 +203,7 @@ export class CodexThreadIdResolver {
const readFromLogs = injected
? (p: number, home: string, report?: CodexCaptureReporter) => injected(p, home, report, startSecond)
: (p: number, home: string, report?: CodexCaptureReporter) => readCodexThreadIdFromLogs(p, home, undefined, minTs, undefined, report, startSecond);
const defaultHome = this.opts.defaultHome ?? safeUserHomeDir() ?? os.homedir();
const defaultHome = this.opts.defaultHome ?? os.homedir();
const now = this.opts.now ?? Date.now;
const ttl = this.opts.homeTtlMs ?? 60_000;
@@ -285,19 +285,11 @@ void _resolveRequiresIdentityPin;
function uniqueHomes(candidateHomes: Array<string | undefined>): string[] {
const homes = candidateHomes.filter((home): home is string => Boolean(home));
const userHome = safeUserHomeDir();
if (userHome) homes.push(userHome);
// os.homedir() honors HOME. The passwd home must not bypass an explicit provider/test home.
homes.push(os.homedir());
return [...new Set(homes)];
}
function safeUserHomeDir(): string | undefined {
try {
return os.userInfo().homedir;
} catch {
return undefined;
}
}
/** How a gated read treats rows logged inside the identity's own start second (see {@link lstartToMinTs}). */
export type StartSecondRows =
/** Exclude them: the default, for every caller that can wait for a later row. */
+39 -3
View File
@@ -2,11 +2,15 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import Database from "better-sqlite3";
import { afterEach, describe, expect, it } from "vitest";
import { CodexThreadIdResolver, lstartToMinTs } from "../src/domain/codex-thread-id.js";
import { afterEach, describe, expect, it, vi } from "vitest";
import { CodexThreadIdResolver, lstartToMinTs, readCodexThreadIdFromCandidateHomes } from "../src/domain/codex-thread-id.js";
const roots: string[] = [];
afterEach(() => { for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); });
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllEnvs();
for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
});
const identity = "Sun Sep 6 23:52:32 2026";
const start = lstartToMinTs(identity)!;
function fixture() {
@@ -24,6 +28,38 @@ function fixture() {
};
}
describe("Codex home discovery respects an explicit HOME", () => {
it("reads the default home before resolving a process home and never consults passwd", async () => {
const home = fs.mkdtempSync(path.join(os.tmpdir(), "codex-default-home-")); roots.push(home);
vi.stubEnv("HOME", home);
const passwd = vi.spyOn(os, "userInfo").mockImplementation(() => { throw new Error("passwd home must not be read"); });
const processHome = vi.fn(() => "/synthetic-other-home");
const read = vi.fn((_pid: number, candidate: string) => candidate === home ? "conversation" : undefined);
const resolver = new CodexThreadIdResolver({ readFromLogs: read, resolveHomeDirByPid: processHome });
expect(await resolver.resolve(60422, identity)).toBe("conversation");
expect(read).toHaveBeenCalledExactlyOnceWith(60422, home, expect.any(Function), "exclude");
expect(processHome).not.toHaveBeenCalled();
expect(passwd).not.toHaveBeenCalled();
});
it("candidate-home fallback finds fixture logs without searching the passwd home", () => {
const f = fixture(); f.thread("conversation"); f.log("conversation"); f.finish();
vi.stubEnv("HOME", f.home);
const passwd = vi.spyOn(os, "userInfo").mockImplementation(() => { throw new Error("passwd home must not be read"); });
expect(readCodexThreadIdFromCandidateHomes(60422, [])).toBe("conversation");
expect(passwd).not.toHaveBeenCalled();
});
it("an empty fixture home never resolves another home's conversation", () => {
const foreign = fixture(); foreign.thread("foreign-conversation"); foreign.log("foreign-conversation"); foreign.finish();
const home = fs.mkdtempSync(path.join(os.tmpdir(), "codex-empty-home-")); roots.push(home);
vi.stubEnv("HOME", home);
const passwd = vi.spyOn(os, "userInfo").mockReturnValue({ homedir: foreign.home } as ReturnType<typeof os.userInfo>);
expect(readCodexThreadIdFromCandidateHomes(60422, [])).toBeUndefined();
expect(passwd).not.toHaveBeenCalled();
});
});
describe("native conversation identity excludes same-process auxiliary threads", () => {
it("resolves the first native conversation while title generation is the newest log activity", async () => {
const f = fixture(); f.thread("conversation"); f.log("conversation"); f.log("title", start + 2);
@@ -21,7 +21,10 @@ function runSetup(env: Record<string, string | undefined> = {}) {
env: { PATH: process.env.PATH, TMPDIR: tmp, ...env },
});
expect(child.status, child.stderr).toBe(0);
const homes = JSON.parse(child.stdout.trim().split("\n").pop()!) as { openrigHome: string; home: string; tmpdir: string; scratch: string };
const homes = JSON.parse(child.stdout.trim().split("\n").pop()!) as {
openrigHome: string; home: string; tmpdir: string; scratch: string;
osHome: string; codexHome?: string; claudeConfigDir?: string; xdgConfig: string; codexVersion: string;
};
return { tmp, ...homes };
}
const fixtureEntries = (tmp: string) => fs.readdirSync(tmp).filter((name) => name.startsWith(".openrig-test-fixture-home-"));
@@ -29,7 +32,22 @@ const fixtureEntries = (tmp: string) => fs.readdirSync(tmp).filter((name) => nam
* Not tsx's loader cache, which the child's launcher makes before the setup runs. */
const leftovers = (tmp: string) => fs.readdirSync(tmp).filter((name) => !name.startsWith("tsx-"));
describe.skipIf(process.env["OPENRIG_E2E_REAL_CODEX"] === "1")("fixture homes are removed when the worker exits", () => {
describe("fixture homes are removed when the worker exits", () => {
it.each([undefined, "1"])("isolates provider homes and runtime lookup with native opt-in=%s", optIn => {
const { tmp, openrigHome, home, osHome, codexHome, claudeConfigDir, xdgConfig, codexVersion } = runSetup({
HOME: "/synthetic-host-home", CODEX_HOME: "/synthetic-host-codex",
CLAUDE_CONFIG_DIR: "/synthetic-host-claude", XDG_CONFIG_HOME: "/synthetic-host-xdg",
OPENRIG_E2E_REAL_CODEX: optIn,
});
expect(home).toBe(`${openrigHome}-user-home`);
expect(osHome).toBe(home);
expect(codexHome).toBeUndefined();
expect(claudeConfigDir).toBeUndefined();
expect(xdgConfig).toBe(path.join(home, ".config"));
expect(codexVersion).toBe("codex-cli 0.139.0");
expect(leftovers(tmp)).toEqual([]);
});
it("removes the fixture home and the user home", () => {
const { tmp, openrigHome, home } = runSetup();
expect(path.dirname(openrigHome)).toBe(tmp);
+7 -1
View File
@@ -3,11 +3,17 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import "../../../../test/hermetic-env.setup.js";
// What a test does: make a temp dir the usual way, with os.tmpdir().
const scratch = fs.mkdtempSync(path.join(os.tmpdir(), "test-scratch-"));
fs.writeFileSync(path.join(scratch, "file"), "x");
console.log(JSON.stringify({ openrigHome: process.env["OPENRIG_HOME"], home: process.env["HOME"], tmpdir: os.tmpdir(), scratch }));
console.log(JSON.stringify({
openrigHome: process.env["OPENRIG_HOME"], home: process.env["HOME"], tmpdir: os.tmpdir(), scratch,
osHome: os.homedir(), codexHome: process.env["CODEX_HOME"], claudeConfigDir: process.env["CLAUDE_CONFIG_DIR"],
xdgConfig: process.env["XDG_CONFIG_HOME"],
codexVersion: execFileSync("codex", ["--version"], { encoding: "utf-8", timeout: 5000 }).trim(),
}));
// With "wait", stay alive for the test to signal: a vitest worker is ended with SIGTERM while it is running.
if (process.argv[2] === "wait") setInterval(() => {}, 1000);
+9 -4
View File
@@ -26,7 +26,7 @@ import {
} from "node:fs";
import { tmpdir } from "node:os";
import { delimiter, join } from "node:path";
import { realRuntimesRequested, writeRuntimeStubs } from "../../../../test/stub-runtimes.js";
import { writeRuntimeStubs } from "../../../../test/stub-runtimes.js";
/**
* Inherited environment variables that redirect the CLI/daemon client at a
@@ -280,7 +280,7 @@ export interface HermeticScaffold {
}
export interface PrepareHermeticEnvOptions {
/** Base environment to derive the clean child env from. Defaults to process.env. */
/** Base environment to derive the clean child env from. Explicit provider roots are preserved; defaults to process.env. */
baseEnv?: EnvLike;
/**
* The A3-R3 injected clock. When set, the scaffold's child env carries
@@ -331,8 +331,8 @@ export function prepareHermeticEnv(opts: PrepareHermeticEnvOptions = {}): Hermet
mkdirSync(dir, { recursive: true });
}
// codex and claude resolve to stubs inside the scaffold, so a scenario daemon never runs the host's
// real binaries (they can hang under a scratch HOME). Tests that opt into real runtimes keep them.
const runtimeStubDir = realRuntimesRequested(baseEnv) ? undefined : writeRuntimeStubs(root);
// real binaries (they can hang under a scratch HOME). Native Codex e2e tests use their own harness.
const runtimeStubDir = writeRuntimeStubs(root);
// Test-only command seam: every child `tmux` call gets one explicit private
// socket. TMUX_TMPDIR remains for compatibility, but is never the selector.
@@ -358,6 +358,11 @@ process.exit(result.status ?? 1);
const env: Record<string, string | undefined> = { ...baseEnv };
for (const name of DAEMON_TARGET_ENV_VARS) delete env[name];
for (const name of CREDENTIAL_ENV_VARS) delete env[name];
// Inherited provider roots belong to the runner; explicit ones are test inputs.
if (opts.baseEnv === undefined) {
for (const name of ["CODEX_HOME", "CLAUDE_CONFIG_DIR"]) delete env[name];
}
delete env.OPENRIG_E2E_REAL_CODEX;
// Scrub any injected-clock var (defense in depth behind the fail-closed guard) so
// only an explicit injectClockNow can set it below — never an inherited value.
for (const name of TEST_CLOCK_ENV_VARS) delete env[name];
+31 -1
View File
@@ -1,4 +1,4 @@
import { describe, it, expect, afterEach } from "vitest";
import { describe, it, expect, afterEach, vi } from "vitest";
import { existsSync, statSync } from "node:fs";
import { delimiter, join } from "node:path";
import {
@@ -16,6 +16,7 @@ import {
describe("prepareHermeticEnv scaffold", () => {
const scaffolds: HermeticScaffold[] = [];
afterEach(() => {
vi.unstubAllEnvs();
for (const s of scaffolds.splice(0)) s.cleanup();
});
const make = (baseEnv?: Record<string, string | undefined>) => {
@@ -88,6 +89,35 @@ describe("prepareHermeticEnv scaffold", () => {
expect(s.env).not.toBe(base);
});
it("scrubs inherited provider roots and the native opt-in from scenario children", () => {
vi.stubEnv("CODEX_HOME", "/synthetic-host-codex");
vi.stubEnv("CLAUDE_CONFIG_DIR", "/synthetic-host-claude");
vi.stubEnv("OPENRIG_E2E_REAL_CODEX", "1");
const s = make();
expect(s.env.HOME).toBe(s.home);
for (const key of ["CODEX_HOME", "CLAUDE_CONFIG_DIR", "OPENRIG_E2E_REAL_CODEX"]) {
expect(s.env[key]).toBeUndefined();
}
expect(process.env.CODEX_HOME).toBe("/synthetic-host-codex");
expect(process.env.CLAUDE_CONFIG_DIR).toBe("/synthetic-host-claude");
expect(process.env.OPENRIG_E2E_REAL_CODEX).toBe("1");
});
it("preserves explicit provider roots but still scrubs the native opt-in", () => {
const base = {
HOME: "/synthetic-host-home", CODEX_HOME: "/synthetic-host-codex",
CLAUDE_CONFIG_DIR: "/synthetic-host-claude", OPENRIG_E2E_REAL_CODEX: "1",
};
const s = make(base);
expect(s.env.HOME).toBe(s.home);
expect(s.env.CODEX_HOME).toBe(base.CODEX_HOME);
expect(s.env.CLAUDE_CONFIG_DIR).toBe(base.CLAUDE_CONFIG_DIR);
expect(s.env.OPENRIG_E2E_REAL_CODEX).toBeUndefined();
expect(base.CODEX_HOME).toBe("/synthetic-host-codex");
expect(base.CLAUDE_CONFIG_DIR).toBe("/synthetic-host-claude");
expect(base.OPENRIG_E2E_REAL_CODEX).toBe("1");
});
it("cleanup removes the scaffold root", () => {
const s = prepareHermeticEnv({ baseEnv: { HOME: "/real-home" } });
const root = s.root;
+7 -5
View File
@@ -428,7 +428,8 @@ describe("createDaemon startup composition", () => {
});
it("passes daemon CLI reachability env and PATH into launched tmux sessions", async () => {
vi.stubEnv("PATH", "/proof/openrig/bin:/usr/bin:/bin");
const launchPath = `/proof/openrig/bin${path.delimiter}${process.env.PATH}`;
vi.stubEnv("PATH", launchPath);
vi.stubEnv("OPENRIG_PORT", "17433");
vi.stubEnv("OPENRIG_HOST", "127.0.0.1");
const cmuxFactory: CmuxTransportFactory = async () => {
@@ -448,7 +449,7 @@ describe("createDaemon startup composition", () => {
.map((call) => call[0])
.find((cmd) => cmd.includes("tmux new-session"));
expect(newSessionCmd).toBeDefined();
expect(newSessionCmd).toContain("-e 'PATH=/proof/openrig/bin:/usr/bin:/bin'");
expect(newSessionCmd).toContain(`-e 'PATH=${launchPath}'`);
expect(newSessionCmd).toContain("-e 'OPENRIG_PORT=17433'");
expect(newSessionCmd).toContain("-e 'OPENRIG_HOST=127.0.0.1'");
@@ -465,7 +466,8 @@ describe("createDaemon startup composition", () => {
const saved = saveEnv("HOME", "CODEX_HOME", "PATH", "OPENRIG_RECOVERY_PROVIDER_AUTH_ENV_ALLOWLIST", "OPENAI_API_KEY");
process.env.HOME = daemonHome;
delete process.env.CODEX_HOME;
process.env.PATH = "/proof/openrig/bin:/usr/bin:/bin";
const launchPath = `/proof/openrig/bin${path.delimiter}${process.env.PATH}`;
process.env.PATH = launchPath;
process.env.OPENRIG_RECOVERY_PROVIDER_AUTH_ENV_ALLOWLIST = "OPENAI_API_KEY";
process.env.OPENAI_API_KEY = "gap7-openai-key";
const tmuxExec = vi.fn<ExecFn>(async () => "");
@@ -479,7 +481,7 @@ describe("createDaemon startup composition", () => {
expect(result.deps.sessionEnv).toMatchObject({
HOME: daemonHome,
CODEX_HOME: path.join(daemonHome, ".codex"),
PATH: "/proof/openrig/bin:/usr/bin:/bin",
PATH: launchPath,
OPENAI_API_KEY: "gap7-openai-key",
});
@@ -489,7 +491,7 @@ describe("createDaemon startup composition", () => {
const command = tmuxExec.mock.calls.map((call) => call[0]).find((line) => line.includes("tmux new-session"));
expect(command).toContain(`-e 'HOME=${daemonHome}'`);
expect(command).toContain(`-e 'CODEX_HOME=${path.join(daemonHome, ".codex")}'`);
expect(command).toContain("-e 'PATH=/proof/openrig/bin:/usr/bin:/bin'");
expect(command).toContain(`-e 'PATH=${launchPath}'`);
expect(command).toContain("-e 'OPENAI_API_KEY=gap7-openai-key'");
} finally {
result?.db.close();
@@ -5,11 +5,11 @@ import { tmpdir } from "node:os";
import { delimiter, join, sep } from "node:path";
import { probeCodexDaemonSupport } from "../src/domain/codex-daemon-support.js";
import { prepareHermeticEnv, type HermeticScaffold } from "./helpers/hermetic-env.js";
import { realRuntimesRequested } from "../../../test/stub-runtimes.js";
// Daemon tests must never run the host's real `codex` or `claude`. Under a fixture HOME the host's
// ~/.local/bin/codex wrapper can hang (mise use -g), and every daemon start probes `codex --version`.
// The shared vitest setup and prepareHermeticEnv put stubs first on PATH instead.
const realRuntimes = process.env["OPENRIG_E2E_REAL_CODEX"] === "1";
function resolveOnPath(binary: string, pathVar: string): string | undefined {
for (const dir of pathVar.split(delimiter)) {
@@ -19,7 +19,22 @@ function resolveOnPath(binary: string, pathVar: string): string | undefined {
return undefined;
}
describe.skipIf(realRuntimes)("stub codex and claude on the test PATH", () => {
describe("stub codex and claude on the test PATH", () => {
it.each(["seat-handover-model-fidelity-e2e.test.ts", "seat-lifecycle-set-model-resume-e2e.test.ts"])(
"allows only the opted-in native e2e file %s", name => {
const testPath = join(import.meta.dirname, name);
expect(realRuntimesRequested({ OPENRIG_E2E_REAL_CODEX: "1" }, testPath)).toBe(true);
expect(realRuntimesRequested({}, testPath)).toBe(false);
expect(realRuntimesRequested({ OPENRIG_E2E_REAL_CODEX: "1" }, join(tmpdir(), name))).toBe(false);
},
);
it("does not grant the native exception to ordinary tests or plain Node setup consumers", () => {
const env = { OPENRIG_E2E_REAL_CODEX: "1" };
expect(realRuntimesRequested(env, import.meta.filename)).toBe(false);
expect(realRuntimesRequested(env)).toBe(false);
});
it("resolves codex and claude inside the fixture root under the vitest setup", () => {
const root = realpathSync(process.env["OPENRIG_HOME"]!);
for (const binary of ["codex", "claude"]) {
@@ -86,10 +101,11 @@ describe.skipIf(realRuntimes)("stub codex and claude on the test PATH", () => {
}
});
it("leaves the host's binaries alone when real runtimes are opted in", () => {
it("keeps scenario runtimes stubbed even when the native e2e opt-in is inherited", () => {
const s = prepareHermeticEnv({ baseEnv: { HOME: "/real-home", PATH: "/usr/bin", OPENRIG_E2E_REAL_CODEX: "1" } });
scaffolds.push(s);
expect(s.env["PATH"]?.split(delimiter).some((dir) => dir.startsWith(s.root) && existsSync(join(dir, "codex")))).toBe(false);
expect(s.env["PATH"]?.split(delimiter).some((dir) => dir.startsWith(s.root) && existsSync(join(dir, "codex")))).toBe(true);
expect(s.env["OPENRIG_E2E_REAL_CODEX"]).toBeUndefined();
});
});
});
+12 -2
View File
@@ -29,7 +29,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { writeRuntimeStubs } from "./stub-runtimes.js";
import { realRuntimesRequested, writeRuntimeStubs } from "./stub-runtimes.js";
// Scrub inherited instance selectors before product modules capture their defaults.
// Tests can still set explicit overrides after setup; test/provider opt-in flags
@@ -115,7 +115,12 @@ process.env["OPENRIG_COMPACTION_OUT_ROOT"] = path.join(fixtureHome, "compaction-
// to this fixture. Merely changing HOME would not override OPENRIG_HOME or DB.
// Keep ordinary user files outside the state root, as in a real installation.
// Otherwise file-transfer correctly rejects every ~/ path as OpenRig state.
if (process.env["OPENRIG_E2E_REAL_CODEX"] !== "1") {
// Vitest sets testPath before setupFiles. Plain Node setup consumers have no native-runtime exception.
const testPath = process.env["VITEST"] ? (await import("vitest")).expect.getState().testPath : undefined;
const realCodexTest = realRuntimesRequested(process.env, testPath);
// These files exercise Codex only; an inherited Claude override never belongs to their fixture.
delete process.env["CLAUDE_CONFIG_DIR"];
if (!realCodexTest) {
const userHome = `${fixtureHome}-user-home`;
fs.mkdirSync(userHome, { recursive: true });
process.env["HOME"] = userHome;
@@ -125,6 +130,11 @@ if (process.env["OPENRIG_E2E_REAL_CODEX"] !== "1") {
XDG_DATA_HOME: ".local/share", XDG_CACHE_HOME: ".cache",
})) process.env[key] = path.join(userHome, suffix);
if (os.homedir() !== userHome || process.env["CODEX_HOME"] !== undefined
|| process.env["CLAUDE_CONFIG_DIR"] !== undefined) {
throw new Error("test provider homes must stay inside the fixture");
}
// codex and claude resolve to stubs that answer at once, so no test runs the host's real
// binaries (a codex wrapper that hangs under a fixture HOME leaves its probes running).
// The stubs live beside the fixture home and go away with it. One PATH entry per worker.
+13 -3
View File
@@ -10,6 +10,7 @@
// do. The Codex version is >= 0.130 so startup skips the `codex_hooks` feature-flag write.
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const STUBS = {
codex: {
@@ -26,9 +27,18 @@ const STUBS = {
const STUB_NAME = ".openrig-test-runtime-stubs";
/** True when a test run opted into the host's real runtimes (the existing E2E opt-in). */
export function realRuntimesRequested(env: Record<string, string | undefined> = process.env): boolean {
return env["OPENRIG_E2E_REAL_CODEX"] === "1";
const REAL_CODEX_TESTS = [
"seat-handover-model-fidelity-e2e.test.ts",
"seat-lifecycle-set-model-resume-e2e.test.ts",
].map(name => fileURLToPath(new URL(`../packages/daemon/test/${name}`, import.meta.url)));
/** The opt-in permits native runtimes only in the two tests that exercise real Codex. */
export function realRuntimesRequested(
env: Record<string, string | undefined> = process.env,
testPath?: string,
): boolean {
return env["OPENRIG_E2E_REAL_CODEX"] === "1" && testPath !== undefined
&& REAL_CODEX_TESTS.includes(path.resolve(testPath));
}
/**