Skip to content

ZINC: pin upstream, serve it through Lemonade, NVIDIA verified on an RTX 5090 - #14

Merged
bong-water-water-bong merged 4 commits into
mainfrom
zinc-pin
Sep 23, 2026
Merged

bong-water-water-bong merged 4 commits into
mainfrom
zinc-pin

Conversation

@bong-water-water-bong

@bong-water-water-bong bong-water-water-bong commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Update: this PR now also carries #15 (the Lemonade zinc recipe), which was merged into this branch. It also carries the CUDA verification: on an RTX 5090 (Clore.ai, CUDA 13.2), Qwen3.5-9B continues "The capital of France is" with " Paris." at 167–173 tok/s decode. See docs/zinc.md.

Pins ZINC (MIT), which is under active development, as third_party/zinc at upstream main c50b4add (2026-09-23). It is the engine's route to NVIDIA GPUs through ZINC's CUDA backend (Ada sm_89, Blackwell sm_120), and it also brings ZINC's own Vulkan and ROCm kernels.

  • scripts/build-zinc.sh <prefix> [vulkan|rocm|cuda] fetches the Zig release named in ZINC's build.zig.zon (0.15.2) and checks its sha256 against ziglang.org. It builds privately, with nothing installed system-wide.
  • .github/workflows/bump-zinc.yml runs daily and opens a PR when upstream moves. It uses the same HRX_BUMP_TOKEN secret as the other bump workflows.
  • docs/zinc.md covers the pin, the build and the verified results; docs/PORTING.md gains step 6.

Verified on Strix Halo

Backend Result
vulkan Qwen3-0.6B Q4_K_M, prompt 785,6722,315,9625,374: first token 12095 (" Paris"), matching the NPU lane and the CPU reference. Decode 295 tok/s, prefill 817 tok/s.
rocm Builds and links ROCm 7. Its forward pass implements only Qwen3.5/3.6, so it was not run on a model here.
cuda Not built: no NVIDIA GPU on these machines. Upstream validates it on an RTX 5090 and 4090 under WSL2.

Not in this PR

  • The user's fork bong-water-water-bong/zinc has 26 commits that upstream doesn't have: Q2_0/STQ1_0 ternary kernels, LoRA training, and a C-ABI shared library for embedding. They are not carried here.
  • There is no Lemonade backend that spawns zinc yet; that is the next step.

🤖 Generated with Claude Code

third_party/zinc = upstream main c50b4add (2026-09-23). ZINC is a Zig GGUF
engine with Vulkan, ROCm, CUDA and Metal backends; its CUDA backend is the
engine's route to NVIDIA GPUs (sm_89, sm_120).

- scripts/build-zinc.sh <prefix> [vulkan|rocm|cuda]: fetches the Zig release
  zinc's build.zig.zon names (0.15.2), sha256-checked against ziglang.org,
  and builds into <prefix>/<backend>; caches stay out of the submodule.
- .github/workflows/bump-zinc.yml: daily; opens a PR when upstream moves.
- docs/zinc.md, docs/PORTING.md step 6.

Strix Halo: vulkan builds and runs Qwen3-0.6B Q4_K_M to 12095 (" Paris"),
295 tok/s decode; rocm builds and links ROCm 7 (its forward pass is
Qwen3.5/3.6-only); cuda needs an NVIDIA box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…73 tok/s)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Local Lemonade delta 7 (third_party/lemonade/UPSTREAM.md): recipe `zinc`.
load() spawns `zinc -m <gguf> -p <port> -c <ctx>` (RADV_PERFTEST=coop_matrix
unless set) and waits on /health; requests go out without `model` (zinc
rejects any id but its own) and replies carry the Lemonade name. GGUF
download/resolution reuse llamacpp's ops. Qwen3-0.6B-ZINC =
unsloth/Qwen3-0.6B-GGUF:Q4_K_M.

-DONEBIT_ZINC=ON [-DONEBIT_ZINC_BACKEND=vulkan|rocm|cuda] builds zinc with
scripts/build-zinc.sh and `1bit lemonade` defaults zinc.zinc_bin to it.

Strix Halo: tests/zinc_lemonade_e2e.sh PASS (pulled, "Paris." under the
Lemonade name, served by this build's zinc, streaming); ctest
npu_full_elf_md5 and smoke_lemonade pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bong-water-water-bong bong-water-water-bong changed the title ZINC: pin upstream zolotukhin/zinc, build it privately, keep it current ZINC: pin upstream, serve it through Lemonade, NVIDIA verified on an RTX 5090 Sep 23, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bong-water-water-bong
bong-water-water-bong merged commit eacc9c0 into main Sep 23, 2026
1 check passed
@bong-water-water-bong
bong-water-water-bong deleted the zinc-pin branch September 23, 2026 19:55
bong-water-water-bong pushed a commit that referenced this pull request Sep 26, 2026
…oints and three HRX fixes

Our llama.cpp #7 (HRX MoE router route-ids binding length), #9 (decode-split
flash-attention capacity bound), #11 (ZAYA sliding-window attention, ZAYA1-74B),
#12 (MoE router experts-per-wave launch geometry) and #14 (ZAYA legacy
checkpoints; --remote fetches chat_template.jinja).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong pushed a commit that referenced this pull request Sep 26, 2026
…d2dc (#14); ZAYA1-74B docs

llama.cpp #14 converts Zyphra's legacy (Megatron-style) ZAYA checkpoints, such as
ZAYA1-base and ZAYA1-reasoning-base, and makes --remote fetch chat_template.jinja.
docs/vulkan.md gets a ZAYA1-74B-preview section (convert, requantize, serve,
bench) and a legacy-checkpoint section; the README status names the Zyphra family.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Sep 26, 2026
…d2dc; ZAYA1-74B docs (#129)

* ZAYA1 legacy checkpoints and remote chat templates: pin llama.cpp 567d2dc (#14); ZAYA1-74B docs

llama.cpp #14 converts Zyphra's legacy (Megatron-style) ZAYA checkpoints, such as
ZAYA1-base and ZAYA1-reasoning-base, and makes --remote fetch chat_template.jinja.
docs/vulkan.md gets a ZAYA1-74B-preview section (convert, requantize, serve,
bench) and a legacy-checkpoint section; the README status names the Zyphra family.

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

* registry: regenerate for llama.cpp 567d2dc

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

---------

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong pushed a commit that referenced this pull request Oct 13, 2026
…d the sentinel recovered

Add-only session addendum. Three results about the instrument itself: the read path is REFUTED as the cause (two
independent read paths, memcpy and synchronising hrx_synchronous_d2h, identical on 138,256 of ~138k observations,
killing an explanation that would have implicated every host-side read in the campaign); arena slot reuse is why
intermediate reads are unfaithful (ggml_argsort allocates a fresh i32 tensor that must hold ranks and holds a
later tensor's float data instead, because the fingerprint reads after the whole graph ran and the allocator
reassigns dead slots), with FRESH_TRANSIENT_ARENA checked and shown not to help; and faithfulness is positional,
since graphs are per-layer so tail:N reads a layer's last nodes - the tail:N methodology survives, but its own
first node is the exposed one and the record's single named NaN stage (ffn_shexp-41) sits at the head of its
window, making that attribution weaker than presented.

Two exclusions added, one recovered from a run that was never read out: stale/never-written logits EXCLUDED for
Face A via the engine's own sentinel (GGML_HRX_SENTINEL fills the logits with payload NaN 0x7FC0DEAD, the guard
reports sentinel=; the 315nan session armed it, the fill line is present in 20 of 21 arms, and all 17 fault lines
read sentinel=0, so the device wrote the region with NaNs - a mechanism the campaign's own log had already decided
two days earlier); and device-side access violations not detected but qualified, using HRX_HAL_SANITIZER=asan plus
the engine's existing device-event sink, a device-side kernel-attributed channel never switched on, across three
in-regime arms (two Face A, one Face B) with zero reports and no positive control run.

Records a near-deterministic Face-B configuration (6 of 6 arms, all in regime, all three cells, req=1-11, versus
req=6-54 for Face A) whose model text is valid UTF-8 and a mid-essay fragment, offered as a tool and explicitly
not as evidence since its relationship to the production fault is an untested hypothesis.

Updates the criterion table: cause still not located, now with 9 candidates closed rather than 7, and with a named
reason the remaining surface resists - no host-side read of a mid-graph tensor is trustworthy. Records the session's
harness defects #10-#14 including the rig default BIN not implementing the fingerprint at all and the NANONLY
sweep that could not distinguish 'every layer clean' from 'no Face-A arm occurred', reported as mis-targeted rather
than as a negative, plus the five parse slips in one family. Carries forward the one sentence that matters: both
faces are now a value being wrong when a consumer reads it, and no host-side read can say where it was first
written, so the next step is a read whose fidelity does not depend on the allocator's reuse.
bong-water-water-bong pushed a commit that referenced this pull request Oct 13, 2026
Chasing defect #15 turned up a second operability problem. The frozen rig gates every arm on load1 < 4
(315-disc.sh:91), and against the current tenancy that threshold is unreachable: loadavg sits at 4.72 with tctl at
44.5C while our own side-processes are verified at zero (evictonly=0, CPU workers=0) and GTT is at 26GiB, i.e. the
box is as quiet as we can make it and the gate still refuses. The load is other agents' workloads - clickhouse,
redis, dockerd, node.

Two consequences. First, a run can stall indefinitely without failing: a swap script of mine sat in exactly this
loop with every one of our preconditions satisfied, and only a self-imposed iteration cap let it proceed, while the
rig's own gate has no such cap - and an arm that never starts is indistinguishable from outside from an arm that
ran and produced nothing, the same ambiguity this campaign has hit twice before in defect #13's inert EXTRA_ENV and
#14's NANONLY sweep that could not distinguish clean from not-run. Second, the gate is a property of the original
tenancy: load1 < 4 and tctl < 80 were calibrated for a box this campaign had to itself, so with other agents
running servers and CPU work the gate measures THEIR load and silently changes what in-regime means for our arms -
not the GTT regime itself, since arms carry their own gtt_start, but the ambient condition the campaign has treated
as constant, which is the baseline against which section 39's inducing effect and the whole ambient-vs-forced
comparison are drawn.

Not resolved here: the honest statement is that this campaign's ambient baseline and its arm-starts are now
conditional on a box it no longer exclusively owns, and no arm in this record was tagged with the foreign load
present when it ran. The rig's newer 'resident_inventory (foreign, not ours)' line is a step toward recording that
and should be read as required context for any arm from this session onward.
bong-water-water-bong pushed a commit that referenced this pull request Oct 14, 2026
…e finding

Pre-check before running the section-40 chain probe: four of its node names (ffn_inp-41, ffn_norm-41, ffn_inp-42,
ffn_norm-42) have NEVER appeared in any fingerprint line this campaign has recorded, while ffn_shexp-41 appears 30
times, ffn_swiglu-41 30, ffn_moe_out-41 and ffn_out-41 7 each, l_out-41 9, attn_norm-42 2. That is not the names
being wrong: every one of those runs used NANONLY=1, which prints nothing for a tensor with zero NaN, so a name that
never appears is one whose tensor was always clean when read or was never inside the window - and both readings are
unavailable from the logs, the same ambiguity as defect #14.

Consequence for section 40's evidence: it concluded that every earlier call's window was clean so the corruption is
born at block 41, but with NANONLY a clean window and an unmeasured window look identical, and ffn_inp-41/ffn_norm-41
were never in any name list since the probe covering them is the one section 40 says is written down but not yet
run. Its positive claim therefore rests on the absence of lines for windows that were in tail:16, which is sound for
those windows and says nothing about nodes the window never reached.

The conflict at full strength: in section 40's arm the earliest layer read was 41 via the window and it was clean
before it; in this session's arms l_out-0 carries NaN and the stream saturates by layer 15. Since l_out-<layer> IS
inside a tail:16 window, being last in its chunk, those tensors would have printed in section 40's arm had they
carried NaN - they did not, and here they do, so the two accounts cannot both be general.

Reconciliation, and why it is a finding: the arms are not the same episode, the fault is intermittent with varying
trigger position (recorded req values include 6, 8, 10, 34, 54, 59), and the origin now measured varies with it -
r1e and r2e both 1984 at l_out-0 with identical shape at different token counts, while r1c (CPU cell) diverges with
46 at l_out-0 saturating by layer 2. So the fault is not anchored to one node or layer: one arm puts the earliest
NaN at layer 0, another episode puts the first corrupted window at block 41, which is what a position-independent
mechanism looks like - one where the corruption follows which MEMORY REGION was affected rather than which op ran.

That fits the trigger better than a fixed site ever has: the intervention is KFD eviction, which acts on memory
regions rather than ops; the corrupted magnitude is a fixed count (1984 = 2048-64 in two arms of different token
counts, a region rather than a row and not a proportion); the whole tensor saturates quickly once any of it is NaN,
so the observable is the position of the first corruption, free to move with the region; and the two faces never
co-occur across 142 arms, consistent with one region being hit per episode.

States the hypothesis falsifiably: the fault is a corruption of a transient memory region whose contents at the
moment of the event decide where the first NaN appears, not a defect in a particular layer, op or kernel, predicting
that the origin layer varies across Face-A arms while the trigger stays fixed. Falsified by running ten or more
in-regime Face-A arms with the early-layer selector and showing the first NaN-bearing layer is the same in every
one. That is cheap - the selector exists and the dose is known to give Face A with a clean quiet cell - and unlike
every previous framing it distinguishes the two accounts without a new instrument.
bong-water-water-bong pushed a commit that referenced this pull request Oct 14, 2026
…, and the prediction is untestable as written

Reviewing the previous commit against its own evidence, the case for the origin moving is weaker than it states.
First, the arms cited do not differ in the way the hypothesis needs: r1e and r2e are both eviction arms and both give
l_out-0 = 1984, identical, while the one arm that differs (r1c, 46) is the CPU-pressure CELL - so the variation
separates cells, not episodes of the same trigger, and under the objective's trigger (eviction) the layer-0
signature was constant across two arms of 98 and 135 tokens, which is evidence for a fixed region rather than a
position-independent one. Second, layer 0 is already NaN in every arm measured, so which layer carries the first NaN
cannot vary below what the selector can see: all three Face-A arms put NaN at l_out-0 and the earliest selector this
session read IS layer 0, so a prediction about movement in the first affected layer is not testable with this
selector, and the stated falsification could come out same purely because the instrument cannot see earlier - the
defect #14 ambiguity written into a pre-registered-looking prediction. Third, section 40's contrary reading rests on
an absence, which I used to weaken it correctly but which applies equally to my comparison against it: a clean layer
0 inferred from a missing NANONLY line is not a measured clean layer 0.

What survives: ffn_inp-41, ffn_norm-41, ffn_inp-42 and ffn_norm-42 have never appeared in any recorded fingerprint
while ffn_shexp-41 has 30 times, so section 40's chain probe has genuinely never been run and its conclusion rests on
windows rather than those nodes, which is why the combined probe is worth running; layer 0 carries NaN in all three
Face-A arms measured (1984, 1984, 46), which is new, replicated within-session and in direct tension with section
40's generalisation; and under the eviction trigger the layer-0 count was constant at 1984 across two arms of
different size.

What does not survive: the claim that the origin moves across arms and the prediction that tests it, so the
region-following mechanism remains a hypothesis unsupported by any variation between episodes of the same trigger.
What would test it is arms reading earlier than layer 0 - the embedding and layer 0's internals - where movement
could be seen, which is a displacement-exposed read, i.e. the read-at-production instrument again. The honest
position is the one this campaign keeps arriving at: the measurement that would separate the remaining hypotheses is
the one the host-side instrument cannot make.
bong-water-water-bong pushed a commit that referenced this pull request Oct 14, 2026
To run three probes back to back I wrote three scripts each shaped as 'while ps | grep <predecessor>; do sleep; done;
clean; launch the next one'. Three disc sessions then appeared at once, including two copies of 315lout4, and
315chain-run.log was empty - stage 1 never started - while stage 2 launched anyway, because the guard is 'wait WHILE
the predecessor is running', which is satisfied immediately when the predecessor does not exist at all. The guard
answers 'is it running now?' rather than 'did it ever run and finish?'. Two sessions then composed the regime
simultaneously, so neither arm's cell means what its label says and every arm in that window is unusable.

Why this is the same defect one layer up: it is defect #14 with the sign flipped. There, absence of output was read
as a clean result; here, absence of a process is read as completion. Both are a predicate on a proxy standing in for
the event of interest, and both silently proceed. The campaign now has four instances of this shape - #13, where an
EXTRA_ENV named a feature the build does not implement and absence of effect was read as absence of an effect; #14;
#16; and the pkill-on-name traps of #11/#12 where killing the wrapper was read as stopping the run.

Records the fix and what I did instead: the correct guard records evidence of the predecessor's completion via a
sentinel line written after its last arm, and additionally requires that it started, but chaining is not worth that
machinery here, so I abandoned it and ran the stages directly one at a time, verifying each launch by its own log
header before starting the next - slower, but it cannot silently skip a stage, which is the failure mode that cost
two composed regimes and about twenty minutes of box time this session is already over budget on. Cost: two
simultaneous compositions whose arms are discarded, the section-40 chain probe never ran and must be re-issued, and
one duplicate 315lout4 session, with no conclusions drawn from any arm in that window.
bong-water-water-bong added a commit that referenced this pull request Oct 21, 2026
…PU token (#484)

Three late-caught bugs of 2026-10-20 (gpu-kernels #11, #14) passed this gate.
It now covers the paths they broke:

- MODEL_TABLE takes a fifth column of llama-server arguments. Two new rows serve
  Qwen3-0.6B with -fa on -ctk q8_0 (V f16) and with -ctk q8_0 -ctv q8_0, with
  kat and the 7.1K-token needle. Their long prompts decode past 1,024 KV cells,
  where the add-on's split decode kernel fa_decode_q8_d128 takes over. Rows with
  server arguments skip kld (the bases are recorded per model file, f16 cache).
  Goldens recorded with the private kernels (gpu-kernels 449a809e, this pin):
  kat-qwen3-0.6b-kq8.json and kat-qwen3-0.6b-kvq8.json; both miss varied-720w
  ("25") exactly as the f16 golden does.
- The ops phase runs the add-on's ggml-hrx-fa-decode-bench on fa_decode_q8_d128
  with V f16 and V q8_0 (Hq/Hkv 16/8 and 32/8, n_kv 1031/2048/4103) against its
  double-precision reference; the build adds that target when the add-on is in.
  test-backend-ops has no FLASH_ATTN_EXT case with K and V of different types.
- The gate takes ~/GPU-TOKEN before its first GPU step: it waits up to
  HW_GATE_TOKEN_WAIT s (3600) logging the holder, writes "hw-gate [run N] pid P
  <time>", removes it on exit only if the content is still its own, and fails
  with exit 2 naming the holder on timeout. Two #481 gate runs died "server
  failed (rc 137)" while Hyperloom held the token. HW_GATE_TOKEN=none skips it.

Measured on strixhalo, performance mode:
- token: a fake holder with HW_GATE_TOKEN_WAIT=15 gives "gpu-token FAIL", exit 2;
  the recording run waited 960 s for Hyperloom's token, took it, released it
- fa-decode-q8: 6/6 PASS, NMSE 1.3e-13 to 2.3e-13, 5 repeats bit-identical

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong pushed a commit that referenced this pull request Oct 21, 2026
…ftmax sinks decline (#15), startup ABI check (#16)

Closes the census: 43 of 43 runnable cells with 0 nodes off HRX.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Oct 21, 2026
…ftmax sinks decline (#15), startup ABI check (#16) (#481)

Closes the census: 43 of 43 runnable cells with 0 nodes off HRX.

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong pushed a commit that referenced this pull request Oct 22, 2026
The first on-device run of the npu phase passed with an empty tok_s (a checker bug,
npu-kernels #14): the floor check only ran when a number was present. Require a number;
unit-checked on three checker outputs (90.50 PASS, empty FAIL, 41.2 below a 60 floor FAIL).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bong-water-water-bong pushed a commit that referenced this pull request Oct 22, 2026
npu-kernels #14 fixes the awk printf redirection that left tok_s empty, so the gate npu phase speed floor now has a number to check (11.05 ms/step -> 90.50 tok/s on both gawk and mawk).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bong-water-water-bong added a commit that referenced this pull request Oct 22, 2026
* npu: a hardware-gate NPU phase, closed+encrypted kernel delivery, docs

Make the NPU engine shippable with the runtime and kernels closed, as compiled
binaries only:

- scripts/hw-gate.sh: a neutral `npu` phase (not in the default phases; add with
  --phases npu) that auto-skips with no NPU (/dev/accel/accel0) or no checker, and
  otherwise runs a private checker ($HW_GATE_NPU_CHECK) inside the gate's GPU claim
  and under ~/npu.lock, reads its neutral result lines (oracle vs an fp64 reference,
  greedy64, prefill, reproducibility) and applies a tok/s floor (HW_GATE_NPU_FLOOR).
  The checker and kernels are private; the gate carries no method.
- config/npu-kernels.config: the private NPU-kernels pin the release fetches
  (ONEBIT_NPU_PRIVATE), same shape as config/gpu-kernels.config.
- docs/npu.md, docs/releases.md: the NPU kernels ship as compiled, encrypted
  artifacts that `1bit` verifies and decrypts in memory at load (fails clean on a
  wrong key, no network callback); no source or method is public.

Proven on Strix Halo (private npu-kernels npu/ship-sealed-kernels): the sealed dx
kernel set decodes BYTE-IDENTICAL logits to the plain set (Qwen3-0.6B, 24/24 vs
fp64, md5 9a405cf6...), 64/64 greedy at ~90 tok/s, a foreign-key set refused cleanly,
a plain ELF refused under REQUIRE_SEALED, 0 amdxdna/IOMMU faults. The on-device gate
`npu` phase run and wiring the kernels into the weekly package are the remaining
steps (strixhalo was under a maintenance freeze).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* config/npu-kernels.config: pin the neutral-metadata commit

Moves the NPU-kernels pin to 1bit-MONSTER/npu-kernels npu/ship-sealed-kernels HEAD
(neutral sealed metadata). The pin moves to the merge commit when PR #13 lands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* weekly: pack the sealed NPU kernels (WEEKLY_NPU_KERNELS, off until the npu gate passes)

The release side of shipping the NPU kernels closed, compiled and encrypted only:

- scripts/weekly.sh build: with WEEKLY_NPU_KERNELS=1, fetch config/npu-kernels.config and
  call the private checkout's one entry point (addons/release-kernels.sh <out> <key dir>),
  which installs the kernel key for the runtime build and writes the sealed sets to
  $BUILD/npu/kernels; build with ONEBIT_NPU_PRIVATE and ONEBIT_NPU_RELEASE=ON, so the
  packed 1bit loads sealed kernels only. The policy lives in the binary: the package has no
  launcher wrapper (Lemonade execs 1bit directly), so an environment variable could be unset.
  Default off until the hardware gate's npu phase passes on Strix Halo.
- scripts/package-linux.sh: packs $BUILD/npu/kernels beside 1bit (npu/kernels), where the
  route looks for it.
- package_checks (npu): every file under npu/kernels but layout.json is sealed, never a
  plaintext ELF; every ELF in the tarball is for the host (x86-64); the packed 1bit has no
  symbol table and its strings name no private source file and no build-machine home.
  Private-source file names now include the NPU-kernels checkout.
- config/npu-kernels.config: pin the private branch head that has the entry point; neutral
  require= list. The pin moves to the merge commit when npu-kernels #13 lands.
- docs/releases.md: the switch, the checks, the key directory (ONEBIT_NPU_KEY_DIR).

Checked on ryzen: bash -n; package_checks on a synthetic good package passes all four new
checks, and on a bad one fails each (plaintext kernel ELF, an EM 264 ELF, an unstripped 1bit
with 25 symbols, a private source name, a /home/ path). Not run on Strix Halo yet
(maintenance freeze): no weekly build with the switch on has been made.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* config/npu-kernels.config: pin the npu-kernels merge commit; npu-only gate needs no llama.cpp

- Pin 1bit-MONSTER/npu-kernels at dfe511236d03, the npu-dx merge of #13 (sealed kernel
  loading, release entry point, sealed-only release switch). fetch-private.sh fetches it and
  finds every require= file.
- scripts/hw-gate.sh: the llama.cpp binary check applies to the HRX phases only, so
  --phases npu runs on a box with no HRX build. Dry run on ryzen (no NPU): npu SKIP, exit 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs/npu.md: how to run the NPU engine; package README line when it carries the kernels

Neutral: "the 1bit NPU engine runs the model on the XDNA 2 NPU", the serve command for a Qwen3
NPU model directory (0.6B, 1.7B, 4B), Lemonade's onebit npu backend, and that the kernels ship
inside the package. README.txt gains the NPU serve line only when the package has npu/kernels.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* hw-gate npu: no tok/s reported is a failure, not a skipped floor

The first on-device run of the npu phase passed with an empty tok_s (a checker bug,
npu-kernels #14): the floor check only ran when a number was present. Require a number;
unit-checked on three checker outputs (90.50 PASS, empty FAIL, 41.2 below a 60 floor FAIL).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* npu-kernels pin dfe51123 -> 6eee7607: npu-check reports tok/s

npu-kernels #14 fixes the awk printf redirection that left tok_s empty, so the gate npu phase speed floor now has a number to check (11.05 ms/step -> 90.50 tok/s on both gawk and mawk).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Fable 5.1 <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.

1 participant