Skip to content

ci: fuzz weekly, measure the tree's branch and condition coverage, and date the linter count - #1558

Merged
jmrplens merged 12 commits into
bp-reproducible-buildsfrom
bp-analysis
Oct 28, 2026
Merged

jmrplens merged 12 commits into
bp-reproducible-buildsfrom
bp-analysis

Conversation

@jmrplens

@jmrplens jmrplens commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Closes #1542
Refs #1269

Three answers of the OpenSSF Best Practices form rested on evidence weaker than they claimed. dynamic_analysis had the race detector and the e2e suite but no tool that varies its inputs: the module carries eleven fuzz targets and go test only ever ran their seed corpora. test_branch_coverage80 asks for the share of the code's branches the suite covers, measured with a FLOSS tool, and the repository had gobco figures for three packages, none for the tree, and none in the mode that counts branches. warnings cites the linters the configuration enables, and .golangci.yml and the static analysis page said 55 where it enables 52. This pull request gives each of the three the evidence it lacked; whether each answer becomes Met is the maintainer's call, under Before this merges.

.github/workflows/fuzz.yml fuzzes every target with go test -run '^$' -fuzz '^<target>$', one job per target: for ten minutes on Wednesdays at 05:43 UTC, a day no other weekly workflow takes, and when dispatched, and for one minute on a pull request that changes the workflow or its plan. GitHub offers a workflow for dispatch only once its file is on the default branch, so this pull request can run fuzz.yml through that trigger alone; a later change could be dispatched from its branch, and the trigger runs it without anybody having to. No other pull request runs it, and it stays out of ci.yml, so it gates nothing. When a target fails, go test writes the input under the package's testdata/fuzz/<target>/, and the job uploads that directory as fuzz-<package>-<target>. The static analysis page gains a Fuzzing section beside the race detector, with how to reproduce such an input and keep it as a regression seed.

The targets are not written into the workflow. cmd/fuzz_targets lists every package with the toolchain's own go, parses the internal and external test files the go command builds for each, and prints a matrix cell per top-level function the go command reads as a fuzz target (Fuzz, or Fuzz followed by anything but a lower-case letter, so Fuzzy is not one, and a method is not either). A target added anywhere is fuzzed from the next run on, and a plan that finds none fails rather than passing with nothing fuzzed. Its tests hold the workflow to the matrix: the triggers, the read-only token, the anchored -fuzz, the fuzz time per trigger and the path the upload takes.

cmd/measure_conditions takes the whole tree. plan -tree lists every package of ./cmd/... and ./internal/... and deals them round twenty shards, the first package to the first shard, the second to the second and so on round again, so the slow packages of one directory land in different jobs; each shard is a job on each runner. The shell that wrote one record per job is now the measure subcommand, which runs scripts/coverage-conditions.sh over each package of its job one after another, streams the output to the log and leaves each package's gobco.txt and run.env in a directory of its own, so the writer of a record sits beside its reader and their tests hold each to the other. summarize adds each runner's total over the packages it measured, with the share rounded down to a tenth of a percent so it never reads higher than it is (799 of 1000 is 79.9%); a package that was not measured is in no total, and the summary quotes the lines of its output that say why: the script's own, and the panic, the failing test, the test package that did not build or set up, or the compiler position gobco's run printed before them. tree runs a whole -tree dispatch on the machine at hand and writes the same summary, and make coverage-conditions-tree runs it, so the figures can be taken again without a dispatch. conditions.yml gains the tree input, names each job after its package or shard, and gives a shard's job two hours while a job of a named package keeps the hour it had.

gobco counts in one of two modes, and every figure here had come from its default, which counts every atomic condition: each operand of && and || and each comparison wherever it is written, a return statement as much as an if. That is not what the criterion names. gobco 1.3.4 also has -branch, which counts the whole controlling condition of each if and for and each case of a switch, and nothing else. scripts/coverage-conditions.sh now reads GOBCO_MODE, condition by default or branch, refuses any other value before gobco runs, and refuses a report without the figure of its mode as it refuses a 0/0. measure_conditions takes -mode on plan, summarize and tree, reads each record's figure in the words of its mode and heads the summary with it; conditions.yml gains the mode input and passes it to the plan, the measuring step and the summary, and make coverage-conditions-tree takes CONDITIONS_MODE.

I took both figures with that command on 26 October 2026, on linux/amd64, on this branch once its Go code was final, when it sat on main at ed18d7d12, four packages at a time on ten cores of a machine other work was loading too. The stack has since been rebased onto a later main, which brought packages of its own, so the page says which tree was measured and that those packages are in neither total; the only Go change on the branch since is the fuzz target name below. Branch coverage: 265 of the 296 packages below cmd/ and internal/ measured, 44936 of their 44954 outcomes taken, 99.9% rounded down, in an hour and twelve minutes. Condition coverage: the same 265, 53412 of 53432 outcomes evaluated, 99.9%, in an hour and twenty-four minutes. docs/development/testing/README.md records both with the date and both ways to take them again, and says what each mode counts and why the branch figure can leave out no more outcomes than the condition figure over the same packages: Go stops evaluating && and || once the outcome is known, so a decision whose every condition went both ways has gone both ways itself.

The eighteen branch outcomes left are eighteen decisions taken one way in nine packages. Thirteen are the error branch of a call no test makes fail (three on a random read, two on a request build, a stat, a write, a schema build, a catalog build, a marshal, an encode, an unmarshal and a marshal back), two are per-action metadata decorations of the kind make check-spec-conditions exists to find, and three are cases no test reaches: a decoding with no candidate to try, a URL elicitation in a session negotiated at 2026-07-28 or later, and an input schema without its value property. The twenty condition outcomes left are the same places, the elicitation one at its second operand, and two operands in cmd/audit_edition_tier that no decision holds whole.

That run could not measure 31 packages, and none of them fails under go test: twenty-seven hand symbols from an export_test.go to their external test package, which gobco type-checks against the non-test files alone (rillig/gobco#42); three have an external test package that does not import the package, for which gobco writes its bridge with an empty import path (rillig/gobco#33); and two have tests that read, in gobco's copy, what gobco rewrote there: internal/completions the case labels of its switches, and internal/testutil/serialtypecheck the imports of every file of its package, gobco's own among them. gobco v1.3.4 is still its latest release and fixes neither, so scripts/coverage-conditions.sh now stages both shapes, and the two tests read the same thing in the copy as in the source (After the first CI round, below). Measured that way on 27 October, on the rebased branch, the 31 take 3773 of their 3778 branch outcomes and evaluate 4113 of their 4120 condition outcomes, so all 296 packages are measured, 48709 of 48732 branch outcomes and 57525 of 57552 condition outcomes over both runs, 99.9% each, from two trees a day apart; the page says so. The counts beside it are corrected too: twenty-two of the 149 sub-packages of internal/tools that carry an action_specs.go were in the export_test.go state, where the page and scripts/check-spec-conditions.sh said twenty-five of 169.

.golangci.yml and the static analysis page now say what the sentence behind the concurrency cap measured and when: July 2026, when the module had 226 packages and the configuration enabled 52 linters and 3 formatters, which the 55 had counted together. Today it enables the same 52 and 3 (golangci-lint linters on v2.15.0), and git grep -n "55 linters" finds nothing.

How I checked it

  • cmd/fuzz_targets: its eleven tests failed against stubs before the command existed, and pass with 100.0% of statements; GOBCO_GATE=all bash scripts/coverage-conditions.sh ./cmd/fuzz_targets reads 40/40, and make coverage-mutants PKG=./cmd/fuzz_targets killed 24 mutants with none lived, uncovered or timed out. go run ./cmd/fuzz_targets on this tree plans the eleven targets in five packages.
  • cmd/measure_conditions, the tree subcommands: their tests were written first, but what I saw fail before the code existed was the build (undefined: treePackages, runMeasure, measurePackage, runScript and the rest), not an assertion, so the proof that those tests decide the behavior is the mutation run below rather than a red run.
  • cmd/measure_conditions, the reasons a summary gives, the job bound and the mode: those tests were written first and run against the code before them, with stubs for the new signatures where the mode needed them, and failed on their assertions (every new case of the mode, the four new shapes of a reason, and the measure job's bound read as 120 where the test wants the expression). The package passes with 100.0% of statements; gobco reads 270/270 in condition mode and 226/226 in branch mode; gremlins killed 166 mutants with none lived, uncovered or timed out.
  • scripts/coverage-conditions.sh: four new cases in scripts/coverage_conditions_sh_test.py (the mode reaching gobco as -branch before the tags and the test flags, staged or not; a value it cannot read refused before anything runs; a report without the figure of its mode refused; the gate reading a branch report) failed thirteen subtests against the script before the mode, and the whole suite, thirty tests, passes, as do scripts/check_spec_conditions_sh_test.py and shellcheck on both scripts.
  • Each of the eleven fuzz targets ran clean under go test -fuzz for one minute, as the pull request trigger runs them (cmd/server's in 143 s with the build of its instrumented binary, the others in 60 to 72 s).
  • The two whole-tree runs above measured the same 31 packages as not measured, each with its cause quoted in the summary, and the condition run found the same twenty conditions as an earlier run over the tree before the mode existed.
  • go test ./... -count=1 passed at the head before the rebase with the freshness comparisons on and with them deferred, and passes at the rebased head with them deferred, as CI runs a layer below the top. After the rebase it first failed in cmd/fuzz_targets: Read a timestamped job log as GitLab's viewer does: no header, and a line written in pieces whole again #1498 renamed internal/toolutil's target to FuzzScanLogMarkup_RawOffset, and the discovery test named it the old way, so the test names it as the tree spells it now. golangci-lint is clean on both commands, actionlint v1.7.7 accepts conditions.yml and fuzz.yml (the measure job's bound is the expression ${{ inputs.tree && 120 || 60 }}), and make check-supply-chain (the new workflow's actions are pinned to the commits the others use), check-test-file-names, check-test-subtests, check-test-goroutines, check-godocs, check-dead-consts, check-doc-tool-names, check-tenancy, check-md-escaping, check-sdk-context, check-action-ids and check-llms pass.
  • go run ./cmd/gen_testing_docs/ --check and check-site-stats pass, and so do node scripts/check-doc-links.mjs, markdownlint on every changed Markdown file, the table formatter's --check, make check-em-dash over this layer and make check-pr-description over this title and description.

Before this merges

These are the maintainer's, and nothing above claims them:

  • The first runs. I could run neither workflow on GitHub. This pull request runs fuzz.yml through its paths trigger, eleven jobs of one minute each, which should pass; the first ten-minute run is the first Wednesday after the merge, or a dispatch then. conditions.yml is already on main, so gh workflow run conditions.yml --ref bp-analysis -f tree=true -f systems=ubuntu-latest -f gate=all -f mode=branch, and the same with -f mode=condition, should run this branch's version. Its first dispatch, before the fixes below (runs 40467477742 and 40467482058), failed sixteen shards each on the 31 packages gobco could not read; the next should measure all 296, and a shard fails only where a package's figure leaves outcomes untaken under gate=all, which is a measurement, not a failure to measure.
  • The Best Practices form. dynamic_analysis can cite fuzz.yml and its first scheduled run, and warnings the 52 linters of .golangci.yml. For test_branch_coverage80 the evidence is the branch coverage of 99.9% over all 296 packages, from the two runs above; whether that answers the criterion as Met is the maintainer's judgment.
  • Issues to open. One or more for the twenty-three decisions taken one way: the eighteen in nine packages of the first run, with the two cmd/audit_edition_tier conditions beside them, and the five in internal/tools/resourceevents and internal/tools/workitems the second run found, with the two operands of the workitems clear guard beside them, each a test case or a declaration.
  • The testing reference. It is regenerated once, at the top of the stack, which gives the cmd/fuzz_targets row the 100.0% go test -cover reports.

On the rebased stack

I rebased the stack onto main at 8f339f4e8 on 27 October 2026. The checks above ran on this layer before that rebase, except where they say otherwise. On the rebased head, with the two commits the rebase called for (the fuzz target's new name and the dated measurement), go build ./..., go vet ./... and go test ./... -count=1 pass with the freshness comparisons deferred, as CI runs every layer below the top, and every commit carries a Signed-off-by trailer. The committed generated artifacts are regenerated once at the top of the stack, which runs every gate over the combined tree.

After the first CI round

  • Sonar. runScript ran bash by its name (S4036), and now resolves it with exec.LookPath first and runs the absolute path, as cmd/gen_orbit_record and cmd/gen_model_results already run git; a PATH holding no bash is reported as the lookup's failure, which a new test holds. measurePackage and measureAll took eight parameters each (S107); the runner, the module root, the system, the directory of records and the environment are now one measurer value. Both are folded into the commit that added the subcommands. cmd/measure_conditions stays at 100.0% of statements, 272 of 272 conditions and 228 of 228 branches, and gremlins kills the 38 mutants of measure.go and tree.go with none lived or uncovered.
  • The 31 packages the dispatch could not measure (runs 40467477742 and 40467482058, the same 31 with the same causes in both modes). I reproduced each kind on the remote runner with the pinned gobco, v1.3.4, which is still its latest release. scripts/coverage-conditions.sh now stages a package with an external test package that gobco cannot read: each of the package's own test files that declares no test, benchmark, fuzz test or example becomes a file of the package in the copy, renamed without its _test suffix and opened with //go:build gc, which the go command satisfies and gobco's narrower build context does not, so it is compiled into the test binary as before, its names reach the external tests, and gobco leaves it uninstrumented, as it leaves a test file; and an external test package that imports nothing of the package is given a test file that imports it, beside the one name it reads in a file of the package behind the same constraint. No position gobco reports moves, and a package with neither shape is still measured where it is. internal/completions's tests now look for a case label inside its expression, which reads the same labels from gobco's GobcoCover(n, gobco0 == "label"), and internal/testutil/serialtypecheck's test leaves gobco's own gobco_*.go files out of the imports it holds the package to. Of four new cases in scripts/coverage_conditions_sh_test.py, the two staging cases fail against the previous script, on a stand-in gobco that panics on the undefined name and fails setup on the bridge as gobco does, a third shows the stand-in fails that way on the package in place, and a fourth holds that an external test package gobco can read is still measured where it is; its 34 tests pass. On 27 October 2026 I decided to keep this staging, which supersedes my decision of 6 October, recorded in upstream-bugs entry 97 and issue 1099, to leave the export_test.go limit to gobco with no workaround in the staging script. The staging is temporary: it goes when gobco publishes a release with the fixes of Type-check black box tests as go test builds them rillig/gobco#43 (for A black box test that uses a name from export_test.go makes gobco panic with "undefined" rillig/gobco#42) and Resolve black box test import path from go.mod rillig/gobco#39 (for Finding original package path in directory with _test package may fails rillig/gobco#33). Entry 97 records that decision and the workaround, entry 140 is new, for the bridge, and the script, the Makefile and the testing page say the staging is temporary.

@sourcery-ai

sourcery-ai Bot commented Oct 27, 2026

Copy link
Copy Markdown

Sorry @jmrplens, your pull request is larger than the review limit of 150,000 diff characters

@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 3f16a0b and 0ea3617.

Files selected for processing: 39
  • .github/copilot-instructions.md
  • .github/workflows/conditions.yml
  • .github/workflows/fuzz.yml
  • .golangci.yml
  • CLAUDE.md
  • Makefile
  • cmd/fuzz_targets/doc.go
  • cmd/fuzz_targets/main.go
  • cmd/fuzz_targets/main_test.go
  • cmd/fuzz_targets/targets.go
  • cmd/fuzz_targets/targets_test.go
  • cmd/measure_conditions/discover.go
  • cmd/measure_conditions/discover_test.go
  • cmd/measure_conditions/doc.go
  • cmd/measure_conditions/main.go
  • cmd/measure_conditions/main_test.go
  • cmd/measure_conditions/measure.go
  • cmd/measure_conditions/measure_test.go
  • cmd/measure_conditions/measurement.go
  • cmd/measure_conditions/measurement_test.go
  • cmd/measure_conditions/plan.go
  • cmd/measure_conditions/plan_test.go
  • cmd/measure_conditions/record_test.go
  • cmd/measure_conditions/summarize.go
  • cmd/measure_conditions/summarize_test.go
  • cmd/measure_conditions/tree.go
  • cmd/measure_conditions/tree_test.go
  • docs/development/cmd-utilities.md
  • docs/development/development.md
  • docs/development/repository-map.md
  • docs/development/static-analysis.md
  • docs/development/testing/README.md
  • docs/development/upstream-bugs.md
  • internal/completions/completions_test.go
  • internal/testutil/serialtypecheck/serialtypecheck_test.go
  • scripts/check-spec-conditions.sh
  • scripts/check_spec_conditions_sh_test.py
  • scripts/coverage-conditions.sh
  • scripts/coverage_conditions_sh_test.py
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jmrplens
jmrplens added this pull request to stack #1565 October 27, 2026 03:07
@github-actions github-actions Bot added the ci Build pipeline, workflows, or lint configuration label Oct 27, 2026
@jmrplens jmrplens added this to the 3.4.0 milestone Oct 27, 2026
@jmrplens jmrplens changed the title bp analysis ci: fuzz weekly, measure the tree's branch and condition coverage, and date the linter count Oct 27, 2026
@github-actions github-actions Bot added the mcp MCP capabilities beyond tools: resources, prompts, completions, subscriptions, elicitation label Oct 27, 2026
…rrency cap

.golangci.yml and the static analysis page explained run.concurrency
with "across 226 packages and 55 linters", which read as a count of
today's configuration. It is the measurement of July 2026 behind the
cap: the module had 226 packages then, and the 55 were the 52 linters
and 3 formatters the file enabled, counted together. The module has
306 packages now, and the configuration enables 52 linters, which
golangci-lint linters lists, and the same 3 formatters.

Both places now say when the measurement was taken and what it
counted, so nothing in the tree contradicts the 52 linters the OpenSSF
Best Practices answer for the warnings criterion cites. The dash in the
comment goes with the sentence it was in.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…al/, and total each runner

The repository measures condition coverage with gobco package by
package, and had recorded a figure for three packages. The OpenSSF Best
Practices gold criterion test_branch_coverage80 asks for one over the
tree, measured with a FLOSS tool, and nothing could take it: the
workflow measured the packages a dispatch named, one job each, and its
summary added nothing up.

plan takes -tree: every package the go command lists for ./cmd/... and
./internal/..., dealt round twenty shards, the first to the first shard
and so on round again, so the slow packages of one directory land in
different jobs, and each shard is a job on each runner named "tree NN
of 20". A package list beside -tree is refused. A cell of either kind
now carries the packages it measures and the name its job takes.

measure is what each job runs: scripts/coverage-conditions.sh through
bash over each of its packages, one after another, streaming the output
to the log and leaving, per package, gobco.txt and then run.env in a
directory of the records named after it. It replaces the shell that
wrote one record per job, so the record's writer sits beside its reader
and their tests hold each to the other. A package the script could not
measure fails the job once the job's other packages are measured.

summarize adds a total per runner: how many of its planned packages
were measured, the outcomes gobco counted as evaluated of those there
are, and that share rounded down to a tenth of a percent, so 799 of
1000 reads 79.9%. A package that was not measured is in no total, and
the summary says why with the lines of its output that do: the
script's own, and the panic, the failing test, the test package that
did not build or set up, or the compiler position gobco's run printed
before them, which is the only place the cause of the failures the
tree meets is written. A figure too large for an int is refused rather
than wrapped.

tree is a whole -tree dispatch on the machine at hand: it lists the
tree, measures each package, -parallel at a time, and writes the same
summary with its system named local. make coverage-conditions-tree runs
it, so the figure can be taken again without a dispatch.

conditions.yml gains the tree input, names each job by the name its
cell carries, runs measure in its measuring step, and bounds a shard's
job at two hours while a job of a named package keeps the hour it had.
The command reference, the repository map, the development guide and
the trees in CLAUDE.md and the Copilot instructions describe the four
subcommands.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
go test runs a fuzz target's seed corpus as one more test and varies no
input unless it is given -fuzz, and nothing here gave it, so the eleven
targets in five packages (FuzzMeterFor, FuzzEvaluate_IsSoundAgainstGitLab,
FuzzRefuseUnreadable and the rest) only ever ran on their seeds. The
OpenSSF Best Practices answer for dynamic_analysis rested on the race
detector and the e2e suite for want of a tool that varies its inputs.

.github/workflows/fuzz.yml runs each target with go test -run '^$'
-fuzz '^<target>$', one job per target: for ten minutes on Wednesdays,
which no other weekly workflow runs on, and when dispatched, and for
one on a pull request that changes the workflow or its plan. GitHub
offers a workflow for dispatch only once it is on the default branch,
so the pull request that adds it can run it through that trigger alone;
a later change could be dispatched from its branch, and the trigger
runs it without anybody having to. No other pull request runs it, and
it stays out of ci.yml so it gates nothing. A failing input is what go
test writes under the package's testdata/fuzz/<target>, and the job
uploads that directory as its artifact.

The targets are not listed in the workflow. cmd/fuzz_targets lists
every package with the toolchain's own go, parses the test files the go
command builds for each, and prints a matrix cell per top-level function
the go command reads as a fuzz target, so a target added anywhere is
fuzzed from the next run on, and a plan that found none fails instead
of passing with nothing fuzzed. Its tests hold the workflow to the
matrix.

The static analysis page gains a Fuzzing section next to the race
detector, with how to reproduce a failing input and keep it as a
regression seed, and the command reference, the repository map and the
trees in CLAUDE.md and the Copilot instructions name the command.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
The OpenSSF Best Practices criterion test_branch_coverage80 asks what
share of the branches the tests take, and every figure here came from
gobco's default mode, which counts something else: every atomic
condition, each operand of && and || and each comparison wherever it
is written, a return statement as much as an if. gobco 1.3.4 also has
-branch, which counts the whole controlling condition of each if and
for and each case of a switch, and nothing else, which is what the
criterion names.

scripts/coverage-conditions.sh reads GOBCO_MODE: condition, the default
and what every run measured before, or branch, which passes -branch to
gobco and reads the report's Branch coverage figure. Any other value is
refused with status 2 before gobco runs, and a report without the
figure of its mode is refused as a 0/0 is. The gate reads either mode's
report alike, since gobco names a branch taken one way in the words it
uses for a condition.

measure_conditions takes -mode on plan, summarize and tree, refused as
the script would refuse it. summarize reads each record's figure in the
words of its mode, so a record of the other mode is one it cannot use,
and heads the summary with the mode and what its figures count; tree
hands the mode to the script beside the gate. measure takes none, since
the script reads the mode from the environment the job gives it.
conditions.yml gains the mode input and passes it to the plan, the
measuring step and the summary, and make coverage-conditions-tree takes
CONDITIONS_MODE. The testing page, the command reference and the
script's header say what each mode counts, and the repository map and
the trees in CLAUDE.md and the Copilot instructions name the branch
coverage beside the condition coverage.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
cmd/fuzz_targets is a package of its own, so CLAUDE.md counts it: 307
in the module and 334 directories holding tracked Go files, the same
twenty-seven left out of go list. The testing reference that counts
its test functions is regenerated once, at the top of the stack.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…tree

The OpenSSF Best Practices gold criterion test_branch_coverage80 asks
what share of the code's branches the tests cover, measured with a
FLOSS tool, and the testing page had gobco figures for three packages,
all in its condition mode. It now has the tree's in both modes, taken
on 26 October 2026 on linux/amd64 with the tree subcommand four
packages at a time, on this branch once its Go code was final: 265 of
the 296 packages below cmd/ and internal/ measured, 44936 of their
44954 branch outcomes taken and 53412 of their 53432 condition outcomes
evaluated, 99.9% each, rounded down. The eighteen branch outcomes left
are eighteen decisions taken one way in nine packages, thirteen of them
the error branch of a call no test makes fail, and the twenty condition
outcomes are the same places and two operands no decision holds whole.

The page says what each mode counts, and why a decision whose every
condition went both ways has gone both ways itself, so that over the
same packages the branch figure leaves out no more than the condition
figure does, and gives both ways to take each figure again. The 31
packages not measured are named with the limit of gobco's each meets:
twenty-seven hand symbols from an export_test.go to their external
test package, three have an external test package that does not import
the package, for which gobco writes its bridge with an empty import
path, and internal/completions has tests that read its own source for
case labels gobco's copy has rewritten, which its tests could stop
doing. None of them fails under go test. The page says each share is
of the packages measured, and that the tree's as a whole is not known
until gobco reads those 31.

The workflow section names the mode input and the artifact a shard
uploads, and says what the summary quotes for a package not measured.
The counts beside the measurement are corrected: the paragraph on
export_test.go and scripts/check-spec-conditions.sh cite twenty-two of
the 149 sub-packages of internal/tools that carry an action_specs.go,
which is what that gate reads, where they carried twenty-five of 169.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…ve it

#1498 renamed internal/toolutil's fuzz target FuzzLogMarkupRawOffset to
FuzzScanLogMarkup_RawOffset, after the function it fuzzes, so on the
rebased branch the discovery test over the repository found eleven
targets and failed on the one it still named the old way. The test now
names the target as the tree spells it; the discovery itself and the
workflow, which list the targets rather than name them, are unchanged.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…rebase

The branch and condition figures were taken on 26 October over this
branch when it sat on main at ed18d7d, and the page said they were
taken on the branch once its Go code was final. The branch has since
been rebased onto a later main, which brought packages of its own, so
the page now says which tree was measured and that the packages main
gained since are in neither total.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…s copy writes it

TestComplete_EveryCaseOfBothSwitchesIsDriven and
TestCompletionBackingActions_StaysAlignedWithBothSurfaces read the case
labels of completePromptArg and completeResourceArg out of completions.go.
gobco measures the package from a copy in which a tagged switch compares
the argument with each label inside a call of its own,
GobcoCover(n, gobco0 == "label"), and the tests run there too, so the
parser found no label that was a literal on its own, both tests failed,
and the whole-tree measurement could not take the package's figures.

The label is now looked for inside its expression, which reads the same
labels from the source and from gobco's copy.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
… package is held to

TestInitialization_PrecedesGoPackages_InEveryBinary reads the imports of
every file of the package in its directory and holds them to what
go/packages depends on. gobco measures the package from a copy into which
it writes files of its own, gobco_fixed.go and gobco_no_testmain_test.go,
which imports testing, and the test runs there too, so it failed on an
import that is gobco's instrumentation and no binary of the repository
links. A file named gobco_ is now left out of what it reads.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
…t read

The whole-tree measurement could not measure 31 packages, and 30 of them
for two limits of gobco v1.3.4, its latest release, in how it reads an
external test package (package x_test).

gobco type-checks that package through the source importer of
go/importer, which builds the package under test from its non-test files
alone, so a name the package's own test files declare for it is
undefined there and resolveTypes panics before anything is measured
(panic: action_specs_test.go:49:27: undefined: todos.PublishedActionIDs).
That was 27 packages, each handing names over in an export_test.go
(rillig/gobco#42, with the fix in review as rillig/gobco#43, upstream-bugs
entry 97). And gobco bridges the external test package to the
instrumented one through an import of the package it finds in the
directory, so an external test package that imports nothing of the
package, as awardemoji, freezeperiods and groupmarkdownuploads do, gets a
bridge with an empty import path, which go test refuses at setup
(rillig/gobco#33, with a fix in review as rillig/gobco#39, entry 140).

scripts/coverage-conditions.sh now stages both shapes in its copy of the
module. Each of the package's own test files that declares no test,
benchmark, fuzz test or example becomes a file of the package, renamed
without its _test suffix and opened with //go:build gc: the go command
satisfies that constraint, so the file is compiled into the test binary
as before and its names reach the external test package, and gobco's
narrower build context does not, so gobco leaves it uninstrumented, as it
leaves a test file. Any constraint of its own holds already, since the
file builds here, and its lines are blanked. An external test package
that imports nothing of the package is given a test file that imports it
and reads one name declared for that purpose in a file of the package
behind the same constraint. Neither change moves a position gobco reports.
A package with neither shape is run where it is, as before.

go list now also gives the package's name and import path, its own test
files and what its external tests import. Those lines come before the
files the package builds, since $(...) drops the newlines a listing ends
with and read fails at the end of its input.

Upstream-bugs entry 97 recorded the decision of 2026-10-06 to leave this
to gobco. The maintainer decided on 2026-10-27 to keep this staging
instead, which supersedes that decision, since the figure over the whole
tree that the OpenSSF Best Practices gold criterion test_branch_coverage80
asks for has waited on a release that has not come. The staging is
temporary: it goes when gobco publishes a release with the fixes of
rillig/gobco#43 (for rillig/gobco#42) and rillig/gobco#39 (for
rillig/gobco#33). Entry 97 records the decision and the workaround, the
script and the testing page say it is temporary, and entry 140 is new.

Tests first: against the previous script the two new staging cases fail
on the stand-in gobco's panic and setup failure, which a third case shows
the stand-in reproduces in place, and a fourth holds that an external
test package gobco can read is still measured where it is. 34 tests pass.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
The whole-tree run of 26 October could not measure 31 packages, and the
page said the share of the tree was not known until gobco read them. The
staging of an external test package and the two test changes before this
commit make every one readable, and the page now records them: measured
on 27 October 2026 on linux/amd64 over this branch on main at 8f339f4,
the 31 take 3773 of their 3778 branch outcomes and evaluate 4113 of their
4120 condition outcomes, 99.8% each.

The five branch outcomes left are four per-action metadata decorations in
internal/tools/resourceevents and a comparison with the one tool name the
dispatch of internal/tools/workitems is handed; the seven condition
outcomes are those five and two operands of workitems' clear guard whose
decisions go both ways without them. Over all 296 packages the two runs add
up to 48709 of 48732 branch outcomes and 57525 of 57552 condition outcomes,
99.9% each, which the page says is the sum of two trees measured a day
apart.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
@sonarqubecloud

Copy link
Copy Markdown

@jmrplens
jmrplens merged commit 2512c41 into main Oct 28, 2026
61 of 89 checks passed
@jmrplens
jmrplens deleted the bp-analysis branch October 28, 2026 09:38
jmrplens added a commit that referenced this pull request Oct 28, 2026
…ndency and regenerate the testing reference (#1564)

Refs #1269, #1114, #1243, #1129

This is the top of the stack that carries the OpenSSF Best Practices
work of #1269 and the supply-chain layers below it (#1114, #1243,
#1269's Scorecard workflow and #1129's image scan). The layers below
leave the committed generated artifacts stale under the freshness
deferral, and this one regenerates them once, on the tree the merge puts
on `main`. It also carries the fixes the review bots' threads on the
layers asked for, each in a commit of its own (Review fixes, below); it
changes no handler.

The whole stack sits on `main` at `8f339f4e8`, onto which I rebased it
on 27 October 2026, layer by layer and in order. Every commit of it is
signed and carries a `Signed-off-by` trailer: the layers written before
the maintainer adopted the Developer Certificate of Origin got theirs
from `git rebase --signoff`. Where a layer met `main`'s configuration
stack (#1518 to #1525) or the Orbit fixtures' move (#1532) the conflicts
kept both sides, and generated files took `main`'s side to be
regenerated here. The supply-chain stack's own regeneration,
`c-c1-regen`, conflicted with `main`'s testing reference, held nothing
else, and was dropped, so `issue-1129-trivy` is now the top of that
part. The header rewrite of `bp-file-headers` was made again by `make
fix-file-headers` on the rebased tree rather than rebased.

## What it regenerates

Only the testing reference moves, and the action record gains the one
file a review fix reads. The layers and the review fixes add 271 test
functions, 266 under `cmd/` and five in `test/e2e/http`, 41 test files
under `cmd/` and seven packages, so
`docs/development/testing/testing.md` gains them in its totals, its
naming breakdown, its distribution table and the `cmd` coverage table. I
refreshed the counts with `go run ./cmd/gen_testing_docs/
-skip-coverage`, which carries every recorded coverage figure forward
and writes `n/a` for a package no coverage run has measured. The six new
packages that have statements (`cmd/audit_dco`,
`cmd/audit_file_headers`, `cmd/audit_image_vulns`,
`cmd/audit_site_headers`, `cmd/fuzz_targets` and
`cmd/reproduce_release`) carry the 100.0% `go test -covermode=count
-cover` reports for each, and `cmd/internal/fileheader`, which holds
constants alone, keeps `n/a`. A second `-skip-coverage` run keeps every
figure.

Every other generator of `make update-all` runs on this tree and writes
nothing: the brand assets, the action grants, the token footprint, the
site stats, the llms files, the per-domain tool reference in both
languages, the LobeHub manifest, the model corpus ledger and results
page, the action catalog manifest, the benchmark charts and the e2e
coverage record page, and the table formatter after them. The request
inventory, recorded again over the whole unit suite on this tree, does
not move (1680 rows, 976 paths, 1090 of 1090 catalog actions owned by a
package that recorded a request). `go run ./cmd/audit_supply_chain
-record` reads the 32 pinned actions again from GitHub and writes only
the Dockerfile of `hadolint/hadolint-action`, which the review fix for
#1552 reads. The package and file counts in `CLAUDE.md` match the tree:
311 packages in the module, 338 directories holding tracked Go files,
182 under `internal/tools`, 788 non-test and 488 test Go files there.

## Checks on this head

With the freshness comparisons checked
(`GITLAB_MCP_TEST_SNAPSHOT_PARITY` unset):

- `go test ./... -count=1`: 301 packages pass, recording the request
inventory as they run, and `go run ./cmd/gen_request_inventory/ -check`
passes after them (1680 rows, 976 paths). The coverage job's own command
passed with a total of 100.0% of statements on the head before the
review fixes, and `cmd/audit_supply_chain`, the one package whose code
they change, is at 100.0% of statements, 1532 of 1532 conditions and
1118 of 1118 branches under gobco, with gremlins leaving no mutant of
the five files they change lived, uncovered or timed out.
- Every `make` target the generated artifacts job runs, 45 of them with
`check-closed-servers`, `check-supply-chain`, `check-e2e-static` and
`audit-catalog-first`, passes; `check-openplugin`,
`check-server-json-packages` (the eight declared packages),
`check-file-headers` (3434 source files) and `check-dco` (all 73 commits
from `8f339f4e8`) pass locally, since the first two need `jq` and the
network and the last two read git.
- `make analyze` on the remote runner: 30 of 32 steps pass. Step 6,
markdownlint, has no `npx` there, and step 32, the file headers, has no
`.git`; both pass locally, markdownlint over `"**/*.{md,mdx}"`.
- `make test-e2e-http` (780 tests, the 8 that need Docker for a proxy or
a balancer skipped) and `make test-e2e-stdio` (277 tests) pass.
- The server binary and the e2e suite's compiles of the compile job, and
the type-check job's vets for windows/amd64, darwin/arm64 and
linux/arm64, pass.
- Locally: the repository scripts' 517 Python tests, `make
check-em-dash` against `main`, `make check-plan-untracked`, `make
check-mcpb`, the site's `i18n:check`, `facts:check`, `chips:check`,
`llms:check`, `version:check`, `build` and `lint`, and `node
scripts/check-doc-links.mjs` (350 files, 8267 anchors) pass.
- Every layer's head below this one passes `go build ./...`, `go vet
./...` and `go test ./... -count=1` with the freshness comparisons
deferred, as CI runs them.
- `make check-pr-description` passes on this text and on every layer's.

## The release rehearsals

Three layers change what `release.yml` does, and each is rehearsed from
its own branch once the stack is pushed: `issue-1129-trivy` for the
supply-chain layers (the PyPI publish and the image scan),
`bp-release-notes` for the written summary preflight requires, and
`bp-reproducible-builds` for the rebuild compared byte for byte. `gh
workflow run release.yml --ref <branch>` builds, smoke-tests and
dry-runs every publisher from that branch and publishes nothing. Its
race gate runs the unit suite without the deferral, and the packages
whose tests compare a committed artifact pass that way on each of the
three branches and on `bp-image-base`. The review fix to
`scripts/publish-pypi.sh`, whose `--dry-run` the rehearsal's PyPI job
runs, lives on this branch, so the PyPI half of the `issue-1129-trivy`
rehearsal no longer exercises the script `main` will carry, and
`bp-regen` is rehearsed as well for it.

## After the first CI round

The first CI round of the stack asked for fixes in ten layers, each made
in the layer that owns it and folded into the commit it belongs to, or
added as a commit of that layer where it is new work, and every layer
above rebased on it:

- SonarCloud: the duplicated literals of #1550, #1552 and #1557, the
nested anonymous structs of #1553, the eight-parameter functions of
#1558, the tool run by its name from PATH in #1557, #1558, #1562 and
#1563 (each now resolved with `exec.LookPath`, as `cmd/gen_orbit_record`
does), and the test variables of #1562.
- GitGuardian on #1551: the stand-in OIDC token of the PyPI publish
tests is built at run time, and no commit of that layer carries the
literal.
- `windows-latest` on #1555: a file in the release notes directory's
place is refused on every system.
- The whole-tree condition measurement of #1558, which could not measure
31 packages: `scripts/coverage-conditions.sh` stages the two external
test package shapes gobco v1.3.4 cannot read, two tests read the same
thing in gobco's copy as in the source, and the 31 are measured and
recorded. I decided on 27 October to keep that staging, which supersedes
my decision of 6 October to leave the limit to gobco, until gobco
publishes a release with the fixes; upstream-bugs entries 97 and 140,
the script and the testing page say it is temporary.
- #1561 gained the hardened mode the maintainer decided on:
`--allow-plaintext-http` defaults to `true`, and `false` refuses a
plaintext listener off loopback, as register row ADM-015.

This head regenerates the testing reference once more for the tests that
round added. On every layer's head, `go build ./...`, `go vet ./...` and
`go test ./... -count=1` pass with the freshness comparisons deferred,
and every package whose code that round changed is at 100.0% of
statements, with every condition and branch gobco counts there taken and
no mutant lived or uncovered beyond the one constant initializer of
`cmd/server/listen.go` the layer below names.

## Review fixes

The review bots' threads on #1550 to #1562 found ten real gaps, and I
fixed each here, at the top, in a commit of its own, so no layer below
changes and only this pull request runs CI again; every thread, declined
ones included, is answered with the commit or with why it does not
apply. In `cmd/audit_supply_chain` (#1550, #1552, #1555): a GoReleaser
template is a value only the run knows and a build's overrides and a
flags string are read, an absolute `--config`, `dir` or `dist` stays
absolute and is refused, a tag-named image is refused in a job whose
token neither it nor its workflow declares, the images a container
action's Dockerfile builds from are held to their digest, read as
BuildKit binds the file's stages, parser directives and heredocs (the
record gains `hadolint/hadolint-action`'s Dockerfile, whose base is
pinned), a release note crediting an assistant as its author is refused,
and release note headings and fences are read as GitHub renders them.
`TestClient_StatelessAnswersCarryNoSession` holds the stateless
initialize to a JSON-RPC result (#1550), `scripts/publish-pypi.sh`
publishes exactly the six platform wheels, each a regular file (#1551),
the install pages say `make check-repeatable` does not build the
universal macOS binary (#1557), and the Code of Conduct offers the
OpenPGP key for a report sent by email (#1560). Each testable fix came
with a test that failed before it, `cmd/audit_supply_chain` stays at
100.0% of statements, and the testing reference is refreshed for the
seven test functions they add.

## Dependencies

This layer also moves every dependency to its newest release, with no
cooldown, which fixes this PR's CI failure: golang.org/x/net v0.62.0
fixes GO-2026-6860 and GO-2026-6866 in x/net/html, and
check-binary-vulns now reports no advisory on the six binaries. The
other Go moves are tiktoken-go/tokenizer v0.10.0 (whose Codec interface
gained two methods that the cmd/audit_tokens test doubles now
implement), gqlparser v2.5.64, grpc v1.85.0, grpc-gateway v2.33.0 and
their indirect dependencies. The builder image moves to the rebuilt
golang:1.27.3-alpine3.24 digest.

Five actions move: setup-python v7.1.0, markdownlint-cli2-action
v25.0.0, login-action v4.7.0, codeql-action v4.39.1 and
sonarqube-scan-action v8.4.0, with pinned-actions.json re-recorded. The
release tools move to GoReleaser v2.19.0, cosign v3.1.4, oras 1.3.6 and
mcp-publisher v1.8.6, the NuGet validation to the .NET 10.0.403 SDK
(both dnx facts the install page relies on re-verified), and the npm
validation and publishing to Node 24. The PyPI upload's lock is
recompiled (platformdirs 4.13.7, pyasn1 0.6.5).

The site moves to pnpm 12.16.1, Node 24.22.1 and the patch releases of
astro, starlight, markdown-remark, @astrojs/check and typescript-eslint.
The telemetry example moves to collector 0.164.0 and otel-lgtm 0.38.1,
the balancer e2e to HAProxy 3.4, and the e2e Bitbucket fixture to 10.5,
which now starts with basic authentication force-allowed: Bitbucket 10
blocks it by default, and both the fixture's setup script and GitLab's
importer authenticate with it.

Left out on purpose: client-go stays at v3.30.0 because it moves in its
own stack (the server's major tracks it, and a bump moves the served
surface and the fine-grained table); the GitLab CE/EE images stay pinned
because 19.5 is still GitLab's newest release and the fine-grained table
records it; Node 26 is not taken because no 26.x is LTS and it drops
Corepack; TypeScript 7 is refused by the peer ranges of @astrojs/check
and typescript-eslint. The release tools, the SDK and the builder image
were rehearsed with `release.yml` on this branch before merging.

## Before this merges

- **#1517.** It is the outside contribution open on 27 October, based on
`main` at `e3e09d290`. Its 18 changed source files merge with the top of
this stack without a conflict; the file it adds has no header and its
six commits no sign-off, which the header and DCO layers say how to
handle.

Milestone: 3.4.0.

---------

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Build pipeline, workflows, or lint configuration mcp MCP capabilities beyond tools: resources, prompts, completions, subscriptions, elicitation v3.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fuzz on a schedule, measure branch coverage over the whole tree, and state the linter count as measured

1 participant