Skip to content

feat(onboard): apply startup profiles in managed images - #7961

Merged
ericksoa merged 27 commits into
mainfrom
feat/buildless-managed-image-application
Jul 31, 2026
Merged

feat(onboard): apply startup profiles in managed images#7961
ericksoa merged 27 commits into
mainfrom
feat/buildless-managed-image-application

Conversation

@ericksoa

@ericksoa ericksoa commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Applies validated all-agent startup plans inside managed images and defines exact runtime-input cleanup. It preserves a sanitized child environment and deterministic replay for OpenClaw, Hermes, and LangChain Deep Agents Code without enabling production buildless onboarding.

The managed-startup runtime and OCI argv normalizer are deliberately not installed or invoked by a production image entrypoint in this slice. PR3.13 is the named consumer that installs the image-owned assets, wires every applicable OpenClaw, Hermes, and DCode entrypoint together, and qualifies their exact image layouts. Wiring a production entrypoint here would make an incomplete, unqualified subset reachable before the all-agent publication and protected-E2E gates, violating the epic activation invariant. This PR is internally complete as the dormant application/runtime contract; production reachability and entrypoint integration travel with their all-agent image tests in PR3.13.

Related Issue

Part of #7744

Changes

  • Apply validated agent-environment plans to the managed image runtime.
  • Forward or remove each declared runtime input according to its per-agent disposition.
  • Remove OpenClaw-only scheduler controls for Hermes and DCode while preserving the supported OpenClaw set.
  • Keep child commands on a sanitized environment and serialize only validated application exports into the final runtime.
  • Reject NUL, CR, and LF before numeric canonicalization and avoid implicit process.env defaults.
  • Refresh committed replay output with a stable fingerprint while rejecting invalid input before filesystem/coordinator mutation.
  • Keep changed test scaffolding branchless so the codebase-growth policy evaluates the exact behavior linearly.
  • Integrate entrypoint, messaging, and sandbox-launch contracts without activating the stock buildless path.

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: The application/runtime code remains behind dormant managed-startup contracts and changes no supported CLI, configuration, workflow, or runtime 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 covered environment sanitization, agent-gated cleanup, invalid-input ordering, deterministic replay, and absence of 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 fourteen-file diff (+2,308/-66) changes dormant application/runtime internals and focused tests only. No user-visible buildless support is advertised or enabled.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

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

Verification

  • Exact locally validated head/base: 13932b9b2486cd42b6fc47645e2f3936e5308d1d / f8fb820159c4843a19759efc9b0e28d4aa122440 (exact-head CI and protected E2E running)
  • Review budget: 14 files, +2,308/-66; 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: 248 focused tests passed; 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 dormant behavior 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: current main at f8fb820159c4843a19759efc9b0e28d4aa122440 (PR3.4a feat(onboard): map and coordinate startup profiles #7960 remains merged)
  • This slice: PR3.4b branch feat/buildless-managed-image-application at 13932b9b2486cd42b6fc47645e2f3936e5308d1d
  • Next: PR3.5 adds root-owned shared-state transactions and Docker adapters. 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 support for sandboxed agents, including validated runtime environment forwarding and secure configuration setup.
    • OpenClaw now supports fast auto-pair re-entry interval and polling controls.
    • Improved messaging setup by applying only active, enabled channels and relevant post-install steps.
    • Added support for managed startup commands launched with approved environment variables.
  • Bug Fixes

    • Prevented unsupported, malformed, duplicate, or unsafe startup environment settings from being applied.
    • Improved handling of certificates, configuration files, ownership, permissions, and runtime metadata during startup.

ericksoa added 11 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>
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

This change adds managed startup environment normalization, application runtime planning, secure image-runtime execution, shared messaging selectors, and managed post-agent-install behavior. It also forwards OpenClaw fast auto-pair controls and expands validation coverage.

Changes

Managed startup environment and entrypoint

Layer / File(s) Summary
Entrypoint normalization and startup handoff
scripts/lib/entrypoint-env-wrapper.sh, test/entrypoint-env-wrapper.test.ts, ci/env-var-doc-allowlist.json
The wrapper validates approved assignments, exports them after complete validation, preserves command arguments, and documents the managed Hermes hash handoff. Tests cover malformed input, command handling, cleanup, and dashboard settings.
Application runtime planning
src/lib/onboard/managed-startup/agent-environment.ts, src/lib/onboard/managed-startup-profile.test.ts, src/lib/onboard/managed-startup-agent-environment.test.ts, src/lib/onboard/managed-startup/profile.ts
Agent environment mapping now validates OpenClaw runtime values, creates immutable export and cleanup plans, and applies the updated scheduler-input classifications.
Managed startup image runtime
src/lib/onboard/managed-startup/image-runtime.ts, src/lib/onboard/managed-startup-image-runtime.test.ts
The image runtime now validates action plans, executes managed startup operations, seals configuration, handles trusted files and certificates, writes runtime environments, and dispatches internal hash commands.

Messaging build behavior

Layer / File(s) Summary
Messaging selection and managed post-install mode
src/lib/messaging/post-agent-install-selection.ts, src/lib/messaging/applier/build/messaging-build-applier.mts, test/messaging-build-applier.test.ts
Shared selectors normalize active channels and filter renders and build files. The applier accepts --managed-startup-runtime, restricts it to post-agent-install, and skips the OpenClaw doctor step in that mode.

OpenClaw runtime forwarding

Layer / File(s) Summary
Fast auto-pair runtime inputs
src/lib/onboard/sandbox-create-launch.ts, src/lib/onboard/sandbox-create-launch.test.ts
OpenClaw sandbox launches now forward validated fast re-entry interval and polling variables. Tests verify forwarding and non-OpenClaw handling.

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

Possibly related PRs

Suggested labels: area: packaging

Suggested reviewers: apurvvkumaria

Sequence Diagram(s)

sequenceDiagram
  participant EntrypointWrapper
  participant ManagedStartupImageRuntime
  participant AgentEnvironmentMapper
  participant MessagingBuildApplier

  EntrypointWrapper->>ManagedStartupImageRuntime: Pass normalized startup command and environment
  ManagedStartupImageRuntime->>AgentEnvironmentMapper: Map profile and application runtime inputs
  AgentEnvironmentMapper-->>ManagedStartupImageRuntime: Return export and cleanup plan
  ManagedStartupImageRuntime->>MessagingBuildApplier: Apply post-agent-install managed runtime
  MessagingBuildApplier-->>ManagedStartupImageRuntime: Restore messaging configuration
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% 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 summarizes the main change: applying validated startup profiles in managed images.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/buildless-managed-image-application

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 13932b9 in the feat/buildless-manag... branch remains at 96%, unchanged from commit 387cb08 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 13932b9 in the feat/buildless-manag... branch remains at 81%, unchanged from commit 387cb08 in the main branch.

Show a code coverage summary of the most impacted files.
File main 387cb08 feat/buildless-manag... 13932b9 +/-
src/lib/onboard...mage-runtime.ts 93% 52% -41%
src/lib/onboard...box-prebuild.ts 92% 74% -18%
src/lib/actions...ocker-health.ts 82% 65% -17%
src/lib/actions...confirmation.ts 79% 69% -10%
src/lib/onboard...box-gpu-mode.ts 97% 92% -5%
src/lib/onboard/preflight.ts 80% 80% 0%
src/lib/onboard...-environment.ts 92% 96% +4%
src/lib/onboard...host-anchors.ts 90% 94% +4%
src/lib/onboard/docker-cdi.ts 70% 80% +10%
src/lib/messagi...ll-selection.ts 0% 100% +100%

Updated July 31, 2026 08:48 UTC

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Blocking findings reported

Advisor assessment: Blockers require maintainer review
Next action: Review the blockers below.
Findings: 1 blocker · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 1 blocker · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 2 warnings · 0 suggestions
  • Model comparison: normalized findings differ; normalized E2E selections differ; Nemotron reported 1 fewer blocker, 2 more warnings, the same number of 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: channels-add-remove, channels-stop-start, onboard-repair, onboard-resume, cloud-onboard

3 optional E2E recommendations
  • rebuild-openclaw
  • rebuild-hermes
  • hermes-shields-config

Blockers

PRA-1 Blocker — Invoke the entrypoint environment normalizer in shipped entrypoints

  • Location: scripts/lib/entrypoint-env-wrapper.sh:17
  • Category: correctness
  • Problem: The new normalizer is not sourced by a shipped entrypoint. The Dockerfiles invoke `/usr/local/bin/nemoclaw-start` directly, so an OCI `env NAME=value … nemoclaw-start` argv reaches the existing entrypoint without the intended normalization.
  • Impact: Managed startup profile, CA, proxy, and launch-control assignments in the OCI wrapper form are treated as command arguments instead of environment values. The wrapper's allowlist and fail-closed validation do not protect that production path.
  • Fix: Source and invoke `nemoclaw_normalize_entrypoint_env_wrapper "$@"` in each applicable `/usr/local/bin/nemoclaw-start` implementation before it reads managed-startup input or command arguments, then replace argv with the normalized tail.
  • Verification: Read each shipped start script for a source/invocation of `nemoclaw_normalize_entrypoint_env_wrapper`; invoke the existing entrypoint test fixture with the OCI env-wrapper argv form.
  • Test coverage: Add an integration-style entrypoint test for each applicable managed image that invokes its shipped entrypoint with `env NEMOCLAW_STARTUP_PROFILE_B64=… nemoclaw-start …` and verifies the profile reaches managed startup while an unsupported assignment fails.
  • Evidence: scripts/lib/entrypoint-env-wrapper.sh:17-25 defines the normalizer, but repository search found no production caller. Dockerfile:1773, agents/hermes/Dockerfile:1260, and agents/langchain-deepagents-code/Dockerfile:272 set ENTRYPOINT to `/usr/local/bin/nemoclaw-start`. test/entrypoint-env-wrapper.test.ts:20-25 sources and invokes the helper only in a test harness.

Workflow run details

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

ericksoa added 8 commits July 30, 2026 18:52
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>
Preserve the exact reviewed tree while moving the stacked base to merged PR3.3.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Preserve the exact reviewed tree while moving the stacked base to restacked PR3.4a.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Carry the reviewed PR3.4b slice unchanged onto the CodeRabbit feedback fix for PR3.4a.

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

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 4

🧹 Nitpick comments (3)
test/messaging-build-applier.test.ts (1)

1276-1298: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the managed-runtime rejection paths.

The managed happy path is covered. The two new guards are not. Add one test that calls applyMessagingBuildPhase with managedStartupRuntime: true and a phase other than post-agent-install, and one test that runs the CLI with --managed-startup-runtime and --phase runtime-setup. Assert the specific error text in each case. These tests keep managed mode restricted to post-agent-install.

🤖 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/messaging-build-applier.test.ts` around lines 1276 - 1298, Add coverage
for both managed-runtime rejection paths: directly call applyMessagingBuildPhase
with managedStartupRuntime: true and a non-post-agent-install phase, asserting
the specific rejection error text, and run the CLI with
--managed-startup-runtime and --phase runtime-setup, asserting its specific
error output. Keep the existing managed post-agent-install success test
unchanged.
test/entrypoint-env-wrapper.test.ts (1)

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

Reuse HELPER for the normalizer path. sliceBlock already throws when either marker is missing.

🤖 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/entrypoint-env-wrapper.test.ts` around lines 118 - 133, Update the test
snippet construction to reuse the existing HELPER symbol for the normalizer path
instead of independently reading entrypoint-env-wrapper.sh with fs.readFileSync
and path.join. Keep sliceBlock responsible for extracting the block and
preserving its existing missing-marker validation.
src/lib/onboard/managed-startup/image-runtime.ts (1)

1024-1037: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use one comparator for both sorted blocks.

unsetLines sorts with the default Array.prototype.sort (UTF-16 code-unit order). exportLines sorts with localeCompare. localeCompare depends on the active ICU locale, so the export order can differ between build hosts even for the same input. The file is a deterministic artifact, so use the same code-unit comparator in both places.

♻️ Proposed comparator alignment
   const exportLines = Object.entries(output)
-    .sort(([left], [right]) => left.localeCompare(right))
+    .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
     .map(([name, value]) => {

The same comparator appears in validateManagedStartupApplicationRuntimePlan at Line 192; align it too if you take this change.

🤖 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 1024 - 1037,
Use a shared deterministic code-unit comparator for sorting both unsetNames in
unsetLines and Object.entries(output) in exportLines, replacing localeCompare so
artifact ordering is host-independent. Also update
validateManagedStartupApplicationRuntimePlan to use the same comparator, reusing
the existing comparator symbol if available rather than defining inconsistent
sorting logic.
🤖 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/messaging/post-agent-install-selection.ts`:
- Around line 24-77: Extract a shared channel-id normalizer from
selectActiveMessagingChannelIds and use it consistently in
selectEnabledMessagingAgentRender and selectEnabledPostAgentInstallBuildFiles.
Normalize render.channelId and step.channelId before checking membership, and
normalize both channel.channelId and step.channelId when locating the matching
channel and hook phase, while preserving the existing filtering behavior.
- Around line 4-38: Update selectActiveMessagingChannelIds to return
enabledPlanChannels(plan).map(({ channelId }) => channelId), ensuring
plan.disabledChannels is respected. Remove the duplicate local interfaces and
channel-selection logic, while preserving the helper’s canonical enabled-channel
ordering.

In `@src/lib/onboard/managed-startup/image-runtime.ts`:
- Around line 1278-1283: Update the CLI entry check around main so it does not
access require.main when require is unavailable under Vitest’s ESM runner; guard
require before evaluating the entry condition, while preserving the existing
CommonJS behavior and error handling.

In `@test/entrypoint-env-wrapper.test.ts`:
- Around line 156-161: Update runScenario to remove NEMOCLAW_DASHBOARD_PORT and
CHAT_UI_URL from the inherited process environment before spreading extraEnv.
Preserve all other ambient variables and ensure extraEnv is applied last so each
port scenario controls these values deterministically.

---

Nitpick comments:
In `@src/lib/onboard/managed-startup/image-runtime.ts`:
- Around line 1024-1037: Use a shared deterministic code-unit comparator for
sorting both unsetNames in unsetLines and Object.entries(output) in exportLines,
replacing localeCompare so artifact ordering is host-independent. Also update
validateManagedStartupApplicationRuntimePlan to use the same comparator, reusing
the existing comparator symbol if available rather than defining inconsistent
sorting logic.

In `@test/entrypoint-env-wrapper.test.ts`:
- Around line 118-133: Update the test snippet construction to reuse the
existing HELPER symbol for the normalizer path instead of independently reading
entrypoint-env-wrapper.sh with fs.readFileSync and path.join. Keep sliceBlock
responsible for extracting the block and preserving its existing missing-marker
validation.

In `@test/messaging-build-applier.test.ts`:
- Around line 1276-1298: Add coverage for both managed-runtime rejection paths:
directly call applyMessagingBuildPhase with managedStartupRuntime: true and a
non-post-agent-install phase, asserting the specific rejection error text, and
run the CLI with --managed-startup-runtime and --phase runtime-setup, asserting
its specific error output. Keep the existing managed post-agent-install success
test unchanged.
🪄 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: b58ebc9a-7701-42fa-844c-464466c27095

📥 Commits

Reviewing files that changed from the base of the PR and between b14013f and 46fbe19.

📒 Files selected for processing (14)
  • ci/env-var-doc-allowlist.json
  • scripts/lib/entrypoint-env-wrapper.sh
  • src/lib/messaging/applier/build/messaging-build-applier.mts
  • src/lib/messaging/post-agent-install-selection.ts
  • src/lib/onboard/managed-startup-agent-environment.test.ts
  • src/lib/onboard/managed-startup-image-runtime.test.ts
  • src/lib/onboard/managed-startup-profile.test.ts
  • src/lib/onboard/managed-startup/agent-environment.ts
  • src/lib/onboard/managed-startup/image-runtime.ts
  • src/lib/onboard/managed-startup/profile.ts
  • src/lib/onboard/sandbox-create-launch.test.ts
  • src/lib/onboard/sandbox-create-launch.ts
  • test/entrypoint-env-wrapper.test.ts
  • test/messaging-build-applier.test.ts

Comment on lines +4 to +38
interface SelectionHook {
readonly id: string;
readonly phase: string;
}

interface SelectionChannel {
readonly channelId: string;
readonly active?: boolean;
readonly disabled?: boolean;
readonly hooks?: readonly SelectionHook[];
}

interface SelectionPlanBase {
readonly channels: readonly SelectionChannel[];
}

/**
* Canonical active-channel selection for the image applier. Each selection
* consumer must resolve the same active channels and mutable outputs.
*/
export function selectActiveMessagingChannelIds(plan: SelectionPlanBase): string[] {
const seen = new Set<string>();
const channels: string[] = [];
for (const item of plan.channels) {
const channel = String(item.channelId || "")
.trim()
.toLowerCase();
if (!channel || seen.has(channel)) continue;
if (item.active === true && item.disabled !== true) {
seen.add(channel);
channels.push(channel);
}
}
return channels;
}

@coderabbitai coderabbitai Bot Jul 31, 2026

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
# Locate the existing enabled-channel helpers and compare their filter semantics.
set -uo pipefail
rg -nP --type=ts -C 12 '\b(enabledPlanChannels|filterEnabledPlanEntries)\s*(<|\()' src/lib/messaging
rg -nP --type=ts -C 4 '\b(enabledPlanChannels|filterEnabledPlanEntries)\s*\(' src | rg -v '\.test\.'

Repository: NVIDIA/NemoClaw

Length of output: 23824


🏁 Script executed:

#!/bin/bash
set -uo pipefail
printf '%s\n' '--- target file ---'
cat -n src/lib/messaging/post-agent-install-selection.ts
printf '%s\n' '--- usages and imports ---'
rg -n -C 8 'selectActiveMessagingChannelIds|post-agent-install-selection|getActiveChannelIdsFromPlan|disabledChannels' src/lib/messaging
printf '%s\n' '--- manifest declarations ---'
rg -n -C 8 'interface SandboxMessaging(ChannelPlan|Plan)|type SandboxMessaging(ChannelPlan|Plan)|disabledChannels' src/lib/messaging/manifest*

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -uo pipefail
printf '%s\n' '--- target imports/usages outside its declaration ---'
rg -n 'post-agent-install-selection|selectActiveMessagingChannelIds|selectEnabledMessagingAgentRender|selectEnabledPostAgentInstallBuildFiles' --glob '!src/lib/messaging/post-agent-install-selection.ts' .
printf '%s\n' '--- shared filter and plan types ---'
cat -n src/lib/messaging/applier/plan-filter.ts
rg -n -C 4 'export interface SandboxMessaging(ChannelPlan|Plan)|export type MessagingChannelId|disabledChannels:' src/lib/messaging/manifest.ts
printf '%s\n' '--- all active-channel helper definitions ---'
rg -n -C 6 'function (getActiveChannelIdsFromPlan|enabledPlanChannels|selectActiveMessagingChannelIds)|export function (getActiveChannelIdsFromPlan|enabledPlanChannels|selectActiveMessagingChannelIds)' src/lib/messaging

Repository: NVIDIA/NemoClaw

Length of output: 6881


🏁 Script executed:

#!/bin/bash
set -uo pipefail
printf '%s\n' '--- build-applier call sites ---'
sed -n '330,360p' src/lib/messaging/applier/build/messaging-build-applier.mts
sed -n '950,990p' src/lib/messaging/applier/build/messaging-build-applier.mts
printf '%s\n' '--- behavioral comparison ---'
python3 - <<'PY'
def local_selector(plan):
    seen, out = set(), []
    for item in plan["channels"]:
        channel = str(item.get("channelId") or "").strip().lower()
        if not channel or channel in seen:
            continue
        if item.get("active") is True and item.get("disabled") is not True:
            seen.add(channel)
            out.append(channel)
    return out

def shared_selector(plan):
    disabled = set(plan["disabledChannels"])
    return [
        item["channelId"]
        for item in plan["channels"]
        if item.get("active") and not item.get("disabled") and item["channelId"] not in disabled
    ]

cases = [
    {
        "name": "disabledChannels-only exclusion",
        "plan": {
            "channels": [{"channelId": "telegram", "active": True, "disabled": False}],
            "disabledChannels": ["telegram"],
        },
    },
    {
        "name": "normal active channel",
        "plan": {
            "channels": [{"channelId": "telegram", "active": True, "disabled": False}],
            "disabledChannels": [],
        },
    },
]
for case in cases:
    local = local_selector(case["plan"])
    shared = shared_selector(case["plan"])
    print(case["name"], "local=", local, "shared=", shared, "different=", local != shared)
PY

Repository: NVIDIA/NemoClaw

Length of output: 2639


Use the shared enabled-channel helper.

selectActiveMessagingChannelIds() ignores plan.disabledChannels, so build rendering can apply effects for disabled channels. Use enabledPlanChannels(plan).map(({ channelId }) => channelId) and remove the duplicate local shapes.

🤖 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/messaging/post-agent-install-selection.ts` around lines 4 - 38,
Update selectActiveMessagingChannelIds to return
enabledPlanChannels(plan).map(({ channelId }) => channelId), ensuring
plan.disabledChannels is respected. Remove the duplicate local interfaces and
channel-selection logic, while preserving the helper’s canonical enabled-channel
ordering.

Source: Coding guidelines

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in stacked follow-up #7976 (27c72f5f888e9301b3cce1894431ef5de3669425) so #7961’s already-green head remains stable. The focused/local validation for this exact five-file debt slice passed; exact-head GitHub qualification is running. Leaving this thread open until #7976 is green.

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.

@ericksoa, acknowledged. The fix is in stacked follow-up #7976. I will keep this thread open until the exact-head GitHub qualification completes successfully.

You are interacting with an AI system.

Comment on lines +24 to +77
export function selectActiveMessagingChannelIds(plan: SelectionPlanBase): string[] {
const seen = new Set<string>();
const channels: string[] = [];
for (const item of plan.channels) {
const channel = String(item.channelId || "")
.trim()
.toLowerCase();
if (!channel || seen.has(channel)) continue;
if (item.active === true && item.disabled !== true) {
seen.add(channel);
channels.push(channel);
}
}
return channels;
}

export function selectEnabledMessagingAgentRender<
Render extends {
readonly agent: string;
readonly channelId: string;
},
>(
plan: SelectionPlanBase & {
readonly agent: string;
readonly agentRender: readonly Render[];
},
): Render[] {
const active = new Set(selectActiveMessagingChannelIds(plan));
return plan.agentRender.filter(
(render) => render.agent === plan.agent && active.has(render.channelId),
);
}

export function selectEnabledPostAgentInstallBuildFiles<
Step extends {
readonly channelId: string;
readonly kind: string;
readonly hookId?: string;
},
>(
plan: SelectionPlanBase & {
readonly buildSteps: readonly Step[];
},
): Step[] {
const active = new Set(selectActiveMessagingChannelIds(plan));
return plan.buildSteps.filter((step) => {
if (!active.has(step.channelId) || step.kind !== "build-file") return false;
if (!step.hookId) return true;
const hookPhase = plan.channels
.find((channel) => channel.channelId === step.channelId)
?.hooks?.find((hook) => hook.id === step.hookId)?.phase;
return hookPhase === undefined || hookPhase === "post-agent-install";
});
}

@coderabbitai coderabbitai Bot Jul 31, 2026

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

Normalize channel ids on both sides of every comparison.

selectActiveMessagingChannelIds stores trimmed, lowercased ids at lines 28-30. The consumers compare raw ids: line 53 (active.has(render.channelId)), line 70 (active.has(step.channelId)), and line 73 (channel.channelId === step.channelId). If a plan carries a channel id with different case or surrounding whitespace in agentRender or buildSteps, the selection returns empty and the render and build-file effects are dropped without an error. Extract one normalizer and apply it to both sides.

🐛 Proposed fix for asymmetric normalization
+function normalizeChannelId(channelId: string): string {
+  return channelId.trim().toLowerCase();
+}
+
 export function selectActiveMessagingChannelIds(plan: SelectionPlanBase): string[] {
   const seen = new Set<string>();
   const channels: string[] = [];
   for (const item of plan.channels) {
-    const channel = String(item.channelId || "")
-      .trim()
-      .toLowerCase();
+    const channel = normalizeChannelId(item.channelId ?? "");
   const active = new Set(selectActiveMessagingChannelIds(plan));
   return plan.agentRender.filter(
-    (render) => render.agent === plan.agent && active.has(render.channelId),
+    (render) => render.agent === plan.agent && active.has(normalizeChannelId(render.channelId)),
   );
   const active = new Set(selectActiveMessagingChannelIds(plan));
   return plan.buildSteps.filter((step) => {
-    if (!active.has(step.channelId) || step.kind !== "build-file") return false;
+    const channelId = normalizeChannelId(step.channelId);
+    if (!active.has(channelId) || step.kind !== "build-file") return false;
     if (!step.hookId) return true;
     const hookPhase = plan.channels
-      .find((channel) => channel.channelId === step.channelId)
+      .find((channel) => normalizeChannelId(channel.channelId) === channelId)
       ?.hooks?.find((hook) => hook.id === step.hookId)?.phase;
📝 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
export function selectActiveMessagingChannelIds(plan: SelectionPlanBase): string[] {
const seen = new Set<string>();
const channels: string[] = [];
for (const item of plan.channels) {
const channel = String(item.channelId || "")
.trim()
.toLowerCase();
if (!channel || seen.has(channel)) continue;
if (item.active === true && item.disabled !== true) {
seen.add(channel);
channels.push(channel);
}
}
return channels;
}
export function selectEnabledMessagingAgentRender<
Render extends {
readonly agent: string;
readonly channelId: string;
},
>(
plan: SelectionPlanBase & {
readonly agent: string;
readonly agentRender: readonly Render[];
},
): Render[] {
const active = new Set(selectActiveMessagingChannelIds(plan));
return plan.agentRender.filter(
(render) => render.agent === plan.agent && active.has(render.channelId),
);
}
export function selectEnabledPostAgentInstallBuildFiles<
Step extends {
readonly channelId: string;
readonly kind: string;
readonly hookId?: string;
},
>(
plan: SelectionPlanBase & {
readonly buildSteps: readonly Step[];
},
): Step[] {
const active = new Set(selectActiveMessagingChannelIds(plan));
return plan.buildSteps.filter((step) => {
if (!active.has(step.channelId) || step.kind !== "build-file") return false;
if (!step.hookId) return true;
const hookPhase = plan.channels
.find((channel) => channel.channelId === step.channelId)
?.hooks?.find((hook) => hook.id === step.hookId)?.phase;
return hookPhase === undefined || hookPhase === "post-agent-install";
});
}
function normalizeChannelId(channelId: string): string {
return channelId.trim().toLowerCase();
}
export function selectActiveMessagingChannelIds(plan: SelectionPlanBase): string[] {
const seen = new Set<string>();
const channels: string[] = [];
for (const item of plan.channels) {
const channel = normalizeChannelId(item.channelId ?? "");
if (!channel || seen.has(channel)) continue;
if (item.active === true && item.disabled !== true) {
seen.add(channel);
channels.push(channel);
}
}
return channels;
}
export function selectEnabledMessagingAgentRender<
Render extends {
readonly agent: string;
readonly channelId: string;
},
>(
plan: SelectionPlanBase & {
readonly agent: string;
readonly agentRender: readonly Render[];
},
): Render[] {
const active = new Set(selectActiveMessagingChannelIds(plan));
return plan.agentRender.filter(
(render) => render.agent === plan.agent && active.has(normalizeChannelId(render.channelId)),
);
}
export function selectEnabledPostAgentInstallBuildFiles<
Step extends {
readonly channelId: string;
readonly kind: string;
readonly hookId?: string;
},
>(
plan: SelectionPlanBase & {
readonly buildSteps: readonly Step[];
},
): Step[] {
const active = new Set(selectActiveMessagingChannelIds(plan));
return plan.buildSteps.filter((step) => {
const channelId = normalizeChannelId(step.channelId);
if (!active.has(channelId) || step.kind !== "build-file") return false;
if (!step.hookId) return true;
const hookPhase = plan.channels
.find((channel) => normalizeChannelId(channel.channelId) === channelId)
?.hooks?.find((hook) => hook.id === step.hookId)?.phase;
return hookPhase === undefined || hookPhase === "post-agent-install";
});
}
🤖 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/messaging/post-agent-install-selection.ts` around lines 24 - 77,
Extract a shared channel-id normalizer from selectActiveMessagingChannelIds and
use it consistently in selectEnabledMessagingAgentRender and
selectEnabledPostAgentInstallBuildFiles. Normalize render.channelId and
step.channelId before checking membership, and normalize both channel.channelId
and step.channelId when locating the matching channel and hook phase, while
preserving the existing filtering behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in stacked follow-up #7976 (27c72f5f888e9301b3cce1894431ef5de3669425) so #7961’s already-green head remains stable. The focused/local validation for this exact five-file debt slice passed; exact-head GitHub qualification is running. Leaving this thread open until #7976 is green.

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.

@ericksoa, acknowledged. Keep this thread open until #7976 completes GitHub qualification. The finding remains pending on that follow-up.

You are interacting with an AI system.

Comment thread src/lib/onboard/managed-startup/image-runtime.ts
Comment on lines +156 to +161
return spawnSync("bash", [scriptPath], {
encoding: "utf-8",
timeout: 5000,
env: { ...process.env, PATH: `${fakeBin}:${process.env.PATH || ""}`, ...extraEnv },
});
}

@coderabbitai coderabbitai Bot Jul 31, 2026

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

Make the port scenarios hermetic.

runScenario spreads the whole ambient environment into the child. The scenario at lines 196-206 asserts the 18789 default, and the scenario at lines 184-194 asserts a specific port. If a developer shell or a CI job exports NEMOCLAW_DASHBOARD_PORT or CHAT_UI_URL, these assertions change result without any code change. Remove the two variables that the test controls, then apply extraEnv.

Based on learnings: hermetic tests in this repo must not inherit CI or local environment values that can activate or override behavior under assertion.

🧪 Proposed fix for environment inheritance
     function runScenario(setArgs: string, extraEnv: Record<string, string> = {}) {
+      const baseEnv = { ...process.env };
+      delete baseEnv.NEMOCLAW_DASHBOARD_PORT;
+      delete baseEnv.CHAT_UI_URL;
       return spawnSync("bash", [scriptPath], {
         encoding: "utf-8",
         timeout: 5000,
-        env: { ...process.env, PATH: `${fakeBin}:${process.env.PATH || ""}`, ...extraEnv },
+        env: { ...baseEnv, PATH: `${fakeBin}:${process.env.PATH || ""}`, ...extraEnv },
       });
📝 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
return spawnSync("bash", [scriptPath], {
encoding: "utf-8",
timeout: 5000,
env: { ...process.env, PATH: `${fakeBin}:${process.env.PATH || ""}`, ...extraEnv },
});
}
function runScenario(setArgs: string, extraEnv: Record<string, string> = {}) {
const baseEnv = { ...process.env };
delete baseEnv.NEMOCLAW_DASHBOARD_PORT;
delete baseEnv.CHAT_UI_URL;
return spawnSync("bash", [scriptPath], {
encoding: "utf-8",
timeout: 5000,
env: { ...baseEnv, PATH: `${fakeBin}:${process.env.PATH || ""}`, ...extraEnv },
});
}
🤖 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/entrypoint-env-wrapper.test.ts` around lines 156 - 161, Update
runScenario to remove NEMOCLAW_DASHBOARD_PORT and CHAT_UI_URL from the inherited
process environment before spreading extraEnv. Preserve all other ambient
variables and ensure extraEnv is applied last so each port scenario controls
these values deterministically.

Source: Learnings

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in stacked follow-up #7976 (27c72f5f888e9301b3cce1894431ef5de3669425) so #7961’s already-green head remains stable. The focused/local validation for this exact five-file debt slice passed; exact-head GitHub qualification is running. Leaving this thread open until #7976 is green.

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.

@ericksoa, acknowledged. The fix is tracked in stacked follow-up PR #7976.

I will leave this thread open until the exact-head GitHub qualification is green.


🧠 Learnings used
Learnt from: laitingsheng
Repo: NVIDIA/NemoClaw PR: 2511
File: test/onboard-brave-validation.test.ts:45-48
Timestamp: 2026-04-27T09:53:20.358Z
Learning: In this repo’s test suite, prefer the established POSIX PATH separator `:` when constructing `process.env.PATH` in tests (e.g., `PATH: `${fakeBin}:${process.env.PATH || ""}``). Do not replace it with `path.delimiter` in these unit/integration tests, because they only run on Linux runners in CI here; `windows-latest` is limited to WSL e2e tests in `.github/workflows/wsl-e2e.yaml` and is already POSIX-compliant.

Learnt from: ericksoa
Repo: NVIDIA/NemoClaw PR: 3205
File: test/onboard.test.ts:4236-4250
Timestamp: 2026-05-07T23:34:28.652Z
Learning: For hermetic messaging-channel tests in this repo (e.g., files like test/onboard.test.ts), ensure the spawned helper script does not inherit CI/local messaging credentials/config that could enable extra channels. Before calling createSandbox() inside the helper script, delete/remove unrelated messaging env vars such as DISCORD_* and TELEGRAM_* (tokens/config). This prevents inherited environment from activating additional channels and breaking/destabilizing Slack-only (or other single-channel) assertions.

You are interacting with an AI system.

ericksoa added 4 commits July 30, 2026 21:18
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Carry the reviewed PR3.4b slice unchanged onto the serialized PR3.4a transaction contract.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Restack PR3.4b without changing its review patch.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Base automatically changed from feat/buildless-profile-application-coordinator to main July 31, 2026 05:10
@copy-pr-bot

copy-pr-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Trigger exact-head CI after the canonical GitHub bot restack without changing the reviewed tree.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@ericksoa ericksoa self-assigned this Jul 31, 2026
@ericksoa

Copy link
Copy Markdown
Contributor Author

Advisor PRA-1 disposition for exact head deb15814281aa534a4828fb3aef62bd387688781: intentionally deferred by the stack activation invariant, not left unowned. This review unit is the dormant all-agent image-runtime and OCI-argv normalization contract. Installing and invoking it from production entrypoints here would make an incomplete, unqualified buildless path reachable before the all-agent image and protected-E2E gates.

The named consumer is PR3.13, which installs the image-owned runtime and wrapper and wires every applicable OpenClaw, Hermes, and DCode entrypoint together. PR3.14b/3.14c qualify all-agent multiarch, GPU/local-inference, and recovery behavior; PR3.15 is the only production activation slice. Those obligations are explicit in #7744 and in this PR description. Wiring only this precursor now would violate the maintainer-approved atomic activation boundary, so no production-caller change belongs in #7961.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@ericksoa
ericksoa merged commit 7eb0369 into main Jul 31, 2026
66 of 67 checks passed
@ericksoa
ericksoa deleted the feat/buildless-managed-image-application branch July 31, 2026 12:31
jyaunches pushed a commit that referenced this pull request Jul 31, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Adds root-owned, transactional managed shared-state application and the
first Docker adapter. The slice applies and rolls back validated
OpenClaw, Hermes, and LangChain Deep Agents Code state atomically while
keeping production buildless activation and durable crash recovery in
later review units.

## Related Issue

Part of #7744

Supersedes #7962; the implementation tree and review patch are
identical, but this branch corrects an immutable restack commit-message
line that violated `commitlint`.

## Changes

- Define the managed shared-state transaction contract, ownership/mode
checks, commit receipt, and idempotent rollback.
- Add Docker staging and root-apply adapters that execute with `env -i`.
- Forward only the six allowlisted OpenClaw scheduler controls through
the clean root path.
- Validate application controls before completion-file inspection or
filesystem/transaction mutation.
- Refresh a verified same-profile runtime and completion digest without
starting a duplicate shared-state transaction.
- Cover OpenClaw, Hermes, and DCode transaction, replay, ownership,
mode, cleanup, and failure behavior.
- Intentionally expose no production cutover caller in this slice.
PR3.10 owns the transactional bootstrap/cutover integration after the
driver-neutral lifecycle exists; PR3.12 owns restart-spanning
persistence; PR3.15 owns production activation. Wiring these primitives
directly into current onboarding here would create the partial runtime
activation prohibited by #7744.

## Type of Change

- [x] 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

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: This transaction layer
remains dormant and does not change a supported CLI, configuration,
runtime selection, workflow, or support statement.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: Exact-diff and
independent P1/P2 reviews covered clean-exec forwarding, pre-mutation
validation, same-profile replay, ownership/mode enforcement, atomic
commit, and rollback. The absence of a production caller is required by
this review boundary: PR3.10 integrates cutover only after PR3.6–3.9
establish lifecycle parity, and PR3.15 activates the complete all-agent
path. No P1/P2 remains inside this slice.
- [ ] 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: `no-docs-needed`
- Evidence: Reviewed the exact 10-file, `+3,251/-37` patch from
`7eb0369e7a0bc91f6ae99fc1eaad3cd274a34c8a` through
`83f29daf129adf51bbffa43106633ee748b7eefc` against the NemoClaw Writing
Guide and controlled terms. The stable patch ID is unchanged from
`13932b9b…511141002`: `9a4ead01527f33245de5ed62412dca1395e33a13`. The
patch adds dormant internal managed-startup image-runtime, root-apply,
shared-state transaction, and Docker-adapter primitives with co-located
source tests. It changes no Markdown, `docs/`, CLI command or action,
public configuration, default, output, workflow, or live-E2E path.
Production-import scans found no activation caller outside the new
internal module graph, so no user-facing documentation change is needed
for this restack.
- Agent: Codex Desktop
<!-- docs-review-head-sha: 83f29da -->
<!-- docs-review-agents-blob-sha: c052d60 -->

## DGX Station Hardware Evidence

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

## Verification

- Exact locally validated head/base:
`83f29daf129adf51bbffa43106633ee748b7eefc` /
`7eb0369e7a0bc91f6ae99fc1eaad3cd274a34c8a` (exact-tree local validation
passed; refreshed remote qualification will run)
- Review budget: 10 files, `+3,251/-37`; no documentation paths. Stable
patch ID: `9a4ead01527f33245de5ed62412dca1395e33a13`.
- [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: 87
slice-focused tests and 290 cross-slice regression tests passed; 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 dormant transaction slice.
- [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)
- [ ] 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)

## Stack

- Base: merged PR3.4b #7961 on `main` at
`7eb0369e7a0bc91f6ae99fc1eaad3cd274a34c8a`
- This slice: PR3.5 branch `feat/buildless-shared-state-transactions-v2`
at `83f29daf129adf51bbffa43106633ee748b7eefc`
- Next: PR3.6 introduces the driver-neutral lifecycle and sandbox-action
parity. 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>



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

* **New Features**
* Added managed startup support for applying configuration through
Docker.
* Added completion verification and waiting so startup progress can be
confirmed.
* Added transaction-based handling for shared application state,
including commit and rollback.

* **Reliability Improvements**
* Added safeguards for invalid, oversized, tampered, or incomplete
startup data.
  * Improved recovery after interrupted or failed startup operations.
* Preserved file ownership, permissions, and contents during state
restoration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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 platform: container Affects Docker, containerd, Podman, or images

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant