Skip to content

feat(runtime): add dormant Podman managed bootstrap transaction - #8052

Merged
prekshivyas merged 230 commits into
feat/podman-native-cpu-proof-v2from
feat/podman-bootstrap-authority
Aug 5, 2026
Merged

feat(runtime): add dormant Podman managed bootstrap transaction#8052
prekshivyas merged 230 commits into
feat/podman-native-cpu-proof-v2from
feat/podman-bootstrap-authority

Conversation

@ericksoa

@ericksoa ericksoa commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the dormant, provider-owned Podman managed-bootstrap transaction as one additive batch: exact held-workload and watcher ownership, durable journaled replacement and rollback, and image-owned startup application for OpenClaw, Hermes, and Deep Agents Code. The provider remains absent from production selection, so this PR does not activate or advertise Podman support.

Stacked on #8276. Consolidates the source material preserved from #8052, #8055, and #8056. Part of #7744.

Related Issue

Part of #7744.

Changes

  • Adds the managed-bootstrap operation scope to the driver-neutral container-engine boundary.
  • Discovers and stably re-inspects one exact held Podman workload, then durably leases the exact watcher process with PID-reuse and competing-owner protection.
  • Journals engine authority, immutable original and replacement identities, image/specification fingerprints, state volume, watcher lease, and every monotonic mutation boundary.
  • Prepares a stopped final-labelled replacement while retaining the original; pre-commit rollback removes only the proven replacement and owned state volume and restores the exact original.
  • Stages one protected root-apply request into the exact replacement, starts it, and authenticates image-owned completion for OpenClaw, Hermes, and LangChain Deep Agents Code.
  • Derives the provider's agent type from the neutral root-apply request and uses an exhaustive provider-owned all-agent map, avoiding a Podman switch or new dependency in central orchestration.
  • Adds internal lifecycle documentation and source-boundary coverage. Production registry selection, persisted post-commit recovery, GPU/local inference, installer qualification, and supported activation remain later [Epic] Support native Podman with buildless managed onboarding #7744 gates.

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: documentation changes describe an internal dormant lifecycle contract; no command, option, default, or supported runtime is exposed.
  • 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: the implementation binds every mutation to exact engine, process, runtime, volume, image, and journal authority; ambiguity and drift fail closed.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer reviewed the completed changes
  • Result: no-public-docs-needed
  • Evidence: src/lib/onboard/managed-bootstrap/README.md and src/lib/onboard/lifecycle-contracts.md record the internal dormant contract; public documentation would incorrectly imply supported Podman activation.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit: Not applicable to this dormant CPU/bootstrap transaction slice.
  • Station profile/scenario: Not applicable.
  • Result: Not applicable.
  • Supporting evidence: Native rootless Podman qualification remains workflow-owned and the parent feat(runtime): add dormant Podman CPU lifecycle proof #8276 exact-head CPU proof is green.

Verification

  • 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 the target branch when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — 79/79 focused managed-bootstrap tests, 8/8 source-boundary tests, and 3/3 Podman workflow-contract tests pass on exact head 085adaee04b7; all four exact-head CodeRabbit threads are fixed and resolved.
  • Applicable broad local gate passed — repository architecture, CLI and plugin typecheck, source-shape, test-size, conditional scan, formatting, and exact-delta secret scanning pass.
  • 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) — not applicable; no user-facing docs changed.
  • Doc pages follow the style guide (doc changes only) — not applicable.
  • New doc pages include SPDX header and frontmatter (new pages only) — not applicable.

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

Summary by CodeRabbit

  • New Features

    • Added managed-image onboarding and runtime orchestration for faster sandbox creation without legacy image builds.
    • Added Docker managed-bootstrap persistence and recovery support.
    • Added sandbox lifecycle support for qualified rootless Podman environments.
  • Bug Fixes

    • Rebuilds now stop safely when workload configuration or runtime authority changes.
    • Improved preservation of managed images during sandbox recreation.
  • Tests

    • Expanded end-to-end coverage for managed-image onboarding, rebuild safety, Docker, and Podman lifecycle behavior.

ericksoa added 30 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>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
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>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@github-actions github-actions Bot added v0.0.102 and removed v0.0.101 labels Aug 3, 2026
@apurvvkumaria apurvvkumaria self-assigned this Aug 3, 2026
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>

@apurvvkumaria apurvvkumaria left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Security review: approved

Reviewed the current branch revision across the repository’s nine security categories.

  • Trust and authorization: the observed OpenShell namespace must now match the caller-provided namespace before the workload can be treated as owned.
  • Input validation: empty, malformed, oversized, and mismatched ownership values fail closed.
  • Injection and command execution: no command construction or execution boundary changed.
  • Secrets and sensitive output: no credential handling or diagnostic output changed.
  • Concurrency and lifecycle safety: the existing double-inspection and immutable runtime identity checks remain intact.
  • Error handling: namespace failures stop before mutation and use the existing bounded ownership diagnostic.
  • Dependency and supply chain: no dependency or lockfile change.
  • Regression coverage: 13 focused tests pass, including empty-input and cross-namespace rejection; CLI build and typecheck pass.
  • Documentation: an independent writer confirmed no user-facing docs are needed because this path remains internal and unsupported.

No blocking findings remain.

Base automatically changed from feat/podman-command-adapter to feat/managed-image-publication August 3, 2026 23:41
@apurvvkumaria apurvvkumaria removed their assignment Aug 4, 2026
Base automatically changed from feat/managed-image-publication to main August 4, 2026 08:37
cv
cv previously requested changes Aug 4, 2026

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed commit 14d8f522f as the root of the runtime stack. The PR description defines a dormant watcher-lease and held-workload authority slice, but the effective diff contains about 100 files and 13,000 additions across managed images, bootstrap adapters, snapshots, clone restore, Hermes brokering, messaging, inference, and other runtime work. That effective scope does not match the approved slice or its recorded tests and documentation review. The branch also conflicts with current main, and image, audit, CI, and E2E checks fail.

Reconstruct this root from current main with only the container-engine boundary, Podman adapter/socket authority, held-workload inspection, watcher lease, lifecycle contract, and directly protecting tests required by this slice. Do not carry descendant runtime, snapshot, broker, image-publication, or messaging changes into the root. Because the root must change before descendants can be reviewed reliably, the remaining stack stays blocked until this reconstruction passes its gates.

@github-actions github-actions Bot added v0.0.103 Release target and removed v0.0.102 labels Aug 4, 2026
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 5, 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.

@ericksoa
ericksoa changed the base branch from main to feat/podman-native-cpu-proof-v2 August 5, 2026 04:37
@ericksoa ericksoa changed the title feat(runtime): add durable Podman bootstrap authority feat(runtime): add dormant Podman managed bootstrap transaction Aug 5, 2026

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

🧹 Nitpick comments (17)
src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.ts (1)

602-607: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Verify the durable re-read after the rollback authorization write.

recordStateVolume, recordReplacement, and recordOriginalStopped each reload the journal and compare it with sameJournal before they return. authorizeRollback returns the in-memory updated object without that proof. The durable outcome is still pinned by the exclusive decision file, so this is a consistency gap rather than a correctness hole. Align the most safety-critical transition with its siblings.

♻️ Proposed durable re-read
       atomicWrite(directory, target, serializePodmanBootstrapJournal(updated), false);
-      return updated;
+      const persisted = load(bootstrapIdentity);
+      if (!persisted || !sameJournal(persisted, updated)) {
+        fail("rollback authorization was not durably re-readable");
+      }
+      return persisted;
🤖 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-bootstrap/podman-bootstrap-journal.ts` around lines
602 - 607, Update authorizeRollback to re-read the journal after atomicWrite and
verify the persisted result with sameJournal before returning. Return the
durable re-read only after that comparison, matching the validation behavior of
recordStateVolume, recordReplacement, and recordOriginalStopped.
src/lib/onboard/managed-bootstrap/docker-runtime.test.ts (1)

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

Move the temporary state-root cleanup into afterEach.

fs.rmSync(stateRoot, ...) runs at the end of the test body. If an earlier assertion in the same test fails, the removal never runs and the temporary directory leaks. Track the root and remove it in a teardown hook, as podman-bootstrap-journal.test.ts does with its roots array.

♻️ Proposed cleanup handling
+const roots: string[] = [];
+
+afterEach(() => {
+  for (const root of roots.splice(0)) fs.rmSync(root, { force: true, recursive: true });
+});
-    const stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-docker-runtime-"));
+    const stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-docker-runtime-"));
+    roots.push(stateRoot);
     await expect(lifecycle.recoverUnfinished()).resolves.toEqual({ receipts: [], failures: [] });
-    fs.rmSync(stateRoot, { recursive: true, force: true });

Based on learnings: in suite-level teardown hooks, clean up resources Vitest does not manage, for example temporary directories and files.

Also applies to: 109-110

🤖 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-bootstrap/docker-runtime.test.ts` at line 36, Move
temporary state-root cleanup out of the test body and into an afterEach
teardown. Track each directory created by the tests around stateRoot, then
remove all tracked roots with fs.rmSync using the existing cleanup options,
following the roots-array pattern in podman-bootstrap-journal.test.ts.

Source: Learnings

src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.test.ts (1)

108-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for two fail-closed guards on destructive paths.

Two guards in the store have no test:

  • create rejects a stale rollback decision (podman-bootstrap-journal.ts Lines 463-465).
  • removeAfterRollback rejects any phase other than rollback-authorized (podman-bootstrap-journal.ts Lines 613-615).

Both guards protect journal removal and journal creation. Add one test for each so a later change cannot weaken them silently.

Also applies to: 157-172

🤖 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-bootstrap/podman-bootstrap-journal.test.ts` around
lines 108 - 122, Add two tests in the journal store test suite: verify `create`
rejects a stale rollback decision, and verify `removeAfterRollback` rejects
every phase other than `rollback-authorized`. Assert each operation fails closed
and preserves the journal state, covering the guards in `create` and
`removeAfterRollback` without changing existing behavior.
src/lib/onboard/managed-bootstrap/podman-held-workload.test.ts (2)

259-267: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the image-drift test title with the guard it exercises.

The second inspect returns OTHER_IMAGE_ID, which fails the expected-image check inside parseObservation at podman-held-workload.ts Line 249. The stable-capture comparison at Line 304 never runs. The test proves drift rejection, but it does not prove the stable-capture guard that the title claims.

To exercise the stable-capture comparison, vary a field that both inspections accept individually. For example, drop expectedImageContentId from the input so the per-inspection image check is skipped and only the two-observation comparison can fail.

🤖 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-bootstrap/podman-held-workload.test.ts` around lines
259 - 267, Update the test around inspect to exercise the stable-capture
comparison rather than parseObservation’s expected-image check: remove
expectedImageContentId from the input used by inspect(fake.engine), while
keeping the differing second inspection output. Preserve the assertion that the
operation rejects with “image content changed.”

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

Fail loudly when the capture queue is exhausted.

queue.shift() as ContainerEngineCommandResult returns undefined after the queued outputs run out. Several tests supply fewer outputs than the maximum call count on purpose. If the production code later makes one extra call, the test fails with a TypeError on result.status instead of a clear message. Throw an explicit error instead.

♻️ Proposed guard
-  const capture = vi.fn(() => queue.shift() as ContainerEngineCommandResult);
+  const capture = vi.fn(() => {
+    const next = queue.shift();
+    if (!next) throw new Error("Podman engine capture was called more times than the test queued.");
+    return next;
+  });
🤖 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-bootstrap/podman-held-workload.test.ts` around lines
78 - 94, Update the test helper function engineWith so its capture mock checks
the result of queue.shift() and throws an explicit, descriptive error when the
output queue is exhausted; otherwise return the queued
ContainerEngineCommandResult unchanged.
src/lib/onboard/managed-bootstrap/podman-held-workload.ts (1)

150-175: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Scope the discovery filter to the sandbox namespace.

discoverRuntimeId filters on openshell.managed and openshell.sandbox-name only. The namespace is validated at Line 287 and checked against the label at Line 214, after discovery. On a host that runs the same sandbox name in two namespaces, the list returns two entries and the call fails with "found 2" instead of resolving the correct workload. Add the namespace filter so discovery selects one workload, and keep the post-inspect label check as the authority.

♻️ Proposed filter
 function discoverRuntimeId(engine: ContainerEngine, sandboxName: string): string {
+  // The caller-provided namespace is validated before this call.
   const output = capture(
     engine,
     [
       "container",
       "ls",
       "--all",
       "--no-trunc",
       "--filter",
       `label=${PODMAN_MANAGED_LABEL}=true`,
       "--filter",
       `label=${PODMAN_SANDBOX_NAME_LABEL}=${sandboxName}`,
+      "--filter",
+      `label=${PODMAN_SANDBOX_NAMESPACE_LABEL}=${sandboxNamespace}`,
       "--format",
       "json",
     ],

This change requires passing sandboxNamespace into discoverRuntimeId from Line 291.

🤖 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-bootstrap/podman-held-workload.ts` around lines 150 -
175, Update discoverRuntimeId to accept sandboxNamespace and add the
corresponding namespace label filter to the Podman container listing arguments
alongside the existing managed and sandbox-name filters. Update its call site in
the surrounding bootstrap flow to pass sandboxNamespace, while preserving the
post-inspect namespace label check as the authoritative validation.
src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.ts (1)

44-61: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Incomplete Container Privilege Flag Denylist (CWE-250)

Exploitability: Difficult

Align FORBIDDEN_RUNTIME_FLAGS with its security contract.

runtimeArgs is passed directly to container create, but the denylist rejects --privileged while allowing --cap-add, --security-opt, --device, --userns, and --pid. Reject these privilege-affecting flags, or document why they are allowed.

🤖 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-bootstrap/podman-bootstrap-replacement.ts` around
lines 44 - 61, Update FORBIDDEN_RUNTIME_FLAGS to reject the privilege-affecting
runtime options --cap-add, --security-opt, --device, --userns, and --pid
alongside --privileged, ensuring runtimeArgs cannot bypass the intended
container security restrictions.
src/lib/actions/sandbox/rebuild-dcode-orchestrator.ts (1)

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

Remove the non-null assertion by narrowing on replacement first.

Line 276 uses replacement!. The assertion holds today because of the early return at Line 245. TypeScript cannot prove it. A future edit to the Line 245 condition can turn this into a runtime undefined passed to revalidateDcodeReplacementAtMutationEdge. Branch on managedWorkloadRebuild first so the compiler narrows replacement.

♻️ Proposed narrowing without the assertion
-      const valid = await (managedWorkloadRebuild
-        ? revalidateManagedDcodeWorkloadAtMutationEdge({
-            sandboxName,
-            entry,
-            resumeConfig,
-            toolDisclosure,
-            dcodeAutoApprovalMode,
-            skipLiveRoute,
-            gatewayPort,
-            log,
-            bail: capturedBail,
-            checkGatewaySchema: () => deps.checkGatewaySchema(sandboxName, capturedBail),
-          })
-        : revalidateDcodeReplacementAtMutationEdge({
-            sandboxName,
-            entry,
-            resumeConfig,
-            toolDisclosure,
-            dcodeAutoApprovalMode,
-            skipLiveRoute,
-            gatewayPort,
-            log,
-            bail: capturedBail,
-            checkGatewaySchema: () => deps.checkGatewaySchema(sandboxName, capturedBail),
-            replacement: replacement!,
-          }));
+      const sharedInput = {
+        sandboxName,
+        entry,
+        resumeConfig,
+        toolDisclosure,
+        dcodeAutoApprovalMode,
+        skipLiveRoute,
+        gatewayPort,
+        log,
+        bail: capturedBail,
+        checkGatewaySchema: () => deps.checkGatewaySchema(sandboxName, capturedBail),
+      };
+      const valid = managedWorkloadRebuild
+        ? await revalidateManagedDcodeWorkloadAtMutationEdge(sharedInput)
+        : replacement
+          ? await revalidateDcodeReplacementAtMutationEdge({ ...sharedInput, replacement })
+          : 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/actions/sandbox/rebuild-dcode-orchestrator.ts` around lines 244 -
277, In the validation try block, branch on managedWorkloadRebuild before
selecting the revalidation call so the non-managed path narrows replacement
after the existing preflight guard. Update
revalidateDcodeReplacementAtMutationEdge invocation to pass the narrowed
replacement directly, and remove the replacement! assertion without changing
either validation flow.
src/lib/onboard/runtime-provider/podman.test.ts (1)

195-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Do not assert the exact key order of the global provider registry.

Line 196 pins Object.keys(CURRENT_RUNTIME_PROVIDER_BUNDLES) to ["docker", "kubernetes"], including order. Adding an unrelated provider or reordering the declarations breaks this Podman test for a reason unrelated to Podman dormancy. Line 197 already states the actual claim.

Keep line 197 and drop line 196, or relax it to a membership check.

♻️ Proposed change
   it("stays outside the production-selectable registry", () => {
-    expect(Object.keys(CURRENT_RUNTIME_PROVIDER_BUNDLES)).toEqual(["docker", "kubernetes"]);
     expect(CURRENT_RUNTIME_PROVIDER_BUNDLES).not.toHaveProperty("podman");
   });

As per path instructions: prefer "observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions".

🤖 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/runtime-provider/podman.test.ts` around lines 195 - 198,
Remove the exact Object.keys ordering assertion from the test “stays outside the
production-selectable registry”; retain the
CURRENT_RUNTIME_PROVIDER_BUNDLES.not.toHaveProperty("podman") assertion, which
directly verifies Podman remains unavailable.

Source: Path instructions

src/lib/adapters/podman/index.test.ts (1)

47-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover every authority field in the identity test.

The test proves only that inode changes authorityId. podmanAuthorityId builds its canonical object field by field, so a dropped field would silently give two different sockets the same identity. podman.ts compares authorityId to prove that two engines bind the same endpoint, so a collision would defeat that check.

Extend the test with it.each over socketPath, device, mode, ownerUid, and a non-empty directoryChain so each field is proven to change the digest.

As per path instructions: "Prefer observable outcomes through the public boundary" and flag tests whose claim is broader than what they exercise.

🤖 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/adapters/podman/index.test.ts` around lines 47 - 62, Expand the
identity coverage in the “gives different socket authorities different opaque
identities” test by using it.each for socketPath, device, mode, ownerUid, and a
non-empty directoryChain, varying one field at a time from AUTHORITY and
asserting authorityId changes. Preserve the existing inode case or include it in
the same table so every field used by podmanAuthorityId is verified through the
public createPodmanContainerEngine boundary.

Source: Path instructions

src/lib/actions/sandbox/rebuild-managed-workload-mutation-guard.test.ts (1)

60-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Retitle and split this test; the title does not describe what it asserts.

The title states "before deletion", but the body never calls revalidateManagedWorkloadRebuildBeforeDelete. It calls prepareManagedRebuildProfileHandoff and asserts profile staging. The test also asserts two separate behaviors: successful reasoning-authority staging (lines 88-117) and the unsupported-API error (lines 129-142).

Split it into two tests with titles that name the observed behavior, for example "stages OpenClaw reasoning authority for a compatible-endpoint target" and "rejects an unsupported managed startup inference API".

The suite is named for the mutation guard. Consider moving the profile-staging tests next to agents/managed-workload-rebuild-profile.ts so each file covers one unit.

As per path instructions: "Prefer observable outcomes through the public boundary" and "conditionals that make a test pass without exercising its claim".

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

In `@src/lib/actions/sandbox/rebuild-managed-workload-mutation-guard.test.ts`
around lines 60 - 143, Split the current test into two tests: rename the
successful case to describe staging OpenClaw reasoning authority for a
compatible-endpoint target, and isolate the unsupported managed startup
inference API assertion in a test titled for that rejection. Keep each test
focused on its observed prepareManagedRebuildProfileHandoff behavior, and move
these profile-staging tests alongside agents/managed-workload-rebuild-profile.ts
if that is the established unit-test location rather than the mutation-guard
suite.

Sources: Coding guidelines, Path instructions

src/lib/onboard/runtime-provider/podman.ts (1)

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

Link the deferred slices to a GitHub issue.

The doc comment names the deferred slices and deferred carries them into four unsupported surfaces. The path instruction for src/** requires the remaining work to be linked in GitHub when a PR migrates only a slice. Add the tracking issue reference to this comment so the exit criteria for the dormant provider live in GitHub rather than only in code prose.

As per path instructions: "If a PR intentionally migrates only a slice, it must say so and link the remaining work in GitHub."

🤖 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/runtime-provider/podman.ts` around lines 65 - 82, Add the
GitHub tracking issue reference to the doc comment above
createPodmanRuntimeProviderBundle, explicitly linking the deferred managed
startup, recovery, GPU, local inference, installer, and protected E2E
qualification work. Keep the existing scope description and deferred runtime
behavior unchanged.

Source: Path instructions

src/lib/onboard/managed-workload/onboard-orchestration.ts (1)

402-409: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the managed-image contract constants next to the image contract.

Lines 406 and 408 hardcode uid: 1000, gid: 1000, workdir: "/sandbox", and expectedSupervisorArgv: ["/opt/openshell/bin/openshell-sandbox"] inside an orchestration module. These values describe the managed image, not the onboarding sequence.

input.workload.source.contract already carries capabilityContractVersion and startupProfileContractVersion. If the managed image changes its agent uid, gid, workdir, or supervisor path, this literal becomes a second source of truth that drifts silently from the image.

Export these constants from the managed-image contract module and read them here.

#!/bin/bash
# Description: Find where the managed-image contract defines agent identity and supervisor path.
set -euo pipefail

rg -nP 'openshell-sandbox|/sandbox\b|uid:\s*1000|gid:\s*1000' -C 4 --type=ts -g '!**/dist/**' | head -80
rg -nP 'capabilityContractVersion' -l --type=ts -g '!**/dist/**'
🤖 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-workload/onboard-orchestration.ts` around lines 402 -
409, Move the managed-image values currently hardcoded in the orchestration
object—agent uid, gid, workdir, and supervisor argv—into the managed-image
contract module alongside the existing image contract constants, and export
them. Update the orchestration code constructing the managed workload contract
to import and reuse those exported constants instead of defining duplicate
literals, while preserving the existing contract fields and argv structure.
src/lib/onboard/runtime-provider/podman-lifecycle.test.ts (1)

133-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the identity and label guards.

This suite proves ambiguity, unknown state, engine scope, and retry convergence. Three fail-closed guards in podman-lifecycle.ts have no test:

  • Line 103: the inspected Id differs from the ID pinned by ps ("identity changed after it was pinned").
  • Lines 112-119: openshell.managed is not "true", or openshell.sandbox-name does not equal the requested sandbox name.
  • Lines 92-100: inspect returns unreadable JSON, or an array whose length is not 1.

These guards decide whether a mutation targets the correct container, so they carry the most risk in this module.

♻️ Proposed additional cases
it("refuses a container whose inspected identity or labels do not match", () => {
  const drifted = harness({ running: true, status: "running" });
  drifted.capture
    .mockImplementationOnce(() => ({
      status: 0,
      stdout: `${CONTAINER_ID}\t${CONTAINER_NAME}\n`,
      stderr: "",
    }))
    .mockImplementationOnce(() => ({
      status: 0,
      stdout: JSON.stringify([
        {
          Id: "b".repeat(64),
          Name: CONTAINER_NAME,
          Config: { Labels: { [PODMAN_MANAGED_LABEL]: "true" } },
          State: { Running: true, Paused: false, Status: "running" },
        },
      ]),
      stderr: "",
    }));
  const hook = vi.fn();
  expect(stopPodmanSandbox(drifted.input, { beforeStop: hook }, drifted.engine)).toMatchObject({
    exitCode: 1,
    message: expect.stringContaining("identity changed after it was pinned"),
  });
  expect(hook).not.toHaveBeenCalled();

  const unmanaged = harness({ running: true, status: "running" });
  unmanaged.capture
    .mockImplementationOnce(() => ({
      status: 0,
      stdout: `${CONTAINER_ID}\t${CONTAINER_NAME}\n`,
      stderr: "",
    }))
    .mockImplementationOnce(() => ({
      status: 0,
      stdout: JSON.stringify([
        {
          Id: CONTAINER_ID,
          Name: CONTAINER_NAME,
          Config: { Labels: { [PODMAN_MANAGED_LABEL]: "false" } },
          State: { Running: true, Paused: false, Status: "running" },
        },
      ]),
      stderr: "",
    }));
  expect(startPodmanSandbox(unmanaged.input, unmanaged.engine)).toMatchObject({
    exitCode: 1,
    message: expect.stringContaining(PODMAN_MANAGED_LABEL),
  });
});

it("refuses unreadable inspect output", () => {
  const runtime = harness({ running: true, status: "running" });
  runtime.capture
    .mockImplementationOnce(() => ({
      status: 0,
      stdout: `${CONTAINER_ID}\t${CONTAINER_NAME}\n`,
      stderr: "",
    }))
    .mockImplementationOnce(() => ({ status: 0, stdout: "not json", stderr: "" }));

  expect(startPodmanSandbox(runtime.input, runtime.engine)).toMatchObject({
    exitCode: 1,
    message: expect.stringContaining("unreadable JSON"),
  });
});
🤖 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/runtime-provider/podman-lifecycle.test.ts` around lines 133 -
158, Add tests in the Podman lifecycle suite covering the remaining fail-closed
guards: reject inspected identity changes, mismatched managed or sandbox-name
labels, unreadable inspect JSON, and inspect arrays whose length is not one. Use
the existing harness and lifecycle functions, assert the expected failure
messages, and verify mutation hooks are not called where applicable.
src/lib/adapters/container-engine.test.ts (1)

114-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the result and bound validation paths.

This suite proves identity, timeout, and argument validation. It does not prove two other guarantees of the boundary:

  • normalizedResult rejects a malformed capture return. An injected capture is an untrusted collaborator here, so a returned object with a missing stdout, a negative status, or a non-Error error should raise "invalid result".
  • MAX_ARGUMENTS and MAX_ARGUMENT_BYTES reject oversized input before capture.
♻️ Proposed additional cases
it("rejects malformed capture results and oversized arguments", () => {
  const engine = createContainerEngineCommand({
    operation: "host-doctor",
    engineId: "podman",
    displayName: "Podman",
    authorityId: "test:podman-socket",
    executable: "podman",
    capture: () => ({ status: -1, stdout: "", stderr: "" }),
  });
  expect(() => engine.capture(["info"])).toThrow("invalid result");

  const bounded = createContainerEngineCommand({
    operation: "host-doctor",
    engineId: "podman",
    displayName: "Podman",
    authorityId: "test:podman-socket",
    executable: "podman",
    capture: vi.fn(() => ({ status: 0, stdout: "", stderr: "" })),
  });
  expect(() => bounded.capture(new Array(513).fill("x"))).toThrow("too many arguments");
  expect(() => bounded.capture(["x".repeat(16 * 1024 + 1)])).toThrow("arguments[0] is invalid");
});
🤖 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/adapters/container-engine.test.ts` around lines 114 - 145, Extend the
test coverage around createContainerEngineCommand and engine.capture to validate
malformed capture results, including invalid status, missing stdout, and
non-Error errors, all throwing “invalid result.” Add boundary cases proving
MAX_ARGUMENTS and MAX_ARGUMENT_BYTES reject oversized input before the injected
capture function is called.
test/e2e/support/workload-source-env.test.ts (1)

27-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the Podman target and both pass-through guards.

Use podman-cpu-lifecycle, which the Podman workflow passes to the live test. Add cases for a missing E2E_TARGET_ID and an existing NEMOCLAW_FROM_DOCKERFILE. The workflow does not set E2E_WORKLOAD_SOURCE, so assert its intended legacy Dockerfile behavior or set the variable if the job should use managed images.

🤖 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/e2e/support/workload-source-env.test.ts` around lines 27 - 41, Update
the parameterized tests around resolveLiveE2eWorkloadSourceEnv to use the
workflow’s podman-cpu-lifecycle target, and add coverage for missing
E2E_TARGET_ID and an existing NEMOCLAW_FROM_DOCKERFILE. Account for the
workflow’s absent E2E_WORKLOAD_SOURCE by asserting the intended legacy
Dockerfile behavior or explicitly setting the variable when managed-image
behavior is required.

Source: Path instructions

test/helpers/managed-image-buildless-e2e.ts (1)

257-404: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract and type the managed-bootstrap fake.

The inline replacement implements all nine ManagedBootstrapAdapter methods inside String.raw, so TypeScript does not check its method inputs or receipt fields. A contract change can leave this test green while it uses stale shapes. Move the fake to a typed test/helpers/*.ts factory and assign it as ManagedBootstrapAdapter; use .ts, because the CLI project includes test/**/*.ts but not test/**/*.cts.

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

In `@test/helpers/managed-image-buildless-e2e.ts` around lines 257 - 404, Extract
the inline replacement passed to createDockerManagedBootstrapAdapter into a
typed .ts helper factory under test/helpers, and assign the returned fake
explicitly as ManagedBootstrapAdapter. Preserve all nine method implementations
and their behavior, while typing each method’s inputs and receipts so contract
changes are checked; update the test to use the factory instead of defining the
object inside String.raw.

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/actions/sandbox/rebuild-preflight-guards.ts`:
- Around line 245-256: Move the registry.getSandbox(sandboxName) call into the
existing try block in the guard containing
managedWorkloadRebuildHandoffMatchesEntry. Ensure registry read failures are
caught alongside provider and durable-authority parsing failures so the function
returns the existing fail-closed refusal result instead of propagating the
exception.

In `@src/lib/onboard/lifecycle-contracts.md`:
- Around line 201-204: Update the reference to `#7744` in the migration-status
paragraph to use the same linked form already established in lines 167–176,
preserving the surrounding wording and explicitly keeping the remaining work
navigable.

In `@src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.test.ts`:
- Around line 413-419: Update the argv secrecy assertion near the existing
harness.calls checks to verify ENVIRONMENT[1] is absent from every recorded
command, not just harness.calls[0]. Preserve the existing journal and watcher
assertions.

In `@src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.ts`:
- Around line 348-372: Update runtimeArguments to reject attached shorthand
forms beginning with -d, -e, or -l, in addition to the existing exact
forbidden-flag checks, before Podman is invoked. Preserve the current validation
for other forbidden flags and add regression tests covering attached -e and -l
arguments (and the required -d prefix behavior).

In `@src/lib/onboard/managed-bootstrap/podman-watcher-lease.ts`:
- Around line 426-436: Update recoverUnfinishedLease and the lease lifecycle
around quiesceAndProve to persist holder identity plus a liveness/expiry signal,
and only call resumeAndProve and store.clear after proving the recorded holder
is dead. Ensure an active stopped lease remains untouched until its holder is
confirmed dead, then allow the existing acquire flow to proceed.

In `@src/lib/onboard/managed-workload/onboard-orchestration.ts`:
- Around line 391-395: Update the contract validation guard in the managed image
onboarding flow to also reject an empty input.intendedWorkloadArgv by checking
its length before bootstrap lifecycle creation. Preserve the existing validation
for bootstrapIdentity, request, and intendedWorkloadArgv being absent, and keep
the current error behavior.

In `@src/lib/onboard/runtime-provider/podman-lifecycle.ts`:
- Around line 174-220: Propagate the caller namespace through
RuntimeProviderLifecycleInput into resolveManagedContainer, add
PODMAN_SANDBOX_NAMESPACE_LABEL to the container lookup filter, and validate the
returned container namespace in parsePodmanManagedContainer. Reject namespace
mismatches before mutation, and add coverage for mismatched namespaces and
identical sandbox names across namespaces.

In `@test/e2e/live/podman-cpu-lifecycle.test.ts`:
- Around line 79-123: Move the “verify all-agent restart identity and final
at-rest state” progress.phase call before the restart and final stop assertions
in the loop. Split the existing loop work so initial start/stop checks remain
under the exercise phase, while restart identity and final at-rest verification
execute after entering the declared final phase; ensure the final phase records
work for every agent and remains the last phase.

In `@test/helpers/managed-image-buildless-e2e.ts`:
- Line 711: Remove the nullish fallback from the assertion in the managed image
registration test and assert registration?.agent directly against agent, so an
omitted agent field fails the test.

In `@test/helpers/onboard-script-mocks.cjs`:
- Around line 178-211: Update mockManagedImageFallback so it no longer activates
from the NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK environment variable at module
load. Capture both original functions before overriding them, then return a
teardown function that restores catalog.resolveManagedImageCatalogFromGhcr and
dockerProvider.createDockerRuntimeProviderBundle; remove the load-time
invocation while leaving the managed-image buildless test unchanged.

---

Nitpick comments:
In `@src/lib/actions/sandbox/rebuild-dcode-orchestrator.ts`:
- Around line 244-277: In the validation try block, branch on
managedWorkloadRebuild before selecting the revalidation call so the non-managed
path narrows replacement after the existing preflight guard. Update
revalidateDcodeReplacementAtMutationEdge invocation to pass the narrowed
replacement directly, and remove the replacement! assertion without changing
either validation flow.

In `@src/lib/actions/sandbox/rebuild-managed-workload-mutation-guard.test.ts`:
- Around line 60-143: Split the current test into two tests: rename the
successful case to describe staging OpenClaw reasoning authority for a
compatible-endpoint target, and isolate the unsupported managed startup
inference API assertion in a test titled for that rejection. Keep each test
focused on its observed prepareManagedRebuildProfileHandoff behavior, and move
these profile-staging tests alongside agents/managed-workload-rebuild-profile.ts
if that is the established unit-test location rather than the mutation-guard
suite.

In `@src/lib/adapters/container-engine.test.ts`:
- Around line 114-145: Extend the test coverage around
createContainerEngineCommand and engine.capture to validate malformed capture
results, including invalid status, missing stdout, and non-Error errors, all
throwing “invalid result.” Add boundary cases proving MAX_ARGUMENTS and
MAX_ARGUMENT_BYTES reject oversized input before the injected capture function
is called.

In `@src/lib/adapters/podman/index.test.ts`:
- Around line 47-62: Expand the identity coverage in the “gives different socket
authorities different opaque identities” test by using it.each for socketPath,
device, mode, ownerUid, and a non-empty directoryChain, varying one field at a
time from AUTHORITY and asserting authorityId changes. Preserve the existing
inode case or include it in the same table so every field used by
podmanAuthorityId is verified through the public createPodmanContainerEngine
boundary.

In `@src/lib/onboard/managed-bootstrap/docker-runtime.test.ts`:
- Line 36: Move temporary state-root cleanup out of the test body and into an
afterEach teardown. Track each directory created by the tests around stateRoot,
then remove all tracked roots with fs.rmSync using the existing cleanup options,
following the roots-array pattern in podman-bootstrap-journal.test.ts.

In `@src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.test.ts`:
- Around line 108-122: Add two tests in the journal store test suite: verify
`create` rejects a stale rollback decision, and verify `removeAfterRollback`
rejects every phase other than `rollback-authorized`. Assert each operation
fails closed and preserves the journal state, covering the guards in `create`
and `removeAfterRollback` without changing existing behavior.

In `@src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.ts`:
- Around line 602-607: Update authorizeRollback to re-read the journal after
atomicWrite and verify the persisted result with sameJournal before returning.
Return the durable re-read only after that comparison, matching the validation
behavior of recordStateVolume, recordReplacement, and recordOriginalStopped.

In `@src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.ts`:
- Around line 44-61: Update FORBIDDEN_RUNTIME_FLAGS to reject the
privilege-affecting runtime options --cap-add, --security-opt, --device,
--userns, and --pid alongside --privileged, ensuring runtimeArgs cannot bypass
the intended container security restrictions.

In `@src/lib/onboard/managed-bootstrap/podman-held-workload.test.ts`:
- Around line 259-267: Update the test around inspect to exercise the
stable-capture comparison rather than parseObservation’s expected-image check:
remove expectedImageContentId from the input used by inspect(fake.engine), while
keeping the differing second inspection output. Preserve the assertion that the
operation rejects with “image content changed.”
- Around line 78-94: Update the test helper function engineWith so its capture
mock checks the result of queue.shift() and throws an explicit, descriptive
error when the output queue is exhausted; otherwise return the queued
ContainerEngineCommandResult unchanged.

In `@src/lib/onboard/managed-bootstrap/podman-held-workload.ts`:
- Around line 150-175: Update discoverRuntimeId to accept sandboxNamespace and
add the corresponding namespace label filter to the Podman container listing
arguments alongside the existing managed and sandbox-name filters. Update its
call site in the surrounding bootstrap flow to pass sandboxNamespace, while
preserving the post-inspect namespace label check as the authoritative
validation.

In `@src/lib/onboard/managed-workload/onboard-orchestration.ts`:
- Around line 402-409: Move the managed-image values currently hardcoded in the
orchestration object—agent uid, gid, workdir, and supervisor argv—into the
managed-image contract module alongside the existing image contract constants,
and export them. Update the orchestration code constructing the managed workload
contract to import and reuse those exported constants instead of defining
duplicate literals, while preserving the existing contract fields and argv
structure.

In `@src/lib/onboard/runtime-provider/podman-lifecycle.test.ts`:
- Around line 133-158: Add tests in the Podman lifecycle suite covering the
remaining fail-closed guards: reject inspected identity changes, mismatched
managed or sandbox-name labels, unreadable inspect JSON, and inspect arrays
whose length is not one. Use the existing harness and lifecycle functions,
assert the expected failure messages, and verify mutation hooks are not called
where applicable.

In `@src/lib/onboard/runtime-provider/podman.test.ts`:
- Around line 195-198: Remove the exact Object.keys ordering assertion from the
test “stays outside the production-selectable registry”; retain the
CURRENT_RUNTIME_PROVIDER_BUNDLES.not.toHaveProperty("podman") assertion, which
directly verifies Podman remains unavailable.

In `@src/lib/onboard/runtime-provider/podman.ts`:
- Around line 65-82: Add the GitHub tracking issue reference to the doc comment
above createPodmanRuntimeProviderBundle, explicitly linking the deferred managed
startup, recovery, GPU, local inference, installer, and protected E2E
qualification work. Keep the existing scope description and deferred runtime
behavior unchanged.

In `@test/e2e/support/workload-source-env.test.ts`:
- Around line 27-41: Update the parameterized tests around
resolveLiveE2eWorkloadSourceEnv to use the workflow’s podman-cpu-lifecycle
target, and add coverage for missing E2E_TARGET_ID and an existing
NEMOCLAW_FROM_DOCKERFILE. Account for the workflow’s absent E2E_WORKLOAD_SOURCE
by asserting the intended legacy Dockerfile behavior or explicitly setting the
variable when managed-image behavior is required.

In `@test/helpers/managed-image-buildless-e2e.ts`:
- Around line 257-404: Extract the inline replacement passed to
createDockerManagedBootstrapAdapter into a typed .ts helper factory under
test/helpers, and assign the returned fake explicitly as
ManagedBootstrapAdapter. Preserve all nine method implementations and their
behavior, while typing each method’s inputs and receipts so contract changes are
checked; update the test to use the factory instead of defining the object
inside String.raw.
🪄 Autofix

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: 89ad893a-5ac7-4617-b7c7-71e8544408dd

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe2319 and 8af8166.

📒 Files selected for processing (99)
  • .github/workflows/e2e.yaml
  • .github/workflows/podman-cpu-proof.yaml
  • ci/protected-managed-image-runtime-activation-v1.json
  • ci/source-shape-test-budget.json
  • scripts/checks/run-managed-image-openshell-e2e.ts
  • src/lib/actions/sandbox/agents/managed-workload-rebuild-profile.ts
  • src/lib/actions/sandbox/rebuild-dcode-orchestrator.test.ts
  • src/lib/actions/sandbox/rebuild-dcode-orchestrator.ts
  • src/lib/actions/sandbox/rebuild-dcode-preflight.ts
  • src/lib/actions/sandbox/rebuild-gpu-opt-out.ts
  • src/lib/actions/sandbox/rebuild-managed-workload-mutation-guard.test.ts
  • src/lib/actions/sandbox/rebuild-pipeline.ts
  • src/lib/actions/sandbox/rebuild-preflight-guards.ts
  • src/lib/actions/sandbox/rebuild-preflight-phase.ts
  • src/lib/actions/sandbox/rebuild-preflight-target-phase.ts
  • src/lib/adapters/container-engine.test.ts
  • src/lib/adapters/container-engine.ts
  • src/lib/adapters/podman/index.test.ts
  • src/lib/adapters/podman/index.ts
  • src/lib/adapters/podman/socket-authority.test.ts
  • src/lib/adapters/podman/socket-authority.ts
  • src/lib/onboard.ts
  • src/lib/onboard/build-context-stage.test.ts
  • src/lib/onboard/build-context-stage.ts
  • src/lib/onboard/command-support.test.ts
  • src/lib/onboard/command-support.ts
  • src/lib/onboard/command.test.ts
  • src/lib/onboard/command.ts
  • src/lib/onboard/lifecycle-contracts.md
  • src/lib/onboard/machine/core-flow-phases.test.ts
  • src/lib/onboard/machine/handlers/provider-inference-route-containment.test.ts
  • src/lib/onboard/machine/handlers/provider-inference.test-support.ts
  • src/lib/onboard/machine/handlers/provider-inference.ts
  • src/lib/onboard/managed-bootstrap/README.md
  • src/lib/onboard/managed-bootstrap/docker-authority-store.test.ts
  • src/lib/onboard/managed-bootstrap/docker-authority-store.ts
  • src/lib/onboard/managed-bootstrap/docker-runtime.test.ts
  • src/lib/onboard/managed-bootstrap/docker-runtime.ts
  • src/lib/onboard/managed-bootstrap/docker-test-fixture.ts
  • src/lib/onboard/managed-bootstrap/docker.ts
  • src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.test.ts
  • src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.ts
  • src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.test.ts
  • src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.ts
  • src/lib/onboard/managed-bootstrap/podman-held-workload.test.ts
  • src/lib/onboard/managed-bootstrap/podman-held-workload.ts
  • src/lib/onboard/managed-bootstrap/podman-image-transaction.test.ts
  • src/lib/onboard/managed-bootstrap/podman-image-transaction.ts
  • src/lib/onboard/managed-bootstrap/podman-watcher-lease.test.ts
  • src/lib/onboard/managed-bootstrap/podman-watcher-lease.ts
  • src/lib/onboard/managed-bootstrap/runtime-create.ts
  • src/lib/onboard/managed-workload/onboard-orchestration.ts
  • src/lib/onboard/runtime-provider/contract.ts
  • src/lib/onboard/runtime-provider/docker.ts
  • src/lib/onboard/runtime-provider/podman-lifecycle.test.ts
  • src/lib/onboard/runtime-provider/podman-lifecycle.ts
  • src/lib/onboard/runtime-provider/podman-preflight.test.ts
  • src/lib/onboard/runtime-provider/podman-preflight.ts
  • src/lib/onboard/runtime-provider/podman.test.ts
  • src/lib/onboard/runtime-provider/podman.ts
  • src/lib/onboard/runtime-provider/registry.ts
  • src/lib/onboard/runtime-provider/runtime-provider-contract.test.ts
  • src/lib/onboard/sandbox-create-intent-types.ts
  • src/lib/onboard/sandbox-create-plan-extra-providers.test.ts
  • src/lib/onboard/sandbox-create-plan-materialization.ts
  • src/lib/onboard/sandbox-create-plan.test.ts
  • src/lib/onboard/sandbox-create-plan.ts
  • src/lib/onboard/sandbox-gpu-create-flow.test.ts
  • src/lib/onboard/sandbox-gpu-create-flow.ts
  • src/lib/onboard/sandbox-gpu-create-run-attempt.ts
  • src/lib/onboard/types.ts
  • test/e2e/fixtures/shell-probe.ts
  • test/e2e/fixtures/workload-source-env.ts
  • test/e2e/live/bedrock-runtime-compatible-anthropic-raw-command.ts
  • test/e2e/live/podman-cpu-lifecycle.test.ts
  • test/e2e/mock-parity.json
  • test/e2e/support/bedrock-runtime-compatible-anthropic-progress.test.ts
  • test/e2e/support/e2e-cross-runtime-compatibility.test.ts
  • test/e2e/support/podman-cpu-proof-workflow.test.ts
  • test/e2e/support/workload-source-env.test.ts
  • test/helpers/managed-image-buildless-e2e.ts
  • test/helpers/onboard-script-mocks.cjs
  • test/onboard-extra-provider-reconciliation.test.ts
  • test/onboard-installer-restore-intent.test.ts
  • test/onboard-managed-image-buildless-e2e.test.ts
  • test/onboard-mcp-observability-redirect.test.ts
  • test/onboard-messaging.test.ts
  • test/onboard-prepared-build-context.test.ts
  • test/onboard-reservation-recreate.test.ts
  • test/onboard-sandbox-build.test.ts
  • test/onboard-sandbox-recreation.test.ts
  • test/onboard-terminal-dashboard.test.ts
  • test/pr-e2e-gate-signal-shards.test.ts
  • test/pr-e2e-gate.test.ts
  • test/pr-risk-plan.test.ts
  • test/runtime-provider-source-shape.test.ts
  • test/shellquote-sandbox.test.ts
  • tools/advisors/risk-plan.mts
  • tools/e2e/managed-image-protected-runtime-workflow-boundary.mts
💤 Files with no reviewable changes (5)
  • src/lib/onboard/sandbox-create-plan-extra-providers.test.ts
  • src/lib/onboard/machine/handlers/provider-inference-route-containment.test.ts
  • src/lib/onboard/machine/handlers/provider-inference.test-support.ts
  • src/lib/onboard/machine/core-flow-phases.test.ts
  • src/lib/onboard/sandbox-create-plan.ts

Comment thread src/lib/onboard/lifecycle-contracts.md
Comment thread src/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.ts
Comment thread src/lib/onboard/managed-bootstrap/podman-watcher-lease.ts

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

🛑 Comments failed to post (6)
src/lib/actions/sandbox/rebuild-preflight-guards.ts (1)

245-256: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Move the registry read inside the try so the stated fail-closed contract holds.

Line 245 calls registry.getSandbox(sandboxName) outside the try. The comment at lines 254-255 states that every provider or durable-authority parse failure converts into one fail-closed result. A registry read is a durable-state read and can throw on a corrupted or unreadable registry file. That error escapes the guard instead of returning the refusal result, so the caller at rebuild-pipeline.ts line 253 never sees a structured message and validateAtDeleteEdge at line 327 throws out of the destroy phase.

🛡️ Proposed fix
   if (!handoff) return null;
-  const current = registry.getSandbox(sandboxName);
   try {
+    const current = registry.getSandbox(sandboxName);
     const provider = current
       ? requireRuntimeProviderBundleForSandbox(current, CURRENT_RUNTIME_PROVIDER_BUNDLES)
       : null;
📝 Committable suggestion

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

  if (!handoff) return null;
  try {
    const current = registry.getSandbox(sandboxName);
    const provider = current
      ? requireRuntimeProviderBundleForSandbox(current, CURRENT_RUNTIME_PROVIDER_BUNDLES)
      : null;
    if (provider && managedWorkloadRebuildHandoffMatchesEntry(handoff, current, provider)) {
      return null;
    }
  } catch {
    // Convert every provider or durable-authority parse failure into one
    // fail-closed mutation-edge result below.
  }
🤖 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/actions/sandbox/rebuild-preflight-guards.ts` around lines 245 - 256,
Move the registry.getSandbox(sandboxName) call into the existing try block in
the guard containing managedWorkloadRebuildHandoffMatchesEntry. Ensure registry
read failures are caught alongside provider and durable-authority parsing
failures so the function returns the existing fail-closed refusal result instead
of propagating the exception.
src/lib/onboard/managed-workload/onboard-orchestration.ts (1)

391-395: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check how intendedWorkloadArgv is produced and consumed.
set -euo pipefail

rg -nP '\bintendedWorkloadArgv\b' -C 6 --type=ts -g '!**/dist/**'

Repository: NVIDIA/NemoClaw

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -euo pipefail
printf '%s\n' '--- tracked target ---'
git ls-files -- src/lib/onboard/managed-workload/onboard-orchestration.ts
printf '%s\n' '--- target lines ---'
sed -n '350,430p' src/lib/onboard/managed-workload/onboard-orchestration.ts
printf '%s\n' '--- all references ---'
rg -n -C 5 'intendedWorkloadArgv|bootstrapIdentity|bootstrap launch contract' . -g '!**/dist/**' -g '!**/node_modules/**' || true
printf '%s\n' '--- related files ---'
fd -t f . src/lib/onboard/managed-workload | sort

Repository: NVIDIA/NemoClaw

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail
printf '%s\n' '--- orchestration guard ---'
cat -n src/lib/onboard/managed-workload/onboard-orchestration.ts | sed -n '360,415p'
printf '%s\n' '--- exact references in relevant source ---'
rg -n 'intendedWorkloadArgv' src/lib/onboard --glob '*.ts' --glob '*.tsx' | head -n 160
printf '%s\n' '--- argv helper definitions and calls ---'
rg -n -C 8 'function (openshellSandboxCommandEnvValue|renderManagedBootstrapHeldCommand)|const (openshellSandboxCommandEnvValue|renderManagedBootstrapHeldCommand)|openshellSandboxCommandEnvValue\(' src/lib/onboard --glob '*.ts'
printf '%s\n' '--- target outline ---'
ast-grep outline src/lib/onboard/managed-workload/onboard-orchestration.ts

Repository: NVIDIA/NemoClaw

Length of output: 24172


🏁 Script executed:

#!/bin/bash
set -euo pipefail
printf '%s\n' '--- argv validation and rendering ---'
cat -n src/lib/onboard/managed-bootstrap/adapter.ts | sed -n '900,990p'
printf '%s\n' '--- command serialization ---'
cat -n src/lib/onboard/docker-startup-command-env.ts | sed -n '1,45p'
printf '%s\n' '--- resolve function callers ---'
rg -n -C 12 'resolveOnboardManagedBootstrapLaunch' src --glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- intended argv producer ---'
cat -n src/lib/onboard/sandbox-gpu-create-flow.ts | sed -n '55,115p'
printf '%s\n' '--- launch handoff ---'
cat -n src/lib/onboard/sandbox-gpu-create-run-attempt.ts | sed -n '75,125p'
printf '%s\n' '--- targeted tests ---'
rg -n -C 8 'resolveOnboardManagedBootstrapLaunch|empty.*argv|intendedWorkloadArgv: \[\]' src --glob '*.test.ts' --glob '*.spec.ts'

Repository: NVIDIA/NemoClaw

Length of output: 21103


🏁 Script executed:

#!/bin/bash
set -euo pipefail
printf '%s\n' '--- assertArgv definition ---'
rg -n -B 8 -A 18 'function assertArgv|const assertArgv|export .*assertArgv' src/lib/onboard/managed-bootstrap/adapter.ts
printf '%s\n' '--- startup command construction ---'
rg -n -C 10 'intendedSandboxStartupCommand|sandboxStartupCommand' src/lib/onboard/managed-workload/onboard-orchestration.ts src/lib/onboard/sandbox-gpu-create-flow.ts src/lib/onboard.ts --glob '*.ts'
printf '%s\n' '--- orchestration tests ---'
fd -t f -e ts . src/lib/onboard | rg 'managed-workload|orchestration|onboard.*test' | sort | xargs -r rg -n -C 6 'resolveOnboardManagedBootstrapLaunch|managed-image'
printf '%s\n' '--- standalone JavaScript truthiness probe ---'
node - <<'JS'
const values = [null, undefined, [], ['env'], ['env', '/usr/local/bin/nemoclaw-start']];
for (const value of values) {
  console.log(JSON.stringify(value), {
    guardPasses: !value,
    length: Array.isArray(value) ? value.length : null,
    nonEmptyGuardRejects: !value || value.length === 0,
  });
}
JS

Repository: NVIDIA/NemoClaw

Length of output: 50371


Reject an empty intendedWorkloadArgv. assertArgv requires at least one argument, so add input.intendedWorkloadArgv.length === 0 to this guard before the malformed contract reaches bootstrap lifecycle creation.

🤖 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-workload/onboard-orchestration.ts` around lines 391 -
395, Update the contract validation guard in the managed image onboarding flow
to also reject an empty input.intendedWorkloadArgv by checking its length before
bootstrap lifecycle creation. Preserve the existing validation for
bootstrapIdentity, request, and intendedWorkloadArgv being absent, and keep the
current error behavior.
src/lib/onboard/runtime-provider/podman-lifecycle.ts (1)

174-220: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether the lifecycle input carries a namespace and how siblings enforce it.
set -euo pipefail

ast-grep outline src/lib/onboard/runtime-provider/contract.ts --items all
rg -nP 'RuntimeProviderLifecycleInput' -A 20 src/lib/onboard/runtime-provider/contract.ts
rg -nP 'namespace' -C 3 -g 'src/lib/onboard/**' -g '!**/dist/**' --type=ts | head -80

Repository: NVIDIA/NemoClaw

Length of output: 6985


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- lifecycle provider implementation ---'
cat -n src/lib/onboard/runtime-provider/podman-lifecycle.ts | sed -n '1,240p'

echo '--- lifecycle input construction and namespace references ---'
rg -n -C 5 'createLifecycle|\.lifecycle\.(start|stop|verifyStarted)|RuntimeProviderLifecycleInput|PODMAN_SANDBOX_NAMESPACE_LABEL|resolveManagedContainer|parsePodmanManagedContainer' src/lib/onboard src/lib/state src/lib 2>/dev/null | head -300

echo '--- SandboxEntry definition ---'
rg -n -C 8 'interface SandboxEntry|type SandboxEntry|namespace' src/lib/state/registry src/lib/state src/lib | head -240

Repository: NVIDIA/NemoClaw

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- SandboxEntry fields ---'
cat -n src/lib/state/registry/types.ts | sed -n '68,125p'

echo '--- RuntimeProviderLifecycleInput call sites ---'
rg -n -C 8 'sandbox: .*sandbox|sandboxName: .*sandbox|startPodmanSandbox|stopPodmanSandbox|lifecycle\.start|lifecycle\.stop' src/lib/onboard/runtime-provider src/lib/onboard | head -260

echo '--- namespace-bearing lifecycle or sandbox fields ---'
rg -n -C 4 'sandboxNamespace|openshell.*namespace|namespace.*openshell|PODMAN_SANDBOX_NAMESPACE_LABEL' src/lib/onboard/runtime-provider src/lib/onboard/managed-bootstrap src/lib/state/registry/types.ts

Repository: NVIDIA/NemoClaw

Length of output: 47681


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- lifecycle entrypoint invocations ---'
rg -n -C 12 '\.(start|stop)\(.*lifecycle|lifecycle\.(start|stop)|provider.*lifecycle' src/lib --glob '*.ts' --glob '*.tsx' | head -320

echo '--- all lifecycle input object construction ---'
rg -n -C 10 'RuntimeProviderLifecycleInput|sandboxName:' src/lib --glob '*.ts' | rg -B 10 -A 10 'RuntimeProviderLifecycleInput|lifecycle\.|start\(|stop\(' | head -320

echo '--- OpenShell namespace source ---'
rg -n -C 6 'sandboxNamespace|SANDBOX_NAMESPACE|namespace' src/lib/onboard/managed-bootstrap src/lib/onboard/runtime-provider src/lib/onboard.ts --glob '*.ts' | head -320

echo '--- current lifecycle tests for lookup filters and namespace mismatch ---'
cat -n src/lib/onboard/runtime-provider/podman-lifecycle.test.ts | sed -n '1,260p'

Repository: NVIDIA/NemoClaw

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

contract = Path("src/lib/onboard/runtime-provider/contract.ts").read_text()
start = Path("src/lib/actions/sandbox/start.ts").read_text()
lifecycle = Path("src/lib/onboard/runtime-provider/podman-lifecycle.ts").read_text()

input_block = re.search(
    r"export interface RuntimeProviderLifecycleInput \{(?P<body>.*?)\n\}",
    contract,
    re.S,
)
assert input_block, "RuntimeProviderLifecycleInput was not found"
input_fields = re.findall(r"readonly\s+([A-Za-z0-9_]+)\s*:", input_block.group("body"))

start_block = re.search(
    r"const input = \{(?P<body>.*?)\n\s*\};",
    start,
    re.S,
)
assert start_block, "lifecycle input construction was not found"
constructed_fields = re.findall(r"^\s*([A-Za-z0-9_]+)\s*,?$", start_block.group("body"), re.M)

resolver = lifecycle[
    lifecycle.index("function resolveManagedContainer("):
    lifecycle.index("function resultForFailure(")
]
has_namespace_lookup_filter = "PODMAN_SANDBOX_NAMESPACE_LABEL" in resolver
parser = lifecycle[
    lifecycle.index("function parsePodmanManagedContainer("):
    lifecycle.index("function commandDetail(")
]
has_namespace_equality_check = bool(
    re.search(
        r"PODMAN_SANDBOX_NAMESPACE_LABEL\]\s*!==\s*expected\.",
        parser,
    )
)

print("RuntimeProviderLifecycleInput fields:", input_fields)
print("constructed lifecycle input fields:", constructed_fields)
print("resolver namespace filter:", has_namespace_lookup_filter)
print("parser namespace equality check:", has_namespace_equality_check)

assert "sandboxNamespace" not in input_fields
assert "sandboxNamespace" not in constructed_fields
assert not has_namespace_lookup_filter
assert not has_namespace_equality_check
PY

Repository: NVIDIA/NemoClaw

Length of output: 371


Add namespace matching to Podman lifecycle resolution. RuntimeProviderLifecycleInput has no namespace, and resolveManagedContainer neither filters nor compares PODMAN_SANDBOX_NAMESPACE_LABEL; a single container with the wrong namespace can therefore be mutated. Propagate the caller namespace, filter by it, and assert it in parsePodmanManagedContainer. Add mismatch and same-name cross-namespace tests.

🤖 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/runtime-provider/podman-lifecycle.ts` around lines 174 - 220,
Propagate the caller namespace through RuntimeProviderLifecycleInput into
resolveManagedContainer, add PODMAN_SANDBOX_NAMESPACE_LABEL to the container
lookup filter, and validate the returned container namespace in
parsePodmanManagedContainer. Reject namespace mismatches before mutation, and
add coverage for mismatched namespaces and identical sandbox names across
namespaces.
test/e2e/live/podman-cpu-lifecycle.test.ts (1)

79-123: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Move the fourth phase marker before the work it names.

E2E_PHASES declares "verify all-agent restart identity and final at-rest state" as the final phase. The test calls progress.phase for that phase at Line 123, after the loop has already performed the restart and at-rest assertions at Lines 114-121.

Two consequences follow. If a restart assertion fails, the progress record attributes the failure to phase 3 and the final phase never runs. The final phase also records no work, so the phase evidence does not describe what the test proved.

Split the loop so the restart and final at-rest checks run under the phase that names them.

♻️ Proposed restructure
   progress.phase("exercise all-agent exact-container start and stop");
+  const lifecycles = [];
   for (const agent of AGENTS) {
     ...
     expect(stopped.Id).toBe(initial.Id);
     expect(stopped.State).toMatchObject({ Paused: false, Running: false, Status: "exited" });
+    lifecycles.push({ agentEngines, initial, input, lifecycle, sandboxName });
+  }
 
+  progress.phase("verify all-agent restart identity and final at-rest state");
+  for (const { agentEngines, initial, input, lifecycle, sandboxName } of lifecycles) {
     expect(lifecycle.start(input)).toEqual({ exitCode: 0 });
     const restarted = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);
     expect(restarted.Id).toBe(initial.Id);
     expect(restarted.State).toMatchObject({ Paused: false, Running: true, Status: "running" });
     expect(lifecycle.stop(input, { beforeStop: vi.fn() })).toEqual({
       exitCode: 0,
       state: "stopped",
     });
+    const atRest = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);
+    expect(atRest.Id).toBe(initial.Id);
+    expect(atRest.State).toMatchObject({ Paused: false, Running: false, Status: "exited" });
   }
-  progress.phase("verify all-agent restart identity and final at-rest state");
 });

As per path instructions for .github/workflows/e2e.yaml, which reference test/e2e/README.md: "Declare ordered semantic phases and always reach the final release 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.

  progress.phase("exercise all-agent exact-container start and stop");
  const lifecycles = [];
  for (const agent of AGENTS) {
    const sandboxName = `podman-${agent}`;
    const agentEngines = engines();
    const agentBundle = createPodmanRuntimeProviderBundle({ engines: agentEngines });
    const lifecycle = supportedLifecycle(agentBundle);
    const sandbox: SandboxEntry = { agent, name: sandboxName, openshellDriver: "podman" };
    const input: RuntimeProviderLifecycleInput = {
      environment: process.env,
      log: vi.fn(),
      sandbox,
      sandboxName,
    };
    const beforeStop = vi.fn();
    const initial = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);

    expect(initial.State).toMatchObject({ Paused: false, Running: false });
    expect(["configured", "created"]).toContain(initial.State.Status);
    expect(agentBundle.preflightDoctor.preflightLifecycle("start", input)).toBeNull();
    expect(lifecycle.start(input)).toEqual({ exitCode: 0 });
    await lifecycle.verifyStarted(
      input,
      vi.fn(async () => undefined),
    );

    const running = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);
    expect(running.Id).toBe(initial.Id);
    expect(running.State).toMatchObject({ Paused: false, Running: true, Status: "running" });

    expect(lifecycle.stop(input, { beforeStop })).toEqual({ exitCode: 0, state: "stopped" });
    expect(beforeStop).toHaveBeenCalledExactlyOnceWith();
    const stopped = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);
    expect(stopped.Id).toBe(initial.Id);
    expect(stopped.State).toMatchObject({ Paused: false, Running: false, Status: "exited" });
    lifecycles.push({ agentEngines, initial, input, lifecycle, sandboxName });
  }

  progress.phase("verify all-agent restart identity and final at-rest state");
  for (const { agentEngines, initial, input, lifecycle, sandboxName } of lifecycles) {
    expect(lifecycle.start(input)).toEqual({ exitCode: 0 });
    const restarted = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);
    expect(restarted.Id).toBe(initial.Id);
    expect(restarted.State).toMatchObject({ Paused: false, Running: true, Status: "running" });
    expect(lifecycle.stop(input, { beforeStop: vi.fn() })).toEqual({
      exitCode: 0,
      state: "stopped",
    });
    const atRest = inspectContainer(agentEngines.sandboxLifecycle, sandboxName);
    expect(atRest.Id).toBe(initial.Id);
    expect(atRest.State).toMatchObject({ Paused: false, Running: false, Status: "exited" });
  }
🤖 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/e2e/live/podman-cpu-lifecycle.test.ts` around lines 79 - 123, Move the
“verify all-agent restart identity and final at-rest state” progress.phase call
before the restart and final stop assertions in the loop. Split the existing
loop work so initial start/stop checks remain under the exercise phase, while
restart identity and final at-rest verification execute after entering the
declared final phase; ensure the final phase records work for every agent and
remains the last phase.

Source: Path instructions

test/helpers/managed-image-buildless-e2e.ts (1)

711-711: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the ?? "openclaw" default so a missing agent field fails.

registration?.agent ?? "openclaw" substitutes a default when the registration omits agent. For the openclaw case the assertion then passes even if the field is absent. The test claims that registration records the agent, so a missing field must fail.

Assert the field directly.

💚 Proposed fix
-  expect(registration?.agent ?? "openclaw").toBe(agent);
+  expect(registration?.agent).toBe(agent);

If the production path intentionally omits agent for the default OpenClaw registration, keep the default but assert that intent explicitly for each agent.

As per path instructions: "Flag ... conditionals that make a test pass without exercising its claim."

📝 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.

  expect(registration?.agent).toBe(agent);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/helpers/managed-image-buildless-e2e.ts` at line 711, Remove the nullish
fallback from the assertion in the managed image registration test and assert
registration?.agent directly against agent, so an omitted agent field fails the
test.

Source: Path instructions

test/helpers/onboard-script-mocks.cjs (1)

178-211: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find every producer and consumer of the managed-image fallback switch.
set -euo pipefail

echo "=== who sets NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK ==="
rg -n -C4 'NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK'

echo "=== who calls mockManagedImageFallback ==="
rg -n -C4 'mockManagedImageFallback'

echo "=== who requires onboard-script-mocks ==="
rg -n -C3 'onboard-script-mocks'

Repository: NVIDIA/NemoClaw

Length of output: 207


🏁 Script executed:

#!/bin/bash
set +e

echo "=== tracked candidate files ==="
git ls-files 'test/helpers/onboard-script-mocks.cjs' 'test/helpers/managed-image-buildless-e2e.ts'

echo "=== environment variable references ==="
rg -n -C4 --hidden --glob '!node_modules/**' 'NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK' . || true

echo "=== mockManagedImageFallback references ==="
rg -n -C5 --hidden --glob '!node_modules/**' 'mockManagedImageFallback' . || true

echo "=== onboard-script-mocks references ==="
rg -n -C3 --hidden --glob '!node_modules/**' 'onboard-script-mocks' . || true

echo "=== focused helper lines ==="
sed -n '150,230p' test/helpers/onboard-script-mocks.cjs

echo "=== focused managed-image test ranges ==="
sed -n '420,455p' test/helpers/managed-image-buildless-e2e.ts
sed -n '560,600p' test/helpers/managed-image-buildless-e2e.ts

Repository: NVIDIA/NemoClaw

Length of output: 38668


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Vitest projects and setup-file placement ==="
sed -n '90,145p' vitest.config.ts
sed -n '215,242p' vitest.config.ts

echo "=== parent-process environment stubs ==="
sed -n '1,75p' test/onboard-messaging.test.ts
sed -n '1,45p' test/onboard-sandbox-build.test.ts
sed -n '1,40p' test/onboard-sandbox-recreation.test.ts

echo "=== onboard messaging child invocation ==="
sed -n '75,145p' test/onboard-messaging.test.ts

echo "=== one child-process consumer with fallback environment ==="
sed -n '130,190p' test/onboard-extra-provider-reconciliation.test.ts
sed -n '145,190p' test/onboard-installer-restore-intent.test.ts

echo "=== helper setup and teardown references ==="
rg -n -C5 'unstubAllEnvs|restoreAllMocks|afterEach|beforeEach|setupFiles' test vitest.config.ts | head -240

echo "=== all direct environment assignments with nearby process launch ==="
rg -n -C8 'NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK' test --glob '*.ts'

Repository: NVIDIA/NemoClaw

Length of output: 47652


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

root = Path(".")
needle = "NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK"
helper = Path("test/helpers/onboard-script-mocks.cjs")
config = Path("vitest.config.ts")

print("=== setup-file registrations ===")
for i, line in enumerate(config.read_text().splitlines(), 1):
    if "onboard-script-mocks.cjs" in line:
        print(f"{config}:{i}:{line.strip()}")

print("=== references classified by nearby process context ===")
for path in sorted(root.glob("test/**/*.ts")):
    lines = path.read_text(errors="replace").splitlines()
    for i, line in enumerate(lines):
        if needle not in line:
            continue
        start = max(0, i - 8)
        end = min(len(lines), i + 9)
        window = "\n".join(lines[start:end])
        kind = "child-process env" if re.search(r"\b(spawnSync|spawn)\s*\(", window) else "worker/test env"
        print(f"{path}:{i+1}:{kind}:{line.strip()}")

print("=== helper mutation sites ===")
for i, line in enumerate(helper.read_text().splitlines(), 1):
    if "resolveManagedImageCatalogFromGhcr =" in line or "createDockerRuntimeProviderBundle =" in line or "mockManagedImageFallback()" in line:
        print(f"{helper}:{i}:{line.strip()}")
PY

Repository: NVIDIA/NemoClaw

Length of output: 2287


Make the fallback mock explicit and restorable.

Multiple onboarding tests set NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK for child processes that preload onboard-script-mocks.cjs. The helper applies both overrides at module load and never restores them. Remove the load-time activation, return a restore function, and restore both original functions during teardown. Keep the managed-image buildless test unchanged because it does not set this variable.

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

In `@test/helpers/onboard-script-mocks.cjs` around lines 178 - 211, Update
mockManagedImageFallback so it no longer activates from the
NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACK environment variable at module load.
Capture both original functions before overriding them, then return a teardown
function that restores catalog.resolveManagedImageCatalogFromGhcr and
dockerProvider.createDockerRuntimeProviderBundle; remove the load-time
invocation while leaving the managed-image buildless test unchanged.

Source: Coding guidelines

@ericksoa
ericksoa dismissed cv’s stale review August 5, 2026 05:10

Superseded by exact reconstructed head 8af8166: the stale 14d8f52 cross-cutting diff was replaced append-only by the intended 14-file managed-bootstrap batch on #8276. Focused validation and current exact-head CI/advisor cycles cover the rebuilt patch.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@prekshivyas
prekshivyas merged commit ee60626 into feat/podman-native-cpu-proof-v2 Aug 5, 2026
66 of 68 checks passed
@prekshivyas
prekshivyas deleted the feat/podman-bootstrap-authority branch August 5, 2026 06:08
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 v0.0.103 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants