From 99e25451a138f961f40c7532aa086859a812f4c8 Mon Sep 17 00:00:00 2001 From: freeqaz Date: Thu, 29 Oct 2026 20:26:19 +0000 Subject: [PATCH 1/3] fix(compute): probe the native primary model as the worker gets it On Dedicated OpenClaw the startup model probe copied the authored primary catalog row and provider fragment. A row authored with the provider prefix (openai/) and its own baseUrl passed admission, but OpenClaw's probe refused to route a known model in that shape, so the Agent failed at deploy while the native worker (which gets the split id) would have worked. The probe now gets the primary row nativeRuntimeConfiguration renders for the worker (split id, checked and normalized endpoint) and that endpoint as the provider default, so the unchecked provider baseUrl never reaches it. Admission renders the native catalog through the probe configuration. --- .../src/drivers/compute/kubernetes/index.ts | 45 ++++++++++++------- docs/flows/harness-execution-topology.md | 3 +- tests/conformance/kubernetes-compute.test.mjs | 18 ++++++-- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/apps/controller/src/drivers/compute/kubernetes/index.ts b/apps/controller/src/drivers/compute/kubernetes/index.ts index ec4c9888c9..0d981f85ac 100644 --- a/apps/controller/src/drivers/compute/kubernetes/index.ts +++ b/apps/controller/src/drivers/compute/kubernetes/index.ts @@ -2646,14 +2646,15 @@ function referenceSetting(value: unknown, setting: string): string | undefined { // The startup model probe gets only the selected provider's settings, the primary model's entry // and the binding's credential, so a reference there (other than the binding's apiKey) cannot // resolve. Another Compute may resolve it, so Configuration save accepts it; Kubernetes admission -// names the setting, never its value (finding 2828). On Dedicated OpenClaw the native worker -// gets only the selected catalog entries, so the probe gets only the primary model's entry: no -// other entry can supply its endpoint (findings 2882, 2883). +// names the setting, never its value (finding 2828). On Dedicated OpenClaw (`nativeAuthMethod` +// set) the probe gets the primary model's entry as nativeRuntimeConfiguration renders it for the +// native worker: no other entry can supply its endpoint (findings 2882, 2883), and the probe +// resolves the same model ID and checked endpoint as the worker (finding 2903). function harnessProbeConfiguration( configuration: OpenClawConfigurationDocument, embedded = false, settingError: (setting: string, requirement: string) => Error = preparationSettingError, - native = false, + nativeAuthMethod?: HarnessAuthSnapshot["method"], ): object { const model = harnessPrimaryModel(configuration); const { providerId, environmentName } = harnessModelAuthentication(configuration, embedded); @@ -2678,12 +2679,19 @@ function harnessProbeConfiguration( if (fragment !== undefined && environmentName === CUSTOM_MODEL_API_KEY) { fragment.apiKey = { source: "env", provider: "default", id: CUSTOM_MODEL_API_KEY }; } - if (native && fragment !== undefined) { - const matches = nativeOpenAiModelSelections(configuration)?.find( - (selection) => selection.reference === model, - )?.matches; - // nativeRuntimeConfiguration refuses any other count; the probe never gets another entry. - fragment.models = matches?.length === 1 ? [matches[0]!.entry] : []; + if (nativeAuthMethod !== undefined) { + // An authored row may carry the provider prefix (openai/); OpenClaw's probe then cannot + // route a known model whose row endpoint differs from the provider's, while the worker gets + // the split ID. The rendered row also makes its checked endpoint the provider default, so an + // unchecked provider baseUrl never reaches the probe. + const rendered = nativeRuntimeConfiguration(configuration, settingError, nativeAuthMethod); + const { id } = splitModelRef(model); + // The rendering holds one row for each selected model, the primary included. + const primary = rendered.models.providers.openai.models.find((row) => row.id === id)!; + if (fragment !== undefined) { + fragment.baseUrl = primary.baseUrl; + fragment.models = [primary]; + } } return { agents: { @@ -3049,7 +3057,7 @@ function nativeRuntimeConfiguration( configuration: OpenClawConfigurationDocument, createError: (setting: string, requirement: string) => Error, authMethod: HarnessAuthSnapshot["method"], -): object { +) { const defaults = asRecord(asRecord(configuration.agents)?.defaults); if (asRecord(defaults?.embeddedAgent)?.runtimeServer !== undefined) { throw createError( @@ -4694,11 +4702,14 @@ export class KubernetesComputeDriver implements ComputeDriver { "Harness authentication requires a compatible model provider.", ); } + // On Dedicated OpenClaw this renders the native catalog too (nativeRuntimeConfiguration). if (openclaw) { - harnessProbeConfiguration(configuration, embedded, callerSettingError, native); - } - if (native) { - nativeRuntimeConfiguration(configuration, callerSettingError, auth.method); + harnessProbeConfiguration( + configuration, + embedded, + callerSettingError, + native ? auth.method : undefined, + ); } if (codex) { requireCodexGatewayConfigurationShape(configuration); @@ -15162,7 +15173,9 @@ chmodSync(${JSON.stringify(nodeStatePath)}, 0o700);`; revision.configuration, embedded, preparationSettingError, - revision.harness.mode === "dedicated", + revision.harness.mode === "dedicated" + ? revision.harnessAuth.method + : undefined, ), ), }, diff --git a/docs/flows/harness-execution-topology.md b/docs/flows/harness-execution-topology.md index 1b2fc27af1..b76d8b9904 100644 --- a/docs/flows/harness-execution-topology.md +++ b/docs/flows/harness-execution-topology.md @@ -154,7 +154,8 @@ checks each selected model (`apps/controller/src/drivers/native-openai-catalog.t the selection Compute renders) against the immutable source endpoint before attachment, and a refusal names the `baseUrl` setting and both hosts. Catalog ids match after trimming, as OpenClaw merges such entries, so a second match -fails. The startup model probe gets only the primary model's entry. +fails. The startup model probe gets only the primary model's entry as rendered +for the worker, and its endpoint as the provider default. `apps/controller/src/drivers/compute/kubernetes/runtime-entrypoints.ts:NATIVE_WORKER_ENTRYPOINT` writes capacity/isolation settings and Driver-provided `OPENCLAW_WORKSPACE_DIR`; Compute and Sandbox supply private temporary directories. diff --git a/tests/conformance/kubernetes-compute.test.mjs b/tests/conformance/kubernetes-compute.test.mjs index eb13473c56..e6f96c6683 100644 --- a/tests/conformance/kubernetes-compute.test.mjs +++ b/tests/conformance/kubernetes-compute.test.mjs @@ -1701,6 +1701,9 @@ test("native OpenClaw preparation and activation use only outbound Sandbox enrol "https://models.example.test:443/inference/v1/"; nativeConfiguration.models.providers.openai.api = "openai-completions"; const [selectedEntry] = nativeConfiguration.models.providers.openai.models; + // OpenClaw also names a catalog entry by the full reference; the worker and the startup probe + // get the split model ID (finding 2903). + selectedEntry.id = "openai/team/model"; // The worker gets every selected model; the startup probe checks only the primary. nativeConfiguration.agents.defaults.model = { primary: "openai/team/model", @@ -1844,14 +1847,23 @@ test("native OpenClaw preparation and activation use only outbound Sandbox enrol nativeConfig.models.providers.openai.models.map(({ id }) => id), ["team/model", "team/fallback"], ); - // OpenClaw merges catalog entries whose ids match after trimming, so the startup probe gets - // only the primary model's entry, as written (findings 2882, 2883). + // The startup probe gets only the primary model's entry (OpenClaw merges entries whose ids + // match after trimming; findings 2882, 2883), as the worker gets it: the split model ID and the + // checked endpoint, also as the provider default. OpenClaw refuses to probe a known model under + // the authored prefix when its endpoint differs from the provider's (finding 2903), and the + // unchecked provider baseUrl never reaches the probe. const probeConfig = JSON.parse( provisions[0].requirements.environment.find( ({ name }) => name === "OPENCLAW_HARNESS_PROBE_CONFIG", ).value, ); - assert.deepEqual(probeConfig.models.providers.openai.models, [selectedEntry]); + assert.deepEqual(probeConfig.models.providers.openai.models, [ + nativeConfig.models.providers.openai.models[0], + ]); + assert.equal( + probeConfig.models.providers.openai.baseUrl, + "https://models.example.test/inference/v1", + ); // The provider's observed Pod readiness and enrolled node are independent of provisioning. // Until the provider's Pod is Ready, the wait has no named reason. const podPending = await prepare(); From 49e7f5378155054b4954d7439c810e1c54ed743a Mon Sep 17 00:00:00 2001 From: freeqaz Date: Thu, 29 Oct 2026 20:29:06 +0000 Subject: [PATCH 2/3] style(compute): format the probe call --- apps/controller/src/drivers/compute/kubernetes/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/controller/src/drivers/compute/kubernetes/index.ts b/apps/controller/src/drivers/compute/kubernetes/index.ts index 0d981f85ac..cd49e3b3c2 100644 --- a/apps/controller/src/drivers/compute/kubernetes/index.ts +++ b/apps/controller/src/drivers/compute/kubernetes/index.ts @@ -15173,9 +15173,7 @@ chmodSync(${JSON.stringify(nodeStatePath)}, 0o700);`; revision.configuration, embedded, preparationSettingError, - revision.harness.mode === "dedicated" - ? revision.harnessAuth.method - : undefined, + revision.harness.mode === "dedicated" ? revision.harnessAuth.method : undefined, ), ), }, From b4e315ece555d8b9409b3c5659774642b0b33835 Mon Sep 17 00:00:00 2001 From: freeqaz Date: Thu, 29 Oct 2026 20:54:26 +0000 Subject: [PATCH 3/3] fix(compute): keep the probe row on the provider endpoint The startup probe row carried its own baseUrl, so the runtime's upfront credential check, which skips rows with a baseUrl, no longer ran on Dedicated OpenClaw, even for the standard api_key shape. Give the probe the rendered row without its baseUrl; the provider default already carries the same checked endpoint (finding 2904). --- .../src/drivers/compute/kubernetes/index.ts | 10 +++++++--- docs/flows/harness-execution-topology.md | 2 +- tests/conformance/kubernetes-compute.test.mjs | 19 +++++++++---------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/apps/controller/src/drivers/compute/kubernetes/index.ts b/apps/controller/src/drivers/compute/kubernetes/index.ts index cd49e3b3c2..2ac39132ee 100644 --- a/apps/controller/src/drivers/compute/kubernetes/index.ts +++ b/apps/controller/src/drivers/compute/kubernetes/index.ts @@ -2649,7 +2649,8 @@ function referenceSetting(value: unknown, setting: string): string | undefined { // names the setting, never its value (finding 2828). On Dedicated OpenClaw (`nativeAuthMethod` // set) the probe gets the primary model's entry as nativeRuntimeConfiguration renders it for the // native worker: no other entry can supply its endpoint (findings 2882, 2883), and the probe -// resolves the same model ID and checked endpoint as the worker (finding 2903). +// resolves the same model ID and checked endpoint as the worker (finding 2903). The checked +// endpoint is the provider default, not a row setting (finding 2904). function harnessProbeConfiguration( configuration: OpenClawConfigurationDocument, embedded = false, @@ -2689,8 +2690,11 @@ function harnessProbeConfiguration( // The rendering holds one row for each selected model, the primary included. const primary = rendered.models.providers.openai.models.find((row) => row.id === id)!; if (fragment !== undefined) { - fragment.baseUrl = primary.baseUrl; - fragment.models = [primary]; + // The row inherits its endpoint from the provider, so the runtime's upfront credential + // check, which skips rows with their own baseUrl, still applies (finding 2904). + const { baseUrl, ...row } = primary; + fragment.baseUrl = baseUrl; + fragment.models = [row]; } } return { diff --git a/docs/flows/harness-execution-topology.md b/docs/flows/harness-execution-topology.md index b76d8b9904..ab422636e5 100644 --- a/docs/flows/harness-execution-topology.md +++ b/docs/flows/harness-execution-topology.md @@ -155,7 +155,7 @@ the selection Compute renders) against the immutable source endpoint before attachment, and a refusal names the `baseUrl` setting and both hosts. Catalog ids match after trimming, as OpenClaw merges such entries, so a second match fails. The startup model probe gets only the primary model's entry as rendered -for the worker, and its endpoint as the provider default. +for the worker, with its endpoint moved to the provider default. `apps/controller/src/drivers/compute/kubernetes/runtime-entrypoints.ts:NATIVE_WORKER_ENTRYPOINT` writes capacity/isolation settings and Driver-provided `OPENCLAW_WORKSPACE_DIR`; Compute and Sandbox supply private temporary directories. diff --git a/tests/conformance/kubernetes-compute.test.mjs b/tests/conformance/kubernetes-compute.test.mjs index e6f96c6683..73c16d703e 100644 --- a/tests/conformance/kubernetes-compute.test.mjs +++ b/tests/conformance/kubernetes-compute.test.mjs @@ -1848,22 +1848,21 @@ test("native OpenClaw preparation and activation use only outbound Sandbox enrol ["team/model", "team/fallback"], ); // The startup probe gets only the primary model's entry (OpenClaw merges entries whose ids - // match after trimming; findings 2882, 2883), as the worker gets it: the split model ID and the - // checked endpoint, also as the provider default. OpenClaw refuses to probe a known model under + // match after trimming; findings 2882, 2883), as the worker gets it: the split model ID, and + // the checked endpoint as the provider default. OpenClaw refuses to probe a known model under // the authored prefix when its endpoint differs from the provider's (finding 2903), and the - // unchecked provider baseUrl never reaches the probe. + // unchecked provider baseUrl never reaches the probe. The row carries no baseUrl of its own: + // the runtime's upfront credential check skips rows that do (finding 2904). const probeConfig = JSON.parse( provisions[0].requirements.environment.find( ({ name }) => name === "OPENCLAW_HARNESS_PROBE_CONFIG", ).value, ); - assert.deepEqual(probeConfig.models.providers.openai.models, [ - nativeConfig.models.providers.openai.models[0], - ]); - assert.equal( - probeConfig.models.providers.openai.baseUrl, - "https://models.example.test/inference/v1", - ); + const { baseUrl: workerEndpoint, ...workerPrimary } = + nativeConfig.models.providers.openai.models[0]; + assert.equal(workerEndpoint, "https://models.example.test/inference/v1"); + assert.deepEqual(probeConfig.models.providers.openai.models, [workerPrimary]); + assert.equal(probeConfig.models.providers.openai.baseUrl, workerEndpoint); // The provider's observed Pod readiness and enrolled node are independent of provisioning. // Until the provider's Pod is Ready, the wait has no named reason. const podPending = await prepare();