From 34b3fcc21a79a01be8d497ea11b654641d86591b Mon Sep 17 00:00:00 2001 From: bong-water-water-bong Date: Mon, 26 Oct 2026 00:10:32 -0300 Subject: [PATCH 1/2] os: the onebit manifest reads the keyed catalogue, so it lists the entries The manifest producer matched nothing and wrote `mode=live` with zero entries, which made the models row PASS VACUOUSLY ("0 onebit HRX entries and 0 NPU entries listed") - worse than a failure, because it hides a real gap. Candidate 6's build log said it plainly: "onebit-models: 0 hrx + 0 npu", and candidate 7's gate R then showed the row skipping on "0 HRX + 0 NPU entries are registered". Cause: the vendored server_models.json is a dict KEYED BY MODEL ID ({"Qwen2.5-0.5B-Instruct-CPU": {...}, ...}), not a list of dicts carrying an "id" field. The walk only looked for an "id" field, found none at the top level, and descended into the values without ever associating a record with its key. Now: a dict value that looks like a record ("recipe" or "onebit_backend" present) is taken with its KEY as the id when it has none; lists and nested dicts are still walked. Verified against the real catalogue on the candidate 6/7 build: before: 0 hrx + 0 npu after : 26 hrx + 0 npu (GLM-4.7-Flash-1bit, LFM2-1.2B-1bit, MiniCPM-V-4.6-1bit, MiniCPM5-1B-1bit, Qwen2.5-Coder-1.5B-Instruct-1bit, ...) The npu count stays 0 because the four "onebit_backend" hits in this catalogue are not "npu" - that is the catalogue's content, not a parsing gap. bash -n clean. --- os/mini/mkimage.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/os/mini/mkimage.sh b/os/mini/mkimage.sh index 43c7498f..7300cddc 100755 --- a/os/mini/mkimage.sh +++ b/os/mini/mkimage.sh @@ -370,6 +370,10 @@ except Exception as e: # The catalogue has been both a list of model dicts and a dict of {section: [model dicts]}; walk # whichever it is so a layout change does not silently produce an empty manifest. def models(node): + # The vendored catalogue is a dict KEYED BY MODEL ID ({"Qwen2.5-0.5B-Instruct-CPU": {...}}); earlier + # shapes were a list of dicts with an "id" field. Handle both, and take the key as the id when the + # record has none - the first version only looked for an "id" field, so it matched nothing in the + # keyed catalogue and wrote a manifest with zero entries, which made the models row pass vacuously. if isinstance(node, list): for x in node: yield from models(x) @@ -377,8 +381,14 @@ def models(node): if "id" in node and isinstance(node.get("id"), str): yield node else: - for v in node.values(): - yield from models(v) + for k, v in node.items(): + if isinstance(v, dict) and ("recipe" in v or "onebit_backend" in v): + rec = dict(v) + rec.setdefault("id", k) + if isinstance(rec.get("id"), str): + yield rec + else: + yield from models(v) rows, seen = [], set() for m in models(data): recipe = m.get("recipe") From 9e97a69530e17c3bda5bce7e39d26a975e8ff120 Mon Sep 17 00:00:00 2001 From: bong-water-water-bong Date: Mon, 26 Oct 2026 00:11:09 -0300 Subject: [PATCH 2/2] os: the NPU marker is inside recipe_options, so those entries are not labelled hrx Follow-up to the walk fix on this branch: onebit_backend does exist in the vendored catalogue, but it is nested inside each record's recipe_options, not at the record's top level: /Qwen3-0.6B-NPU-1bit/recipe_options -> "npu" /Qwen3-1.7B-NPU-1bit/recipe_options -> "npu" /Qwen3-4B-NPU-1bit/recipe_options -> "npu" /Qwen3.6-35B-A3B-NPU-1bit/recipe_options -> "npu" Reading it from the top level alone classified all four NPU models as "hrx", which would have made the models row expect them in Lemonade's HRX list and fail there for the wrong reason. Both places are now consulted, so either shape works. Verified against the real catalogue (candidate 6/7 build tree, 286 entries): before both fixes: 0 hrx + 0 npu (vacuous pass) walk fix only : 26 hrx + 0 npu (NPU entries mislabelled) now : 22 hrx + 4 npu = 26, matching the catalogue's 26 recipe==onebit entries exactly npu Qwen3-0.6B-NPU-1bit, Qwen3-1.7B-NPU-1bit, Qwen3-4B-NPU-1bit, Qwen3.6-35B-A3B-NPU-1bit. bash -n clean. --- os/mini/mkimage.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/os/mini/mkimage.sh b/os/mini/mkimage.sh index 7300cddc..4874f813 100755 --- a/os/mini/mkimage.sh +++ b/os/mini/mkimage.sh @@ -392,7 +392,13 @@ def models(node): rows, seen = [], set() for m in models(data): recipe = m.get("recipe") + # The NPU marker lives INSIDE recipe_options (recipe_options: {"onebit_backend": "npu"}), not at the + # record's top level - reading it from the top missed all four NPU entries and would have labelled + # them "hrx". Check both places so either shape works. + opts = m.get("recipe_options") backend = m.get("onebit_backend") + if isinstance(opts, dict) and opts.get("onebit_backend"): + backend = opts.get("onebit_backend") if recipe == "onebit": kind = "npu" if backend == "npu" else "hrx" elif backend == "npu":