Skip to content

[Metal] thread-scoped command-buffer commit limits - #36

Merged
Gajesh2007 merged 3 commits into
mainfrom
claude/super-cmdbuf
Oct 21, 2026
Merged

Gajesh2007 merged 3 commits into
mainfrom
claude/super-cmdbuf

Conversation

@jonathan308

@jonathan308 jonathan308 commented Oct 16, 2026 •

Copy link
Copy Markdown

Summary

Adds a per-thread override for when Metal command buffers commit. mlx_metal_set_thread_command_buffer_limits(max_ops, max_mb, &prev_ops, &prev_mb) makes command buffers encoded on the calling thread commit after max_ops operations or max_mb megabytes of distinct inputs, instead of the device defaults. Zero keeps a default, and the previous values are returned so the caller can restore them.

Evaluation encodes on the thread that calls eval/async_eval. A caller can therefore give single-token decode steps larger buffers, so the CPU encodes further ahead of the GPU, while prefill keeps the defaults and the memory bound they give. On Ultra parts the defaults are 50 ops / 50 MB, and the MB counter includes weight inputs, so multi-GB MoE expert banks force a commit after almost every expert operation.

Partitioning work into command buffers does not change results. The device defaults and the MLX_MAX_OPS_PER_BUFFER / MLX_MAX_MB_PER_BUFFER environment overrides behave as before when no thread limit is set.

fork.yaml describes the change, and scripts/check_forkdiff.py passes against upstream main.

Why not the environment variables

MLX_MAX_OPS_PER_BUFFER=1000 MLX_MAX_MB_PER_BUFFER=4000 speeds up decode just as much. But the setting is process-wide: temporaries live until their command buffer completes, so an 8K-token prefill of Nemotron 3.5 Super peaked about 75 GB higher.

Layers

Layer PR
Layr-Labs/mlx this PR
Layr-Labs/mlx-swift Layr-Labs/mlx-swift#57 (GPU.withCommandBufferLimits)
Layr-Labs/mlx-swift-lm Nemotron Super decode scope, to be merged into Layr-Labs/mlx-swift-lm#315
Layr-Labs/d-inference pins via Layr-Labs/d-inference#1517

Test

Full Nemotron 3.5 Super NVFP4 checkpoint, M3 Ultra (60-core GPU, 256 GB), release build. Greedy decoding, 27-token prompt, 64 tokens. The engine scopes only single-token plain decode steps, at 100 ops with no MB limit. Prefill, mixed steps and speculative rounds keep the defaults.

Commit limit plain, 1 row plain, 8 rows MTP, 1 row MTP, 8 rows
Defaults (50 ops / 50 MB) 34.2 tok/s 81.9 tok/s 47.3 tok/s 61.3 tok/s
Scoped: 100 ops, no MB limit 43.0 tok/s 91.4 tok/s 47.9 tok/s 61.3 tok/s
  • Limits of 200, 300 and 1000 ops gave the same rates as 100.
  • The MLX peak during decode is unchanged: 75.8 GB at 1 row, 79.2 GB at 8. The 8K-prefill peak is unchanged at 81.07 GB.
  • Outputs:
    • Under every limit, the greedy loop reproduces all 60 golden tokens.
    • Speculative (MTP) tokens equal the plain tokens.
    • All 8 batched rows match their 1-row tokens.
  • The SDK debug suite on this MLX build ran 249 tests: 0 failed, 21 skipped.
  • The fork's own C++ unit tests were not run.

Merge Danger

Door: two-way. This is an additive C entry point and a thread-local check in Device::get_max_ops_mb_per_buffer. With no thread limit set, behaviour is unchanged.

🤖 Generated with Claude Code

jonathan308 and others added 2 commits October 16, 2026 07:14
Add mlx_metal_set_thread_command_buffer_limits(max_ops, max_mb, ...): the
calling thread's command buffers commit after max_ops operations or
max_mb megabytes of distinct inputs instead of the device defaults (zero
keeps a default; the previous values are returned for restoring).
Evaluation encodes on the thread that calls eval or async_eval, so a
caller can give decode steps larger buffers (the CPU encodes further
ahead of the GPU) while prefill keeps the defaults and their memory
bound. The defaults (50 ops / 50 MB on Ultra parts) count weight inputs,
so multi-GB expert banks commit after almost every MoE operation.
Partitioning work into command buffers does not change results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Declare mlx_metal_set_thread_command_buffer_limits with MLX_API, as the sibling gemma4 entry points are, so a shared libmlx built with hidden visibility exports it. Include the header from device.cpp only, and keep the C++ helper in the file's anonymous namespace.
@Gajesh2007
Gajesh2007 merged commit 71271f0 into main Oct 21, 2026
10 of 11 checks passed
Gajesh2007 added a commit to Layr-Labs/mlx-swift that referenced this pull request Oct 21, 2026
Source/Cmlx/mlx moves from the PR branch tip 5e28974c to the squash-merged main commit 71271f08, which carries the same change plus the MLX_API export of mlx_metal_set_thread_command_buffer_limits.
Gajesh2007 pushed a commit to Layr-Labs/mlx-swift that referenced this pull request Oct 21, 2026
Exposes the fork's thread-scoped Metal command-buffer commit limits (Layr-Labs/mlx#36) to Swift as GPU.withCommandBufferLimits(_:_:) with GPU.CommandBufferLimits: command buffers encoded on the calling thread commit after the given operation count or megabytes of distinct inputs and outputs instead of the device defaults; the previous limits are restored on exit (also when the body throws). Results do not depend on the partitioning. Adds the C header Source/Cmlx/include/mlx/command_buffer_limits.h, pins Source/Cmlx/mlx to the merged mlx#36 (71271f08), and covers the API with CommandBufferLimitsTests (nesting, throwing body, negative values, thread isolation, identical results). fork.yaml describes the change.
Gajesh2007 added a commit to Layr-Labs/d-inference that referenced this pull request Oct 21, 2026
Brings in master through 927172b (#1394, #1524) and the merged-dependency pins through the parent: mlx 71271f08 (Layr-Labs/mlx#36) and mlx-swift 81f58d79 (Layr-Labs/mlx-swift#57). The libs/mlx-swift-lm conflict resolves to 8df7bb62, the squash-merged Layr-Labs/mlx-swift-lm#317 on main (same tree as the SDK this branch's fixes were tested against, a00f4730).
@Gajesh2007

Copy link
Copy Markdown
Member

Merge-readiness review (independent reviewer agents + local validation) before merging:

  • Verified thread-locality: CommandEncoder::needs_commit / get_max_ops_mb_per_buffer run only inside gpu::eval on the thread that calls eval/async_eval; every eval commits its buffers before returning, so no buffer mixes two threads' limits. Partitioning does not change results.
  • Fixed (f7fb1563, in the squash): the C entry point is declared with MLX_API like the sibling gemma4 entry points (libmlx builds with hidden visibility, so a shared build would not have exported it); the header is included from device.cpp only; the C++ helper moved into the file's anonymous namespace.
  • clang-format 21.1.8 clean; compiled through mlx-swift's Cmlx; Swift-side coverage added in GPU: thread-scoped command-buffer commit limits mlx-swift#57 (CommandBufferLimitsTests). Squash 71271f08 has the same tree as the reviewed head.

Gajesh2007 added a commit to Layr-Labs/d-inference that referenced this pull request Oct 21, 2026
…ion slot (#1517)

* refactor(nemotron-super): open the pinned checkpoint in ProviderCore

NemotronSuperCheckpoint reads the pinned index, template, configuration
facts and shard set, then streams every shard against its pin before any
tensor header is parsed. The qualification runner now uses it instead of
its own copy, and the managed provider load can share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): owner serves its slot's model id under an admission floor

The load transaction, builder, bindings and request plan take the model id
they serve (the qualification hook keeps its Hugging Face id), and the LOAD
claim's KV headroom can be smaller than the grant, which stays the engine's
admission ceiling. Staging is a seam: the pinned checkpoint (config
identity, index and shard pins) in production; fixtures stage through the
same claim, fences and builder. The chat route exposes one engine per raw
body with media optionally refused, partitions text prefix reuse by a
per-request cache scope, refuses logit_bias it cannot honor, and reports
the reference prompt length for usage floors. NemotronSuperServingPolicy
holds the production identity, defaults and load plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): production discovery and a managed provider slot

Discovery advertises exactly nvidia-nemotron-3.5-super with the canonical
nemotron_h_omni declaration and the pinned config and index; the registry
id with other metadata is withheld and every other id of the architecture
stays unsupported, as on providers without this change. Its ModelInfo
carries the managed LOAD footprint (installation and head claim, prefill
lane, admission KV) and no media.

ensureModelLoaded hands the load to ProviderLoop+NemotronSuper: the grant
comes from the ordinary re-slice held under the re-slice gate until
installation, NemotronSuperLocalLoad claims LOAD and builds the owner
(depth-3 rectangular MTP and eight requests by default; mtp_mode, the kill
switch and an explicit engine_v2_max_concurrent change them; the plain
owner reuses exact text prefixes when the grant holds the bank), and the
slot retires only through the owner. Generic factories, the standalone
server, warm rebuilds and the generic local engine refuse Super.

Coordinator and unified local-endpoint requests run on the owner's chat
route: pinned prompt contract, reasoning, tools, structured output,
cancellation and usage, with prefix reuse partitioned by the authenticated
scope and no cache receipts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): gated full-checkpoint run of the managed slot

DARKBLOOM_SUPER_MANAGED_LIVE=1 with NEMOTRON_SUPER_CHECKPOINT_DIR loads the
pinned checkpoint through the production loader and serves one encrypted
coordinator request; the capacity quote case checks cold quotes against the
managed LOAD footprint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): managed slot, discovery, RAM floor and registration

Inference describes the managed slot and its load plan; the registry pages
describe exact-identity discovery and the registration contract; hardware
pages derive the 192 GB floor from the default LOAD footprint; configuration,
direct mode and test pages cover the controls, local endpoint and suites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the Super production head ed14432e

The SDK makes NemotronSuperCheckpointPin public so the managed load opens
every shard against the same pins NemotronSuperInstallation stages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(nemotron-super): apply the production refactor review

The managed load releases the re-slice gate at one success and one failure
site, the generic re-slice shares the shrink, restore and grow helpers, and
an identity helper replaces repeated checks. A load that retains a native
fault no longer restores survivors' larger grants while its weights stay
resident.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): serve images on the speculating owner

The owner's image binding no longer refuses work when text speculation is
installed: the engine runs each image request target-only, with no draft
or assistant state, while text keeps speculating. The slot routes inline
JPEG/PNG requests from the coordinator and the local endpoint to that
binding and is marked as serving images.

Tests cover image output equal to the plain owner's, text that stays
token-exact beside an image, coordinator and local image requests on the
managed slot, an image arriving mid-stream beside speculating text, and a
media reservation that does not fit refused before allocation. A gated
full-checkpoint test asks PNG and JPEG questions and runs one beside text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): advertise image input with its media charge

Advertise image input for nemotron_h_omni only under the exact registry
id nvidia-nemotron-3.5-super; every other id, alias or spelling stays
text-only, and discovery still withholds the id without its pinned
config and index. The id now lives in ModelMediaPolicy and the serving
policy references it.

The serving policy states the image binding's worst-case reservation for
one encoded request with 1...16 images: ingress at the 8 MiB body and the
pinned template, each image's decoder workspace at the 64 Mi pixel
ceiling, both copies of the decoded body, visual tokens up to the 16,384
context and one largest-grid (13,312 patches) tower and projector
activation. The discovery footprint adds the single-image charge
(3,888,385,020 bytes), so estimated_memory_gb becomes 126.32 GiB;
native_load_transient_bytes is unchanged, as media is not LOAD.

Capacity quotes for Super vision probes charge that reservation: cold
probes against free_for_load_gb beyond the footprint's single image,
resident probes against load_usable_gb net of load_headroom_gb, and more
than 16 images is a capability reject. Text probes and other models keep
their quotes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): images on the managed slot and their memory

Inline JPEG and PNG images are served on the slot, target-only on the
speculating owner; the registry input, direct-mode controls, discovery
and changelog say so. The discovery footprint now includes one image
request's media charge (126.3 GiB); the RAM derivation is updated and
192 GB still holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): advertise forced tool_choice for the registry id

The Super owner prompt-forces required and named tool calls on text
requests and withholds output until the call validates, so the
provider lists `nvidia-nemotron-3.5-super` in `tool_constraint_models`.
Image requests still refuse tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the Super image head c1879c2e

Pins the SDK head whose Super MTP targets serve image requests
target-only, and lists its new test file in the SDK harness template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): check streamed image responses for leaked media

SSE frames start with `data:`, so the hook test now checks that no
base64 media reaches a streamed or JSON image response instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): list the image tests of the managed slot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): opt-in paged attention KV for the Super owner

DARKBLOOM_NEMOTRON_SUPER_KV_BACKEND=paged builds the owner's engine on the
segmented native paged backend (explicit: a paged construction failure
fails the load). The engine's process-memory owner then charges pages and
recurrent rows and the bridge reserves no request bytes, so the owner
reserves every configured request's decode-row workspace from before the
first request until native retirement, like the prefill lane. The prefix
bank lives in the contiguous engine: an explicit opt-in with paged fails
the load and the managed plan keeps its default bank off. Tests cover the
switch, a paged owner serving concurrent text and images with every charge
retired and text equal to contiguous, paged speculation equal to plain
paged text, and the prefix refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): paged KV switch, runner charge rows and harness lists

The runner reports the paged owner's decode rows as decode_rows and counts
them among held request charges; the harness templates list the paged SDK
and provider suites; configuration, architecture and runner docs and the
changelog fragment describe the switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): exact text prefix reuse on the speculating owner

The default production owner (depth-3 exact rectangular speculation) now
gets the same resident checkpoint bank as the plain owner: the MTP target
is built with `exactPrefixCheckpoints` and the SDK assistant checkpoints
its queued prompt history beside the target state, so a restored request
drafts from the same history as a cold one. `loadPlan` turns
`DARKBLOOM_PREFIX_CACHE_MEMORY` on for either owner when the grant holds
every request's fixed bytes beside the bank (an operator value or kill
switch still wins); the eight-request default at a 100 GiB grant now
claims 27 GiB of KV headroom including the 1 GiB bank.
`NemotronSuperLocalPrefixCache.config` no longer takes `speculates`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): prefix reuse on the speculating owner

The provider prefix test now also runs a speculating owner (depth 3,
exact rectangular): restored text equals a second speculating owner's
cold text and both owners hold their lane and bank until retirement. The
SDK harness template lists the speculative prefix suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the speculative prefix head 05a81d81

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): retire an unpublished paged owner when its decode rows cannot be sized

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): fail closed on non-finite image features

The owner checks the evaluated tower and projector features before
submission; a non-finite value fails the request instead of decoding as a
stream of `<unk>`. A regression test decodes one ImageIO picture as PNG
and as JPEG and checks that both normalize alike and stay inside the
reference normalization's range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): re-sample memory while an unloaded slot's pages return

On the full checkpoint macOS returns about 40 GiB of an unloaded Super
slot's pages up to a second after Metal drops them, so a reload right
after an unload was refused. A Super retirement now opens the bounded
two-second re-sample window that a Qwen4 retirement already uses; a load
in that window samples again instead of refusing. The gate itself is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): reload, image concurrency, video, forced tools, live runs

Fixture tests: unload releases the owner, engine, model and head and the
slot reloads while the OS still withholds the unloaded pages; a second
concurrent image gets 429 and video gets 400 with nothing held; forced
tool_choice validates required and named calls and refuses plain text.
Gated full-checkpoint tests move to their own file and add a greedy text
identity run for comparing builds, a reload run and an image matrix
(sizes, formats, content check, concurrency, history, reasoning).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(nemotron-super): apply the image refactor review

Comments and docs that still said a speculating owner refuses images now
describe target-only image requests; the managed route's signature is
current; the Super model type is one constant beside its id. The docs
record the saturating activation-overflow policy and the reload window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): exact prefix reuse of image turns

With the owner's checkpoint bank and a cache scope, an image request is
bound to an identity of its evaluated features and spans
(`PreparedSubmission.hybridPrefixIdentity`), hashed while its media
charge still covers the copy, and submitted with prefix caching on in
that scope: a later turn with the same images at the same positions
restores the deepest chunk checkpoint, on the speculating owner
target-only. The HTTP hook and managed slot pass the request's cache
scope to the image binding as they do for text; without a scope an image
request stays cold and never publishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): image turn prefix reuse on both owners

The provider prefix test runs a direct image turn on the plain and on
the speculating owner: the next turn with the same image restores the
deepest chunk boundary and streams the cold owner's text, and an image
request without a cache scope neither restores nor publishes. The SDK
harness template lists the bound-image suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(nemotron-super): apply the speculative prefix refactor review

Bank and test docs describe image turns and the shared bank of both
owners. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the speculative prefix head c9cef667

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): direct mode reuses prefixes of image turns

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): bound the live reasoning image case; runner doc

A debug build decodes about three tokens a second, so the reasoning-on
image case keeps its output inside the request deadline. The runner doc
no longer says the speculating hook refuses images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the Super saturating head 9930b553

Pins the SDK head whose Super installation and MTP head quantize
activations with saturating overflow, plus the media-fallback refactor
review, and lists the new SDK test files in the harness template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): drop the PNG/JPEG decode diagnostic print

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): cite the SDK doc by its submodule path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): run the generic checkpoint suites in the SDK harness

The prefix bank's causal-media and durable-store gates change shared CBv2
code, so the Super SDK harness template also lists the hybrid media
identity/lifecycle and complete-checkpoint suites that cover it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): gated full-checkpoint paged logit comparison

NemotronSuperPagedLiveTests stages the pinned checkpoint once and runs
one 16,000-token prompt in 2,048-token chunks and 64 decode steps on
contiguous and paged attention KV, plus a contiguous arm prefilled in
1,024-token chunks that calibrates the trunk's sensitivity to attention
reduction order. Paged prefill must be bitwise equal; paged decode must
stay within the larger of 0.1 and twice the calibration arm's deviation.
Gated by DARKBLOOM_SUPER_PAGED_LIVE=1 and NEMOTRON_SUPER_CHECKPOINT_DIR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): do not race the response tracker after a reset

After the client resets the image stream, the transport may already have
retired the response body, so the tracker can read 0 or 1. The test now
asserts what the reset must not change: the native pump and its media and
KV charges stay held until the terminal gate opens, and the tracker and
charges reach zero after retirement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): size the prefix bank from the KV grant

The pinned checkpoint's owner sized its exact-prefix checkpoint bank at
min(1 GiB, grant / 8), about two long prompts, however large the grant.
NemotronSuperServingPolicy.defaultPrefixBankBytes now takes half of what
the grant leaves beside every request's fixed bytes, the prefill lane
bound and one image's media charge, at most a quarter of the grant (the
engine's own ceiling after a re-slice) and 16 GiB, in 256 MiB steps, and
no bank below 1 GiB. The managed slot's load plan and the builder size
the same bank from the same grant, requests and keys; the plan turns the
switch on only for a non-zero size and adds the bank to the LOAD claim,
and the bank is carved from the grant and reserved until native
retirement exactly as before. An operator size still wins, synthetic
checkpoints keep the generic default, and the Super bank allows one
entry per 256 MiB (at least 32).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the merged Super production head b5608a59

The SDK head merges the speculative prefix reuse line, the fused SSD fix
and the engine's fail-closed check for non-finite sampled logits; the
harness template lists the new logit tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): draft depth by decoding requests on the managed slot

`DARKBLOOM_NEMOTRON_SUPER_MTP_DEPTH` also takes a comma list of up to
eight depths 1...3: entry N for N requests decoding in the step, the
last for more (rectangular verification only; the largest entry sizes
admission). The managed slot's speculating owner now defaults to
`3,2,2,2,1` instead of a fixed depth 3, keeping an operator's value.

Measured on the full checkpoint with natural prompts (greedy, every
output byte-identical to the plain owner's), aggregate tok/s for the
plain owner and depths 1 / 2 / 3: one client 32.5 and 47.3 / 54.9 / 57.0,
two 50.7 and 60.9 / 65.0 / 63.9, four 67.2 and 77.3 / 78.4 / 76.7, eight
82.8 and 86.9 / 84.7 / 80.5. These numbers need the SDK's cached
recurrent spec (mlx-swift-lm claude/super-serving2); without it every
depth served slower than plain decode.

A new policy test pins the managed grant on the measured 256 GB Mac: a
lone slot's re-slice grant is the whole fleet KV budget (above 150 GiB),
which holds the speculating and plain admissions uncapped and the
25.9 GB minimum for eight speculative requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): serving policy benchmark and runner engine counters

The qualification runner gains `GET /runner/engine` (cumulative
speculation counters and, with CBV2_STEP_PROFILE=1, step-profiler phase
timings) and `POST /runner/engine/profile/reset`; its identity records
the depth table; and its speculative KV default scales with the width
like the managed slot's admission (26 GiB for eight requests) instead of
a flat 12 GiB that refused MTP x 8.

`bench_serving.py` serves 16 natural prompts (code, chat, math and
long-form; `serving_prompts.json`) greedy at several client counts,
optionally behind nonce-led filler documents, and records per-request
TTFT and decode rate, aggregate throughput, acceptance, depth
selections, per-level memory windows and every output's text;
`compare` checks byte-identical output across receipts and levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): observe in-flight charges without racing event delivery

Under a loaded full suite the owner test's event callbacks ran after the
request had retired, so it never saw the request charge beside the lane.
The test now holds the request before its native terminal, reads the
budget there (request charge and lane), then lets it retire. The image
cancel case accepts a request that finished before its first chunk was
consumed, since the fixture can buffer every token first; it still
requires no other error, native retirement and a usable bridge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the Super decode fusion head 484482d6

The SDK head adds bit-identical fused router-select, expert combine,
Mamba step and residual-norm decode kernels; the harness template lists
their tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): serve 65,536 tokens of context, accept up to 262,144

The managed slot serves and advertises 65,536 tokens (was 16,384). The
bridge builder, the image binding and the runner accept up to
NemotronSuperServingPolicy.maximumContextTokens (262,144), verified on
the full checkpoint: needle retrieval from 31,935 to 261,160 tokens and
finite logits through 131,056 tokens with each prefill chunk inside the
lane.

The served context is bounded by the default speculating owner: its
assistant's fixed request charge grows 30,216 bytes per token of the
configured window. At 65,536 it is 4,429,739,396 bytes and eight
requests' LOAD claim (36 GiB of KV headroom) is admitted on 256 GB; at
131,072 the claim is refused. The speculative request bound, the
admission KV, the discovery footprint (136.3 GiB) and the media charge
(sixteen largest images now fit the context) follow; the plain owner's
fixed bytes do not change.

NemotronSuperLongContextLiveTests (gated) prefills the pinned checkpoint
to the served context and checks finite logits and the per-chunk
transient against the prefill lane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): the default claim with the speculating owner's bank

At the 65,536-token context the default eight speculating requests with
the 1 GiB prefix bank claim 130.7 GB at LOAD and are admitted on 256 GB
(measured minimum grant 39,507,064,973 bytes, below the 37 GiB claim).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): pin the bank in the slot floor; runner managed load claim

The re-slice floor and the minimum grant of a Super owner include its
prefix bank exactly once. A re-slice cannot safely give the bank up
today: the engine only lowers the bank's own limit to a quarter of the
new grant and keeps entries pinned by in-flight restores and captures,
and the shared ledger holds the bank's whole reservation until native
retirement, so a floor without the bank would hand a newcomer memory the
bank still holds. A test pins the difference at the bank's budget.

The qualification runner's --managed-load-claim holds the managed
slot's LOAD KV headroom (requests' fixed bytes, 2 GiB and the default
bank) instead of the whole grant, so a production-sized grant loads as
it would in the provider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): gated long-context fact retrieval through the slot

With `DARKBLOOM_SUPER_NEEDLE=1` the live suite asks for one fact from a
synthetic document of about 32,000 tokens through the managed slot's
served context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): bound the default bank by the load's ledger room

The builder reserves the prefill lane and then the bank while the LOAD
claim (installation, any head and admission KV including the bank) is
still held and the installed weights already count as MLX usage. On the
256 GiB machine a 16 GiB default bank therefore did not fit: the
production-sized claim was refused on the full checkpoint. The default
is now also at most half of the memory cap less the activation reserve,
twice the LOAD claim bytes, the requests' fixed bytes and the lane bound.
A lone slot gets no default bank on 192 GiB, 8.5 GiB for the speculating
owner (16 GiB plain) on 256 GiB and 16 GiB on 512 GiB. The loadPlan and
the bank config take the machine's memory for tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): one LOAD claim holds the pinned owner, each byte once

The managed load kept its whole LOAD claim (installation, head and
admission KV with the bank) until the owner was built, while the
installed weights already counted as MLX usage, and then admitted the
prefill lane and the bank on top: the weights and the bank were counted
twice. A 16 GiB bank failed to load on 256 GB, and the speculating owner
could not pass the post-install recheck on 192 GB with any bank.

The pinned checkpoint's single claim, taken before any allocation, now
also holds the lane at its measured bound and the bank
(NemotronSuperLocalBridgeBuilder.pinnedOwnerShares), with the bank in
the claim once. A load that cannot hold its whole owner is refused
before a shard is read into memory. Once the installation and head are
resident, their share passes to live usage (reducePendingLoad, as the
ordinary managed load does), and the builder hands the lane and bank
shares to their own reservations right before taking them. The default
bank's load bound counts the claim once: a lone slot gets 16 GiB on
192, 256 and 512 GiB. Tests pin one claim that refuses one byte short
and loads exactly, and the arithmetic per machine size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): per-token draft history charge; runner MLX limits

The bridge prices a sequence-scaled MTP declaration linearly: each
request's shared reservation adds the assistant's 30,216 bytes per
token of prompt plus max_tokens beside the 8,192 of KV, and the
projection's slack joins the fixed bytes so the reservation bounds the
engine's charge. The speculating owner's fixed request bytes fall from
4,429,739,396 at 65,536 configured tokens (6.41 GB at 131,072) to
2,450,016,143 at any context; eight speculating requests at 131,072
configured tokens need a 22.8 GB minimum grant and load. The policy's
speculative bound becomes 2.5 GiB, the default admission KV 22 GiB
beside the bank and the discovery footprint 122.3 GiB.

The runner now pins MLX's memory limit and 8 GiB buffer-cache cap with
MLXMemoryGuard before loading, as the managed load does; without it
MLX kept freed buffers to its default limit and the shared budget,
which counts cached buffers as used, refused work the provider admits.
Its ready file reports the limits and each request's per-token rates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): LOAD claim holds the larger sequential transient, not both

The managed load claimed the installation quote (payload plus staging and
backend allowance) and the head supplement (payload plus 4x construction)
as a sum, although the installation releases its staging before the head
is constructed. NemotronSuperLoadClaim now claims both payloads and the
larger transient, and the load returns MLX's cached staging buffers
(synchronize, clearCache) before the head loads, under the same MiMo
reclamation guard as the shared budget's reclaimer; when reclamation is
unsafe at claim time both transients are summed as before, and if it
becomes unsafe after installation the load fails closed.

Pinned checkpoint: claim 83,870,320,160 bytes (was 90,948,937,344); the
plain claim is unchanged. Measured on the full checkpoint at the previous
head (37 GiB grant, eight speculating requests): MLX active peaked at
76,711,620,772 bytes and the process footprint at 78,794,480,240 while the
LOAD charge was held. The scan-time footprint drops from 136.32 to
129.73 GiB. The runner logs the claim's parts in load.quote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): gated full-checkpoint memory ledger

NemotronSuperMemoryLiveTests (DARKBLOOM_SUPER_MEMORY_LIVE=1 and
NEMOTRON_SUPER_CHECKPOINT_DIR, exclusive GPU) measures MLX active memory,
its peak and the process footprint around each native phase: installation
and head against their quotes, every 2,048-token chunk of one prompt and
decode steps on contiguous and paged storage (bytes per context token and
chunk transients against the prefill lane), one chunk under the stage
profile's memory recording, and the largest image grid's tower against the
media activation bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): runner emulates a smaller Mac's memory

--emulate-physical-gib N builds the runner's process budget with an N GiB
total and this machine's available memory minus what N GiB lacks, sets
MLXMemoryGuard's limits for N GiB before any weights, and applies the
managed slot's load gate (scan-time footprint plus activation reserve and
minimum KV against available-for-load memory) before loading, refusing as
the provider would. Qualification only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): name every managed load refusal

A refused managed Super load recorded "native construction was refused"
for any failure outside a short list, which hid the post-install LOAD
claim recheck (loadAdmissionLost), a refused decode-row reservation and a
bank the engine did not construct. Each now has its own reason; only an
error from outside the load keeps the generic one. The reasons carry no
paths or payload data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): plan the prefix bank the builder builds

The load plan sized the bank as the pinned checkpoint's from the host's
memory, while the builder sized it from the installed checkpoint, so an
owner that is not the pinned checkpoint planned an admission for one bank
and built another, and the result depended on the machine running it.
The slot loader now carries both inputs, its pinned-ness and the machine
memory it sizes for; the plan, the LOAD claim's shares and the builder all
read them. The production loader passes the pinned checkpoint and the
host's memory, so its behavior is unchanged.

The managed-slot fixture takes its own machine memory. Tests pin plan
bank == built bank for pinned and generic owners at 64 and 256 GiB, the
fixture holding exactly its planned bank on both, and derive the policy
expectations from admissionKVBytes instead of literals that predate the
65,536-token context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(prefix-cache): how the recurrent bank's default is sized

The hybrid bank takes DARKBLOOM_CBV2_HYBRID_PREFIX_BYTES, else a model
policy's own default (the pinned Nemotron Super owner's, from the grant
and the machine), else min(1 GiB, grant / 8). The configuration reference
counted the Super LOAD claim twice in the bank's load bound; the claim now
counts it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): emulation as a dedicated Mac, with its grant and bank

The emulated budget's available memory is the emulated total minus this
process's footprint (other processes are assumed to fit beside the cap),
never above what this machine really has; the default prefix bank is sized
for the emulated Mac (an explicit size, or none when it does not fit), and
load.quote logs the KV grant the provider's re-slice would give there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(nemotron-super): read the lane and LOAD bytes from one policy function each

The LOAD claim's lane share, the builder's lane, the default bank and the
discovery footprint now read the pinned lane bound from
NemotronSuperLocalPrefill.pinnedLaneBytes, the requested chunk from
requestedChunkTokens, and the pinned LOAD bytes from
NemotronSuperServingPolicy.pinnedLoadClaimBytes, so a new lane bound,
installation claim or per-request charge reaches every one of them.
No value changes; a chunk key above 65,536 tokens (up to the 262,144
maximum) now sizes the default bank as it sizes the lane share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin the Super decode command-buffer heads

mlx-swift-lm e03757bc scopes larger Metal command buffers to the Super
owner's plain decode steps and batches the full SSD prefill windows (still
bit-identical); mlx-swift 45c7461 and mlx 5e28974c add the thread-scoped
command-buffer commit limits it uses (C++ only, no kernel change). MTP
rounds are not scoped. The harness template lists the new MLXVLM source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): the load transient runs in the serving room it precedes

The single LOAD claim reserves the owner's lane, bank and KV floor beside
the installation and head, but none of that room is allocated before the
load has returned its transient buffers. With reclamation the claim now
adds only the transient beyond that room: both payloads plus
max(transient, serving room) in all. The plain owner also returns the
installation's cached buffers before serving, and either phase fails closed
if reclamation became unsafe. Without reclamation both transients are
claimed beside the room, as before.

The scan-time footprint is the resident payloads plus the larger of the
transient and the serving room (lane, admission KV, one image): 121.45 GiB
for the default configuration (was 129.73 after the sequential-transient
fix, 136.32 before it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): live ledger reports KV rows and a fenced-prefill arm

The gated ledger prints every attention row's storage type, dtype and bytes
after the first and last chunk and after decode, and repeats the first three
chunks with the residual stream evaluated every 8 and every 2 layers
(setSuperPrefillFenceLayers, measurement only), timed, to separate the
chunk's own working set from intermediates retained by queued command
buffers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(nemotron-super): measured 2 GiB activation floor for the Super slot

The 5.5 GiB default reserve is sized for models whose request charges hold
only KV. Super's requests also reserve their decode row, recurrent
generations and verify/draft workspace, its owner-wide lane covers prefill
chunks and its media charge each image's tower and projector, so the
reserve counted those activations twice. Full checkpoint, M3 Ultra, eight
~500-token requests with default speculation: MLX peaked 5.5 GB above the
pre-request resident set while 27.6 GB of request charges were held; the
only memory no charge covers is what the owner keeps resident after its
requests (0.94 GB speculating, 0.23 GB plain). Floor: 2 GiB. A serving set
with an unmeasured model keeps the default; the operator's raise-only
override still applies. The platform's servability table must carry the
same entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): the LOAD claim's transient, the 2 GiB floor and the memory tools

hardware-support and inference describe the claim (resident payloads, the
serving room, the larger load transient only beyond that room) with the
measured load peak, the 121.5 GiB footprint and the 124.5 GiB gate under the
2 GiB floor; the constants table lists the Super floor. The runner doc covers
--emulate-physical-gib, the load.quote parts and the gated memory ledger
test; the changelog fragment carries the footprint and the reserve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): reserve the speculating owner's round work once

The SDK now reports the assistant's per-owner MTP bytes (`ownerBytes`):
the depth policy's largest round of draft steps, one history chunk,
scratch, the verify window, the captured windows' extra recurrent
generations and the worst-case whole-history copies. Requests no longer
carry these. The local bridge builder reserves them in the shared budget
after the decode rows and before the bank, holds them until native
retirement like the lane, and refuses the build (`roundRefused`) when the
budget cannot hold them. An unresolved declaration reserves Int.max, so it
always refuses.

The runner reports the reservation as the `mtp_round` charge row. Its
ready file adds `request_charge`, which breaks one request's fixed and
per-token charge into engine and bridge fixed bytes, recurrent generation,
decode row, the projected MTP split with its owner, window and step counts,
and the fixed, owner and per-token declarations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): the Super plain decode command buffers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): the one-claim boundary follows the serving-room overlap

oneLoadClaimHoldsThePinnedOwnerOnce computes the claim as the transaction
does: the installation's payload and only the staging beyond the lane, bank
and KV floor it also holds. One byte less free is still refused before
anything is installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): the runner holds the lone Super slot's activation reserve

The runner's budget now carries the serving-set reserve the provider loop
sets for a Super-only serving set (its measured 2 GiB floor) instead of the
5.5 GiB process default, so its ledger and the emulated Mac admit as the
managed slot does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(nemotron-super): split the speculating admission into request and owner bounds

The LOAD claim's KV headroom charged 2.5 GiB per speculating request, which
was measured while each request carried a whole round of MTP work. The SDK
now reserves that work once per owner (`mtp_round`), so the headroom is
split the same way:

- Per request: 576 MiB (measured 533,401,365 at depth 3; 512 MiB plain is
  unchanged).
- Once per owner: `speculativeOwnerBoundBytes`, computed from the engine's
  own counts for the depth table in effect. These come from
  CBv2MTPAllocationLimits: round draft steps, window positions,
  captured-window generations and worst-case history copies. Each count is
  multiplied by its pinned unit (from the ready charges). The total gets
  64 MiB of allocator slack and is rounded up to 64 MiB.
  - Default table, 8 requests, 65,536 tokens: 5.03 GB, against a measured
    4,935,449,637.
  - One request at 133,120 tokens: 2.89 GB, against a measured
    2,791,734,886.
  - It shrinks to 4.36 GB with "3,2,1". A malformed setting counts as
    adaptive at the largest depth (11.9 GB).
- Eight speculating requests now claim 11.19 GiB of headroom instead of
  22 GiB. The discovery footprint drops from 122.3 to 111.5 GiB.

The round is a third LOAD-claim owner share beside the lane and the bank.
The builder hands each share over right before taking its exact
reservation, in the order lane, round, bank, so no byte is counted twice.
It still refuses the build when the engine's exact `ownerBytes` does not
fit. NemotronSuperLocalMTP.config builds the CBv2MTPConfig for both the
engine and the policy.

The runner passes its environment and `--max-context` to the managed
admission and the default grant. Its `request_charge` reports
`policy_owner_bound` and `history_copies` beside the engine's exact
`owner`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(nemotron-super): the 8.5 GiB measured prefill lane

hardware-support carries the lane at the SDK's measured bound and the
footprint and gate it gives (118.0 and 121.0 GiB; 134.4 GiB of physical
memory, 192 GB leaving 51.8 GiB). inference separates the first chunk's
whole-prompt KV allocation (charged to the request) from a chunk's own
transient (7.50-7.67 GB measured, inside the lane). The changelog fragment
follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(nemotron-super): default bank sizes under the split admission

The default bank is half of what the grant leaves beside the admission, the
lane and one image. Eight speculating requests now claim 11.19 GiB instead
of 36 GiB, so the bank at the sample grants is 3.5, 9, 15, 16 and 16 GiB
(34, 45, 60, 100 and 155 GiB grants). The chunk comparison moves to a
50 GiB grant, below the 16 GiB cap, where the 12 GiB and 20 GiB lanes leave
11.5 and 7.5 GiB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the Super long-context head 82fe57b1

The SDK sizes speculation state once per owner and per request and
charges the assistant's growing state per reserved token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(libs): pin mlx-swift-lm at the Super memory merge 620766e9

The SDK prefill lane bound is the measured chunk peak, and the
installation load quote is split into payload and staging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin corrected Nemotron Super workspace documentation

* Preserve Super distribution notices and qualify managed effort controls

* test(nemotron-super): encode effort acceptance bodies before the live client

Swift 6 rejected the @sendable live client closure capturing [String: Any] controls and a local body builder. Every request body is now encoded to Data before the closure; the cases and assertions are unchanged. The full-checkpoint run passed: 16 control variants, 6 tool and 6 JSON cases, 4 refusals and exact cold/warm results for high, medium and none.

* feat(nemotron-super): serve paged KV with exact prefix reuse by default

The managed Super slot could use paged attention KV or the exact recurrent
prefix bank, never both: the builder refused the combination and the plan
kept the bank off on paged storage. SDK 53b11b32 lets the bank host on
segmented paged KV (`CBv2HybridPrefixCacheConfig.permitsPagedStorage`).

- `NemotronSuperServingPolicy.loadPlan` selects paged KV unless the operator
  sets `DARKBLOOM_NEMOTRON_SUPER_KV_BACKEND`, and sizes the default bank the
  same way on either storage. The builder alone still defaults to contiguous.
- The Super bank config opts into paged storage; the factory carves the bank
  from the paged grant as from the contiguous one and hands it to the engine.
  A paged backend that cannot host it fails the load (`unavailable`).
- The `pagedPrefixReuse` refusal and the paged bank exclusion are removed.

Synthetic owners on paged storage restore the deepest chunk boundary after
another request reused the donor's pages and stream the cold paged text,
plain and speculating; managed-slot fixtures now run with 64-wide attention
heads, the paged kernels' minimum, so they exercise the default.

A paged slot with a bank still claims its whole grant (`slotKVBytesClaim`): its segmented pool follows the grant minus the bank carve, as contiguous storage does, so co-resident heartbeats do not count the bank as free.

* docs(nemotron-super): gate the model to Apple M5 Max 128 GB and M5 Ultra

The platform registration now lists min_ram_gb 128 and required_provider_capabilities ["apple_m5"], so Nemotron 3.5 Super is offered only to attested M5 providers with at least 128 GB (M5 Max 128 GB, M5 Ultra). Replace the stale 192 GB floor in the hardware requirements, hardware support, registry reference and changelog fragment with that gate and the existing load-gate arithmetic: the default eight-request speculating owner fits a 128 GB cap with 19.1 GiB to spare, but not beside a 16-image request. Only the 256 GB M3 Ultra is measured on hardware; a physical M5 Max 128 GB run is still pending.

* fix(nemotron-super): honor or refuse every reasoning control, never ignore one

Live checks showed chat_template_kwargs.reasoning_effort, chat_template_kwargs.thinking, reasoning.max_tokens, reasoning.exclude and reasoning.summary accepted with 200 and ignored. NemotronSuperPromptContract.controls now honors OpenRouter's chat_template_kwargs.thinking as enable_thinking (same conflict rules as every spelling) and accepts Responses reasoning.summary levels auto/concise/detailed (the full reasoning is returned). It refuses (unsupportedControl, HTTP 400) any other chat_template_kwargs key and any other reasoning member unless null or exclude:false. Effort levels none/off/0, medium and high are unchanged. The coordinator's prompt sidecar applies the identical rule (Layr-Labs/darkbloom-platform#118).

* feat(nemotron-super): serve the full verified 262,144-token context

The managed slot capped its served and advertised context at 65,536 for
prefill time and long-document recall. Those are routing decisions for the
coordinator, not a provider cap: contextTokens is now maximumContextTokens
(262,144), the ceiling verified on the full checkpoint.

Only the speculating owner's whole-history copies grow with the window
(16 copies for the default table and eight requests, 1,536 bytes per token
each): its round bound goes from 5,033,164,800 to 9,865,003,008 bytes (the
engine reserved 9,767,287,845 on the full checkpoint), admission KV from
11.2 to 15.7 GiB and the default discovery footprint from 93.14 to
97.64 GiB. On a 128 GiB Mac the load gate needs 100.6 GiB under the
115.2 GiB cap and the default prefix bank is 5.25 GiB.

Tests keep the measured references pinned at 65,536 and assert the served
value, the round bound and the footprint at 262,144; the 128 GiB-Mac claim
test runs at both. Docs, the runner guide and the changelog fragment carry
the new figures.

* Pin mlx and mlx-swift to the merged command-buffer limits

libs/mlx moves from the PR head 5e28974c to 71271f08 (squash-merged Layr-Labs/mlx#36, which also exports the entry point with MLX_API) and libs/mlx-swift from 45c74613 to 81f58d79 (squash-merged Layr-Labs/mlx-swift#57, which pins that mlx and adds the API's tests). Same API and Metal kernels.

---------

Co-authored-by: Jonathan Spangler <5055076+jonathan308@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Gajesh Naik <26431906+Gajesh2007@users.noreply.github.com>
jonathan308 added a commit that referenced this pull request Oct 22, 2026
Brings the cluster stack onto the single-Mac release chain. Every single-Mac
commit except three is a cherry-pick of a commit this branch already has, so
the conflicts (unified_memory.rst, custom_kernel.cpp,
process_allocation_cap_metal.cpp, fork.yaml) keep this branch's side, which
also carries the later fenced-input, host-cut and sealed-sum changes. New here:

- #36 thread-scoped command-buffer commit limits (merged cleanly; its fork.yaml
  section is added by hand).
- The single-Mac revert of the page-backed footprint commit does not travel
  through this merge (its net change on the single-Mac side is zero); the next
  commit reverts this branch's original 4d91320 explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants