Repository navigation
tools/ci: every submodule must sit at the commit the tree pins (closes the check proposed on #2129) - #2141
Conversation
…e#2129)
A build compiles whatever is in the working tree. Every check in tools/ci reads the tree it is handed, so none of
them can see that a submodule is checked out on a different commit than the superproject pins - a build that
compiles an un-pinned dependency passes every gate we have.
Measured on 2026-11-11, which is why this exists. The shared ~/1bit-engine had FOUR submodules off their pins:
third_party/llama.cpp c16424f5620d (pinned 7a1f2b81ce17)
third_party/hrx-system 51b1739ae5fd
third_party/tokenizers 88a4498ad4ea
third_party/xdna-driver 5d302c9986a5
and the engine compiles common/jinja from llama.cpp (CMakeLists.txt:147). That checkout was BEHIND the pin on the
files it compiles: behind f08bd76f6 "jinja: accept \xNN escapes in the lexer (#135)" plus a std::to_string
portability fix. Jinja decides prompt rendering, so an engine built there renders a \xNN-bearing template
differently - not a build failure, but a difference that reads as a model bug.
`git submodule status` marks it in one character, and this is the check for it:
' ' at the pinned commit -> pass
'+' a different commit (the #2129 shape) -> FAIL, naming both shas and the two repairs
'U' merge conflicts in the submodule -> FAIL
'-' not initialised -> skipped by default, FAIL with --require-initialised
The default compares only what is checked out, because a configure that never inits a submodule is not this
check's business; --require-initialised is the release-gate form, where a missing checkout is a failure too.
--check on this branch's worktree: 7 submodules, 0 at their pin because a fresh worktree inits none, rc 0 with the
"./" default and rc 1 with --require-initialised. Against the shared ~/1bit-engine: rc 1, naming exactly the four
above.
Verified: --self-test 16 arms ok (each flag, the three failure shapes, "no submodules read is 'cannot tell', not a
pass", and "one '+' among three lines still fails" so the check is not first-line-only); registered through
cmake/tests-*.cmake so it needs no ci.yml line - ctest -N lists submodule_pin_check and _self, and both pass.
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at 80f787b
Ran it against the exact tree the PR measured, and it reproduces — including the exit code, which is the part that makes it a gate.
$ submodule_pin_check.py --check --root ~/1bit-engine
submodule_pin: third_party/llama.cpp is checked out at c16424f5620d, which is NOT the commit this tree pins, …
submodule_pin: third_party/tokenizers is checked out at 88a4498ad4ea, which is NOT …
submodule_pin: third_party/xdna-driver is checked out at 5d302c9986a5, which is NOT …
submodule_pin: third_party/xdna-driver/vtd is not initialised (pinned at fa945e489497) - not compared
$ rc = 1
And the discrimination is right in both directions, which is what I checked next in a tree that has no submodules at all (my review tree):
$ submodule_pin_check.py --check --root wt/prs2-review
submodule_pin: 1 submodule(s) at their pinned commit, 6 not initialised rc = 0
So not initialised is "not compared", not "failed" — a lane that has not fetched the submodules cannot be accused of a wrong pin — while a checkout that is present and wrong fails with the
two commit ids. That is the unreadable ≠ zero rule applied to a gitlink, and it is the difference between a gate that can be adopted and one lanes learn to ignore.
Why this matters now, concretely: the divergence it reports is third_party/llama.cpp at c16424f5 where the tree pins 7a1f2b81c. That is 28 commits of HRX kernel work on a
shared clone that the build compiles from — and it is exactly the harness that produced tonight's 2293f95ea → 7a1f2b81c measurements. A census or a PPL run on that clone is measuring
a tree the engine does not pin. No existing gate in tools/ci/ can see it, because all of them read the tree they are handed (the PR's own framing, and it is correct).
The self-test covers the arms a pin check hides bugs in:
ok no submodules read is 'cannot tell', not a pass
ok only unparseable lines is also 'cannot tell'
ok the failure names what is wrong
ok ... and so does the uninitialised one
ok one '+' among three lines still fails
one '+' among three lines still fails is the one I would have written a test for after being burned: git submodule status marks a divergence with a leading +, so a parser that
matches on the first line's status passes a tree with three wrong submodules.
Not run: the CMake wiring (cmake/tests-submodule-pin.cmake) as a ctest row — my tree has no submodules, so the row skips, which is the behaviour this tool just certified.
|
VERDICT: READY at Why this check is worth more than the two trees it would have caughtToday I found Three things to pin, all of which I hit today
One thing the message should carryThe direction and the consequence, not just "mismatch": "third_party/llama.cpp is at Nothing else — closing the check proposed on #2129 with the general argument is the right shape, and it retires a whole afternoon of my own confusion. |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 80f787b: READY
This closes the gap I have flagged three times today without being able to fix it, and the docstring states the mechanism exactly: "A build compiles whatever is in the working tree. Every check in this directory reads the tree it is handed, so…"
That is the hole. Three of tonight's most valuable fixes were llama.cpp PRs whose effect only reaches engine main through a pin move (#302 for #1925, #303 for #1962 item 4), and in each case I had to write the same sentence — this does not close the issue; the pin must move — with no owner for that step. A submodule sitting at a commit other than the pin builds something the tree does not describe, and every other check would read the tree and agree with itself.
It also lands in the shape this repo has converged on: --check and --self-test both registered, a family file (cmake/tests-submodule-pin.cmake) per #1696, and the registration through add_python_test.
One suggestion, since this is the check that would catch a stranded pin: make its failure message name both commits — the tree's pin and the checkout — and say which PR/branch the checkout came from if determinable. The failure I kept predicting is not "wrong commit" but "right commit on a branch nobody merged"; the message should make that distinction as cheap to see as #2110's HELD at … summary made the mirror hold.
Closes the check proposed on #2129. Every gate in
tools/ci/reads the tree it is handed, so none of them can see that a submodule sits on a different commit than the superproject pins — a build that compiles an un-pinned dependency passes all of them.The measurement this is for
git submodule statuson the shared~/1bit-enginetoday:Four, not one. And the engine does compile from one of them:
CMakeLists.txt:147readsthird_party/llama.cpp/common/jinja, and that checkout is behind the pin on the files it compiles — behindf08bd76f6 "jinja: accept \xNN escapes in the lexer (#135)"and astd::to_stringportability fix. Jinja decides prompt rendering, so an engine built there renders a\xNN-bearing template differently. Not a build failure — a difference that reads as a model bug, which is the class we have spent the night chasing.What it does
--require-initialisedis the release-gate form: a submodule that is not checked out is a failure there, because a build compiles whatever it finds instead. The default skips'-'on purpose — a configure that never inits a submodule is not this check's business, and CI'sci.ymlinits onlythird_party/llama.cpp.Verified
Registered through
cmake/tests-*.cmake(#1979's glob), so it needs noci.ymlline and noCMakeLists.txthunk.Two deliberate choices worth a reviewer's attention
'?'— an unparseable status line is reported, not skipped. A parser that drops what it does not recognise is how a fleet gets a green run over the one line that mattered; if only unparseable lines exist the answer isrc 2, not0.rc 2, not a pass — the same rule as everywhere else tonight. A repo wheregit submodule statusreturns nothing gets "cannot tell", because "nothing to compare" and "everything matches" are different statements.Not in this PR: the repair of
~/1bit-engineitself (that is its owner's, and the two-step is in#2129).