Skip to content

feat(local-ai): route RTX Spark unified-memory SKUs through NVIDIA's fixed recipe table - #1497

Open
joelagnel wants to merge 12 commits into
openclaw:mainfrom
joelagnel:feat/rtx-spark-recipes
Open

joelagnel wants to merge 12 commits into
openclaw:mainfrom
joelagnel:feat/rtx-spark-recipes

Conversation

@joelagnel

@joelagnel joelagnel commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1496

Routes RTX Spark unified-memory SKUs through NVIDIA's fixed recipe table instead of
the generic capacity fit-test, and extends the catalog, manifest, and installer so the
recipes NVIDIA validated can actually be expressed and acquired.

Discrete GPU machines keep their existing behavior. The documented "GPU names and
CPU/GPU SKU pairings are not part of qualification" invariant stays true for every
non-Spark device: the Spark path is a separate decision layer, and an explicitly
requested model still uses the same capacity fit-test everywhere.

What changed

  • Detect RTX Spark from the driver-reported GPU name, and route its default pick
    through RtxSparkInferenceSelector using geometric-midpoint SKU bands.
  • New SpeculativeDecodingMode.None and DraftDFlash, plus an optional separately
    pinned LocalModelRunRecipe.DraftWeights (DFlash's draft checkpoint lives in a
    different Hugging Face repository than the target weights).
  • New manifest schema 5 (AdditionalModelAssets / AdditionalModelPaths) so extra
    split-GGUF shards and the draft checkpoint are recorded and re-verified in the same
    hub cache. Schema 4 receipts are written and serialized exactly as before.
  • HuggingFaceModelInstaller.InstallAdditionalAssetAsync acquires those assets with
    the same resumable download, hash verification, and promotion flow as the primary
    weights.
  • BuildPreset branches on the recipe's speculative decoding mode and emits a real
    spec-draft-model path for DFlash.
  • Runtime pin bumped b10655 to b11026 (x64 moves to CUDA 13.4, matching arm64).

Required proof pools

  • windows-wsl-dgx-blackwell: RTX Spark SKU detection, recipe routing, multi-asset acquisition, draft file identity at native load, native llama-server serving, and pre-bump install upgrade. Validated on a real 48 GB RTX Spark (arm64) and a native x64 RTX 5090.
  • windows-winui-interactive: the Local AI model picker size text and the setup review download list changed. Captured live on a real 48 GB RTX Spark; screenshot in the PR comments.

Validation

Real 48 GB RTX Spark (arm64 runtime, CUDA 13), current head, repository-required closeout:
./build.ps1 succeeded with exit code 0 ("All builds succeeded"), including proof-pool and
documentation-flow validation.

Suite Result
OpenClaw.Tray.Tests 3069 passed, 0 failed
OpenClaw.Shared.Tests 4113 passed, 0 failed
OpenClaw.Connection.Tests 803 passed, 0 failed
OpenClaw.SetupEngine.Tests 1194 passed, 0 failed

CI is green on every unit and validation job at this head. The remaining Revocation recovery E2E, Setup and connect E2E, Network recovery E2E, and CI Gate failures reproduce
identically on main and are independent of this branch.

Real behavior proof

Ground truth for the SKU boundary came from the real device rather than an assumption.
A direct nvcuda.dll probe (cuInit / cuDeviceGetCount / cuMemGetInfo_v2) on the
48 GB RTX Spark reports a total of 48,585,498,624 bytes, which is what
CudaHostHardwareProbe reads and what the 48 GB band is anchored to. nvidia-smi
reports 24,512 MiB on the same device via NVML, which is the under-report that #1422
already established cannot be trusted here.

Changed UI strings, rendered from the real catalog at current head on the RTX Spark
box:

== Local AI model picker entries (size = LocalModelCatalog.TotalDownloadSizeBytes) ==
  Qwen3.8 27B (UD-Q4_K_M)                  (16.46 GB)   [before: 16.46 GB]
  Qwen3.6 35B-A3B (UD-Q4_K_M)              (22.66 GB)   [before: 22.66 GB]
  Qwen3.6 27B (Q4_K_M)                     (17.11 GB)   [before: 17.11 GB]
  Qwen3.6 35B-A3B (UD-IQ4_XS)              (18.21 GB)   [before: 18.21 GB]
  Qwen3.8 27B (UD-Q4_K_M, DFlash)          (17.61 GB)   [before: 16.46 GB]

== Setup review "exact commands" download lines per recipe ==
  qwen3.8-27b-mtp-ud-q4-k-m:      1 download line
  qwen3.6-35b-a3b-mtp-q4-k-m:     1 download line
  qwen3.6-27b-mtp-q4-k-m:         1 download line
  qwen3.6-35b-a3b-mtp-ud-iq4-xs:  1 download line
  qwen3.8-27b-dflash-ud-q4-k-m:   2 download lines   (adds the draft checkpoint)

Every pre-existing recipe renders byte-identical to before. Only the DFlash recipe
changes, which is the defect this fixes: its draft checkpoint was not disclosed at all.

The 96 GB Qwen3.8 Flash-Next recipe is not part of this PR. Split-GGUF loading needs the
shard filenames while the hub cache's integrity model supplies a content-addressed path,
and reconciling the two needs install-flow surface that belongs in its own change. Tracked
in #1508.

Multi-asset acquisition and native serving are proven on the device. Running the production
setup steps on the 48 GB Spark installs the DFlash recipe, writes a schema-5 receipt recording
the draft checkpoint at its pinned size and digest, resolves that asset from its own Hugging
Face repository into the shared hub cache, wires spec-draft-model to the verified path, and
llama-server serves 32 generated tokens from the recipe.

Upgrading an existing pre-bump install is proven on the same device. A receipt rewritten to the
retired b10655 runtime is retained as the upgrade baseline instead of ending setup: the new
runtime installs, the verified model is reused rather than re-downloaded, the receipt is
replaced in place at b11026, and the upgraded install serves generated tokens. This is shown
against a receipt in the exact shape a pre-bump release could have written: schema 4, the
pre-existing default model, and no additional-asset fields. Schema 4 is preserved through the
upgrade, so an install made before this change is not migrated onto the new schema. Full
redacted traces are in the PR comments.

Native x64 is hardware-verified at this head: on an RTX 5090 the production setup steps install
the b11026 CUDA 13.4 x64 runtime, acquire the model, launch the router, and generate tokens. The
pinned binary reports build 11026, commit b49650adb, matching LlamaRuntimeCatalog.ReleaseCommitSha.

The draft checkpoint's file identity is proven at the native boundary. An unchanged verified
draft loads and serves; a draft whose snapshot entry is replaced after verification is rejected
at reconcile against its pinned size and digest, so llama-server is never launched and the
replaced bytes never reach native I/O. At launch the preset receives the handle-resolved
physical path from that same verification rather than the receipt path.

The b10655 to b11026 upgrade is proven on both architectures: on native x64 (RTX 5090) and on
arm64 (RTX Spark). In each case the pre-bump receipt is retained as the upgrade baseline, the new
runtime installs, the verified model is reused rather than re-downloaded, the receipt is replaced
in place at b11026, and the upgraded install serves generated tokens. Full redacted traces are in
the PR comments.

32 GB RTX Spark behavior

RtxSparkInferenceSelector.SelectDefault returns no recommendation below the 32/48 GB geometric
midpoint, surfaced as LocalInferenceSelectionFailureCode.NotRecommendedForSku rather than
InsufficientGpuMemory, so a product rule is never reported as a capacity failure. In the setup UI
the Local AI option is presented as unavailable and setup proceeds without it. In the pipeline with
Local AI explicitly enabled, PreflightLocalAiHardwareStep returns a terminal result; that step is
gated by CanSkip => !ctx.Config.LocalAi.Enabled. A 32 GB Spark is excluded from selection rather
than failing the host, so a discrete GPU beside it still qualifies through the generic path.

This follows NVIDIA's fixed SKU table, which specifies no local model for the 32 GB SKU. Adopting it
as default setup behavior is a maintainer decision.

Notes for reviewers

The Spark recipes, including DFlash, are reachable from the model picker on
sufficiently large non-Spark GPUs, because PopulateLocalAiModels evaluates each
catalog entry as an explicit request and explicit requests intentionally bypass SKU
routing. The default pick is unaffected and is covered by a regression test. Called
out as known and deliberate rather than silently changed.

Adds GpuInfo.IsRtxSpark, additive only. Groundwork for routing RTX
Spark's unified-memory SKUs through a fixed recipe table instead of
the generic capacity fit-test.
@joelagnel

Copy link
Copy Markdown
Contributor Author

/review

@clawsweeper

clawsweeper Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 23, 2026
@clawsweeper

clawsweeper Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex review: blocked before merge. Reviewed September 25, 2026, 7:30 PM ET / 23:30 UTC (Revision 26).

ClawSweeper review

What this changes

The branch adds fixed RTX Spark model recommendations, verified downloads for a separate DFlash draft model, Local AI receipt support for that asset, setup and tray changes, and a newer pinned llama-server runtime.

Merge readiness

⛔ Blocked before merge - 5 items remain

Current main and the latest release still use generic model selection for RTX Spark. A patch-equivalent maintainer replacement is open at #1520, but the change has not landed on main and this repository’s close policy does not permit closing this PR yet. The prior 32 GB setup-copy finding also remains.

Priority: P2
Reviewed head: 24ff075a215226aa1b9092c40ffa8e0e49ac25d0
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) Real hardware and upgrade evidence is strong, while the unresolved 32 GB diagnostic and downgrade contract limit merge readiness.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The changed Spark selection, asset acquisition, receipt, and native launch paths are tied to a visible Spark setup screenshot and redacted production-step traces showing after-change DFlash serving and schema-4 upgrades on real Spark and x64 hardware. The screenshot is from an earlier patch head and does not show the remaining 32 GB diagnostic. Existing-state upgrade compatibility is supported; older-build downgrade behavior remains a separate decision.
Patch quality 🦐 gold shrimp (3/6) 1 actionable review finding remain.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The changed Spark selection, asset acquisition, receipt, and native launch paths are tied to a visible Spark setup screenshot and redacted production-step traces showing after-change DFlash serving and schema-4 upgrades on real Spark and x64 hardware. The screenshot is from an earlier patch head and does not show the remaining 32 GB diagnostic. Existing-state upgrade compatibility is supported; older-build downgrade behavior remains a separate decision.
Evidence reviewed 9 items Current-main and release gap: Main and the v2026.9.4 release choose defaults by catalog priority and capacity without a Spark SKU branch.
Introduced selection: The introduced patch selects a Spark recipe from complete GPU facts and binds that choice to the selected adapter.
Prior finding persists: The new NotRecommendedForSku result falls through the locale-neutral diagnostic to Unknown; a regression test explicitly records that outcome. Setup and tray then render generic requirements copy.
Findings 1 actionable finding [P2] Explain the 32 GB no-default result accurately
Security None None.

How this fits together

Windows Local AI reads GPU facts to choose a model and runtime. Setup downloads pinned files and records an installation receipt, which the runtime verifies before launching llama-server.

flowchart LR
A[GPU facts] --> B[Model selection]
B --> C[Fixed Spark recipe or generic choice]
C --> D[Setup downloads]
D --> E[Installation receipt]
E --> F[File verification]
F --> G[Local inference]
Loading

Decision needed

Question Recommendation
Is losing access to a schema-5 DFlash installation when a user runs an older app build an accepted downgrade contract for the Spark release? Accept and document recovery: Allow older builds to report no usable DFlash install and document the supported path back to the newer build.

Why: The new receipt is intentionally unreadable to older builds; source and upgrade proof establish forward compatibility, but only maintainers can accept the downgrade behavior and its user-facing recovery obligation.

Before merge

  • Explain the 32 GB no-default result accurately (P2) - The new NotRecommendedForSku result reaches GetUnavailableReason, which has no matching arm and returns Unknown; setup and tray consequently show generic hardware-requirements copy for a deliberate no-default policy. Add a distinct localized reason and update the test that currently expects Unknown. This is the unresolved finding from the prior review.
  • Resolve merge risk (P1) - An older app build cannot read a new schema-5 DFlash receipt. Maintainers need to accept and document the resulting loss of a usable Local AI installation on downgrade, or provide a safe recovery path before landing.
  • Resolve merge risk (P1) - A fresh 32 GB Spark receives a deliberate no-default result, but setup and tray describe it as a generic requirements failure; users cannot tell that the hardware policy, rather than missing prerequisites, disabled Local AI.
  • Complete next step (P2) - Carry the 32 GB diagnostic fix into the maintainer replacement, decide the schema-5 downgrade contract, and land only one Spark PR.
  • Resolve maintainer decision - Resolve the maintainer decision shown above before merge.

Findings

  • [P2] Explain the 32 GB no-default result accurately — src/OpenClaw.Shared/Inference/Catalog/LocalInferenceSelector.cs:20
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Code and test growth production +1156 net lines, tests +883 net lines The production growth spans selection, downloads, receipts, and native launch, with substantial focused regression coverage.
Changed files 25 files The recipe crosses shared selection, setup, connection, and tray boundaries.

Root-cause cluster

Relationship: superseded
Canonical: #1520
Summary: The open maintainer replacement claims a patch-equivalent landing stack for the same Spark request; the original branch remains open pending the permitted close boundary and should not merge alongside it.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge-risk options

Maintainer options:

  1. Document schema-5 downgrade recovery (recommended)
    Record the expected older-build failure and an in-product recovery route while retaining the verified schema-4 upgrade behavior.
  2. Accept older-build loss of Local AI
    Explicitly accept that an older build treats a DFlash installation as unusable until the newer build is restored.

Technical review

Best possible solution:

Land one Spark recipe branch after giving the 32 GB no-default result accurate localized setup and tray copy and recording the older-build recovery contract; keep split-GGUF support in #1508.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a feature request. Current main’s generic selection is source-verifiable, and the linked hardware traces exercise the proposed Spark path.

Is this the best way to solve the issue?

Mostly yes: a separate Spark selector preserves the discrete-GPU path, and pinned asset verification supports DFlash. The 32 GB diagnostic and downgrade contract still need resolution.

Full review comments:

  • [P2] Explain the 32 GB no-default result accurately — src/OpenClaw.Shared/Inference/Catalog/LocalInferenceSelector.cs:20
    The new NotRecommendedForSku result reaches GetUnavailableReason, which has no matching arm and returns Unknown; setup and tray consequently show generic hardware-requirements copy for a deliberate no-default policy. Add a distinct localized reason and update the test that currently expects Unknown. This is the unresolved finding from the prior review.
    Confidence: 0.96

Overall correctness: patch is incorrect
Overall confidence: 0.94

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 5a59535216ee.

Labels

Label changes:

No label changes.

Label justifications:

  • P2: This is a bounded hardware recommendation improvement with a remaining user-facing diagnostic defect.
  • merge-risk: 🚨 compatibility: Schema-5 DFlash receipts and the runtime pin affect older-build access and installation upgrades.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The changed Spark selection, asset acquisition, receipt, and native launch paths are tied to a visible Spark setup screenshot and redacted production-step traces showing after-change DFlash serving and schema-4 upgrades on real Spark and x64 hardware. The screenshot is from an earlier patch head and does not show the remaining 32 GB diagnostic. Existing-state upgrade compatibility is supported; older-build downgrade behavior remains a separate decision.
  • proof: sufficient: Contributor real behavior proof is sufficient. The changed Spark selection, asset acquisition, receipt, and native launch paths are tied to a visible Spark setup screenshot and redacted production-step traces showing after-change DFlash serving and schema-4 upgrades on real Spark and x64 hardware. The screenshot is from an earlier patch head and does not show the remaining 32 GB diagnostic. Existing-state upgrade compatibility is supported; older-build downgrade behavior remains a separate decision.

Evidence

What I checked:

Likely related people:

  • Joel: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Scott Hanselman: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • karkarl: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Map the 32 GB no-default result to accurate localized setup and tray copy.
  • Record a maintainer decision and recovery guidance for older builds reading schema-5 receipts.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (25 earlier review cycles; latest 8 shown)
  • reviewed 2026-09-25T09:32:33.400Z sha 362e728 :: blocked before merge. :: [P2] Explain the 32 GB no-default result accurately
  • reviewed 2026-09-25T11:07:22.159Z sha 362e728 :: blocked before merge. :: [P2] Explain the 32 GB no-default result accurately
  • reviewed 2026-09-25T11:20:55.068Z sha 362e728 :: blocked before merge. :: [P2] Explain the 32 GB no-default result accurately
  • reviewed 2026-09-25T12:43:57.445Z sha 362e728 :: blocked before merge. :: [P2] Explain the 32 GB no-default result accurately
  • reviewed 2026-09-25T13:03:24.784Z sha 362e728 :: blocked before merge. :: [P1] Recompute availability after the managed model ID arrives | [P2] Explain the 32 GB no-default result in setup and tray
  • reviewed 2026-09-25T17:07:22.581Z sha 362e728 :: blocked before merge. :: [P1] Recompute availability when the managed model ID arrives | [P2] Explain the 32 GB no-default result accurately
  • reviewed 2026-09-25T21:20:04.762Z sha 24ff075 :: blocked before merge. :: [P2] Explain the 32 GB no-default result accurately
  • reviewed 2026-09-25T22:55:50.055Z sha 24ff075 :: blocked before merge. :: [P2] Explain the 32 GB no-default result accurately

@joelagnel
joelagnel force-pushed the feat/rtx-spark-recipes branch from 13f474d to b9c116b Compare September 23, 2026 23:11
@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. labels Sep 23, 2026
@joelagnel
joelagnel force-pushed the feat/rtx-spark-recipes branch from b9c116b to f94cf90 Compare September 23, 2026 23:47
@joelagnel

Copy link
Copy Markdown
Contributor Author

Real behavior proof: RTX Spark SKU routing on real hardware

Captured from the live WinUI setup flow on a real 48 GB RTX Spark at current head (f94cf905), isolated data dir, app window only.

RTX Spark 48GB Local AI setup review

This shows the full changed path working end to end:

  • Device detected: 45.2 GiB CUDA-visible on NVIDIA RTX Spark N1X (6144-core Blackwell RTX GPU), read through cuMemGetInfo uncapped.
  • SKU routing applied: the 48 GB SKU resolves to Qwen 3.6 35B-A3B, exactly what NVIDIA's table specifies for this tier.
  • Pinned profile honoured: 96K context is the SKU's fixed 98,304-token profile, not a generic best-fit. This is the value that the setup round-trip fix restored; before that fix this screen would have shown the generic profile instead.
  • Download disclosure: Qwen 3.6 35B-A3B, 18.2 GB from Hugging Face comes from TotalDownloadSizeBytes, covering weights plus any shards and draft checkpoint.

Validation

Real 48 GB RTX Spark (arm64 runtime, CUDA 13), current head, dotnet build openclaw-windows-node.slnx -c Debug clean with 0 errors.

Suite Result
OpenClaw.Shared.Tests 4113 passed, 0 failed
OpenClaw.Connection.Tests 803 passed, 0 failed
OpenClaw.SetupEngine.Tests 1193 passed, 0 failed

Every review finding from the previous passes is fixed and squashed into the commit it belongs to: GPU binding for Spark recipes, retired-runtime launch compatibility across the b10655 bump, hash verification of additional model assets before native load, and preservation of the SKU profile when setup round-trips the recommended model id. Each fix ships with a regression test, and each test was confirmed to fail without its fix.

@joelagnel

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Real behavior proof has been added: a live WinUI capture from a real 48 GB RTX Spark at current head showing Spark detection, the SKU-selected recipe, its pinned 96K context profile, and the download disclosure. The PR body and proof-pool declarations are updated to match. All four prior review findings are fixed and squashed into their originating commits, each with a regression test confirmed to fail without its fix.

@clawsweeper

clawsweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Sep 24, 2026
@joelagnel
joelagnel force-pushed the feat/rtx-spark-recipes branch from f94cf90 to e6ce446 Compare September 24, 2026 01:18
@joelagnel

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

[P1] Keep existing runtime receipts upgradeable during setup - fixed and squashed into build(local-ai): bump managed llama-server runtime to b11026.

Confirmed against the source: ValidateRecipeMatch compared the receipt's EngineVersion and RuntimeId against the current catalog pin, so a b10655 receipt threw InvalidDataException at reconcile, which ReconcileLocalAiInstallationStep converted into a terminal "Run uninstall to remove it before retrying setup." A stale pin is an expected upgrade, not a corrupt install.

The receipt is now validated against the runtime it actually recorded (LlamaRuntimeCatalog.FindInstalled), including its artifact receipts and managed install directory, and a version difference is reported as a pending upgrade instead of throwing. Reconcile hands that case back to the acquirers: the runtime is dropped so the new pin installs, the verified model and its additional assets are preserved so the upgrade does not re-download them, and the prior receipt is retained as the baseline the manifest step replaces in place.

Regression added: Reconciler_UpgradesRetiredRuntimeReceiptInsteadOfFailingSetup. Verified it fails without the fix with exactly the pre-fix error:

System.IO.InvalidDataException : The existing managed Local AI installation does not match
the selected runtime, GPU, and model recipe.

and passes with it, returning Reused: false, RuntimeInstall: null, the model install preserved, and the b10655 receipt retained as the upgrade baseline.

Validation

Real 48 GB RTX Spark, current head e6ce446a, solution build clean with 0 errors.

Suite Result
OpenClaw.Shared.Tests 4113 passed, 0 failed
OpenClaw.Connection.Tests 803 passed, 0 failed
OpenClaw.SetupEngine.Tests 1194 passed, 0 failed

12 commits, largest 374 lines. Every review finding to date is fixed and squashed into its originating commit, each with a regression test confirmed to fail without its fix.

@clawsweeper

clawsweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@joelagnel

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Real-path traces captured on a real 48 GB RTX Spark at current head e6ce446a, running the production setup steps (PreflightLocalAiHardware → ReconcileLocalAiInstallation → AcquireLocalAiRuntime → AcquireLocalAiModel → PersistLocalAiManifest → StartLocalAiRuntime → VerifyLocalAiInference) against the live Hugging Face origin and a real llama-server process. Paths redacted to <HOME>.

1. Multi-asset acquisition and native serving

Installed the DFlash recipe, whose draft checkpoint is a second pinned asset in a different Hugging Face repository than the primary weights.

== Steps ==
  [Success]  preflight-local-ai-hardware: Selected Qwen3.8 27B (UD-Q4_K_M, DFlash) for NVIDIA RTX Spark N1X (6144-core Blackwell RTX GPU).
  [Skipped]  reconcile-local-ai-installation: No completed managed Local AI installation was found.
  [Success]  acquire-local-ai-runtime: Installed llama-server b11026.
  [Success]  acquire-local-ai-model: Downloaded Qwen3.8 27B (UD-Q4_K_M, DFlash) from its pinned Hugging Face revision.
  [Success]  persist-local-ai-manifest: Recorded the verified llama-server and Hugging Face installation.
  [Success]  start-local-ai-runtime: The companion-owned llama-server router is healthy.
  [Success]  verify-local-ai-inference: Verified 32 generated tokens with the selected model.

== Manifest ==
  schemaVersion    = 5
  engineVersion    = b11026
  runtimeId        = b11026-cuda13-arm64
  modelCatalogId   = qwen3.8-27b-dflash-ud-q4-k-m
  contextLength    = 262144
  additionalAsset  = Qwen3.8-27B-DFlash2-Q4_K_M.gguf  1143006816 bytes
                     sha256=1a25c56858e1ebe93f2718ac1d49d1151f9323325c1bbfd6209370f4db131ebd
  additionalPath   = <HOME>\.cache\huggingface\hub\models--z-lab--Qwen3.8-27B-DFlash2-GGUF\
                     snapshots\2d9571f8ce46e151f61c6499c99dee6079e1d610\Qwen3.8-27B-DFlash2-Q4_K_M.gguf

== llama-server preset ==
  model                       = <HOME>\.cache\huggingface\hub\models--unsloth--Qwen3.8-27B-GGUF\
                                snapshots\313447f257f7ebde0b968e4778feef774546ed81\Qwen3.8-27B-UD-Q4_K_M.gguf
  spec-type                   = draft-dflash
  spec-draft-model            = <HOME>\.cache\huggingface\hub\models--z-lab--Qwen3.8-27B-DFlash2-GGUF\
                                snapshots\2d9571f8ce46e151f61c6499c99dee6079e1d610\Qwen3.8-27B-DFlash2-Q4_K_M.gguf
  spec-draft-n-max            = 7
  spec-draft-backend-sampling = true

This exercises the whole added path end to end: schema-5 receipt written with the draft checkpoint recorded at its pinned size and digest, the asset resolved into the shared hub cache from its own repository and revision, spec-draft-model wired to that verified path, and llama-server serving 32 generated tokens from the recipe.

2. Existing pre-bump install upgrading

Took the completed install above, rewrote its receipt to the retired b10655 runtime as a pre-bump install would appear on disk, and reran setup.

== Seeded pre-bump receipt ==
  engineVersion = b10655   runtimeId = b10655-cuda13-arm64

== Steps ==
  [Success]  preflight-local-ai-hardware: Selected Qwen3.8 27B (UD-Q4_K_M, DFlash) for NVIDIA RTX Spark N1X.
  [Skipped]  reconcile-local-ai-installation: The existing Local AI receipt was retained while incomplete assets are repaired.
  [Success]  acquire-local-ai-runtime: Installed llama-server b11026.
  [Skipped]  acquire-local-ai-model: Reusing the verified managed Local AI model.
  [Success]  persist-local-ai-manifest: Recorded the verified llama-server and Hugging Face installation.

== Receipt after upgrade ==
  engineVersion = b11026   runtimeId = b11026-cuda13-arm64

The b10655 receipt is retained as the upgrade baseline rather than ending setup, the new runtime is installed, the verified model and its draft checkpoint are reused instead of being downloaded again, and the receipt is replaced in place at b11026.

Validation

Real 48 GB RTX Spark, head e6ce446a, solution build clean with 0 errors.

Suite Result
OpenClaw.Shared.Tests 4113 passed, 0 failed
OpenClaw.Connection.Tests 803 passed, 0 failed
OpenClaw.SetupEngine.Tests 1194 passed, 0 failed

CI unit suites are green at this head. The remaining Revocation recovery E2E, Setup and connect E2E, Network recovery E2E, and CI Gate failures reproduce identically on main and are independent of this branch.

@clawsweeper

clawsweeper Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. labels Sep 24, 2026
@clawsweeper

clawsweeper Bot commented Sep 25, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@joelagnel

joelagnel commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor Author

@shanselman @karkarl — this is ready for a merge decision at head 362e7283. Consolidating everything here so it can be judged from one comment instead of two days of scrollback.

What it does

Routes RTX Spark unified-memory devices through NVIDIA's fixed SKU table instead of the generic capacity fit-test, in a parallel decision layer that leaves the dGPU path untouched. 12 commits, no fixup commits — every finding from every review cycle is squashed into the commit it belongs to, each with a regression test verified to fail without its fix.

Karen's gates, discharged

Your triage is pinned to head e6ce446a, which is four heads old. Both items you assigned are done since:

  • Native x64 b11026 install/upgrade plus inference proof — hardware-verified on an RTX 5090. The production setup steps install the b11026 CUDA 13.4 x64 runtime, acquire the model, launch the router, and generate tokens. The pinned binary reports build 11026, commit b49650adb, matching LlamaRuntimeCatalog.ReleaseCommitSha. The b10655→b11026 upgrade is proven on both architectures.
  • Explicit documentation of the 32 GB behavior — the PR body now carries a dedicated section covering the selector outcome, the setup-UI presentation, and the pipeline behavior.

A re-triage at 362e7283 would replace the stale NEEDS_HUMAN_TEST verdict.

Scott's P1, fixed

LocalAiPageViewModel.RefreshAvailabilityAsync called device-only Evaluate(hardware), so a lone 32 GB Spark returned NotRecommendedForSku and took CanRetrySetup and CanChangeModel down with IsSetupAvailable. It now reads the managed-install receipt and calls the existing EvaluateForConfiguredAvailability. Your three regression cases are in LocalAiPageViewModelTests; the first and third fail without the fix. ClawSweeper cleared the P1 at 09:33 UTC.

I also confirmed the Change Model route you asked about: ChangeModel() opens onboarding with no seed, and the installed selection survives through config.LocalAi.SelectedModelId, which the Capabilities gate evaluates with the same helper.

Next steps

  1. Approval of the fixed SKU table. Karen called this out as maintainer-owned. Scott has already decided the 32 GB policy (no default recommendation); this is the remaining sign-off on the table as a whole.
  2. NotRecommendedForSku reason copy. It has no arm in LocalInferenceEligibilityDiagnostics.GetUnavailableReason, so it falls through to Unknown and a fresh 32 GB Spark gets generic unavailable copy. A real fix is a new LocalInferenceUnavailableReasonKind plus strings in six locale files, which would have me authoring five translations. Current behavior is pinned by a test. Happy to take it here or as a follow-up.
  3. Spark recipes in the picker on non-Spark GPUs. PopulateLocalAiModels evaluates every catalog entry as an explicit request, and explicit requests intentionally bypass SKU routing, so a sufficiently large dGPU can select the Spark-tuned DFlash recipe. The default pick is unaffected and regression-tested. Flagged as deliberate; say the word if you want it gated.
  4. An unlabeled P2. ClawSweeper moved P1→P2 at 09:33 UTC without publishing any finding text — no review, no inline comment. I asked it to publish and it acked at 10:44 but has not yet. I would rather not guess at it than claim it fixed.

Compatibility

The merge-risk: 🚨 compatibility label points at schema 5. An older build reading a schema-5 receipt throws InvalidDataException at LocalAiManifest.cs:574, and every consumer already catches it — LocalAiSetupSteps.cs:122,143, LocalAiGatewayProviderCoordinator.cs:59,117, CapabilitiesPage.xaml.cs:403. None of those catches are added by this PR; they are all pre-existing, so schema 5 degrades to "no usable install, re-run setup" through exactly the mechanism schema 4 already shipped with. Schema 4 is also preserved through the runtime upgrade, so installs made before this change are not migrated onto the new schema.

CI and follow-ups

Every unit, integration and validation job passes at this head, including Core and CLI tests, Tray, setup, and integration tests and UI, functional, and accessibility tests. The four red checks — CI Gate, Setup and connect E2E, Network recovery E2E, Revocation recovery E2E — reproduce identically on main and are tracked under #1498.

Required local validation at this head on a real 48 GB RTX Spark: ./build.ps1 exit 0, Shared 4119, Tray 3072, Connection 803, SetupEngine 1194, zero failures.

The 96 GB split-GGUF recipe was removed rather than shipped in a state that would either fail to load or weaken hub-cache file identity; that work is tracked in #1508.

@joelagnel

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review — head is unchanged at 362e7283. Since your last pass the PR description was corrected (the "Notes for reviewers" section referenced a 128 GB explicit alternative that is not part of this PR; that recipe was removed and is tracked in #1508) and the open items were consolidated into a single handover comment for the maintainers.

Still outstanding from your side: the P2 you set at 09:33:20Z has no published finding text — no review, no inline comment, nothing on the PR that states what it is. Please include it in this pass so it can be acted on.

@clawsweeper

clawsweeper Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@shanselman shanselman added the status: 🚢 actively landing A maintainer or agent is actively driving this item through implementation, validation, or merge. label Sep 25, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. label Sep 25, 2026
@shanselman

Copy link
Copy Markdown
Collaborator

Maintainer closeout at exact head 362e7283: not mergeable yet.

Blocking source finding: LocalAiPageViewModel.Activate() starts the runtime refresh and then the availability refresh, but RefreshAvailabilityAsync() captures _runtimeSnapshot.ModelId synchronously before the runtime refresh can publish the manifest-backed snapshot. On a first visit after app start, LlamaServerRuntimeService can still expose its initial ModelId = null; a 32 GB Spark is then fixed at NotRecommendedForSku. Applying the later runtime snapshot only raises properties and does not re-run availability, while Recheck is unavailable because this is not a probe error. That leaves Retry Setup and Change Model disabled until the user navigates away and back.

I reproduced this with a temporary ordering regression: initial runtime snapshot had no model, delayed runtime refresh returned a valid managed receipt, and the gated hardware probe completed afterward. The current code failed Assert.True(viewModel.IsLocalAiAvailable) (Expected: True, Actual: False). The temporary test was removed and the worktree is clean. Please add the permanent delayed-refresh regression and recompute availability when the runtime model id changes, or otherwise coordinate availability with the completed runtime refresh.

Validation: ./build.ps1 passed. Focused suites passed: Shared 59; Tray 44; Connection 109 passed/5 skipped; SetupEngine 51 passed/1 skipped. Full suites passed after one known MCP disposal-timing test passed in isolation and on the required rerun: Shared 4,116 passed/35 skipped; Tray 3,072; Connection 799 passed/5 skipped; SetupEngine 1,194 passed/1 skipped. Exact origin/main (273b0182) merge-tree creation is conflict-free (ff3afb4b). Existing Spark/RTX 5090 hardware proof remains applicable because the post-01096aee production delta is only this ViewModel path.

The required CI Gate also remains red and the ruleset blocks merge. Downloaded PR and exact-main E2E artifacts show the same Gateway 2026.9.6 (eb377ac) post-wizard restart refusal in setup, revocation, and network recovery lanes, matching #1498 (Gateway 2026.9.6 blocks WSL setup at guarded post-wizard restart). No branch-only E2E failure was found, but the required gate cannot be waived.

@shanselman shanselman removed the status: 🚢 actively landing A maintainer or agent is actively driving this item through implementation, validation, or merge. label Sep 25, 2026
@clawsweeper clawsweeper Bot added P1 Urgent regression or broken agent/channel workflow affecting real users now. and removed P2 Normal priority bug or improvement with limited blast radius. labels Sep 25, 2026
@bkudiess bkudiess added status: 🚢 actively landing A maintainer or agent is actively driving this item through implementation, validation, or merge. and removed status: 🚢 actively landing A maintainer or agent is actively driving this item through implementation, validation, or merge. labels Sep 25, 2026
RTX Spark's total CUDA-visible memory identifies the physical memory
SKU, not usable capacity the way it does on a discrete GPU, so the
generic priority/fit-test default pick doesn't apply. Adds:

- SpeculativeDecodingMode.None/DraftDFlash and an optional separate
  draft checkpoint (LocalModelRunRecipe.DraftWeights), needed because
  DFlash uses an independently pinned draft GGUF rather than an
  embedded MTP draft layer.
- Three RTX Spark catalog recipes: Qwen3.6-35B-A3B IQ4_XS (48GB SKU),
  and Qwen3.8-27B Q4_K_M with DFlash n=7 (128GB SKU default). The
  64GB-SKU recipe reuses the existing default model's
  ctx-131072-q8_0 profile -- no new catalog entry needed.
- RtxSparkInferenceSelector, a fixed SKU-to-recipe table keyed off
  GpuVisibleMemoryBytes (empirically verified against a real 48GB
  RTX Spark unit). Wired into LocalInferenceSelector.Select only for
  the no-requested-model default path; explicit model requests and
  every non-Spark GPU keep using the untouched generic fit-test.
- GetRequiredMemoryBytes/GetDraftKvCacheMemoryBytes now account for a
  DFlash recipe's separate draft weights and skip draft KV entirely
  for SpeculativeDecodingMode.None.
BuildPreset hardcoded spec-type = draft-mtp unconditionally. Branches
on LocalModelRunRecipe.SpeculativeDecoding so DraftDFlash recipes
emit spec-type = draft-dflash plus spec-draft-model, and None emits
no spec-* lines at all.

DraftDFlash still requires a resolved draftModelPath; that path isn't
threaded from the install manifest yet (the DFlash draft checkpoint
isn't acquired/verified through HuggingFaceModelInstaller in this
change), so BuildPreset fails closed with InvalidDataException rather
than silently omitting the draft model. Follow-up work.
Adds SKU-boundary coverage for the new RtxSparkInferenceSelector
(32/48/64/128GB) and a case proving a non-Spark GPU with Spark-sized
memory still takes the generic path.

Evaluate_RoutesRuntimeByArchitectureWithoutGpuSkuPairing used "NVIDIA
RTX Spark N1X" as an arbitrary placeholder name to prove GPU name
doesn't affect runtime routing. That's no longer SKU-irrelevant now
that RTX Spark has real SKU routing, so the fixture GPU name is
swapped to a generic dGPU.
LocalModelCatalog.AdditionalArtifacts() gives every acquirer, manifest, and
launch path a single fixed ordering for a recipe's non-primary pinned
artifacts. Today that is the DFlash draft checkpoint.

GetRequiredMemoryBytes now includes the draft checkpoint's weights, so a
DFlash recipe does not under-report the memory it needs during qualification.
b10655 (CUDA 13.3 on x64) predates DFlash draft-decoding support and
the 96GB recipe's validated build. Bumping to b11026 puts both x64 and
arm64 on CUDA 13.4 and covers every RTX Spark recipe with one pinned
runtime, matching the existing single-global-pin design instead of
adding per-recipe runtime routing.
…sets

Extends LocalAiInstallManifest with AdditionalModelAssets/
AdditionalModelPaths (schema 5) so a recipe's DFlash draft checkpoint
or extra split-GGUF shards can be recorded and re-verified alongside
the primary weights receipt in the same Hugging Face hub cache.
Schema 4 manifests are untouched -- the new fields are empty and
absent from JSON unless a recipe actually pins additional assets.

Each additional-asset receipt derives its own repository/revision from
its own SourceUrl (not the primary ModelId) since the DFlash draft
checkpoint is pinned from a different HF repo than the primary weights.

Adds LocalAiInstallManifest.UsesHubCache so schema 4 and schema 5 are
treated identically everywhere the manifest previously branched only
on the exact HubCacheReceiptSchemaVersion value.

AdditionalModelAssets/AdditionalModelPaths are left at their unset
ImmutableArray default (not .Empty) so JsonIgnoreCondition.WhenWritingDefault
actually omits them for schema-3/4 manifests -- .Empty is a distinct,
non-default array instance the condition never matches, so writing it
would have added new fields to every existing schema-4 receipt and
broken older app builds' strict unknown-field rejection. UsesHubCache
is marked [JsonIgnore] for the same reason: it's a derived read helper,
not part of the persisted contract. ResolveAndValidate normalizes the
unset default to .Empty immediately after load so every in-memory
reader keeps using ordinary IsEmpty/Length calls safely.
Adds HuggingFaceModelInstaller.InstallAdditionalAssetAsync, mirroring
InstallAsync's resumable-download/verify/promote flow for a recipe's
non-primary pinned artifacts (DFlash draft checkpoint, extra
split-GGUF shards). Deliberately duplicated rather than refactored
out of InstallAsync to avoid any risk to that heavily-tested primary
weights path; the one thing it omits is the legacy app-owned
compatibility copy, since every recipe using an additional asset is
new since the hub cache became the primary store.
…ncile

AcquireLocalAiModelStep now downloads/verifies a recipe's additional
assets (via LocalModelCatalog.AdditionalArtifacts) right after its
primary weights, records the results on SetupContext, and rolls the
context field back on failure -- the hub-cache artifacts themselves
survive rollback the same way the primary weights' do, since there is
no legacy copy to delete. PersistLocalAiManifestStep writes them into
a schema-5 manifest (schema 4 unchanged for recipes with none), and
leaves the two new manifest fields at their unset default rather than
an explicitly-built empty array when there is nothing to add, so a
plain schema-4 install keeps omitting them from JSON.

LocalAiInstallReconciler gains VerifyAdditionalAssetAsync so a reused
install re-verifies a recipe's additional assets against the hub
cache, not just its primary weights, before treating the install as
still valid. LocalAiReconcileResult now also carries the additional
asset installs it just verified, reconstructed from the manifest's own
already-verified receipts: recovery for a broken runtime (model and
additional assets still valid) previously left SetupContext's
additional-install list empty because AcquireLocalAiModelStep's reuse
skip never re-runs acquisition, which made PersistLocalAiManifestStep
hard-fail on the very installs this series adds an acquisition step
for. The setup review consent screen now also lists every additional
artifact (a DFlash draft checkpoint, or extra split-GGUF shards) as
its own download line instead of only the primary weights.
…unch preset

BuildCore resolves the DFlash draft checkpoint and passes it to BuildPreset, so
spec-draft-model is finally emitted for real. ValidateArtifactReceipts also checks
additional-asset receipts against the catalog, as it already does for the primary
weights and runtime artifacts.

The path handed to llama-server is the handle-resolved physical path from the same
verification that opened the file, not the persisted snapshot path. The hub cache
hands out a handle-resolved path precisely so a snapshot-link replacement cannot
change the file identity a native reader finally opens, which is how the primary
model is already bound; the draft checkpoint now gets the same guarantee instead of
re-deriving its path from its receipt.

LlamaServerRuntimeService's schema checks use LocalAiInstallManifest.UsesHubCache so
schema-5 installs are treated as hub-cache-backed, matching schema 4, instead of
falling into the legacy schema-3 branch.
A recipe's pinned weights are not everything it downloads or loads: a DFlash
recipe also pulls a separate draft checkpoint, and llama-server loads both.
Ranking, the post-launch GPU-load sanity check, and the user-facing size
disclosure all read the primary weights alone, so a DFlash recipe understated
its footprint and the setup review omitted the draft checkpoint entirely.

Adds LocalModelCatalog.TotalDownloadSizeBytes (weights plus draft checkpoint)
and switches SelectDefaultModelAndProfile's tie-break and fallback,
LocalAiGpuVerification's minimum-load-delta check, and the setup UI's model
picker and detail text to use it.

SelectDefaultModelAndProfile also excludes priority-0, explicit-alternative
models from the generic default and fallback pool, so a model reachable only
through a specific SKU can never win the generic pick as the catalog grows.
…are incomplete

An RTX Spark GPU whose CUDA memory couldn't be read reads as 0 bytes,
which the fixed SKU table treated as a legitimately-too-small SKU
(NotRecommendedForSku) instead of the real problem: unreadable
capacity. Select() now only takes the Spark SKU-routing branch when
the Spark GPU's facts are actually complete, so an incomplete-facts
Spark GPU falls through to the generic path and gets correctly
diagnosed as HardwareFactsIncomplete, same as any other GPU.

Also bumps LocalAiPortHandoffTests' Spark fixture from an arbitrary
~24GiB (a pre-SKU-routing placeholder, now below the smallest 32GB
tier) to a real 48GB SKU's measured cuMemGetInfo total, so it again
resolves to the 24GB recipe instead of "not recommended."
@joelagnel
joelagnel force-pushed the feat/rtx-spark-recipes branch from 362e728 to 24ff075 Compare September 25, 2026 21:16
@joelagnel

Copy link
Copy Markdown
Contributor Author

@shanselman — the ordering race is fixed and pushed as 24ff075a. Your diagnosis was exact: RefreshAvailabilityAsync captured _runtimeSnapshot.ModelId synchronously, so a runtime refresh that resolved the receipt afterwards never re-entered the calculation.

Fix, in ApplyRuntimeSnapshot — four lines, taking the "recompute availability when the runtime model id changes" option you offered:

string? previousModelId = _runtimeSnapshot.ModelId;
_runtimeSnapshot = snapshot;
OnPropertyChanged(null);
if (IsActive && !string.Equals(previousModelId, snapshot.ModelId, StringComparison.Ordinal))
    StartAvailabilityRefresh();

That is the single funnel every snapshot passes through — both OnRuntimeStateChanged and RefreshRuntimeSnapshotAsync land there, and both arrive via ApplyOnUiThread, so StartAvailabilityRefresh is called on the UI thread like every other caller. No new field, no new method, no new profile or recovery API.

Permanent regression: Spark32Gb_WhenReceiptArrivesAfterAvailability_RecomputesAndKeepsRetrySetupReachable, modelling your repro — initial snapshot with ModelId = null, a gated runtime refresh publishing a valid managed receipt afterwards, asserting the entry point closed before the gate opens and CanRetrySetup reachable after. It needed two properties on the existing FakeLocalAiRuntime (RefreshResult, RefreshDelay), following the StopResult pattern already there.

Proven against your requirement, reverting only the ViewModel and keeping the test:

new test other Spark32Gb tests
without the fix FAIL 3 passed
with the fix pass 3 passed

Both changes are squashed into the commits they belong to — the fix into feat(local-ai): route RTX Spark defaults through a fixed SKU table, the regression into test(local-ai): cover RTX Spark SKU routing. Still 12 commits, no fixup commits.

Validation at this head on a real 48 GB RTX Spark: ./build.ps1 exit 0, Tray 3,073 passed (the +1 is this regression), Connection 803, SetupEngine 1,194. Shared reported 4,118 passed with the same MCP disposal-timing test you hit; it passes in isolation on the rerun. This branch touches no MCP files.

Your point about the existing hardware proof still applying holds for this head too — the delta since 362e7283 is only this ViewModel path and its test.

@joelagnel

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review — head is now 24ff075a. Since your last pass: the availability/runtime-refresh ordering race is fixed in ApplyRuntimeSnapshot (recompute when the model id changes), with a delayed-refresh regression proven to fail without the fix. Both squashed into the commits they belong to; still 12 commits. Full required floor green on Spark hardware.

@clawsweeper

clawsweeper Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot removed the P1 Urgent regression or broken agent/channel workflow affecting real users now. label Sep 25, 2026
@karkarl

karkarl commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

Maintainer replacement #1520 was created from a clean rebase of this exact 12-commit patch stack onto current main. The external contributor branch was not force-pushed. git range-diff reports all 12 commits patch-equivalent (=); replacement head is 7e51993, includes #1515, and carries updated validation/proof plus the current-head DGX rerun blocker. Do not merge both PRs.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local AI: route RTX Spark unified-memory SKUs through NVIDIA's fixed recipe table

4 participants