Skip to content

ci: MORPH_BUILD_BANK_GUI is enabled by no job, and nothing checks that a declared option is built at all #605

Description

@Yaraslaut

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:

  1. Add the missing options to the all optional features job, so the name becomes true.
  2. 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.
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ciSubsystem: cibugSomething isn't workingtriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions