feat(onboard): construct managed startup profiles - #7959
Conversation
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>
📝 WalkthroughWalkthroughChangesManaged startup orchestration
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
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit 2a048ae in the TypeScript / code-coverage/cliThe overall coverage in commit 2a048ae in the Show a code coverage summary of the most impacted files.
Updated |
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge. |
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (8)
src/lib/onboard/host-proxy-env.ts (1)
15-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated 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: exportHOST_PROXY_URL_ENV_NAMESas the single source of truth.src/lib/onboard/managed-startup/onboard-profile.ts#L44-L44: delete the localHOST_PROXY_URL_INPUTStuple 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 valueDrop the branch on a constant fixture.
PEMis a fixed fixture, so one side of this ternary is dead and it silently encodes an assumption aboutnormalizeCertificateBlocks. 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 winDigest 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 winBare
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.eachblocks 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 insideencodedProfile.
encodedProfileis base64url, so a leaked credential there would not appear as plaintext in the serialized string. Decoding it before thenot.toContainassertions 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 liftLegacy-Docker env reconciliation has no reachable consumer.
profileEnvironmentinsrc/lib/onboard/managed-startup/onboard-profile.ts(Lines 168-193) passes only thePROFILE_ENVIRONMENT_INPUTSallowlist plus proxy vars.NEMOCLAW_MODEL,CHAT_UI_URL,NEMOCLAW_TOOL_DISCLOSURE,NEMOCLAW_INFERENCE_*,NEMOCLAW_MESSAGING_PLAN_B64,NEMOCLAW_HERMES_*,NEMOCLAW_DCODE_AUTO_APPROVAL, andNEMOCLAW_OBSERVABILITYare 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 valueOptional: 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-codechecks 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 winAdd 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.tsLine 211)- dashboard whose
agentdiffers frominput.agent→dashboard for … cannot be used by …(Line 158)- unknown
input.agent→unsupported agent(Line 88)modeoutsideapply/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
📒 Files selected for processing (8)
src/lib/onboard/host-proxy-env.tssrc/lib/onboard/managed-startup-image-runtime.test.tssrc/lib/onboard/managed-startup-onboard-profile.test.tssrc/lib/onboard/managed-startup-profile-builder.test.tssrc/lib/onboard/managed-startup/hold.tssrc/lib/onboard/managed-startup/image-runtime.tssrc/lib/onboard/managed-startup/onboard-profile.tssrc/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"; |
There was a problem hiding this comment.
📐 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 || trueRepository: 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"
doneRepository: 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
| 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"] : []), | ||
| ]; | ||
| } |
There was a problem hiding this comment.
🗄️ 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.tsRepository: 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.tsRepository: 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 1000Repository: 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 800Repository: 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)
PYRepository: 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.
| const remote = | ||
| bindAddress === "0.0.0.0" || | ||
| input.wslExposure || | ||
| !["127.0.0.1", "localhost", "::1", "[::1]"].includes(new URL(input.chatUiUrl).hostname); |
There was a problem hiding this comment.
🩺 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.
| 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.
| const credentialProxyReplayRequired = | ||
| agent !== "langchain-deepagents-code" && | ||
| hasCredentialBearingHostProxyEnvironment(input.environment); |
There was a problem hiding this comment.
🎯 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.tsRepository: 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.tsRepository: 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.tsRepository: 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.shRepository: 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.
| if (Array.isArray(value)) { | ||
| return { | ||
| agents: value as ManagedStartupJsonObject[], | ||
| defaults: emptyDefaults, | ||
| main: {}, | ||
| }; | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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.
| compatibility: | ||
| input.agent === "openclaw" | ||
| ? (JSON.parse(JSON.stringify(inference.compatibility ?? {})) as ManagedStartupJsonObject) | ||
| : null, | ||
| inputModalities: input.agent === "openclaw" ? parseInputModalities(input.environment) : null, |
There was a problem hiding this comment.
🎯 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.
<!-- 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 -->
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
Type of Change
Quality Gates
Documentation Writer Review
no-docs-needed+2,714/-1) adds dormant managed-profile construction and tests only. It changes no user-visible command, configuration, default, workflow, or support statement.DGX Station Hardware Evidence
Verification
2a048aeecb4bd491daadc3c109f9b845031a6e04/37f6a8a650ff500f7054bae8d36b00e7b42edd41+2,714/-1; no documentation paths.Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailablenpm run validate:prpassed;git diff --checkis clean.npm testfor broad runtime/test-harness changes;npm run checkfor repo-wide validation/coverage changes — command/result: Exact-head required CI is the broad gate for this inert slice.npm run docsbuilds without warnings (doc changes only)Stack
mainat merged PR3.2 commit37f6a8a650ff500f7054bae8d36b00e7b42edd41feat/buildless-image-runtime-constructionat2a048aeecb4bd491daadc3c109f9b845031a6e04Signed-off-by: Aaron Erickson aerickson@nvidia.com
Summary by CodeRabbit
New Features
Tests