Context / problem
When an attestation runs as part of a pull request / merge request, Chainloop today treats it the same as a build on main/a release. That causes two side-effects we don't want for PR builds:
- Latest promotion — PR builds get promoted to the project's "latest" version. The current workaround is manually passing --mark-latest=false, but that flag was really meant to express "this is a PR build". We're overloading a low-level flag to express a higher-level concept.
- Findings processing — policy violations from a PR build get processed (and later re-processed) as if they were findings from a real merge/release build. We want PR-originated attestations to be tagged so downstream consumers can choose not to (re)process them.
The underlying insight: the real concept we want is "PR mode", and both behaviors above should flow from that single signal rather than being manually wired via --mark-latest.
Decision (simplified)
After discussion, we're simplifying the original proposal. Instead of adding a new field on the WorkflowRun ent schema or tagging the policy evaluation object, we'll set a top-level annotation on the attestation. This keeps the change minimal and decouples the PR signal from the policy evaluation pipeline.
Well-known annotation key: chainloop.dev/is-pull-request with value "true".
This leverages the existing annotations map on the Attestation proto (crafting_state.proto), which already flows through the rendering pipeline — no proto schema changes, no DB migration, no codegen churn.
Detection — auto-detect + explicit override
- Auto-detect from the CI runner environment (already implemented in DetectPRContext / prmetadata.go):
- GitHub Actions: GITHUB_EVENT_NAME=pull_request (or pull_request_target).
- GitLab: CI_PIPELINE_SOURCE=merge_request_event / CI_MERGE_REQUEST_IID.
- Dagger: delegates to the parent CI env vars.
- Explicit --pr / --pr=false flag on chainloop attestation init overrides auto-detection (zero-config in CI, escapable for edge cases / self-hosted / generic runner).
Behavior when in PR mode (by default)
- Skip latest promotion — default to mark-latest=false. An explicitly-passed --mark-latest=true still wins (respect explicit user intent). This replaces the manual --mark-latest=false workaround.
- Set the chainloop.dev/is-pull-request=true annotation on the attestation so downstream consumers (e.g. findings processing) can detect PR-originated attestations and skip (re)processing.
Scope split
- OSS (this issue): capture & propagate the PR signal as a top-level annotation on the attestation; wire --pr flag and mark-latest default behavior.
- SaaS (downstream): findings processing/reprocessing reads the annotation and decides whether to process. Not in scope for this issue.
Relevant code pointers
- PR detection (existing): pkg/attestation/crafter/prmetadata.go (DetectPRContext), pkg/attestation/crafter/collector_prmetadata.go.
- Runner env vars: pkg/attestation/crafter/runners/githubaction.go, gitlabpipeline.go, daggerpipeline.go.
- CLI flags: app/cli/cmd/attestation_init.go (--mark-latest).
- Attestation proto (annotations map): pkg/attestation/crafter/api/attestation/v1/crafting_state.proto (Attestation.annotations, field 5).
- Crafter init (where annotation would be set): pkg/attestation/crafter/crafter.go (initialCraftingState).
- Action layer: app/cli/pkg/action/attestation_init.go.
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri
Context / problem
When an attestation runs as part of a pull request / merge request, Chainloop today treats it the same as a build on main/a release. That causes two side-effects we don't want for PR builds:
The underlying insight: the real concept we want is "PR mode", and both behaviors above should flow from that single signal rather than being manually wired via --mark-latest.
Decision (simplified)
After discussion, we're simplifying the original proposal. Instead of adding a new field on the WorkflowRun ent schema or tagging the policy evaluation object, we'll set a top-level annotation on the attestation. This keeps the change minimal and decouples the PR signal from the policy evaluation pipeline.
Well-known annotation key: chainloop.dev/is-pull-request with value "true".
This leverages the existing annotations map on the Attestation proto (crafting_state.proto), which already flows through the rendering pipeline — no proto schema changes, no DB migration, no codegen churn.
Detection — auto-detect + explicit override
Behavior when in PR mode (by default)
Scope split
Relevant code pointers
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri