Skip to content

fix(desktop): enable Windows mesh-llm builds and address Compute Share startup/MeshLLM debug logging/non-image models trying to parse image input - #3223

Open
stevepresley wants to merge 9 commits into
block:mainfrom
stevepresley:fix/windows-mesh-llm-2836-upstream
Open

Conversation

@stevepresley

@stevepresley stevepresley commented Jul 27, 2026

Copy link
Copy Markdown

fix: enable Windows mesh-llm desktop builds

Closes #2836
Closes #3300

Summary

  • Enable the mesh-llm feature for Windows release and canary desktop builds.
  • Bundle required Windows MeshLLM native runtime DLL dependencies as Tauri resources and register their directory at runtime.
  • Avoid Windows stack overflows during mesh start and managed-agent start by running heavy startup work on larger-stack OS threads.
  • Make Compute Share startup more resilient/idempotent and fix stale progress UI behavior.
  • Add opt-in MeshLLM diagnostic logging controls in Compute → Advanced.
  • Add relay-mesh protection so prior tool-result images are not replayed into text-only shared-compute LLM requests.

Validation

Validated locally on Windows 11:

  • cargo fmt --manifest-path desktop/src-tauri/Cargo.toml --all
  • cargo check --manifest-path desktop/src-tauri/Cargo.toml --features mesh-llm
  • pnpm typecheck
  • cargo check -p buzz-agent
  • cargo build -p buzz-agent --release --target x86_64-pc-windows-msvc
  • bash scripts/bundle-sidecars.sh x86_64-pc-windows-msvc
  • pnpm tauri build --target x86_64-pc-windows-msvc --bundles nsis --features mesh-llm --config "{\"bundle\":{\"createUpdaterArtifacts\":false}}"

Manual validation:

  • Installed the NSIS build on Windows 11.
  • Verified Settings → Compute no longer shows the mesh-llm feature-stub behavior.
  • Verified Compute Share starts and serves requests.
  • Verified Windows MeshLLM native runtime dependency loading works with bundled MinGW DLLs.
  • Verified managed buzz-agent starts from the installed app.
  • Verified relay-mesh LLM calls no longer fail with the text-only model media-input 422 caused by replayed tool-result images.
  • Verified MeshLLM diagnostic logging toggle works at runtime.

Related/out of scope findings

Windows relay-mesh validation also reproduced existing ACP/agent delivery behavior where a model may produce Activity/final text without publishing a DM/channel message, and failed ACP turns can be retried later. That is out of scope for this Windows mesh-llm packaging/runtime PR.

Refs #2698
Refs #2421
Refs #2681

@stevepresley
stevepresley requested a review from a team as a code owner July 27, 2026 22:08
@michaelneale

Copy link
Copy Markdown
Contributor

@stevepresley thanks - that is a big change and not all windows related is it?

@stevepresley

stevepresley commented Jul 28, 2026

Copy link
Copy Markdown
Author

@stevepresley thanks - that is a big change and not all windows related is it?

It started as mostly Windows related, but these three items are cross-platform.

  • Make Compute Share startup more resilient/idempotent and fix stale progress UI behavior.
  • Add opt-in MeshLLM diagnostic logging controls in Compute → Advanced.
  • Add relay-mesh protection so prior tool-result images are not replayed into text-only shared-compute LLM requests.

I'll update the title

@stevepresley stevepresley changed the title fix(desktop): enable Windows mesh-llm builds fix(desktop): enable Windows mesh-llm builds and address Compute Share startup/MeshLLM debug logging/non-image models trying to parse image input Jul 28, 2026
@stevepresley

stevepresley commented Jul 28, 2026

Copy link
Copy Markdown
Author

@michaelneale opened #3300 to track the 3 other issues fixed in this PR. Let me know if you want me to split it out into a second PR

Signed-off-by: stevepresley <github@stevepresley.net>
…m-2836-upstream

Signed-off-by: stevepresley <github@stevepresley.net>

# Conflicts:
#	desktop/src-tauri/src/commands/mesh_llm.rs
#	desktop/src/features/mesh-compute/ui/MeshComputeSettingsCard.tsx
Signed-off-by: stevepresley <github@stevepresley.net>
@stevepresley
stevepresley force-pushed the fix/windows-mesh-llm-2836-upstream branch from dcd0ed9 to c35bbda Compare July 31, 2026 18:30
Signed-off-by: stevepresley <github@stevepresley.net>
@stevepresley
stevepresley force-pushed the fix/windows-mesh-llm-2836-upstream branch from 073672b to b478dad Compare July 31, 2026 18:44
tlongwell-block added a commit that referenced this pull request Aug 3, 2026
…#4524)

## Summary

Official Linux desktop packages (`.deb` / AppImage) are built without
`--features mesh-llm`, so they ship the `mesh_llm_stubs` backend and
Settings → Compute always fails with `mesh-llm feature not enabled`.
This PR adds the feature flag to the two Linux build commands:

- `release.yml` → `release-linux` job
- `linux-canary.yml` → canary build

That's the whole diff — 2 lines. Fixes #3788 (Linux); see also #3841
(dup with UI-gating PR #3914) and the Windows twin #2836/#3223.

## Why no native prebuild step (unlike the macOS job)

The macOS job carries Metal llama prebuild/cache steps from #798. Linux
doesn't need an equivalent:

- `mesh-llm-host-runtime` is compiled with `dynamic-native-runtime` and
installs the recommended runtime on first use (verified by sha256
checksum over HTTPS; upstream's signature verification path is not yet
implemented — default policy is `RequireChecksum`, per
`mesh-llm-runtime-install/src/lib.rs`)
(`desktop/src-tauri/src/mesh_llm/mod.rs` —
`initialize_mesh_native_runtime`), so release builds work on clean
machines without bundling llama.cpp.
- Upstream publishes Linux x86_64/aarch64 runtime bundles for the pinned
`v0.74.0` line, and `scripts/ensure-mesh-native-runtime.sh` already maps
`meshllm-native-runtime-linux-x86_64-cpu` / `linux-aarch64-cpu` for
local/e2e use.
- The unmerged branch `micn/mesh-node-download` (`96f29417a`) treats
even the macOS prebuild steps as removable dead weight for the same
reason.

## Background

The omission is historical drift, not a decision: Linux packaging
predates the mesh feature flag (#693), mesh became opt-in for
build-cost/reliability reasons (#823, #1183), and #1221 re-enabled it
for releases by editing only the macOS build line. `release-linux` and
the later `linux-canary` copy were never revisited.

The mesh shutdown hard-exit/relaunch path is gated `all(mesh-llm,
target_os = "macos")` because ggml/Metal destructors abort on macOS;
ordinary mesh shutdown (`shutdown_mesh_runtime`) is cross-platform, so
Linux falls through to the generic path.

## Validation

- [x] `./bin/cargo check --manifest-path desktop/src-tauri/Cargo.toml
--features mesh-llm` green at base `2c0ac2467` (feature graph compiles
at the pinned v0.74.0 line)
- [ ] Linux canary run with this change: AppImage/.deb build succeeds
and binary contains real `mesh_llm` symbols (not `mesh_llm_stubs`)
- [ ] Installed package: cold-start → Settings → Compute → runtime
download → serve → clean shutdown

The last two need a Linux run/host. **Note (from review):**
`linux-canary.yml` is `workflow_dispatch`-only and its `Require main`
step rejects non-main refs, so the canary cannot run on this branch
pre-merge — and `.github/workflows/**` matches no ci.yml paths-filter,
so this PR's own CI does not exercise the changed lines. Validation
sequencing is therefore merge → dispatch linux-canary on main →
live-package pass, with a trivial 2-line revert as the escape hatch.

Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: stevepresley <github@stevepresley.net>
@jds1-ai

jds1-ai commented Aug 9, 2026

Copy link
Copy Markdown

Built and tested this PR on Windows 11 / RTX 5060 Ti. The feature side works. Two things worth
adding: one small CI blocker in the PR itself, and one blocker that sits entirely upstream of Buzz.

The build is correct — verified against a control

Built fix/windows-mesh-llm-2836-upstream merged onto current main, via windows-canary.yml.
Scanning the resulting buzz-desktop.exe for ASCII strings, with the stock 0.5.8 release binary as
a negative control:

string this PR's build stock 0.5.8
mesh-llm feature not enabled 0 1
mesh_llm_stubs 0 1
mesh-llm-host-runtime 813 0
iroh 445 0

Binary 147.9 MB vs 86.5 MB. Settings → Compute renders a real panel, detects the GPU, and populates
the model catalogue — no stub error. #2836's symptom is resolved by this PR.

1. CI blocker: windows-latest no longer ships libgomp-1.dll

scripts/bundle-windows-mesh-runtime-deps.ps1 fails on a stock runner. The job gets through sidecar
compilation and then:

Bundled libgcc_s_seh-1.dll from C:\Program Files\Git\mingw64\bin\...
Bundled libstdc++-6.dll     from C:\Program Files\Git\mingw64\bin\...
Exception: scripts\bundle-windows-mesh-runtime-deps.ps1:31
  Missing Windows MeshLLM runtime dependency: libgomp-1.dll
##[error]Process completed with exit code 1

Runner image is now windows-2025-vs2026. None of the first three search directories
(C:\msys64\mingw64\bin, C:\msys64\ucrt64\bin, C:\ProgramData\mingw64\mingw64\bin) are
populated on it, so the script falls through to Git for Windows' MinGW — which carries three of the
four DLLs but not the OpenMP runtime. The C:\ProgramData\mingw64\mingw64\bin entry in the search
list suggests this was written against an older image that installed MinGW via Chocolatey there.

The script fails loudly rather than shipping a partial bundle, which is the right behaviour.

We worked around it with a step before the bundler that installs the toolchain and then asserts
all four are resolvable:

- name: Provide MinGW runtime DLLs for the bundler
  shell: pwsh
  run: |
    $need = 'libgcc_s_seh-1.dll','libstdc++-6.dll','libgomp-1.dll','libwinpthread-1.dll'
    $dirs = 'C:\msys64\mingw64\bin','C:\msys64\ucrt64\bin',
            'C:\ProgramData\mingw64\mingw64\bin','C:\Program Files\Git\mingw64\bin'
    function Get-Missing { $need | Where-Object { $n=$_; -not ($dirs | Where-Object { Test-Path (Join-Path $_ $n) }) } }
    if (Get-Missing) { & C:\msys64\usr\bin\pacman.exe -Sy --noconfirm --needed mingw-w64-x86_64-gcc }
    if (Get-Missing) { choco install mingw -y --no-progress | Out-Null }
    $still = Get-Missing
    if ($still) { throw "MinGW runtime still unavailable: $($still -join ', ')" }

MSYS2's pacman resolved it on the runner (missing before: libgomp-1.dll, then supplied). With
that step the canary builds clean and produces the NSIS installer.

2. Bundled DLLs replace the runtime's own copies

Worth knowing regardless of the above: after the app runs, the libstdc++-6.dll inside
%LOCALAPPDATA%\mesh-llm\native-runtimes\<ver>\<runtime-id>\lib is byte-identical to the copy in
resources/mesh-llm/windows-x86_64, where before it matched the one shipped in the runtime archive
(different SHA-256 and mtime). So the bundled dependencies don't merely sit earlier in the search
order — they end up replacing what the runtime shipped with itself.

In our case it changed nothing, because both builds are missing the same symbols (see below). But if
a future runtime archive ships a correct libstdc++-6.dll, this would silently substitute a
different one for it.

3. The remaining blocker is upstream of Buzz

With the build correct and the panel working, enabling Share this machine still fails:

mesh native runtime failed to install or load:
load native runtime meshllm-native-runtime-windows-x86_64-vulkan ...: LoadLibraryExW failed

Two independent upstream causes, both measured against freshly downloaded MeshLLM archives with no
Buzz involved:

(a) The MinGW Windows runtimes don't load. ggml.dll imports 54 symbols from libstdc++-6.dll
and 5 are absent from the copy shipped in the same archive — std::codecvt_utf8_utf16<wchar_t> /
std::codecvt<wchar_t,char,mbstate_t> members. LoadLibraryExW returns
ERROR_PROC_NOT_FOUND (127). Reproduces on vulkan and cpu, at v0.74.0 and v0.75.0. The
MSVC-built cuda12 artifact is unaffected. Filing separately with MeshLLM.

(b) No Blackwell-capable Windows CUDA runtime exists. native-runtimes.json gives
windows-x86_64-cuda12 gpu_arches = 75,80,86,87,89,90; sm_120 appears only on
linux-x86_64-cuda13. On an RTX 50-series card the resolver correctly rejects cuda12 and falls
through to the broken vulkan.

Net: on Windows + Blackwell, Share Compute cannot start a node even with this PR merged. On
sm 75–90 hardware the cuda12 path looks viable, provided cudart64_12 / cublas64_12 /
cublasLt64_12 are available — they are not on a machine with current (CUDA 13) drivers, and adding
them made all five CUDA libraries load for us.

None of this is an argument against merging — the PR does what it says. It is an argument for
expecting #2836 reports to continue from RTX 50-series users afterwards, and possibly for surfacing
the resolver's rejection reason in the UI: at verbose logging the app reports only that the chosen
runtime failed to load, never that a different one was evaluated and rejected, which makes this
considerably harder to diagnose than it needs to be.

Minor

  • Opening Settings → Compute and toggling sharing spawns briefly visible console windows.
  • The model catalogue reports vram=32.558606745 on a 16 GB card, and the panel shows
    "32 GB AI memory", so recommendations are sized against roughly double the real VRAM.

Happy to test builds on Windows 11 / RTX 5060 Ti.

Signed-off-by: stevepresley <github@stevepresley.net>
Signed-off-by: stevepresley <github@stevepresley.net>
jds1-ai added a commit to jds1-ai/buzz that referenced this pull request Aug 10, 2026
…wn bootstrap

Our 08-09 fork run installed the MinGW runtime DLLs before the bundler. PR block#3223
now bootstraps them itself (pacman -> choco -> assert). Leaving our step in would
let that bootstrap borrow strength from ours and pass without being exercised, so
it is removed.

Replaced with a read-only probe that installs nothing and records which of the
four DLLs the stock windows-latest runner actually has, so a bundler PASS can be
attributed to the bootstrap rather than to a runner that already had them.

Signed-off-by: jds1-ai <jds1-ai@users.noreply.github.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@stevepresley

Copy link
Copy Markdown
Author

Thanks @jds1-ai - made the following updates:

  • Fixed Windows canary/CI DLL bundling failure:

    • Bundler now bootstraps missing MinGW runtime DLLs via MSYS2 pacman, then Chocolatey fallback.
    • Still fails loudly if required DLLs remain unavailable.
  • Avoided replacing MeshLLM runtime archive DLLs:

    • Buzz no longer overwrites DLLs already present in the downloaded MeshLLM runtime lib directory.
    • Bundled DLLs are only copied when missing.
  • Fixed Windows catalog VRAM over-reporting:

    • Settings → Compute catalog now uses dedicated GPU VRAM on Windows instead of MeshLLM’s survey value that includes system RAM offload.
    • A 16 GB card should no longer display/recommend as ~32 GB AI memory.
  • Reduced/removed console window flashes during catalog probing:

    • Windows catalog probes for nvidia-smi / PowerShell now run with hidden child-process flags.

Validation performed:

  • cargo fmt
  • git diff --check
  • PowerShell script parse check
  • cargo check --features mesh-llm from desktop/src-tauri

Give it another go and let me know how it works for you. I'm trying to get another box with an nVidia card setup to test locally as well.

@jds1-ai

jds1-ai commented Aug 10, 2026

Copy link
Copy Markdown

Tested 2b16d630 on Windows 11 / RTX 5060 Ti. Thanks for picking up the CI blocker — one result,
one observation about a second hunk in the same commit, and one note about coverage.

1. The bundler bootstrap works on a stock runner — confirmed

I re-ran the canary in a fork with our own MinGW-install workaround removed, so the bootstrap in
bundle-windows-mesh-runtime-deps.ps1 is what's actually under test rather than something already
satisfied for it. The only fork edit is the github.repository job guard.

To make the result attributable I added a read-only probe before the bundler that installs nothing
and records the stock runner's pre-state — otherwise a runner that happened to ship the four DLLs
would let the bootstrap "pass" without executing anything:

runner image: win25-vs2026 / 20260803.193.1
dir C:\msys64\mingw64\bin                         exists=True
dir C:\msys64\ucrt64\bin                          exists=True
dir C:\ProgramData\mingw64\mingw64\bin            exists=False
dir C:\Program Files\Git\mingw64\bin              exists=True
pacman present: True
choco present:  True
PRESENT  libgcc_s_seh-1.dll  <-  C:\Program Files\Git\mingw64\bin
PRESENT  libstdc++-6.dll     <-  C:\Program Files\Git\mingw64\bin
MISSING  libgomp-1.dll
PRESENT  libwinpthread-1.dll <-  C:\Program Files\Git\mingw64\bin
PROBE_MISSING_BEFORE=libgomp-1.dll

So the runner really was missing libgomp-1.dll — the bootstrap had something to do, and the
result is attributable to it. Your script then:

Missing Windows MeshLLM runtime dependencies before bootstrap: libgomp-1.dll
... pacman installs mingw-w64-x86_64-gcc (487.68 MiB, gcc 16.2.0)
Bundled libgcc_s_seh-1.dll from C:\msys64\mingw64\bin\libgcc_s_seh-1.dll
Bundled libstdc++-6.dll     from C:\msys64\mingw64\bin\libstdc++-6.dll
Bundled libgomp-1.dll       from C:\msys64\mingw64\bin\libgomp-1.dll
Bundled libwinpthread-1.dll from C:\msys64\mingw64\bin\libwinpthread-1.dll

PASS on the MSYS2 branch; the choco fallback was not exercised (and so remains untested by this
run). Whole job ~22 min against a warm Cargo cache, comfortably inside the 60-minute timeout;
pacman added roughly 30 s. This is the same fork pipeline that previously failed at exactly this
step, with our own MinGW workaround removed, so it's a real before/after.

To be precise about what this rests on: the verdict comes from the bundler step's own log, not
from the artifact — see §5, the canary's uploaded artifact turned out to be stale. The bootstrap
demonstrably ran and did its work; that part is unaffected.

One incidental effect worth knowing: because C:\msys64\mingw64\bin sorts first in the candidate
list, once pacman runs all four DLLs come from MSYS2 GCC 16.2.0 rather than from Git for
Windows. The bundled libstdc++-6.dll is therefore a different, newer build than the one a
developer bundling locally off Git for Windows would get. That interacts with §2 below, and it also
means the bundle's contents now depend on which branch of the bootstrap fired — worth pinning if
reproducibility matters.

(Minor: pacman -Sy without -Su is the documented MSYS2 partial-upgrade hazard. It worked here,
and on a throwaway runner the blast radius is nil, but -Syu is the safer spelling.)

2. mesh_llm.rs: the new skip-if-present guard makes the bundle inert on the MinGW runtimes

The same commit changes the per-DLL copy in prepare_windows_mesh_runtime_dependencies from an
unconditional std::fs::copy(src, dst) to:

if dst.is_file() {
    append_mesh_debug_log(app, format!("windows mesh runtime dependency already present; not replacing file={}", dst.display()));
    continue;
}

Measured on this box, against the extracted runtime tree and a pre-run backup of it:

  • dst is the runtime's own …\native-runtimes\<ver>\meshllm-native-runtime-windows-x86_64-<flavor>\lib\.

  • The MinGW archives already ship all four of libgcc_s_seh-1.dll, libstdc++-6.dll,
    libgomp-1.dll, libwinpthread-1.dll.

  • Under the previous unconditional copy, all four were in fact overwritten. SHA-256, pristine
    archive vs the same files after a build of this PR ran:

    DLL pristine (archive) after run (bundled)
    libstdc++-6.dll 3529d11c… 887c21db…
    libgcc_s_seh-1.dll 278f1101… b37c1770…
    libgomp-1.dll ae3939c3… acf25eee…
    libwinpthread-1.dll 1bbd639c… d54ed5ba…

The MeshLLM diagnostic log added by this PR confirms it directly — this is the app's own output on
every sharing attempt, for all four DLLs and for both installed runtimes:

windows mesh runtime dependency already present; not replacing
  file=…\native-runtimes\0.74.0\meshllm-native-runtime-windows-x86_64-vulkan\lib\libstdc++-6.dll
windows mesh runtime dependency already present; not replacing
  file=…\native-runtimes\0.74.0\meshllm-native-runtime-windows-x86_64-cuda12\lib\libstdc++-6.dll
prepared windows mesh runtime dependency dirs=…\Buzz\resources\mesh-llm\windows-x86_64;
  …\meshllm-native-runtime-windows-x86_64-cuda12\lib;…-vulkan\lib

Note it skips for cuda12 as well, whose archive ships none of these four — because an earlier
build of this PR, with the unconditional copy, already put them there. That's the sharper form of
the issue: the guard is sticky.
It isn't only "the MinGW archive already occupies those
filenames"; it's that once any version has populated them by any means, no later version can
refresh them. A machine that has run today's build will keep today's DLLs indefinitely, even if a
future Buzz bundles corrected ones — the upgrade silently no-ops. That's the part I'd weigh, more
than the current-release behaviour.

To be clear about impact: this breaks nothing today. Every libstdc++-6.dll I have — the
archive's, the bundled one, and Git for Windows' — is missing the same five codecvt exports, so
the substitution was never fixing the 127. And skip-if-present does cure a real hazard I'd flagged
earlier in this thread: the old behaviour would silently clobber a correct archive DLL if a future
release shipped one.

The two halves of 2b16d630 pull against each other, though. Per §1, the bootstrap changes
where the bundled DLLs come from — after pacman fires, libstdc++-6.dll is MSYS2 GCC 16.2.0's
rather than Git for Windows'. I checked whether that newer build helps: parsing its export table, it
is also missing all five symbols (7058 exports), so it would not have fixed the 127 even if it
had been applied. That's why I'm confident the skip costs nothing today.

The structural point is what remains: one half of the commit changes what goes into the bundle, and
the other half guarantees it can never reach a runtime directory that has already been populated. If
a future MeshLLM release ships a corrected libstdc++-6.dll the skip is right; if the intended cure
for the 127 was ever "bundle a good one", the skip closes that door — and closes it hardest on the
machines that already ran an affected build.

The question is just whether "archive is authoritative, the bundle only fills gaps" is the intended
semantics. If it is, this is right and worth a comment saying so. If the bundle was meant to be able
to repair a bad archive DLL, this removes that ability on exactly the two runtimes that are
currently broken — and the debug line reads "already present; not replacing", which is accurate but
easy to skim past when diagnosing a load failure.

3. Nothing in this PR's CI exercises the script

The checks on 2b16d630 are zizmor, Semgrep OSS, and DCO Check. windows-canary.yml is
workflow_dispatch:-only and release.yml runs on release, so bundle-windows-mesh-runtime-deps.ps1
— including the bootstrap just added to it — runs on no check in this PR. A green PR here
carries no signal about the thing that was fixed. Not necessarily worth restructuring CI over, but
worth knowing that the fix is currently only validated by someone running the canary by hand, which
is what this comment is.

4. 7e2644d4 (dedicated VRAM) — confirmed fixed in an installed build; the fallback is not

Confirmed end-to-end. I installed a canary built from 7e2644d4 (verified by hash that it was
actually that build — see §5) on the 16 GB RTX 5060 Ti / 32 GB RAM machine. Settings → Compute now
reads:

Recommended for this machine (NVIDIA GeForce RTX 5060 Ti, 16 GB AI memory).

That's the ~32 GB over-report I raised, fixed, and the recommendation is sized sensibly against it
(a 2.7 GB model, "Fits well"). Marker scan confirms it's a real mesh build rather than the stub:
mesh_llm_stubs 0, mesh-llm-host-runtime 813, iroh 445, 147.9 MB vs stock 0.5.8's 86.5 MB.

The premise in your code comment ("a 16 GB card with 32 GB system RAM reads as ~32 GB") is this
machine exactly, which is why I reported it. Running the two probes the patch uses:

> nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits
NVIDIA GeForce RTX 5060 Ti, 16311

> Get-CimInstance Win32_VideoController | Select-Object Name,AdapterRAM | ConvertTo-Json -Compress
{"Name":"NVIDIA GeForce RTX 5060 Ti","AdapterRAM":4293918720}

The nvidia-smi path gives 16311 MiB and format_rated_capacity renders "16 GB" — correct, and it
fixes the ~32 GB over-report I reported.

The Win32_VideoController fallback reports 4 GB for the same 16 GB card. AdapterRAM is a
uint32, so it saturates at 4293918720 bytes (4095 MiB) and cannot represent any card above 4 GB.
This is a long-standing WMI limitation, not something specific to this machine. So whenever
nvidia-smi isn't usable — not on PATH, non-NVIDIA hardware, or windows_command_output
returning None because the exit status isn't success — the catalog silently swaps a ~2x
over-report for a 4x under-report, and the picker starts recommending models far smaller than the
card can hold. It fails quietly and plausibly, which is the awkward part: 4 GB is a believable
number for a GPU, so nothing looks wrong.

Worth noting that the accompanying unit test can't catch this. Its fixture is:

r#"[{"Name":"GPU A","AdapterRAM":8589934592},{"Name":"GPU B","AdapterRAM":"4294967296"}]"#

Both values are unreachable in practice — 4294967296 is exactly one byte past u32::MAX, so
AdapterRAM can never return either. The parser is being tested against input the data source
cannot produce, so it passes regardless of the saturation behaviour. If the fallback is kept, a
fixture of 4293918720 for a large card would be the one that bites.

If DXGI is available to you, IDXGIAdapter::GetDesc / DedicatedVideoMemory is the usual
replacement — it's 64-bit and doesn't need a child process. Failing that, it may be better to return
no VRAM figure at all than a saturated one, since "unknown" degrades more honestly than "4 GB".

Second, smaller point on both paths: they sum VRAM across adapters. parse_nvidia_smi_catalog_output
accumulates total over every row, and the controller path does .map(|(_, bytes)| *bytes).sum().
For a catalog that ranks what will fit, that reproduces the class of error this commit fixes — two
16 GB cards would present as 32 GB and attract a model that fits in neither one alone, absent layer
splitting. summarize_gpu_names already special-cases multi-GPU for display ("N GPUs"), so the
multi-adapter case is clearly in scope. Max-per-adapter is likely the safer figure for
recommendations, with the sum kept for any pooled-placement view. Flagging this one as reasoning
from the code rather than a measurement — this machine is single-GPU, so unlike the points above I
have not actually observed the two-card behaviour.

The console-window half is fixed for the case you described, but not for enabling sharing.
Opening Settings → Compute is now clean — no flashes, and a process watcher confirms the catalog's
probe (nvidia-smi --query-gpu=name,memory.total …, i.e. your windows_nvidia_smi_gpus) spawns
with no conhost attached. That also settles which path produced the 16 GB: the primary one, not
the Win32_VideoController fallback.

Toggling Share this machine still flickers, repeatedly. Watching child processes, the sharing
path spawns all of these with a conhost.exe attached — which is what the visible window is:

nvidia-smi -L                                                   + conhost
nvidia-smi --query-gpu=index,compute_cap --format=csv,noheader  + conhost
nvidia-smi                                                      + conhost
vulkaninfo                                                      + conhost
powershell -NoProfile -Command "Get-CimInstance Win32_VideoController | Select-Object -ExpandProperty Name"  + conhost

That last one is not your call site — yours is Select-Object Name,AdapterRAM | ConvertTo-Json -Compress. This is the upstream mesh_llm_system::hardware::survey(), which your commit bypasses
for the catalog but which the runtime-resolution path still calls. The set repeats while sharing
retries, which is why it reads as several flickers rather than one — the diagnostic log shows the
frontend polling mesh_node_status every ~4 s and the dependency-preparation block re-running
alongside it, so the survey is not a one-shot at toggle time.

So the fix is real and correctly scoped to what your comment claims; the remaining flashes come from
the upstream survey rather than from catalog.rs. Flagging it mainly so the symptom isn't reported
back at you as "still broken" after this merges.

5. Separate pre-existing bug: windows-canary.yml can upload a stale installer

This one isn't yours — it's in the canary workflow on main, and it matters here because the canary
is how people are going to validate this PR by hand.

Three consecutive green runs on my fork uploaded the same binary:

run head makensis produced uploaded sha256
4 957b5fab Buzz_0.5.9-test.4 test.4 4ccf4da0…
5 34859e0c Buzz_0.5.9-test.5 test.4 4ccf4da0…
6 eaf9aa9f Buzz_0.5.9-test.6 test.4 4ccf4da0…

Byte-identical, 68,095,414 bytes each. Runs 5 and 6 compiled their own installers and then shipped
run 4's. Two things combine:

  1. The Cargo cache path: exclusion
    !desktop/src-tauri/target/**/release/bundle
    
    removes the directory entry but not its contents — @actions/glob needs a trailing /** — so
    bundle/nsis/*.exe from a previous run is restored into the workspace.
  2. Locate NSIS installer then does
    EXE=$(find "$BUNDLE_DIR/nsis" -name '*.exe' -type f | head -1)
    and takes whichever entry comes first, with no check that it's the one this run built.

So the canary can hand you a green artifact that does not contain the commit you dispatched it for,
with nothing in the run to indicate it. I nearly drew a conclusion about §4 from one of these before
hashing it — the artifact's ID and timestamp are legitimately new each run even when the bytes are
recycled, so only the hash distinguishes them.

What worked in my fork, if it's useful: delete bundle/nsis after the cache restore so exactly one
installer can exist, and have the locate step assert the filename carries the version the run
derived, erroring on more than one match rather than picking. Fixing the glob to
!desktop/src-tauri/target/**/release/bundle/** would address the cache half, but the assertion is
worth having regardless — it's the part that turns a silent wrong answer into a failed step. Happy
to open this as its own issue or PR against main rather than tangling it with yours.

Still outside this PR

With this build installed, enabling sharing on the 5060 Ti fails as expected:

mesh native runtime failed to install or load: load native runtime
meshllm-native-runtime-windows-x86_64-vulkan from
C:\Users\<user>\AppData\Local\mesh-llm\native-runtimes\0.74.0\meshllm-native-runtime-windows-x86_64-vulkan:
LoadLibraryExW failed

That confirms the resolver rejects windows-x86_64-cuda12 on sm_120 and falls through to
vulkan, rather than attempting cuda12 and failing there. I also re-checked whether bundling a
newer libstdc++ could rescue the Vulkan path: after the bootstrap runs, the DLL Buzz bundles is
MSYS2 GCC 16.2.0's, and parsing its export table it is also missing all five symbols (7058
exports). So no available libstdc++-6.dll on this machine satisfies ggml.dll — which, usefully
for §2, means the skip-if-present guard costs nothing today.

Unchanged from my earlier comment, and not an argument against merging: sm_120 is published only
for linux-x86_64-cuda13, so on RTX 50-series the resolver correctly rejects windows-x86_64-cuda12
(gpu_arches = 75,80,86,87,89,90) and falls through to the MinGW runtimes, which don't load. A
reporter on the upstream issue has also now measured that the coordinator excludes v0.74.0 peers
from stage-splits on stage_protocol_generation, so pinning back isn't a workaround either. Expect
#2836 reports to continue from Blackwell users after this merges.

Signed-off-by: stevepresley <github@stevepresley.net>
@jds1-ai

jds1-ai commented Aug 10, 2026

Copy link
Copy Markdown

Our comments crossed — mine went up about twenty minutes after yours and doesn't reference it, sorry
about that. It does answer "give it another go", though: everything in your list is tested above on
Windows 11 / RTX 5060 Ti. Short version — bootstrap PASS on a stock runner (MSYS2 branch; the
Chocolatey fallback still hasn't been exercised by anything), VRAM now reads 16 GB, and catalog
probing is flash-free.

One thing worth separating out, because your description and the code differ slightly:

Buzz no longer overwrites DLLs already present in the downloaded MeshLLM runtime lib directory.

That's the right intent, and it settles the question I raised — but dst.is_file() can't distinguish
a DLL the archive shipped from one Buzz itself copied there on an earlier run. The
diagnostic log shows the consequence: it skips all four in the cuda12 lib directory, and that
archive ships none of them. They're only there because a previous build of this PR, with the
unconditional copy, put them there.

So the guard reads as "never replace the archive's files" but behaves as "never replace anything,
including our own". Once Buzz has populated a runtime directory, no later Buzz can update it — a
future release bundling corrected DLLs would silently no-op on every machine that ran an affected
build. If you want the stated semantics, the discriminator has to be provenance rather than
presence: track what Buzz wrote (a sidecar manifest, or compare against the bundled resource's hash
and replace only on a match to a previous bundled version), so archive-provided files stay
untouched while Buzz's own stay updatable.

Not urgent for this PR — as noted above, no libstdc++-6.dll available on this machine satisfies
ggml.dll anyway, including the MSYS2 gcc 16.2.0 one the bootstrap now bundles, so nothing is being
lost today. It's the upgrade path I'd want on the record.

On validation: cargo fmt / git diff --check / parse check / cargo check --features mesh-llm are
all static, and per §3 above nothing in this PR's CI executes the bundler — so the canary run is
currently the only thing that does. Worth knowing that the canary has its own stale-artifact bug
(§5) which will hand you a previous run's installer; I'd fix that before trusting a canary build to
tell you whether a change worked.

I'm trying to get another box with an nVidia card setup to test locally as well.

Happy to keep covering that in the meantime — this machine is a 16 GB Blackwell (RTX 5060 Ti, sm_120)
on Windows 11, which is also the configuration that hits the runtime blocker, so it exercises both
the working paths and the broken one. Push a revision and I'll build and test it the same way.

@jds1-ai

jds1-ai commented Aug 11, 2026

Copy link
Copy Markdown

Two things — one on 3aaea807 which landed while I was writing, one correcting a claim of mine
earlier in this thread.

3aaea807 is the right diagnosis, but it makes the 4 GB problem live rather than fixing it

Good catch on the serde mapping: rename_all = "PascalCase" turns adapter_ram into AdapterRam,
which never matches WMI's AdapterRAM. Before that commit the field always deserialized to None,
so parse_one dropped every controller and the fallback returned an empty list — effectively no
VRAM figure at all.

With the rename in place the fallback now genuinely reads the value, which is exactly the case I
measured above: on this 16 GB card WMI returns AdapterRAM = 4293918720 (4095 MiB), because it's a
uint32 and saturates just under 4 GiB. So the path that previously produced nothing will now
produce a confident 4 GB for any card above that. The saturation point in my earlier comment
still stands, and this commit is what makes it reachable.

Worth knowing how it got through, because it bears on §3 of my earlier comment: the unit test
shipped alongside it asserts

vec![("GPU A".to_string(), 8_589_934_592), ("GPU B".to_string(), 4_294_967_296)]

which the pre-3aaea807 code could not have produced — it would have returned an empty vec, so that
test must have been failing. Your validation list was cargo fmt / git diff --check / parse check
/ cargo check --features mesh-llm, and cargo check doesn't run tests; the PR's own checks are
zizmor, Semgrep OSS and DCO Check. So nothing in the loop would have surfaced it. A cargo test -p buzz-desktop --features mesh-llm locally would have.

And once you do run it, note both fixture values are above u32::MAX4294967296 is exactly one
byte past it — so AdapterRAM can never return either, and the test still can't catch the
saturation. 4293918720 for a large card is the fixture that bites.

Correcting myself: there is a working Windows runtime, just not a GPU one

I said there was no working Windows path at all. That's wrong for CPU. Upstream rebuilt the Windows
cpu runtime with MSVC between mesh-llm v0.74.0 and v0.75.0, and it loads clean — verified
by import table, not filename:

archive ggml.dll imports toolchain load
v0.74.0 vulkan libgcc_s_seh-1, libstdc++-6 MinGW 127
v0.75.0 vulkan libgcc_s_seh-1, libstdc++-6 MinGW 127
v0.75.0 cpu msvcp140, vcruntime140, vcruntime140_1 MSVC ok

The switch is cpu-only; I re-downloaded v0.75.0 vulkan to confirm. Details and a matching
retraction upstream: Mesh-LLM/mesh-llm#1215.

What it does and doesn't change here:

  • GPU story unchanged. vulkan is still MinGW and still fails, sm_120 is still published only
    for linux-x86_64-cuda13, so everything I reported about Windows + RTX 50-series stands.
  • A CPU participant looks reachable, but not from here. Cargo.toml pins all six mesh crates to
    v0.74.0, and skippy_abi differs across versions (0.1.32 / 0.1.35) and is matched exactly, so
    SDK and runtime have to move as a pair. Deliberate work, not a bump — and whether CPU-only sharing
    is worth offering is a product call.
  • One interaction: the v0.75.0 cpu archive ships no MinGW runtime DLLs at all (it carries
    vcomp140.dll and drops the lib prefix), so for that runtime
    bundle-windows-mesh-runtime-deps.ps1 would have nothing to do. The bundler stays necessary for
    vulkan.

Not asking you to widen this PR — it does what it says and I'd merge it. Flagging it because "no
Windows path exists" was my framing and it turns out to be true only of the GPU path.

Signed-off-by: stevepresley <github@stevepresley.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants