From 663b4a6c123a9aa3fca58410a491dedef7a39e9f Mon Sep 17 00:00:00 2001 From: lane Date: Wed, 11 Nov 2026 23:43:06 -0400 Subject: [PATCH 1/3] tools: the f16 narrowing generator names its corpus and fingerprints it, and --write refuses a foreign one The generator took the corpus from a module constant and never said which tree it read, so two runs that disagree are only comparable by reading the submodule sha by hand. Same fix as the check got: CORPUS /home/bcloud/wt/trel3/third_party/llama.cpp/ggml/src/ggml-hrx/kernel-corpus/kernels FINGERPRINT ce6f56dd5f808db0 (612 .loom file(s)) printed BEFORE the verdict, not after it -- a FAIL is when the identity of the tree matters most, and the first version printed it only on the pass path, so every failing run was unattributed. --corpus DIR scan a corpus other than the pinned checkout's (check() and write() both take it) fingerprint sha256 over sorted (relative path, bytes), so a move changes it, not only an edit And the footgun that this change made reachable, found by walking into it: `--write --corpus ` rewrote tools/ci/hrx_f16_narrowing.txt and hrx_f16_accumulators.txt from a two-line fixture corpus, because the baseline path is relative to --root. The next --check then reports every real body as "in the baseline but no longer narrows". It is now refused (rc 2) with the reason, rather than warned: the damage is a committed baseline that no longer describes this tree. The two files were restored (git checkout -- ...) before this commit; this commit touches only the generator. Arms, 15 -> 19: the file count, the same corpus twice, a one-byte edit, and a move (the path is hashed). Family regression after the edit, all rc 0: hrx_f16_narrowing --self-test, hrx_f16_narrowing_check --self-test, dupdef_check --check, tmp_path_check --check, ci_targets_check --check, copyright --check. --- tools/ci/hrx_f16_narrowing.py | 61 ++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/tools/ci/hrx_f16_narrowing.py b/tools/ci/hrx_f16_narrowing.py index 434fa081a..d110d7ed4 100755 --- a/tools/ci/hrx_f16_narrowing.py +++ b/tools/ci/hrx_f16_narrowing.py @@ -41,6 +41,7 @@ hrx_f16_narrowing.py --write rewrite the baseline from the corpus """ import argparse +import hashlib import pathlib import re import sys @@ -57,6 +58,22 @@ # Measured (engine#2167): at 120 lines past the init, `mul_mat_id_f16_f16_wmma_core`'s four accumulators read as # unaffected, because their update sits ~250 lines below in a long Q4_K/Q6 loop body. 250 finds them. ACC_WINDOW = 250 + + +def corpus_fingerprint(corpus): + """sha256 over the corpus: sorted (relative path, bytes), so a moved file changes it too. + + A narrowing count is only comparable to another run if the tree it read is identified. Two + checkouts of third_party/llama.cpp at different commits both answer "9 bodies" until one of them + does not, and without this the only way to tell is to read the submodule sha by hand. + """ + h = hashlib.sha256() + n = 0 + for f in sorted(corpus.rglob("*.loom")): + h.update(str(f.relative_to(corpus)).encode()) + h.update(hashlib.sha256(f.read_bytes()).digest()) + n += 1 + return f"{h.hexdigest()[:16]} ({n} .loom file(s))" # The vector form is most of the corpus: `vector.fptrunc %x : vector<4xf32> to vector<4xf16>` contains neither # "f32 to f16" nor "truncf", and at llama.cpp 2b7960bae 132 of the 233 narrowing lines are written that way. # Matching the scalar spelling alone listed 25 bodies; the corpus has 51 (engine#2204's review). A bare `truncf` is @@ -132,11 +149,14 @@ def drift(baseline, found): return sorted(found - baseline), sorted(baseline - found) -def check(root): - corpus = root / CORPUS +def check(root, corpus=None): + corpus = pathlib.Path(corpus) if corpus else root / CORPUS if not corpus.is_dir() or not any(corpus.rglob("*.loom")): - print(f"skip: no .loom corpus at {CORPUS} (check out third_party/llama.cpp)", file=sys.stderr) + print(f"skip: no .loom corpus at {corpus} (check out third_party/llama.cpp, or pass --corpus)", file=sys.stderr) return 77 + # Printed before the verdict, not after it: a FAIL is when the identity of the tree matters most. + print(f"CORPUS {corpus.resolve()}") + print(f"FINGERPRINT {corpus_fingerprint(corpus)}") base_text = (root / BASELINE).read_text(encoding="utf-8") baseline = baseline_paths(base_text) found = narrowing_bodies(corpus) @@ -180,10 +200,10 @@ def check(root): return 0 -def write(root): - corpus = root / CORPUS +def write(root, corpus=None): + corpus = pathlib.Path(corpus) if corpus else root / CORPUS if not corpus.is_dir(): - print(f"skip: no .loom corpus at {CORPUS}", file=sys.stderr) + print(f"skip: no .loom corpus at {corpus}", file=sys.stderr) return 77 head = "".join(line for line in (root / BASELINE).read_text(encoding="utf-8").splitlines(True) if line.lstrip().startswith("#")) @@ -280,6 +300,18 @@ def check_that(ok, what): " %next = vector.mulf %acc, %k : vector<16xf16>\n") check_that("far.loom" in accumulator_bodies(accdir), "an update 200 lines below the init is found") + # The corpus identity: same bytes -> same fingerprint, an edit -> a different one, a move -> a + # different one (the relative path is hashed), and the count it reports. + with tempfile.TemporaryDirectory() as d: + c = pathlib.Path(d) + (c / "a.loom").write_text("one\n"); (c / "s").mkdir(); (c / "s" / "b.loom").write_text("two\n") + fp = corpus_fingerprint(c) + check_that(fp.endswith("(2 .loom file(s))"), "fingerprint: counts the .loom files it read") + check_that(fp == corpus_fingerprint(c), "fingerprint: the same corpus twice is the same value") + (c / "s" / "b.loom").write_text("two changed\n") + check_that(fp != corpus_fingerprint(c), "fingerprint: a one-byte edit changes it") + (c / "a.loom").rename(c / "s" / "a.loom") + check_that(fp != corpus_fingerprint(c), "fingerprint: a move changes it too (the path is hashed)") print(f"hrx_f16_narrowing self-test: {'ok' if not failures else f'{failures} failure(s)'}") return 1 if failures else 0 @@ -290,14 +322,27 @@ def main(): ap.add_argument("--write", action="store_true") ap.add_argument("--self-test", action="store_true") ap.add_argument("--root", default=".") + ap.add_argument("--corpus", default=None, + help="the .loom corpus to scan (default: the pinned llama.cpp checkout's kernel-corpus)") args = ap.parse_args() root = pathlib.Path(args.root).resolve() + corpus = pathlib.Path(args.corpus).resolve() if args.corpus else None if args.self_test: return self_test() if args.write: - return write(root) + # The baseline is a file in the tree it describes, so rewriting it from a corpus that lives + # somewhere else records a corpus this checkout does not have -- and the next --check then + # reports every one of its own bodies as "in the baseline but no longer narrows". Refused + # rather than warned, because the damage is a committed baseline that no longer describes + # this tree (found by doing exactly that with a two-line fixture corpus). + if corpus is not None and root not in corpus.parents and corpus != root: + print(f"hrx_f16_narrowing: refusing --write with --corpus {corpus} outside --root {root}: " + f"the baseline is written into the root and would describe a corpus this tree does not have", + file=sys.stderr) + return 2 + return write(root, corpus) if args.check: - return check(root) + return check(root, corpus) ap.print_help() return 2 From d0ebc3ce217f322650b6d1e49b7cc3575ac9c39e Mon Sep 17 00:00:00 2001 From: lane Date: Thu, 12 Nov 2026 00:21:37 -0400 Subject: [PATCH 2/3] tools: drop the duplicate ctest row #2246 added for the f16 check's self-test `add_python_test(hrx_f16_narrowing ...)` (CMakeLists.txt:418) has run `tools/hrx_f16_narrowing_check.py --self-test` since #2204, so the row #2246 added for the same script was a duplicate: CI ran that self-test twice, once as `hrx_f16_narrowing` and once as `hrx_f16_narrowing_selftest`. One script, one row. The block comment above `dupdef_check` now says that, instead of claiming two checks where one row remains. Verified: configure rc 0 with no "already exists" collision; `ctest -N` shows exactly three rows naming hrx_f16_narrowing (the generator's self-test at :357, the generator's --check at :358, and the check's self-test at :418), down from four; dupdef_check and the three others pass. Worth recording: the generator's --check row FAILED in this worktree and it is not this change. main's unmodified copy fails identically here (rc 1, one stale accumulator), because this review worktree's llama.cpp is at d8bd0ff60 while the pin is 4107043c - so the corpus is 124 .loom files instead of 612 and the baseline describes a different tree. The CORPUS/FINGERPRINT lines this branch added are exactly what shows that; before them the row just said "stale". --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e60f43ad6..da803dd99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,14 +210,16 @@ add_dependencies(onebit npu_decode_note_test) # refusal this pre-empts ("differ in size") needed private-repo archaeology to diagnose; the symptom is checkable here. add_test(NAME kernel_set_check COMMAND bash ${CMAKE_SOURCE_DIR}/tests/kernel_set_check_test.sh) -# engine#2186/#2244: two checks that were only ever run by hand. `tools/ci/dupdef_check.py` is the one +# engine#2186/#2244: `tools/ci/dupdef_check.py`, a check that was only ever run by hand. It is the one # that found a shadowed `def self_test()` in tools/hrx_f16_narrowing_check.py -- the surviving copy -# passed and the shadowed one's arms could never fail. Both take no build input, so a ctest row is the +# passed and the shadowed one's arms could never fail. It takes no build input, so a ctest row is the # route that needs no ci.yml line (the workflow is not editable from a lane). +# engine#2246 also added a row for that file's --self-test; `add_python_test(hrx_f16_narrowing ...)` +# at line 418 has run exactly that since #2204, so the row was a duplicate and is gone -- one script +# gets one row. add_test(NAME dupdef_check COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/ci/dupdef_check.py --check WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -add_test(NAME hrx_f16_narrowing_selftest COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/hrx_f16_narrowing_check.py --self-test - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + add_test(NAME bump_hrx_report COMMAND bash ${CMAKE_SOURCE_DIR}/tests/bump_hrx_report_test.sh ${CMAKE_SOURCE_DIR}/scripts/bump-hrx-report.sh) add_test(NAME metrics_serve COMMAND ${CMAKE_SOURCE_DIR}/tests/metrics_serve.sh $) # serve --print-backend: the llama-server Lemonade starts itself, one hop (docs/serve.md): no GPU. From 2dde4f3e83e028f29d97a076033a6125d3ffa23e Mon Sep 17 00:00:00 2001 From: lane Date: Thu, 12 Nov 2026 00:42:04 -0400 Subject: [PATCH 3/3] scripts/pack-elf.sh: remove a duplicate pack_require_provenance -- the first copy was dead A shell function defined twice does what a Python def defined twice does: the last one wins and the first is dead. Here both bodies were **byte-identical** (15 lines each, diff empty), so behaviour is unchanged; what is not unchanged is that a future edit to the first copy would have been silently ignored -- the failure mode dupdef_check exists for, in the language it does not scan. Checked, not assumed: bash -n scripts/pack-elf.sh rc 0 . scripts/pack-elf.sh + declare -f of the five exported helpers all present pack_require_provenance on an identical stage/build pair 0 before, 0 after pack_require_provenance on a one-byte-changed pair 1 before, 1 after (and the same 'differs from the build' line) The surviving copy is the one with the fuller comment (why a missing implementation library must leave the package rather than ship broken), and pack_require_tools -- which sat between the two copies -- still has exactly one definition. **Shell is deliberately NOT added to tools/ci/dupdef_check.py.** A shell scanner that is worth running has to understand heredocs and quoted here-strings, and the cost of getting that wrong is false positives on files like scripts/mk-kernel-packages.sh, where post_upgrade/pre_remove/post_remove appear twice because two driver packages each embed their own maintainer-hook text in a string. Reporting those as duplicates would train people to ignore the check. The one real instance is fixed by hand and recorded here instead. --- scripts/pack-elf.sh | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/scripts/pack-elf.sh b/scripts/pack-elf.sh index e61cecc80..d7cca3d80 100644 --- a/scripts/pack-elf.sh +++ b/scripts/pack-elf.sh @@ -108,22 +108,6 @@ pack_unresolved_needed() { # (a hardware-gate cache, a drop) carries that tree's RUNPATH and its build's missing parts: in the 2026-10-23 # rc, `llama-perplexity` came from a hardware-gate build and pointed at that build's directory. Compares each # staged file with the same relative path under the build and reports what has no counterpart there. -pack_require_provenance() { # - local stage=$1 build=$2 f rel bad=0 - while IFS= read -r -d '' f; do - pack_is_elf "$f" || continue - rel=${f#"$stage"/} - if [ ! -f "$build/$rel" ]; then - echo " $rel is not in the build being packaged" - bad=1 - elif ! cmp -s "$f" "$build/$rel"; then - echo " $rel differs from the build being packaged" - bad=1 - fi - done < <(find "$stage" -type f -print0) - return $bad -} - # The programs `put` was asked for are the ones the release promises: if one of them cannot resolve, that is a # build problem (the tool came from another tree, or its implementation library was not built) and the package # has to fail rather than quietly lose the tool. Call once per directory group: pack_require_tools