Symptom
TestAttestGitlabPRCommandTestSuite fails 10/10 subtests. Every failure is the same rejected payload:
Error: Input payload validation failed: map[
data_json.pull_requests[0].FoundPullRequestV1.author: Extra inputs are not permitted
data_json.pull_requests[0].FoundPullRequestV1.title / base_ref / head_ref / created_at / merged_at: Extra inputs are not permitted
data_json.pull_requests[0].FoundPullRequestV2.commits: Field required
]
First seen in CI on 2026-08-10 (run 31365784844, on an unrelated docs PR #1080). The previous run, 2026-08-04 (run 30917903429), was green on identical code.
Root cause
- GitLab now returns
200 [] from GET /projects/:id/merge_requests/:iid/commits for the test's fixture MR (kosli-dev/merkely-gitlab-demo !1, merged 2024-10-10).
GetMergeRequestCommits (internal/gitlab/gitlab.go:166) returns an empty slice with err == nil, so nothing aborts.
PREvidence.Commits is tagged json:"commits,omitempty" (internal/types/types.go:14), so the empty slice is dropped from the JSON entirely.
- The API validates
pull_requests against List[FoundPullRequestV1 | FoundPullRequestV2]. V1 forbids the extended fields; V2 requires commits. Matching neither, both branches are reported.
commits has been required on FoundPullRequestV2 since the model was introduced (server, 2025-07-03); nothing changed server-side.
Why it changed now
GitLab deleted merge-request commit rows for old merge requests some time between 2026-08-04 14:22 UTC and 2026-08-10 07:33 UTC.
Evidence it was present on 2026-08-04: the green run's coverage reports GetMergeRequestCommits at 81.2%. The function has 16 statements across 9 blocks; a successful call that iterates commits covers 13 (81.25%), while a call returning [] covers only 7 (43.75%). The loop body ran.
The retention boundary, bisected against gitlab-org/gitlab (project 278964) anonymously:
| MR |
merged_at |
/commits |
| !211375 |
2025-11-19 |
[] |
| !214116 |
2025-11-25T20:21Z |
[] |
| !214312 |
2025-11-26T23:57Z |
3 commits |
| !214582 |
2025-11-28 |
1 commit |
| !249198 |
2026-08-10 |
1 commit |
Cutoff: between 2025-11-25T20:21Z and 2025-11-26T23:57Z. Merge requests whose diff predates it return []; newer ones return commits. Reproducible on GitLab's own repository, so it is not specific to our project or token.
The merge_request_diffs record survives — /merge_requests/1/versions still reports state: "collected", real_size: "1" — and the head commit is still readable via /repository/commits/:sha. So this looks like loss of merge_request_diff_commits rows, not the diff-file pruning described in gitlab-foss#37639, which explicitly says those rows should be retained.
Unknown: whether the deletion was one-off or is a rolling window. If rolling, the affected range grows over time. Re-probing !212000 and !214582 in a week would tell.
Customer impact
Low. kosli attest pullrequest gitlab normally runs immediately after a merge, when the MR diff is minutes old and well inside the retained window. It fails only when attesting a commit whose MR diff predates the cutoff — backfills, replays, or re-runs of historical pipelines. kosli assert pullrequest gitlab uses the same V2 path but posts no attestation, so it is unaffected.
Proposed fix
- Drop
omitempty from Commits in internal/types/types.go:14 so the field always serialises. commits: [] validates against FoundPullRequestV2, and the server's set_relevant_commits validator handles an empty list (the merge commit is still recorded).
- Decide whether a zero-commit MR is legitimate. If not,
GetMergeRequestCommits should error explicitly rather than silently attest an MR with no commits.
- Normalise
Approvers from nil to []any{} in newPRGitlabEvidenceV2, as the V1 path already does (internal/gitlab/gitlab.go:95). The API tolerates null today, so this is consistency, not a fix.
- Add a serialisation test asserting
commits is always present in a V2 payload.
- Stop pinning a 2024 MR in the test fixture. The suite depends on GitLab's live answer for a two-year-old merge request staying constant; it didn't, and that fixture can never return commits again. Either use a fake (the repo already uses
httpfake elsewhere) or a fixture that is maintained.
PREvidence is shared by GitHub, Bitbucket and Azure, so the omitempty defect is latent wherever a provider can return zero commits.
Symptom
TestAttestGitlabPRCommandTestSuitefails 10/10 subtests. Every failure is the same rejected payload:First seen in CI on 2026-08-10 (run 31365784844, on an unrelated docs PR #1080). The previous run, 2026-08-04 (run 30917903429), was green on identical code.
Root cause
200 []fromGET /projects/:id/merge_requests/:iid/commitsfor the test's fixture MR (kosli-dev/merkely-gitlab-demo!1, merged 2024-10-10).GetMergeRequestCommits(internal/gitlab/gitlab.go:166) returns an empty slice witherr == nil, so nothing aborts.PREvidence.Commitsis taggedjson:"commits,omitempty"(internal/types/types.go:14), so the empty slice is dropped from the JSON entirely.pull_requestsagainstList[FoundPullRequestV1 | FoundPullRequestV2]. V1 forbids the extended fields; V2 requirescommits. Matching neither, both branches are reported.commitshas been required onFoundPullRequestV2since the model was introduced (server, 2025-07-03); nothing changed server-side.Why it changed now
GitLab deleted merge-request commit rows for old merge requests some time between 2026-08-04 14:22 UTC and 2026-08-10 07:33 UTC.
Evidence it was present on 2026-08-04: the green run's coverage reports
GetMergeRequestCommitsat 81.2%. The function has 16 statements across 9 blocks; a successful call that iterates commits covers 13 (81.25%), while a call returning[]covers only 7 (43.75%). The loop body ran.The retention boundary, bisected against
gitlab-org/gitlab(project 278964) anonymously:/commits[][]Cutoff: between 2025-11-25T20:21Z and 2025-11-26T23:57Z. Merge requests whose diff predates it return
[]; newer ones return commits. Reproducible on GitLab's own repository, so it is not specific to our project or token.The
merge_request_diffsrecord survives —/merge_requests/1/versionsstill reportsstate: "collected",real_size: "1"— and the head commit is still readable via/repository/commits/:sha. So this looks like loss ofmerge_request_diff_commitsrows, not the diff-file pruning described in gitlab-foss#37639, which explicitly says those rows should be retained.Unknown: whether the deletion was one-off or is a rolling window. If rolling, the affected range grows over time. Re-probing !212000 and !214582 in a week would tell.
Customer impact
Low.
kosli attest pullrequest gitlabnormally runs immediately after a merge, when the MR diff is minutes old and well inside the retained window. It fails only when attesting a commit whose MR diff predates the cutoff — backfills, replays, or re-runs of historical pipelines.kosli assert pullrequest gitlabuses the same V2 path but posts no attestation, so it is unaffected.Proposed fix
omitemptyfromCommitsininternal/types/types.go:14so the field always serialises.commits: []validates againstFoundPullRequestV2, and the server'sset_relevant_commitsvalidator handles an empty list (the merge commit is still recorded).GetMergeRequestCommitsshould error explicitly rather than silently attest an MR with no commits.Approversfrom nil to[]any{}innewPRGitlabEvidenceV2, as the V1 path already does (internal/gitlab/gitlab.go:95). The API toleratesnulltoday, so this is consistency, not a fix.commitsis always present in a V2 payload.httpfakeelsewhere) or a fixture that is maintained.PREvidenceis shared by GitHub, Bitbucket and Azure, so theomitemptydefect is latent wherever a provider can return zero commits.