Repository navigation
Description
The coverage gate rounds, and it does its judging in awk.
.github/workflows/ci.yml asserts exact 100% statement coverage like this:
go tool cover -func=coverage.out | tail -n +1 > /tmp/cov.txt
below=$(go tool cover -func=coverage.out | awk '$3 != "100.0%"')Two separate defects, found while replaying this gate locally to check #48 rather than approximating it.
⛔⛔ -func prints one decimal, so the gate cannot see the last 0.05%
go tool cover -func formats each percentage to one decimal place. A function at 99.96% prints 100.0%, matches $3 != "100.0%" as equal, and passes. The gate therefore asserts "every function rounds to 100.0%", which is not what its own step name claims ("Assert exact 100% statement coverage") and not what this repo wants.
This is a gate that can pass for the wrong reason, which is the failure mode the gate exists to prevent. It has not yet done so here — covblocks reports 0 uncovered blocks and 0 uncovered statements of 1815 and 2484 on the current head — so this is a hole, not an outage. A hole in a gate is worth closing while it is still cheap to prove it was never exercised.
covblocks counts blocks and statements exactly and takes a --max:
covblocks --max 0 coverage.out
1815 block(s), 2484 statement(s); 0 block(s) uncovered, 0 statement(s) uncovered
It exits non-zero above the maximum, so the verdict is the tool's exit status rather than a string comparison on rounded output.
⛔ And the verdict is computed in awk, inside a repository whose rule forbids exactly that
awk '$3 != "100.0%"' is the judging step: a filter deciding pass or fail. A pipeline cannot distinguish "no answer" from "zero failures" — if go tool cover fails, writes nothing, or changes its column order, below is empty and the gate reports success. covblocks fails loudly instead, because an unreadable profile is an error and not an empty set.
The first line is dead too: | tail -n +1 > /tmp/cov.txt copies the output unchanged and nothing reads the file afterwards.
Proposed change
Replace both lines with one covblocks --max 0 coverage.out step, after installing covblocks the way the qpdf judge is installed in the same workflow. The step name then describes what the step does.
⛔ Not folded into #48: that PR is a linearization refusal and this is CI. Separating them keeps the question "did the gate change, or did the code?" answerable from the history.
🤖 Generated with Claude Code
Prior art: priorart "coverage gate rounds awk cover -func" at 2026-11-15 10:42 CET reported prior art; checked: priorart's 28 hits match on the words 'gate' and 'coverage' only: #45 installed the qpdf judge and added REQUIRE_QPDF but never touched the coverage step; #27 added the macOS and Windows lanes; #29 is a go-version bump; #40, #42, #43, #44, #48 are linearization work whose 'gate' is a refusal in the checker, not a CI step. Searches for 'covblocks' and 'awk' over the tracker return zero. Nothing covers the -func rounding hole or the awk verdict.
Activity