Skip to content

feat(inference): add dual DGX Spark express profile - #8129

Merged
ericksoa merged 56 commits into
mainfrom
feat/dual-dgx-spark-express
Aug 4, 2026
Merged

feat(inference): add dual DGX Spark express profile#8129
ericksoa merged 56 commits into
mainfrom
feat/dual-dgx-spark-express

Conversation

@ericksoa

@ericksoa ericksoa commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an Experimental Express path that detects a qualified two-system DGX Spark cluster and selects its managed-vLLM profile from the compiled catalog. Cluster cardinality and serving configuration now live in YAML, while generic TypeScript adapters validate and execute the ranked topology without encoding a fixed cluster size. Existing runtimes and explicit legacy inference choices remain authoritative.

Changes

  • Compile and validate managed-inference preset and recipe YAML for the installer and packaged runtime. The catalog is required because selection policy and serving configuration must be data-driven; catalog compiler and package-contract tests protect the generated boundary.
  • Register a generic vllm.managed-cluster/v1 materializer and vllm.managed-cluster.lifecycle/v1 lifecycle with the host-cluster.direct-cx7 topology contract. A direct profile-specific implementation would make every compatible profile require TypeScript changes; adapter-registry and catalog-compiler tests protect registration and compatibility checks.
  • Resolve cluster.nodeCount from the discovered readiness set, validate it against YAML execution.nodeCount, and materialize every topology node as an ordered rank. Topology and materializer tests cover the shipped two-node profile and a synthetic three-node profile through the same adapters, with execution eligibility determined by direct master-address reachability instead of a fixed cardinality check.
  • Generalize discovery, execution, lifecycle, runtime receipts, recovery, and rollback to ranked node and binding arrays. Discovery, executor, lifecycle, installer, and receipt suites protect multi-node identity, ordering, ownership, and fail-closed behavior.
  • Enumerate per-rank runtime bindings and per-node discovery claims during uninstall. Scanner and uninstall tests protect exact receipt-owned cleanup while preserving conflicting or orphaned state.
  • Keep the shipped automatic profile at nodeCount: 2 in YAML for DeepSeek V4 Flash 0731 on two DGX Spark systems. A profile that uses the registered contracts can be added through preset and recipe YAML; a different execution or qualification contract still requires a registered adapter.
  • Document selection, pretrusted SSH, topology validation, host-global ownership, recovery, and uninstall behavior.

The profile remains Experimental until physical two-system end-to-end validation is complete.

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:
  • 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: Maintainer-authorized change. The implementation preserves pretrusted SSH, pinned host-key, exact ownership, and fail-closed cleanup boundaries; physical cluster validation remains required before promotion beyond Experimental.
  • 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: docs-updated
  • Evidence: Reviewed the effective seven-file documentation and security patch plus the CodeRabbit catalog-loader remediation after the branch-sync reconciliation; confirmed main's merged documentation refresh drops out against current origin/main, the generated DGX Spark platform row remains in sync with ci/platform-matrix.json, and the PR's terminology, profile-selection, YAML extensibility, --trust-remote-code omission, SHA-256 verification before snapshot-code copy, managed-runtime ownership, legacy receipt discovery, uninstall behavior, and fail-closed catalog narrowing remain accurate; npm run catalog:check, python3 scripts/generate-platform-docs.py --check, and git diff --check passed.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

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

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 — managed-inference and catalog suites passed 204 tests; adjacent runtime suites passed 245 tests; canonical catalog/YAML-only/watch-trigger checks, semantic-phase coverage, package-contract coverage, and the isolated Express installer lane also passed. CLI build, CLI typecheck, catalog validation, repository architecture checks, and strict documentation validation passed.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result: exact-head CI / Pull Request passed after the isolated flaky shard rerun, and the required seven-scenario E2E / PR Gate passed
  • 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: Aaron Erickson aerickson@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added experimental distributed vLLM serving across two qualifying DGX Spark systems.
    • Added automatic profile selection, topology validation, authenticated endpoints, readiness checks, and managed runtime recovery.
    • Added support for DeepSeek V4 Flash 0731 on dual-DGX Spark configurations.
  • Documentation
    • Added setup guidance for installation, networking, startup, verification, rollback, restart, and uninstall.
    • Clarified platform support and distributed workflow limitations.
  • Bug Fixes
    • Improved cleanup safety, state detection, API-key handling, and failure reporting.
  • Chores
    • Added automated inference catalog compilation and validation checks.

@copy-pr-bot

copy-pr-bot Bot commented Aug 3, 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 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 820448fb-bf0b-414f-b317-d75074fdd0be

📥 Commits

Reviewing files that changed from the base of the PR and between 2ee7d11 and 3191c00.

📒 Files selected for processing (1)
  • ci/source-architecture-budget.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • ci/source-architecture-budget.json

📝 Walkthrough

Walkthrough

Added a managed two-DGX Spark vLLM profile for DeepSeek V4 Flash. Added catalog schemas, discovery, topology qualification, plan materialization, distributed execution, runtime receipts, recovery, cleanup, onboarding integration, validation hooks, and documentation.

Changes

Managed inference catalog and distributed serving

Layer / File(s) Summary
Catalog contracts and compilation
managed-inference/*, src/lib/inference/serving/*, package.json, test/managed-inference-catalog-compiler.test.ts
Expanded serving schemas and types. Added catalog integrity, registries, loading, resolution, compilation, validation, and the two-DGX Spark preset and recipe.
Topology discovery and selection
src/lib/inference/serving/managed-cluster-discovery*, src/lib/inference/serving/managed-cluster-topology*
Added strict host probing, pinned SSH transport, reciprocal rail checks, readiness reports, topology artifacts, and automatic preset selection.
Plan materialization and execution
src/lib/inference/serving/managed-cluster-materialize*, src/lib/inference/serving/managed-cluster-executor*, src/lib/inference/serving/managed-cluster-lifecycle*, src/lib/inference/serving/managed-cluster-installer*
Added immutable distributed plans, preparation operations, Docker orchestration, worker-first readiness, lifecycle ownership, rollback, and installation.
Runtime receipts and cleanup
src/lib/inference/serving/managed-cluster-runtime-receipt*, src/lib/actions/uninstall/*, src/lib/inference/local.ts
Added durable receipt persistence, endpoint recovery, state discovery, generalized managed-vLLM bindings, exact cleanup, lifecycle locks, and fail-closed uninstall behavior.
Integration and documentation
docs/*, scripts/install.sh, src/lib/onboard/*, .pre-commit-config.yaml, test/*
Updated onboarding, platform messaging, navigation, state documentation, catalog checks, watch triggers, and related tests.

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

Possibly related issues

Possibly related PRs

  • NVIDIA/NemoClaw#8152: Extends the serving catalog compiler, schemas, types, and validation APIs.
  • NVIDIA/NemoClaw#8158: Extends runtime ownership, recovery, SSH binding, and uninstall behavior.
  • NVIDIA/NemoClaw#8183: Extends managed-inference schemas, types, compiler validation, and readiness requirements.

Suggested labels: area: ci, area: architecture

Suggested reviewers: apurvvkumaria

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.56% 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 and concisely describes the main change: adding an Express profile for dual DGX Spark inference.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dual-dgx-spark-express

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

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@github-code-quality

github-code-quality Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 3191c00 in the feat/dual-dgx-spark-... branch remains at 96%, unchanged from commit f504948 in the main branch.


Updated August 04, 2026 09:27 UTC

Comment thread src/lib/inference/serving/dual-spark-lifecycle.ts Fixed
Comment thread src/lib/inference/serving/dual-spark-discovery-production.ts Fixed
Comment thread src/lib/inference/serving/dual-spark-lifecycle.ts Fixed
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Informational

Advisor assessment: Informational / low confidence
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions
Status: PR review advisor failed: PR review advisor SDK execution failed: session: 400: {"message":"litellm.BadRequestError: AzureException BadRequestError - {\n \"error\": {\n \"message\": \"Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 1 instead.\",\n \"type\": \"invalid_request_error\",\n \"param\": \"max_output_tokens\",\n \"code\": \"integer_below_min_value\"\n }\n}. Received Model Group=azure/openai/gpt-5.6-terra\nAvailable Model Group Fallbacks=None","type":null,"param":null,"code":"400"}; turn: scope-risk-map-analysis: 400: {"message":"litellm.BadRequestError: AzureException BadRequestError - {\n \"error\": {\n \"message\": \"Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 1 instead.\",\n \"type\": \"invalid_request_error\",\n \"param\": \"max_output_tokens\",\n \"code\": \"integer_below_min_value\"\n }\n}. Received Model Group=azure/openai/gpt-5.6-terra\nAvailable Model Group Fallbacks=None","type":null,"param":null,"code":"400"}

Model lanes

  • GPT-5.6 Terra (primary): Failed
  • Nemotron 3 Ultra (second opinion): Failed

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

E2E guidance

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

Recommended E2E: cloud-inference, cloud-onboard, security-posture, inference-routing, network-policy, onboard-repair, onboard-resume

Workflow run details

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

@ericksoa ericksoa self-assigned this Aug 3, 2026
ericksoa added 17 commits August 3, 2026 07:39
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
…xpress

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>
…xpress

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@ericksoa ericksoa added feature PR adds or expands user-visible functionality provider: vllm vLLM local or hosted provider behavior labels Aug 3, 2026
@ericksoa

ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@senthilr-nv senthilr-nv added the v0.0.102 Release target label Aug 4, 2026
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@prekshivyas prekshivyas self-assigned this Aug 4, 2026

@senthilr-nv senthilr-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review result: no blocking code or regression defect found in the reviewed PR-owned diff. Build, catalog validation, CLI typecheck, repository checks, focused managed-cluster/runtime/uninstall tests, installer tests, the focused package contract, and strict docs validation passed.

Two non-blocking clarity follow-ups:

  • ci/platform-matrix.json still classifies the full “Local vLLM (managed install/start)” provider surface as “Tested with limitations,” while the dedicated two-DGX-Spark guide correctly labels the new automatic profile Experimental with physical two-node E2E pending. Please align the matrix source and regenerated documentation so the provider table communicates that distinction.
  • RFC #7636 accepts the generic managed-serving catalog architecture but intentionally does not qualify a specific backend, model, platform, or agent. Since this is a maintainer-owned product-scope decision, please make that decision explicit in the PR record, including ownership, lifecycle/compatibility expectations, security acceptance, and the physical-validation gate.

The dedicated guide otherwise documents the elevated container privileges, partial bearer coverage, operator-enforced firewall boundary, credential visibility, host-global ownership, cleanup behavior, and pending validation accurately.

GitHub checks are authoritative and are not part of this review rationale.

@senthilr-nv senthilr-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review result: no blocking code or regression defect found in the reviewed PR-owned diff. Build, catalog validation, CLI typecheck, repository checks, focused managed-cluster/runtime/uninstall tests, installer tests, the focused package contract, and strict docs validation passed.

Two non-blocking clarity follow-ups:

  • ci/platform-matrix.json still classifies the full Local vLLM (managed install/start) provider surface as Tested with limitations, while the dedicated two-DGX-Spark guide correctly labels the new automatic profile Experimental with physical two-node E2E pending. Please align the matrix source and regenerated documentation so the provider table communicates that distinction.
  • RFC #7636 accepts the generic managed-serving catalog architecture but intentionally does not qualify a specific backend, model, platform, or agent. Since this is a maintainer-owned product-scope decision, please make that decision explicit in the PR record, including ownership, lifecycle/compatibility expectations, security acceptance, and the physical-validation gate.

The dedicated guide otherwise documents the elevated container privileges, partial bearer coverage, operator-enforced firewall boundary, credential visibility, host-global ownership, cleanup behavior, and pending validation accurately.

GitHub checks are authoritative and are not part of this review rationale.

ericksoa and others added 6 commits August 4, 2026 00:10
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>
@ericksoa

ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Resolved the advisor security blocker on the current head. The managed recipe no longer enables --trust-remote-code; the pinned model declares native DeepseekV4ForCausalLM support with no remote-code mapping. The one required encoder module is now declared with an exact SHA-256 digest in YAML, validated by the catalog contract, and verified before the executor copies it into the runtime package. The executor fails closed on a mismatch. Focused catalog/preparation/materializer/executor coverage is green, and the exact-head repository validation passes.

@ericksoa

ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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

@ericksoa

ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (11)
src/lib/inference/vllm-managed-cluster-runtime-receipt.test.ts (1)

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

Derive the listening ports from the plan instead of hard-coding 8000 and 25000.

snapshot hard-codes the API port and the rendezvous port. Both values come from the compiled catalog recipe through plan.apiPort and plan.masterPort. If the recipe changes either port, classifyManagedClusterExistingState stops observing the expected listener and these tests fail with an unclear reason instead of a port mismatch. Pass the plan into snapshot and read the ports from it.

The rolePlan.endpoint ? 8000 : 0 guard is also unreachable, because materializeManagedClusterVllmPlan always sets endpoint for the head role.

♻️ Proposed plan-derived ports
 function snapshot(
+  runtimePlan: ManagedClusterVllmPlan,
   rolePlan: ManagedClusterVllmRolePlan,
   containerId: string,
 ): ManagedClusterNodeSnapshot {
@@
     listeningPorts:
-      rolePlan.role === "head" ? [rolePlan.endpoint ? 8000 : 0, 25000].filter(Boolean) : [25000],
+      rolePlan.role === "head"
+        ? [runtimePlan.apiPort, runtimePlan.masterPort]
+        : [runtimePlan.masterPort],
 }
🤖 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/inference/vllm-managed-cluster-runtime-receipt.test.ts` around lines
191 - 192, Update snapshot to accept the compiled plan and derive listeningPorts
from plan.apiPort and plan.masterPort instead of hard-coded 8000 and 25000
values. Remove the unreachable rolePlan.endpoint guard, preserve the head/worker
port structure, and update all snapshot callers to pass the plan through.
src/lib/inference/serving/managed-cluster-ssh-binding.ts (1)

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

Link the retirement issue and state exit criteria for this compatibility alias layer.

This module forwards every managed vLLM SSH binding name to the legacy vllm-station-ssh-binding owner. The comment states the transitional intent but does not link the tracking issue or give observable exit criteria. Without those, the dual naming can persist indefinitely and new callers can keep choosing either name.

Add a GitHub issue or PR link for the migration of the legacy Station path to the serving catalog, and state when this alias module gets deleted.

As per path instructions: "Retain an old path only for a demonstrated external/persisted-data contract or a bounded confidence/rollback window. Keep the replacement authoritative, freeze the old path against new callers and features, link the retirement issue or PR in GitHub, and state observable exit criteria."

🤖 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/inference/serving/managed-cluster-ssh-binding.ts` around lines 9 -
13, Update the module comment above the managed vLLM SSH binding aliases to
include a GitHub issue or PR link tracking migration of the legacy Station path
to the serving catalog, explicitly identify the serving catalog as
authoritative, and state observable criteria for deleting this compatibility
module, including completion of migration and confirmation that no external or
persisted-data consumers require the legacy name.

Source: Path instructions

src/lib/inference/serving/managed-cluster-runtime-receipt.ts (1)

592-598: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Isolate each binding rollback so one failure does not skip the rest.

The try wraps the whole loop. If clearManagedVllmSshBinding throws for the newest state path, the loop stops and the older created binding trees stay on disk. A later persistManagedClusterVllmRuntimeReceipt call then fails with Managed cluster SSH binding state already exists. Move the try inside the loop so each path is cleared independently.

♻️ Proposed rollback isolation
   } catch (error) {
-    try {
-      for (const statePath of createdBindingStatePaths.reverse()) {
+    for (const statePath of createdBindingStatePaths.reverse()) {
+      try {
         clearManagedVllmSshBinding(statePath);
+      } catch {
+        // Preserve the receipt persistence error.
       }
-    } catch {
-      // Preserve the receipt persistence error.
     }
     throw error;
   }
🤖 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/inference/serving/managed-cluster-runtime-receipt.ts` around lines
592 - 598, Move the try/catch from around the entire rollback loop into the loop
over createdBindingStatePaths in the receipt persistence flow. Wrap each
clearManagedVllmSshBinding(statePath) call independently so one failure does not
prevent older binding paths from being cleared, while preserving the existing
behavior of swallowing rollback errors and retaining the persistence error.
src/lib/inference/serving/managed-cluster-ssh-binding.test-support.ts (1)

5-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused retargetManagedVllmSshBindingFixture alias. No repository consumer uses it. Add the alias when a test requires it.

🤖 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/inference/serving/managed-cluster-ssh-binding.test-support.ts` around
lines 5 - 8, Remove the unused retargetManagedVllmSshBindingFixture alias from
the re-export block, while preserving createManagedVllmSshBindingFixture. Do not
add a replacement alias unless a consuming test requires it.

Source: Coding guidelines

managed-inference/schemas/recipe.schema.json (2)

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

Reuse the imageDigest definition for runtime.image.

Line 121 repeats the exact regex that #/$defs/imageDigest declares at line 894. Two copies of a 100-character digest pattern will drift. Reference the definition instead.

♻️ Proposed refactor
             "image": {
-              "type": "string",
-              "pattern": "^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*(?::[0-9]+)?/)?(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*`@sha256`:[0-9a-f]{64}$"
+              "$ref": "`#/`$defs/imageDigest"
             },
🤖 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 `@managed-inference/schemas/recipe.schema.json` around lines 119 - 122, Update
the runtime.image schema definition to reference the existing
`#/`$defs/imageDigest definition instead of duplicating its pattern regex,
preserving the same validation behavior and removing the inline pattern.

844-860: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Reject contradictory tmpfs options in the schema. The materializer checks allowed values and duplicates but forwards both rw/ro or exec/noexec to Docker. Add mutual-exclusion constraints for these option pairs.

🤖 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 `@managed-inference/schemas/recipe.schema.json` around lines 844 - 860, Update
the tmpfs options schema around the options array to reject configurations
containing both mutually exclusive values: rw with ro, and exec with noexec.
Preserve the existing enum, maxItems, and uniqueItems constraints while adding
schema-level validation for each conflicting pair.
src/lib/inference/serving/catalog.ts (2)

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

Preserve the adapter validation message.

The catch block replaces every thrown error with a fixed string. A maintainer who breaks a recipe then sees "does not satisfy its registered adapter contract" with no cause. Include the original message.

♻️ Proposed fix
-  } catch {
-    registrationError = "does not satisfy its registered adapter contract";
+  } catch (error) {
+    registrationError = `does not satisfy its registered adapter contract: ${
+      error instanceof Error ? error.message : String(error)
+    }`;
   }
🤖 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/inference/serving/catalog.ts` around lines 267 - 275, Update the
catch block around registries.validateRecipe in the recipe registration
validation flow to capture the thrown error and preserve its original message in
registrationError, while retaining the fixed fallback only when the thrown value
has no usable message. Keep the existing ServingCatalogValidationError format
and successful validation behavior unchanged.

456-472: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Unregistered binding qualifications pass compilation.

Line 460 resolves descriptor with optional chaining. When topologyQualifications has no entry for qualificationId@schemaVersion, the output and outputSchema comparisons are skipped and the binding is accepted. validatePresetRequirements validates registry membership only for topologyRequirement entries, not for bindings. The mismatch surfaces later in assertCatalogSelection in src/lib/inference/serving/managed-cluster-materialize.ts, which fails at selection time instead of at compile time. Reject an unregistered binding qualification here so the catalog fails closed during compilation.

♻️ Proposed fix
     const key = `${expected.qualificationId}@${String(expected.schemaVersion)}`;
     const descriptor = registries.topologyQualifications?.get(key);
+    if (registries.topologyQualifications && !descriptor) {
+      throw new ServingCatalogValidationError(
+        `Preset ${preset.metadata.id} binding ${name} references unknown topology qualification ${key}.`,
+      );
+    }
     if (
🤖 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/inference/serving/catalog.ts` around lines 456 - 472, Update the
binding validation loop over recipeBindings to reject any binding whose
`${qualificationId}@${schemaVersion}` key is absent from
registries.topologyQualifications. Make the descriptor lookup mandatory before
comparing actual and expected outputs, while preserving the existing mismatch
validation and ServingCatalogValidationError behavior.
src/lib/inference/serving/adapter-registry.ts (1)

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

Align the replacement-text bounds with the schema.

Lines 340 and 342 bound expectedText and replacementText at 65_536. managed-inference/schemas/recipe.schema.json bounds both at 16384 at lines 810 and 815. Schema validation runs first, so the checks here can never fire and the two files now hold different limits. Use one value, and reference the schema limit in a comment or a shared constant.

🤖 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/inference/serving/adapter-registry.ts` around lines 337 - 345, Update
the replacement validation around expectedText and replacementText to use the
schema’s 16,384-character maximum instead of 65,536, keeping both checks aligned
with the recipe schema. Reference the shared limit or document the
schema-derived value with a comment so the validation rules cannot diverge.
src/lib/inference/serving/generate-catalog.ts (1)

67-69: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Make catalog:check compare the generated serialization with dist/managed-inference/catalog.json when the artifact exists. The current check only validates source compilation, while runtime and packaging consume the ignored dist artifact.

🤖 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/inference/serving/generate-catalog.ts` around lines 67 - 69, Update
checkServingCatalog to compare the generated catalog serialization from
compileServingCatalog with dist/managed-inference/catalog.json when that
artifact exists, while retaining source compilation validation and handling a
missing artifact without failing the check.
managed-inference/schemas/preset.schema.json (1)

235-278: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Constrain value for between. The resolver accepts between only with exactly two numeric bounds and returns false for other shapes, including reversed bounds. The schema and catalog checks still allow these invalid requirements to pass and remain silently unmatched. Add an if/then rule for two numeric items and validate bound ordering semantically.

🤖 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 `@managed-inference/schemas/preset.schema.json` around lines 235 - 278,
Constrain the value branch when operator is between so it must be an array
containing exactly two numeric items. Update the catalog validation for these
requirements to reject reversed bounds as well as any non-numeric or incorrectly
sized value, matching resolver behavior and preventing silently unmatched
entries.
🤖 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/inference/serving/adapter-registry.ts`:
- Around line 511-518: Update getManagedInferenceServingCatalogRegistries so its
receipts and readinessContracts sets include the authoritative llama.cpp
contract references required by llama.cpp recipes, ensuring compilation reaches
validateLlamaCppPreset. If those contracts are not available, remove the
llama.cpp-only schema requirements instead of registering nonexistent
references; preserve the existing vLLM behavior.

In `@src/lib/inference/serving/catalog-loader.ts`:
- Around line 39-42: Update the catalog-loading flow around
parseCompiledServingCatalogJson to validate the parsed result against the
managed catalog schema, including required spec.requirements,
spec.plan.bindings, spec.bindings, and execution.nodeCount fields, before
assigning it to loadedCatalog. Reject invalid catalogs rather than relying on
the CompiledManagedInferenceCatalog type assertion, while preserving
immutableManagedInferenceCopy for valid results.

In `@src/lib/inference/serving/catalog.ts`:
- Around line 325-333: Populate SERVING_READINESS_REGISTRY with observation
entries for the readiness signals used by serving presets, including each
signal’s valueType and required role metadata. Ensure the declared roles match
what validateLlamaCppPreset looks up so comparison requirements resolve
successfully instead of reporting “no value type” or missing roles.

In `@src/lib/inference/vllm-managed-cluster-runtime-receipt.test.ts`:
- Around line 300-309: Add explicit definedness assertions for currentPreset and
currentRecipe immediately after their find calls in the test, before invoking
managedInferenceDigest; retain the existing digest comparisons after these
assertions so the test verifies both lookups succeeded and the expected digests
match.

---

Nitpick comments:
In `@managed-inference/schemas/preset.schema.json`:
- Around line 235-278: Constrain the value branch when operator is between so it
must be an array containing exactly two numeric items. Update the catalog
validation for these requirements to reject reversed bounds as well as any
non-numeric or incorrectly sized value, matching resolver behavior and
preventing silently unmatched entries.

In `@managed-inference/schemas/recipe.schema.json`:
- Around line 119-122: Update the runtime.image schema definition to reference
the existing `#/`$defs/imageDigest definition instead of duplicating its pattern
regex, preserving the same validation behavior and removing the inline pattern.
- Around line 844-860: Update the tmpfs options schema around the options array
to reject configurations containing both mutually exclusive values: rw with ro,
and exec with noexec. Preserve the existing enum, maxItems, and uniqueItems
constraints while adding schema-level validation for each conflicting pair.

In `@src/lib/inference/serving/adapter-registry.ts`:
- Around line 337-345: Update the replacement validation around expectedText and
replacementText to use the schema’s 16,384-character maximum instead of 65,536,
keeping both checks aligned with the recipe schema. Reference the shared limit
or document the schema-derived value with a comment so the validation rules
cannot diverge.

In `@src/lib/inference/serving/catalog.ts`:
- Around line 267-275: Update the catch block around registries.validateRecipe
in the recipe registration validation flow to capture the thrown error and
preserve its original message in registrationError, while retaining the fixed
fallback only when the thrown value has no usable message. Keep the existing
ServingCatalogValidationError format and successful validation behavior
unchanged.
- Around line 456-472: Update the binding validation loop over recipeBindings to
reject any binding whose `${qualificationId}@${schemaVersion}` key is absent
from registries.topologyQualifications. Make the descriptor lookup mandatory
before comparing actual and expected outputs, while preserving the existing
mismatch validation and ServingCatalogValidationError behavior.

In `@src/lib/inference/serving/generate-catalog.ts`:
- Around line 67-69: Update checkServingCatalog to compare the generated catalog
serialization from compileServingCatalog with
dist/managed-inference/catalog.json when that artifact exists, while retaining
source compilation validation and handling a missing artifact without failing
the check.

In `@src/lib/inference/serving/managed-cluster-runtime-receipt.ts`:
- Around line 592-598: Move the try/catch from around the entire rollback loop
into the loop over createdBindingStatePaths in the receipt persistence flow.
Wrap each clearManagedVllmSshBinding(statePath) call independently so one
failure does not prevent older binding paths from being cleared, while
preserving the existing behavior of swallowing rollback errors and retaining the
persistence error.

In `@src/lib/inference/serving/managed-cluster-ssh-binding.test-support.ts`:
- Around line 5-8: Remove the unused retargetManagedVllmSshBindingFixture alias
from the re-export block, while preserving createManagedVllmSshBindingFixture.
Do not add a replacement alias unless a consuming test requires it.

In `@src/lib/inference/serving/managed-cluster-ssh-binding.ts`:
- Around line 9-13: Update the module comment above the managed vLLM SSH binding
aliases to include a GitHub issue or PR link tracking migration of the legacy
Station path to the serving catalog, explicitly identify the serving catalog as
authoritative, and state observable criteria for deleting this compatibility
module, including completion of migration and confirmation that no external or
persisted-data consumers require the legacy name.

In `@src/lib/inference/vllm-managed-cluster-runtime-receipt.test.ts`:
- Around line 191-192: Update snapshot to accept the compiled plan and derive
listeningPorts from plan.apiPort and plan.masterPort instead of hard-coded 8000
and 25000 values. Remove the unreachable rolePlan.endpoint guard, preserve the
head/worker port structure, and update all snapshot callers to pass the plan
through.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f5963ebf-19f5-4924-921d-db451b32475f

📥 Commits

Reviewing files that changed from the base of the PR and between 475f5bd and 3f7c4fe.

📒 Files selected for processing (50)
  • .pre-commit-config.yaml
  • ci/source-architecture-budget.json
  • docs/index.yml
  • docs/inference/set-up-vllm-on-two-dgx-sparks.mdx
  • docs/manage-sandboxes/uninstall-nemoclaw.mdx
  • docs/reference/host-files-and-state.mdx
  • managed-inference/recipes/vllm.deepseek-v4-flash-0731.spark-dual.v1.yaml
  • managed-inference/schemas/preset.schema.json
  • managed-inference/schemas/recipe.schema.json
  • package.json
  • scripts/install.sh
  • src/lib/actions/uninstall/run-plan-dual-station.test.ts
  • src/lib/actions/uninstall/run-plan.ts
  • src/lib/inference/local.ts
  • src/lib/inference/serving/adapter-registry.test.ts
  • src/lib/inference/serving/adapter-registry.ts
  • src/lib/inference/serving/catalog-loader.ts
  • src/lib/inference/serving/catalog.ts
  • src/lib/inference/serving/generate-catalog.ts
  • src/lib/inference/serving/managed-cluster-discovery-production.ts
  • src/lib/inference/serving/managed-cluster-discovery.test.ts
  • src/lib/inference/serving/managed-cluster-discovery.ts
  • src/lib/inference/serving/managed-cluster-executor.test.ts
  • src/lib/inference/serving/managed-cluster-executor.ts
  • src/lib/inference/serving/managed-cluster-fixture.test-support.ts
  • src/lib/inference/serving/managed-cluster-identifiers.ts
  • src/lib/inference/serving/managed-cluster-installer.test.ts
  • src/lib/inference/serving/managed-cluster-installer.ts
  • src/lib/inference/serving/managed-cluster-lifecycle.test.ts
  • src/lib/inference/serving/managed-cluster-lifecycle.ts
  • src/lib/inference/serving/managed-cluster-materialize.test.ts
  • src/lib/inference/serving/managed-cluster-materialize.ts
  • src/lib/inference/serving/managed-cluster-preparation.test.ts
  • src/lib/inference/serving/managed-cluster-preparation.ts
  • src/lib/inference/serving/managed-cluster-runtime-receipt-path.ts
  • src/lib/inference/serving/managed-cluster-runtime-receipt.ts
  • src/lib/inference/serving/managed-cluster-ssh-binding.test-support.ts
  • src/lib/inference/serving/managed-cluster-ssh-binding.ts
  • src/lib/inference/serving/managed-cluster-topology.test.ts
  • src/lib/inference/serving/managed-cluster-topology.ts
  • src/lib/inference/serving/managed-runtime-receipts.ts
  • src/lib/inference/serving/resolver.test.ts
  • src/lib/inference/serving/resolver.ts
  • src/lib/inference/serving/types.ts
  • src/lib/inference/vllm-api-key.ts
  • src/lib/inference/vllm-docker-env.ts
  • src/lib/inference/vllm-dual-station.test.ts
  • src/lib/inference/vllm-managed-cluster-runtime-receipt.test.ts
  • src/lib/inference/vllm.ts
  • src/lib/onboard.ts
💤 Files with no reviewable changes (1)
  • src/lib/inference/serving/managed-runtime-receipts.ts
🚧 Files skipped from review as they are similar to previous changes (31)
  • src/lib/onboard.ts
  • scripts/install.sh
  • ci/source-architecture-budget.json
  • docs/index.yml
  • src/lib/inference/serving/managed-cluster-preparation.test.ts
  • src/lib/inference/serving/managed-cluster-runtime-receipt-path.ts
  • src/lib/inference/serving/managed-cluster-installer.test.ts
  • src/lib/inference/serving/managed-cluster-lifecycle.test.ts
  • .pre-commit-config.yaml
  • docs/inference/set-up-vllm-on-two-dgx-sparks.mdx
  • src/lib/inference/vllm-dual-station.test.ts
  • src/lib/inference/local.ts
  • src/lib/inference/serving/managed-cluster-installer.ts
  • src/lib/inference/serving/managed-cluster-preparation.ts
  • src/lib/inference/serving/resolver.ts
  • src/lib/inference/vllm.ts
  • src/lib/inference/serving/managed-cluster-topology.ts
  • src/lib/actions/uninstall/run-plan.ts
  • src/lib/inference/serving/managed-cluster-discovery.ts
  • src/lib/inference/vllm-docker-env.ts
  • src/lib/inference/serving/adapter-registry.test.ts
  • src/lib/inference/serving/managed-cluster-fixture.test-support.ts
  • managed-inference/recipes/vllm.deepseek-v4-flash-0731.spark-dual.v1.yaml
  • src/lib/inference/serving/managed-cluster-lifecycle.ts
  • src/lib/inference/serving/managed-cluster-topology.test.ts
  • src/lib/inference/serving/managed-cluster-discovery-production.ts
  • src/lib/inference/serving/managed-cluster-discovery.test.ts
  • src/lib/inference/serving/managed-cluster-materialize.ts
  • src/lib/inference/serving/resolver.test.ts
  • src/lib/inference/serving/managed-cluster-executor.test.ts
  • src/lib/inference/serving/managed-cluster-executor.ts

Comment thread src/lib/inference/serving/adapter-registry.ts
Comment thread src/lib/inference/serving/catalog-loader.ts
Comment thread src/lib/inference/serving/catalog.ts
Comment thread src/lib/inference/vllm-managed-cluster-runtime-receipt.test.ts
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

ericksoa and others added 4 commits August 4, 2026 00:47
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@cjagwani

cjagwani commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Product-scope blocker for head 655a464: this PR creates a new Experimental Express integration, canonical DGX Spark documentation, managed serving profile, and reusable cluster lifecycle/materializer contracts, but GitHub reports no closing or connected issue and I could not find an accepted issue/design for the dual-DGX-Spark surface. The PR body also leaves physical two-system hardware validation unchecked. Green unit/CI evidence is not sufficient to approve a new supported product surface. Please link the accepted issue or design decision that defines scope, owner, lifecycle, compatibility/security boundaries, and validation expectations, then attach exact-head physical two-system E2E evidence—or record an explicit maintainer waiver that states what may ship as Experimental and what remains gated. The documentation-writer receipt is bound to 655a464, so it remains current only while that is the head.

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed commit 3191c0030. This PR creates a new automatic dual-DGX Spark Express profile, managed-cluster runtime, YAML extension contract, uninstall lifecycle, and canonical user documentation. The PR body does not link an accepted issue or design decision that defines product ownership, lifecycle, compatibility, security, and physical qualification. Calling the path Experimental does not satisfy the product-scope gate. Add the accepted product decision or stop this canonical integration work.

The PR also states that physical two-system end-to-end validation is incomplete. That evidence is required before this hardware-dependent profile can be approved. At 79 files and roughly 19,000 added lines, the change combines catalog/schema foundations, generic adapters, discovery, execution, lifecycle, receipts, uninstall, one concrete profile, and documentation. Provide an accepted sequence with independently reviewable boundaries and qualification stop conditions, or an approved reason this must land atomically.

The maintainer update from main is complete and CI is running. Current checks and the documentation-writer receipt must be regenerated for this commit after the scope and qualification gates are resolved.

@ericksoa
ericksoa merged commit 3835080 into main Aug 4, 2026
44 of 51 checks passed
@ericksoa
ericksoa deleted the feat/dual-dgx-spark-express branch August 4, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: docs Documentation, examples, guides, or docs build area: inference Inference routing, serving, model selection, or outputs area: install Install, setup, prerequisites, or uninstall flow area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow feature PR adds or expands user-visible functionality platform: dgx-spark Affects DGX Spark hardware or workflows provider: vllm vLLM local or hosted provider behavior v0.0.102 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants