Repository navigation
fix(mcp): restore managed image credential discovery #9792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+1,383
−216
Merged
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
e94c514
fix(ci): align managed MCP supervisor version
prekshivyas e769c75
ci(mcp): classify OpenShell revision failure
prekshivyas e950965
ci(mcp): require preset failure evidence
prekshivyas 52b23ee
fix(ci): bind MCP waiver to candidate commit
senthilr-nv 290799f
ci(mcp): remove OpenShell revision waiver
prekshivyas c6e8280
test(ci): require managed MCP supervisor pins
prekshivyas 168cff5
fix(ci): preserve managed MCP qualification environment
prekshivyas 9b0725d
fix(ci): activate exact managed MCP image
prekshivyas fcfad8e
test(mcp): classify credential sync failures
prekshivyas 838e177
fix(mcp): classify gateway inference credentials
prekshivyas 636409e
fix(mcp): resolve revisioned discovery credentials
prekshivyas e4c5537
fix(mcp): project live credential revision to mcporter
prekshivyas 5df42c1
ci(mcp): qualify bridge changes in managed images
prekshivyas 00aea74
Merge remote-tracking branch 'origin/main' into codex/fix-mcp-live-cr…
prekshivyas 6d1108f
test(mcp): scope managed image discovery topology
prekshivyas f250e90
fix(mcp): use live revision in rewrite probe
prekshivyas 47a3a18
merge(main): refresh PR branch
senthilr-nv 2432b0d
test(mcp): track full-scope raw policy proof
senthilr-nv 52b2db1
merge(main): include latest base update
senthilr-nv 5f5266c
fix(rebuild): restore MCP policies through owned lifecycle
prekshivyas 75aefaf
fix(onboard): preserve journaled policy selection
prekshivyas a51adb1
fix(rebuild): carry filtered MCP policy presets
prekshivyas 6c05be2
fix(mcp): verify exact adapter credential revision
prekshivyas bccdcf7
fix(rebuild): reseed policies after owned delete
cjagwani 83e992c
fix(ci): wait for public managed digest
cjagwani 4136947
test(e2e): expose MCP rebuild diagnostics
cjagwani eea65ac
fix(rebuild): persist filtered policy resume intent
jyaunches d79472d
fix(rebuild): preserve journaled MCP policy ownership
cjagwani 992f81d
Merge branch 'main' into codex/fix-mcp-live-credential-projection
cjagwani File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import fs from "node:fs"; | ||
| import os from "node:os"; | ||
| import path from "node:path"; | ||
|
|
||
| import { afterEach, describe, expect, it } from "vitest"; | ||
|
|
||
| import { assertOpenShell2847FailureEvidence } from "../tools/e2e/assert-openshell-2847-mcp-failure.mts"; | ||
|
|
||
| const roots: string[] = []; | ||
| const resultNames = [ | ||
| "openclaw-mcp-concurrent-add-first.result.json", | ||
| "openclaw-mcp-concurrent-add-second.result.json", | ||
| ]; | ||
|
|
||
| function writeJson(file: string, value: unknown): void { | ||
| fs.mkdirSync(path.dirname(file), { recursive: true }); | ||
| fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`); | ||
| } | ||
|
|
||
| function makeEvidence(): string { | ||
| const parent = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-openshell-2847-")); | ||
| roots.push(parent); | ||
| const root = path.join(parent, "pass-1"); | ||
| const shell = path.join(root, "mcp-bridge", "shell"); | ||
| const stderr = [ | ||
| "\u001b[32m\u2713\u001b[0m Policy version 5 submitted (hash: abcdef012345)", | ||
| "\u001b[32m\u2713\u001b[0m Policy version 5 loaded (active version: 5)", | ||
| "\u001b[32m\u2713\u001b[0m Policy version 6 submitted (hash: bcdef0123456)", | ||
| "\u001b[32m\u2713\u001b[0m Policy version 6 loaded (active version: 6)", | ||
| "Preset not found: mcp-bridge-concurrent", | ||
| "\u001b[32m\u2713\u001b[0m Policy version 7 submitted (hash: cdef01234567)", | ||
| "\u001b[32m\u2713\u001b[0m Policy version 7 loaded (active version: 7)", | ||
| "OpenShell did not synchronize the expected credential revision for placeholder 'FAKE_MCP_SECRET' into sandbox 'e2e-pr-exact-mcp-1' after provider attachment or update.", | ||
| "", | ||
| ].join("\n"); | ||
| for (const name of resultNames) { | ||
| writeJson(path.join(shell, name), { | ||
| exitCode: 1, | ||
| signal: null, | ||
| stderr, | ||
| timedOut: false, | ||
| }); | ||
| } | ||
| writeJson(path.join(root, "risk-signal.json"), { | ||
| version: 1, | ||
| jobId: "mcp-bridge", | ||
| shardId: "openclaw", | ||
| expectedSha: "a".repeat(40), | ||
| testedSha: "a".repeat(40), | ||
| passed: 0, | ||
| failed: 1, | ||
| skipped: 0, | ||
| pending: 0, | ||
| unhandledErrors: 0, | ||
| runReason: "failed", | ||
| }); | ||
| writeJson(path.join(root, "mcp-bridge", "cleanup.json"), { | ||
| passed: ["destroy sandbox e2e-pr-exact-mcp-1"], | ||
| failures: [], | ||
| }); | ||
| return root; | ||
| } | ||
|
|
||
| afterEach(() => { | ||
| for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); | ||
| }); | ||
|
|
||
| describe("OpenShell credential-revision failure evidence", () => { | ||
| it("accepts both exact credential-revision failures with completed cleanup", () => { | ||
| const root = makeEvidence(); | ||
|
|
||
| expect(assertOpenShell2847FailureEvidence(root)).toBe("e2e-pr-exact-mcp-1"); | ||
| }); | ||
|
|
||
| it("rejects a different command failure", () => { | ||
| const root = makeEvidence(); | ||
| const resultFile = path.join(root, "mcp-bridge", "shell", resultNames[0]); | ||
| const result = JSON.parse(fs.readFileSync(resultFile, "utf8")) as Record<string, unknown>; | ||
| result.stderr = "OpenClaw tool discovery failed\n"; | ||
| writeJson(resultFile, result); | ||
|
|
||
| expect(() => assertOpenShell2847FailureEvidence(root)).toThrow( | ||
| /different terminal diagnostic/u, | ||
| ); | ||
| }); | ||
|
|
||
| it("rejects incomplete sandbox cleanup", () => { | ||
| const root = makeEvidence(); | ||
| writeJson(path.join(root, "mcp-bridge", "cleanup.json"), { | ||
| passed: [], | ||
| failures: ["destroy sandbox e2e-pr-exact-mcp-1"], | ||
| }); | ||
|
|
||
| expect(() => assertOpenShell2847FailureEvidence(root)).toThrow( | ||
| /does not confirm sandbox cleanup/u, | ||
| ); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import fs from "node:fs"; | ||
| import path from "node:path"; | ||
| import { pathToFileURL } from "node:url"; | ||
|
|
||
| const ANSI_ESCAPE = /\u001b\[[0-9;]*m/gu; | ||
| const RESULT_BASENAMES = [ | ||
| "openclaw-mcp-concurrent-add-first.result.json", | ||
| "openclaw-mcp-concurrent-add-second.result.json", | ||
| ] as const; | ||
| const POLICY_STATUS = | ||
| /^\u2713 Policy version \d+ (?:submitted \(hash: [0-9a-f]+\)|loaded \(active version: \d+\))$/u; | ||
| const REVISION_FAILURE = | ||
| /^OpenShell did not synchronize the expected credential revision for placeholder 'FAKE_MCP_SECRET' into sandbox '(e2e-pr-exact-mcp-([12]))' after provider attachment or update\.$/u; | ||
|
|
||
| type JsonObject = Record<string, unknown>; | ||
|
|
||
| function readJsonObject(file: string): JsonObject { | ||
| const stat = fs.lstatSync(file); | ||
| if (!stat.isFile() || stat.isSymbolicLink()) { | ||
| throw new Error(`OpenShell #2847 evidence is not a regular file: ${file}`); | ||
| } | ||
| const value: unknown = JSON.parse(fs.readFileSync(file, "utf8")); | ||
| if (value === null || typeof value !== "object" || Array.isArray(value)) { | ||
| throw new Error(`OpenShell #2847 evidence is not a JSON object: ${file}`); | ||
| } | ||
| return value as JsonObject; | ||
| } | ||
|
|
||
| function commandSandbox(file: string, expectedPass: string): string { | ||
| const result = readJsonObject(file); | ||
| if (result.exitCode !== 1 || result.timedOut !== false || result.signal !== null) { | ||
| throw new Error(`OpenShell #2847 result has an unexpected process outcome: ${file}`); | ||
| } | ||
| if (typeof result.stderr !== "string") { | ||
| throw new Error(`OpenShell #2847 result has no stderr text: ${file}`); | ||
| } | ||
| const lines = result.stderr | ||
| .replace(ANSI_ESCAPE, "") | ||
| .split(/\r?\n/u) | ||
| .map((line) => line.trim()) | ||
| .filter(Boolean); | ||
| const failure = lines.at(-1)?.match(REVISION_FAILURE); | ||
| if (!failure || failure[2] !== expectedPass) { | ||
| throw new Error(`OpenShell #2847 result has a different terminal diagnostic: ${file}`); | ||
| } | ||
| if ( | ||
| lines.length < 7 || | ||
| lines | ||
| .slice(0, -1) | ||
| .some( | ||
| (line) => line !== "Preset not found: mcp-bridge-concurrent" && !POLICY_STATUS.test(line), | ||
| ) | ||
| ) { | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| throw new Error(`OpenShell #2847 result contains an unexpected diagnostic: ${file}`); | ||
| } | ||
| return failure[1]; | ||
| } | ||
|
|
||
| export function assertOpenShell2847FailureEvidence(artifactDirectory: string): string { | ||
| const root = path.resolve(artifactDirectory); | ||
| const pass = path.basename(root).match(/^pass-([12])$/u)?.[1]; | ||
| if (!pass) throw new Error("OpenShell #2847 evidence directory must end in pass-1 or pass-2"); | ||
|
|
||
| const sandboxes = RESULT_BASENAMES.map((basename) => | ||
| commandSandbox(path.join(root, "mcp-bridge", "shell", basename), pass), | ||
| ); | ||
| if (new Set(sandboxes).size !== 1) { | ||
| throw new Error("OpenShell #2847 command results identify different sandboxes"); | ||
| } | ||
| const sandbox = sandboxes[0]; | ||
|
|
||
| const signal = readJsonObject(path.join(root, "risk-signal.json")); | ||
| const expectedSignal: JsonObject = { | ||
| version: 1, | ||
| jobId: "mcp-bridge", | ||
| shardId: "openclaw", | ||
| passed: 0, | ||
| failed: 1, | ||
| skipped: 0, | ||
| pending: 0, | ||
| unhandledErrors: 0, | ||
| runReason: "failed", | ||
| }; | ||
| if ( | ||
| Object.entries(expectedSignal).some(([key, value]) => signal[key] !== value) || | ||
| typeof signal.expectedSha !== "string" || | ||
| !/^[0-9a-f]{40}$/u.test(signal.expectedSha) || | ||
| signal.testedSha !== signal.expectedSha | ||
| ) { | ||
| throw new Error("OpenShell #2847 evidence has an unexpected E2E risk signal"); | ||
| } | ||
|
|
||
| const cleanup = readJsonObject(path.join(root, "mcp-bridge", "cleanup.json")); | ||
| if ( | ||
| !Array.isArray(cleanup.failures) || | ||
| cleanup.failures.length !== 0 || | ||
| !Array.isArray(cleanup.passed) || | ||
| !cleanup.passed.includes(`destroy sandbox ${sandbox}`) | ||
| ) { | ||
| throw new Error("OpenShell #2847 evidence does not confirm sandbox cleanup"); | ||
| } | ||
| return sandbox; | ||
| } | ||
|
|
||
| if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { | ||
| try { | ||
| const root = process.argv[2]; | ||
| if (!root || process.argv.length !== 3) { | ||
| throw new Error( | ||
| "Usage: npx tsx tools/e2e/assert-openshell-2847-mcp-failure.mts ARTIFACT_DIR", | ||
| ); | ||
| } | ||
| const sandbox = assertOpenShell2847FailureEvidence(root); | ||
| console.log( | ||
| `::warning title=OpenShell #2847::Accepted the exact provider credential revision failure for ${sandbox}. Remove this waiver after NVIDIA/OpenShell#2847 is released and pinned.`, | ||
| ); | ||
| } catch (error) { | ||
| console.error(`::error::${error instanceof Error ? error.message : String(error)}`); | ||
| process.exitCode = 1; | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.