feat(runtime): add dormant Podman managed bootstrap transaction - #8052
feat(runtime): add dormant Podman managed bootstrap transaction#8052prekshivyas merged 230 commits into
Conversation
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
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>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
apurvvkumaria
left a comment
There was a problem hiding this comment.
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.
cv
left a comment
There was a problem hiding this comment.
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.
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (17)
src/lib/onboard/managed-bootstrap/podman-bootstrap-journal.ts (1)
602-607: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winVerify the durable re-read after the rollback authorization write.
recordStateVolume,recordReplacement, andrecordOriginalStoppedeach reload the journal and compare it withsameJournalbefore they return.authorizeRollbackreturns the in-memoryupdatedobject 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 winMove 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, aspodman-bootstrap-journal.test.tsdoes with itsrootsarray.♻️ 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 winAdd coverage for two fail-closed guards on destructive paths.
Two guards in the store have no test:
createrejects a stale rollback decision (podman-bootstrap-journal.tsLines 463-465).removeAfterRollbackrejects any phase other thanrollback-authorized(podman-bootstrap-journal.tsLines 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 winAlign the image-drift test title with the guard it exercises.
The second inspect returns
OTHER_IMAGE_ID, which fails the expected-image check insideparseObservationatpodman-held-workload.tsLine 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
expectedImageContentIdfrom 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 valueFail loudly when the capture queue is exhausted.
queue.shift() as ContainerEngineCommandResultreturnsundefinedafter 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 aTypeErroronresult.statusinstead 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 winScope the discovery filter to the sandbox namespace.
discoverRuntimeIdfilters onopenshell.managedandopenshell.sandbox-nameonly. 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
sandboxNamespaceintodiscoverRuntimeIdfrom 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 winIncomplete Container Privilege Flag Denylist (CWE-250)
Exploitability: Difficult
Align
FORBIDDEN_RUNTIME_FLAGSwith its security contract.
runtimeArgsis passed directly tocontainer create, but the denylist rejects--privilegedwhile 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 valueRemove the non-null assertion by narrowing on
replacementfirst.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 runtimeundefinedpassed torevalidateDcodeReplacementAtMutationEdge. Branch onmanagedWorkloadRebuildfirst so the compiler narrowsreplacement.♻️ 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 winDo 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 winCover every authority field in the identity test.
The test proves only that
inodechangesauthorityId.podmanAuthorityIdbuilds its canonical object field by field, so a dropped field would silently give two different sockets the same identity.podman.tscomparesauthorityIdto prove that two engines bind the same endpoint, so a collision would defeat that check.Extend the test with
it.eachoversocketPath,device,mode,ownerUid, and a non-emptydirectoryChainso 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 winRetitle and split this test; the title does not describe what it asserts.
The title states "before deletion", but the body never calls
revalidateManagedWorkloadRebuildBeforeDelete. It callsprepareManagedRebuildProfileHandoffand 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.tsso 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 valueLink the deferred slices to a GitHub issue.
The doc comment names the deferred slices and
deferredcarries them into four unsupported surfaces. The path instruction forsrc/**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 winMove the managed-image contract constants next to the image contract.
Lines 406 and 408 hardcode
uid: 1000,gid: 1000,workdir: "/sandbox", andexpectedSupervisorArgv: ["/opt/openshell/bin/openshell-sandbox"]inside an orchestration module. These values describe the managed image, not the onboarding sequence.
input.workload.source.contractalready carriescapabilityContractVersionandstartupProfileContractVersion. 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 winCover the identity and label guards.
This suite proves ambiguity, unknown state, engine scope, and retry convergence. Three fail-closed guards in
podman-lifecycle.tshave no test:
- Line 103: the inspected
Iddiffers from the ID pinned byps("identity changed after it was pinned").- Lines 112-119:
openshell.managedis not"true", oropenshell.sandbox-namedoes 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 winAdd 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:
normalizedResultrejects a malformed capture return. An injectedcaptureis an untrusted collaborator here, so a returned object with a missingstdout, a negativestatus, or a non-Errorerrorshould raise "invalid result".MAX_ARGUMENTSandMAX_ARGUMENT_BYTESreject 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 winCover 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 missingE2E_TARGET_IDand an existingNEMOCLAW_FROM_DOCKERFILE. The workflow does not setE2E_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 liftExtract and type the managed-bootstrap fake.
The inline replacement implements all nine
ManagedBootstrapAdaptermethods insideString.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 typedtest/helpers/*.tsfactory and assign it asManagedBootstrapAdapter; use.ts, because the CLI project includestest/**/*.tsbut nottest/**/*.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
📒 Files selected for processing (99)
.github/workflows/e2e.yaml.github/workflows/podman-cpu-proof.yamlci/protected-managed-image-runtime-activation-v1.jsonci/source-shape-test-budget.jsonscripts/checks/run-managed-image-openshell-e2e.tssrc/lib/actions/sandbox/agents/managed-workload-rebuild-profile.tssrc/lib/actions/sandbox/rebuild-dcode-orchestrator.test.tssrc/lib/actions/sandbox/rebuild-dcode-orchestrator.tssrc/lib/actions/sandbox/rebuild-dcode-preflight.tssrc/lib/actions/sandbox/rebuild-gpu-opt-out.tssrc/lib/actions/sandbox/rebuild-managed-workload-mutation-guard.test.tssrc/lib/actions/sandbox/rebuild-pipeline.tssrc/lib/actions/sandbox/rebuild-preflight-guards.tssrc/lib/actions/sandbox/rebuild-preflight-phase.tssrc/lib/actions/sandbox/rebuild-preflight-target-phase.tssrc/lib/adapters/container-engine.test.tssrc/lib/adapters/container-engine.tssrc/lib/adapters/podman/index.test.tssrc/lib/adapters/podman/index.tssrc/lib/adapters/podman/socket-authority.test.tssrc/lib/adapters/podman/socket-authority.tssrc/lib/onboard.tssrc/lib/onboard/build-context-stage.test.tssrc/lib/onboard/build-context-stage.tssrc/lib/onboard/command-support.test.tssrc/lib/onboard/command-support.tssrc/lib/onboard/command.test.tssrc/lib/onboard/command.tssrc/lib/onboard/lifecycle-contracts.mdsrc/lib/onboard/machine/core-flow-phases.test.tssrc/lib/onboard/machine/handlers/provider-inference-route-containment.test.tssrc/lib/onboard/machine/handlers/provider-inference.test-support.tssrc/lib/onboard/machine/handlers/provider-inference.tssrc/lib/onboard/managed-bootstrap/README.mdsrc/lib/onboard/managed-bootstrap/docker-authority-store.test.tssrc/lib/onboard/managed-bootstrap/docker-authority-store.tssrc/lib/onboard/managed-bootstrap/docker-runtime.test.tssrc/lib/onboard/managed-bootstrap/docker-runtime.tssrc/lib/onboard/managed-bootstrap/docker-test-fixture.tssrc/lib/onboard/managed-bootstrap/docker.tssrc/lib/onboard/managed-bootstrap/podman-bootstrap-journal.test.tssrc/lib/onboard/managed-bootstrap/podman-bootstrap-journal.tssrc/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.test.tssrc/lib/onboard/managed-bootstrap/podman-bootstrap-replacement.tssrc/lib/onboard/managed-bootstrap/podman-held-workload.test.tssrc/lib/onboard/managed-bootstrap/podman-held-workload.tssrc/lib/onboard/managed-bootstrap/podman-image-transaction.test.tssrc/lib/onboard/managed-bootstrap/podman-image-transaction.tssrc/lib/onboard/managed-bootstrap/podman-watcher-lease.test.tssrc/lib/onboard/managed-bootstrap/podman-watcher-lease.tssrc/lib/onboard/managed-bootstrap/runtime-create.tssrc/lib/onboard/managed-workload/onboard-orchestration.tssrc/lib/onboard/runtime-provider/contract.tssrc/lib/onboard/runtime-provider/docker.tssrc/lib/onboard/runtime-provider/podman-lifecycle.test.tssrc/lib/onboard/runtime-provider/podman-lifecycle.tssrc/lib/onboard/runtime-provider/podman-preflight.test.tssrc/lib/onboard/runtime-provider/podman-preflight.tssrc/lib/onboard/runtime-provider/podman.test.tssrc/lib/onboard/runtime-provider/podman.tssrc/lib/onboard/runtime-provider/registry.tssrc/lib/onboard/runtime-provider/runtime-provider-contract.test.tssrc/lib/onboard/sandbox-create-intent-types.tssrc/lib/onboard/sandbox-create-plan-extra-providers.test.tssrc/lib/onboard/sandbox-create-plan-materialization.tssrc/lib/onboard/sandbox-create-plan.test.tssrc/lib/onboard/sandbox-create-plan.tssrc/lib/onboard/sandbox-gpu-create-flow.test.tssrc/lib/onboard/sandbox-gpu-create-flow.tssrc/lib/onboard/sandbox-gpu-create-run-attempt.tssrc/lib/onboard/types.tstest/e2e/fixtures/shell-probe.tstest/e2e/fixtures/workload-source-env.tstest/e2e/live/bedrock-runtime-compatible-anthropic-raw-command.tstest/e2e/live/podman-cpu-lifecycle.test.tstest/e2e/mock-parity.jsontest/e2e/support/bedrock-runtime-compatible-anthropic-progress.test.tstest/e2e/support/e2e-cross-runtime-compatibility.test.tstest/e2e/support/podman-cpu-proof-workflow.test.tstest/e2e/support/workload-source-env.test.tstest/helpers/managed-image-buildless-e2e.tstest/helpers/onboard-script-mocks.cjstest/onboard-extra-provider-reconciliation.test.tstest/onboard-installer-restore-intent.test.tstest/onboard-managed-image-buildless-e2e.test.tstest/onboard-mcp-observability-redirect.test.tstest/onboard-messaging.test.tstest/onboard-prepared-build-context.test.tstest/onboard-reservation-recreate.test.tstest/onboard-sandbox-build.test.tstest/onboard-sandbox-recreation.test.tstest/onboard-terminal-dashboard.test.tstest/pr-e2e-gate-signal-shards.test.tstest/pr-e2e-gate.test.tstest/pr-risk-plan.test.tstest/runtime-provider-source-shape.test.tstest/shellquote-sandbox.test.tstools/advisors/risk-plan.mtstools/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
There was a problem hiding this comment.
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
tryso the stated fail-closed contract holds.Line 245 calls
registry.getSandbox(sandboxName)outside thetry. 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 atrebuild-pipeline.tsline 253 never sees a structured message andvalidateAtDeleteEdgeat 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 | sortRepository: 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.tsRepository: 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, }); } JSRepository: NVIDIA/NemoClaw
Length of output: 50371
Reject an empty
intendedWorkloadArgv.assertArgvrequires at least one argument, so addinput.intendedWorkloadArgv.length === 0to 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 -80Repository: 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 -240Repository: 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.tsRepository: 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 PYRepository: NVIDIA/NemoClaw
Length of output: 371
Add namespace matching to Podman lifecycle resolution.
RuntimeProviderLifecycleInputhas no namespace, andresolveManagedContainerneither filters nor comparesPODMAN_SANDBOX_NAMESPACE_LABEL; a single container with the wrong namespace can therefore be mutated. Propagate the caller namespace, filter by it, and assert it inparsePodmanManagedContainer. 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_PHASESdeclares "verify all-agent restart identity and final at-rest state" as the final phase. The test callsprogress.phasefor 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 referencetest/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 missingagentfield fails.
registration?.agent ?? "openclaw"substitutes a default when the registration omitsagent. 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
agentfor 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.tsRepository: 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()}") PYRepository: NVIDIA/NemoClaw
Length of output: 2287
Make the fallback mock explicit and restorable.
Multiple onboarding tests set
NEMOCLAW_TEST_MANAGED_IMAGE_FALLBACKfor child processes that preloadonboard-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
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
ee60626
into
feat/podman-native-cpu-proof-v2
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
managed-bootstrapoperation scope to the driver-neutral container-engine boundary.Type of Change
Quality Gates
Documentation Writer Review
no-public-docs-neededsrc/lib/onboard/managed-bootstrap/README.mdandsrc/lib/onboard/lifecycle-contracts.mdrecord the internal dormant contract; public documentation would incorrectly imply supported Podman activation.DGX Station Hardware Evidence
Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshing the target branch when hooks were skipped or unavailable79/79focused managed-bootstrap tests,8/8source-boundary tests, and3/3Podman workflow-contract tests pass on exact head085adaee04b7; all four exact-head CodeRabbit threads are fixed and resolved.npm run docsbuilds without warnings (doc changes only) — not applicable; no user-facing docs changed.Signed-off-by: Aaron Erickson aerickson@nvidia.com
Summary by CodeRabbit
New Features
Bug Fixes
Tests