Skip to content

feat(onboard): add managed shared-state transactions - #7969

Merged
jyaunches merged 40 commits into
mainfrom
feat/buildless-shared-state-transactions-v2
Jul 31, 2026
Merged

feat(onboard): add managed shared-state transactions#7969
jyaunches merged 40 commits into
mainfrom
feat/buildless-shared-state-transactions-v2

Conversation

@ericksoa

@ericksoa ericksoa commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

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 [Epic] Support native Podman with buildless managed onboarding #7744.

Type of Change

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

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: This transaction layer remains dormant and does not change a supported CLI, configuration, runtime selection, workflow, or support statement.
  • 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 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

  • 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

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.
  • 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: 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.
  • 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: merged PR3.4b feat(onboard): apply startup profiles in managed images #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

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.

ericksoa added 27 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>
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>
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>
Preserve the exact reviewed tree while moving the stacked base to restacked PR3.4b.

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>
Carry the reviewed PR3.5 slice unchanged onto the CodeRabbit feedback restack through PR3.4b.

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 serialized PR3.4a transaction contract.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Carry the reviewed PR3.5 slice unchanged onto the serialized PR3.4a transaction contract.

The review patch remains unchanged through PR3.4b.

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

Managed startup now supports canonical root-apply requests, secure shared-state transactions, completion verification, and Docker-backed application and rollback flows. Tests cover supported agents, replay behavior, filesystem integrity, container identity, retries, and failure recovery.

Changes

Managed startup lifecycle

Layer / File(s) Summary
Canonical root-apply requests
src/lib/onboard/managed-startup/root-apply.ts, src/lib/onboard/managed-startup/managed-startup-root-apply.test.ts
Defines bounded, immutable root-apply requests. Serialization and parsing enforce canonical JSON, profile fingerprints, schema fields, size limits, and corporate-CA integrity.
Shared-state transaction storage and restoration
src/lib/onboard/managed-startup/shared-state-transaction.ts, src/lib/onboard/managed-startup/managed-startup-shared-state-transaction.test.ts
Snapshots managed files and directories, validates receipts and manifests, and supports secure resume, rollback, exact restoration, and commit operations.
Image runtime completion and root application
src/lib/onboard/managed-startup/image-runtime.ts, src/lib/onboard/managed-startup/managed-startup-image-runtime.test.ts
Publishes runtime-environment files and completion markers. Adds root request application, replay handling, completion verification, bounded waiting, and CLI transaction commands.
Docker root application
src/lib/onboard/managed-startup/docker-root-apply.ts, src/lib/onboard/managed-startup/docker-root-apply.test.ts
Validates container identity and immutable image references. Executes root requests with controlled environment forwarding, transaction probes, one retry, and transaction metadata on failures.
Docker shared-state finalization
src/lib/onboard/managed-startup/docker-shared-state.ts, src/lib/onboard/managed-startup/docker-shared-state.test.ts
Preserves rollback receipts, commits shared state through controlled Docker helpers, and restores or removes failed workloads when finalization fails.

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

Possibly related PRs

  • NVIDIA/NemoClaw#6690: Provides related temporary-root and cleanup infrastructure used by managed-startup filesystem and transaction tests.
  • NVIDIA/NemoClaw#7946: Introduces profile schemas, fingerprints, agents, and runtime-input contracts consumed by root-apply validation.
  • NVIDIA/NemoClaw#7960: Shares managed-startup profile mapping and agent-environment application flows extended by this change.

Suggested labels: area: sandbox

Suggested reviewers: cv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.33% 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 describes the main addition of managed shared-state transaction support.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/buildless-shared-state-transactions-v2

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

@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 83f29da in the feat/buildless-share... branch remains at 96%, unchanged from commit 7eb0369 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 83f29da in the feat/buildless-share... branch remains at 81%, unchanged from commit 7eb0369 in the main branch.

Show a code coverage summary of the most impacted files.
File main 7eb0369 feat/buildless-share... 83f29da +/-
src/lib/onboard/docker-cdi.ts 80% 70% -10%
src/lib/onboard...mage-runtime.ts 52% 49% -3%
src/lib/onboard/preflight.ts 80% 80% 0%
src/lib/actions...confirmation.ts 69% 79% +10%
src/lib/actions...ocker-health.ts 65% 82% +17%
src/lib/onboard...box-prebuild.ts 74% 92% +18%
src/lib/onboard...-transaction.ts 0% 78% +78%
src/lib/onboard...p/root-apply.ts 0% 84% +84%
src/lib/onboard...shared-state.ts 0% 87% +87%
src/lib/onboard...r-root-apply.ts 0% 94% +94%

Updated July 31, 2026 12:57 UTC

ericksoa added 2 commits July 30, 2026 21:39
Restack PR3.4b without changing its review patch.

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

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
github-actions Bot and others added 4 commits July 31, 2026 05:13
Preserve the exact reviewed PR3.5 patch while inheriting the canonical restacked PR3.4b base.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Trigger exact-head CI after the canonical GitHub bot restack without changing the reviewed tree.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Preserve the exact reviewed PR3.5 tree while inheriting the signed exact-head validation trigger.

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

Copy link
Copy Markdown
Contributor Author

Advisor PRA-1 warning disposition for exact head 0ad91a50b29402f77dfe14576282c3047512f8aa: accepted and assigned to the first slice that has a real production cutover/recovery path. #7969 intentionally provides dormant root-apply and shared-state transaction primitives; it has no production cutover caller, so a live gateway/sandbox convergence test here would require premature partial activation.

PR3.10b introduces the Docker bootstrap/cutover adapter, PR3.12a/3.12b add durable restart recovery, and PR3.14c now explicitly requires injected root-apply, supervisor, and commit failures followed by independent gateway and sandbox probes compared with reported onboarding status. Epic #7744 has been updated with that exact protected-E2E obligation. The warning is therefore covered without widening this transaction-only review unit.

ericksoa added 2 commits July 31, 2026 00:08
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
ericksoa added 2 commits July 31, 2026 01:34
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@ericksoa
ericksoa changed the base branch from feat/buildless-managed-image-application to main July 31, 2026 12:30
@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.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (10)
src/lib/onboard/managed-startup/shared-state-transaction.ts (1)

448-483: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider syncing the parent directory after renameSync.

atomicWriteTrustedFile calls fsyncSync on the file descriptor, then renameSync. The rename itself is not durable until the parent directory is synced. After a host crash, the manifest or a backup can be absent while the transaction directory exists, which is the partial state that blocks a later resume.

The PR description defers durable restart recovery to a later slice, so this can move with that work.

🤖 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/shared-state-transaction.ts` around lines 448
- 483, Update atomicWriteTrustedFile to sync the parent directory after
fs.renameSync completes, using the existing parent path, so the rename is
durable before the function returns. Keep the current file fsync, cleanup, and
error-handling behavior unchanged.
src/lib/onboard/managed-startup-shared-state-transaction.test.ts (1)

265-314: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the read-only receipt rollback path.

The suite never sets readOnlyReceipt: true. That option changes requireTransactionIdentity, requireTrustedTransactionPath, loadManifest, and the final removeTransactionDirectory step, and production uses it through the --rollback-shared-state-transaction --read-only-receipt helper in src/lib/onboard/managed-startup/docker-shared-state.ts. src/lib/onboard/managed-startup/docker-shared-state.test.ts only asserts the Docker argv, so no test exercises the restore behavior for a read-only receipt.

requireReadOnlyReceiptMount requires a real read-only filesystem, so a unit test needs a seam or an EROFS stub for fs.openSync on the probe path. Add a case that proves the receipt directory survives the rollback and that the managed output is restored exactly.

🤖 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-shared-state-transaction.test.ts` around
lines 265 - 314, Add a test in the managed startup shared-state transaction
suite covering rollback with readOnlyReceipt: true, using an appropriate seam or
EROFS stub for the read-only mount probe. Create a transaction with modified
managed output, invoke rollbackManagedStartupSharedStateTransaction with the
read-only receipt option, and assert the output is restored byte-for-byte while
transactionDirectory remains present. Exercise the production read-only receipt
path, including its identity/path validation and manifest handling.
src/lib/onboard/managed-startup/image-runtime.ts (1)

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

Remove the duplicate unset-name validation.

Line 1085 already validates every key of configurationEnvironment. validateManagedStartupApplicationRuntimePlan already validates every entry of unsetEnvironment with the same pattern. unsetNames is built only from those two validated sources, so the loop at Lines 1099-1103 cannot fail.

Based on learnings: avoid adding validation around internal helper logic when there is no realistic failure mode; only validate at system boundaries.

♻️ Proposed simplification
-  for (const name of unsetNames) {
-    if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)) {
-      fail(`invalid runtime environment key ${JSON.stringify(name)}`);
-    }
-  }
   return { output, unsetNames: [...unsetNames].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/image-runtime.ts` around lines 1085 - 1104,
Remove the redundant key-pattern validation loop over unsetNames in the managed
startup runtime handling. Keep the existing validations for
configurationEnvironment and validatedApplicationRuntime.unsetEnvironment, the
export/unset conflict check, and the final sorted unsetNames result unchanged.

Source: Learnings

src/lib/onboard/managed-startup/docker-root-apply.test.ts (1)

244-281: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add table rows for the malformed and ambiguous inspect guards.

inspectExactContainer rejects malformed inspect JSON at docker-root-apply.ts Lines 87-89 and an inspect result that does not contain exactly one container at Lines 90-92. Neither guard has a test. Both are pre-exec guards, so a regression would let a root exec proceed against an unverified container.

Add two rows to this table: one with a non-JSON inspect string, and one with a two-element inspect array.

💚 Proposed table rows
     {
       label: "short caller identity",
       containerId: "b".repeat(12),
       inspect: stableInspect(),
       error: /full lowercase Docker container ID/u,
     },
+    {
+      label: "malformed inspect output",
+      containerId: CONTAINER_ID,
+      inspect: "not-json",
+      error: /malformed inspect output/u,
+    },
+    {
+      label: "ambiguous inspect result",
+      containerId: CONTAINER_ID,
+      inspect: `[${stableInspect().slice(1, -1)},${stableInspect().slice(1, -1)}]`,
+      error: /exactly one managed-startup container/u,
+    },
🤖 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/docker-root-apply.test.ts` around lines 244 -
281, Add two cases to the existing “rejects $label before root exec” table for
inspectExactContainer: one passing a non-JSON inspect string and one passing a
two-element inspect array. Assert each throws the corresponding validation error
and preserves the expectation that dockerSpawnSync is not called.

Source: Path instructions

src/lib/onboard/managed-startup/docker-shared-state.test.ts (2)

213-287: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add coverage for the unbacked-container removal branch.

finalizeDockerManagedStartupSharedState calls removeFailedUnbackedContainer when patchResult is absent, at docker-shared-state.ts Lines 262 and 269. Every test in this file passes patchResult: result(), so dockerRm is never invoked and that branch is untested. The branch deletes a container, so a regression can remove a container that must be kept, or leave an unbacked failed container behind.

Add one test with patchResult: null and supervisorReady: false, and assert that dockerRm receives the transaction container ID.

🤖 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/docker-shared-state.test.ts` around lines 213
- 287, Add a test alongside the existing finalizeDockerManagedStartupSharedState
cases that passes patchResult: null and supervisorReady: false, provides a
dockerRm mock, invokes finalizeDockerManagedStartupSharedState, and asserts
dockerRm is called with the transaction’s container ID.

Source: Path instructions


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

The restoreAllMocks hook is redundant.

The cli Vitest project imports test/helpers/vitest-state-isolation.ts and enables restoreMocks, so spies created with vi.spyOn are restored between tests already. Keep suite teardown for resources Vitest does not manage, such as the temporary receipt directories that removeReceiptParents removes.

Based on learnings: rely on project-level isolation for Vitest spies and mocks; in suite-level teardown, only clean up resources Vitest does not manage.

🤖 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/docker-shared-state.test.ts` around lines 48
- 50, Remove the redundant afterEach hook that calls vi.restoreAllMocks in the
test suite, while retaining teardown for unmanaged resources such as temporary
receipt directories and removeReceiptParents cleanup.

Source: Learnings

src/lib/onboard/managed-startup/docker-shared-state.ts (2)

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

Validate the receipt path before the copy.

The bind-mount safety check runs at Lines 120-122, after docker cp completes. The check protects the --mount specification built at Line 163, so no unsafe path reaches Docker today. Moving the check to immediately after secureTempFile skips a pointless container copy and keeps the guard next to the value it validates.

♻️ Proposed reordering
   const receiptPath = secureTempFile(RECEIPT_TEMP_PREFIX);
+  if (receiptPath.includes(",") || /[\r\n\0]/u.test(receiptPath)) {
+    cleanupReceiptBestEffort(receiptPath);
+    throw new Error("Managed-startup rollback receipt path is unsafe for a Docker bind mount");
+  }
   try {
     const copy = dockerRun(
       [
         "cp",
         `${transaction.containerId}:${MANAGED_STARTUP_SHARED_TRANSACTION_DIRECTORY}`,
         receiptPath,
       ],
       DOCKER_MUTATION_OPTIONS,
     );
     if (!hasZeroDockerExitStatus(copy)) {
       throw new Error(
         `Could not copy the managed-startup rollback receipt from the failed container: ${commandDetail(copy)}`,
       );
     }
-    if (receiptPath.includes(",") || /[\r\n\0]/u.test(receiptPath)) {
-      throw new Error("Managed-startup rollback receipt path is unsafe for a Docker bind mount");
-    }
     return receiptPath;
🤖 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/docker-shared-state.ts` around lines 105 -
123, Move the receiptPath safety validation immediately after secureTempFile in
the rollback receipt flow, before invoking dockerRun for the copy. Keep the
existing unsafe-character conditions and error message unchanged, and retain the
subsequent copy-status handling and return behavior.

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

Extract the duplicated commandDetail helper.

docker-root-apply.ts Lines 60-71 defines the same helper with the same body and a different tail length. Two copies in one PR will drift. Move one implementation into a shared module and accept the tail length as a parameter.

🤖 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/docker-shared-state.ts` around lines 45 - 55,
Extract the duplicated commandDetail helper shared by docker-shared-state.ts and
docker-root-apply.ts into a common module, parameterizing the maximum tail
length so each caller preserves its existing limit. Update both call sites to
import and pass their respective lengths, then remove the local helper
definitions.
src/lib/onboard/managed-startup/docker-root-apply.ts (1)

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

Align the retry comment with the retry condition.

The comment states that the retry reconciles the lost-acknowledgement case only. The loop retries every non-zero exit status, including deterministic failures such as an invalid launch control or a profile identity mismatch. Those failures always repeat, so the second attempt adds one wasted 300 s exec window to the failure path.

The behavior is safe because beginManagedStartupSharedStateTransaction detects the pending manifest on the second attempt and preserves the original backups. Update the comment to state that all failures are retried once, or restrict the retry to the ambiguous statuses.

🤖 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/docker-root-apply.ts` around lines 167 - 198,
Update the retry comment above the attempt loop to accurately state that every
non-zero execution result is retried once, including deterministic failures; do
not change the existing retry behavior in the loop.
src/lib/onboard/managed-startup-image-runtime.test.ts (1)

568-635: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add a negative test for the root:root 0444 guard.

verifyManagedStartupImageCompletion rejects a completion marker or runtime handoff that is not root-owned mode 0444 (image-runtime.ts Lines 1186-1193 and 1202-1209). No test in this group exercises that rejection. A regression that drops the ownership or mode check still passes every test here.

Add one case that leaves the marker at mode 0644, or that reports a non-zero uid through mockDescriptorOwnership, and assert the "must be root:root mode 0444" failure.

🤖 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 568 -
635, Add a negative test in the managed startup completion tests covering the
root:root mode 0444 guard in verifyManagedStartupImageCompletion. Reuse
writeCompletionFixture and mockDescriptorOwnership, alter the completion marker
or runtime handoff to mode 0644 (or simulate non-zero ownership), and assert the
call throws the “must be root:root mode 0444” error.

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.

Inline comments:
In `@src/lib/onboard/managed-startup-image-runtime.test.ts`:
- Around line 485-490: Update the first-application assertion in the managed
startup image runtime test to require transactionPending: true, while retaining
transactionPending: false for the replay assertion so both state transitions are
covered.

In `@src/lib/onboard/managed-startup/docker-shared-state.ts`:
- Around line 257-263: Update the managed startup failure-recovery path around
quiesceManagedStartupContainer and rollbackManagedStartupSharedState so rollback
exceptions preserve and compose the original commit failure, matching the
receipt-preservation behavior at lines 226–235. Ensure either cleanup failure
reports both the cleanup error and the failure created from
commandDetail(commit), rather than replacing it.

In `@src/lib/onboard/managed-startup/image-runtime.ts`:
- Around line 1536-1539: Update the usage message in main to include the
supported --rollback-shared-state-transaction --agent &lt;agent&gt;
--read-only-receipt invocation alongside the existing transaction options,
matching the rollback form accepted by main and used by docker-shared-state.ts.

In `@src/lib/onboard/managed-startup/shared-state-transaction.ts`:
- Around line 625-640: Wrap the fs.lstatSync call in
requireTrustedTransactionPath with error handling that converts a missing
transaction artifact into the established managed-startup transaction failure,
preserving the original cause or actionable target context. Ensure partial
transaction directories missing backupDirectory or manifestFile produce the
wrapped failure consistently for begin, rollback, and commit callers while
leaving valid metadata validation unchanged.
- Around line 921-944: Update restoreDirectoryMetadata to handle non-empty
directories created during startup by tracking or safely removing
transaction-owned contents within the managed sandbox before directory removal,
while preserving boundary validation and rollback safety. Ensure
managedOutputTargets includes the manifest-declared agent/skills directory so
its contents are captured and restored. Add a regression test covering files
created under .deepagents/.state and .deepagents/skills, verifying rollback
completes and the transaction is no longer pending.

---

Nitpick comments:
In `@src/lib/onboard/managed-startup-image-runtime.test.ts`:
- Around line 568-635: Add a negative test in the managed startup completion
tests covering the root:root mode 0444 guard in
verifyManagedStartupImageCompletion. Reuse writeCompletionFixture and
mockDescriptorOwnership, alter the completion marker or runtime handoff to mode
0644 (or simulate non-zero ownership), and assert the call throws the “must be
root:root mode 0444” error.

In `@src/lib/onboard/managed-startup-shared-state-transaction.test.ts`:
- Around line 265-314: Add a test in the managed startup shared-state
transaction suite covering rollback with readOnlyReceipt: true, using an
appropriate seam or EROFS stub for the read-only mount probe. Create a
transaction with modified managed output, invoke
rollbackManagedStartupSharedStateTransaction with the read-only receipt option,
and assert the output is restored byte-for-byte while transactionDirectory
remains present. Exercise the production read-only receipt path, including its
identity/path validation and manifest handling.

In `@src/lib/onboard/managed-startup/docker-root-apply.test.ts`:
- Around line 244-281: Add two cases to the existing “rejects $label before root
exec” table for inspectExactContainer: one passing a non-JSON inspect string and
one passing a two-element inspect array. Assert each throws the corresponding
validation error and preserves the expectation that dockerSpawnSync is not
called.

In `@src/lib/onboard/managed-startup/docker-root-apply.ts`:
- Around line 167-198: Update the retry comment above the attempt loop to
accurately state that every non-zero execution result is retried once, including
deterministic failures; do not change the existing retry behavior in the loop.

In `@src/lib/onboard/managed-startup/docker-shared-state.test.ts`:
- Around line 213-287: Add a test alongside the existing
finalizeDockerManagedStartupSharedState cases that passes patchResult: null and
supervisorReady: false, provides a dockerRm mock, invokes
finalizeDockerManagedStartupSharedState, and asserts dockerRm is called with the
transaction’s container ID.
- Around line 48-50: Remove the redundant afterEach hook that calls
vi.restoreAllMocks in the test suite, while retaining teardown for unmanaged
resources such as temporary receipt directories and removeReceiptParents
cleanup.

In `@src/lib/onboard/managed-startup/docker-shared-state.ts`:
- Around line 105-123: Move the receiptPath safety validation immediately after
secureTempFile in the rollback receipt flow, before invoking dockerRun for the
copy. Keep the existing unsafe-character conditions and error message unchanged,
and retain the subsequent copy-status handling and return behavior.
- Around line 45-55: Extract the duplicated commandDetail helper shared by
docker-shared-state.ts and docker-root-apply.ts into a common module,
parameterizing the maximum tail length so each caller preserves its existing
limit. Update both call sites to import and pass their respective lengths, then
remove the local helper definitions.

In `@src/lib/onboard/managed-startup/image-runtime.ts`:
- Around line 1085-1104: Remove the redundant key-pattern validation loop over
unsetNames in the managed startup runtime handling. Keep the existing
validations for configurationEnvironment and
validatedApplicationRuntime.unsetEnvironment, the export/unset conflict check,
and the final sorted unsetNames result unchanged.

In `@src/lib/onboard/managed-startup/shared-state-transaction.ts`:
- Around line 448-483: Update atomicWriteTrustedFile to sync the parent
directory after fs.renameSync completes, using the existing parent path, so the
rename is durable before the function returns. Keep the current file fsync,
cleanup, and error-handling behavior 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: 42da9b04-c7f8-4039-bcf7-7813500ee21f

📥 Commits

Reviewing files that changed from the base of the PR and between 7eb0369 and 504b8b7.

📒 Files selected for processing (10)
  • src/lib/onboard/managed-startup-image-runtime.test.ts
  • src/lib/onboard/managed-startup-root-apply.test.ts
  • src/lib/onboard/managed-startup-shared-state-transaction.test.ts
  • src/lib/onboard/managed-startup/docker-root-apply.test.ts
  • src/lib/onboard/managed-startup/docker-root-apply.ts
  • src/lib/onboard/managed-startup/docker-shared-state.test.ts
  • src/lib/onboard/managed-startup/docker-shared-state.ts
  • src/lib/onboard/managed-startup/image-runtime.ts
  • src/lib/onboard/managed-startup/root-apply.ts
  • src/lib/onboard/managed-startup/shared-state-transaction.ts

Comment on lines 485 to +490
expect(first).toMatchObject({ adapterApplied: false, fingerprint });
expect(second).toMatchObject({ adapterApplied: false, fingerprint });
expect(second).toMatchObject({
adapterApplied: false,
fingerprint,
transactionPending: false,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert transactionPending: true on the first application.

Line 486 asserts transactionPending: false for the replay. Line 485 does not assert transactionPending for the first application. A regression that always returns false still passes this test. Add the positive assertion so the test pins both sides of the new field.

💚 Proposed assertion
-    expect(first).toMatchObject({ adapterApplied: false, fingerprint });
+    expect(first).toMatchObject({
+      adapterApplied: false,
+      fingerprint,
+      transactionPending: true,
+    });
📝 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
expect(first).toMatchObject({ adapterApplied: false, fingerprint });
expect(second).toMatchObject({ adapterApplied: false, fingerprint });
expect(second).toMatchObject({
adapterApplied: false,
fingerprint,
transactionPending: false,
});
expect(first).toMatchObject({
adapterApplied: false,
fingerprint,
transactionPending: true,
});
expect(second).toMatchObject({
adapterApplied: false,
fingerprint,
transactionPending: 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-image-runtime.test.ts` around lines 485 -
490, Update the first-application assertion in the managed startup image runtime
test to require transactionPending: true, while retaining transactionPending:
false for the replay assertion so both state transitions are covered.

Source: Path instructions

Comment on lines +257 to +263
const failure = new Error(
`OpenShell supervisor reconnected, but managed shared-state commit failed: ${commandDetail(commit)}`,
);
quiesceManagedStartupContainer(transaction, deps);
rollbackManagedStartupSharedState(transaction, receiptPath, deps);
if (!input.patchResult) removeFailedUnbackedContainer(transaction, deps);
return { supervisorReady: false, failure };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Preserve the original commit failure when rollback throws.

quiesceManagedStartupContainer at Line 260 and rollbackManagedStartupSharedState at Line 261 both throw on failure. When either throws, the failure Error built at Line 257 is discarded, so the operator sees only the rollback error and loses the commit failure detail that started the recovery.

The receipt-preservation path at Lines 226-235 already composes both messages. Apply the same composition here.

🔧 Proposed composition
     const failure = new Error(
       `OpenShell supervisor reconnected, but managed shared-state commit failed: ${commandDetail(commit)}`,
     );
-    quiesceManagedStartupContainer(transaction, deps);
-    rollbackManagedStartupSharedState(transaction, receiptPath, deps);
+    try {
+      quiesceManagedStartupContainer(transaction, deps);
+      rollbackManagedStartupSharedState(transaction, receiptPath, deps);
+    } catch (recoveryError) {
+      throw new Error(
+        `${failure.message}; shared-state recovery then failed: ${
+          recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
+        }`,
+      );
+    }
     if (!input.patchResult) removeFailedUnbackedContainer(transaction, deps);
📝 Committable suggestion

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

Suggested change
const failure = new Error(
`OpenShell supervisor reconnected, but managed shared-state commit failed: ${commandDetail(commit)}`,
);
quiesceManagedStartupContainer(transaction, deps);
rollbackManagedStartupSharedState(transaction, receiptPath, deps);
if (!input.patchResult) removeFailedUnbackedContainer(transaction, deps);
return { supervisorReady: false, failure };
const failure = new Error(
`OpenShell supervisor reconnected, but managed shared-state commit failed: ${commandDetail(commit)}`,
);
try {
quiesceManagedStartupContainer(transaction, deps);
rollbackManagedStartupSharedState(transaction, receiptPath, deps);
} catch (recoveryError) {
throw new Error(
`${failure.message}; shared-state recovery then failed: ${
recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
}`,
);
}
if (!input.patchResult) removeFailedUnbackedContainer(transaction, deps);
return { supervisorReady: false, failure };
🤖 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/docker-shared-state.ts` around lines 257 -
263, Update the managed startup failure-recovery path around
quiesceManagedStartupContainer and rollbackManagedStartupSharedState so rollback
exceptions preserve and compose the original commit failure, matching the
receipt-preservation behavior at lines 226–235. Ensure either cleanup failure
reports both the cleanup error and the failure created from
commandDetail(commit), rather than replacing it.

Comment on lines +1536 to +1539
fail(
"usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction] --agent <agent>",
);
}

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

Add the rollback form to the usage message.

main accepts --rollback-shared-state-transaction --agent <agent> --read-only-receipt at Lines 1596-1610, and docker-shared-state.ts invokes exactly that form. The usage message does not list it. An operator who mistypes the rollback invocation receives a usage message that hides the rollback path, which is the recovery path for a failed commit.

🔤 Proposed usage text
     fail(
-      "usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction] --agent <agent>",
+      "usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction|--rollback-shared-state-transaction] --agent <agent> [--profile-fingerprint <sha256>] [--read-only-receipt]",
     );
📝 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
fail(
"usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction] --agent <agent>",
);
}
fail(
"usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction|--rollback-shared-state-transaction] --agent <agent> [--profile-fingerprint <sha256>] [--read-only-receipt]",
);
}
🤖 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 1536 - 1539,
Update the usage message in main to include the supported
--rollback-shared-state-transaction --agent &lt;agent&gt; --read-only-receipt
invocation alongside the existing transaction options, matching the rollback
form accepted by main and used by docker-shared-state.ts.

Comment on lines +625 to +640
function requireTrustedTransactionPath(
target: string,
mode: number,
options: ResolvedOptions,
): void {
const stat = fs.lstatSync(target);
if (
stat.isSymbolicLink() ||
(mode === TRANSACTION_DIRECTORY_MODE ? !stat.isDirectory() : !stat.isFile()) ||
(!options.readOnlyReceipt &&
(stat.uid !== options.trustedUid || stat.gid !== options.trustedGid)) ||
modeOf(stat) !== mode
) {
fail(`transaction artifact has unsafe metadata: ${target}`);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Wrap the lstatSync call so a partial transaction directory reports an actionable failure.

requireTrustedTransactionPath calls fs.lstatSync(target) without a guard. loadManifest checks only that transactionDirectory exists. If a previous begin was interrupted after mkdirSync and its cleanup did not run, backupDirectory or manifestFile is missing. Every later begin, rollback, and commit call then throws a bare ENOENT: no such file or directory instead of the wrapped Managed startup shared-state transaction failed: ... message. Callers that classify errors by code === "ENOENT" can also misread that error.

🛡️ Proposed fix
 function requireTrustedTransactionPath(
   target: string,
   mode: number,
   options: ResolvedOptions,
 ): void {
-  const stat = fs.lstatSync(target);
+  let stat: fs.Stats;
+  try {
+    stat = fs.lstatSync(target);
+  } catch {
+    fail(`transaction artifact is missing or unreadable: ${target}`);
+  }
   if (

Also applies to: 663-669

🤖 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/shared-state-transaction.ts` around lines 625
- 640, Wrap the fs.lstatSync call in requireTrustedTransactionPath with error
handling that converts a missing transaction artifact into the established
managed-startup transaction failure, preserving the original cause or actionable
target context. Ensure partial transaction directories missing backupDirectory
or manifestFile produce the wrapped failure consistently for begin, rollback,
and commit callers while leaving valid metadata validation unchanged.

Comment on lines +921 to +944
function restoreDirectoryMetadata(
receipts: readonly DirectoryReceipt[],
options: ResolvedOptions,
): void {
for (const receipt of [...receipts].reverse()) {
const target = absoluteTarget(receipt.path, options);
if (receipt.state === "absent") {
try {
fs.rmdirSync(target);
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
fail(`could not remove newly created managed directory ${target}`);
}
continue;
}
if (directoryMatchesReceipt(target, receipt)) continue;
const stat = fs.lstatSync(target);
if (stat.isSymbolicLink() || !stat.isDirectory()) {
fail(`managed directory changed type during restore: ${target}`);
}
fs.chownSync(target, receipt.uid, receipt.gid);
fs.chmodSync(target, receipt.mode);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find writers into the deepagents state/skills directories and the managed output enumeration.
set -euo pipefail

rg -n -C4 --iglob '!**/node_modules/**' '\.deepagents|deepagents/skills|"skills"|"\.state"' src scripts | head -100

echo '--- managedOutputTargets enumeration ---'
ast-grep run --pattern 'function managedOutputTargets($$$) { $$$ }' --lang typescript src/lib/onboard/managed-startup/shared-state-transaction.ts

Repository: NVIDIA/NemoClaw

Length of output: 7522


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- file map ---'
ast-grep outline src/lib/onboard/managed-startup/shared-state-transaction.ts

echo '--- target and restore symbols ---'
rg -n -C8 'managedOutputTargets|restoreDirectoryMetadata|restoreFiles|verifyRestoration|DirectoryReceipt|langchain-deepagents|\.state|skills' \
  src/lib/onboard/managed-startup/shared-state-transaction.ts src/lib src scripts

echo '--- related path and writer symbols ---'
rg -n -C5 'deepagents|stateDir|skills|mkdirSync|writeFileSync|write.*state|write.*skill|installFreshSharedSkill' \
  src/lib src scripts | head -300

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- file map ---'
ast-grep outline src/lib/onboard/managed-startup/shared-state-transaction.ts

echo '--- target and restore symbols ---'
rg -n -C8 'managedOutputTargets|restoreDirectoryMetadata|restoreFiles|verifyRestoration|DirectoryReceipt|langchain-deepagents|\.state|skills' \
  src/lib/onboard/managed-startup/shared-state-transaction.ts src/lib scripts

echo '--- related path and writer symbols ---'
rg -n -C5 'deepagents|stateDir|skills|mkdirSync|writeFileSync|write.*state|write.*skill|installFreshSharedSkill' \
  src/lib src scripts | head -300

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- transaction symbols ---'
rg -n '^(export )?(async )?function |^(export )?const |managedOutputTargets|restoreDirectoryMetadata|restoreFiles|verifyRestoration|DirectoryReceipt' \
  src/lib/onboard/managed-startup/shared-state-transaction.ts

echo '--- transaction implementation ---'
sed -n '1,220p' src/lib/onboard/managed-startup/shared-state-transaction.ts
sed -n '820,1010p' src/lib/onboard/managed-startup/shared-state-transaction.ts

echo '--- exact deepagents references outside tests ---'
rg -n -C3 --glob '*.ts' --glob '!*.test.ts' \
  'langchain-deepagents-code|\.deepagents|/skills|"\.state"|stateDirs|managedOutputTargets' src/lib src scripts

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- managed output construction ---'
sed -n '285,380p' src/lib/onboard/managed-startup/shared-state-transaction.ts
sed -n '750,855p' src/lib/onboard/managed-startup/shared-state-transaction.ts

echo '--- managed startup profile target declarations ---'
rg -n -C12 'selectEnabledPostAgentInstallBuildFiles|managed.*target|state.*dir|config\.toml|skills|\.state' \
  src/lib/onboard/managed-startup src/lib/messaging src/lib/agent/defs.ts

echo '--- all non-test writers to the relevant paths ---'
rg -n -C5 --glob '*.ts' --glob '!*.test.ts' \
  'agent/skills|/skills/|\.state|state/|writeFileSync|appendFileSync|mkdirSync|rmSync|rmdirSync' \
  src/lib/onboard src/lib/skill-install.ts src/lib/agent

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- managedOutputTargets and callers ---'
sed -n '285,355p' src/lib/onboard/managed-startup/shared-state-transaction.ts
sed -n '750,785p' src/lib/onboard/managed-startup/shared-state-transaction.ts
rg -n -C8 'beginManagedStartupSharedStateTransaction|rollbackManagedStartupSharedStateTransaction|commitManagedStartupSharedStateTransaction' \
  src/lib/onboard src/lib/onboard/managed-startup --glob '*.ts'

echo '--- transaction tests involving directories and rollback ---'
rg -n -C10 'rollback|directory|rmdir|ENOTEMPTY|\.state|skills|managed startup shared state' \
  src/lib/onboard/managed-startup --glob '*.test.ts'

echo '--- relevant agent manifest and skill-install paths ---'
rg -n -C10 'name:.*langchain-deepagents-code|state_dirs|\.state|agent/skills|skills' \
  agents src/lib/agent src/lib/skill-install.ts --glob '!*.test.ts' 2>/dev/null | head -240

Repository: NVIDIA/NemoClaw

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- Deep Agents Code definitions ---'
git ls-files | rg '(^|/)(agents?|plugins?|.*deepagents.*)' | head -120

echo '--- Deep Agents Code state declarations ---'
rg -n -C12 --glob '*.json' --glob '*.yaml' --glob '*.yml' --glob '*.toml' --glob '*.ts' \
  'langchain-deepagents-code|state_dirs|\.state|agent/skills|skills' . | head -260

echo '--- apply path writes and commands ---'
rg -n -C8 'applyManagedStartupImageProfile|applyManagedStartupProfile|config\.toml|\.state|agent/skills|mkdir.*skills|mkdir.*state|write.*skills' \
  src/lib/onboard/managed-startup src/lib/agent src/lib/skill-install.ts scripts --glob '!*.test.ts' | head -320

echo '--- rollback test file around Deep Agents cases ---'
sed -n '50,130p' src/lib/onboard/managed-startup-shared-state-transaction.test.ts

Repository: NVIDIA/NemoClaw

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- Deep Agents Code manifest ---'
cat agents/langchain-deepagents-code/manifest.yaml

echo '--- Deep Agents Code startup/config writers ---'
rg -n -C8 --glob '!*.test.*' \
  'write|mkdir|state|skills|config\.toml|\.deepagents|agent/skills|\.state' \
  agents/langchain-deepagents-code/{start.sh,generate-config.ts,dcode-launcher.sh,dcode-wrapper.sh,managed-dcode-runtime.py,patch-managed-deepagents-code.py,manifest.yaml}

echo '--- all repository references to the exact managed directories ---'
rg -n -C5 --glob '!*.test.*' \
  '"/sandbox/\.deepagents/(?:\.state|skills)|\.deepagents/(?:\.state|skills)|/\.deepagents/(?:\.state|skills)|root/skills|root/\.state' \
  src agents scripts test docs

Repository: NVIDIA/NemoClaw

Length of output: 50372


Make rollback handle non-empty newly created managed directories.

restoreFiles does not track files inside .deepagents/.state or .deepagents/skills. If startup creates such a file, fs.rmdirSync returns ENOTEMPTY, so rollback fails before verifyRestoration and leaves the transaction pending. The manifest also declares agent/skills, but managedOutputTargets does not record it. Track and restore directory contents, or remove only transaction-owned contents after validating the sandbox boundary. Add a regression test with files in these directories.

🤖 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/shared-state-transaction.ts` around lines 921
- 944, Update restoreDirectoryMetadata to handle non-empty directories created
during startup by tracking or safely removing transaction-owned contents within
the managed sandbox before directory removal, while preserving boundary
validation and rollback safety. Ensure managedOutputTargets includes the
manifest-declared agent/skills directory so its contents are captured and
restored. Add a regression test covering files created under .deepagents/.state
and .deepagents/skills, verifying rollback completes and the transaction is no
longer pending.

@jyaunches
jyaunches merged commit 1e57000 into main Jul 31, 2026
43 checks passed
@jyaunches
jyaunches deleted the feat/buildless-shared-state-transactions-v2 branch July 31, 2026 18:27
@senthilr-nv senthilr-nv mentioned this pull request Aug 1, 2026
23 tasks
senthilr-nv added a commit that referenced this pull request Aug 1, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Adds the canonical dated changelog entry for `v0.0.100` so the
maintainer release plan can verify the pre-tag documentation
prerequisite. The entry summarizes the user-facing changes merged since
`v0.0.99` and links to the relevant guides.

## Changes

- Add `docs/changelog/2026-07-31.mdx` with the exact `## v0.0.100`
heading.
- Cover restored OpenClaw pairing, transactional replacement, Deep
Agents Code, onboarding recovery, lifecycle cleanup, Hermes builds, host
provenance, documentation, and trusted E2E evidence.
- Distinguish active Docker and Kubernetes runtime-bundle enforcement
from the still-inactive managed shared-state transaction foundation.

## Source Coverage

The release entry maps the doc-impacting merged PRs in the
`v0.0.99..main` release range to `docs/changelog/2026-07-31.mdx`: #8021,
#8024, #7973, #8028, #7947, #7788, #7884, #8023, #7969, #8020, #7989,
#8000, #7907, #7942, #7567, #8013, #7955, #8017, #8014, #8015, #7629,
#7644, #7821, #7971, and #7991.

PR #7974 was reviewed after the final rebase and excluded because it
changes internal maintainer-skill attribution policy and tests only; it
does not change a user-facing product or documentation surface.

## 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: the
changelog contract test validates the dated entry, version heading, SPDX
form, and route constraints.
- [ ] 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-31.mdx`; exact-head review passed
for `6093f44f`; writing rules and documentation style reviewed; `npx
vitest run test/changelog-docs.test.ts` passed 6/6; `npm run docs`
passed with zero Fern errors and two generic Fern upgrade notices.
- Agent: Codex Desktop
<!-- docs-review-head-sha: 6093f44 -->
<!-- docs-review-agents-blob-sha: 3dd7c24 -->

## DGX Station Hardware Evidence

- [ ] Tested on DGX Station
- Tested commit: Not applicable; no DGX Station host script changed.
- Station profile/scenario: Not applicable.
- Result: Not applicable.
- Supporting evidence: Not applicable.

## 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 at `6093f44f`.
- [ ] 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 a dated
prose-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) —
validation passed with zero errors; Fern emitted two generic upgrade
notices.
- [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)
— the changelog entry has the required parser-safe MDX SPDX header;
dated changelog entries intentionally do not use page frontmatter.

---

Signed-off-by: Senthil Ravichandran <senthilr@nvidia.com>


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

## Summary by CodeRabbit

* **Documentation**
  * Added release notes for v0.0.100.
* Documented improvements to restore pairing, sandbox replacement,
onboarding recovery, lifecycle cleanup, runtime handling, build support,
host readiness, and end-to-end validation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Senthil Ravichandran <senthilr@nvidia.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.

2 participants