feat(onboard): add managed startup profile schema - #7946
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>
📝 WalkthroughWalkthroughAdds a versioned managed startup profile contract with strict validation, canonical encoding/decoding, fingerprinting, security hardening, agent-specific rules, comprehensive tests, and Dockerfile-triggered test watch coverage. ChangesManaged startup profile
Estimated code review effort: 5 (Critical) | ~90 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Caller
participant validateManagedStartupProfile
participant encodeManagedStartupProfile
participant decodeManagedStartupProfile
participant fingerprintManagedStartupProfile
Caller->>validateManagedStartupProfile: submit profile
validateManagedStartupProfile-->>Caller: canonical profile
Caller->>encodeManagedStartupProfile: encode profile
encodeManagedStartupProfile-->>Caller: base64url payload
Caller->>decodeManagedStartupProfile: decode payload
decodeManagedStartupProfile-->>Caller: validated profile
Caller->>fingerprintManagedStartupProfile: fingerprint profile
fingerprintManagedStartupProfile-->>Caller: SHA-256 digest
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit ea5bd8a in the TypeScript / code-coverage/cliThe overall coverage in commit ea5bd8a 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: 1 warning · 0 suggestionsWarningsWarnings do not block.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (6)
src/lib/onboard/managed-startup/profile.ts (3)
752-773: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPrefer
Array#sortover a hand-rolled insertion sort.Default
Array#sort()already orders by UTF-16 code units, matching the>comparison here, and avoids the O(n²) path plus per-writedefineProperty. Inputs areObject.keysoutput or arrays already proven to hold only indexed data properties, so nothing is gained by re-implementing the algorithm.♻️ Proposed refactor
-function sortStrings(values: string[]): string[] { - for (let index = 1; index < values.length; index += 1) { - const selected = values[index] as string; - let insertion = index; - while (insertion > 0 && (values[insertion - 1] as string) > selected) { - Object.defineProperty(values, String(insertion), { - configurable: true, - enumerable: true, - value: values[insertion - 1], - writable: true, - }); - insertion -= 1; - } - Object.defineProperty(values, String(insertion), { - configurable: true, - enumerable: true, - value: selected, - writable: true, - }); - } - return values; -} +function sortStrings(values: string[]): string[] { + return values.sort(); +}🤖 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.ts` around lines 752 - 773, Replace the hand-rolled insertion sort in sortStrings with the built-in Array#sort using its default ordering, preserving the function’s in-place mutation and returned array behavior.
685-691: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDescriptor dance is unnecessary over module-owned arrays.
SECRET_VALUE_PATTERNS(and theString#matchresult at Line 704) are internally produced and cannot be attacker-shaped, unlike the payload arrays handled bymapArrayByIndex. Plain iteration reads better here.Based on learnings: avoid adding "defensive" validation around internal helper logic when there is no realistic failure mode; reserve it for system boundaries.
♻️ Proposed simplification
function valueLooksLikeSecret(value: string): boolean { - for (let index = 0; index < SECRET_VALUE_PATTERNS.length; index += 1) { - const descriptor = Object.getOwnPropertyDescriptor(SECRET_VALUE_PATTERNS, String(index)); - if (descriptor && "value" in descriptor && descriptor.value.test(value)) return true; - } - return false; + return SECRET_VALUE_PATTERNS.some((pattern) => pattern.test(value)); }🤖 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.ts` around lines 685 - 691, In valueLooksLikeSecret, replace the indexed property-descriptor lookup with direct iteration over SECRET_VALUE_PATTERNS and test each pattern against value. Apply the same simplification to the String#match result around the referenced matching logic, removing unnecessary descriptor-based validation while preserving the existing matching behavior.Source: Learnings
1173-1185: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDerive per-agent allowed values from the capability table instead of restating them.
MANAGED_STARTUP_PROFILE_CAPABILITIESis documented (Lines 326-330) as the authoritative negotiation table, andvalidateInferencealready readsinferenceApisfrom it (Lines 1398-1406). ButvalidateWebSearchrestateswebSearchProviders,validateDashboardrestatesdashboardModes(Lines 1303-1307, 1336-1340),validateInferencerestatesinputModalities(Line 1425), andvalidateTuningrestatestuningFields(Lines 1527-1550). A table edit will silently leave these validators stale, and the capability test atsrc/lib/onboard/managed-startup-profile.test.ts:374would still pass.♻️ Example for webSearch
- const provider = requireStringEnum<ManagedStartupWebSearchProvider>( - webSearch.provider, - new Set(agent === "openclaw" ? ["brave", "tavily"] : ["tavily"]), - "agentConfig.webSearch.provider", - ); + const provider = requireStringEnum<ManagedStartupWebSearchProvider>( + webSearch.provider, + new Set<string>(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].webSearchProviders), + "agentConfig.webSearch.provider", + );🤖 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.ts` around lines 1173 - 1185, Update validateWebSearch and the other capability-backed validators—validateDashboard, validateInference, and validateTuning—to derive each agent’s allowed values from MANAGED_STARTUP_PROFILE_CAPABILITIES instead of duplicating literals or sets. Reuse the existing capability-table lookup pattern used by validateInference for inferenceApis, while preserving each validator’s current validation and error behavior.src/lib/onboard/managed-startup-profile.test.ts (3)
966-1012: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Object.prototypemutations are file-global side effects — worth asserting cleanup.Both tests correctly delete the injected property in
finally, but a nested pollution test that fails beforefinally(or a futureit.concurrent) would silently contaminate the rest of the file. Adding a post-assertion that the property is gone makes the isolation self-checking.♻️ Proposed tweak
expect(caught).toBeInstanceOf(Error); expect((caught as Error).message).toMatch(/custom JSON serializer/); expect(serializerInvoked).toBe(false); + expect("toJSON" in Object.prototype).toBe(false);🤖 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.test.ts` around lines 966 - 1012, Add post-cleanup assertions to both Object.prototype pollution tests, verifying that toJSON and bundleSha256 are absent after each finally block. Use an own-property check so the tests self-verify isolation without changing the existing cleanup or validation behavior.
250-260: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueStrengthen or drop the model-leak assertion at Line 258.
encodedis base64url, sonot.toContain(model)passes trivially and does not express a real transport property. If the intent is "the model is carried but opaque", assert on the decoded profile instead.♻️ Proposed tweak
expect(decodeManagedStartupProfile(encoded)).toEqual(validated); - expect(encoded).not.toContain(profile.inference.model); + expect(decodeManagedStartupProfile(encoded).inference.model).toBe(profile.inference.model); expect(fingerprintManagedStartupProfile(profile)).toMatch(/^[a-f0-9]{64}$/);🤖 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.test.ts` around lines 250 - 260, Replace the ineffective encoded-string model assertion in the managed startup profile test with a meaningful decoded-profile assertion: verify that decodeManagedStartupProfile(encoded) preserves profile.inference.model while retaining the existing canonical round-trip and fingerprint checks. Remove the trivial encoded not-to-contain expectation.
1029-1062: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueGuard the
Array.prototypedescriptor restore.
mapDescriptor/sortDescriptorare cast withas PropertyDescriptor; if either lookup ever returnedundefined, thefinallyblock itself throws and leavesArray.prototypepoisoned for every subsequent test in the worker. A non-null assertion at capture time (or anif (descriptor)guard) keeps the failure local.♻️ Proposed tweak
- const mapDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, "map"); - const sortDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, "sort"); + const mapDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, "map"); + const sortDescriptor = Object.getOwnPropertyDescriptor(Array.prototype, "sort"); + if (!mapDescriptor || !sortDescriptor) throw new Error("missing Array.prototype descriptors");🤖 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.test.ts` around lines 1029 - 1062, Guard restoration of the Array.prototype descriptors in the test around serializeManagedStartupProfile: avoid blindly casting potentially undefined mapDescriptor or sortDescriptor values, and restore each property only when its descriptor was successfully captured. Keep the existing cleanup in the finally block and ensure a failed lookup cannot leave Array.prototype poisoned.
🤖 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/profile.ts`:
- Around line 1014-1017: Update configuredDashboardPort to use the URL scheme
when no explicit port is present: retain 18,789 for HTTP or other existing
cases, but return HTTPS’s default port 443 so standard HTTPS dashboard URLs pass
requirePort(..., 1024). Preserve explicit port handling unchanged.
In `@test/helpers/vitest-watch-triggers.ts`:
- Line 50: Update the Dockerfile alternative in the watch-trigger pattern used
by vitest configuration so it matches only the repository-root Dockerfile after
paths are normalized to repository-relative form. Preserve the explicit
agents/hermes and agents/langchain-deepagents-code Dockerfile matches, while
excluding unrelated nested paths such as agents/other/Dockerfile.
---
Nitpick comments:
In `@src/lib/onboard/managed-startup-profile.test.ts`:
- Around line 966-1012: Add post-cleanup assertions to both Object.prototype
pollution tests, verifying that toJSON and bundleSha256 are absent after each
finally block. Use an own-property check so the tests self-verify isolation
without changing the existing cleanup or validation behavior.
- Around line 250-260: Replace the ineffective encoded-string model assertion in
the managed startup profile test with a meaningful decoded-profile assertion:
verify that decodeManagedStartupProfile(encoded) preserves
profile.inference.model while retaining the existing canonical round-trip and
fingerprint checks. Remove the trivial encoded not-to-contain expectation.
- Around line 1029-1062: Guard restoration of the Array.prototype descriptors in
the test around serializeManagedStartupProfile: avoid blindly casting
potentially undefined mapDescriptor or sortDescriptor values, and restore each
property only when its descriptor was successfully captured. Keep the existing
cleanup in the finally block and ensure a failed lookup cannot leave
Array.prototype poisoned.
In `@src/lib/onboard/managed-startup/profile.ts`:
- Around line 752-773: Replace the hand-rolled insertion sort in sortStrings
with the built-in Array#sort using its default ordering, preserving the
function’s in-place mutation and returned array behavior.
- Around line 685-691: In valueLooksLikeSecret, replace the indexed
property-descriptor lookup with direct iteration over SECRET_VALUE_PATTERNS and
test each pattern against value. Apply the same simplification to the
String#match result around the referenced matching logic, removing unnecessary
descriptor-based validation while preserving the existing matching behavior.
- Around line 1173-1185: Update validateWebSearch and the other
capability-backed validators—validateDashboard, validateInference, and
validateTuning—to derive each agent’s allowed values from
MANAGED_STARTUP_PROFILE_CAPABILITIES instead of duplicating literals or sets.
Reuse the existing capability-table lookup pattern used by validateInference for
inferenceApis, while preserving each validator’s current validation and error
behavior.
🪄 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: f56dc565-0e1b-4b07-8bac-7c8443044f9b
📒 Files selected for processing (6)
ci/source-shape-test-budget.jsonsrc/lib/onboard/managed-startup-profile.test.tssrc/lib/onboard/managed-startup/profile.tssrc/lib/onboard/managed-startup/transport.tstest/helpers/vitest-watch-triggers.tstest/vitest-watch-triggers.test.ts
| function configuredDashboardPort(value: string): number { | ||
| const explicit = new URL(value).port; | ||
| return explicit === "" ? 18_789 : Number(explicit); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Look for the established default dashboard port and any remote/443 dashboard handling.
rg -nP --type=ts -C3 '\b18[_]?789\b' | head -60
fd -e ts -i dashboard src/lib/onboard --exec rg -nP -C2 '\b(443|https:)\b' {} \; | head -60Repository: NVIDIA/NemoClaw
Length of output: 153
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file=src/lib/onboard/managed-startup/profile.ts
wc -l "$file"
echo '--- around configuredDashboardPort ---'
sed -n '980,1045p' "$file"
echo '--- around requireHttpUrl / port handling ---'
sed -n '1288,1395p' "$file"
echo '--- symbol search ---'
rg -n "configuredDashboardPort|requireHttpUrl|requirePort|dashboard port|18789|443|https://" src/lib/onboard -SRepository: NVIDIA/NemoClaw
Length of output: 50371
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file=src/lib/onboard/managed-startup/profile.ts
wc -l "$file"
echo '--- around configuredDashboardPort ---'
sed -n '980,1045p' "$file"
echo '--- around requireHttpUrl / port handling ---'
sed -n '1288,1395p' "$file"
echo '--- symbol search ---'
rg -n "configuredDashboardPort|requireHttpUrl|requirePort|dashboard port|18789|443|https://" src/lib/onboard -SRepository: NVIDIA/NemoClaw
Length of output: 50371
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "configuredDashboardPort|requireHttpUrl|requirePort" src/lib/onboard/managed-startup/profile.ts
sed -n '1310,1385p' src/lib/onboard/managed-startup/profile.tsRepository: NVIDIA/NemoClaw
Length of output: 3953
🏁 Script executed:
#!/bin/bash
set -euo pipefail
testfile=src/lib/onboard/managed-startup/profile.test.ts
wc -l "$testfile"
echo '--- dashboard-related tests ---'
rg -n "dashboard|18789|443|https://" "$testfile" -n -C 2Repository: NVIDIA/NemoClaw
Length of output: 232
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files 'src/lib/onboard/*profile*.test.ts' 'src/lib/onboard/**/profile*.test.ts' 'src/lib/onboard/**/managed-startup*test.ts' | sort
echo '--- URL probe ---'
node - <<'JS'
for (const u of ['http://dash.example.com', 'https://dash.example.com', 'https://dash.example.com:443', 'http://dash.example.com:80']) {
const url = new URL(u);
console.log(u, '=> protocol=', url.protocol, 'port=', JSON.stringify(url.port), 'origin=', url.origin);
}
JSRepository: NVIDIA/NemoClaw
Length of output: 666
🏁 Script executed:
#!/bin/bash
set -euo pipefail
testfile=src/lib/onboard/managed-startup-profile.test.ts
wc -l "$testfile"
echo '--- dashboard tests ---'
rg -n -C 3 "dashboard|18789|443|https://dash|OpenClaw|Hermes" "$testfile"
echo '--- requireHttpUrl / configuredDashboardPort context ---'
sed -n '930,1025p' src/lib/onboard/managed-startup/profile.ts
sed -n '1298,1378p' src/lib/onboard/managed-startup/profile.tsRepository: NVIDIA/NemoClaw
Length of output: 13443
🏁 Script executed:
#!/bin/bash
set -euo pipefail
testfile=src/lib/onboard/managed-startup-profile.test.ts
wc -l "$testfile"
echo '--- dashboard-related cases ---'
rg -n -C 3 "dashboard|18789|443|https://dash|mode|publicPort|port" "$testfile"Repository: NVIDIA/NemoClaw
Length of output: 12487
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 "dashboard\.url|dashboard\.port|publicPort|18789|reverse proxy|remote dashboard|loopback-forwarded|mode: \"remote\"" src/lib/onboard src/lib/README.md src/lib/onboard/machine/README.mdRepository: NVIDIA/NemoClaw
Length of output: 50372
Honor the URL scheme’s default port
configuredDashboardPort() treats any portless URL as 18,789, so https://dash.example.com only passes if :18789 is spelled out. Since requirePort(..., 1024) also rejects 443, a standard HTTPS reverse proxy can’t be represented. If HTTPS dashboards are in scope, make the fallback scheme-aware.
🤖 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.ts` around lines 1014 - 1017, Update
configuredDashboardPort to use the URL scheme when no explicit port is present:
retain 18,789 for HTTP or other existing cases, but return HTTPS’s default port
443 so standard HTTPS dashboard URLs pass requirePort(..., 1024). Preserve
explicit port handling unchanged.
|
|
||
| export const vitestWatchTriggerPatterns: VitestWatchTriggerPattern[] = [ | ||
| { | ||
| pattern: /(?:^|\/)(?:Dockerfile|agents\/(?:hermes|langchain-deepagents-code)\/Dockerfile)$/, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Restrict the Dockerfile match to the repository root.
The Dockerfile alternative currently matches unrelated nested paths such as agents/other/Dockerfile, causing unnecessary managed-startup-profile tests. Match repository-relative paths or otherwise anchor this alternative to the repository root.
Proposed direction
- pattern: /(?:^|\/)(?:Dockerfile|agents\/(?:hermes|langchain-deepagents-code)\/Dockerfile)$/,
+ pattern: /^(?:Dockerfile|agents\/(?:hermes|langchain-deepagents-code)\/Dockerfile)$/,Apply this after normalizing paths to repository-relative form.
🤖 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 `@test/helpers/vitest-watch-triggers.ts` at line 50, Update the Dockerfile
alternative in the watch-trigger pattern used by vitest configuration so it
matches only the repository-root Dockerfile after paths are normalized to
repository-relative form. Preserve the explicit agents/hermes and
agents/langchain-deepagents-code Dockerfile matches, while excluding unrelated
nested paths such as agents/other/Dockerfile.
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>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/onboard/managed-startup-profile.test.ts (1)
252-273: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSource-text scraping is CWD-dependent and fails opaquely on file moves.
path.join(process.cwd(), relativePath)assumes the Vitest working directory is the repo root, and any rename of the six scanned files (orscripts/nemoclaw-start.sh) surfaces as anENOENTfromreadFileSyncat module load rather than a meaningful contract failure. Resolving relative toimport.meta.urland asserting readability up front makes the failure self-explanatory.Also note the path instruction preference for observable outcomes over source-text assertions; the
source-shape-contractmarker at Line 474 suggests this is a sanctioned exception, so this is a robustness nit rather than an objection to the approach.♻️ Resolve paths from the module location
+const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../.."); const STOCK_RUNTIME_INPUTS = new Set( RUNTIME_INPUT_SOURCE_FILES.flatMap((relativePath) => [ - ...readFileSync(path.join(process.cwd(), relativePath), "utf8").matchAll( + ...readFileSync(path.join(REPO_ROOT, relativePath), "utf8").matchAll( QUOTED_RUNTIME_INPUT_RE, ), ]).map((match) => match[1] as string), );🤖 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.test.ts` around lines 252 - 273, Update the source-file loading around RUNTIME_INPUT_SOURCE_FILES and OPENCLAW_AUTO_PAIR_CONSUMER_INPUTS to resolve paths relative to import.meta.url rather than process.cwd(). Add an upfront readability/assertion check for every scanned file, including scripts/nemoclaw-start.sh, so renames or missing files produce a clear contract failure instead of an opaque readFileSync ENOENT.Source: Path instructions
🤖 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.
Nitpick comments:
In `@src/lib/onboard/managed-startup-profile.test.ts`:
- Around line 252-273: Update the source-file loading around
RUNTIME_INPUT_SOURCE_FILES and OPENCLAW_AUTO_PAIR_CONSUMER_INPUTS to resolve
paths relative to import.meta.url rather than process.cwd(). Add an upfront
readability/assertion check for every scanned file, including
scripts/nemoclaw-start.sh, so renames or missing files produce a clear contract
failure instead of an opaque readFileSync ENOENT.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 873140b8-cf25-48f2-8060-4f59c734a621
📒 Files selected for processing (3)
ci/source-shape-test-budget.jsonsrc/lib/onboard/managed-startup-profile.test.tssrc/lib/onboard/managed-startup/profile.ts
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
<!-- 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
Defines the dormant, versioned managed startup-profile contract for OpenClaw, Hermes, and LangChain Deep Agents Code. It adds bounded canonical serialization and transport for later image-owned startup work without changing a production onboarding caller or activating buildless support.
Related Issue
Part of #7744
Changes
Type of Change
Quality Gates
Documentation Writer Review
no-docs-neededDGX Station Hardware Evidence
Verification
ea5bd8a8e617563133b1f8fc2843d6b7ba407511/0030ba4d3e50a0402a440776f83bace03304493738329e80c82301bba2cec38fe13d7c68c42bc29d,7f3859a32e9791ec40337785d72cad8674b37c96,32561a916f0b60336761a460120259adf7281f6e,087a9a58765bfc6e51fe3ae54c8b1f8ed007877f,e1845dd7a4ea3f1dca67aaa91544d234bbcc8bcf,244e3e642fd687dd9ea88158fcd335b8ad485304,24033160bb8a986ce32b6a6dd26ef1a709388d55, andea5bd8a8e617563133b1f8fc2843d6b7ba407511each contain an SSH signature andSigned-off-by: Aaron Erickson <aerickson@nvidia.com>; GitHub reports every pushed signature as valid andVerified.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:prthen passed after the final current-main merge on the exact head.npm testfor broad runtime/test-harness changes;npm run checkfor repo-wide validation/coverage changes — command/result: Not applicable because this slice adds an inert internal contract and focused test mapping. Exact-head required CI remains the broad gate.npm run docsbuilds without warnings (doc changes only)Stack
mainat0030ba4d3e50a0402a440776f83bace033044937(including merged PR3.1 commita8c7ab01ef8442e6edc3b071c758d680ecd29549)feat/buildless-startup-profile-v2atea5bd8a8e617563133b1f8fc2843d6b7ba407511validate:pr, diff and review-budget checks, and an independent P1/P2 audit before push.Signed-off-by: Aaron Erickson aerickson@nvidia.com