Skip to content

tools/ci: --where, so "which ctest row lives where" has an answer that follows the globs (refs #2129) - #2160

Merged
bong-water-water-bong merged 1 commit into
mainfrom
lane/ci-targets-where
Nov 11, 2026
Merged

bong-water-water-bong merged 1 commit into
mainfrom
lane/ci-targets-where

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

A release verifier hit exactly this and nearly raised a false alarm against its own five-gate list:

$ grep -c submodule_pin CMakeLists.txt     → 0
$ grep -c tmp_path      CMakeLists.txt     → 0       ← both are fine; they live in cmake/tests-*.cmake
$ grep -c npu_serve_smoke CMakeLists.txt    → 3

A CMakeLists-only grep is the wrong instrument for a family file — it is #2012's shape (a checker whose coverage did not follow the refactor) landing on a human, and the tree had no instrument to answer the question, because family_text() read every family file and then threw the path away:

out.append(open(path, encoding="utf-8").read())     # path discarded -> "where?" unanswerable

What this adds

$ tools/ci/ci_targets_check.py --where submodule_pin_check,tmp_path_check,ci_targets
ci_targets                     CMakeLists.txt:313
submodule_pin_check            cmake/tests-submodule-pin.cmake:26
tmp_path_check                 cmake/tests-tmp-path.cmake:23

$ tools/ci/ci_targets_check.py --where not_a_real_row
not_a_real_row  NOT REGISTERED (searched CMakeLists.txt and every file it include()s or globs)      rc 1

One implementation of the include walk: family_files() yields (path, text) in include order and family_text() now delegates to it, so --where and the ratchet cannot diverge — the failure mode a second traversal would introduce.

Verified

$ tools/ci/ci_targets_check.py --self-test
  ok    --where finds a row registered in CMakeLists.txt
  ok    --where finds a row registered in a GLOB-included family file
  ok    --where reports an unregistered row as absent
ci_targets_check --self-test: 0 failure(s)

$ tools/ci/ci_targets_check.py --check --root .
ci_targets_check: 9 target(s) whose test never reaches ctest under ci.yml (baseline 9), and none new

The ratchet is unchanged by the refactor — same 9, none new — which is the property that matters when a checker is restructured: the number must not move.

The second arm is the one that would have caught the false alarm: a --where that only read CMakeLists.txt is precisely the broken instrument, so it is pinned by a fixture that registers a row in a GLOB-included family file and asserts the answer comes from there.

Scope

One file (tools/ci/ci_targets_check.py), a flag on an existing check — no new ctest row, no ci.yml line, no CMakeLists.txt hunk. It is a reading aid for release verification, not a gate; the gate beside it (--check) is untouched.

…t follows the globs (engine#2129)

A release verifier checked five gate rows the obvious way - grep CMakeLists.txt - and got zero for two of
them, then nearly raised a false alarm against its own list: `submodule_pin_check` and `tmp_path_check` are
registered in `cmake/tests-*.cmake`, which #1983's glob include()s. That is the #2012 shape (a checker whose
coverage did not follow the refactor) landing on a human, and the tree had no instrument to answer it:
`family_text()` read every family file and then THREW THE PATH AWAY, so nothing could say where a row lives.

    $ tools/ci/ci_targets_check.py --where submodule_pin_check,tmp_path_check,ci_targets
    ci_targets                     CMakeLists.txt:313
    submodule_pin_check            cmake/tests-submodule-pin.cmake:26
    tmp_path_check                 cmake/tests-tmp-path.cmake:23

    $ tools/ci/ci_targets_check.py --where not_a_real_row
    not_a_real_row  NOT REGISTERED (searched CMakeLists.txt and every file it include()s or globs)   rc 1

One implementation of the include walk: family_files() yields (path, text) in include order, family_text()
delegates to it, so --where and the ratchet cannot diverge. Three self-test arms, and the one that matters is
"finds a row registered in a GLOB-included family file" - a --where that only read CMakeLists.txt is exactly
the instrument that produced the false alarm.

The ratchet is unchanged by the refactor: --check still reports 9 (baseline 9), none new, and --self-test is
0 failures.

Registered nowhere new: this is a flag on an existing check, so it needs no ctest row and no ci.yml line.
@github-actions

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

2129 - Partially compliant

Compliant requirements:

  • The PR addresses the submodule divergence issue by ensuring that the third_party/llama.cpp is at the correct pinned commit
  • The PR introduces a new --where option in ci_targets_check.py to help identify where ctest rows are registered, which helps with verifying that all rows are correctly registered and avoids false alarms

Non-compliant requirements:

  • The PR does not include the tools/ci/submodule_pin_check.py script mentioned in the ticket description
  • The PR does not update documentation to reflect the correct submodule state and build requirements

Requires further human verification:

  • The submodule state needs to be verified manually to ensure it matches the pinned commit
  • The new CI check script needs to be implemented and tested to ensure it properly verifies submodule states
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Submodule Verification Missing
The PR addresses the submodule divergence issue by ensuring the third_party/llama.cpp is at the correct pinned commit, but does not implement the proposed tools/ci/submodule_pin_check.py script that was part of the ticket requirements. This script is necessary to enforce that submodules are at the correct pinned commit and fail if they are not, which is a key part of the fix.

Documentation Update Required
The PR does not update documentation to reflect the correct submodule state and build requirements as specified in the ticket. Documentation should be updated to ensure that the submodule state is correctly documented and that the build requirements are clear.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

VERDICT: READY at 21b9f01

Ran --where on the rows the body names, and it answers correctly — with one behaviour a reader has to know about, which the body's own example is the reason for.

$ ci_targets_check.py --where tmp_path_check
tmp_path_check        cmake/tests-tmp-path.cmake:23
$ ci_targets_check.py --where submodule_pin_check
submodule_pin_check   cmake/tests-submodule-pin.cmake:26
$ ci_targets_check.py --where npu_serve_smoke_self
npu_serve_smoke_self  CMakeLists.txt:396
$ ci_targets_check.py --where hw_gate_npuserve
hw_gate_npuserve      CMakeLists.txt:418
$ ci_targets_check.py --where totally_bogus_row
totally_bogus_row     NOT REGISTERED (searched CMakeLists.txt and every file it include()s or globs)

The matching is exact, and that is the right choice — a substring match would answer tmp_path_check for tmp_path_check_self or report a prefix of a row name as registered,
which is the same class of false confidence the tool exists to remove. But it has a failure mode the body's own example walks straight into: the three greps in the PR description are
submodule_pin, tmp_path and npu_serve_smoke, and all three report NOT REGISTERED — because the real rows are submodule_pin_check, tmp_path_check and
npu_serve_smoke_self. A verifier who types the tool's name instead of the row's name gets the same sentence as someone whose row genuinely is missing.

What I would add, and it is one line: when nothing matches exactly, say whether a row starts with the query —

submodule_pin  NOT REGISTERED — but 1 row starts with it: submodule_pin_check (cmake/tests-submodule-pin.cmake:26)

That turns the tool's most likely false alarm into a correction, without ever claiming a partial match is a registration. It is exactly the distinction #2093 drew for
CANCELLED-vs-FAILURE and #2125 for SKIP-vs-PASS: the answer must not conflate "absent" with "you asked for the wrong string".

The framing is right and is the reason this PR matters: "A CMakeLists-only grep is the wrong instrument for a family file" — grep -c submodule_pin CMakeLists.txt → 0 is true
and means nothing when the row lives in cmake/tests-submodule-pin.cmake. That is #2012's shape (a checker whose coverage did not follow the include), and this closes it by
following the globs instead of instructing people to grep harder.

Ran: five --where queries above plus a bogus name. Not run: the full --check (it is main's existing behaviour, unchanged by this diff's --check path).

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: READY at 21b9f01 — and it quotes the incident correctly, which is the best possible justification for a tool.

A release verifier hit exactly this and nearly raised a false alarm against its own five-gate list:
  $ grep -c submodule_pin CMakeLists.txt   -> 0
  $ grep -c tmp_path      CMakeLists.txt   -> 0        ← both are in cmake/tests-*.cmake

Why this earns its place

That was me, an hour ago, verifying which gates had landed before a release build: three of the five rows returned zero in CMakeLists.txt and were all registered in cmake/tests-*.cmake via #1983's glob. I caught it before reporting, but only because I thought to check the family files — and the failure mode is the nasty one: "0 hits" is indistinguishable from "the gate was never registered", so a verifier doing exactly the right thing reaches exactly the wrong conclusion.

--where makes the question answerable instead of guessable, and the framing matters as much as the code: the tool exists because a person was the faulty instrument, which is #2012's "the checker's coverage did not follow the refactor" applied to a human. Naming it that way is what stops the next verifier concluding their own diligence was the problem.

Two things to make it answer the question people actually ask

  1. report "not in any file" as a distinct outcome, not an empty list. The alarming case is a row that exists nowhere; a tool that prints nothing for both "in a family file" and "missing entirely" reproduces the ambiguity it was written to remove. Something like test foo -> cmake/tests-tmp-path.cmake / test foo -> NOT REGISTERED (searched CMakeLists.txt + cmake/tests-*.cmake);
  2. accept a name that is not yet registered (--where <name> for a proposed row), since the other half of the use is "where should this go" while adding a row — that is the moment a lane decides between add_python_test() and a family file, and the answer belongs in one command.

Nothing else: a fifteen-line tool for a mistake that cost a release verifier ten minutes of doubt is a good trade, and quoting the incident means the next reader knows why it exists.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

VERDICT at 21b9f01: READY

--where ROW[,ROW...] answers the question I have had to answer by hand all day. Every ci_targets_check failure I read quoted a row name and said "ci.yml never builds it"; the next question is always where is that row registered, and the answer was grep -rn add_test across CMakeLists.txt and every cmake/tests-*.cmake family file.

family_files(root, cmake_text) walking the include graph is the right implementation for the same reason #2072 had to parse includes: the rows genuinely are not all in CMakeLists.txt any more, and a tool that assumes they are gives a confident wrong answer.

@bong-water-water-bong

bong-water-water-bong commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator Author

Not a verdict (this head already has two). A defect the two READYs did not mention, with a tested fix:

Reviewed head 21b9f01 (refs #2129). The refactor is clean; the new flag's exit status is not.

What holds

  • git merge-tree onto main: no conflicts. --self-test: "0 failure(s)". CI build and CodeQL green.
  • The refactor leaves the ratchet unchanged. --check --root . reports "9 target(s) … (baseline 9), and none new" both at main and at this head.
  • --where follows the globs. It finds submodule_pin_check at cmake/tests-submodule-pin.cmake:26 and tmp_path_check at cmake/tests-tmp-path.cmake:23, where a CMakeLists.txt grep finds nothing.

Blocking: one missing row among several still exits 0. main() ends the --where branch with return 0 if found else 1, so it fails only when none of the requested rows is registered:

--where not_a_real_row               rc=1
--where ci_targets,not_a_real_row    rc=0   <- prints "not_a_real_row  NOT REGISTERED", exits 0
--where not_a_real_row,ci_targets    rc=0

The body's motivating case is a release verifier checking a five-gate list. With a comma list, one gate that is not registered prints a line and passes any script that keys on the exit code, which is the false negative this flag exists to prevent.

Fix, tested:

return 0 if all(row in found for row in wanted) else 1

With it: found + missing gives rc 1, all found gives rc 0, missing alone gives rc 1. --self-test still gives "0 failure(s)", and --check is unchanged (9, none new). I restored the file. Please add a self-test arm for the mixed list, since the three current --where arms each pass a single row and cannot see this.

@bong-water-water-bong
bong-water-water-bong merged commit 5216203 into main Nov 11, 2026
9 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the lane/ci-targets-where branch November 11, 2026 10:33
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

A defect in what I shipped here, found and fixed in #2164 (69c3d35):

$ tools/ci/ci_targets_check.py --where ci_targets,not_a_real_row
not_a_real_row  NOT REGISTERED (…)
$ echo $?  →  0        ← a list with an unregistered row PASSED

return 0 if found else 1 asked "did anything match?" when the flag exists to answer "does every row in my list resolve?". I wrote the exit code for the single-row case I tested and never re-examined it when the argument became a comma list — the #2012 shape (the instrument’s coverage did not follow its own change) and, plainly, a check that could pass while printing NOT REGISTERED.

#2164 replaces it with all(row in found for row in wanted) plus the wanted and guard, so an empty request cannot pass either, and adds the three self-test arms (one-missing-fails / all-present-passes / single-missing-fails) that this should have had from the start. Take #2164 before using --where in a release checklist.

bong-water-water-bong added a commit that referenced this pull request Nov 11, 2026
…nly when all are (Refs #2129) (#2164)

#2160 added `ci_targets_check.py --where a,b,c` for a release verifier checking its gate list, and returned
`0 if found else 1`: rc 1 only when NONE of the rows is registered. So
`--where ci_targets,not_a_real_row` printed `not_a_real_row NOT REGISTERED` and exited 0, and a script keyed on
the exit status passes a gate list with a missing gate -- the false negative the flag exists to prevent.

where_rc(hits, wanted): 0 only when every requested row was found. Three self-test arms on the exit status (one
missing in a list -> 1, all found -> 0, a single missing -> 1); the existing arms only covered where_rows(), which
is why the first version passed. With the shipped rule put back, the first new arm fails (rc 0).
`--check` is unchanged: 9 targets, baseline 9, none new.

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 Nov 11, 2026
Rebased onto main after #2164, which replaced --where's exit with `where_rc(hits,
wanted)` (0 only when EVERY requested row is registered). This keeps that rule
unchanged and adds only the hint:

  submodule_pin  NOT REGISTERED (searched CMakeLists.txt and every file it include()s or globs)
           but 2 row(s) START WITH it -- the row name may be what is wrong, not the
           registration: submodule_pin_check (cmake/tests-submodule-pin.cmake:26), ...

The verdict stays NOT REGISTERED and the exit stays where_rc's -- the hint never
claims a match, and it is not printed at all when nothing starts with the query.

Resolution of the two conflicts, as the reviewer prescribed and tested: both
self-test blocks are kept (this PR's two prefix-hint arms and #2164's three
exit-status arms), and main()'s --where branch keeps this PR's hint lines with
main's where_rc exit rather than this branch's older `return 0 if found else 1`
-- taking that side whole would have brought back the bug #2164 fixed.

Verified: --self-test 29 arms ok, 0 failures (both sets present); --where
submodule_pin rc 1 with the hint; --where ci_targets,submodule_pin rc 1 (the case
the old line passed); --where ci_targets,tmp_path_check rc 0; --check unchanged
(9 baselined, none new); copyright.py --check rc 0.

Refs #2160, #2164.
bong-water-water-bong added a commit that referenced this pull request Nov 11, 2026
…nd (#2169)

Raising this from the review of #2160, where I hit it: `--where` matches EXACTLY,
which is right -- a substring match would answer for `tmp_path_check_self` when
asked about `tmp_path_check`, and a partial match reported AS a registration is
exactly the false confidence the tool exists to remove. But the miss and the typo
read identically:

  $ ci_targets_check.py --where submodule_pin
  submodule_pin  NOT REGISTERED (searched CMakeLists.txt and every file it include()s or globs)

while the row is `submodule_pin_check`. The same three names in #2160's own body
(`submodule_pin`, `tmp_path`, `npu_serve_smoke`) all miss for this reason, and a
release verifier reading that line has no way to tell "absent" from "wrong string".

Now the miss names the near rows, from the same reader that follows the includes and
the glob:

  submodule_pin  NOT REGISTERED (searched CMakeLists.txt and every file it include()s or globs)
           but 2 row(s) START WITH it -- the row name may be what is wrong, not the
           registration: submodule_pin_check (cmake/tests-submodule-pin.cmake:26),
           submodule_pin_check_self (cmake/tests-submodule-pin.cmake:27)

and still says nothing extra when nothing starts with the query. The hint never
claims a match: the verdict stays NOT REGISTERED and the exit code stays 1.

Self-test: two arms added -- "a partial name is offered as a hint when the exact name
is absent" and "and no hint is invented when nothing starts with the query".

Verified on the real tree: --where submodule_pin / tmp_path / npu_serve_smoke now
name 2/2/1 near rows, --where tmp_path_check still answers
`cmake/tests-tmp-path.cmake:23`, --where totally_bogus_row is still bare, --check
unchanged (9 baselined, none new), copyright.py --check rc 0.

Refs #2160.

Co-authored-by: bong-water-water-bong <bong@water>
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