From 5b0217da7cbbcbc29d0d48b0ed60fb9b0d98d1c0 Mon Sep 17 00:00:00 2001 From: Rebecca Sliter <571084+rsliter@users.noreply.github.com> Date: Mon, 17 Aug 2026 06:04:08 -0700 Subject: [PATCH 1/5] test(cli): guard inference set config read exit code --- test/inference-set-config-read-exit.test.ts | 117 ++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 test/inference-set-config-read-exit.test.ts diff --git a/test/inference-set-config-read-exit.test.ts b/test/inference-set-config-read-exit.test.ts new file mode 100644 index 00000000000..07882a628d5 --- /dev/null +++ b/test/inference-set-config-read-exit.test.ts @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { testTimeoutOptions } from "./helpers/timeouts"; + +const CLI = path.join(import.meta.dirname, "..", "bin", "nemoclaw.js"); +const SANDBOX = "issue-9104-alpha"; + +describe("inference set sandbox configuration read failures", () => { + let home: string; + let openshell: string; + let openshellLog: string; + + beforeEach(() => { + home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-9104-")); + openshell = path.join(home, "openshell"); + openshellLog = path.join(home, "openshell.log"); + fs.writeFileSync( + openshell, + [ + "#!/usr/bin/env bash", + `printf '%s\\n' "$*" >> ${JSON.stringify(openshellLog)}`, + "printf '%s\\n' 'exec session setup failed: container not ready' >&2", + "exit 1", + ].join("\n"), + { mode: 0o755 }, + ); + + const registryDir = path.join(home, ".nemoclaw"); + fs.mkdirSync(registryDir, { recursive: true }); + fs.writeFileSync( + path.join(registryDir, "sandboxes.json"), + JSON.stringify({ + sandboxes: { + [SANDBOX]: { + agent: "openclaw", + gpuEnabled: false, + model: "nvidia/llama-3.3-nemotron-super-49b-v1", + name: SANDBOX, + policies: [], + provider: "nvidia-prod", + }, + }, + defaultSandbox: SANDBOX, + }), + { mode: 0o600 }, + ); + }); + + afterEach(() => { + fs.rmSync(home, { force: true, recursive: true }); + }); + + it.each([ + [ + "global", + [ + "inference", + "set", + "--provider", + "nvidia-prod", + "--model", + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", + "--sandbox", + SANDBOX, + "--no-verify", + ], + ], + [ + "sandbox-first", + [ + SANDBOX, + "inference", + "set", + "--provider", + "nvidia-prod", + "--model", + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", + "--no-verify", + ], + ], + ])( + "%s inference set exits with status 1 when OpenShell cannot read the sandbox configuration (#9104)", + testTimeoutOptions(30_000), + (_grammar, argv) => { + const result = spawnSync(process.execPath, [CLI, ...argv], { + encoding: "utf8", + env: { + ...process.env, + HOME: home, + NEMOCLAW_OPENSHELL_BIN: openshell, + NEMOCLAW_STATUS_PROBE_TIMEOUT_MS: "2000", + NEMOCLAW_TEST_NO_SLEEP: "1", + }, + killSignal: "SIGKILL", + timeout: 30_000, + }); + const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`; + + expect(result.error).toBeUndefined(); + expect(result.signal).toBeNull(); + expect(output).toContain("Cannot read openclaw config (/sandbox/.openclaw/openclaw.json)"); + const openshellCalls = fs.readFileSync(openshellLog, "utf8").trim().split("\n"); + expect(openshellCalls).toHaveLength(1); + expect(openshellCalls[0]).toContain("sandbox exec"); + expect(openshellCalls[0]).toContain("cat /sandbox/.openclaw/openclaw.json"); + expect(openshellCalls).not.toContainEqual(expect.stringMatching(/\binference set\b/u)); + expect(result.status).toBe(1); + }, + ); +}); From 2ef2c30931631367c5747471b1395a2b08d92f12 Mon Sep 17 00:00:00 2001 From: Rebecca Sliter <571084+rsliter@users.noreply.github.com> Date: Mon, 17 Aug 2026 06:21:00 -0700 Subject: [PATCH 2/5] test(cli): assert failed inference set preserves state --- test/inference-set-config-read-exit.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/inference-set-config-read-exit.test.ts b/test/inference-set-config-read-exit.test.ts index 07882a628d5..34394346874 100644 --- a/test/inference-set-config-read-exit.test.ts +++ b/test/inference-set-config-read-exit.test.ts @@ -16,6 +16,7 @@ describe("inference set sandbox configuration read failures", () => { let home: string; let openshell: string; let openshellLog: string; + let registryFile: string; beforeEach(() => { home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-9104-")); @@ -34,8 +35,9 @@ describe("inference set sandbox configuration read failures", () => { const registryDir = path.join(home, ".nemoclaw"); fs.mkdirSync(registryDir, { recursive: true }); + registryFile = path.join(registryDir, "sandboxes.json"); fs.writeFileSync( - path.join(registryDir, "sandboxes.json"), + registryFile, JSON.stringify({ sandboxes: { [SANDBOX]: { @@ -89,6 +91,7 @@ describe("inference set sandbox configuration read failures", () => { "%s inference set exits with status 1 when OpenShell cannot read the sandbox configuration (#9104)", testTimeoutOptions(30_000), (_grammar, argv) => { + const registryBefore = fs.readFileSync(registryFile, "utf8"); const result = spawnSync(process.execPath, [CLI, ...argv], { encoding: "utf8", env: { @@ -106,6 +109,8 @@ describe("inference set sandbox configuration read failures", () => { expect(result.error).toBeUndefined(); expect(result.signal).toBeNull(); expect(output).toContain("Cannot read openclaw config (/sandbox/.openclaw/openclaw.json)"); + expect(output).not.toContain("Setting OpenShell inference route"); + expect(fs.readFileSync(registryFile, "utf8")).toBe(registryBefore); const openshellCalls = fs.readFileSync(openshellLog, "utf8").trim().split("\n"); expect(openshellCalls).toHaveLength(1); expect(openshellCalls[0]).toContain("sandbox exec"); From 7a69f034f6417974e5c64a813995aca9ef614c2b Mon Sep 17 00:00:00 2001 From: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:16:53 -0700 Subject: [PATCH 3/5] ci: retrigger dynamic code scanning Signed-off-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com> From 6b953d086d549398eba9f9bf246ec573adcaafff Mon Sep 17 00:00:00 2001 From: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:45:09 -0700 Subject: [PATCH 4/5] ci: trigger checks after automated main sync Signed-off-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com> From d7cda36ea8cafa4da3399143cfda0095a2609b62 Mon Sep 17 00:00:00 2001 From: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com> Date: Mon, 17 Aug 2026 16:04:50 -0700 Subject: [PATCH 5/5] test(e2e): align launchable authorization retry fixture Signed-off-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com> --- test/e2e/support/e2e-collaborator-permission-retry.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/support/e2e-collaborator-permission-retry.test.ts b/test/e2e/support/e2e-collaborator-permission-retry.test.ts index 635d785e2ae..fd352addd78 100644 --- a/test/e2e/support/e2e-collaborator-permission-retry.test.ts +++ b/test/e2e/support/e2e-collaborator-permission-retry.test.ts @@ -36,9 +36,9 @@ const AUTHORIZATION_STEPS: AuthorizationStep[] = [ name: "Authorize release qualification waiver", }, { - deniedMessage: "Launchable image publication requires a repository maintainer or administrator", - mismatchMessage: "Launchable image publication permission response did not match the actor", - name: "Authorize Launchable image publication", + deniedMessage: "Launchable E2E requires a repository maintainer or administrator", + mismatchMessage: "Launchable E2E permission response did not match the actor", + name: "Authorize Launchable E2E maintainer dispatch", }, ];