Skip to content

feat(runtime): define provider state-mutation contract - #8186

Open
jyaunches wants to merge 3 commits into
mainfrom
codex/issue-7744-state-mutation
Open

feat(runtime): define provider state-mutation contract#8186
jyaunches wants to merge 3 commits into
mainfrom
codex/issue-7744-state-mutation

Conversation

@jyaunches

@jyaunches jyaunches commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR defines a dormant, provider-neutral stateMutation contract. Docker, Kubernetes, and the MXC test fixture remain explicitly unsupported, so runtime behavior does not change.

Related Issue

Related to #7744.

Changes

Type of Change

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

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: The contract is dormant, every current provider remains unsupported, and no CLI, configuration, output, default, workflow, or supported product behavior changes.
  • 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: Codex Desktop ran the NemoClaw maintainer security-code-review skill on the exact published diff at d167fb83c. Two adversarial findings—inherited serialization hooks and non-scalar Unicode aliases—were fixed. All nine categories then passed with no remaining findings; the reviewed 10-file diff has SHA-256 b9366ef6b8816a8f05b11537de54b4068c7c1976c1a96af6e201935134e24d42.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: no-docs-needed
  • Evidence: Reviewed all 10 changed files at d167fb83c. The change defines and hardens a dormant internal provider contract; every current provider remains explicitly unsupported, and no CLI, configuration, output, default, workflow, documentation route, or supported behavior changes.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit: Not applicable.
  • Station profile/scenario: Not applicable.
  • Result: Not applicable; scripts/prepare-dgx-station-host.sh is unchanged.
  • Supporting evidence: Not applicable.

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 origin/main when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — exact-head security validation passed 118/118 provider and transaction tests. The merged-candidate proxy/provider subset passed 33/33 tests. The CLI build and full CLI/test type-check passed.
  • Applicable broad gate passed — not selected; exact-head targeted validation and normal hooks passed, and the fresh remote CI cycle is authoritative for the current head.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Julie Yaunches jyaunches@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added a standardized runtime state-mutation capability with plan validation, integrity checks, supported operations, and recovery handling.
    • Added public access to state-mutation plans, selectors, surfaces, and preparation utilities.
  • Bug Fixes

    • Improved runtime-provider validation to detect missing, incomplete, or invalid state-mutation capabilities.
    • Clearly identifies environments where state mutation is unsupported.

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
@jyaunches jyaunches self-assigned this Aug 4, 2026
@copy-pr-bot

copy-pr-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: aa8913ba-d332-4929-8554-9b1643e76c06

📥 Commits

Reviewing files that changed from the base of the PR and between 7178610 and d167fb8.

📒 Files selected for processing (2)
  • src/lib/onboard/runtime-provider/state-mutation.test.ts
  • src/lib/onboard/runtime-provider/state-mutation.ts
📝 Walkthrough

Walkthrough

This PR adds a stateMutation surface to the runtime provider contract, defining plan types, selectors, execution context, durable fences, and activation proofs. It implements a plan preparation/validation function, adds registry checks requiring supported surfaces to expose acquire, assertFenced, activate, and recover, and marks Docker, Kubernetes, and test provider bundles as unsupported for this capability. Tests cover contract validation and plan preparation edge cases.

Changes

State-mutation contract and validation

Layer / File(s) Summary
State-mutation contract types and surface
src/lib/onboard/runtime-provider/contract.ts
Adds contract and schema version constants. Adds types for selectors, plans, prepared plans, execution context, durable fences, and activation proofs. Adds RuntimeProviderStateMutationSurface with acquire, assertFenced, activate, recover. Registers stateMutation on RuntimeProviderBundle.
Plan preparation and validation implementation
src/lib/onboard/runtime-provider/state-mutation.ts, state-mutation.test.ts, access.ts
Implements prepareRuntimeProviderStateMutationPlan, which snapshots input, validates selectors/digests/schema, rejects accessors and unsupported fields, and returns a frozen canonical plan with digests. Exports the function and its types publicly. Tests cover baseline plans, digest sensitivity, accessor rejection, invalid paths, duplicate selectors, and size bounds.
Registry validation of stateMutation surface
src/lib/onboard/runtime-provider/registry.ts, runtime-provider-contract.test.ts
Adds stateMutation to validated bundle surfaces. Requires supported surfaces to use the expected contract version and provide acquire, assertFenced, activate, recover. Tests cover missing surface, wrong contract version, and incomplete durable-fence protocol.
Provider bundle stateMutation declarations
docker.ts, managed-workload-rebuild-transaction.test.ts, test/helpers/runtime-provider-bundle.ts, test/runtime-provider-source-shape.test.ts
Docker and Kubernetes bundles declare stateMutation as unsupported with provider-specific reasons. Test bundle helpers add the same field. Source-shape tests validate state-mutation.ts stays provider-neutral and is included in the source inventory.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#8032: Extends the same state-mutation surface with a clone operation and clone-handoff logic, modifying the same contract, docker, and registry files.

Suggested labels: feature, area: onboarding, area: architecture

Suggested reviewers: apurvvkumaria, brandonpelfrey

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: defining the runtime provider state-mutation contract.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-7744-state-mutation

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

@github-code-quality

github-code-quality Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit d167fb8 in the codex/issue-7744-sta... branch remains at 96%, unchanged from commit 3835080 in the main branch.


Updated August 04, 2026 12:43 UTC

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Failed after a partial review · low confidence · 0 blockers · 0 warnings · 0 suggestions

Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate.

3 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • established — state mutation at src/lib/onboard/runtime-provider/state-mutation.ts:24: Retain the established term for this provider contract.
  • define — durable fence at src/lib/onboard/runtime-provider/runtime-provider-contract.test.ts:453: Retain the term with the contract's adjacent definition of its opaque provider proof.
  • define — activation proof at src/lib/onboard/runtime-provider/docker.ts:358: Retain the term with the RuntimeProviderStateMutationActivationProof contract definition.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: onboard-repair, onboard-resume, cloud-onboard

1 optional E2E recommendation
  • rebuild-openclaw

Workflow run details

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

@jyaunches
jyaunches marked this pull request as ready for review August 4, 2026 11:51

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

🧹 Nitpick comments (3)
src/lib/onboard/runtime-provider/state-mutation.test.ts (2)

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

The test title claims scope sensitivity, but the test does not assert it.

The test changes intent and projectionSha256 only. It never changes selectors or stateRoot, so it does not prove that planSha256 binds the plan scope. Add a case that changes the selector set. A provider that trusts planSha256 depends on that property.

♻️ Proposed additional case
     const changedProjection = prepareRuntimeProviderStateMutationPlan({
       ...plan(),
       projectionSha256: "b".repeat(64),
     });
+    const changedScope = prepareRuntimeProviderStateMutationPlan({
+      ...plan(),
+      selectors: [{ kind: "path", path: "scripts" }],
+    });
 
     expect(protectionTransition.planSha256).not.toBe(restore.planSha256);
     expect(changedProjection.planSha256).not.toBe(restore.planSha256);
+    expect(changedScope.planSha256).not.toBe(restore.planSha256);
     expect(changedProjection.projectionSha256).toBe("b".repeat(64));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/runtime-provider/state-mutation.test.ts` around lines 39 -
53, Extend the test around prepareRuntimeProviderStateMutationPlan to add a plan
variant with a changed selectors set, while keeping intent and projectionSha256
unchanged. Assert that this variant’s planSha256 differs from
restore.planSha256, proving selector scope is included in the digest.

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

Bind each rejection case to its own error message.

fail prefixes every message with "Runtime provider state-mutation plan is invalid", so /state-mutation plan is invalid/u matches every validation error. Each case in this table passes when the plan is rejected for any reason, including a reason unrelated to its label. Add an expected-message column so each case proves the cause it names.

Based on path instructions: "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."

♻️ Proposed change to assert the specific cause
   it.each([
-    ["relative state root", () => ({ ...plan(), stateRoot: "sandbox/.hermes" })],
-    ["filesystem root", () => ({ ...plan(), stateRoot: "/" })],
-    ["system state root", () => ({ ...plan(), stateRoot: "/etc/nemoclaw" })],
-    ["state-root traversal", () => ({ ...plan(), stateRoot: "/sandbox/../etc" })],
+    ["relative state root", () => ({ ...plan(), stateRoot: "sandbox/.hermes" }), /state root/u],
+    ["filesystem root", () => ({ ...plan(), stateRoot: "/" }), /state root/u],
+    ["system state root", () => ({ ...plan(), stateRoot: "/etc/nemoclaw" }), /state root/u],
+    ["state-root traversal", () => ({ ...plan(), stateRoot: "/sandbox/../etc" }), /state root/u],
     [
       "relative-path traversal",
       () => ({
         ...plan(),
         selectors: [{ kind: "path", path: "scripts/../../etc" }],
       }),
+      /canonical relative path/u,
     ],
     [
       "control characters",
       () => ({
         ...plan(),
         selectors: [{ kind: "path", path: "scripts\u0000escape" }],
       }),
+      /bounded exact string/u,
     ],
     [
       "uppercase projection digest",
       () => ({
         ...plan(),
         projectionSha256: "A".repeat(64),
       }),
+      /lowercase SHA-256/u,
     ],
-  ])("rejects %s (`#7744`)", (_label, value) => {
-    expect(() => prepareRuntimeProviderStateMutationPlan(value())).toThrow(
-      /state-mutation plan is invalid/u,
-    );
+  ])("rejects %s (`#7744`)", (_label, value, expected) => {
+    expect(() => prepareRuntimeProviderStateMutationPlan(value())).toThrow(expected);
   });
🤖 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/state-mutation.test.ts` around lines 91 -
121, Update the parameterized rejection cases in the test around
prepareRuntimeProviderStateMutationPlan to include an expected error-message
pattern for each labeled invalid input, then assert that case-specific pattern
instead of the shared /state-mutation plan is invalid/u prefix. Ensure every
case verifies the validation reason it is intended to cover, including path,
traversal, control-character, and projection-digest failures.

Source: Path instructions

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

19-19: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

PREFIX_PATTERN accepts . and .. as complete prefixes.

canonicalRelativePath rejects the segments . and .. at Line 121. PREFIX_PATTERN does not apply the same rule, so { kind: "prefix", prefix: ".." } and { kind: "prefix", prefix: "." } pass validation. The prefix cannot contain / or \, so it cannot compose a path escape today, and no provider consumes the surface yet. A future provider that matches directory entries below stateRoot would match the .. and . entries themselves. Reject both values in the validator so the prefix selector keeps the same traversal rules as the path selector.

♻️ Proposed change to reject dot prefixes
     const prefix = boundedString(selector.prefix, `selector ${String(index)} prefix`, 128);
     if (!PREFIX_PATTERN.test(prefix)) fail(`selector ${String(index)} prefix is not canonical`);
+    if (prefix === "." || prefix === "..") {
+      fail(`selector ${String(index)} prefix is not canonical`);
+    }
     return Object.freeze({ kind: "prefix", prefix });

Also applies to: 152-156

🤖 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/state-mutation.ts` at line 19, Update
PREFIX_PATTERN and its corresponding validation at the later prefix-selector
path to reject the complete values "." and "..", while continuing to allow other
valid alphanumeric, dot, underscore, and hyphen prefixes up to 128 characters.
Keep the existing canonicalRelativePath traversal rules consistent without
changing unrelated validation.
🤖 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 `@test/runtime-provider-source-shape.test.ts`:
- Around line 133-136: Update the forbidden API assertions in the runtime
provider source-shape test to also reject the provider terms kubernetes and k8s
and the process APIs exec, execSync, and fork, while preserving the existing
checks for docker, podman, hermes, mxc, child_process, execFile, spawn, shell,
command, and callback.

---

Nitpick comments:
In `@src/lib/onboard/runtime-provider/state-mutation.test.ts`:
- Around line 39-53: Extend the test around
prepareRuntimeProviderStateMutationPlan to add a plan variant with a changed
selectors set, while keeping intent and projectionSha256 unchanged. Assert that
this variant’s planSha256 differs from restore.planSha256, proving selector
scope is included in the digest.
- Around line 91-121: Update the parameterized rejection cases in the test
around prepareRuntimeProviderStateMutationPlan to include an expected
error-message pattern for each labeled invalid input, then assert that
case-specific pattern instead of the shared /state-mutation plan is invalid/u
prefix. Ensure every case verifies the validation reason it is intended to
cover, including path, traversal, control-character, and projection-digest
failures.

In `@src/lib/onboard/runtime-provider/state-mutation.ts`:
- Line 19: Update PREFIX_PATTERN and its corresponding validation at the later
prefix-selector path to reject the complete values "." and "..", while
continuing to allow other valid alphanumeric, dot, underscore, and hyphen
prefixes up to 128 characters. Keep the existing canonicalRelativePath traversal
rules consistent without changing unrelated validation.
🪄 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: 6186b180-dbc3-4b84-91cc-4cff42d801c2

📥 Commits

Reviewing files that changed from the base of the PR and between 6a838ff and 7178610.

📒 Files selected for processing (10)
  • src/lib/onboard/managed-workload-rebuild-transaction.test.ts
  • src/lib/onboard/runtime-provider/access.ts
  • src/lib/onboard/runtime-provider/contract.ts
  • src/lib/onboard/runtime-provider/docker.ts
  • src/lib/onboard/runtime-provider/registry.ts
  • src/lib/onboard/runtime-provider/runtime-provider-contract.test.ts
  • src/lib/onboard/runtime-provider/state-mutation.test.ts
  • src/lib/onboard/runtime-provider/state-mutation.ts
  • test/helpers/runtime-provider-bundle.ts
  • test/runtime-provider-source-shape.test.ts

Comment on lines +133 to +136
expect(providerContract.stateMutation).not.toMatch(/\b(?:docker|podman|hermes|mxc)\b/iu);
expect(providerContract.stateMutation).not.toMatch(
/(?:child_process|execFile|spawn|shell|command|callback)/iu,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover all forbidden provider and process APIs.

The test accepts kubernetes, k8s, exec, execSync, and fork. A later state-mutation.ts change can add provider routing or process execution through these names and still pass.

Add these names to the forbidden patterns.

🤖 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/runtime-provider-source-shape.test.ts` around lines 133 - 136, Update
the forbidden API assertions in the runtime provider source-shape test to also
reject the provider terms kubernetes and k8s and the process APIs exec,
execSync, and fork, while preserving the existing checks for docker, podman,
hermes, mxc, child_process, execFile, spawn, shell, command, and callback.

Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant