From 7cb2647b1269d89b4074753b4fcaf040993e22c6 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sat, 14 Nov 2026 13:40:29 +0100 Subject: [PATCH 1/2] ci: the foreign judge had never run in CI, and nothing could have said so MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⛔⛔ EVERY TEST THAT AGREES WITH qpdf HAS BEEN SKIPPING ON EVERY LANE. Measured on run 43595391186: the string "qpdf" appears ZERO times in the whole log. ⛔ AND THE TIME IS THE WITNESS, which is the only thing that could have been noticed: this job reports ok github.com/go-pdfkit/reader 0.197s (CI) ok github.com/go-pdfkit/reader 0.758s (a workstation with qpdf) The sweep generates 42 files by invoking qpdf; it cannot do that in 0.197s. So the agreement across 1, 2, 3, 5, 25 and 100 pages in five shapes, AND the negative control that an ordinary file is REFUSED by the foreign judge — the one #44 added yesterday — have never been checked by anything. ⛔ `go test` WITHOUT -v PRINTS NOTHING ABOUT A SKIP, so the log was not hiding this badly, it could not report it at all. And coverage stayed at 100.0% throughout: the same statements are reached by the tests that need no qpdf, so the coverage gate cannot see this and never could. Two gates were green about a third thing neither measures. ⛔ THIS REPOSITORY ALREADY HOLDS THE OPPOSITE STANDARD, one job down, about a missing qemu emulator: "A missing emulator FAILS this lane. A lane that reports success for an architecture it never ran is the one outcome this matrix exists to rule out." A missing foreign judge is that outcome for a different reason. go-pdfkit/ops already does it right for pdftoppm. ⛔ REQUIRE_QPDF, NOT CI, because not every lane CAN run it and a rule that demands the impossible gets deleted. Under qemu-user the test binary is emulated and an execve of the host's x86-64 qpdf is not something the emulator can carry out; the Windows lane has no apt. So ONE lane installs qpdf and sets REQUIRE_QPDF, and there a missing judge FAILS. The others skip, which is now a stated design instead of an accident nobody could see. Measured, both directions, rather than predicted: REQUIRE_QPDF=1, qpdf off PATH -> FAIL, naming the reason unset, qpdf off PATH -> skip, exit 0 ⛔ aptneed FOR THE INSTALL, because a mirror outage must not arrive as a missing judge: go-pdfkit/ops#90 was four red runs in half an hour on a third party's DNS. `--need qpdf` asks for the BINARY, since apt saying yes is not qpdf being on PATH. And `qpdf --version` is printed, so which judge agreed is answerable afterwards rather than assumed. ⚠ NOT FIXED, and deliberately: actionlint reports SC2086 on `go test $ex ./...` in the qemu job. It is pre-existing — main carries it at line 141 — and shellcheck is WRONG here. `ex` is empty on the 386 lane, which runs natively on purpose, so quoting it would hand `go test` an empty package pattern and break the one lane that must not be emulated. Reader's CI never ran actionlint, which is why it went unnoticed; silencing it properly is its own change. gofmt clean, go vet clean, 0 uncovered blocks of 1806. Refs #40. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ linearized_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07f37fb..5f1cf49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,10 +26,43 @@ jobs: check-latest: true cache: true + # ⛔⛔ THE FOREIGN JUDGE HAD NEVER RUN ON ANY LANE. Measured on run + # 43595391186: "qpdf" appears ZERO times in the whole log, and this job + # reported `ok 0.197s` where the same suite takes 0.758s on a machine + # that has qpdf. Every test that agrees with qpdf — the sweep over 42 + # generated files, and the control that an ordinary file is REFUSED — + # skipped, and `go test` without -v says nothing about a skip, so + # nothing in the log could have told anyone. Coverage stayed at 100% + # the whole time: the same statements are reached by the tests that + # need no qpdf, so that gate cannot see this and never could. + # + # ⛔ This repository already holds the opposite standard one job down, + # about a missing qemu emulator: "A lane that reports success for an + # architecture it never ran is the one outcome this matrix exists to + # rule out." + # + # aptneed because a mirror outage must not read as a missing judge: + # go-pdfkit/ops#90 was four red runs in half an hour on a third + # party's DNS. --need is the binary, not the package: apt saying yes is + # not qpdf being on PATH. + - name: Install qpdf, the foreign judge the linearization tests print by + run: | + GOBIN=/usr/local/bin go install github.com/go-fleettools/fleettools/cmd/aptneed@latest + sudo aptneed --need qpdf qpdf + + # Printed on purpose: the version is in the log, so "which qpdf agreed" + # is answerable after the fact rather than assumed. + - name: Say which qpdf will judge + run: qpdf --version + - name: go vet run: go vet ./... + # REQUIRE_QPDF makes a missing judge FAIL here instead of skipping. It + # is set on this lane only, because this is the lane that installs it. - name: Test with coverage + env: + REQUIRE_QPDF: "1" run: go test -covermode=set -coverprofile=coverage.out ./... - name: Assert exact 100% statement coverage diff --git a/linearized_test.go b/linearized_test.go index 960856e..c1e37c9 100644 --- a/linearized_test.go +++ b/linearized_test.go @@ -938,10 +938,43 @@ func TestCheckLinearizedAgreesWithQpdfAcrossPageCountsAndShapes(t *testing.T) { } } +// qpdfOrSkip finds the foreign judge, and REFUSES TO SKIP IT UNDER CI. +// +// ⛔⛔ IT SKIPPED EVERYWHERE, AND NOWHERE MORE QUIETLY THAN IN CI. Measured on +// run 43595391186: the word "qpdf" appears ZERO times in the whole log, and +// the suite reports `ok 0.197s` where the same suite takes 0.758s on a +// workstation that has qpdf. Every test that agrees with qpdf — the sweep +// across 42 generated files, and the negative control that an ordinary file +// is refused — has never run on any lane, and `go test` without -v prints +// nothing about a skip, so the log could not say so either. Coverage stayed +// at 100% throughout, because the same statements are reached by the tests +// that need no qpdf: that gate cannot see this and never could. +// +// ⛔ THIS REPOSITORY ALREADY HOLDS THE OPPOSITE STANDARD, one job away. Of a +// missing qemu emulator its own workflow says: "A missing emulator FAILS this +// lane. A lane that reports success for an architecture it never ran is the +// one outcome this matrix exists to rule out." A missing foreign judge is the +// same outcome for a different reason, and go-pdfkit/ops already does this +// for pdftoppm — its render tests t.Fatal when CI is set. +// +// ⛔ IT KEYS ON REQUIRE_QPDF AND NOT ON CI, because not every lane CAN run it +// and a rule that demands the impossible gets deleted. Under qemu-user the +// test binary is emulated and an `execve` of the host's x86-64 qpdf is not +// something the emulator can carry out; the Windows lane has no apt. So ONE +// lane installs qpdf and sets REQUIRE_QPDF, and on that lane a missing judge +// is a failure. The others skip, which is now a stated design rather than an +// accident nobody could see. +// +// Off CI it skips too: a contributor without qpdf should be able to run the +// suite, and that skip is visible to them because they chose it. func qpdfOrSkip(t *testing.T) string { t.Helper() path, err := exec.LookPath("qpdf") if err != nil { + if os.Getenv("REQUIRE_QPDF") != "" { + t.Fatal("REQUIRE_QPDF is set and qpdf is not installed: the foreign judge is the whole " + + "point of this test, and a lane that skips it reports success for an agreement it never checked") + } t.Skip("qpdf is not installed, so there is no independent linearizer to agree with") } return path From b25130e1aa08795b359573d00de1cce16c9dbbb7 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sat, 14 Nov 2026 21:07:37 +0100 Subject: [PATCH 2/2] ci: Go 1.27.4, because installing a fleet tool requires it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⛔⛔ SECOND OCCURRENCE IN TWO DAYS, AND I PREDICTED THIS ONE IN WRITING. The first push of the qpdf lane failed on go: …/cmd/aptneed@latest: fleettools@v0.112.0 requires go >= 1.27.4 (running go 1.27.3; GOTOOLCHAIN=local) go-pdfkit/ops hit the identical line on 2026-11-13, and after fixing it I wrote on that pull request: "Worth knowing for other repositories in the fleet that pin a toolchain and install fleettools commands in CI: the same pin blocks them the moment they try." Then I walked into it myself, one repository later, the next day. A note is applied when somebody remembers to apply it, and at the moment of adding an install step one is thinking about the install. So it is recorded as a recurrence rather than a third note. The gate that catches it is a check over `.github/workflows` for a `/cmd/@latest` install beside a `go-version` pin below that module's `go` directive — already filed as pending on go-fleettools/fleettools#103, where the `@latest` half of the same problem lives. `tagscan` cannot see either: it reads repositories, not workflows. All three jobs move to 1.27.4, not only the one that installs aptneed. The other two do not need it, but a matrix measuring on two different toolchains is a difference nobody chose, and this fleet has already been bitten by `stable` quietly selecting a minor. ⛔ GOTOOLCHAIN IS STILL LEFT ALONE. That is what made this a clean failure naming both versions instead of a runner silently fetching 1.27.4 and measuring a chain nobody picked. go.mod stays at `go 1.27.1`: that is the language version this module needs, not the toolchain a runner must have, and nothing here requires it to move. actionlint reports only the pre-existing SC2086 discussed in the previous commit. Refs #40, #44. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f1cf49..8ff0801 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,18 @@ jobs: - uses: actions/setup-go@v7 with: - go-version: '1.27.3' + # ⛔ 1.27.4 BECAUSE INSTALLING A FLEET TOOL REQUIRES IT. fleettools + # declares `go >= 1.27.4`, so 1.27.3 could not install aptneed at + # all: "requires go >= 1.27.4 (running go 1.27.3; + # GOTOOLCHAIN=local)". GOTOOLCHAIN is left alone on purpose — a + # runner that silently fetched a newer toolchain would measure one + # nobody chose — so the pin is what has to move. + # + # ⛔ SECOND OCCURRENCE IN TWO DAYS: go-pdfkit/ops hit the identical + # line on 2026-11-13. Any repository in this fleet pinning below + # 1.27.4 fails the moment it installs a fleettools command, and the + # failure names both versions, so it is at least legible. + go-version: '1.27.4' check-latest: true cache: true @@ -127,7 +138,7 @@ jobs: - uses: actions/checkout@v7 - uses: actions/setup-go@v7 with: - go-version: '1.27.3' + go-version: '1.27.4' # ⛔ `qemu-user-static` is a VIRTUAL package on Ubuntu 26.04, which # `ubuntu-latest` became on 2026-10-19, and apt refuses to choose between its # two providers: @@ -190,5 +201,5 @@ jobs: - uses: actions/checkout@v7 - uses: actions/setup-go@v7 with: - go-version: '1.27.3' + go-version: '1.27.4' - run: go test ./...