Skip to content

feat: capture commit signature and PR base_ref in pull_request attestations#959

Merged
AlexKantor87 merged 5 commits into
mainfrom
claude/5892-pr-signature-baseref
Jun 16, 2026
Merged

feat: capture commit signature and PR base_ref in pull_request attestations#959
AlexKantor87 merged 5 commits into
mainfrom
claude/5892-pr-signature-baseref

Conversation

@AlexKantor87

@AlexKantor87 AlexKantor87 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Producer side of kosli-dev/server#5892 — populates the new V2 pull_request attestation fields across all four SCM providers. Server counterpart: kosli-dev/server#5898 (merged).

Ready — server-first ordering satisfied. The server schema (kosli-dev/server#5898, commit 91bcb8f) is now deployed to both kosli prods (prod-aws and prod-us-aws), so FoundPullRequestV2 accepts base_ref. CI is green against staging, which also carries the change.

Release note for cutting the next cli version: base_ref is rejected (extra_forbidden) by any server still pre-#5898. Self-hosted customers must upgrade the server before the cli (server-first) — worth calling out in the release notes.

What

Adds three optional fields to pull_request attestation evidence:

  • commit-level verified (*bool) + signature_state (*string)
  • PR-level base_ref

verified is tri-state: nil = unsigned/unknown, false = present-but-invalid, true = verified — so unsigned commits stay distinct from invalid signatures.

Provider coverage

Field GitHub GitLab Bitbucket Azure
verified / signature_state ✅ inline GraphQL ✅ +1 call/commit ❌ not in API ❌ not in SDK
base_ref ✅ (raw refs/heads/…)
  • GitHubsignature { isValid state } on the commit node + baseRefName, in both GraphQL paths.
  • GitLabTargetBranchbase_ref; per-commit GetGPGSignature, where a 404 means unsigned (handled, not fatal); other errors propagate.
  • Bitbucketdestination.branch.namebase_ref; per-commit signatures not exposed by the API.
  • AzureTargetRefNamebase_ref, kept raw (refs/heads/…) to match head_ref; per-commit signatures not exposed by the SDK.

All new fields are optional/omitempty, so other providers and existing consumers are unaffected.

Tests

Pure mappers unit-tested: GitHub buildPREvidence (signed / invalid / unsigned / base_ref), GitLab resolveGitlabSignature (404 / error / verified / unverified) + gitlabMRRefs, Bitbucket bitbucketBranchName, Azure azurePRRefs. go build ./..., go vet, and golangci-lint all clean. The full integration suite runs in CI.

🤖 Generated with Claude Code

AlexKantor87 and others added 5 commits June 15, 2026 15:46
Adds the GitHub producer side for the V2 pull_request attestation fields
added on the server (kosli-dev/server#5892):

- types.Commit gains Verified (*bool) and SignatureState (*string),
  mapped from the GraphQL commit signature { isValid, state }. An unsigned
  commit (nil signature node) leaves both nil, keeping "unsigned" distinct
  from a present-but-invalid signature (verified=false).
- types.PREvidence gains BaseRef, mapped from the PR baseRefName, in both
  GraphQL paths (PREvidenceByPRNumber and PREvidenceForCommitV2).

All new fields are optional/omitempty, so other providers and existing
consumers are unaffected. GitLab, Bitbucket and Azure are follow-up slices
(Bitbucket and Azure cannot supply per-commit signatures).

Requires the server schema change to deploy first, since base_ref lands on
FoundPullRequestV2 (extra="forbid").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
base_ref is mapped from the MR TargetBranch. Per-commit signatures are fetched via GetGPGSignature; GitLab returns 404 for unsigned commits, which is treated as unsigned (verified left nil) rather than failing the attestation, while other errors propagate. The pure gitlabCommitVerification mapper (verified only when status=="verified") is unit-tested. Part of kosli-dev/server#5892.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
base_ref is mapped from destination.branch.name. Bitbucket Cloud does not expose per-commit signature verification, so verified/signature_state are omitted. Part of kosli-dev/server#5892.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
base_ref is mapped from TargetRefName, kept raw (refs/heads/...) to match the existing head_ref. Azure DevOps does not expose per-commit signature verification, so verified/signature_state are omitted. Part of kosli-dev/server#5892.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract small pure seams from the provider PR-evidence builders so the new
server#5892 mappings are unit-testable, and add the tests. No behaviour change.

- GitLab: resolveGitlabSignature isolates the GetGPGSignature result handling —
  404 -> unsigned (nil), other errors propagate, otherwise map
  verification_status. Covered for all four outcomes. gitlabMRRefs maps the
  head/base branch names.
- Bitbucket: bitbucketBranchName extracts the source/destination branch name
  (used for both head_ref and base_ref); covered.
- Azure: azurePRRefs maps the raw head/base ref names; covered.

Part of kosli-dev/server#5892.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AlexKantor87's task in 3m 24s —— View job


Claude PR Review

  • Static analysis (go build, go vet, go mod tidy)
  • Review changed files
  • Post review feedback

Reviewed the producer-side changes that populate verified / signature_state / base_ref across the four SCM providers. This is a clean, well-tested PR. Summary below; two non-blocking notes posted inline.

Static analysis ✅

  • go build ./... — clean
  • go vet ./... — clean
  • go mod tidy → no changes to go.mod / go.sum

(The full integration suite needs the local Kosli server + provider tokens, so it runs in CI — the new logic is exercised by pure-mapper unit tests, which is exactly the right testability split.)

Strengths 👍

  • Tri-state verified is modeled correctly. *bool + omitempty means nil (unsigned/unknown) is omitted, while &false (present-but-invalid) and &true are both serialized — a plain bool would have collapsed false into "unset". The distinction is explicitly tested (TestBuildPREvidence_Unsigned… vs …RecordsCommitSignature). Good call.
  • GitLab 404-means-unsigned handling is isolated in resolveGitlabSignature and unit-tested for all four outcomes (404 → nil, non-404 → propagate, verified, unverified). Clear, well-commented, and the rationale references server#5892.
  • Shared buildPREvidence keeps the two GitHub GraphQL paths (by-commit and by-PR-number) consistent — base_ref and signatures are populated identically in both, so the hybrid fallback doesn't drop fields.
  • Small, pure mapper functions (gitlabMRRefs, azurePRRefs, bitbucketBranchName, gitlabCommitVerification) make the new behavior trivially testable without HTTP fakes.
  • All new fields are omitempty, so other providers/consumers are unaffected. Migration story is sound.

Notes (non-blocking)

  1. base_ref shape differs by provider — Azure sends refs/heads/main, the others send main. Intentional and self-consistent with head_ref, but a "merged into main" policy must handle both forms. Flagged inline on azure.go — worth confirming the policy/server layer normalizes or documenting it.
  2. GitLab per-commit signature fetch is N+1 and can fail the whole attestation on a non-404 error. Deliberate (security intent), but a flaky signature endpoint can now abort an otherwise-valid pull_request attestation. Flagged inline on gitlab.go.
  3. Minor — signature_state vocabulary differs by provider: GitHub emits GraphQL states (VALID, INVALID, …), GitLab emits its verification_status (verified, unverified, …). The verified bool normalizes the semantics, so this is only relevant if a consumer reads the raw string — just be aware downstream.

Draft status

Correctly marked draft: this sends base_ref, which FoundPullRequestV2 (extra="forbid") only accepts once server #5898 is deployed to prod. Good that the dependency is called out in the description — hold the merge until the server is live.

Nice work — the design decisions are sound and the test coverage on the pure mappers is exactly where it should be.
· claude/5892-pr-signature-baseref

Comment thread internal/azure/azure.go
Comment thread internal/gitlab/gitlab.go
@AlexKantor87 AlexKantor87 marked this pull request as ready for review June 16, 2026 06:50
@AlexKantor87 AlexKantor87 enabled auto-merge (squash) June 16, 2026 07:07
@AlexKantor87 AlexKantor87 merged commit 1a74c56 into main Jun 16, 2026
34 of 36 checks passed
@AlexKantor87 AlexKantor87 deleted the claude/5892-pr-signature-baseref branch June 16, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants