diff --git a/tests/runner-e2e/harness-env.ts b/tests/runner-e2e/harness-env.ts index 8a98fe1a73..df7596992e 100644 --- a/tests/runner-e2e/harness-env.ts +++ b/tests/runner-e2e/harness-env.ts @@ -87,6 +87,30 @@ export function resolvePaperclipRemoteRunnerBinaryForHarness( : undefined; } +/** + * Keep fixture-only provider switches scoped to the one isolated harness that + * needs them. In particular, the pinned legacy OpenCode model is routed by the + * paid gateway and may not appear in OpenCode's public model catalog. + */ +export function buildRunnerE2EProcessEnvironment( + source: NodeJS.ProcessEnv, + executions: readonly MatrixExecution[], +): NodeJS.ProcessEnv { + const result = { ...source }; + delete result.OPENCODE_ALLOW_ALL_MODELS; + if ( + executions.length > 0 && + executions.every( + (execution) => + execution.profile.generation === "legacy" && + execution.profile.provider === "opencode", + ) + ) { + result.OPENCODE_ALLOW_ALL_MODELS = "true"; + } + return result; +} + /** * Build the environment inherited by the Paperclip server. Paid credentials * deliberately stay in the launcher/Playwright process and cross the server diff --git a/tests/runner-e2e/launch.ts b/tests/runner-e2e/launch.ts index d0fe4ef3d2..ebe10543ca 100644 --- a/tests/runner-e2e/launch.ts +++ b/tests/runner-e2e/launch.ts @@ -22,6 +22,7 @@ import { packageEvidence } from "./evidence.js"; import { classifyFailure, shouldRetryFailure } from "./failure-classifier.js"; import { buildRunnerCampaign } from "./history.js"; import { + buildRunnerE2EProcessEnvironment, resolvePaperclipRemoteRunnerBinaryForHarness, resolvePaperclipRunnerBinaryForHarness, } from "./harness-env.js"; @@ -395,7 +396,7 @@ async function runAttempt(input: { repositoryRoot, ); const childEnv: NodeJS.ProcessEnv = { - ...process.env, + ...buildRunnerE2EProcessEnvironment(process.env, executions), PATH: providerPath, PAPERCLIP_RUNNER_E2E_EXECUTION_IDS: JSON.stringify( executions.map((candidate) => candidate.id), diff --git a/tests/runner-e2e/support.test.ts b/tests/runner-e2e/support.test.ts index c40b6a605d..c1eb609a11 100644 --- a/tests/runner-e2e/support.test.ts +++ b/tests/runner-e2e/support.test.ts @@ -10,11 +10,12 @@ import { classifyFailure, shouldRetryFailure } from "./failure-classifier.js"; import { assertIsolatedServerEnvironment, buildPaperclipServerEnvironment, + buildRunnerE2EProcessEnvironment, resolvePaperclipRemoteRunnerBinaryForHarness, resolvePaperclipRunnerBinaryForHarness, runnerE2EServerControlPaths, } from "./harness-env.js"; -import { runnerExecutionById } from "./catalog.js"; +import { runnerExecutionById, runnerMatrix } from "./catalog.js"; import { assertEmbeddedDatabaseIsolation } from "./instance-isolation.js"; import { evaluateMatchers } from "./matchers.js"; import { @@ -116,6 +117,41 @@ describe("runner E2E local binary resolution", () => { }); }); +describe("runner E2E provider environment", () => { + const legacyLocal = runnerExecutionById( + "core-compatibility.legacy-opencode.local.message-marker", + ); + const legacyDaytona = runnerExecutionById( + "core-compatibility.legacy-opencode.daytona.message-marker", + ); + const nativeOpenCode = runnerExecutionById( + "core-compatibility.runner-opencode.local.message-marker", + ); + const breadthOpenCode = runnerMatrix.find( + (execution) => execution.suite.id === "openrouter-model-breadth", + )!; + + it("allows the pinned model only for isolated legacy OpenCode harnesses", () => { + for (const execution of [legacyLocal, legacyDaytona]) { + expect( + buildRunnerE2EProcessEnvironment( + { KEEP_ME: "yes", OPENCODE_ALLOW_ALL_MODELS: "ambient" }, + [execution], + ), + ).toEqual({ KEEP_ME: "yes", OPENCODE_ALLOW_ALL_MODELS: "true" }); + } + + for (const execution of [nativeOpenCode, breadthOpenCode]) { + expect( + buildRunnerE2EProcessEnvironment( + { KEEP_ME: "yes", OPENCODE_ALLOW_ALL_MODELS: "ambient" }, + [execution], + ), + ).toEqual({ KEEP_ME: "yes" }); + } + }); +}); + describe("runner E2E server port allocation", () => { it("rejects direct and derived embedded-Postgres collisions", () => { expect(runnerE2EServerPortConflictsWithDatabase(44_329)).toBe(true);