Skip to content

Commit 5a3a358

Browse files
jyaunchescvgithub-actions[bot]
authored andcommitted
fix(e2e): bind Deep Agents Code onboarding to the published platform reference (#9392)
<!-- markdownlint-disable MD041 --> The Deep Agents Code amd64 live job now selects the exact published `linux/amd64` platform reference from the candidate-bound publication contract instead of using the multi-platform index. After merge, the producer will emit the same platform reference. Manual PR E2E passes the contract-selected reference explicitly to initial onboarding and fresh re-onboarding. Final evidence remains bound to the selected candidate, the complete publication contract, the official repository, the platform, and its exact digest. Fixes #9386 - Root-cause key: `DCode final qualification / publication-to-onboarding handoff / amd64 runner received the multi-platform index ref instead of the published linux/amd64 manifest ref` - Source runs: - https://github.com/NVIDIA/NemoClaw/actions/runs/32076801912 - https://github.com/NVIDIA/NemoClaw/actions/runs/32081571309 - https://github.com/NVIDIA/NemoClaw/actions/runs/32089878527 - https://github.com/NVIDIA/NemoClaw/actions/runs/32091103413 - Failed jobs: - https://github.com/NVIDIA/NemoClaw/actions/runs/32076801912/job/95532157545 - https://github.com/NVIDIA/NemoClaw/actions/runs/32081571309/job/95545647820 - https://github.com/NVIDIA/NemoClaw/actions/runs/32089878527/job/95575261210 - https://github.com/NVIDIA/NemoClaw/actions/runs/32091103413/job/95584858770 - Stable signature: `Deep Agents Code sandbox image is missing base resolution metadata` - Scope: one publication-to-onboarding platform-reference mismatch. The publication validator exported the multi-platform index as `base_ref`, while final qualification requires the locally recorded platform manifest. Existing tests accepted the index at the publication boundary and then supplied fabricated amd64 runtime metadata, so they did not exercise the handoff that failed live. The third recurrence, for main commit `4a179614ac8a07273fce8f1cb679b33c0444f5c4`, first failed published-base import proof with `ModuleNotFoundError: No module named 'deepagents'`. Fallback onboarding and all target-specific cloud checks then passed before final evidence immediately reproduced the same missing-metadata signature. It is additional evidence for this root cause, not a separate claim. The fourth automatic recurrence, for main commit `fb01aff8ed67596dcdfe38cb6f5dccdcba301a33`, used index digest `sha256:772546…` with amd64 child `sha256:5331ab…`. Phases 1–7 passed, including onboarding, lifecycle, every cloud check, and fresh re-onboarding. Phase 9 alone failed with `Deep Agents Code sandbox image is missing base resolution metadata` before cleanup passed. The earlier `ModuleNotFoundError` was the expected negative import gate, whose step succeeded. This is confirmed pre-fix recurrence evidence, not a separate claim. The first exact-candidate manual run, https://github.com/NVIDIA/NemoClaw/actions/runs/32094360622, checked out candidate `5c164b9c79cb3b7cb65f7ed1f0b99f026152c097` but used the trusted workflow from base commit `fb01aff8ed67596dcdfe38cb6f5dccdcba301a33`. Its Deep Agents Code job, https://github.com/NVIDIA/NemoClaw/actions/runs/32094360622/job/95582856763, failed phase 1 before onboarding because the trusted helper emitted the index while that candidate required the amd64 platform reference. This was trusted-workflow/candidate selection skew, not the original runtime metadata failure. The follow-up makes the validated candidate-bound contract authoritative for platform selection and passes its exact amd64 reference explicitly to both onboarding consumers without mutating ambient process state. This failed run does not count toward the two-pass acceptance criterion. - Validate imports from and export the contract's exact `linux/amd64` platform reference. - Select the fixed platform reference from the complete candidate-bound contract when publication evidence exists. - Pass that exact reference explicitly to initial onboarding and fresh re-onboarding; retain the ambient reference only for local runs without publication evidence. - Require completed-image evidence to match the same platform reference. - Reuse one target-platform constant across Docker validation, publication output, and final evidence checks. - Keep the complete multi-platform index contract as candidate and publication authority. - Cover the emitted reference, trusted-workflow/candidate revision skew, both onboarding consumers, invalid explicit references, missing metadata, and opposite-platform evidence. - [x] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [x] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [x] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: independent exact-commit review passed all security-rubric categories for producer selection, explicit consumer propagation, and final runtime evidence - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: Not applicable; this PR does not change `scripts/prepare-dgx-station-host.sh`. - [ ] Tested on DGX Station - Tested commit: not applicable - Station profile/scenario: not applicable - Result: not applicable - Supporting evidence: not applicable - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [ ] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable — pre-commit and commit-msg passed; pre-push passed its applicable checks, but `tsc-cli` was skipped because unchanged `origin/main` fails at `src/lib/state/portable-uninstall-retirement.test.ts:299` with TS2365 - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — focused E2E-support tests: 4 files, 100 tests; `npm run test:changed`: growth guardrails 22 tests, 621 executed tests passed, and 7 designed skips - [ ] The live E2E target passes twice for the commit under review — the `ubuntu-repo-cloud-langchain-deepagents-code` job must pass in two runs - [ ] Applicable broad gate passed — not applicable; this is a focused internal E2E publication handoff and targeted coverage exercises both affected boundaries - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) PASS. Reviewed commit `ce1a03d80d55ee726242f66a72313a5d8eadd31a` and its complete nine-file diff against base SHA `fb01aff8ed67596dcdfe38cb6f5dccdcba301a33`. Publication provenance, candidate SHA, the official repository, immutable digests, fixed `linux/amd64` selection, explicit consumer propagation, final runtime evidence, and retention of the complete index contract remain fail closed. The change does not mutate process-global state or introduce an injection path. Independent verification passed 100 focused tests, 621 changed tests, and 22 growth tests. PASS. Reviewed commit `ce1a03d80d55ee726242f66a72313a5d8eadd31a` and its complete nine-file diff against base SHA `fb01aff8ed67596dcdfe38cb6f5dccdcba301a33`. No public or owning E2E documentation update is required because the change is internal publication-evidence plumbing and does not change a supported command, artifact schema, dispatch procedure, credential procedure, or operator action. Changed identifiers, test titles, and error text follow NemoClaw writing rules. Independent `git diff --check` passed. --- Signed-off-by: Julie Yaunches <jyaunches@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> * **Reliability Improvements** * Improved validation of published base-image references for the target platform. * Ensured onboarding and cloud checks consistently use the verified image reference when one is provided. * Added safeguards against mutable or mismatched image references and unsupported platform metadata. * **Testing** * Expanded end-to-end coverage for image selection, platform compatibility, reference precedence, and publication evidence handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Julie Yaunches <jyaunches@nvidia.com> Signed-off-by: Carlos Villela <cvillela@nvidia.com> Co-authored-by: Carlos Villela <cvillela@nvidia.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 8d00488 commit 5a3a358

9 files changed

Lines changed: 137 additions & 41 deletions

test/e2e/fixtures/phases/onboarding.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export interface OnboardingCleanup {
6969
}
7070

7171
export interface OnboardingOptions {
72+
dcodeBaseImageReference?: string;
7273
sandboxName?: string;
7374
timeoutMs?: number;
7475
}
@@ -240,7 +241,11 @@ export class OnboardingPhaseFixture {
240241
);
241242
}
242243
const sandboxName = sandboxNameFromOptions(environment.onboarding, options);
243-
const baseImageReference = requireDcodeBaseImageReference();
244+
const baseImageReference = requireDcodeBaseImageReference(
245+
options.dcodeBaseImageReference === undefined
246+
? process.env
247+
: { [DCODE_BASE_IMAGE_ENV]: options.dcodeBaseImageReference },
248+
);
244249
const apiKey = this.secrets.required("NVIDIA_INFERENCE_API_KEY");
245250
this.registerSandboxCleanup(sandboxName);
246251
const result = await this.host.nemoclaw([...ONBOARD_ARGS, "--observability"], {

test/e2e/live/cloud-experimental-checks.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,14 @@ export function buildCloudExperimentalCommandEnv(
6666
sandboxName: string,
6767
apiKey: string,
6868
base: NodeJS.ProcessEnv = process.env,
69-
options: { forwardDcodeBaseImage?: boolean } = {},
69+
options: { dcodeBaseImageReference?: string; forwardDcodeBaseImage?: boolean } = {},
7070
): NodeJS.ProcessEnv {
7171
const dcodeBaseImage = options.forwardDcodeBaseImage
72-
? requireDcodeBaseImageReference(base)
72+
? requireDcodeBaseImageReference(
73+
options.dcodeBaseImageReference === undefined
74+
? base
75+
: { [DCODE_BASE_IMAGE_ENV]: options.dcodeBaseImageReference },
76+
)
7377
: undefined;
7478
return {
7579
...buildAvailabilityProbeEnv(base),
@@ -135,7 +139,9 @@ export async function runE2eCloudExperimentalChecks(
135139
targetId: string,
136140
sandboxName: string,
137141
checkScripts: readonly string[],
138-
context: Pick<E2ETargetFixtures, "artifacts" | "host" | "secrets">,
142+
context: Pick<E2ETargetFixtures, "artifacts" | "host" | "secrets"> & {
143+
dcodeBaseImageReference?: string;
144+
},
139145
): Promise<void> {
140146
const apiKey = context.secrets.optional("NVIDIA_INFERENCE_API_KEY") ?? "";
141147
await context.artifacts.writeJson(
@@ -150,6 +156,7 @@ export async function runE2eCloudExperimentalChecks(
150156
artifactName: `cloud-experimental-${path.basename(scriptPath, ".sh")}`,
151157
cwd: REPO_ROOT,
152158
env: buildCloudExperimentalCommandEnv(sandboxName, apiKey, process.env, {
159+
dcodeBaseImageReference: context.dcodeBaseImageReference,
153160
forwardDcodeBaseImage: scriptPath === DEEPAGENTS_FRESH_REONBOARD_CHECK,
154161
}),
155162
redactionValues: [apiKey],

test/e2e/live/dcode-base-image-runtime-evidence.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import fs from "node:fs";
66
import { readSandboxBaseImageResolutionMetadata } from "../../../src/lib/sandbox-base-image/label-codec.ts";
77
import type { SandboxBaseImageResolutionMetadata } from "../../../src/lib/sandbox-base-image/types.ts";
88
import {
9-
DCODE_BASE_IMAGE_ONBOARD_PLATFORM,
9+
DCODE_BASE_IMAGE_TARGET_PLATFORM,
1010
type DcodeBaseImageContract,
11-
type DcodePlatform,
1211
parseDcodeBaseImageContract,
1312
} from "../../../tools/e2e/dcode-base-image-contract.mts";
1413
import { requireDcodeBaseImageReference } from "../fixtures/dcode-base-image.ts";
@@ -23,7 +22,7 @@ export interface DcodeBaseImageRuntimeEvidence {
2322
digest: string;
2423
image: string;
2524
imageId: string;
26-
platform: DcodePlatform;
25+
platform: typeof DCODE_BASE_IMAGE_TARGET_PLATFORM;
2726
reference: string;
2827
sandboxImage: string;
2928
source: "override";
@@ -79,23 +78,27 @@ export function parseDcodeBaseImagePublicationEvidence(
7978
const contract = parseDcodeBaseImageContract(evidence.base);
8079
if (
8180
requireDcodeBaseImageReference(environment) !==
82-
contract.platformReferences[DCODE_BASE_IMAGE_ONBOARD_PLATFORM]
81+
contract.platformReferences[DCODE_BASE_IMAGE_TARGET_PLATFORM]
8382
) {
8483
throw new Error(
85-
`Deep Agents Code onboarding reference does not match the published ${DCODE_BASE_IMAGE_ONBOARD_PLATFORM} base contract`,
84+
`Deep Agents Code onboarding reference does not match the published ${DCODE_BASE_IMAGE_TARGET_PLATFORM} base contract`,
8685
);
8786
}
8887
return contract;
8988
}
9089

90+
export function dcodeBaseImageReferenceForContract(contract: DcodeBaseImageContract): string {
91+
return contract.platformReferences[DCODE_BASE_IMAGE_TARGET_PLATFORM];
92+
}
93+
9194
export function loadDcodeBaseImagePublicationEvidence(
9295
targetId: string,
9396
evidencePath: string,
9497
environment: NodeJS.ProcessEnv = process.env,
9598
): DcodeBaseImageContract | undefined {
9699
if (targetId !== DCODE_BASE_IMAGE_TARGET_ID) return undefined;
97-
requireDcodeBaseImageReference(environment);
98100
if (!fs.existsSync(evidencePath)) {
101+
requireDcodeBaseImageReference(environment);
99102
if (environment.GITHUB_ACTIONS === "true") {
100103
throw new Error("Deep Agents Code GitHub Actions run is missing published base evidence");
101104
}
@@ -107,14 +110,6 @@ export function loadDcodeBaseImagePublicationEvidence(
107110
);
108111
}
109112

110-
function platformFor(metadata: SandboxBaseImageResolutionMetadata): DcodePlatform {
111-
const platform = `${metadata.os}/${metadata.architecture}`;
112-
if (platform !== "linux/amd64" && platform !== "linux/arm64") {
113-
throw new Error(`Deep Agents Code base resolution used unsupported platform '${platform}'`);
114-
}
115-
return platform;
116-
}
117-
118113
export function verifyDcodeBaseImageRuntimeEvidence(
119114
contract: DcodeBaseImageContract,
120115
sandboxImage: string,
@@ -126,9 +121,13 @@ export function verifyDcodeBaseImageRuntimeEvidence(
126121
if (!metadata) {
127122
throw new Error("Deep Agents Code sandbox image is missing base resolution metadata");
128123
}
129-
const platform = platformFor(metadata);
130-
const expectedDigest = contract.platformDigests[platform];
131-
const expectedReference = contract.platformReferences[platform];
124+
if (`${metadata.os}/${metadata.architecture}` !== DCODE_BASE_IMAGE_TARGET_PLATFORM) {
125+
throw new Error(
126+
`Deep Agents Code sandbox image did not use the published ${DCODE_BASE_IMAGE_TARGET_PLATFORM} base digest`,
127+
);
128+
}
129+
const expectedDigest = contract.platformDigests[DCODE_BASE_IMAGE_TARGET_PLATFORM];
130+
const expectedReference = dcodeBaseImageReferenceForContract(contract);
132131
if (
133132
metadata.schema !== 1 ||
134133
metadata.imageName !== contract.image ||
@@ -139,15 +138,15 @@ export function verifyDcodeBaseImageRuntimeEvidence(
139138
metadata.ref !== `${metadata.imageName}@${metadata.digest}`
140139
) {
141140
throw new Error(
142-
`Deep Agents Code sandbox image did not use the published ${platform} base digest`,
141+
`Deep Agents Code sandbox image did not use the published ${DCODE_BASE_IMAGE_TARGET_PLATFORM} base digest`,
143142
);
144143
}
145144
return {
146145
contractReference: contract.reference,
147146
digest: metadata.digest,
148147
image: metadata.imageName,
149148
imageId: metadata.imageId,
150-
platform,
149+
platform: DCODE_BASE_IMAGE_TARGET_PLATFORM,
151150
reference: metadata.ref,
152151
sandboxImage,
153152
source: metadata.source,

test/e2e/live/registry-targets.test.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { cloudExperimentalChecksForOnboarding } from "./cloud-experimental-check
1818
import { runE2eCloudExperimentalChecks } from "./cloud-experimental-checks.ts";
1919
import {
2020
captureDcodeBaseImageRuntimeEvidence,
21+
dcodeBaseImageReferenceForContract,
2122
loadDcodeBaseImagePublicationEvidence,
2223
} from "./dcode-base-image-runtime-evidence.ts";
2324
import { buildLiveTargetRunPlan } from "./run-plan.ts";
@@ -95,9 +96,10 @@ for (const [targetIndex, target] of listTargets().entries()) {
9596
target.id,
9697
artifacts.pathFor("dcode-base-image.json"),
9798
);
98-
for (const secret of target.requiredSecrets ?? []) {
99-
secrets.required(secret);
100-
}
99+
const dcodeBaseImageReference = dcodeBaseContract
100+
? dcodeBaseImageReferenceForContract(dcodeBaseContract)
101+
: undefined;
102+
requireRegistryTargetSecrets(target.id, target.requiredSecrets ?? [], secrets);
101103

102104
expect(
103105
fs.existsSync(CLI_DIST_ENTRYPOINT),
@@ -137,6 +139,7 @@ for (const [targetIndex, target] of listTargets().entries()) {
137139
progress.phase("onboard the registry-selected sandbox");
138140
const instance = await onboard.from(ready, {
139141
sandboxName: `e2e-reg-${targetIndex.toString(36)}`,
142+
dcodeBaseImageReference,
140143
});
141144

142145
// Lifecycle phase runs between onboard and state-validation.
@@ -170,12 +173,15 @@ for (const [targetIndex, target] of listTargets().entries()) {
170173
expect(checkScripts).toEqual(
171174
cloudExperimentalChecksForOnboarding(target.environment.onboarding),
172175
);
173-
for (const scriptPath of checkScripts) {
174-
expect(fs.existsSync(path.join(REPO_ROOT, scriptPath))).toBe(true);
175-
}
176+
expect(
177+
checkScripts.every((scriptPath) =>
178+
Object.is(fs.existsSync(path.join(REPO_ROOT, scriptPath)), true),
179+
),
180+
).toBe(true);
176181
expect(fs.existsSync(E2E_CLOUD_EXPERIMENTAL_CHECKS_DIR)).toBe(true);
177182
await runE2eCloudExperimentalChecks(target.id, instance.sandboxName, checkScripts, {
178183
artifacts,
184+
dcodeBaseImageReference,
179185
host,
180186
secrets,
181187
});

test/e2e/support/dcode-base-image-contract.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { join } from "node:path";
88
import { describe, expect, it, vi } from "vitest";
99

1010
import {
11-
DCODE_BASE_IMAGE_ONBOARD_PLATFORM,
11+
DCODE_BASE_IMAGE_TARGET_PLATFORM,
1212
main,
1313
validateDcodeBaseImageContract,
1414
validateDcodeBaseImageImports,
@@ -19,21 +19,22 @@ const RUN_ATTEMPT = 2;
1919
const HEAD_SHA = "a".repeat(40);
2020
const IMAGE = "ghcr.io/nvidia/nemoclaw/langchain-deepagents-code-sandbox-base";
2121
const DIGEST = `sha256:${"b".repeat(64)}`;
22+
const AMD64_DIGEST = `sha256:${"c".repeat(64)}`;
23+
const ARM64_DIGEST = `sha256:${"d".repeat(64)}`;
24+
const AMD64_REFERENCE = `${IMAGE}@${AMD64_DIGEST}`;
2225

2326
function contract(overrides: Record<string, unknown> = {}): Record<string, unknown> {
24-
const amd64 = `sha256:${"c".repeat(64)}`;
25-
const arm64 = `sha256:${"d".repeat(64)}`;
2627
return {
2728
contractVersion: 1,
2829
agent: "langchain-deepagents-code",
2930
image: IMAGE,
3031
digest: DIGEST,
3132
reference: `${IMAGE}@${DIGEST}`,
3233
platforms: ["linux/amd64", "linux/arm64"],
33-
platformDigests: { "linux/amd64": amd64, "linux/arm64": arm64 },
34+
platformDigests: { "linux/amd64": AMD64_DIGEST, "linux/arm64": ARM64_DIGEST },
3435
platformReferences: {
35-
"linux/amd64": `${IMAGE}@${amd64}`,
36-
"linux/arm64": `${IMAGE}@${arm64}`,
36+
"linux/amd64": AMD64_REFERENCE,
37+
"linux/arm64": `${IMAGE}@${ARM64_DIGEST}`,
3738
},
3839
sourceRevision: HEAD_SHA,
3940
run: { id: RUN_ID, attempt: RUN_ATTEMPT },
@@ -72,7 +73,7 @@ describe("Deep Agents Code E2E base contract", () => {
7273
"run",
7374
"--rm",
7475
"--platform",
75-
DCODE_BASE_IMAGE_ONBOARD_PLATFORM,
76+
DCODE_BASE_IMAGE_TARGET_PLATFORM,
7677
"--network",
7778
"none",
7879
"--cap-drop",
@@ -128,4 +129,5 @@ describe("Deep Agents Code E2E base contract", () => {
128129
/did not prove both required imports/u,
129130
);
130131
});
132+
131133
});

test/e2e/support/dcode-base-image-runtime-evidence.test.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { SandboxBaseImageResolutionMetadata } from "../../../src/lib/sandbo
77
import { DCODE_BASE_IMAGE, DCODE_BASE_IMAGE_ENV } from "../fixtures/dcode-base-image.ts";
88
import {
99
DCODE_BASE_IMAGE_TARGET_ID,
10+
dcodeBaseImageReferenceForContract,
1011
loadDcodeBaseImagePublicationEvidence,
1112
parseDcodeBaseImagePublicationEvidence,
1213
verifyDcodeBaseImageRuntimeEvidence,
@@ -74,6 +75,19 @@ function resolutionMetadata(
7475
}
7576

7677
describe("Deep Agents Code published base runtime evidence", () => {
78+
it("selects the linux/amd64 platform reference when trusted manual PR E2E supplies it", () => {
79+
const environment = publicationEnvironment({
80+
GITHUB_ACTIONS: "true",
81+
GITHUB_EVENT_NAME: "workflow_dispatch",
82+
GITHUB_SHA: "f".repeat(40),
83+
NEMOCLAW_E2E_EXPECTED_SHA: CANDIDATE_REVISION,
84+
});
85+
const contract = parseDcodeBaseImagePublicationEvidence(publicationEvidence(), environment);
86+
87+
expect(dcodeBaseImageReferenceForContract(contract)).toBe(AMD64_REFERENCE);
88+
expect(environment[DCODE_BASE_IMAGE_ENV]).toBe(AMD64_REFERENCE);
89+
});
90+
7791
it("records the completed sandbox image only when its platform digest matches publication", () => {
7892
const contract = parseDcodeBaseImagePublicationEvidence(
7993
publicationEvidence(),
@@ -218,10 +232,19 @@ describe("Deep Agents Code published base runtime evidence", () => {
218232
/did not use the published linux\/amd64 base digest/,
219233
],
220234
[
221-
"the opposite platform digest",
235+
"the opposite platform digest for amd64",
222236
resolutionMetadata({ digest: ARM64_DIGEST, ref: ARM64_REFERENCE }),
223237
/did not use the published linux\/amd64 base digest/,
224238
],
239+
[
240+
"self-consistent opposite-platform metadata",
241+
resolutionMetadata({
242+
architecture: "arm64",
243+
digest: ARM64_DIGEST,
244+
ref: ARM64_REFERENCE,
245+
}),
246+
/did not use the published linux\/amd64 base digest/,
247+
],
225248
[
226249
"a different image repository",
227250
resolutionMetadata({ imageName: "ghcr.io/example/base" }),
@@ -240,7 +263,7 @@ describe("Deep Agents Code published base runtime evidence", () => {
240263
[
241264
"an unsupported platform",
242265
resolutionMetadata({ architecture: "ppc64le" }),
243-
/used unsupported platform/,
266+
/did not use the published linux\/amd64 base digest/,
244267
],
245268
])("rejects %s", (_label, metadata, expectedError) => {
246269
const contract = parseDcodeBaseImagePublicationEvidence(

test/e2e/support/e2e-phase-onboarding.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ interface CleanupCall {
3030
}
3131

3232
const DCODE_BASE_IMAGE_REF = `${DCODE_BASE_IMAGE}@sha256:${"a".repeat(64)}`;
33+
const DCODE_BASE_IMAGE_INDEX_REF = `${DCODE_BASE_IMAGE}@sha256:${"b".repeat(64)}`;
3334

3435
async function withProcessEnvironment<T>(
3536
values: Record<string, string | undefined>,
@@ -213,6 +214,39 @@ describe("onboarding phase fixture", () => {
213214
});
214215
});
215216

217+
it("uses the contract-selected Deep Agents Code base image reference instead of the ambient publication index", async () => {
218+
const runner = new FakeRunner();
219+
runner.enqueue(shellResult(0, "onboarded\n"));
220+
const secrets = new FakeSecrets({ NVIDIA_INFERENCE_API_KEY: "secret-token" });
221+
const onboard = new OnboardingPhaseFixture(new HostCliClient(runner), secrets);
222+
223+
await withProcessEnvironment({ [DCODE_BASE_IMAGE_ENV]: DCODE_BASE_IMAGE_INDEX_REF }, () =>
224+
onboard.from(ready({ onboarding: "cloud-langchain-deepagents-code" }), {
225+
dcodeBaseImageReference: DCODE_BASE_IMAGE_REF,
226+
sandboxName: "e2e-dcode-cloud",
227+
}),
228+
);
229+
230+
expect(runner.calls[0]?.options?.env?.[DCODE_BASE_IMAGE_ENV]).toBe(DCODE_BASE_IMAGE_REF);
231+
});
232+
233+
it("rejects an invalid explicit Deep Agents Code base image reference before onboarding side effects", async () => {
234+
const runner = new FakeRunner();
235+
const cleanup = new FakeCleanup();
236+
const secrets = new FakeSecrets({ NVIDIA_INFERENCE_API_KEY: "secret-token" });
237+
const onboard = new OnboardingPhaseFixture(new HostCliClient(runner), secrets, cleanup);
238+
239+
await expect(
240+
onboard.from(ready({ onboarding: "cloud-langchain-deepagents-code" }), {
241+
dcodeBaseImageReference: `${DCODE_BASE_IMAGE}:latest`,
242+
sandboxName: "e2e-dcode-cloud",
243+
}),
244+
).rejects.toThrow(/requires .* to be the immutable official/);
245+
expect(secrets.requiredCalls).toEqual([]);
246+
expect(cleanup.calls).toEqual([]);
247+
expect(runner.calls).toEqual([]);
248+
});
249+
216250
it.each([
217251
["a missing reference", undefined],
218252
["an empty reference", " "],

test/e2e/support/platform-parity-cloud-experimental.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,4 +752,24 @@ assert_status_mode disabled
752752

753753
expect(env[DCODE_BASE_IMAGE_ENV]).toBe(baseImageReference);
754754
});
755+
756+
it("forwards the contract-selected Deep Agents Code base image reference instead of the ambient publication index", () => {
757+
const indexReference = `${DCODE_BASE_IMAGE}@sha256:${"a".repeat(64)}`;
758+
const platformReference = `${DCODE_BASE_IMAGE}@sha256:${"b".repeat(64)}`;
759+
const env = buildCloudExperimentalCommandEnv(
760+
"deepagents-sandbox",
761+
"secret-key",
762+
{
763+
HOME: "/home/runner",
764+
PATH: "/usr/bin",
765+
[DCODE_BASE_IMAGE_ENV]: indexReference,
766+
},
767+
{
768+
dcodeBaseImageReference: platformReference,
769+
forwardDcodeBaseImage: true,
770+
},
771+
);
772+
773+
expect(env[DCODE_BASE_IMAGE_ENV]).toBe(platformReference);
774+
});
755775
});

0 commit comments

Comments
 (0)