Skip to content

chore: carry a copyright line and SPDX-License-Identifier: MIT in every source file - #1563

Merged
jmrplens merged 3 commits into
bp-dcofrom
bp-file-headers
Oct 28, 2026
Merged

jmrplens merged 3 commits into
bp-dcofrom
bp-file-headers

Conversation

@jmrplens

@jmrplens jmrplens commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Refs #1269

What this changes

Every source file of the repository now opens with two lines, Copyright (c) 2026 José M. Requena Plens and SPDX-License-Identifier: MIT, written as comments of its language. These answer the OpenSSF Best Practices criteria copyright_per_file and license_per_file, which ask every source file to name its copyright holder and its license, the second in the SPDX form near the top of the file. Until now no source file said either.

The pull request has three commits: a refactor of cmd/godoc_tool that its mutation gate asked for once this layer touched it; the gate with its fix mode, the generators' share of the header and what the header would otherwise break; and the rewrite of the tree made by that fix mode. The testing reference that counts the new tests is regenerated once, at the top of the stack.

The gate

cmd/internal/fileheader holds the two statements, CopyrightStatement and LicenseStatement, and Go, the form a Go file opens with: the two lines as comments and a blank line. Without the blank line the header would be read as the package's doc comment, or would join a generated file's Code generated comment. The gate and every generator read the same constants, so a header a generator writes is the header the gate holds it to.

cmd/audit_file_headers judges the files git holds in its index and the untracked ones it would add, read with git ls-files. I chose git's list over a walk of the tree because the question is which files the repository publishes. A walk would read what .gitignore keeps out (plan/, dist/, site/node_modules with its thousands of files somebody else wrote) unless it named each of them again, and the name rule of internal/sourcewalk leaves out every dot-directory, which would leave out the scripts under .github/scripts. Files of a checkout nested in the tree are not listed, which is the answer the marker rule of internal/sourcewalk gives a walk. The cost is that the command runs in a checkout, not in a copy of the tree without its .git.

Of those files it judges Go (testdata included), shell, PowerShell, jq, Python, Ruby, JavaScript and TypeScript in each of their extensions, CSS, Astro components, Makefiles and the Dockerfile. Markdown, MDX, JSON, YAML (the workflows included), TOML, the pinned GraphQL schema, plain text, SVG and images are documentation, data and configuration and carry no header. Each kind gets the header in its comment syntax, after what has to stay above it to keep working: a shebang, an encoding declaration on line 1 or 2 for Python and Ruby, a Dockerfile's parser directives, a CSS @charset rule, and the frontmatter fence of an Astro component, whose header lives in the frontmatter so that it is not rendered into the page. In a Go file the header comes before any build constraint, as in the Go project's own files.

A file fails when its header is not those two lines, in order, exactly there. It also fails at every comment line whose text opens with the word Copyright or with an SPDX tag other than the header: a second copy of the header, a header written below the package doc comment, or the notice of a file somebody else wrote. The word has to end there, so the doc comment of CopyrightStatement is not a notice. Each line is read alone, without the syntax of the file around it, so a line of a multi-line string or a heredoc that opens with a comment marker is read as a comment. The rule errs towards reporting, since a notice it missed would be a file somebody else wrote stamped with this one. Running it over the tree found two lines of ours: the doc comment of the constant, then named Copyright, which is why the word has to end there and the constant is now CopyrightStatement, and a line of the gate's own comment, which I reworded.

-fix writes the header into every file that does not open with it and carries no other notice, followed by a blank line unless the file has one there already, keeping every other byte and the file's mode. An Astro component without a frontmatter is given one around the header. A file with another notice is never rewritten. No file in the tree carries one today: the module vendors nothing and the site's dependencies stay in site/node_modules.

make check-file-headers runs the gate and make fix-file-headers its fix mode. It is step 32 of make analyze, and a step of the generated job of ci.yml, which runs on every pull request and every push to main.

The generators and what the header moves

cmd/gen_action_catalog_manifest, cmd/gen_action_grants and cmd/gen_brand open the Go file each writes with the header, and the three generated files are regenerated with it, never edited. cmd/gen_action_grants/table_stub.go.txt, the stand-in the generator compiles in table_gen.go's place, is Go source under a .txt name the gate does not read, and it carries the header by hand in the same commit. cmd/godoc_tool's fix -move-package-doc writes the header into the doc.go it creates.

The same commit changes what the header would break or leave pointing at the wrong line, so that making the rewrite again on a later main loses none of it. scripts/setup-orbit-fixtures.sh --help printed lines 2 to 44 of the script, which with the header would have printed the header and lost the last three lines of its usage comment; it now prints the usage comment by pattern, and its help test holds the whole comment. Three quoted compiler messages name a line the header moves three down: the internal/tools/todos failure quoted in the testing README and in upstream-bugs.md (49:27, now 52:27) and the redeclaration panic quoted in scripts/coverage-conditions.sh (5:6, now 8:6), each written as the rewrite leaves it. A comment of the e2e suite that named line 28 of internal/tools/catalog_filter.go names the declaration instead.

Other line references in docs/development to positions in this repository's own files had already drifted before this change, and I left those as they were: settings.go:35 in the testing README, for one, is a comment and not the err != nil the README names, and moving it by three would not make it right.

The godoc_tool refactor

Touching godoc_tool meant holding it to the mutation gate, and on the base seven of its mutants lived and two were not covered: the finding and count-table comparators, whose < mutated to <= cannot be told apart where the keys are already known to differ, and the case expression of a tagless switch over the test-function families. The first commit makes the comparators cmp.Or chains of three-way comparisons and the families a table, and cuts the count table with min; these order and count as before. It also changes one behavior: sortFindings becomes stable. sort.Slice could put findings that share all four keys in either order, so two runs over one tree could list them differently, and slices.SortStableFunc keeps the order they were found in.

The rewrite

The third commit was made by make fix-file-headers, run afresh on this layer after the stack was rebased onto main at 8f339f4e8 rather than rebased itself: 3419 files, 3278 of them Go, 54 Python, 36 shell, 18 MJS, 13 Astro, 9 CSS, 5 TypeScript, and the Ruby, PowerShell, JavaScript and jq files, the Makefile and the Dockerfile. One edit is by hand. cmd/audit_test_closed_servers pins the line of every finding in its planted module, which the header moves three lines down, so its 64 positions and the three literals beside them move by three; each new value is what the audit reports. That main had not changed the audit, so the two test files are the hand-edited ones of the first rewrite, and every other file the first rewrite stamped whose content main had not changed came out of the fresh run byte for byte the same.

Documentation

docs/development/file-headers.md says what the header is, which files carry it and where, how a generated file gets it, what happens to a file this repository did not write, how a change of holder or year is made, and how this rewrite lands. The command reference, the static analysis reference (its make analyze row and the two new targets), the repository map and the docs/development index name the command. CONTRIBUTING.md gains a Copyright and License Headers subsection under Code Standards and a checklist item, and the pull request template a checkbox. CLAUDE.md gains the rule and the link, the command in the tree and the package counts (311 in the module, 338 directories holding tracked Go files). AGENTS.md gains the rule among its invariants and counts 32 analyze steps. The Go instructions say a new file opens with the header before its package clause, the Copilot instructions name the command, the create-mcp-tool skill's templates open with the header, and the go-source-documentation skill and the go-source-documenter agent say the header sits above a file's own comment and is not a doc comment.

Tests

I wrote the tests first. Against stubs of every function, with the Makefile and ci.yml as they stood before, 14 of the command's 16 test functions failed; the two that passed expect an empty answer, which is the stubs'. The cases I added later failed first too: the jq cases before the kind existed; the word boundary's four cases (// CopyrightStatement is ..., // Copyright_holder ..., // Copyright2026 ..., // Copyrighted material) under the earlier prefix rule; a fixture planted beside a user ignore file naming *.sh, which lost its script until the tests' git isolation also replaced the user configuration directory; the help test of setup-orbit-fixtures.sh against the fixed line range; and godoc_tool's stable-sort test, which sorts thirty-two findings over two keys and failed against sort.Slice and against slices.SortFunc. The three generators' new tests and godoc_tool's three doc.go move tests failed before each wrote the header; two more doc.go expectations, in godoc_tool's bound tests, I found when the change made them fail, and updated.

The cases run over real repositories built in temporary directories, with git's system and user configuration and the user's ignore file kept out: a clean tree, a planted file without the header, a third-party notice, a header beside another notice, the fix mode writing and keeping an executable script's mode, the fix mode leaving a file with another notice and writing nothing over a clean tree, a file deleted from the working tree, a symbolic link, a directory that is not a repository, and a file that cannot be stated, read or rewritten. The rule's own cases cover each kind and each preamble, the header absent and out of place, and what the fix writes, each fix judged clean afterwards. Two contract tests hold the Makefile and ci.yml to the targets and the step.

On the tree itself, the gate passes over all 3432 source files, and with an untracked Go file and an untracked shell script planted without the header it fails naming both.

What only the maintainer can do

  • Landing the rewrite. Three lines inserted at the top of a file conflict only with a change to that file's first lines, so the rewrite conflicts with few pull requests; what it asks of each one is the header in every source file it adds. The plan has it merge with no other stack open. I suggest it lands by being made again rather than rebased, because by then main will have gained source files without the header, and made again it is exactly the fix mode's output over that tree: on the main of that moment, the first two commits rebased as usual, then make fix-file-headers run afresh, then the positions in cmd/audit_test_closed_servers moved again, which go test ./cmd/audit_test_closed_servers/ names, and the testing reference regenerated. Everything else the header needs by hand travels in the second commit, so it is rebased rather than made again.
  • fix(tools): handle paginated merge request diffs #1517. It is the outside contribution open on 27 October, based on main at e3e09d290. I merged each of the 18 source files it changes with git merge-file, its base against the top of this stack, and none conflicts. The file it adds, internal/tools/mrchanges/action_specs_test.go, has no header and fails the gate until its author runs make fix-file-headers after rebasing.
  • LICENSE. It names the holder as "Jose Manuel Requena Plens", and the header uses the spelling decided for it, "José M. Requena Plens". I left LICENSE unchanged.
  • The Orbit fixtures. The Python files under test/fixtures/orbit now carry the header. The fixture projects on GitLab.com keep the content they were created with, since scripts/setup-orbit-fixtures.sh pushes a fixture only when it creates the project; no recorded answer depends on their line numbers.
  • The Best Practices form. copyright_per_file and license_per_file can be answered Met once this merges, with make check-file-headers and docs/development/file-headers.md as the evidence.

Checks run

  • go test ./cmd/audit_file_headers/ -count=1 -cover: 100.0% of statements; make coverage-conditions PKG=./cmd/audit_file_headers in condition mode (94/94) and branch mode (56/56); make coverage-mutants PKG=./cmd/audit_file_headers: 65 killed, 0 lived, 0 not covered.
  • cmd/godoc_tool: 100.0% of statements, 626/626 conditions, 500/500 branches, 246 mutants killed, 0 lived, 0 not covered, after the refactor commit and again after the gate commit.
  • cmd/gen_brand, cmd/gen_action_catalog_manifest and cmd/gen_action_grants: 100.0% of statements, every condition and branch, and 56, 7 and 98 mutants killed with none lived or not covered.
  • go build ./..., go vet ./..., go test -tags e2e -c over ./test/e2e/gitlab/..., and go test ./... -count=1: pass.
  • make test-e2e-harness, make test-e2e-http and make test-e2e-stdio: pass.
  • golangci-lint config verify, golangci-lint fmt --diff and golangci-lint run over the tree with the analysis tags, and every other step of make analyze, the new step 32 included: pass.
  • make check-action-catalog-manifest, make brand-check and make check-action-grants: the three generated files are current.
  • make check-mcpb: pass, and the manifest mcpb/platform.jq derives for each of the three platforms is byte for byte the one it derived before the header.
  • python3 -m unittest discover -s scripts -p "*_test.py": 509 tests pass.
  • The site's i18n:check, facts:check, chips:check, llms:check, version:check, build and lint, and node scripts/check-doc-links.mjs: pass.
  • npx markdownlint-cli2 and go run ./cmd/format_md_tables/ --check on the changed pages: pass.
  • make check-testing-docs, make check-dco DCO_BASE=bp-dco, scripts/check-em-dash.sh diff bp-dco and make check-pr-description on this text: pass.

On the rebased stack

I rebased the stack onto main at 8f339f4e8 on 27 October 2026 and made the rewrite again on it, as described above. That main carries #1532, which lengthened the usage comment of scripts/setup-orbit-fixtures.sh, printed it as lines 2 to 44 and added a test of a nested namespace, so the gate commit's change to the same lines conflicted: the merge keeps the help printed by pattern, and the help test beside #1532's. The checks above ran on this layer before that rebase. On the rebased head, go build ./..., go vet ./... and go test ./... -count=1 pass with the freshness comparisons deferred, as CI runs every layer below the top, make check-file-headers passes over all 3432 source files, 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

SonarCloud found listFiles running git by its name (S4036). It now resolves git with exec.LookPath and runs the absolute path, as cmd/gen_orbit_record and cmd/gen_model_results already do, and a PATH holding no git is reported as the lookup's failure, which a new test holds; the change is folded into the gate's commit. cmd/audit_file_headers stays at 100.0% of statements, 96 of 96 conditions and 58 of 58 branches, golangci-lint is clean, and gremlins kills the three mutants of files.go with none lived or uncovered. The layers below changed in this round without adding a source file, so the rewrite commit is the fix mode's output it was, rebased; the gate commit also moves the line scripts/coverage-conditions.sh now quotes from internal/tools/todos, action_specs_test.go:49:27, to 52:27, as it moves the same quotation elsewhere. make check-file-headers passes over the same 3432 files.

@jmrplens
jmrplens added this pull request to stack #1565 October 27, 2026 03:07

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, we are unable to review this pull request

The GitHub API does not allow us to fetch diffs exceeding 300 files, and this pull request has 3450

@coderabbitai

coderabbitai Bot commented Oct 27, 2026

Copy link
Copy Markdown

Walkthrough

Warning

Review details and warnings were omitted to fit the comment limit.

@github-actions github-actions Bot added security Security-related issue ci Build pipeline, workflows, or lint configuration distribution Release artifacts, packaging, and install channels transport stdio and HTTP transports, the server process, and the transport e2e modules mcp MCP capabilities beyond tools: resources, prompts, completions, subscriptions, elicitation telemetry OpenTelemetry export and the MCP semantic conventions site The documentation site's build, components and scripts, not its content tooling The audit and generator commands under cmd/, and the Makefile targets that run them platform Windows and macOS specific source, and the cross-platform CI leg e2e The end-to-end suite and its Docker fixtures labels Oct 27, 2026
@jmrplens jmrplens added this to the 3.4.0 milestone Oct 27, 2026
@jmrplens jmrplens changed the title bp file headers chore: carry a copyright line and SPDX-License-Identifier: MIT in every source file Oct 27, 2026
The finding and count-table comparators of the audit report were chains
of ifs, each a strict comparison of two keys already known to differ, so
gremlins' boundary mutants, < turned into <=, behave exactly as the
original and live. The case of the tagless switch over the test-function
families tested name == "TestMain" beside a Test prefix that already
implies it, which left two mutants gremlins could not see as covered.
On the base seven mutants lived and two were not covered.

The comparators are now cmp.Or chains of three-way comparisons, the
families a table with the redundant comparison gone, and the count table
is cut with min.

sortFindings also becomes stable. sort.Slice could put findings that
share all four keys in either order, so two runs over one tree could
list them differently; slices.SortStableFunc keeps the order they were
found in. The new test sorts thirty-two findings over two keys, enough
that the sort package's unstable sort no longer orders by insertion,
and it failed against sort.Slice and against slices.SortFunc before
passing against the stable sort.

The package keeps every statement covered, 626 of 626 conditions and
500 of 500 branches, and gremlins kills all 246 mutants.

Refs #1269

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
Every source file is to open with two lines, the copyright statement
"Copyright (c) 2026 José M. Requena Plens" and
"SPDX-License-Identifier: MIT", which answer the OpenSSF Best Practices
criteria copyright_per_file and license_per_file. This commit adds the
gate, the fix mode that writes the header, the generators' share of it
and what the header would otherwise break; the header itself reaches
the tree in the next commit, made by the fix mode.

cmd/internal/fileheader holds the two statements and Go, the form a Go
file opens with: the two lines as comments and a blank line, without
which the header would be read as the package's doc comment or join a
generated file's "Code generated" comment. The gate and every generator
read the same constants.

cmd/audit_file_headers judges the files git holds in its index and the
untracked ones it would add, read with git ls-files. That list is what
the repository publishes: a walk would read what .gitignore keeps out
(plan/, dist/, site/node_modules), and the name rule of
internal/sourcewalk would leave out .github/scripts. Of those it judges
Go, shell, PowerShell, Python, Ruby, jq, JavaScript and TypeScript, CSS,
Astro, Makefiles and the Dockerfile, each with the header in its
comment syntax and after what has to stay above it: a shebang, an
encoding declaration on line 1 or 2 for Python and Ruby, a Dockerfile's
parser directives, a CSS @charset rule, and the fence of an Astro
component's frontmatter, which is where its header lives so that it is
not rendered into the page.

A file fails when the header is not those two lines, in order, exactly
there, and at every comment line whose text opens with the word
Copyright or an SPDX tag other than the header: a second copy, a header
below the package doc comment, or the notice of a file somebody else
wrote. The word has to end there, so the doc comment of
fileheader.CopyrightStatement is not a notice. Each line is read alone,
so a line of a multi-line string or a heredoc that opens with a comment
marker counts as a comment, which errs towards reporting. -fix writes
the header into a file that only lacks it, keeping every other byte and
its mode, and never rewrites a file with another notice, so a file this
repository did not write is not stamped.

make check-file-headers runs it and make fix-file-headers its fix mode.
It is step 32 of make analyze and a step of ci.yml's generated job.

cmd/gen_action_catalog_manifest, cmd/gen_action_grants and cmd/gen_brand
open the Go file each writes with the header, and the three generated
files are regenerated with it. cmd/gen_action_grants/table_stub.go.txt,
the Go source that generator compiles in its output's place, carries it
too, by hand, since the gate does not read a .txt file.
cmd/godoc_tool's fix -move-package-doc writes it into the doc.go it
creates.

Four things the header moves are changed here, so that making the
rewrite again on a later main does not lose them.
scripts/setup-orbit-fixtures.sh printed its help as lines 2 to 44 of
itself, which with the header would print the header and lose the last
three lines of the usage comment; it now prints the comment by its text,
and its help test holds the whole comment beside #1532's nested
namespace test. Three quoted compiler messages name a line the
header moves three down, gobco's on internal/tools/todos in the testing
README and upstream-bugs.md (49:27, now 52:27) and the redeclaration
panic in scripts/coverage-conditions.sh (5:6, now 8:6), and are written
as the next commit leaves them. A comment of the e2e suite that named
line 28 of internal/tools/catalog_filter.go names the declaration.

docs/development/file-headers.md says what the header is, which files
carry it and where, how a generated file gets it, what happens to a file
this repository did not write and how the rewrite lands. The command
reference, the static analysis reference, the repository map and the
docs/development index name the command. CONTRIBUTING gains a Copyright
and License Headers subsection under Code Standards and a checklist
item, and the pull request template a checkbox. CLAUDE.md gains the rule
and the link, the command in the tree and the package counts (311 in the
module, 338 directories holding tracked Go files). AGENTS.md gains the
rule among its invariants and counts 32 analyze steps. The Go
instructions say a new file opens with the header, the Copilot
instructions name the command, the create-mcp-tool templates open with
the header, and the go-source-documentation skill and the
go-source-documenter agent say it sits above a file's own comment and
is not a doc comment.

The tests were written first. Against stubs of every function, with the
Makefile and ci.yml as they stood before, 14 of the command's 16 test
functions failed; the two that passed expect an empty answer, which is
the stubs'. The cases for jq failed before the kind existed, the word
boundary's four cases failed under a prefix rule, and a fixture planted
with a user ignore file naming *.sh lost its script until the tests'
git isolation also replaced the user configuration directory. The
generators' three new tests and godoc_tool's three doc.go move tests
failed before each wrote the header, two more doc.go expectations in
its bound tests were updated when the change made them fail, and the
help test of setup-orbit-fixtures.sh failed on the fixed range. The
command covers every statement, condition and branch and gremlins kills
every mutant; godoc_tool, the three generators and fileheader keep
every statement, condition and branch with no mutant left alive.

Refs #1269

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

Every source file of the repository now opens with "Copyright (c) 2026
José M. Requena Plens" and "SPDX-License-Identifier: MIT", as comments
of its language, which answers the OpenSSF Best Practices criteria
copyright_per_file and license_per_file.

make fix-file-headers wrote the header into the 3419 files that lacked
it: 3278 Go files (testdata included), 54 Python, 36 shell, 18 MJS, 13
Astro, 9 CSS, 5 TypeScript, and the Ruby, PowerShell, JavaScript and jq
files, the Makefile and the Dockerfile. A shebang, the Dockerfile's
syntax directive and each Astro component's frontmatter fence stay
above it, and every other byte and every file mode is kept. The three
generated Go files and the action grants stub carry it already, from
the previous commit.

One edit is by hand. cmd/audit_test_closed_servers pins the line of
every finding in its planted module, which the header moves three lines
down, so the 64 positions of plantedFindings and the three literals
beside them move by three; each of the new values is what the audit
reports.

go build, go vet, the e2e build, the whole unit suite, the harness,
HTTP and stdio transport suites, golangci-lint and its formatters over
the tree, every step of make analyze, the Python script tests, the
bundle manifests jq derives and the site's checks, build and lint pass
over the rewritten tree, and make check-file-headers reports all 3432
source files carrying the header.

Refs #1269

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

Copy link
Copy Markdown

@jmrplens
jmrplens merged commit 0b20816 into main Oct 28, 2026
62 of 91 checks passed
@jmrplens
jmrplens deleted the bp-file-headers 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>
jmrplens added a commit that referenced this pull request Oct 28, 2026
…ition tier stack (#1571)

Refs #1280
Refs #1240

This is the top of the access level and edition tier stack. The four
layers below it, the access levels of #1280 and the three layers that
answer #1240, regenerate nothing under the freshness deferral, and this
layer regenerates once every committed artifact they move. It adds no
behavior of its own: one commit, the regeneration and the R-PATH
declaration that can only land with it.

The stack sits on `main` at `6c9066aef`, onto which I rebased it on 28
October 2026, layer by layer and in order. Every commit is signed and
carries a `Signed-off-by` trailer. The five files the layers add carry
the copyright and license header of #1563, written by `make
fix-file-headers` into the commit that adds each one. `main` took row
140 of the upstream register for gobco, so the two rows the layers add
are 141 and 142. The regeneration was made again on the rebased tree
rather than rebased.

## The regeneration

The layers changed the surface every generated artifact describes. The
access level parameters publish the levels each route accepts as integer
enums, the group access request approval reads the group and the
requester for a Minimal access approval, and the group, member, LDAP
link, group variable, issue, merge request, notification and project
parameters are tier tagged or described by tier.

- The tool snapshots of the individual surface and of the opaque,
compact and full meta schemas, which change on the tools those
parameters reach (`gitlab_access`, `gitlab_group`, `gitlab_member_role`
and `gitlab_project` on the opaque meta surface; `gitlab_admin`,
`gitlab_ci_variable`, `gitlab_issue` and `gitlab_user` as well on the
full one).
- The per-domain tool reference of the access, admin, ci-variable,
group, issue, member-role, merge-request, project and user groups, in
English and Spanish, the four llms files, the token footprint with the
README block it writes, and the testing reference, refreshed with
`-skip-coverage`.
- The action request record and the fine-grained table, which gain the
two optional reads of `access.approve_group` (`GET /groups/:id` and `GET
/groups/:id/members/:user_id`), and the fine-grained permissions
reference that prints them.
- The request inventory, recorded again on the whole unit suite with the
freshness checks on and merged: it gains those two reads and the
`new_epic` flag a group notification settings update now sends, and
nothing else moves.

The site stats, the brand assets, the manifests, the model evaluation
pages, the charts and the e2e coverage page regenerate unchanged, and a
second run of every generator over the committed result changes nothing.

## The R-PATH declaration

R-PATH reads the group the approval fetches as a `GroupDetail` the
package never publishes. I declared it beside the LDAP link listing's
existence probe, as a read the package makes for its own use: it is sent
only when `access.approve_group` is asked for `access_level` 5 on a
licensed session, reads `parent_id` alone to refuse the approval on a
subgroup before it is sent, and returns nothing of the answer. The
declaration lands here because only this commit's inventory holds the
read, and a declaration that answers nothing fails the audit.

## How I tested it

With the freshness checks on, on this head: `go test ./... -count=1`
passes (301 packages), `make analyze` passes every step (markdownlint,
which needs Node, and the file header check, which reads git's file
list, run locally on the same tree), every check the "Generated
artifacts" job of CI runs passes, among them `check-tool-reference`,
`check-llms`, `check-footprint`, `check-site-stats`,
`check-testing-docs`, `check-request-inventory`, `check-file-headers`,
`audit-1to1-paths`, `check-action-grants`,
`check-action-grants-derivation`, `audit-1to1-grants`,
`check-e2e-static` and `check-tenancy`, as do `check-server-json`,
`check-server-json-packages` and `check-openplugin`, `make check-dco`
passes on every commit of the stack, `make test-e2e-http` (780 tests, 8
of them skipped for want of Docker on the runner) and `make
test-e2e-stdio` (277 tests) pass, and the e2e packages compile. The site
passes `i18n:check`, `facts:check`, `chips:check`, `llms:check`, `build`
and `lint`, and `node scripts/check-doc-links.mjs` passes.

Against Docker GitLab 19.5.0 both complete runs passed with the skip
gate on, on this stack as it stood on `e3e09d290` before the rebase:
`make test-e2e-ce` ran 1321 tests with no failure and one skip, the
declared opt-in throttle probe; `make test-e2e-ee`, licensed, ran 1650
tests with no failure and no skip. The scenarios the layers added passed
on all three surfaces: on CE, the Minimal access approval refused on the
Free tier, a level the route does not grant refused with the request
still pending, an Owner-level project access token granted to an Owner's
token, and an issue created for two assignees keeping only the first; on
EE, the same refusal and token, the Minimal access approval refused on a
subgroup and granted on a licensed top-level group, and an instance
custom role on an Owner base refused. I did not run them again after the
rebase. What `main` brought in between does not reach those scenarios'
requests: the header lines are comments, the plain HTTP listener check
of #1561 is read only by an HTTP listener while the suite drives the
binary over stdio, the Orbit fixtures moved for the GitLab.com suite,
the Bitbucket fixture's image moved to 10.5 for the import coverage
behind its own profile, and of the dependency updates of #1564 the
binary links `golang.org/x/net`, whose IDNA package `internal/gitlab`
uses, and the gRPC modules of the OTLP exporter, which runs only with
telemetry turned on; the unit suite and both transport suites above run
on them.

## #1517

#1517 is open, and the maintainer has asked for changes before merging
it. This stack leaves to it everything it does or was asked to do: the
offset paging of the merge request diffs in `mr_review.changes_get`, the
page walk of the inline comment pre-check under RQB-013 with its check
of the position's SHAs against the merge request's `diff_refs`, and the
curated text, request declarations and test moves the review asked for;
nothing here touches `mrchanges`, `mrdiscussions`, `mrdraftnotes`,
`internal/toolutil/diff_position.go` or the tenancy register. Its unused
`order_by`, `sort`, `pagination` and `page_token` are #1530 and the
pre-check's `position_type` is #1531, both after #1517. Two hand-written
files are changed by both, in different blocks, so a rebase of either is
mechanical: `cmd/audit_1to1/internal/paths/sent_declarations.go`, where
this stack adds the access request probe to the existence probes and its
reason after the LDAP one while #1517 adds the merge request rows to the
position pre-check block and their reason, and the
`internal/tools/testdata/tools_meta.json` golden, where this stack
changes the `gitlab_access`, `gitlab_group`, `gitlab_member_role` and
`gitlab_project` entries and #1517 the `gitlab_mr_review` one. Both
change generated files: `tools_individual.json`,
`tools_meta_compact.json` and `tools_meta_full.json`,
`docs/development/request-inventory.json`,
`docs/development/action-requests.json`,
`internal/tools/actiongrants/table_gen.go`,
`docs/development/token-footprint.md` and
`site/src/data/token-footprint.json` with the README token block,
`docs/development/testing/testing.md`, `llms-full.txt`,
`llms-full-individual-tools.txt` and `llms-full-meta-tools.txt`.
Whichever of #1517 and this stack lands second regenerates those files
on top of the other, together with the `mr-review` tool reference, which
only #1517 changes and this stack regenerates unchanged.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
jmrplens added a commit that referenced this pull request Oct 29, 2026
…1582)

Refs #1547

This is the top of the localization stack for #1547. The five layers
below it add the message catalog and `GITLAB_MCP_LANGUAGE` with English
alone, teach `audit_md_escaping` the catalog and add the person-facing
text gate, move every text the server shows a person directly into the
catalog with English unchanged byte for byte, add the Spanish catalog,
and document the setting with ADR-0027. They run their suites under
`GITLAB_MCP_TEST_SNAPSHOT_PARITY=deferred`, and this one regenerates the
committed artifacts once, over the whole stack. It adds no behavior of
its own.

The stack sits on `main` at `d6f108bcd`, onto which I rebased it on 28
October 2026, layer by layer and in order, since it had been built on
`8f339f4e8` and `main` took 24 pull requests since. Every commit is
signed and carries a `Signed-off-by` trailer. Every source file the
layers add, the planted modules of `cmd/audit_i18n` and
`cmd/audit_md_escaping` included, carries the copyright and license
header of #1563, written by `make fix-file-headers` into the commit that
adds it, and the lines the person-facing text gate's findings tree pins
moved down three with it. The gate is now step 33 of `make analyze`,
after the header gate, so the second layer renumbers every step to `/33`
and moves the header gate's Makefile test to `[32/33]`. The language
field and the `AllowPlaintextHTTP` field of #1561 sit side by side in
the HTTP overlay and in the server's flag struct, and the package counts
in `CLAUDE.md` are 312 and 339 after the first layer and 313 and 349
after the second. The regeneration was made on the rebased tree.

## The regeneration

I ran every generator of `make update-all`, with the testing reference
written by `go run ./cmd/gen_testing_docs/ -skip-coverage`. Every other
generator found its output current: the layers move no tool, action,
schema, description, resource or prompt, which is what the stack
promises for anyone who leaves the setting unset, and the documentation
layer already carries what the site pages and `server.json` say about
the setting. The testing reference moves its package, file and test
counts to the ones the stack adds.

## How I checked it

On this branch, with `GITLAB_MCP_TEST_SNAPSHOT_PARITY` unset:

- `go test ./... -count=1` passes over all 313 packages.
- `make analyze` passes its 33 steps: 31 on a copy of the tree without
`.git` or Node, and markdownlint and the copyright and license header
gate in the checkout.
- Every target of CI's `generated` job passes, `check-testing-docs`,
`check-llms`, `check-tool-reference`, `check-site-stats`,
`check-footprint`, `check-action-grants`, `check-i18n`,
`check-md-escaping`, `check-gateway-chars`, `check-surface-quality`,
`check-server-json` and `check-server-json-packages` among them, and so
do `check-dco`, `check-em-dash` and `check-plan-untracked`.
- `make test-e2e-http` (811 tests, the 8 that need Docker skipped) and
`make test-e2e-stdio` (313 tests) pass. Both drive the real binary with
the setting unset, `en`, `EN-GB`, `es`, `es-419` and through the flag,
and ask the `interactive.issue_create` questions in English and in
Spanish, with the property keys, the option values and the listings
unchanged in both.
- The end-to-end suite under `test/e2e/gitlab` and its harness compile
under the `e2e` tag, `go vet -tags e2e` passes on them, and the
harness's own tests pass.
- The site's `i18n:check`, `facts:check`, `chips:check`, `llms:check`,
`build` and `lint` pass, and `scripts/check-doc-links.mjs` finds every
local link and anchor of 352 files.

Each layer below passes on its own as well, checked out alone after the
rebase: `go build ./...`, `go vet ./...` and with the `e2e` tag, the
compile of `test/e2e/gitlab`, `go test ./... -count=1` under
`GITLAB_MCP_TEST_SNAPSHOT_PARITY=deferred`, `make golangci-lint` over
the whole module, the gates each layer's description names (`check-i18n`
from the second layer up, where it exists), `check-file-headers`,
`check-dco`, `check-em-dash`, markdownlint on the Markdown it changes,
`check-doc-links`, and both transport modules: 790 HTTP and 288 stdio
tests on the first two layers, 794 and 292 on the third, 811 and 313 on
the fourth and fifth.

Signed-off-by: José M. Requena Plens <mail@jmrp.io>
jmrplens added a commit that referenced this pull request Oct 29, 2026
…'s diff, publish the pagination of mr_review.changes_get, and say when a prompt read only the first page of changed files (#1584)

An inline discussion or draft note on a file past the first page of a merge request's diff was refused locally as "not in the merge request diff", because the position pre-check read one page of 100 files, and `mr_review.changes_get` returned one page without saying how to continue. Nikita Turchaninov (slowaner) found and fixed both in #1517, and that work is his: the pre-check walks the diff a page at a time and stops at the file, bounded by the new register row RQB-013; it compares the merge request's `diff_refs` with the position before and after the walk; and it leaves the write to GitLab whenever its evidence is incomplete. `changes_get` publishes its pagination, and its Files line reads `N`, `N of total` or `N shown, more available`.

This pull request carries #1517 to land it for 3.4.0, with him as co-author, and I'll close #1517 when it merges. His six commits carry no `Signed-off-by:` trailer, which the Developer Certificate of Origin needs on every commit since #1562, and adding one means rewriting his branch, so I squashed them into one commit of mine on today's `main`, `fix(tools): handle paginated merge request diffs`, with a `Co-authored-by:` trailer naming him, and opened it from a branch of this repository. The merge conflicted only in generated files (the README token block, the testing reference and the token footprint), and I regenerated them from the merged tree rather than taking a side. The branch is rebased on the localization stack (#1577 to #1582), where only the testing reference conflicted, regenerated the same way. #1530 and #1531 stay separate, as agreed in the review of #1517.

## What I added on top

- `internal/tools/mrchanges/action_specs_test.go`, the one file the pull request adds, opens with the copyright and license header #1563 requires, written by `make fix-file-headers`.
- The doc comment of `toolutil.MergeRequestDiffForPositionCheck` says again why skipping the check is safe: the write goes to the same instance with the same credential, so a 401, 403 or 404 on the listing or on the snapshot read is the refusal the write is about to get, and a transient failure, a snapshot that moved or an exhausted page budget leaves the write judged by GitLab alone. Both `validatePosition` comments that send the reader there are true again, and name the snapshot case.
- The same gap in the prompts. The five prompts that read a merge request's changed files (`summarize_mr_changes`, `review_mr`, `suggest_mr_reviewers`, `mr_risk_assessment` and `mr_description_quality`) read one page of 100, as their descriptions have said since issue 1169, but counted it as the whole diff: a review or a risk assessment of a merge request changing 250 files read "Files changed: 100", with the line totals of those 100 and nothing saying the other 150 were never read. Their count now reads as `changes_get`'s does, and when the merge request changes more files a line says that every count and list covers the page alone and that `mr_review.changes_get` reads the rest from page 2 with `per_page` 100. A merge request within one page gets exactly the message it got before. `TestMRDiffPrompts_MoreFilesThanOnePage_SayWhatTheyRead` fails on the parent in every partial case, and the prompt section of the site says the same in English and Spanish. The line stays English under `GITLAB_MCP_LANGUAGE=es`, like the rest of a `prompts/get` body, since the model reads it and ADR-0027 keeps what the model reads out of the message catalog.
- `suggest_mr_reviewers` reads the merge request and its changed files as `review_mr` does, but its exclusion entry named only `project.members`, so an operator who excluded `mr_review.changes_get` or `merge_request.get` was still served the changed paths through it, and with the line above it would have named the excluded action. It now names both, and `TestRegister_ExcludingAnActionRemovesThePromptServingTheSameData` fails without that.

## The other diff routes

I went through every handler that reads a diff for the same gap. Whether GitLab pages a route is what the live record says it takes: only the merge request diffs, the diff versions and the commit diff declare `page` and `per_page`.

| Route | Read by | Paged | Affected |
| --- | --- | --- | --- |
| `GET /projects/:id/merge_requests/:merge_request_iid/diffs` | `mr_review.changes_get` and the position pre-check of `mr_review.discussion_create`, `mr_review.draft_note_create` and `mr_review.draft_note_update` | Yes | Yes, fixed by slowaner's commit |
| The same route | the five prompts above | Yes | Yes, fixed here: still one page, now said in the message |
| `GET /projects/:id/merge_requests/:merge_request_iid/versions` | `mr_review.diff_versions_list` | Yes | No: it takes `page` and `per_page` and publishes its pagination |
| `GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id` | `mr_review.diff_version_get` | No | No |
| `GET /projects/:id/merge_requests/:merge_request_iid/raw_diffs` | `mr_review.raw_diffs` | No | No |
| `GET /projects/:id/repository/commits/:sha/diff` | `repository.commit_diff` | Yes | No: it takes `page` and `per_page` and publishes its pagination |
| `GET /projects/:id/repository/compare` | `repository.compare` and the `generate_release_notes`, `compare_branches` and `audit_commit_hygiene` prompts | No | No |
| `GET /projects/:id/merge_requests/:merge_request_iid/changes` | nothing | No | No |

## How I tested it

On the head, `go test ./... -count=1` passes (303 packages), recording the request inventory on the whole unit suite, and `gen_request_inventory -check` finds the committed inventory current. `golangci-lint` passes on every package the branch touches. `audit-1to1-paths`, `audit-1to1-grants`, `check-action-grants`, `check-action-grants-derivation`, `check-tenancy`, `check-surface-quality`, `check-tool-reference`, `check-meta-descriptions`, `check-action-ids`, `check-sdk-context`, `check-md-escaping`, `check-i18n`, `check-test-file-names`, `check-test-subtests`, `check-test-goroutines`, `check-godocs`, `check-llms`, `check-testing-docs`, `check-footprint`, `check-site-stats`, `check-md-tables`, `check-doc-tool-names`, `check-e2e-static`, `check-action-catalog-manifest`, `audit-catalog-first`, `check-file-headers`, `check-dco` and `check-em-dash` pass, and the three `test/e2e/gitlab` packages compile and vet under the `e2e` tag. Every generator of `make update-all` runs clean on the head, the testing reference with `-skip-coverage`, which its own commit refreshed. The site passes `i18n:check`, `facts:check`, `chips:check` and `llms:check`, and `node scripts/check-doc-links.mjs` passes. I did not run the Docker suites against a GitLab.

<details>
<summary>Design notes from slowaner's description</summary>

The position pre-check searches for the requested old or new path one page at a time and stops at the first match. It retains no earlier pages. Discussion create and draft-note create and update share this lookup and preserve the requested body, position and SHAs.

Before trusting the listing, the helper reads the merge request's `diff_refs` and compares all three SHAs with the position. It reads them again after finding the file or finishing the listing. Both snapshot reads are kept: the second detects a push during the search, so evidence from a newer diff cannot justify a local refusal. A successful lookup costs k+2 reads for a match on page k, plus the write. This remains best effort, since GitLab validates changes after the final read. A changed, missing or unreadable snapshot, a listing error, cancellation, non-advancing pagination or an exhausted page budget makes the pre-check advisory, and GitLab judges the original write. Only a complete, stable result can produce the existing file or line error. A matching file with no available patch skips line validation.

The walk is bounded by RQB-013 at 100 actual list calls of 100 files. A match or completed listing on call 100 is valid; call 101 is never sent. Reaching the bound adds no caller quota and does not reject the write. GitLab documents a maximum `diff_max_files` of 3,000, normally at most 30 pages at 100 files per page; the 100-call bound also ends a malformed walk that keeps advertising another page. GitLab's own diff-size limits still apply.

| Item | Answer |
| --- | --- |
| VAL-001 | Allow: bounds the diff pages one advisory position check may read. |
| VAL-002 | `KeyRequest`; mint cost is nothing. Each invocation has its own finite walk; this is not an allowance accumulated by a caller. |
| VAL-003 | Class P, as computed for a request bound by the register. No class D finding. |
| VAL-004 | `RequestBound`, not a share or fairness guarantee. |
| VAL-005 | No claim to protect a process-wide resource or one population from another, so no process partner applies. |
| VAL-006 | At 100 list calls, a match or terminal page is accepted. If another page is needed, discard incomplete evidence and let GitLab judge the unchanged write. No table is keyed on caller input. |
| VAL-007 | No new refusal, channel, code, HTTP status, `Retry-After` or retry class, on HTTP or STDIO. Existing stable-snapshot position errors remain unchanged. |
| VAL-008 | Counts actual diff-list calls within this lookup only. It does not charge an admission, tenant or server quota, and charges no unjudged write. |
| VAL-009 | Fixed source constant `mergeRequestDiffPositionMaxPages = 100`; 100 files per call. No new setting, flag or MCP parameter, so operator-supplied zero and malformed values do not apply. |
| VAL-010 | Debug diagnostics state why the advisory check was skipped. No new refusal metric or caller/address identity label; existing project/MR context identifies the operation. |
| VAL-011 | The same request bound and advisory fallback apply on STDIO. |
| VAL-012 | No population-protection or fairness claim, so fairness measurement is not applicable. Boundary, early-stop and fallback behavior are covered by regression tests. |

`changes_get` publishes pagination metadata in structured output and Markdown. Guidance describes offset continuation, `next_page` as `page`, with `per_page: 100`. The keyset request option #1517 first added, and its synthetic endpoint tests, were removed during its review, and the existing input fields stay as they are for #1530.

</details>

Co-authored-by: Nikita Turchaninov <2010628+slowaner@users.noreply.github.com>
jmrplens added a commit that referenced this pull request Nov 1, 2026
…the PyPI and Homebrew packages (#1696)

Closes #1591

#1563 gave every tracked source file the copyright and license header
and made the Go generators and the create-mcp-tool templates write it.
This closes the places it left out, which create source outside the tree
`make check-file-headers` judges.

Five Go templates still showed a new file opening with its package
clause: the minimal `doc.go` and the `action_specs.go` of the
go-safe-move-refactor skill, the `action_specs.go` of the
modularize-go-package skill, and the two files of the sub-package
example in `docs/development/development.md`. Each now opens with the
two header lines and a blank line, which in the `doc.go` template keeps
the header from being read as the package comment. The forwarding stubs
of that skill's step 5 showed the file with a path comment right above
`package tools` and no header, which would fail the gate and `make
check-godocs`; it keeps its header now. In `development.md` the file
paths moved out of the code blocks into the sentences before them.

Two release scripts write source into what they publish, and neither
carried the header: `scripts/build_pypi.py` writes `__init__.py` and
`__main__.py` into every wheel, and `scripts/update-homebrew-tap.sh`
writes the Ruby formula the tap serves. Each now opens it with a
`FILE_HEADER` of its own, spelled on one line on purpose: the gate reads
each line of a script alone, so a line of a triple-quoted string or a
heredoc opening with the notice would count as a second copy of the
script's own header. In the formula the header comes first, where the
gate puts it in a Ruby file with no shebang, and the `typed` and
`frozen_string_literal` magic comments follow; Ruby reads those in any
comment before the first token.

No gate reads either output, so a test of each script holds it to the
header the script opens with below its shebang, the one the gate ties to
`cmd/internal/fileheader`, and a change of holder or year moves the
oracle with the tree.
`test_the_package_modules_open_with_the_file_header` builds the six
wheels and reads both modules of each, which must still compile.
`test_the_formula_opens_with_the_file_header` renders the formula and
runs its leading comments under Ruby, which freezes a literal only while
the magic comment is read. Both fail on main and pass here.

`docs/development/file-headers.md` names the wheel modules and the
formula among the generated source, and its section on changing the
holder or the year lists the copies the gate does not read. Item 13 of
#1114 ports `build_pypi.py` to Go; if that lands later, the port writes
the header from the `fileheader` constants.

I checked it with every Python script test, shellcheck, `make
check-file-headers`, `check-dco`, `check-em-dash`,
`check-test-subtests`, `check-test-goroutines`, `check-godocs`,
`check-action-ids`, `check-surface-quality`, markdownlint and
`format_md_tables --check` on the touched pages, and
`scripts/check-doc-links.mjs`. No Go package changes.

---------

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 distribution Release artifacts, packaging, and install channels e2e The end-to-end suite and its Docker fixtures mcp MCP capabilities beyond tools: resources, prompts, completions, subscriptions, elicitation platform Windows and macOS specific source, and the cross-platform CI leg security Security-related issue site The documentation site's build, components and scripts, not its content telemetry OpenTelemetry export and the MCP semantic conventions tooling The audit and generator commands under cmd/, and the Makefile targets that run them transport stdio and HTTP transports, the server process, and the transport e2e modules v3.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant