Re-scoped 2026-09-20 from the original report (reproduced verbatim at the
bottom, unedited). The finding is real; its central figure was not, and the
correct figure changes which fix is proportionate. The full triage that
established this is in this comment.
No triage: label is applied — that verdict is the output of an assessment of
this description, which has not been run yet.
The finding
MORPH_BUILD_BANK_GUI is enabled by no CI job, so bank_gui_qml_tests is
built by nothing. #604 is the demonstration that this is not theoretical: that
target went unbuildable on master with every gate green.
Reproduced on master @ a020e69c:
$ grep -c "MORPH_BUILD_BANK_GUI" .github/workflows/ci.yml
0
$ grep -rn "linux-everything" .github/workflows/
(no output)
MORPH_BUILD_BANK_GUI defaults OFF (CMakeLists.txt:28). The
linux-everything configure preset sets it ON — which is how #604 was found
locally — but no workflow runs that preset.
Scope, corrected: it is 1 of 14, not 5 of 14
The original report states that the Linux / all optional features job passes
only five options and omits BANK_GUI, FORMS_QML, FUZZERS,
HMAC_EXAMPLES and LOAD_TESTS. That is the clang-coverage job's configure
block, not this one's — the two are ~1,150 lines apart in ci.yml, and the
coverage job does pass exactly NET/OFFLINE_SQLITE/QT/LADDER, which is
why the names lined up so convincingly.
The all optional features job's actual configure step, at fbb90181 — the
revision the original report itself cites, so this is not drift since filing:
$ git show fbb90181:.github/workflows/ci.yml | sed -n '1747,1790p'
- name: Configure (every optional feature ON)
cmake --preset ${{ matrix.preset }} \
-DMORPH_BUILD_NET=ON \
-DMORPH_BUILD_QT=ON \
-DMORPH_BUILD_FORMS_QML=ON \
-DMORPH_BUILD_LADDER=ON \
-DMORPH_LADDER_RUNGS=all \
-DMORPH_BUILD_OFFLINE_SQLITE=ON \
-DMORPH_BUILD_LOAD_TESTS=ON \
-DMORPH_BUILD_HMAC_EXAMPLES=ON \
-DMORPH_BUILD_FUZZERS=${{ matrix.fuzzers }} \
Byte-identical on today's master. Four of the five named as uncovered are
passed by that job, and have been since d2cb3ae1. BANK_EXAMPLE is the
mirror-image error: the report credits the job with passing it and it does not —
it is built by two other jobs (ci.yml:1233, :2202).
Accounting all 14 declared options against a020e69c:
|
|
Covered by all optional features |
NET, QT, FORMS_QML, LADDER, OFFLINE_SQLITE, LOAD_TESTS, HMAC_EXAMPLES, FUZZERS (8) |
| Preset defaults, ON everywhere |
EXAMPLES, TESTS (2) |
| Have their own dedicated job |
CLANG_TIDY, DOCUMENTATION (2) |
| Covered by other jobs |
BANK_EXAMPLE (1) |
| Built by nothing |
BANK_GUI (1) |
So the job's name is very nearly true rather than broadly false, and the
original report's option 2 — rename the job and stand up a separate periodic
linux-everything build — is a large response to a one-option hole. At this
repository's 26–74 minute cycles a new periodic full build is not cheap to add.
Acceptance — one lane, two commits
(a) Close the hole. Add -DMORPH_BUILD_BANK_GUI=ON (and
-DMORPH_BUILD_BANK_EXAMPLE=ON, which it requires) to the all optional features configure step. That job already has Qt 6.8.1 from aqtinstall and
MORPH_BUILD_FORMS_QML=ON, so it is the one leg that can host it. #604 is
fixed (91515ae5), so this should configure and build green — if it does
not, that is a new finding and gets its own issue; do not fold the fix in.
(b) Make the job's name self-verifying. This is the part that lasts.
AGENTS.md's test is "ask whether the check would still pass if the feature did
nothing", and for (a) alone the answer is yes: nothing stops a fifteenth option
being declared next month and enabled by nobody, and this issue being filed a
third time. Add a check that derives the list rather than asserting it:
enumerate `option(MORPH_BUILD_*` from CMakeLists.txt
→ assert each is either enabled by some job in .github/workflows/,
or present in an explicit, reasoned exemption set
CLANG_TIDY and DOCUMENTATION go in the exemption set with their reason
("has its own dedicated job"). Requirements on the check:
- Prove it is not vacuous: add a declared
MORPH_BUILD_* option that no
job enables, run the check, and paste the real failure output. A gate that
reports success while measuring nothing is the failure this issue is about,
and shipping one here would be the worst available outcome.
- It must be wired into a workflow that actually runs, and the PR must name
which job invokes it.
- Its failure message should say how to cover the option, not how to exempt
it.
scripts/check_branch_coverage.py's resolve_allowlist_source_line() is the
in-tree precedent for the discipline (a gate that fails when its own input data
rots), though not for the mechanism — that resolver matches a source text
against a file, whereas this compares two sets. See #614's PR discussion,
where that reuse question was examined and answered.
Contention
Part (b) adds a workflow step. PR #614 currently edits
.github/workflows/drift-guard.yml — check whether it has landed, and if not,
either wait or place the new step in ci.yml. Part (a) touches ci.yml, which
nothing else holds.
What is still not verified
What would change the verdict
Re-open at the original report's wider scope — rename plus a periodic full
build — if the "built by nothing" set is ever more than one or two options wide
again. Close when BANK_GUI is built by a job and something fails when a
newly declared MORPH_BUILD_* option is enabled by nothing.
Related: #604 (the one broken target, fixed), #608/#614 and #613 (same defect
class — a hand-maintained record with nothing auditing it — different fix
sites; deliberately not consolidated).
Original report, verbatim and unedited (filed 2026-09-20, against fbb90181)
Found while triaging #604 (bank_gui_qml_tests does not compile on master). Filed separately per AGENTS.md: #604 is one broken target, this is the reason no gate noticed — different fix, and fixing either leaves the other.
The finding
No CI job builds bank_gui_qml_tests, and the job whose name implies it would does not enable the option that creates it.
$ grep -n "MORPH_BUILD_BANK_GUI" .github/workflows/ci.yml
(no output)
$ grep -n "linux-everything" .github/workflows/ci.yml
(no output)
bank_gui_qml_tests exists only under MORPH_BUILD_BANK_GUI, which defaults OFF:
CMakeLists.txt:28:option(MORPH_BUILD_BANK_GUI "Build the Qt 6 GUI for the bank example" OFF)
The linux-everything configure preset does set it (MORPH_BUILD_BANK_GUI = ON), which is how #604 was found locally — but CI never runs that preset.
The naming problem, measured
The job a reader would expect to cover this is Linux / all optional features (clang and gcc). It does not enable all optional features. The repository declares 14 MORPH_BUILD_* options:
MORPH_BUILD_BANK_EXAMPLE = OFF MORPH_BUILD_HMAC_EXAMPLES = OFF
MORPH_BUILD_BANK_GUI = OFF MORPH_BUILD_LADDER = OFF
MORPH_BUILD_CLANG_TIDY = OFF MORPH_BUILD_LOAD_TESTS = OFF
MORPH_BUILD_DOCUMENTATION = OFF MORPH_BUILD_NET = OFF
MORPH_BUILD_EXAMPLES = ON MORPH_BUILD_OFFLINE_SQLITE = OFF
MORPH_BUILD_FORMS_QML = OFF MORPH_BUILD_QT = OFF
MORPH_BUILD_FUZZERS = OFF MORPH_BUILD_TESTS = ON
That job's configure line passes five: NET, OFFLINE_SQLITE, QT, LADDER, and (per its own comment) BANK_EXAMPLE. Not passed: BANK_GUI, FORMS_QML, FUZZERS, HMAC_EXAMPLES, LOAD_TESTS. CLANG_TIDY and DOCUMENTATION have their own jobs and are not the concern here.
So five feature options are built by no job, under a job name asserting the opposite. #604 is the demonstration that this is not theoretical: a target went unbuildable on master with every gate green.
Why this is the repository's recurring shape
AGENTS.md opens its "Verify rather than assert" section with exactly this failure — "a control that reports success while measuring nothing" — and lists prior instances: an invalid coverage config, a sanitizer job with no instrumentation, a conformance suite that drove nothing. This is another: a job whose name claims a coverage set it does not have.
Its own test: "Ask whether the check would still pass if the feature did nothing." Here the stronger version applies — the check passes when the feature does not compile.
Verification status
Reproduced by inspection, on master fbb90181. Every claim above is a grep or a file read, quoted verbatim, and is deterministic. I did not run CI, and I did not build any of the five options to see whether the other four are also broken — that is the open question and I am not asserting an answer. #604 establishes that one of the five is broken; the others are untested, which is the point.
I also did not check whether some non-ci.yml workflow builds them. I grepped .github/workflows/ci.yml only.
What would resolve it
Options, not a prescription — which one is right is a cost decision about CI minutes:
- Add the missing options to the
all optional features job, so the name becomes true.
- Keep the job as-is and rename it to what it actually covers, then add a separate periodic (nightly/weekly)
linux-everything build for the full set. A build break found within a day is worth far more than one found by a contributor months later.
- Gate on the preset directly, so
CMakePresets.json and CI cannot drift apart.
Whichever is chosen, the load-bearing requirement is that something builds every declared option, and that no job's name promises coverage it does not deliver.
What would change the verdict
Close this if these five options are deliberately unsupported and not expected to build — but then they should not be declared options, and linux-everything should not claim to enable them.
The finding
MORPH_BUILD_BANK_GUIis enabled by no CI job, sobank_gui_qml_testsisbuilt by nothing. #604 is the demonstration that this is not theoretical: that
target went unbuildable on
masterwith every gate green.Reproduced on
master@a020e69c:MORPH_BUILD_BANK_GUIdefaultsOFF(CMakeLists.txt:28). Thelinux-everythingconfigure preset sets itON— which is how #604 was foundlocally — but no workflow runs that preset.
Scope, corrected: it is 1 of 14, not 5 of 14
The original report states that the
Linux / all optional featuresjob passesonly five options and omits
BANK_GUI,FORMS_QML,FUZZERS,HMAC_EXAMPLESandLOAD_TESTS. That is theclang-coveragejob's configureblock, not this one's — the two are ~1,150 lines apart in
ci.yml, and thecoverage job does pass exactly
NET/OFFLINE_SQLITE/QT/LADDER, which iswhy the names lined up so convincingly.
The
all optional featuresjob's actual configure step, atfbb90181— therevision the original report itself cites, so this is not drift since filing:
Byte-identical on today's
master. Four of the five named as uncovered arepassed by that job, and have been since
d2cb3ae1.BANK_EXAMPLEis themirror-image error: the report credits the job with passing it and it does not —
it is built by two other jobs (
ci.yml:1233,:2202).Accounting all 14 declared options against
a020e69c:all optional featuresNET,QT,FORMS_QML,LADDER,OFFLINE_SQLITE,LOAD_TESTS,HMAC_EXAMPLES,FUZZERS(8)EXAMPLES,TESTS(2)CLANG_TIDY,DOCUMENTATION(2)BANK_EXAMPLE(1)BANK_GUI(1)So the job's name is very nearly true rather than broadly false, and the
original report's option 2 — rename the job and stand up a separate periodic
linux-everythingbuild — is a large response to a one-option hole. At thisrepository's 26–74 minute cycles a new periodic full build is not cheap to add.
Acceptance — one lane, two commits
(a) Close the hole. Add
-DMORPH_BUILD_BANK_GUI=ON(and-DMORPH_BUILD_BANK_EXAMPLE=ON, which it requires) to theall optional featuresconfigure step. That job already has Qt 6.8.1 from aqtinstall andMORPH_BUILD_FORMS_QML=ON, so it is the one leg that can host it. #604 isfixed (
91515ae5), so this should configure and build green — if it doesnot, that is a new finding and gets its own issue; do not fold the fix in.
(b) Make the job's name self-verifying. This is the part that lasts.
AGENTS.md's test is "ask whether the check would still pass if the feature did
nothing", and for (a) alone the answer is yes: nothing stops a fifteenth option
being declared next month and enabled by nobody, and this issue being filed a
third time. Add a check that derives the list rather than asserting it:
CLANG_TIDYandDOCUMENTATIONgo in the exemption set with their reason("has its own dedicated job"). Requirements on the check:
MORPH_BUILD_*option that nojob enables, run the check, and paste the real failure output. A gate that
reports success while measuring nothing is the failure this issue is about,
and shipping one here would be the worst available outcome.
which job invokes it.
it.
scripts/check_branch_coverage.py'sresolve_allowlist_source_line()is thein-tree precedent for the discipline (a gate that fails when its own input data
rots), though not for the mechanism — that resolver matches a source text
against a file, whereas this compares two sets. See #614's PR discussion,
where that reuse question was examined and answered.
Contention
Part (b) adds a workflow step. PR #614 currently edits
.github/workflows/drift-guard.yml— check whether it has landed, and if not,either wait or place the new step in
ci.yml. Part (a) touchesci.yml, whichnothing else holds.
What is still not verified
MORPH_BUILD_BANK_GUI=ONsince build: bank_gui_qml_tests does not compile -- testkit_main.cpp's <testkit/log_level.hpp> is not on its include path #604 was fixed, sowhether
bank_gui_qml_testscompiles ona020e69cis an open question —answering it is step (a). The original report's worry that the other four
options might also be broken is narrowed but not eliminated: four of those
five are built by CI on every push, so they compile;
BANK_GUIis the onegenuinely untested.
CMakePresets.json'slinux-everythingpreset still configurescleanly was not checked.
What would change the verdict
Re-open at the original report's wider scope — rename plus a periodic full
build — if the "built by nothing" set is ever more than one or two options wide
again. Close when
BANK_GUIis built by a job and something fails when anewly declared
MORPH_BUILD_*option is enabled by nothing.Related: #604 (the one broken target, fixed), #608/#614 and #613 (same defect
class — a hand-maintained record with nothing auditing it — different fix
sites; deliberately not consolidated).
Original report, verbatim and unedited (filed 2026-09-20, against
fbb90181)Found while triaging #604 (
bank_gui_qml_testsdoes not compile on master). Filed separately per AGENTS.md: #604 is one broken target, this is the reason no gate noticed — different fix, and fixing either leaves the other.The finding
No CI job builds
bank_gui_qml_tests, and the job whose name implies it would does not enable the option that creates it.bank_gui_qml_testsexists only underMORPH_BUILD_BANK_GUI, which defaultsOFF:The
linux-everythingconfigure preset does set it (MORPH_BUILD_BANK_GUI = ON), which is how #604 was found locally — but CI never runs that preset.The naming problem, measured
The job a reader would expect to cover this is
Linux / all optional features(clang and gcc). It does not enable all optional features. The repository declares 14MORPH_BUILD_*options:That job's configure line passes five:
NET,OFFLINE_SQLITE,QT,LADDER, and (per its own comment)BANK_EXAMPLE. Not passed:BANK_GUI,FORMS_QML,FUZZERS,HMAC_EXAMPLES,LOAD_TESTS.CLANG_TIDYandDOCUMENTATIONhave their own jobs and are not the concern here.So five feature options are built by no job, under a job name asserting the opposite. #604 is the demonstration that this is not theoretical: a target went unbuildable on master with every gate green.
Why this is the repository's recurring shape
AGENTS.md opens its "Verify rather than assert" section with exactly this failure — "a control that reports success while measuring nothing" — and lists prior instances: an invalid coverage config, a sanitizer job with no instrumentation, a conformance suite that drove nothing. This is another: a job whose name claims a coverage set it does not have.
Its own test: "Ask whether the check would still pass if the feature did nothing." Here the stronger version applies — the check passes when the feature does not compile.
Verification status
Reproduced by inspection, on master
fbb90181. Every claim above is a grep or a file read, quoted verbatim, and is deterministic. I did not run CI, and I did not build any of the five options to see whether the other four are also broken — that is the open question and I am not asserting an answer. #604 establishes that one of the five is broken; the others are untested, which is the point.I also did not check whether some non-
ci.ymlworkflow builds them. I grepped.github/workflows/ci.ymlonly.What would resolve it
Options, not a prescription — which one is right is a cost decision about CI minutes:
all optional featuresjob, so the name becomes true.linux-everythingbuild for the full set. A build break found within a day is worth far more than one found by a contributor months later.CMakePresets.jsonand CI cannot drift apart.Whichever is chosen, the load-bearing requirement is that something builds every declared option, and that no job's name promises coverage it does not deliver.
What would change the verdict
Close this if these five options are deliberately unsupported and not expected to build — but then they should not be declared options, and
linux-everythingshould not claim to enable them.