Skip to content

feat(onboard): construct managed startup profiles - #7959

Merged
ericksoa merged 10 commits into
mainfrom
feat/buildless-image-runtime-construction
Jul 31, 2026
Merged

feat(onboard): construct managed startup profiles#7959
ericksoa merged 10 commits into
mainfrom
feat/buildless-image-runtime-construction

Conversation

@ericksoa

@ericksoa ericksoa commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Constructs the dormant managed startup profile and image-runtime plan for OpenClaw, Hermes, and LangChain Deep Agents Code. It converts an exact managed-image selection into bounded, agent-specific startup data without applying state, selecting a container engine, or activating buildless onboarding.

Related Issue

Part of #7744

Changes

  • Add a bounded non-agent hold contract for managed startup.
  • Construct versioned startup profiles for OpenClaw, Hermes, and DCode from explicit onboarding inputs.
  • Build image-runtime, onboard-profile, and profile-builder contracts without reading process-global container-engine state.
  • Preserve the existing proxy and no-proxy behavior through the shared host proxy mapper.
  • Validate agent/platform-specific profile construction, unsupported combinations, and exact input normalization.
  • Keep the slice dormant: it performs no application/shared-state mutation and has no production onboarding caller.

Type of Change

  • 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)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: This is an inert internal construction layer with no production caller, CLI/configuration change, runtime activation, or support claim.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Exact-diff and independent P1/P2 reviews confirmed bounded all-agent construction, no credential material in the profile, no engine-specific central switch, and no production activation.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: no-docs-needed
  • Evidence: The exact eight-file diff (+2,714/-1) adds dormant managed-profile construction and tests only. It changes no user-visible command, configuration, default, workflow, or support statement.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • Exact validated head/base: 2a048aeecb4bd491daadc3c109f9b845031a6e04 / 37f6a8a650ff500f7054bae8d36b00e7b42edd41
  • Review budget: 8 files, +2,714/-1; no documentation paths.
  • 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
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: 156 focused/current-base tests passed, including 54 slice-only construction tests; CLI and plugin builds passed; npm run validate:pr passed; git diff --check is clean.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result: Exact-head required CI is the broad gate for this inert slice.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Stack

  • Base: main at merged PR3.2 commit 37f6a8a650ff500f7054bae8d36b00e7b42edd41
  • This slice: PR3.3 branch feat/buildless-image-runtime-construction at 2a048aeecb4bd491daadc3c109f9b845031a6e04
  • Next: PR3.4a maps and coordinates the application plan. It is not part of this review diff.
  • Buildless support remains disabled until every supported agent and required qualification gate passes.

Signed-off-by: Aaron Erickson aerickson@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added managed startup profile creation for OpenClaw, Hermes, and DCode.
    • Added validation for dashboards, proxies, certificates, inference, tools, messaging, and environment settings.
    • Added secure handling to prevent proxy credentials and other secret material from leaking.
    • Added startup action planning with validated command ordering and execution settings.
    • Added support for managed startup hold behavior.
  • Tests

    • Expanded coverage for profile creation, startup actions, validation, configuration, and security safeguards.

ericksoa added 9 commits July 30, 2026 14:50
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Managed startup orchestration

Layer / File(s) Summary
Profile validation and transport
src/lib/onboard/managed-startup/profile-builder.ts, src/lib/onboard/managed-startup-profile-builder.test.ts
Validates agent-specific inputs, environment semantics, proxy settings, messaging, corporate CA material, and tuning before producing encoded profile transport and digest data.
Onboarding profile resolution
src/lib/onboard/host-proxy-env.ts, src/lib/onboard/managed-startup/onboard-profile.ts, src/lib/onboard/managed-startup-onboard-profile.test.ts
Resolves dashboard modes, filters environment variables, excludes credential-bearing proxy values, and tracks proxy credential replay requirements.
Managed startup image action planning
src/lib/onboard/managed-startup/image-runtime.ts, src/lib/onboard/managed-startup/hold.ts, src/lib/onboard/managed-startup-image-runtime.test.ts
Validates agent-specific image actions and emits immutable, ordered command descriptors with required messaging and execution identities.

Estimated code review effort: 5 (Critical) | ~90+ minutes

Sequence Diagram(s)

sequenceDiagram
  participant Onboarding as buildManagedStartupOnboardProfile
  participant Builder as buildManagedStartupProfile
  participant Planner as buildManagedStartupImageActionPlan
  Onboarding->>Builder: resolved profile inputs
  Builder-->>Onboarding: encoded profile and digest
  Onboarding->>Planner: agent image actions and dashboard state
  Planner-->>Onboarding: immutable ordered commands
Loading

Possibly related PRs

  • NVIDIA/NemoClaw#7946: Provides the managed startup profile schema, capabilities, validation rules, and transport types consumed by these builders and tests.

Suggested reviewers: cv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the PR's primary change: constructing managed startup profiles.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/buildless-image-runtime-construction

Comment @coderabbitai help to get the list of available commands.

@ericksoa ericksoa added area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: security Security controls, permissions, secrets, or hardening feature PR adds or expands user-visible functionality platform: container Affects Docker, containerd, Podman, or images labels Jul 31, 2026
@github-code-quality

github-code-quality Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 2a048ae in the feat/buildless-image... branch remains at 96%, unchanged from commit 37f6a8a in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 2a048ae in the feat/buildless-image... branch remains at 81%, unchanged from commit 37f6a8a in the main branch.

Show a code coverage summary of the most impacted files.
File main 37f6a8a feat/buildless-image... 2a048ae +/-
src/lib/onboard...st-proxy-env.ts 95% 83% -12%
src/lib/inferen...ompatibility.ts 95% 94% -1%
src/lib/messagi.../persistence.ts 92% 92% 0%
src/lib/onboard...startup/hold.ts 0% 0% 0%
src/lib/onboard...rtup/profile.ts 93% 93% 0%
src/lib/sandbox...rce-identity.ts 88% 88% 0%
src/lib/onboard...file-builder.ts 0% 90% +90%
src/lib/onboard...oard-profile.ts 0% 92% +92%
src/lib/onboard...mage-runtime.ts 0% 93% +93%

Updated July 31, 2026 02:05 UTC

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · medium confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 3 warnings · 3 suggestions
  • Model comparison: normalized findings differ; normalized E2E selections differ; Nemotron reported the same number of blockers, 3 more warnings, 3 more suggestions.

Nemotron output stays in workflow artifacts and does not change the assessment above.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: onboard-repair, onboard-resume, cloud-onboard

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

Base automatically changed from feat/buildless-startup-profile-v2 to main July 31, 2026 01:51
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (8)
src/lib/onboard/host-proxy-env.ts (1)

15-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated HTTP/HTTPS proxy variable-name tuple across two modules. The same four proxy URL variable names are declared twice, so adding or renaming one (for example ALL_PROXY) silently desynchronizes credential filtering from profile selection.

  • src/lib/onboard/host-proxy-env.ts#L15-L20: export HOST_PROXY_URL_ENV_NAMES as the single source of truth.
  • src/lib/onboard/managed-startup/onboard-profile.ts#L44-L44: delete the local HOST_PROXY_URL_INPUTS tuple and import the exported constant instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/host-proxy-env.ts` around lines 15 - 20, Export
HOST_PROXY_URL_ENV_NAMES from host-proxy-env.ts as the shared source of truth.
In onboard-profile.ts, remove the local HOST_PROXY_URL_INPUTS tuple and import
and reuse HOST_PROXY_URL_ENV_NAMES for profile selection; update both affected
files accordingly.
src/lib/onboard/managed-startup-profile-builder.test.ts (3)

454-458: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the branch on a constant fixture.

PEM is a fixed fixture, so one side of this ternary is dead and it silently encodes an assumption about normalizeCertificateBlocks. Asserting against the expected normalized text directly keeps the claim explicit.

As per path instructions: "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup-profile-builder.test.ts` around lines 454 -
458, Update the assertions in the test around built.corporateCaB64 and
built.profile.corporateCa.bundleSha256 to use the fixture’s expected normalized
PEM text directly, removing the conditional normalizedPem calculation. Keep both
assertions validating the base64 encoding and SHA-256 digest of that explicit
expected value.

Source: Path instructions


300-303: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Digest assertion re-derives the production computation.

Recomputing sha256(encodedProfile) here passes for whatever the builder does, so it cannot detect a change of hash input or algorithm. Pinning one literal digest for a fixed input profile would make this assertion load-bearing.

As per path instructions: "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup-profile-builder.test.ts` around lines 300 -
303, The test around the managed startup profile builder currently recomputes
the production digest instead of validating a fixed expected value. Replace the
`createHash("sha256")...` assertion in the test with a literal SHA-256 digest
for the fixed `built.encodedProfile` fixture, while preserving the existing
decoded-profile assertion.

Source: Path instructions


573-597: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Bare toThrow() cannot prove the secret screen fired.

Each of these three cases can throw for unrelated reasons (proxy alias handling, extra-agents shape, generic profile validation), so the suite would stay green if credential screening regressed. Assert the specific rejection message per case, as the neighboring it.each blocks do.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup-profile-builder.test.ts` around lines 573 -
597, Update the parameterized test around buildManagedStartupProfile to assert
the specific secret-screening rejection message for each input instead of using
bare toThrow(). Provide the expected message alongside each case, following the
neighboring it.each tests, so proxy credentials, model secrets, and extra-agent
API keys each verify the intended rejection.

Source: Path instructions

src/lib/onboard/managed-startup-onboard-profile.test.ts (1)

386-390: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

JSON.stringify(built) cannot see inside encodedProfile.

encodedProfile is base64url, so a leaked credential there would not appear as plaintext in the serialized string. Decoding it before the not.toContain assertions would make the leak check cover the transport too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup-onboard-profile.test.ts` around lines 386 -
390, Update the credential-leak assertions around serialized built data to also
inspect the decoded contents of encodedProfile, since JSON.stringify(built) only
checks the base64url transport value. Decode encodedProfile before applying the
existing not.toContain checks, while preserving the current serialized-object
assertions.
src/lib/onboard/managed-startup/profile-builder.ts (1)

589-611: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Legacy-Docker env reconciliation has no reachable consumer.

profileEnvironment in src/lib/onboard/managed-startup/onboard-profile.ts (Lines 168-193) passes only the PROFILE_ENVIRONMENT_INPUTS allowlist plus proxy vars. NEMOCLAW_MODEL, CHAT_UI_URL, NEMOCLAW_TOOL_DISCLOSURE, NEMOCLAW_INFERENCE_*, NEMOCLAW_MESSAGING_PLAN_B64, NEMOCLAW_HERMES_*, NEMOCLAW_DCODE_AUTO_APPROVAL, and NEMOCLAW_OBSERVABILITY are never present, so most of this ~190-line reconciliation block is unreachable through the only caller and is exercised only by tests that synthesize the legacy environment themselves.

Consider narrowing this to the knobs the allowlist can actually deliver, and tracking the legacy-Docker migration path in a linked issue instead of carrying the branch now.

As per coding guidelines: "Do not add configuration, fallback, migration, compatibility, or extension layers without a current requirement; identify the current consumer and protecting test."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/profile-builder.ts` around lines 589 - 611,
Narrow assertEnvironmentConsistency to validate only environment variables
delivered by profileEnvironment through PROFILE_ENVIRONMENT_INPUTS and proxy
variables. Remove reconciliation branches for legacy Docker-only variables with
no current consumer, and retain tests only for the reachable allowlisted inputs.
Track the deferred legacy-Docker migration separately rather than preserving
this unreachable compatibility path.

Source: Coding guidelines

src/lib/onboard/managed-startup/image-runtime.ts (1)

15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: centralize the "which agents have messaging" fact.

The messaging-capable agent set is encoded three times (the literal union at Line 15, and the langchain-deepagents-code checks at Lines 213 and 223). Deriving them from one source keeps a fourth agent from being added inconsistently.

♻️ Suggested consolidation
-export type ManagedStartupMessagingAgent = "openclaw" | "hermes";
+export type ManagedStartupMessagingAgent = Exclude<ManagedStartupAgent, "langchain-deepagents-code">;
+
+function hasMessagingPhases(agent: ManagedStartupAgent): boolean {
+  return agent !== "langchain-deepagents-code";
+}
-  const expectedMessagingActions = inputAgent === "langchain-deepagents-code" ? 0 : 1;
+  const expectedMessagingActions = hasMessagingPhases(inputAgent) ? 1 : 0;
@@
-  const expectedOrder =
-    inputAgent === "langchain-deepagents-code"
-      ? ["generate-agent-config"]
-      : ["messaging-runtime-setup", "generate-agent-config", "messaging-post-agent-install"];
+  const expectedOrder = hasMessagingPhases(inputAgent)
+    ? ["messaging-runtime-setup", "generate-agent-config", "messaging-post-agent-install"]
+    : ["generate-agent-config"];

Also applies to: 213-225

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/image-runtime.ts` at line 15, Centralize the
messaging-capable agent definition used by ManagedStartupMessagingAgent and the
langchain-deepagents-code checks around the startup logic. Update the checks
near lines 213–225 to derive their behavior from that shared source, so adding
another messaging agent requires changing only one definition while preserving
current openclaw and hermes behavior.
src/lib/onboard/managed-startup-image-runtime.test.ts (1)

130-230: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add the uncovered fail-closed cases.

The matrix is thorough but leaves every dashboard invariant untested, plus the unknown-agent and invalid-mode guards:

  • missing dashboard action and duplicate dashboard actions → exactly one dashboard construction action is required (image-runtime.ts Line 211)
  • dashboard whose agent differs from input.agentdashboard for … cannot be used by … (Line 158)
  • unknown input.agentunsupported agent (Line 88)
  • mode outside apply/clear (Line 179)

The dashboard checks matter most here since that action emits no command, so these guards are its only observable behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup-image-runtime.test.ts` around lines 130 -
230, Add fail-closed parameterized cases to the existing
buildManagedStartupImageActionPlan test matrix for missing and duplicate
dashboard actions, asserting the exact dashboard-count error; a dashboard agent
mismatch, asserting the agent mismatch error; an unsupported input.agent,
asserting the unsupported-agent error; and an invalid mode outside apply/clear,
asserting rejection. Reuse the existing actionInput fixtures and preserve the
current construction-contract test structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/onboard/managed-startup/hold.ts`:
- Line 4: Remove the unused MANAGED_STARTUP_HOLD_EXECUTABLE export from the
managed-startup hold module, since no implementation or tests consume it. Only
retain the constant if you also wire it into the managed-startup path and add a
test that verifies its usage.

In `@src/lib/onboard/managed-startup/image-runtime.ts`:
- Around line 114-128: The messagingCommand contract does not propagate the
requested mode, preventing clear operations from reaching the applier. Update
messagingCommand and the messaging applier argument handling to thread mode
through and implement the clear branch, or remove mode from the contract and all
callers if it is not required; ensure clear does not reapply the plan.

In `@src/lib/onboard/managed-startup/onboard-profile.ts`:
- Around line 122-125: Update the URL handling in the managed startup onboarding
flow around the remote calculation so malformed or empty input.chatUiUrl values
are caught and surfaced as ManagedStartupOnboardProfileError rather than
allowing new URL to throw a raw TypeError. Preserve the existing localhost and
remote-host detection behavior for valid URLs, including the early disabled
path.
- Around line 201-203: Remove the unused credentialProxyReplayRequired
declaration, or wire it into the credential proxy replay path so
non-"langchain-deepagents-code" agents receive the intended replay behavior.
Trace the existing replay flow in the onboarding logic and ensure the flag
directly controls that behavior rather than remaining an unconsumed value.

In `@src/lib/onboard/managed-startup/profile-builder.ts`:
- Around line 271-277: Update the top-level array handling in the profile
builder to validate every element with the same plain-object validation used by
the object branch’s agents processing, instead of unchecked-casting the array.
Reject invalid entries such as numbers or null before constructing the returned
agents profile, while preserving the existing defaults and main values for valid
arrays.
- Around line 891-895: Update assertAgentSpecificInput to reject non-null
inference.compatibility and non-null input modalities when input.agent is not
"openclaw", instead of silently discarding them in the profile-building flow.
Preserve the existing OpenClaw parsing and compatibility behavior, and apply the
same fail-closed cross-agent validation used for other agent-specific fields.

---

Nitpick comments:
In `@src/lib/onboard/host-proxy-env.ts`:
- Around line 15-20: Export HOST_PROXY_URL_ENV_NAMES from host-proxy-env.ts as
the shared source of truth. In onboard-profile.ts, remove the local
HOST_PROXY_URL_INPUTS tuple and import and reuse HOST_PROXY_URL_ENV_NAMES for
profile selection; update both affected files accordingly.

In `@src/lib/onboard/managed-startup-image-runtime.test.ts`:
- Around line 130-230: Add fail-closed parameterized cases to the existing
buildManagedStartupImageActionPlan test matrix for missing and duplicate
dashboard actions, asserting the exact dashboard-count error; a dashboard agent
mismatch, asserting the agent mismatch error; an unsupported input.agent,
asserting the unsupported-agent error; and an invalid mode outside apply/clear,
asserting rejection. Reuse the existing actionInput fixtures and preserve the
current construction-contract test structure.

In `@src/lib/onboard/managed-startup-onboard-profile.test.ts`:
- Around line 386-390: Update the credential-leak assertions around serialized
built data to also inspect the decoded contents of encodedProfile, since
JSON.stringify(built) only checks the base64url transport value. Decode
encodedProfile before applying the existing not.toContain checks, while
preserving the current serialized-object assertions.

In `@src/lib/onboard/managed-startup-profile-builder.test.ts`:
- Around line 454-458: Update the assertions in the test around
built.corporateCaB64 and built.profile.corporateCa.bundleSha256 to use the
fixture’s expected normalized PEM text directly, removing the conditional
normalizedPem calculation. Keep both assertions validating the base64 encoding
and SHA-256 digest of that explicit expected value.
- Around line 300-303: The test around the managed startup profile builder
currently recomputes the production digest instead of validating a fixed
expected value. Replace the `createHash("sha256")...` assertion in the test with
a literal SHA-256 digest for the fixed `built.encodedProfile` fixture, while
preserving the existing decoded-profile assertion.
- Around line 573-597: Update the parameterized test around
buildManagedStartupProfile to assert the specific secret-screening rejection
message for each input instead of using bare toThrow(). Provide the expected
message alongside each case, following the neighboring it.each tests, so proxy
credentials, model secrets, and extra-agent API keys each verify the intended
rejection.

In `@src/lib/onboard/managed-startup/image-runtime.ts`:
- Line 15: Centralize the messaging-capable agent definition used by
ManagedStartupMessagingAgent and the langchain-deepagents-code checks around the
startup logic. Update the checks near lines 213–225 to derive their behavior
from that shared source, so adding another messaging agent requires changing
only one definition while preserving current openclaw and hermes behavior.

In `@src/lib/onboard/managed-startup/profile-builder.ts`:
- Around line 589-611: Narrow assertEnvironmentConsistency to validate only
environment variables delivered by profileEnvironment through
PROFILE_ENVIRONMENT_INPUTS and proxy variables. Remove reconciliation branches
for legacy Docker-only variables with no current consumer, and retain tests only
for the reachable allowlisted inputs. Track the deferred legacy-Docker migration
separately rather than preserving this unreachable compatibility path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9ec441c3-34cd-47e8-91cb-8835aeeeba90

📥 Commits

Reviewing files that changed from the base of the PR and between 37f6a8a and 2a048ae.

📒 Files selected for processing (8)
  • src/lib/onboard/host-proxy-env.ts
  • src/lib/onboard/managed-startup-image-runtime.test.ts
  • src/lib/onboard/managed-startup-onboard-profile.test.ts
  • src/lib/onboard/managed-startup-profile-builder.test.ts
  • src/lib/onboard/managed-startup/hold.ts
  • src/lib/onboard/managed-startup/image-runtime.ts
  • src/lib/onboard/managed-startup/onboard-profile.ts
  • src/lib/onboard/managed-startup/profile-builder.ts

// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

export const MANAGED_STARTUP_HOLD_EXECUTABLE = "/usr/local/bin/nemoclaw-managed-startup-hold";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find consumers of the hold executable constant and its literal path.
rg -n --type=ts 'MANAGED_STARTUP_HOLD_EXECUTABLE|nemoclaw-managed-startup-hold'

Repository: NVIDIA/NemoClaw

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- all repository references ---'
rg -n 'MANAGED_STARTUP_HOLD_EXECUTABLE|nemoclaw-managed-startup-hold' . || true
printf '%s\n' '--- relevant files ---'
fd -i 'hold|image-runtime|managed-startup' src tests 2>/dev/null || true
printf '%s\n' '--- hold.ts ---'
hold="$(fd -i -t f 'hold.ts' src | head -n 1)"
if [ -n "${hold}" ]; then cat -n "${hold}"; fi
printf '%s\n' '--- image-runtime candidates ---'
while IFS= read -r f; do
  case "$f" in *image-runtime*) cat -n "$f";; esac
done < <(fd -i -t f . src)
printf '%s\n' '--- nearby tests and package scripts ---'
rg -n -i 'managed.startup|image-runtime|hold executable|startup hold|nemoclaw-managed' src tests package.json 2>/dev/null || true

Repository: NVIDIA/NemoClaw

Length of output: 1275


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- image-runtime.ts ---'
cat -n src/lib/onboard/managed-startup/image-runtime.ts
printf '%s\n' '--- managed-startup-image-runtime.test.ts ---'
cat -n src/lib/onboard/managed-startup-image-runtime.test.ts
printf '%s\n' '--- managed-startup directory ---'
for f in src/lib/onboard/managed-startup/*; do
  printf '\n--- %s ---\n' "$f"
  cat -n "$f"
done

Repository: NVIDIA/NemoClaw

Length of output: 50372


Remove or wire MANAGED_STARTUP_HOLD_EXECUTABLE.

The constant has no consumer, and the managed-startup implementation and tests do not reference it. Add its consumer and protecting test, or remove it until that path is implemented.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/hold.ts` at line 4, Remove the unused
MANAGED_STARTUP_HOLD_EXECUTABLE export from the managed-startup hold module,
since no implementation or tests consume it. Only retain the constant if you
also wire it into the managed-startup path and add a test that verifies its
usage.

Source: Coding guidelines

Comment on lines +114 to +128
function messagingCommand(
agent: ManagedStartupMessagingAgent,
phase: "runtime-setup" | "post-agent-install",
): readonly string[] {
return [
"/usr/local/bin/node",
"--experimental-strip-types",
"/src/lib/messaging/applier/build/messaging-build-applier.mts",
"--agent",
agent,
"--phase",
phase,
...(phase === "post-agent-install" ? ["--managed-startup-runtime"] : []),
];
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the messaging build applier's CLI contract and how it decides apply vs clear.
fd -t f 'messaging-build-applier' -x ast-grep outline {} --items all
rg -n --type=ts -C3 -- '--phase|--managed-startup-runtime|"clear"|"apply"' $(fd -t f 'messaging-build-applier')

Repository: NVIDIA/NemoClaw

Length of output: 14420


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- image-runtime ---'
sed -n '1,180p' src/lib/onboard/managed-startup/image-runtime.ts
printf '%s\n' '--- applier phase/main ---'
sed -n '1710,1885p' src/lib/messaging/applier/build/messaging-build-applier.mts
printf '%s\n' '--- plan shape and render/apply paths ---'
sed -n '30,125p' src/lib/messaging/applier/build/messaging-build-applier.mts
sed -n '230,380p' src/lib/messaging/applier/build/messaging-build-applier.mts
printf '%s\n' '--- mode references ---'
rg -n -C4 --glob '*.{ts,tsx,mts}' '\bmode\b|apply|clear|messagingCommand' src/lib/onboard src/lib/messaging test/messaging-build-applier.test.ts test/messaging-build-applier-integrity.test.ts

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- image-runtime relevant section ---'
sed -n '80,145p' src/lib/onboard/managed-startup/image-runtime.ts
printf '%s\n' '--- applier plan and env decoding ---'
sed -n '30,125p' src/lib/messaging/applier/build/messaging-build-applier.mts
sed -n '230,380p' src/lib/messaging/applier/build/messaging-build-applier.mts
printf '%s\n' '--- applier phase dispatch and CLI ---'
sed -n '1715,1885p' src/lib/messaging/applier/build/messaging-build-applier.mts
printf '%s\n' '--- narrowly scoped mode/action references ---'
rg -n -C3 'mode|action|clear|applyMessagingBuildPhase|readMessagingBuildPlanFromEnv|messagingCommand' \
  src/lib/onboard/managed-startup/image-runtime.ts \
  src/lib/messaging/applier/build/messaging-build-applier.mts \
  src/lib/onboard/managed-startup-image-runtime.test.ts

Repository: NVIDIA/NemoClaw

Length of output: 48255


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- action type and plan callers ---'
rg -n -C5 'ManagedStartupApplyMessagingConstructionAction|apply-messaging-plan|buildManagedStartupImageActionPlan|mode: "(apply|clear)"' \
  src test --glob '*.{ts,tsx,mts}'
printf '%s\n' '--- image-runtime interfaces and command construction ---'
sed -n '1,90p' src/lib/onboard/managed-startup/image-runtime.ts
sed -n '140,245p' src/lib/onboard/managed-startup/image-runtime.ts
printf '%s\n' '--- messaging plan producers and clear-related logic ---'
rg -n -C5 'NEMOCLAW_MESSAGING_PLAN_B64|MessagingBuildPlan|disabledChannels|credentialBindings|agentRender|buildSteps' \
  src/lib/onboard src/lib/messaging --glob '*.{ts,tsx,mts}' | head -n 1000

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- managed-startup files ---'
git ls-files 'src/lib/onboard/managed-startup*' 'src/lib/onboard/**/managed-startup*'
printf '%s\n' '--- all production references to the image-plan builder/types ---'
rg -n --glob '!*.test.ts' \
  'buildManagedStartupImageActionPlan|ManagedStartupImageActionPlanInput|apply-messaging-plan|ManagedStartupApplyMessaging' \
  src
printf '%s\n' '--- all clear/apply references in managed-startup sources ---'
rg -n -C4 --glob '!*.test.ts' \
  'messaging|clear|apply|construction|image action' \
  src/lib/onboard/managed-startup src/lib/onboard/managed-startup.ts 2>/dev/null || true
printf '%s\n' '--- nearby profile/mapper definitions ---'
rg -n -C4 --glob '!*.test.ts' \
  'ManagedStartup|messagingPlan|messaging.*mode|mode.*messaging' \
  src/lib/onboard src/lib/messaging | head -n 800

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

image = Path("src/lib/onboard/managed-startup/image-runtime.ts").read_text()
applier = Path("src/lib/messaging/applier/build/messaging-build-applier.mts").read_text()

command = re.search(
    r"function messagingCommand\(.*?\n\}",
    image,
    re.S,
).group(0)
cli = re.search(
    r"function parseMessagingBuildArgs\(.*?\n\}",
    applier,
    re.S,
).group(0)
main = re.search(
    r"export function main\(.*?\n\}",
    applier,
    re.S,
).group(0)
phase = re.search(
    r"export function applyMessagingBuildPhase\(.*?\n\}",
    applier,
    re.S,
).group(0)

print("mode declared in action contract:", 'readonly mode: "apply" | "clear"' in image)
print("mode validated before command construction:", 'action.mode !== "apply" && action.mode !== "clear"' in image)
print("mode referenced by messagingCommand:", bool(re.search(r"messagingCommand.*?mode", command, re.S)))
print("CLI accepts --mode:", "--mode" in cli)
print("main reads only agent/phase/dryRun:", "{ agent, phase, dryRun }" in main)
print("phase dispatcher has clear branch:", bool(re.search(r"\bclear\b", phase)))
print("apply and clear command inputs structurally share argv:", "argv: messagingCommand(action.agent, action.phase)" in image)
PY

Repository: NVIDIA/NemoClaw

Length of output: 453


Thread mode through the messaging applier or remove it from the contract.

The applier accepts only --agent and --phase and has no clear branch, so clear cannot clear the plan and may reapply it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/image-runtime.ts` around lines 114 - 128, The
messagingCommand contract does not propagate the requested mode, preventing
clear operations from reaching the applier. Update messagingCommand and the
messaging applier argument handling to thread mode through and implement the
clear branch, or remove mode from the contract and all callers if it is not
required; ensure clear does not reapply the plan.

Comment on lines +122 to +125
const remote =
bindAddress === "0.0.0.0" ||
input.wslExposure ||
!["127.0.0.1", "localhost", "::1", "[::1]"].includes(new URL(input.chatUiUrl).hostname);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Malformed chatUiUrl escapes the module's error contract.

new URL(input.chatUiUrl) throws a raw TypeError: Invalid URL for an empty or malformed value (the DCode fixture already passes chatUiUrl: "", and only the early disabled return keeps it away from here). Every other rejection in this file surfaces as ManagedStartupOnboardProfileError; validate the URL so callers get one failure type.

🛠️ Proposed fix
+    let hostname: string;
+    try {
+      hostname = new URL(input.chatUiUrl).hostname;
+    } catch {
+      throw new ManagedStartupOnboardProfileError(
+        `dashboard URL '${input.chatUiUrl}' is not a valid URL`,
+      );
+    }
     const remote =
       bindAddress === "0.0.0.0" ||
       input.wslExposure ||
-      !["127.0.0.1", "localhost", "::1", "[::1]"].includes(new URL(input.chatUiUrl).hostname);
+      !["127.0.0.1", "localhost", "::1", "[::1]"].includes(hostname);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const remote =
bindAddress === "0.0.0.0" ||
input.wslExposure ||
!["127.0.0.1", "localhost", "::1", "[::1]"].includes(new URL(input.chatUiUrl).hostname);
let hostname: string;
try {
hostname = new URL(input.chatUiUrl).hostname;
} catch {
throw new ManagedStartupOnboardProfileError(
`dashboard URL '${input.chatUiUrl}' is not a valid URL`,
);
}
const remote =
bindAddress === "0.0.0.0" ||
input.wslExposure ||
!["127.0.0.1", "localhost", "::1", "[::1]"].includes(hostname);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/onboard-profile.ts` around lines 122 - 125,
Update the URL handling in the managed startup onboarding flow around the remote
calculation so malformed or empty input.chatUiUrl values are caught and surfaced
as ManagedStartupOnboardProfileError rather than allowing new URL to throw a raw
TypeError. Preserve the existing localhost and remote-host detection behavior
for valid URLs, including the early disabled path.

Comment on lines +201 to +203
const credentialProxyReplayRequired =
agent !== "langchain-deepagents-code" &&
hasCredentialBearingHostProxyEnvironment(input.environment);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C4 'credentialProxyReplayRequired|dropCredentialBearingProxyUrls' src
rg -n -C4 'langchain-deepagents-code' src/lib/onboard --glob '!*.test.ts' | rg -n -C4 -i 'proxy'

Repository: NVIDIA/NemoClaw

Length of output: 12203


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- managed startup profile consumers ---'
rg -n -C5 'credentialProxyReplayRequired|hostHttpUrl|hostHttpsUrl|NEMOCLAW_PROXY_HOST|NEMOCLAW_PROXY_PORT' src/lib/onboard --glob '*.ts'
printf '%s\n' '--- launch implementation ---'
sed -n '1,190p' src/lib/onboard/sandbox-create-launch.ts
printf '%s\n' '--- profile builder and relevant agent contract ---'
sed -n '120,175p' src/lib/onboard/managed-startup/profile.ts
sed -n '500,550p' src/lib/onboard/managed-startup/profile.ts
printf '%s\n' '--- profile source ---'
sed -n '1,240p' src/lib/onboard/managed-startup/onboard-profile.ts

Repository: NVIDIA/NemoClaw

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- exact replay-flag call graph ---'
rg -n -C8 'credentialProxyReplayRequired' src --glob '!*.test.ts'
printf '%s\n' '--- DCode launch/build proxy handling ---'
rg -n -C8 'langchain-deepagents-code|NEMOCLAW_PROXY_HOST|NEMOCLAW_PROXY_PORT|HTTP_PROXY|HTTPS_PROXY' src/lib/onboard/sandbox-create-launch.ts src/lib/onboard/dockerfile-patch.ts src --glob '!*.test.ts' | grep -i -C4 -E 'langchain-deepagents-code|NEMOCLAW_PROXY_HOST|NEMOCLAW_PROXY_PORT|HTTP_PROXY|HTTPS_PROXY' | head -n 300
printf '%s\n' '--- launch remainder ---'
sed -n '120,190p' src/lib/onboard/sandbox-create-launch.ts
printf '%s\n' '--- Dockerfile patch implementation ---'
rg -n 'function|export|NEMOCLAW_PROXY_HOST|NEMOCLAW_PROXY_PORT|langchain-deepagents-code' src/lib/onboard/dockerfile-patch.ts
sed -n '1,220p' src/lib/onboard/dockerfile-patch.ts

Repository: NVIDIA/NemoClaw

Length of output: 40784


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- all repository consumers of replay intent ---'
rg -n -C5 'credentialProxyReplayRequired|replay.*proxy|proxy.*replay' . --glob '!node_modules/**' --glob '!dist/**' --glob '!*.lock'
printf '%s\n' '--- DCode Dockerfiles and proxy directives ---'
rg --files | rg -i '(^|/)(dockerfile|.*dockerfile.*)$|langchain-deepagents-code'
rg -n -C5 'NEMOCLAW_PROXY_HOST|NEMOCLAW_PROXY_PORT|HTTP_PROXY|HTTPS_PROXY|proxy' . --glob '*Dockerfile*' --glob '!node_modules/**' --glob '!dist/**' | head -n 300
printf '%s\n' '--- profile/environment serialization and onboarding launch callers ---'
rg -n -C6 'buildManagedStartupOnboardProfile|profileEnvironment|prepareSandboxCreateLaunch|buildSandboxRuntimeEnvArgs' src/lib/onboard --glob '!*.test.ts'

Repository: NVIDIA/NemoClaw

Length of output: 42497


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- DCode proxy contract tests ---'
sed -n '1,260p' test/langchain-deepagents-code-proxy-runtime-contract.test.ts
sed -n '1,240p' test/langchain-deepagents-code-proxy-launcher.test.ts
printf '%s\n' '--- DCode Dockerfile proxy-related lines ---'
rg -n -C8 'NEMOCLAW_PROXY_HOST|NEMOCLAW_PROXY_PORT|HTTP_PROXY|HTTPS_PROXY|proxy|root-owned|managed' agents/langchain-deepagents-code/Dockerfile agents/langchain-deepagents-code/Dockerfile.base agents/langchain-deepagents-code/*.sh agents/langchain-deepagents-code/*.py
printf '%s\n' '--- exact tests around managed profile replay ---'
sed -n '300,405p' src/lib/onboard/managed-startup-onboard-profile.test.ts

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- focused DCode proxy assertions ---'
rg -n -C4 'credential|HTTP_PROXY|HTTPS_PROXY|NEMOCLAW_PROXY|trusted|managed|drop|proxy' test/langchain-deepagents-code-proxy-runtime-contract.test.ts test/langchain-deepagents-code-proxy-launcher.test.ts | head -n 260
printf '%s\n' '--- DCode launcher proxy setup ---'
rg -n -C5 'MANAGED_PROXY|PROXY_URL|HTTP_PROXY|HTTPS_PROXY|NEMOCLAW_PROXY|trusted proxy|credential' agents/langchain-deepagents-code/dcode-launcher.sh agents/langchain-deepagents-code/dcode-wrapper.sh agents/langchain-deepagents-code/start.sh

Repository: NVIDIA/NemoClaw

Length of output: 50371


Wire credentialProxyReplayRequired into the replay path or remove it.

DCode intentionally derives proxy settings from root-owned managed host/port files, so it does not lose proxy access. However, this flag has no production consumer; non-DCode agents never receive the intended replay behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/onboard-profile.ts` around lines 201 - 203,
Remove the unused credentialProxyReplayRequired declaration, or wire it into the
credential proxy replay path so non-"langchain-deepagents-code" agents receive
the intended replay behavior. Trace the existing replay flow in the onboarding
logic and ensure the flag directly controls that behavior rather than remaining
an unconsumed value.

Comment on lines +271 to +277
if (Array.isArray(value)) {
return {
agents: value as ManagedStartupJsonObject[],
defaults: emptyDefaults,
main: {},
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Top-level array form skips the element validation applied to agents.

The object branch requires every entry of agents to be a plain object (Line 292), but the bare-array branch casts unchecked. NEMOCLAW_EXTRA_AGENTS_JSON='[1,null]' reaches the profile as agents unless a downstream validator rejects it.

🛠️ Proposed fix
   if (Array.isArray(value)) {
+    if (!value.every((agent) => isPlainObject(agent))) {
+      fail("NEMOCLAW_EXTRA_AGENTS_JSON.agents must be an object list");
+    }
     return {
       agents: value as ManagedStartupJsonObject[],
       defaults: emptyDefaults,
       main: {},
     };
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Array.isArray(value)) {
return {
agents: value as ManagedStartupJsonObject[],
defaults: emptyDefaults,
main: {},
};
}
if (Array.isArray(value)) {
if (!value.every((agent) => isPlainObject(agent))) {
fail("NEMOCLAW_EXTRA_AGENTS_JSON.agents must be an object list");
}
return {
agents: value as ManagedStartupJsonObject[],
defaults: emptyDefaults,
main: {},
};
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/profile-builder.ts` around lines 271 - 277,
Update the top-level array handling in the profile builder to validate every
element with the same plain-object validation used by the object branch’s agents
processing, instead of unchecked-casting the array. Reject invalid entries such
as numbers or null before constructing the returned agents profile, while
preserving the existing defaults and main values for valid arrays.

Comment on lines +891 to +895
compatibility:
input.agent === "openclaw"
? (JSON.parse(JSON.stringify(inference.compatibility ?? {})) as ManagedStartupJsonObject)
: null,
inputModalities: input.agent === "openclaw" ? parseInputModalities(input.environment) : null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Non-OpenClaw compatibility / modalities input is silently discarded.

assertAgentSpecificInput fails closed on cross-agent state (upstreamEndpointUrl, hermesToolGateways, dcodeAutoApprovalMode, …), but a non-null inference.compatibility supplied for Hermes or DCode is dropped here without error. Rejecting it keeps the "state owned by another agent" contract uniform and prevents a caller from believing compatibility overrides were honored.

🛠️ Proposed fix (in `assertAgentSpecificInput`)
   if (input.agent === "hermes") {
     if (
       input.inference.upstreamEndpointUrl !== null ||
+      input.inference.compatibility !== null ||
       input.dcodeAutoApprovalMode !== null ||
       input.observabilityEnabled !== null
     ) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/managed-startup/profile-builder.ts` around lines 891 - 895,
Update assertAgentSpecificInput to reject non-null inference.compatibility and
non-null input modalities when input.agent is not "openclaw", instead of
silently discarding them in the profile-building flow. Preserve the existing
OpenClaw parsing and compatibility behavior, and apply the same fail-closed
cross-agent validation used for other agent-specific fields.

@ericksoa ericksoa added needs: review PR is conflict-free and awaiting maintainer review v0.0.100 labels Jul 31, 2026
@ericksoa
ericksoa merged commit 91fc63e into main Jul 31, 2026
95 of 97 checks passed
@ericksoa
ericksoa deleted the feat/buildless-image-runtime-construction branch July 31, 2026 02:35
@sandl99 sandl99 mentioned this pull request Jul 31, 2026
23 tasks
sandl99 added a commit that referenced this pull request Jul 31, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Adds the canonical July 30 release entry for `v0.0.99` before the
release tag is captured.
The entry covers all 37 merged PRs since `v0.0.98` and bounds
experimental or dormant work without presenting it as supported
behavior.

## Changes

- Adds `docs/changelog/2026-07-30.mdx` with the exact `## v0.0.99`
heading, parser-safe MDX SPDX comment, summary, detailed release
bullets, and published documentation routes.
- Records user-visible recovery, snapshot, shared-route, Hermes,
readiness, inference, image, documentation, and release E2E changes.
- States that the managed-image selection and startup-profile contracts
remain dormant and do not activate buildless onboarding.

Source summary:

- [#7972](#7972) ->
`docs/changelog/2026-07-30.mdx`: Records restored managed OpenClaw
configuration modes during recovery.
- [#7834](#7834) ->
`docs/changelog/2026-07-30.mdx`: Records clone-bound pairing
verification after snapshot restore.
- [#7975](#7975) ->
`docs/changelog/2026-07-30.mdx`: Records managed startup recovery
coverage.
- [#7960](#7960) ->
`docs/changelog/2026-07-30.mdx`: Records dormant startup-profile
coordination without activating a supported surface.
- [#7856](#7856) ->
`docs/changelog/2026-07-30.mdx`: Records persistence of the
credential-free OpenClaw startup command.
- [#7959](#7959) ->
`docs/changelog/2026-07-30.mdx`: Records dormant startup-profile
construction without changing onboarding.
- [#7946](#7946) ->
`docs/changelog/2026-07-30.mdx`: Records the internal startup-profile
schema and transport contract.
- [#7951](#7951) ->
`docs/changelog/2026-07-30.mdx`: Records platform-pull cleanup before
managed-image validation.
- [#7949](#7949) ->
`docs/changelog/2026-07-30.mdx`: Records rejection of retained Hermes
`uv` build cache metadata.
- [#7597](#7597) ->
`docs/changelog/2026-07-30.mdx`: Records separate command and agent
first-turn latency evidence.
- [#7931](#7931) ->
`docs/changelog/2026-07-30.mdx`: Records focused E2E replacement
evidence for retired selectors.
- [#7950](#7950) ->
`docs/changelog/2026-07-30.mdx`: Records exclusion of build-only
BuildKit telemetry from the Deep Agents Code probe.
- [#7665](#7665) ->
`docs/changelog/2026-07-30.mdx`: Records consolidated priority 2 E2E
coverage.
- [#7911](#7911) ->
`docs/changelog/2026-07-30.mdx`: Records the corrected NVIDIA DORI
installation pin.
- [#7934](#7934) ->
`docs/changelog/2026-07-30.mdx`: Records the staging image-family wait
before Brev Launchable deployment.
- [#7772](#7772) ->
`docs/changelog/2026-07-30.mdx`: Records dormant managed-image selection
contracts without activating buildless onboarding.
- [#7941](#7941) ->
`docs/changelog/2026-07-30.mdx`: Records corrected agent-specific
provider and policy guidance.
- [#7819](#7819) ->
`docs/changelog/2026-07-30.mdx`: Records removal of empty Deep Agents
Code provider-switch sections.
- [#7932](#7932) ->
`docs/changelog/2026-07-30.mdx`: Records independent
credential-generation E2E execution.
- [#7840](#7840) ->
`docs/changelog/2026-07-30.mdx`: Records shared-route preservation and
pre-delete peer validation during upgrades.
- [#7874](#7874) ->
`docs/changelog/2026-07-30.mdx`: Records the split between pre-tag
release entries and post-tag Announcements.
- [#7876](#7876) ->
`docs/changelog/2026-07-30.mdx`: Records the writable Hermes runtime
root within lockdown.
- [#7756](#7756) ->
`docs/changelog/2026-07-30.mdx`: Records validated multi-platform
managed-image publication.
- [#7914](#7914) ->
`docs/changelog/2026-07-30.mdx`: Records accepted `uv` version metadata
in Hermes image validation.
- [#7686](#7686) ->
`docs/changelog/2026-07-30.mdx`: Records the explicitly experimental
Microsoft Entra runtime identity reference.
- [#7869](#7869) ->
`docs/changelog/2026-07-30.mdx`: Records classified gateway relaunch
quarantine and rebuild guidance.
- [#7814](#7814) ->
`docs/changelog/2026-07-30.mdx`: Records state restore into replacement
sandboxes and SQLite write verification.
- [#7839](#7839) ->
`docs/changelog/2026-07-30.mdx`: Records quieter onboarding test
execution without a user-facing behavior claim.
- [#7854](#7854) ->
`docs/changelog/2026-07-30.mdx`: Records generalized agent-selection
guidance.
- [#7845](#7845) ->
`docs/changelog/2026-07-30.mdx`: Records isolated CDI test evidence
without a user-facing behavior claim.
- [#7843](#7843) ->
`docs/changelog/2026-07-30.mdx`: Records the corrected Omni sub-agent
model ID.
- [#7908](#7908) ->
`docs/changelog/2026-07-30.mdx`: Records reviewed Hermes and Deep Agents
Code dependency pins.
- [#7887](#7887) ->
`docs/changelog/2026-07-30.mdx`: Records rejection of a symlinked DGX
Station release marker.
- [#7747](#7747) ->
`docs/changelog/2026-07-30.mdx`: Records the internal compute-driver
separation without a user-facing behavior claim.
- [#7660](#7660) ->
`docs/changelog/2026-07-30.mdx`: Records atomic publication of rebuild
recovery manifests.
- [#7661](#7661) ->
`docs/changelog/2026-07-30.mdx`: Records bounded local inference
health-response retention.
- [#7654](#7654) ->
`docs/changelog/2026-07-30.mdx`: Records state preservation across
supervisor relaunch recovery.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

- [ ] Tests added or updated for changed behavior
- [x] Existing tests cover changed behavior — justification:
`test/changelog-docs.test.ts` validates the dated changelog contract,
SPDX comment, version heading, and published routes.
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `docs-updated`
- Evidence: `docs/changelog/2026-07-30.mdx`; the documentation-only diff
passed review against `WRITING.md`, the controlled word list, and
`docs/CONTRIBUTING.md`. The review covered terminology, structure,
active voice, release meaning, product-scope boundaries, and link and
code presentation. Changelog tests passed 6/6, and the docs build
reported 0 errors with 2 pre-existing warnings.
- Agent: Codex CLI
<!-- docs-review-head-sha: 200940f -->
<!-- docs-review-agents-blob-sha: c052d60 -->

## DGX Station Hardware Evidence

- [ ] Tested on DGX Station
- Tested commit:
- Station profile/scenario:
- Result:
- Supporting evidence:

## Verification

- [x] PR description includes a `Signed-off-by:` line and every commit
appears as `Verified` in GitHub
- [x] 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
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npx
vitest run test/changelog-docs.test.ts` passed 6/6 tests.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result: Not applicable to this
documentation-only release entry.
- [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) —
result: Build passed with 0 errors and 2 pre-existing warnings.
- [x] 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)

---
Signed-off-by: San Dang <sdang@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.99 covering snapshot restoration, sandbox
recovery, gateway route upgrades, and Hermes security updates.
* Documented experimental Microsoft Entra runtime identity support and
enhanced readiness checks.
* Added details on managed image validation, trusted CI image promotion,
and end-to-end release evidence.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: security Security controls, permissions, secrets, or hardening feature PR adds or expands user-visible functionality needs: review PR is conflict-free and awaiting maintainer review platform: container Affects Docker, containerd, Podman, or images

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant