Skip to content

dasLLAMA: the .dlim arc - explicit config identity, streaming/offline/cross-box conversion, server surface - #3586

Merged
borisbat merged 31 commits into
masterfrom
bbatkin/dasllama-cm2-arc
Jul 29, 2026
Merged

dasLLAMA: the .dlim arc - explicit config identity, streaming/offline/cross-box conversion, server surface#3586
borisbat merged 31 commits into
masterfrom
bbatkin/dasllama-cm2-arc

Conversation

@borisbat

Copy link
Copy Markdown
Collaborator

The .dlim arc: the prepared-model image becomes an explicit, offline, streamable, cross-box artifact — plus the server surface over it, and two master-level CI lane fixes.

The format (one IMAGE_VERSION bump, 6 -> 7)

  • DlimConfiguration (dasllama_config.das): the config that keys an image is now an explicit struct (cpu backend + layout numbers, vulkan section with resolved caps/wants, metal section) instead of probed process state. The identity is a pure formatter of it; the identity string and the config JSON are embedded in the image meta head, so a mismatched load prints what the image was baked for instead of a hash miss.
  • dasllama-convert grows --config / --dump-config (fail-closed identity verify), --stream, -f vulkan offline bake, --trim, --list / --clean (the dlim GC: only never-loadable images are removed; dry-run by default, --apply acts).

Conversion

  • Streaming converter: plan-mode load_big records FillJobs; the fill pass streams source -> transcode -> image without materializing the big q-planes. Byte-identical to eager (SHA-gated on Q4_K_M and Q8_0); GLM-4.5-Air peak commit 81.9 GB -> 16.34 GB. Streamed planes are sized and gap-filled from the LAYOUT (a job-sum concatenation shifted everything past the first allocated-but-never-written hole — caught by the GLM byte gate, cmp-identical across the full 82.4 GB after the fix).
  • Async dwrite writer: double-buffered bands with a lazy writer thread overlap transcode and device time; das API unchanged. Judged GLM A/B (quiet box): streamed 739 s / 16.36 GB peak vs eager 880 s / 81.87 GB peak — conversion is device-bound, streaming wins both axes.
  • prefetch_map (fio): advisory readahead over a mapped range; cold-conversion weight reads -66% (device-saturated). fmap_open_rw: a writable SHARED mapping twin of fmap_open (win32 mmap shim honors PROT_WRITE).

The offline vulkan bake (plan-as-data)

  • The GPU walk's accept/refuse arithmetic runs against the config's numbers on a dry tier — no instance, no device, no VRAM. VkPlanEntry carries a stage role; the slice replay verifies role + shape per gather and is boundary-exact: a would-refuse attempt (the bake never records refused gathers) is served empty via the shared moe_gpu_would_accept arithmetic, so budget-bounded models replay their refusal instead of falling back to live gathers.
  • P3 trim (--trim): the vulkan flavor drops the CPU weight families a resident plan never reads (-44% on Q4KM); embedding rows split into their own planes; all CPU fall-backs fail closed.
  • P2 imported mirrors: streamed expert mirrors import the mapped image window (VK_EXT_external_memory_host) instead of pinned copies where the driver accepts it. NVIDIA/Windows refuses file-backed pages in both protections (probe-verified 2x2); the rail declines once with a named reason and falls back to pinned — IDS parity between arms.

Cross-box (bake an M1 image from a PC)

  • Repack-by-numbers: the gen repack primitives read math's active_* layout getters (value-identical to the stamps in normal serving); a foreign-backend bake overrides them with the target config's numbers and aliases the reported backend. Gates: PC-baked SmolLM2 q8 and Llama-3.2-1B Q4_K_M images are byte-identical to the M1's own bakes (sha256 both families, incl. the differing k6 mr and wscale16); the M1 maps and serves them token-exact.
  • dasllama_metal_shapes: the metal servable predicate's pure-model shape gates extracted portable (registration fires on every build); -f metal pins the portable backend (the donor contract — bare -f metal was broken on both platforms). A Windows box now mints metal-flavor images byte-identical to the M1's; the M1 metal GPU-serve arm passes post-extraction.

Server surface

  • GET /v1/images (per-slot inventory with verdicts), POST /bake (spawns the converter on the bench worker's proven pattern; the GPU owner bakes the vulkan flavor against the live config), automatic GC at start and on bake completion, config GPU knobs lock while a flavor image serves. Live-gated end to end.

Tests / docs

  • New: tests/fio/fio_dwrite.das, fio_fmap_rw.das, fio_prefetch.das; test_model_image peek/config arms; test_model_image_vulkan.das (the offline dry bake's first regression coverage, via run.das --suite image-vulkan). Handmade RST for the seven new fio symbols + the das2rst group.
  • Also rides: exe-panic visibility fix (jit_run_main_guarded never flushed its printer).

CI fixes (master-level, the two red PRs' lanes)

  • darwin26: the macOS-26 runners' paravirtual device cannot lower cooperative tensors — test_metal_tensor_ops now feints on that exact compile-error signature (any other error still fails loudly on Metal-4 hardware).
  • windows-32: the ConPTY probe's 30 s timeout trips on PowerShell cold start under load — 120 s, REQUIREs unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2

borisbat and others added 28 commits July 27, 2026 14:07
…flushed its printer

Copilot caught this on #3581 after it merged. jit_run_main_guarded built a TextPrinter and streamed
"EXCEPTION: <msg>" into it, then returned without calling output(). TextWriter's destructor only
frees largeBuffer; TextPrinter::output() is the sole thing in the class that printf+fflush's. So the
print half of e2b8186 was dead code: a standalone -exe panic set the right exit status and said
nothing.

Why e2b8186's own check missed it: it validated by observation and shipped no fixture, so nothing
automated ever asserted the text. The exit code was correct throughout, which is the half that was
easy to see.

Proven on this build, not argued: a `panic("boom from the exe entry")` fixture compiled with
-jit -exe and run beside its runtime DLLs now prints

    EXCEPTION: boom from the exe entry

and exits 1. The message is verbatim the literal in this function, which is what attributes the
output to this line rather than to some other path on the panic rail.

Still missing, and named here so it is not lost again: a regression test. An -exe fixture in CI is
awkward (it needs the JIT plus a working linker, and clang-cl link discovery is already flaky on
Windows locally), so the honest home for this is a tests-cpp case driving jit_run_main_guarded with
a panicking context and capturing stdout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ruKZPG8G7SPEUyZtvBzcT
…ak commit 150 -> 82 GB

Three pieces, measured on GLM-4.5-Air Q4_K_M (78.6 GB image):

- fio dwrite_* — cache-bypassing strictly-ascending sequential writer
  (open/append/band/commit/stat/close). Win32: FILE_FLAG_NO_BUFFERING +
  preallocate (ascending writes keep NTFS VDL zero-fill off the bill);
  POSIX: fallocate + fadvise(DONTNEED); macOS: F_NOCACHE + F_PREALLOCATE.
  The handle owns a sector-aligned bounce band, so callers append any
  pointer at any size; dwrite_band/commit hand the band out for
  zero-copy producers; dwrite_stat splits stage vs syscall time.
  save_image rides it: buffered long_fwrite was worth 275 MB/s on big
  images, the direct writer sustains 1664-1720 MB/s per plane (rested-
  drive GLM aggregate 760 MB/s; per-plane MB/s now logged >= 256 MB).
  Meta serializes first so the exact file size preallocates up front;
  the header patches through a normal handle after close.

- win32 mmap shim: PAGE_WRITECOPY -> PAGE_READONLY. Copy-on-write
  sections charge commit for every page that COULD be privatized, so a
  73 GB gguf mapping billed 73 GB of private bytes on top of the model
  (measured 149.9 GB peak for the GLM conversion; 81.9 GB after). Now
  matches POSIX PROT_READ and faults loudly on an accidental write.

- utils/dasllama-convert — offline GGUF -> .dlim (clargs; -m/-o/-q/
  -f planar|metal|vulkan/--force/--keep-stale-tmp). Emits the identity-
  hashed name the load path looks for, and sweeps stale
  *.dlim.<ticks>.tmp siblings by default (killed runs leave full-size
  ones; 177.5 GB of them found on this box).

Output verified byte-identical (SHA256) to the previous writer at
2.4 GB and 82 GB; the load path maps the tool's image directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…matter, config rides the image meta

The .dlim bake config is now ONE explicit struct (dasllama_config.das) instead of scattered
host probes: cpu section (backend + layout numbers + native/wscale OUTCOMES), vulkan section
(RESOLVED coopmat mode / vram_mb / all walk-shaping bits — the old tag folded raw env strings
and missed the dense bits + the cls default split), metal section. Tiers register section
sources at [init]; image_identity(tag) = dlim_identity(config, IMAGE_VERSION, tag), no side
effects (the backend select lives in the cpu source). vk_moe_init's coopmat ladder and budget
resolution extracted into shared resolvers (resolve_coopmat_mode / resolve_weight_cap — auto
vram now rounds down to 256 MB so the identity doesn't churn with heap wiggle) so the config
can never drift from what the device runs; the caps probe is phys-level only, the 518ae164a
stream-reserve carve stays in vk_moe_init.

IMAGE_VERSION 6 -> 7: the meta blob now leads with [identity string][config JSON], so an
identity-mismatched load logs "baked for '<X>', this run is '<Y>'" instead of a bare hash miss
(bounds checks moved ahead of the identity check to make that read safe; a wrong-version image
still declines on the header alone).

dasllama-convert: --dump-config writes this box's config JSON (+ identity); --config x.json
bakes against a supplied one — host-independent knobs applied, then FAIL-CLOSED verified
against the resulting identity (an unavailable backend errors with want/have strings; true
cross-box repack-by-numbers is a later arc slice). dlim_config_from_json tolerates a UTF-8 BOM.
Also fixed: quant_from_string took `var q : QuantMode` — a workhorse-type COPY, so the parsed
quant never reached the caller; LINT003 flagged the dead write and was right. Now QuantMode&.

tests/run.das now works on Windows at all: the env prefix was POSIX-only (cmd.exe has no
VAR=val prefix — now `set "VAR=..."&&` like llvm_tune's tuner_cmd), the log dir fell back to a
nonexistent /tmp (now TMPDIR -> TEMP -> /tmp), and the default binary ./bin/daslang picked up a
STALE top-level exe on the MSVC layout (now the binary running the script). TEMP registered in
env_registry (the tripwire caught the new read); utils/dasllama-convert added to the registry
scan roots.

Gates: test_model_image mechanics arm 9/9 (synthetic carrier + header-patch decline matrix),
smol arm 8/8 (real SmolLM2 regenerated at v7 with the embedded config, mapped back),
test_env_registry 14/14, lint clean on every touched file, das-fmt clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…drop the 256 MB rounding

Boris's catch: no hardware works in 256 MB steps — the rounding existed only to stabilize a
number that fluctuates by construction. Heap SIZE is the hardware constant, so auto resolves
identically every run with no rounding at all; WEIGHT_CAP (4.2 GB) dominates on any card
>= ~5 GB anyway, so auto is a constant 4200 MB on real boxes. Transient VRAM pressure is the
memprio shield's job now, not the budget's; explicit DASLLAMA_GPU_VRAM_MB is untouched.
query_heap_info also fills size without VK_EXT_memory_budget (size needs no extension; only
budget/usage do — heaps rank by live budget when the ext reports it, by raw size otherwise).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…, byte-identical to live

The vulkan flavor now bakes OFFLINE: `dasllama-convert -f vulkan` runs the real GPU upload walk
with a DRY tier — the SAME accept/refuse arithmetic (budget, MAX_STACKS, arena shard cap, rdec
guards) against DlimVulkanConfig's numbers, with every device call gated off (no instance, no
device, no VRAM). vk_moe_init grows a dry branch fed by vulkan_dry_bake_arm (seam:
set_moe_gpu_dry_bake_arm in dasllama_math, so the converter stays vulkan-agnostic); the arm
fail-closes when the config disagrees with the process' env wants (prints both tags).
maxStorageBufferRange moved to init-time capture (g_gpu.msr — config-supplied when dry).

GATE PASSED: offline bake is BYTE-IDENTICAL to the in-process live bake — Qwen3-4B Q4_K_M at
budget 6000, both 4856 MB, same SHA256 (69cf07f2…f0f1). The dry image then MAPPED on a live
GPU-armed load and the walk consumed the baked plan exactly (no divergence warnings).

VkPlanEntry grows `role` (VkBakeRole: cls/dense/shexp/dn/attn/qkv/expert/expert_stream/arena) —
the walk declares its stage via vulkan_bake_role, the slice path verifies it per entry, and P3's
trim will key on it. Collect moved AFTER the upload/placement verdict (was BEFORE, so refused
uploads left phantom plan entries whose validity depended on the device reproducing its budget
verdicts — the old B4 hazard). This fixes the old `-f vulkan`, which did a FULL live VRAM upload
and then wrote an image with empty vkblob/vkplan.

Also: the doubled "vulkan|vulkan|" identity prefix (both load_model_cached branches + the
converter prepended what dlim_vulkan_tag already carries).

Gates: parity SHA256 (above); slice-load parity probe; image mechanics+smol arms 9/9;
lint+format clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…ded "q8"

image_identity / image_path_for / save_image / load_image (+ the model wrappers) take a quant
string, defaulted "q8" — so every existing rail (load_model_cached's q8-gated branch, the
tower/whisper rails whose tags already encode their mode) behaves exactly as before, while the
converter stamps the REAL -q/-config quant into the identity, filename, and embedded config.
Before this, a `-q q4_0` conversion wrote a file whose name and identity claimed q8 — colliding
with the actual q8 image's path. Non-q8 images remain converter-only artifacts (the load path
still images q8 only); they just stop lying about themselves.

Gates: image mechanics+smol arms 9/9 (existing q8/tag defaults byte-stable), converter
compiles+help, lint+format clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…ht families (-44%)

`dasllama-convert -f vulkan --trim` bakes a vulkan image WITHOUT the big CPU weight planes:
after the (dry) bake, trim_model_planes packs the emb/cls region into new Model planes
embq/embs (q8: mblob-style 34B interleaved blocks; kq: the region's plane pair copied verbatim,
repack layout preserved, rebased to superblock 0), sets planes_trimmed (meta 66+3), and FREES
qblob/qscales*/wblob/bf16blob/k*q/k*s/q40*/q51*/mx*/q4* — generate-then-free, so save_image
writes them as empty planes with zero writer changes. fblob (norms/rope) stays. STRICT v1 gate:
every baked-plan role must be arena (pure resident-driver dense, no MTP) or the trim declines.

embed_row serves all three access modes from the trimmed planes (q8 via dequant_embq_row; kq
grp<mr> and disk-order arms over embq/embs rebased); the decode path is otherwise all-GPU on
the resident driver. Fail-closed: the three gather wrappers PANIC on any live gather against a
trimmed model (covers slot re-bind, stale-plan divergence, and every CPU re-gather path), and
mm_cls panics if the classifier is not GPU-served.

The trim is part of the flavor identity: DlimVulkanConfig.trim -> " T" in the tag, driven by
the new DASLLAMA_TRIM knob at serve (registered; ENVIRONMENT.md regenerated) and by --trim in
the converter (the dry-arm wants-check treats trim as save-shaping, not walk-shaping).

Gate so far: Qwen3-4B Q4_K_M trimmed image 2738 MB vs 4856 untrimmed (-44%); maps + slices +
arms the resident driver under DASLLAMA_TRIM=1 with zero divergence warnings; env-registry
14/14; image mechanics+smol arms 9/9; lint+format clean. OPEN: decode IDS parity trimmed vs
untrimmed (the embq read path needs a real generation to prove — next slice).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…mmed CPU fallback fails closed

The C decode gate PASSED at the strongest level: Qwen3-4B Q8_0, resident-driver rail (f32-KV
session), 24 greedy tokens — trimmed image vs untrimmed image IDS are token-for-token IDENTICAL
(the embq read path serves every embed). The trimmed Q8 image is 4475 MB vs ~8.5 GB untrimmed.

blob_only_panic grows a planes_trimmed arm — all six CPU fall-through sites (decode/prefill/
batch-decode stacks + their cls steps) now fail closed with an actionable message (the
commonest trigger: a non-f32-KV session makes the resident overrides decline, e.g.
create_session's f16 default). dasllama-server's bind_gpu_slot re-binds a vulkan-flavor slot
by SLICING its baked planes (same rail as its load) — on a trimmed image a live re-gather
would panic, and on any flavor image the slice replaces redundant re-gather work.

KNOWN PRE-EXISTING (not this arc): Qwen3-4B Q4_K_M on the resident rail generates all-zero
IDS (logits degenerate) — reproduced on an UNTRIMMED image and matching the banked
_vk_prefill_parity degeneracy note; Q4_K_M therefore cannot gate end-to-end decode either way.
Rode along: the 5 pre-existing LINT017s in openai_server.das (int64(length) -> long_length).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…s and docs

New fio builtin `prefetch_map(base, bytes)`: PrefetchVirtualMemory on Windows,
madvise(MADV_WILLNEED) on POSIX — ask the OS to fault a mapped range in AHEAD of a parallel
pass (on-demand faults inside the transcode lanes serialize them; the banked cold-conversion
gap is 9x on Q4_K source). Advisory by contract: false = the OS declined, reads still work.

The gguf loader arms it over the whole source mapping (both the single-file fmap and the
split-shard rail) behind the new DASLLAMA_PREFETCH knob (default on, =0 restores on-demand
faulting for the A/B; registered, ENVIRONMENT.md regenerated) and logs one
"source prefetch armed (N MB)" proof-of-engagement line.

Ships with its contract test (tests/fio/fio_prefetch.das — acceptance over a real fmap,
content intact after the advise, degenerate-argument decline; 3/3) and docs: handmade RST for
prefetch_map plus the six dwrite_* functions (which shipped bare in 8cb3a8b), and a
"Direct IO and mapping advice" group in das2rst so none of them render as Uncategorized.
The judged cold-conversion A/B waits for the quiet box; the engagement smoke passes warm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…plan mode

fill_plan_begin/fill_plan_take + a FillJob record of load_big's call args (name/woff/n/
src_off/fmt). Armed, load_big appends a job and returns instead of transcoding — the ~45-call
weights walk runs UNCHANGED as the plan pass, and the fill pass will re-derive each branch by
replaying load_big's own conditions, so plan and fill cannot drift. Off by default; zero
behavior change (mechanics unchanged, compiles+lints clean). The fill executor (per-plane-
family sorted jobs, job-sized temps, fused per-region repack + wscale, dwrite_band consumers,
scale side-buffers) is the next slice per the banked design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…s through load_big

Under fill_plan_mode the big q-halves (qblob/k*q/q40q/q51q) are never sized — the streamed
fill writes them straight to the image — while the scale halves (6-16%) stay in RAM so the
wscale/save paths see them normally. Models with direct-write loaders (mx4 experts, grouped
deltanet, bf16 PLE, non-q8 modes) disarm the plan BEFORE sizing and convert eagerly with an
empty job list — fail-open to the correct path.

The tied-Q8 classifier/embedding double-copy now routes through load_big (its q8 arm is the
same transcode with the same offsets) so the plan records both copies. PROVEN byte-clean: a
fresh Q8_0 conversion's entire plane region [64, meta_off) — all 4.69 GB — is byte-identical
to the pre-refactor image (the file-level SHA delta is the embedded config JSON recording
GPU-armed vs unarmed box state, by design). Image mechanics+smol arms 9/9.

Still inert — nothing arms fill_plan_mode yet. Next slice: the fill executor (repack-region
enumeration seam on the walkers, per-job temp transcode replay, the save-walk hook + wscale
epilogue after qblob, the mapping-alive streaming entry, converter --stream).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…r, big planes never in RAM

`dasllama-convert --stream` converts without materializing the big q-planes: the plan-mode
walk records FillJobs, load_gguf_streaming keeps the source mapping alive while
save_model_image_streaming's walk streams each jobbed field through fill_stream_plane — per
job: transcode into a job-sized temp (load_big's own arms at temp offset 0), scales land in
the RAM plane, the job's repack regions apply with the q pointer BIASED into the temp (the
repack walkers' collector halves feed the SAME regions — register_stream_layout in
dasllama_layout), and the temp appends to the direct writer. qblob's tail runs the wscale
epilogue — legal because every q-half precedes its scale-half in Model field order. Unstream-
able models (mx4/dn-grouped/bf16-ple/non-q8/shards) fail open to the eager path through the
same save call (empty jobs ≡ eager walk).

GATES PASSED — whole-file SHA identity vs eager conversion, x64-gen mr8 repack armed:
  Qwen3-4B Q4_K_M (kq transcode + grp8 repack + kq emb):  2323ebec…34e7 both
  Qwen3-4B Q8_0 (q8 + repack + tied-cls + wscale-f16):    96b97b33…af01 both
One real divergence found and fixed en route: the plan-mode wscale pre-mark set wscale_f16
unconditionally while the eager convert early-outs on EMPTY qscales (pure-kq models) — the
mark now mirrors the convert's outcome. Image mechanics+smol arms 9/9; lint+format clean.

Remaining for the phase receipts: GLM streamed peak-commit (busy-box-valid), then the judged
GLM wall-clock A/B on the cold box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…ak 81.9 -> 16.34 GB

load_gguf_streaming grows the split-gguf arm (every shard mapping alive across plan-load AND
the blk-save; per-shard prefetch; the transcode kernels resolve shard bases through the meta),
and the blk signature settles on a plain array view served by temp_array in both arms.

THE PHASE'S RECEIPT (busy-box-valid — it is a memory number, not a timing): converting the
73 GB split GLM-4.5-Air Q4_K_M with --stream peaked at 16.34 GB private (sampled at 500 ms)
vs the banked 81.9 GB eager peak — 5x less, the model never in RAM. The wscale epilogue ran
on the real mixed q8+kq model (1.09 B scales converted mid-save); the produced image maps and
parses cleanly (NextN offsets, mr8 marks, flags all sane). Q4_K_M re-gated SHA-identical
after the signature change (2323ebec…).

Honest wall-clock note: streamed GLM took 640 s on a busy box vs eager's ~145 s — the serial
transcode-then-append per job leaves the device idle during transcode and vice versa. The
async-writer overlap (double-buffered temps) is the known next slice; its judged GLM A/B is
the cold-box measurement this arc parks on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQW3fFpEpq8YtqNZTVh7jJ
…boundary-exact slice replay

The import rail: on a vulkan-flavor load, streamed expert mirrors IMPORT the mapped
image window (VK_EXT_external_memory_host) instead of pinned copies — SliceWindow
table recorded while slicing, moe_gpu_slice_window/unmap_notify seams, Model
finalizer notifies the tier before fmap_close. DASLLAMA_VK_IMPORT=0 forces pinned.

Driver verdict on NVIDIA/Windows (probe-verified, _vk_import_probe.das): anon
WRITABLE imports succeed, READ-ONLY file-backed pages are refused with
ERROR_OUT_OF_DEVICE_MEMORY regardless of memory type — so the import declines
once (named reason), disables for the process, and every stack falls back to
pinned copies. The rail self-enables wherever a driver accepts the mapping.

Fixes found by the 35B gate (budget-bounded model, first slice replay WITH a
refusal boundary):

- Boundary-exact slice replay: the bake records only ACCEPTED gathers, but the
  walk still attempts the one the budget refuses at each stage boundary — the
  slicer now asks the tier's shared would-accept arithmetic (vk_moe_resident_verdict
  via the moe_gpu_would_accept seam) and serves a predicted refusal EMPTY, holding
  the cursor: the walk re-refuses, rolls back, re-syncs. Before this, ANY model
  that didn't fully fit declined to live gathers mid-walk.
- carved_budget(): the stream-slot reserve derives at READ time from the loader's
  reported need — the init-time carve was timing-dependent (the expert_q8n probe
  could init the device before the need was known), which made dry-bake and live
  budgets diverge.
- make_imported_buf is fail-soft end to end (create/props/alloc/bind all checked)
  and prefers the host_cached non-device-local memory type — the lowest usable
  bit can be the ReBAR heap, where multi-GB imports exhaust VRAM.
- make_host_buf grows a soft mode: a pinned-mirror alloc failure now reads as the
  streamed walk's budget stop (roll back, stop streaming) instead of a panic.
- moe_gpu_upload_stack tolerates empty planes (the refusal-boundary serve).

Gate receipts (Qwen3.6-35B Q4_K_M, 6000 MB cap): import-ON and import-OFF runs
produce IDENTICAL 24-token IDS; slice replay consumes the plan exactly (no
regather warning, refusal replays at 4787159040 bytes both sides); resident
[35..40) + streamed [0..35) matches the bake.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…ds overlap transcode and device time

DasDirectWriter grows a second band and a lazily-spawned writer thread: a full
band hands off to the thread while the caller stages into the other, so the
converter's transcode work and the device write overlap instead of serializing.
One job in flight, handed off in order — the strictly-ascending cursor NTFS's
valid-data-length watermark demands is preserved. The aligned-bulk direct path
stays synchronous (the caller may retire its pointer the moment append returns)
after draining the in-flight band. Async failures land in the writer's ok state
at the next append/commit/close; band2 allocation failure falls back to the old
synchronous flush. das API and call sites unchanged.

Gates (busy box): streamed and eager conversions reproduce the banked SHA256
byte-for-byte — Q4_K_M 2323ebec…34e7 (eager AND streamed), Q8_0 96b97b33…af01;
fio suite 194 tests 0 failed. Indicative only (unjudged): streamed planes write
377 -> 564 MB/s (Q4KM) / 712 MB/s (Q8), eager 1500 -> 1627 MB/s. The judged GLM
wall-clock A/B waits for the quiet box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…act, band/commit, stat counters

tests/fio/fio_dwrite.das: odd-size appends (sub-sector through multi-band) round-trip
byte-for-byte and close truncates the preallocation to exactly the appended bytes;
the band/commit producer path crosses band boundaries (the async flush + swap point)
intact; append and band/commit interleave in order; bounce/direct stat accounting;
an impossible path declines with null. 5/5, and the suite exercises the new
double-buffered writer thread on every boundary flush.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…ek + B5 weight-bytes fix

image_peek reads what tooling needs WITHOUT loading: header fields plus the v7 meta
head's baked identity and embedded config JSON (pre-v7 layouts are unknowable and
report empty). --list inventories every .dlim beside -m (a GGUF or a directory) with
size, version, verdict, identity; verdicts compare against the identities THIS box
would produce now across flavors and quants — CURRENT / OTHER (loadable, possibly
another box's bake) / STALE vN / BROKEN. --clean GCs only what can never load again
(BROKEN + version-stale), never a loadable image; dry-run by default, --apply acts,
and an applied clean also sweeps the stale-tmp siblings.

Live receipt (Qwen3-4B Q4KM, 9 sibling images): 1 CURRENT, 4 OTHER v7 (trimmed
vulkan bakes + portable-backend configs — kept), 4 STALE v6 flagged for --apply.

B5: model_weights_bytes now counts q51q/q51s, mblob, vkblob, and the trim planes
embq/embs — a flavor image no longer reports its bake bytes as 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…g round-trip

The mechanics suite (CI-safe, no models) grows: image_peek on the synthetic carrier
reads version + baked identity + embedded config without loading, and the embedded
config re-derives the exact identity the image was saved under; DlimConfiguration
JSON round-trips with the identity intact, garbage declines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
… 2x2

The win32 mmap shim now honors PROT_WRITE (PAGE_READWRITE / FILE_MAP_WRITE — a real
shared writable view, writes go back to the file, still no commit charge), and
fmap_open_rw is the writable twin of fmap_open on both platforms. Same ownership
contract; fmap_close unmaps either kind. Tests are a flagged follow-up.

The consumer that motivated it: VK_EXT_external_memory_host imports. The probe 2x2
(_vk_import_probe.das) is now closed on NVIDIA/Windows: anonymous writable memory
imports fine, file-backed memory is refused with ERROR_OUT_OF_DEVICE_MEMORY in BOTH
protections — the blocker is the file backing, not the page protection. So the
imported-mirror rail cannot engage on this driver under any mapping mode, and the
pinned-copy fallback stands; the rail self-enables on drivers that accept
file-backed host pointers. make_imported_buf's comment records the proven rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…UT, not the job sum

The GLM judged A/B caught a phase-D correctness bug the 4B gates could not see: a
plane layout can hold allocated-but-never-written holes — glm4moe's dense-lead
layer gets shexp slices in the wsh spans (layout_offsets allocates all 47 layers)
but has no shexp tensors on disk, so no FillJob covers them. The streamed fill
concatenated jobs, so everything past the first hole landed SHIFTED and the qblob
section came up 17,301,504 bytes short (3 planes x one 4096x1408 slice) — a
corrupt image that its own header could not detect.

Fix: the sizing pass records each streamed q-plane's LAYOUT byte total
(g_stream_plane_total); stream_plane_bytes serves that total; fill_stream_plane
tracks a byte cursor and zero-fills the gaps between jobs and the tail — exactly
the zeros the eager path writes from its zero-initialized RAM planes.

Gates: GLM streamed vs eager now cmp-IDENTICAL across the whole 82.4 GB file
(peak 16.34 GB held); 4B Q4KM/Q8_0 streamed SHAs unchanged (2323ebec…, 96b97b33…).
Judged GLM receipts (quiet box): streamed 739 s / 16.36 GB peak vs eager 880 s /
81.87 GB peak — the writer split shows 99.9% of the eager save inside write
syscalls, so conversion is device-bound at the drive's sustained rate and
streaming wins both axes. (Rates degraded 112 -> 98 -> 73 MB/s across the three
consecutive 78 GB writes — SLC-cache exhaustion; cross-run rate comparisons are
drive-state-confounded, the byte parity is not.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
tests/fio/fio_fmap_rw.das: writes through the mapping persist to the file after
unmap (verified via a read-only remap), mapped content matches the write, and the
decline shapes (missing file, empty file) return null. 3/3. The handmade RST fills
the das2rst stub — the symbol rides the File manipulation group beside fmap_open;
doc/source/stdlib/generated is CI-regenerated and stays untracked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…ession coverage

test_model_image_vulkan.das (run.das --suite image-vulkan, arm `vulkan`): the runner
arms DASLLAMA_GPU + a 2000 MB budget so the probed config carries a vulkan section,
the DRY tier collects the bake with zero device calls (safe on GPU-less boxes), and
the test asserts a non-empty role-stamped plan whose planes sit inside the blob,
then saves the flavor image under the tier's bake tag and maps it back with the
plan and blob intact. The offline vulkan bake previously had zero automated
coverage — only the manual parity probe. run.das grows the per-suite env hook.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…utomatic GC, flavor locks

Library first: dlim_inventory / dlim_clean / dlim_image_verdict / dlim_current_identities
move into dasllama_image (the converter's --list/--clean refactor onto them), so the
server and the CLI share one implementation.

Server: GET /v1/images lists every slot's .dlim siblings (size, version, verdict,
identity) plus the mapped flavor; POST /bake spawns dasllama-convert as a child on
the bench worker's proven single-spawn shape (quiesced-only, one child at a time,
tune-relaunch retry, the converter's receipt lines surface in the log) — planar
slots stream, the GPU owner bakes the vulkan flavor against the server's LIVE
config via a --config handoff; GET /bake serves state/log. The dlim GC (only
never-loadable images) runs at server start and on every bake completion.
ModelSlot carries its source GGUF path; /v1/stats slots grow `flavor`.

Page (existing forge style): each model card gets a .dlim line (serving flavor,
current/stale counts, total GB, per-file tooltip) and a bake button with completion
polling; config GPU knobs lock while the active slot serves a flavor image — a
vulkan .dlim cannot switch to cpu mode, the knobs are baked into the file.

Gates: server suite 19/19 (new /v1/images + /bake decline arms) with a live
tinyllama server; live end-to-end smoke: startup GC reclaimed 7292 MB from 4 stale
images, POST /bake spawned the converter, streamed 1299 MB in 18.5 s to the
identity path, completion drained with receipts, clean shutdown. env-registry
14/14 (no new knobs); lint + format clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…byte-exact M1 images

The gen repack primitives were already number-parametrized (generic grp<mr>
shuffles); only their number SOURCES were stamp-bound. The repack fns now read
math's active_* layout getters — value-identical to the stamped companions during
normal serving (activation caches from them) — and the getters grow a bake
override: pin_backend_for_bake pins a registered name normally, while a FOREIGN
name (arm64-gen on a PC) arms the override with the target config's numbers and
aliases the identity's reported backend (dlim_backend_name). The q51 family gets
a registered bake pointer (x64-gen keeps disk-order q51 slots) reached through
active_repack_q51 at both load-path call sites. The converter's --config path
uses the new pin; the identity verify stays fail-closed and passes naturally when
the override took.

Gates (the plan's verification #3, run against a live M1 over ssh):
- M1 dump-config -> the PC bakes SmolLM2 Q8_0 with it: identity
  v7|q8|arm64-gen|s16|q8 mr4 b0 g4|kq 8/8/4/8|q51 mr1|nat 1101 — and the file is
  BYTE-IDENTICAL to the M1's own local bake (sha256 506d36a9…6ecd), proving the
  mr4 repack a PC never serves.
- K-quant family: Llama-3.2-1B Q4_K_M cross-baked and M1-baked images identical
  (fa4b678c…b8c0) — covers the differing k6 mr4 and the wscale16 epilogue.
- The M1 MAPS the image (MAPPED:true) and serves it token-exact vs its gguf load
  (16/16 greedy IDS parity).
- PC regression: the 4B streamed bake reproduces the banked SHA (2323ebec…34e7) —
  the getter swap is byte-invisible for same-box bakes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…ortable

The Metal drivers' MODEL-SHAPE gates move verbatim into a new portable module
(no das_metal require): the decline enum, MetalNeed + metal_needs/missing/record,
kq_fmt/kq_load GPU-support, kv_share_ok, dn_metal_ok, moe_metal_ok + moe_site_ok,
DECODE/BATCH_NEEDS_OK, decode_shape_decline (public now), and the mint-time
metal_model_servable_impl, whose [init] registration now fires on EVERY build —
the piece that build-locked metal-flavor baking to Apple. dasllama_metal_common
re-exports the module so the Apple driver family keeps its names; the umbrella
requires it unconditionally. The runtime gates (device, PSO, planar, mirrors)
stay in the metal modules.

PC compile + full dlim chain green; the Apple-side build/lint validation runs on
the M1 next (the metal modules cannot compile on Windows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
… contract)

The blob transform reads ROW-MAJOR planes — under a repack backend the servable
gate correctly declines backend_repack, which is why bare -f metal failed on
BOTH boxes. Portable's layout numbers are box-independent, so the metal identity
converges across boxes with no --config: a PC now mints
v7|q8|portable|s16|q8 mr4 b0 g4|kq 4/4/4/4|q51 mr1|nat 1101|metal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
…n32 PTY cold-start timeout

tests/metal/test_metal_tensor_ops.das: macOS-26 CI runners pass the SDK/MSL-4.0
probe but their PARAVIRTUAL device cannot lower cooperative tensors — every
pipeline build fails with the "deferred-static-alloca-size" signature (both open
PRs' darwin26 lanes red on it). A signature-gated pre-probe now feints exactly
that case before any allocation; every other compile error still fails loudly,
so real emitter regressions stay visible on Metal-4 hardware.

tests-cpp/small/test_terminal.cpp: the ConPTY probe timed out at 30s waiting for
DASHERD_READY on a loaded windows-32 runner — Windows PowerShell's cold start
(pwsh absent) can exceed it. 120s; the REQUIREs stay, real transport breakage
still fails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
Copilot AI review requested due to automatic review settings July 28, 2026 21:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR advances the dasLLAMA “.dlim” arc by making prepared model images explicit, portable artifacts (config-identified, streamable/offline-convertible, cross-box) and exposing inventory/bake operations through the dasllama-server admin surface. It also adds new fio primitives to support the conversion pipeline and includes a couple of CI stability fixes plus a JIT exception visibility fix.

Changes:

  • Add DlimConfiguration as an explicit, serializable identity for .dlim images; expand conversion tooling (new dasllama-convert) for streaming/offline/cross-box workflows.
  • Extend dasllama-server with /v1/images inventory and /bake spawning, plus UI wiring in control.html.
  • Add fio APIs (prefetch_map, fmap_open_rw, dwrite_*) with tests/docs, and apply targeted CI/test fixes (Metal runner skip signature, Windows PTY timeout).

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
utils/dasllama-server/test_openai_server.das Adds coverage for /v1/images and /bake endpoints.
utils/dasllama-server/openai_server.das Implements image inventory endpoint and bake child-spawn workflow; tracks slot source paths.
utils/dasllama-server/main.das Passes GGUF source path into server slots and runs .dlim GC on startup.
utils/dasllama-server/control.html Adds UI for .dlim inventory display and bake triggering/polling; locks GPU controls when serving a flavor.
utils/dasllama-convert/main.das New CLI tool to convert GGUF → .dlim (streaming/offline bake/list/clean/config).
tests/metal/test_metal_tensor_ops.das Skips Metal-4 cooperative tensor ops on macOS-26 paravirtual runner signature.
tests/fio/fio_prefetch.das New contract tests for prefetch_map.
tests/fio/fio_fmap_rw.das New contract tests for writable shared mapping fmap_open_rw.
tests/fio/fio_dwrite.das New mechanics tests for direct sequential writer (dwrite_*).
tests-cpp/small/test_terminal.cpp Increases Windows ConPTY integration timeout to reduce CI flakes.
src/builtin/module_jit.cpp Ensures guarded JIT exception printer flushes output.
src/builtin/module_builtin_fio.cpp Adds prefetch_map, fmap_open_rw, and dwrite_*; fixes Windows mmap mapping mode to avoid commit charge.
modules/dasLLAMA/tests/test_model_image.das Extends image tests to validate v7 meta peek + config round-trip.
modules/dasLLAMA/tests/test_model_image_vulkan.das New test for offline/dry Vulkan bake plan collection + .dlim round-trip.
modules/dasLLAMA/tests/test_env_registry.das Includes new converter directory in env registry scan roots.
modules/dasLLAMA/tests/run.das Adds image-vulkan suite; improves TMPDIR/TEMP handling; uses current executable by default; Windows env-prefix handling.
modules/dasLLAMA/tests/CLAUDE.md Documents new image-vulkan suite intent and arming.
modules/dasLLAMA/ENVIRONMENT.md Documents new env knobs (DASLLAMA_PREFETCH, DASLLAMA_VK_IMPORT, DASLLAMA_TRIM, TEMP).
modules/dasLLAMA/dasllama/dasllama_transformer.das Ensures portable Metal shape-gate registration is always pulled in.
modules/dasLLAMA/dasllama/dasllama_metal_shapes.das New portable Metal servability gates (no das_metal dependency) for cross-box baking.
modules/dasLLAMA/dasllama/dasllama_metal_llama.das Removes duplicated shape-gate logic now extracted into dasllama_metal_shapes.
modules/dasLLAMA/dasllama/dasllama_metal_common.das Re-exports extracted shape gates to keep driver family naming consistent.
modules/dasLLAMA/dasllama/dasllama_math.das Adds dry-bake/slice-window/unmap-notify/would-accept hooks and cross-box bake layout overrides.
modules/dasLLAMA/dasllama/dasllama_math_gen.das Routes repack slots through override-aware layout getters; registers q51 repack for cross-box bake.
modules/dasLLAMA/dasllama/dasllama_layout.das Refactors repack region collection and registers streaming layout hooks.
modules/dasLLAMA/dasllama/dasllama_env.das Registers new env knobs and TEMP fallback in env registry.
modules/dasLLAMA/dasllama/dasllama_config.das New DlimConfiguration and identity/tag/JSON helpers for .dlim configuration.
modules/dasLLAMA/.das_module Exports new modules (dasllama_config, dasllama_metal_shapes).
include/daScript/simulate/aot_builtin_fio.h Exposes new fio builtins for AOT.
doc/source/stdlib/handmade/function-fio-prefetch_map-0xc63f5d4e91b2e653.rst Adds stdlib docs for prefetch_map.
doc/source/stdlib/handmade/function-fio-fmap_open_rw-0x2abb9e9d0dfc252e.rst Adds stdlib docs for fmap_open_rw.
doc/source/stdlib/handmade/function-fio-dwrite_stat-0xbf270ab2cb2df9d6.rst Adds stdlib docs for dwrite_stat.
doc/source/stdlib/handmade/function-fio-dwrite_open-0x46f123a0dc4b0ce0.rst Adds stdlib docs for dwrite_open.
doc/source/stdlib/handmade/function-fio-dwrite_commit-0x122d2094a7b99d77.rst Adds stdlib docs for dwrite_commit.
doc/source/stdlib/handmade/function-fio-dwrite_close-0xca8d8214309caf7.rst Adds stdlib docs for dwrite_close.
doc/source/stdlib/handmade/function-fio-dwrite_band-0x160228ade96e97be.rst Adds stdlib docs for dwrite_band.
doc/source/stdlib/handmade/function-fio-dwrite_append-0x6658e660a37b258d.rst Adds stdlib docs for dwrite_append.
doc/reflections/das2rst.das Adds fio doc grouping for new direct-IO/mapping advisory APIs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread utils/dasllama-server/openai_server.das
Comment thread utils/dasllama-server/openai_server.das
Comment thread src/builtin/module_builtin_fio.cpp Outdated
…nts, Copilot fixes

- DAS_NO_FILEIO: the 7 dwrite/prefetch builtins get their GENERATE_IO_STUB entries
  (build_eastl links registrations without fileio bodies)
- fio_dwrite test: the direct-vs-bounce stat split is platform-dependent (win32 demands
  sector alignment, POSIX align=1 takes band-sized runs direct) - assert conservation
  (direct + bounce == total) plus sub-sector staging instead of the win32-only split
- test_model_image_vulkan: STYLE030 nolints - the body is static_if'd on the vulkan
  module existing, so no-vulkan lint builds see every body-only require as unused
- POST /bake: a malformed JSON body is now 400 instead of silently baking the default slot
- shutdown: leave the bench/bake channel to a still-running worker (channel_remove throws
  on outstanding refs) instead of aborting cleanup
- dwrite header comment: per-platform cache semantics stated honestly (NO_BUFFERING /
  F_NOCACHE / buffered + fadvise DONTNEED)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
Copilot AI review requested due to automatic review settings July 28, 2026 22:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

utils/dasllama-convert/main.das:353

  • apply_box_profile_runtime() is called a second time inside the streaming conversion branch, after --config may have pinned a backend via pin_backend_for_bake(...). Because apply_box_profile_runtime can re-pin the kernel backend from the tune sidecar, this can silently override the already-verified config/pin and break the tool’s “fail-closed” config identity guarantee for streamed planar bakes.

borisbat and others added 2 commits July 28, 2026 16:03
…reaming branch

It re-read the tune sidecar AFTER the --config override and its fail-closed
identity verify, so a sidecar carrying runtime knobs (backend pin, wscale_f16,
kquant natives) could silently revert a pinned cross-box streamed bake. The
first call (before --config) already applies the sidecar; the config override
must stay last. Surfaced by a Copilot low-confidence remark - verified real.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcZKEkh1e2z8zPrLX1pKR2
Copilot AI review requested due to automatic review settings July 28, 2026 23:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 42 out of 42 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

tests/fio/fio_dwrite.das:150

  • This test assumes "Z:/no/such/..." is an impossible path, but on POSIX it’s just a relative path with missing intermediate directories (and could theoretically exist), making the intent less portable/robust. Use a path that is guaranteed to be invalid across platforms (e.g. empty string) to assert the “declines without crashing” contract deterministically.

@borisbat
borisbat merged commit 6df9397 into master Jul 29, 2026
37 checks passed
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