Skip to content

os: the onebit manifest reads the keyed catalogue, so it lists the entries - #586

Merged
bong-water-water-bong merged 3 commits into
mainfrom
1bit/onebit-manifest-keyed
Oct 26, 2026
Merged

bong-water-water-bong merged 3 commits into
mainfrom
1bit/onebit-manifest-keyed

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Follow-up to #579, which added the manifest producer. It matched nothing and wrote mode=live with zero entries, so the models row would have passed vacuously — "0 onebit HRX entries and 0 NPU entries listed". A vacuous pass is worse than a failure: it hides the gap.

The evidence was in plain sight and I read past it twice:

  • candidate 6's build log: onebit-models: 0 hrx + 0 npu
  • candidate 7's gate R: SKIP models : … (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": {…}, "Llama-3.2-1B-Instruct-CPU": {…}, …}   286 keys

My walk looked for an "id" field, found none at the top level, and descended into the values without ever associating a record with its key. So every onebit record was invisible.

Fix. 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, so the list-of-dicts shape keeps working.

Verified against the real catalogue on the candidate 6/7 build tree:

hrx npu
before 0 0
after 26 0

e.g. GLM-4.7-Flash-1bit, LFM2-1.2B-1bit, MiniCPM-V-4.6-1bit, MiniCPM5-1B-1bit, Qwen2.5-Coder-1.5B-Instruct-1bit.

npu 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.

Worth pairing with #585 in the next candidate: with this fix the models row becomes a real check on hardware (26 entries expected, each looked up in Lemonade's list), and with #585 the hrx-device row stops failing in the GPU-less VM. Together they turn two vacuous/false rows into the two that would actually have caught c5.

…tries

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.
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 7ba6204)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

579 - Partially compliant

Compliant requirements:

  • The manifest file is now correctly generated from server_models.json
  • The models row will no longer pass vacuously
  • Skip reasons for chat-hrx and chat-npu are now accurate
  • The secureboot row now reads Secure Boot status from firmware variables

Non-compliant requirements:

  • The chat-hrx "did not answer" failure is not addressed in this PR (separate issue)

Requires further human verification:

  • Verification that the chat-hrx "did not answer" issue is resolved in a separate change

585 - Partially compliant

Compliant requirements:

  • The hrx-device row now uses the same device detection logic as gpu
  • Skip reasons now clearly indicate which device is missing

Non-compliant requirements:

  • None

Requires further human verification:

  • None
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Incorrect Manifest Generation for Keyed Catalogue

The manifest generation logic was not correctly handling the keyed catalogue format where model entries are keyed by their ID. Previously, it only looked for an "id" field at the top level of each record, which caused it to miss all entries in the keyed catalogue. This resulted in a manifest with zero entries, leading to a vacuous pass of the models row. The fix correctly identifies records in the keyed catalogue by checking for the presence of "recipe" or "onebit_backend" fields and uses the dictionary key as the ID when the record lacks an "id" field.

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)
    elif isinstance(node, dict):
        if "id" in node and isinstance(node.get("id"), str):
            yield node
        else:
            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)

… 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.
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 9e97a69

@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 7ba6204

@bong-water-water-bong
bong-water-water-bong merged commit e8b0db8 into main Oct 26, 2026
8 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the 1bit/onebit-manifest-keyed branch October 26, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant