From 5dfe1d4b29ad979bae3203c1d8b6b83fbb2fc750 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 4 Nov 2026 10:47:29 -0600 Subject: [PATCH] fix(e2e): configure supported rolling agents with warm policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The warm environment was overridden by each profile’s explicit per-turn agent policy. Use the production managed warm capability to select supported profiles, set their agent policy to warm, and retain per-turn coverage for every qualified native profile. The effective-policy regression fails on the old catalog; 39 affected catalog and controller checks pass. Shared-package type declarations need a fresh build for the broad local typecheck. Co-Authored-By: Paperclip --- tests/runner-e2e/README.md | 3 +++ tests/runner-e2e/catalog.test.ts | 24 ++++++++++++++++--- tests/runner-e2e/catalog.ts | 16 ++++++++++--- .../controller-rolling-deployment.test.ts | 8 ++++--- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/tests/runner-e2e/README.md b/tests/runner-e2e/README.md index bd446143639..cd87a9cdc2b 100644 --- a/tests/runner-e2e/README.md +++ b/tests/runner-e2e/README.md @@ -191,6 +191,9 @@ Postgres. Each controller has an independent compiled build and cache. The copie app retains the checkout's `cli/` and `packages/` dependency links, including the development Runner vendor entry. Shared workspace dependencies mean this is not released-container or Railway evidence. +The warm lane selects providers through the production managed warm-session +capability and explicitly sets the agent policy to warm. The per-turn lane +retains every registered provider's per-turn policy. The read-only recovery observer captures the live predecessor's identity just before handoff, then requires a new controller generation, authenticated Runner attachment, and new canonical events for the original execution. No fixture diff --git a/tests/runner-e2e/catalog.test.ts b/tests/runner-e2e/catalog.test.ts index 044865f6cae..13dae59c047 100644 --- a/tests/runner-e2e/catalog.test.ts +++ b/tests/runner-e2e/catalog.test.ts @@ -33,6 +33,24 @@ import { } from "./selectors.js"; describe("runner E2E catalog", () => { + it("uses effective warm agent policy only for supported rolling warm providers", () => { + const warm = runnerMatrix.filter(cell => cell.suite.id === "rolling-web-deployment-warm"); + const perTurn = runnerMatrix.filter(cell => cell.suite.id === "rolling-web-deployment"); + expect(warm.some(cell => cell.profile.id === "runner-codex")).toBe(true); + expect(warm.some(cell => ["runner-opencode", "runner-acpx-claude", "runner-acpx-grok"].includes(cell.profile.id))).toBe(false); + expect(perTurn.length).toBeGreaterThan(warm.length); + for (const cell of [...warm, ...perTurn]) { + const secretRefs = Object.fromEntries([cell.profile.credential, "DAYTONA_API_KEY"].map(key => [key, + { type: "secret_ref" as const, secretId: "22222222-2222-4222-8222-222222222222", version: "latest" as const }])); + const input = { executionId: cell.id, secretRefs, daytonaImage: `fixture@sha256:${"a".repeat(64)}` }; + const agent = cell.profile.buildAgent({ ...input, environmentId: "env-1", environmentFixtureId: "daytona", workspacePath: "/workspace" }); + const environment = cell.environment.buildEnvironment(input); + const expected = cell.suite.id === "rolling-web-deployment-warm" ? "warm" : "per_turn"; + expect(agent.adapterConfig).toMatchObject({ lifecycleMode: expected }); + expect(environment.config).toMatchObject({ runnerLifecycleMode: expected, reuseLease: expected === "warm" }); + expect(cell.suite.expectedMatrixSize).toBe(cell.suite.profiles.length * cell.suite.tasks.length); + } + }); it("keeps verified artifact transfer a single explicit Daytona run", () => { const id = "api-artifact-transfer.runner-codex.daytona.verified-artifact-copy"; const cells = selectRunnerExecutions(parseRunnerSelectors(["--id", id])); @@ -312,11 +330,11 @@ describe("runner E2E catalog", () => { expect(localIntegrityTasks).toHaveLength(2); expect(openRouterBreadthTasks).toHaveLength(3); expect(runnerSuites.map((suite) => suite.expectedMatrixSize)).toEqual([ - 1, 9, 9, 9, 9, 4, 2, 1, 3, 2, 2, 4, 4, 4, 4, 4, 4, 8, 10, 6, 63, 12, 6, 4, 2, 4, 2, 30, 3, 16, 16, 20, 2, 1, 6, 8, 46, 2, 23, 15, 52, 6, 6, 20, 26, 52, 5, 28, 18, 2, 6, 6, 13, 10, 4, 48, 16, 10, 2, 1, 1, 4, 116, 2, 1, 2, + 1, 9, 2, 9, 9, 4, 2, 1, 3, 2, 2, 4, 4, 4, 4, 4, 4, 8, 10, 6, 63, 12, 6, 4, 2, 4, 2, 30, 3, 16, 16, 20, 2, 1, 6, 8, 46, 2, 23, 15, 52, 6, 6, 20, 26, 52, 5, 28, 18, 2, 6, 6, 13, 10, 4, 48, 16, 10, 2, 1, 1, 4, 116, 2, 1, 2, ]); - expect(validateRunnerCatalog()).toHaveLength(837); - expect(new Set(runnerMatrix.map((entry) => entry.id)).size).toBe(837); + expect(validateRunnerCatalog()).toHaveLength(830); + expect(new Set(runnerMatrix.map((entry) => entry.id)).size).toBe(830); expect( runnerMatrix.filter((entry) => entry.suite.id === "core-compatibility"), ).toHaveLength(48); diff --git a/tests/runner-e2e/catalog.ts b/tests/runner-e2e/catalog.ts index 18c1151aaf3..e3e3162ac9c 100644 --- a/tests/runner-e2e/catalog.ts +++ b/tests/runner-e2e/catalog.ts @@ -1,4 +1,5 @@ import { ROLLING_WEB_DEPLOYMENT_SUITE, ROLLING_WEB_DEPLOYMENT_WARM_SUITE } from "./controller-rolling-deployment.js"; +import { supportsManagedNativeWarmSession } from "../../server/src/services/native-runtime/native-warm-capability.js"; import { FEEDBACK_PERSISTENCE_SUITE, feedbackPersistenceTask, feedbackPersistenceDigest } from "./feedback-persistence.js"; import { followupStopTask, followupStopDigest } from "./followup-stop-resume.js"; import { finalizationRecoveryTask, finalizationRecoveryDigest } from "./finalization-recovery.js"; @@ -1252,15 +1253,24 @@ const rollingDeploymentProfiles = [...runnerProfiles, ...extendedHarnessProfiles return { ...agent, adapterConfig }; } }; }); +const rollingWarmDeploymentProfiles = rollingDeploymentProfiles.filter(profile => { + const backend = profile.provider === "codex" ? "codex_app_server" : profile.provider === "opencode" ? "opencode_server" : "acpx_runtime"; + return supportsManagedNativeWarmSession({ backend, acpxAgent: "acpxAgent" in profile ? profile.acpxAgent : undefined }, + profile.credential === "COPILOT_GITHUB_TOKEN" ? { provider: "github", method: "api_key" } : undefined); +}).map(profile => ({ ...profile, buildAgent(input: AgentFixtureBuildInput) { + const agent = profile.buildAgent(input); + return { ...agent, adapterConfig: { ...(agent.adapterConfig as Record), lifecycleMode: "warm" } }; +} })); + const rollingDeploymentSuites: RunnerSuiteFixture[] = [ { id: ROLLING_WEB_DEPLOYMENT_SUITE, environment: runnerEnvironments.find(environment => environment.id === "daytona")! }, { id: ROLLING_WEB_DEPLOYMENT_WARM_SUITE, environment: daytonaControllerOutageEnvironment }, ].map(({ id, environment }) => ({ ...remoteWorkspaceStorageSuite, id, label: `Rolling control-plane web deployment (${environment.configurationKey ? "warm" : "per-turn"})`, description: "Overlap independent ephemeral web servers while a remote execution stays active; keep ordinary reads, saves, files and a later native continuation working.", - profiles: rollingDeploymentProfiles, environments: [environment], - expectedMatrixSize: rollingDeploymentProfiles.length * remoteWorkspaceStorageSuite.tasks.length, - definitionMetadata: { ...remoteWorkspaceStorageSuite.definitionMetadata, version: 4, controllerDowntimeMs: 0, + profiles: id === ROLLING_WEB_DEPLOYMENT_WARM_SUITE ? rollingWarmDeploymentProfiles : rollingDeploymentProfiles, environments: [environment], + expectedMatrixSize: (id === ROLLING_WEB_DEPLOYMENT_WARM_SUITE ? rollingWarmDeploymentProfiles : rollingDeploymentProfiles).length * remoteWorkspaceStorageSuite.tasks.length, + definitionMetadata: { ...remoteWorkspaceStorageSuite.definitionMetadata, version: 5, controllerDowntimeMs: 0, activeWorkBoundary: "foreground-180-second-cli-reliability-soak-and-live-owner-recheck", workspaceFileSource: environment.configurationKey ? "live" : "saved-exact-snapshot-after-per-turn-retirement", lifecycleCoverage: environment.configurationKey ? "warm-reusable" : "per-turn-disposable", providerCoverage: "registered-native-daytona-profiles", diff --git a/tests/runner-e2e/controller-rolling-deployment.test.ts b/tests/runner-e2e/controller-rolling-deployment.test.ts index 2484abb0212..d9fc3552b08 100644 --- a/tests/runner-e2e/controller-rolling-deployment.test.ts +++ b/tests/runner-e2e/controller-rolling-deployment.test.ts @@ -57,7 +57,7 @@ describe("rolling controller qualification", () => { const [outage] = selectRunnerExecutions(parseRunnerSelectors(["--suite", "remote-workspace-storage"])); expect(controllerOutageForExecution(outage!, {})).toEqual({ signal: "SIGKILL", holdMs: 300_000 }); }); - it("requires both lifecycle modes for every production-qualified native sandbox profile", () => { + it("covers every qualified native profile per turn and each supported warm profile", () => { const executions = ["rolling-web-deployment", "rolling-web-deployment-warm"] .flatMap(suite => selectRunnerExecutions(parseRunnerSelectors(["--suite", suite]))); const native = [...runnerProfiles, ...extendedHarnessProfiles, kimiRunnerProfile] @@ -74,12 +74,14 @@ describe("rolling controller qualification", () => { && (!profile.qualificationCandidate || QUALIFIED_ACPX_PROFILES[profile.qualificationCandidate].qualificationStatus !== "pending")) .map(kind)); expect(new Set(native.map(kind))).toEqual(registeredKinds); + const warmIds = new Set(runnerSuites.find(suite => suite.id === "rolling-web-deployment-warm")!.profiles.map(profile => profile.id)); for (const profile of native) { const cells = executions.filter(execution => execution.profile.id === profile.id); - expect(cells).toHaveLength(2); + expect(cells.filter(cell => cell.suite.id === "rolling-web-deployment")).toHaveLength(1); + expect(cells).toHaveLength(warmIds.has(profile.id) ? 2 : 1); expect(cells.every(cell => !cell.profile.qualificationCandidate)).toBe(true); expect(new Set(cells.map(cell => cell.environment.configurationKey ?? "per_turn"))) - .toEqual(new Set(["per_turn", "warm-controller-outage-v1"])); + .toEqual(new Set(warmIds.has(profile.id) ? ["per_turn", "warm-controller-outage-v1"] : ["per_turn"])); } expect(executions.every(execution => execution.suite.definitionMetadata?.retainedControllerContinuation === true)).toBe(true); });