Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
80a0779
chore: add verification workflow for AI policy enforcement
Sapthagiri777 Feb 4, 2026
fb7dec9
chore: improve verification workflow based on review feedback
Sapthagiri777 Feb 4, 2026
f057251
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 5, 2026
77e5b37
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 6, 2026
a34738c
fix: address critical SHA mismatch and security issues
Sapthagiri777 Feb 8, 2026
f7a823e
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 8, 2026
9d2ca8a
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 9, 2026
91caeb9
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 10, 2026
6b9356a
Update .github/workflows/verify-pr.yml
Sapthagiri777 Feb 10, 2026
1cb1469
fix: address remaining Copilot review feedback
Sapthagiri777 Feb 10, 2026
851a1eb
fix: check both PIPESTATUS values in test-with-log
Sapthagiri777 Feb 10, 2026
8dab0c6
Update .github/workflows/verify-pr.yml
Sapthagiri777 Feb 10, 2026
79a53cd
fix: use secret gist by default for privacy
Sapthagiri777 Feb 10, 2026
0afae3b
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 10, 2026
850c065
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 10, 2026
53de728
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 10, 2026
c446afa
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 11, 2026
233ee84
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 11, 2026
2da63c8
refactor: simplify verification workflow per maintainer feedback
Sapthagiri777 Feb 11, 2026
2d55221
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 11, 2026
a928cb1
fix: add license headers to scripts
Sapthagiri777 Feb 11, 2026
cdeb3d1
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 12, 2026
9eaece3
Merge branch 'main' into add-ai-verification-workflow
Sapthagiri777 Feb 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions .github/workflows/verify-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Copyright (c) 2026 The Jaeger Authors.
# SPDX-License-Identifier: Apache-2.0
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

This is the only workflow file in the repository with a copyright header. All other workflow files in .github/workflows/ lack copyright headers. Consider removing this header to maintain consistency with the established pattern in the codebase.

Copilot uses AI. Check for mistakes.

# This workflow verifies that contributors have run tests locally.
# It uses pull_request_target to run for PRs from forks.
# New contributors must provide a Test-Gist proving tests were run.
#
# Note: Only the HEAD commit is checked. If a PR has multiple commits,
# only the latest commit needs the trailers.

name: Verify PR

on:
pull_request_target:
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need pull_request_target instead of just pull_request?

types: [opened, synchronize, reopened]
Comment on lines +3 to +5
Copy link
Contributor

Choose a reason for hiding this comment

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

Critical workflow event mismatch: The workflow uses pull_request but the PR description explicitly states it should use pull_request_target to work with PRs from forks. With pull_request, the workflow runs in the context of the fork with limited permissions and may not have access to check out code properly or access secrets. This breaks the stated functionality.

on:
  pull_request_target:
    types: [opened, synchronize, reopened]

Security note: When changing to pull_request_target, be aware that this runs in the context of the base repository with write permissions, so the checkout at line 23 is correct (checking out the PR head explicitly), but ensure the verify-pr.sh script doesn't execute any untrusted code from the PR.

Suggested change
on:
pull_request:
types: [opened, synchronize, reopened]
on:
pull_request_target:
types: [opened, synchronize, reopened]

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.


Comment on lines 3 to 6
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The workflow is missing an explicit permissions declaration. When using pull_request_target, it's a security best practice to explicitly restrict permissions to the minimum needed. Other workflows in this repository (e.g., .github/workflows/pr-quota-manager.yml and .github/workflows/waiting-for-author.yml) follow this pattern by declaring permissions.

Since this workflow only reads the PR and doesn't write anything, you should add a permissions block that restricts access. For example:

permissions:
  contents: read
  pull-requests: read

Copilot uses AI. Check for mistakes.
Comment on lines 3 to 6
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

This workflow lacks a 'concurrency' configuration, which means multiple runs for the same PR could execute simultaneously if new commits are pushed quickly. Most other workflows in the repository (e.g., ci-unit-tests.yml) include concurrency control with 'cancel-in-progress: true' to cancel older runs when new commits are pushed. Consider adding a concurrency configuration to avoid redundant runs and save CI resources.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +6
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The workflow is configured for pull_request, but the PR description says enforcement must run on pull_request_target to work reliably for fork PRs/new contributors. If the intent is technical enforcement for all PRs, switch the trigger to pull_request_target and adjust the checkout/verification accordingly (see separate comment about checking out PR code).

Copilot uses AI. Check for mistakes.
# Explicit permissions for security with pull_request_target
permissions:
contents: read
pull-requests: read

jobs:
verify:
runs-on: ubuntu-latest
steps:
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Other workflows in this repo commonly include step-security/harden-runner to audit/block egress, and this workflow performs outbound network calls (curl to gists / api.github.com). Adding harden-runner here would align with the repo’s security posture and reduce risk, especially since this runs on pull_request_target.

Suggested change
steps:
steps:
- name: Harden GitHub Runner
uses: step-security/harden-runner@v2
with:
egress-policy: audit

Copilot uses AI. Check for mistakes.
- name: Harden GitHub Runner
uses: step-security/harden-runner@v2
with:
egress-policy: audit
Comment on lines 15 to 18
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Other workflows pin step-security/harden-runner to an immutable commit SHA (e.g., .github/workflows/ci-unit-tests.yml:25), but this workflow uses the floating @v2 tag. For supply-chain safety and consistency, pin step-security/harden-runner to a commit SHA here as well.

Copilot uses AI. Check for mistakes.

- name: Checkout PR head
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The checkout action version is inconsistent with the rest of the codebase. Most workflows use actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0, but this uses actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2. For consistency and to use the latest stable version, consider updating to v6.0.0 like other workflows.

Copilot uses AI. Check for mistakes.
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 1
Comment on lines 20 to 24
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The checkout uses fetch-depth: 1 which creates a shallow clone with only the most recent commit. While this is good for performance, it means that if a PR contains multiple commits, only the HEAD commit's message will be checked.

If a contributor made multiple commits and only the last one has the Tested-By trailer, the workflow will pass even though earlier commits don't have it. This might be intentional (only checking the latest state), but it's worth considering if you want to check all commits in the PR. If the intent is to only check the HEAD commit, this is fine, but it should perhaps be documented.

Copilot uses AI. Check for mistakes.
Comment on lines 20 to 24
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

Using pull_request_target with actions/checkout@v4 to check out the PR head (line 21) creates a potential security risk. The workflow runs with write permissions to the repository (by default with pull_request_target), but checks out and executes code from an untrusted fork.

While this workflow only runs shell commands to check commit messages and doesn't execute code from the PR, it's still a security concern because:

  1. The checkout itself could be exploited if there are malicious git hooks or submodules
  2. This sets a precedent that could be copied incorrectly in other workflows

Since this workflow only needs to read the commit message, consider using the GitHub API via actions/github-script or gh api to fetch commit information instead of checking out the code. Alternatively, if checkout is necessary, ensure the permissions block explicitly restricts write access (as mentioned in a separate comment).

Copilot uses AI. Check for mistakes.
Comment on lines 20 to 24
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

This repo appears to pin GitHub Actions to full commit SHAs (e.g., actions/checkout@... # v6.0.0) in other workflows. Here actions/checkout@v4 is unpinned, which weakens supply-chain security and is inconsistent with the established pattern. Pin actions/checkout to a specific commit SHA (with an inline version comment) like the other workflows.

Copilot uses AI. Check for mistakes.

- name: Get author association
id: author
run: |
ASSOCIATION="${{ github.event.pull_request.author_association }}"
echo "association=$ASSOCIATION" >> $GITHUB_OUTPUT

# Trusted contributors: OWNER, MEMBER, COLLABORATOR
# Note: CONTRIBUTOR (people with previously merged PRs but not members)
# are still required to provide Test-Gist proof.
if [[ "$ASSOCIATION" == "OWNER" || "$ASSOCIATION" == "MEMBER" || "$ASSOCIATION" == "COLLABORATOR" ]]; then
echo "trusted=true" >> $GITHUB_OUTPUT
else
echo "trusted=false" >> $GITHUB_OUTPUT
fi

echo "Author association: $ASSOCIATION"

- name: Check for Tested-By trailer
id: tested-by
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
echo "Commit message:"
echo "$COMMIT_MSG"
echo "---"

if echo "$COMMIT_MSG" | grep -q "Tested-By:"; then
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Trailer detection/parsing is currently done via substring grep on the whole commit message. This can be fooled by mentioning Tested-By: / Test-Gist: in the body and may mis-parse values. Consider parsing trailers via git interpret-trailers --parse (or at least matching ^Tested-By: / ^Test-Gist: and extracting the value from the parsed trailer block) to align with the documented requirement of commit trailers.

Suggested change
if echo "$COMMIT_MSG" | grep -q "Tested-By:"; then
TRAILERS=$(printf '%s\n' "$COMMIT_MSG" | git interpret-trailers --parse)
echo "Parsed trailers:"
echo "$TRAILERS"
echo "---"
if echo "$TRAILERS" | grep -qE '^Tested-By:[[:space:]]*'; then

Copilot uses AI. Check for mistakes.
echo "✅ Found Tested-By trailer"
echo "found=true" >> $GITHUB_OUTPUT
else
echo "❌ Missing Tested-By trailer"
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The workflow checks for "Tested-By:" using a simple grep (line 62), but doesn't validate the format of the trailer. If a user has empty Git config values or malformed data, the trailer could be invalid but still pass this check (e.g., "Tested-By: <>" would match). Consider adding validation to ensure the trailer follows the expected format "Tested-By: Name " to catch configuration issues early.

Suggested change
if echo "$COMMIT_MSG" | grep -q "Tested-By:"; then
echo "✅ Found Tested-By trailer"
echo "found=true" >> $GITHUB_OUTPUT
else
echo "❌ Missing Tested-By trailer"
# Require a properly formatted Tested-By trailer: Tested-By: Name <email>
if echo "$COMMIT_MSG" | grep -Eq '^Tested-By: .+ <.+@.+>$'; then
echo "✅ Found Tested-By trailer with valid format"
echo "found=true" >> $GITHUB_OUTPUT
else
echo "❌ Missing or malformed Tested-By trailer (expected: Tested-By: Name <email>)"

Copilot uses AI. Check for mistakes.
echo "found=false" >> $GITHUB_OUTPUT
fi
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

grep -q "Tested-By:" will pass even if the string appears in the middle of the commit message or without any identity details. Since the policy requires a commit trailer, consider matching a trailer-form line (e.g., anchored ^Tested-By: .+ <.+>$) and ideally only within the trailer block at the end of the message.

Copilot uses AI. Check for mistakes.

- name: Check for Test-Gist trailer (new contributors only)
id: test-gist
if: steps.author.outputs.trusted == 'false'
run: |
Copy link
Member

Choose a reason for hiding this comment

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

Move all this logic to a script where it can be easily tested. You can even write unit tests for the script (see scripts/utils/compute-tags.test.sh).

COMMIT_MSG=$(git log -1 --pretty=%B)
# Use tree SHA - it represents the code content and doesn't change with commit amend
PR_TREE_SHA=$(git rev-parse HEAD^{tree})

# Extract Test-Gist URL
GIST_URL=$(echo "$COMMIT_MSG" | grep "Test-Gist:" | awk '{print $2}' | head -1)

if [ -z "$GIST_URL" ]; then
echo "❌ Missing Test-Gist trailer. New contributors must run 'make verify-with-proof' before pushing."
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi

echo "Found Test-Gist URL: $GIST_URL"

# Extract Gist ID from URL (handles both 20 and 32 character IDs)
GIST_ID=$(echo "$GIST_URL" | grep -oE '[0-9a-f]{20,32}' | tail -1)

if [ -z "$GIST_ID" ]; then
echo "❌ Could not extract Gist ID from URL: $GIST_URL"
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi

echo "Gist ID: $GIST_ID"

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The script uses 'jq' to parse JSON from the GitHub API response, but doesn't verify that jq is installed before using it. While jq is typically pre-installed on ubuntu-latest runners, it's better to be explicit about dependencies. Consider either adding a check for jq availability at the start of the step, or documenting that the runner requires jq.

Suggested change
# Ensure jq is available before attempting to parse the GitHub API response
if ! command -v jq >/dev/null 2>&1; then
echo "❌ 'jq' is required but not installed on this runner."
echo " Please ensure the runner environment includes 'jq' (e.g., install it before running this workflow)."
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi

Copilot uses AI. Check for mistakes.
# Fetch Gist content securely using GitHub API (not user-provided URL)
GIST_CONTENT=$(curl -s "https://api.github.com/gists/$GIST_ID" | jq -r '.files | to_entries[0].value.content // empty')
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The curl command on line 101 fetches Gist content from the GitHub API without authentication. While this may work for public Gists, it could hit rate limits or fail for private Gists. Consider using authenticated requests via curl -H "Authorization: Bearer ${{ github.token }}" ... to avoid rate limiting issues, especially if multiple PRs are being verified simultaneously.

Copilot uses AI. Check for mistakes.

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

This workflow calls the GitHub API without authentication. That is susceptible to low unauthenticated rate limits and can cause flaky verification failures. Please pass Authorization: Bearer ${{ github.token }} (or set GITHUB_TOKEN and use it) when calling the API.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The curl command doesn't check for HTTP errors. If the GitHub API returns an error (404, 403, 500, etc.), the command will succeed but GIST_CONTENT will be empty or contain error JSON. Consider adding -f flag to curl (fail on HTTP errors) or check the HTTP response code to provide more specific error messages like "Gist not found" vs "API error".

Suggested change
GIST_CONTENT=$(curl -s "https://api.github.com/gists/$GIST_ID" | jq -r '.files | to_entries[0].value.content // empty')
GIST_API_URL="https://api.github.com/gists/$GIST_ID"
GIST_RESPONSE_FILE="$(mktemp)"
HTTP_STATUS=$(curl -sS -w "%{http_code}" -o "$GIST_RESPONSE_FILE" "$GIST_API_URL")
if [ "$HTTP_STATUS" -eq 404 ]; then
echo "❌ Gist not found for ID: $GIST_ID (HTTP 404)"
echo "valid=false" >> $GITHUB_OUTPUT
rm -f "$GIST_RESPONSE_FILE"
exit 1
elif [ "$HTTP_STATUS" -ge 400 ]; then
echo "❌ GitHub API error while fetching Gist ID: $GIST_ID (HTTP $HTTP_STATUS)"
echo "valid=false" >> $GITHUB_OUTPUT
rm -f "$GIST_RESPONSE_FILE"
exit 1
fi
GIST_CONTENT=$(jq -r '.files | to_entries[0].value.content // empty' "$GIST_RESPONSE_FILE")
rm -f "$GIST_RESPONSE_FILE"

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The Gist fetch uses an unauthenticated curl and does not fail on non-2xx responses. This is prone to GitHub API rate limiting and can silently return an error JSON that then looks like “missing content”. Pass Authorization: Bearer ${{ github.token }} (export via env:) and use curl --fail-with-body (or similar) so HTTP errors are handled explicitly.

Copilot uses AI. Check for mistakes.
if [ -z "$GIST_CONTENT" ]; then
echo "❌ Could not fetch Gist content for ID: $GIST_ID"
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

jq -r '.files | to_entries[0].value.content' is brittle: object entry order isn’t guaranteed and a multi-file Gist could make this read the wrong file. It can also hard-fail with a jq error (before your [ -z "$GIST_CONTENT" ] check) if .files is missing (e.g., 404/abuse-limit responses). Prefer selecting the expected filename (e.g., test.log) and handling non-200 responses explicitly (e.g., curl -f + status check, or jq with ?/// empty).

Copilot uses AI. Check for mistakes.

# Extract tree SHA from Gist content (first line should be "Tree SHA: <sha>")
GIST_TREE_SHA=$(echo "$GIST_CONTENT" | grep -oE "Tree SHA: [0-9a-f]{40}" | head -1 | awk '{print $3}')
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The regex pattern for extracting the tree SHA uses grep -oE "Tree SHA: [0-9a-f]{40}" which only matches lowercase hex digits. Git SHAs are typically displayed in lowercase, but to be more robust, consider using [0-9a-f]{40} with case-insensitive matching or [0-9a-fA-F]{40} to handle both cases.

Copilot uses AI. Check for mistakes.

if [ -z "$GIST_TREE_SHA" ]; then
echo "❌ Could not find tree SHA in Gist content."
echo " This may be an older format Gist. Checking for commit SHA..."

# Fallback: check for old "Commit SHA:" format
GIST_COMMIT_SHA=$(echo "$GIST_CONTENT" | grep -oE "Commit SHA: [0-9a-f]{40}" | head -1 | awk '{print $3}')
if [ -n "$GIST_COMMIT_SHA" ]; then
echo " Found old format with Commit SHA: $GIST_COMMIT_SHA"
echo " Note: Old format cannot be verified against tree SHA and is no longer accepted."
fi
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The fallback check for "Commit SHA:" format (lines 116-121) provides a helpful message about old format Gists but then immediately rejects them. This is good for security, but since this is a new feature, there shouldn't be any "old format" Gists yet. This code appears to be defensive programming for a scenario that shouldn't exist. Consider removing this fallback code to simplify the logic, or add a comment explaining why it's needed (e.g., for testing purposes or migration from a previous implementation).

Copilot uses AI. Check for mistakes.

echo "❌ Cannot verify SHA match. Failing verification."
echo " Please re-run 'make verify-with-proof' to generate a Gist that includes the Tree SHA line."
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi

echo "Gist Tree SHA: $GIST_TREE_SHA"
echo "PR Tree SHA: $PR_TREE_SHA"

if [ "$GIST_TREE_SHA" != "$PR_TREE_SHA" ]; then
echo ""
echo "❌ FAILED: Gist tree SHA does not match PR tree SHA."
echo " This means your code changed after running 'make verify-with-proof'."
echo " Please re-run 'make verify-with-proof' and push again."
echo "valid=false" >> $GITHUB_OUTPUT
exit 1
fi

echo "✅ Gist tree SHA matches PR tree SHA"
echo "✅ Test-Gist trailer found and validated"
echo "valid=true" >> $GITHUB_OUTPUT

- name: Set default test-gist output for trusted contributors
id: test-gist-default
if: steps.author.outputs.trusted == 'true'
run: |
echo "Trusted contributor - Test-Gist not required"
echo "valid=not-required" >> $GITHUB_OUTPUT

- name: Verify results
run: |
TRUSTED="${{ steps.author.outputs.trusted }}"
TESTED_BY="${{ steps.tested-by.outputs.found }}"
TEST_GIST="${{ steps.test-gist.outputs.valid }}"
TEST_GIST_DEFAULT="${{ steps.test-gist-default.outputs.valid }}"

# Use default value for trusted contributors
if [ "$TRUSTED" == "true" ]; then
TEST_GIST="$TEST_GIST_DEFAULT"
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The "Check for Test-Gist trailer" step (lines 70-143) will be skipped for trusted contributors, which means steps.test-gist.outputs.valid will be undefined/empty. In the "Verify results" step, line 156 assigns this empty value to TEST_GIST, which is then replaced with TEST_GIST_DEFAULT on line 161. However, if step test-gist-default also fails to run for some reason, both could be empty. While unlikely, consider adding a defensive check or initializing TEST_GIST to a default value to prevent edge cases.

Suggested change
# Use default value for trusted contributors
if [ "$TRUSTED" == "true" ]; then
TEST_GIST="$TEST_GIST_DEFAULT"
# Use default value for trusted contributors, with a defensive fallback
if [ "$TRUSTED" == "true" ]; then
if [ -z "$TEST_GIST_DEFAULT" ]; then
# In case the default step did not run or did not set an output
TEST_GIST="not-required"
else
TEST_GIST="$TEST_GIST_DEFAULT"
fi

Copilot uses AI. Check for mistakes.
fi

echo "========================================"
echo "Verification Summary"
echo "========================================"
echo "Author association: ${{ steps.author.outputs.association }}"
echo "Trusted contributor: $TRUSTED"
echo "Tested-By found: $TESTED_BY"

if [ "$TRUSTED" == "false" ]; then
echo "Test-Gist valid: $TEST_GIST"
else
echo "Test-Gist: not required (trusted contributor)"
fi
echo "========================================"

# Check Tested-By for everyone
if [ "$TESTED_BY" != "true" ]; then
echo ""
echo "❌ FAILED: Missing Tested-By trailer in commit message."
echo ""
echo "Please run 'make verify-with-proof' before pushing your changes."
echo "This will run tests and add the required trailers to your commit."
echo ""
exit 1
fi

# Check Test-Gist for new contributors only
if [ "$TRUSTED" == "false" ] && [ "$TEST_GIST" != "true" ]; then
echo ""
echo "❌ FAILED: New contributors must provide a valid Test-Gist."
echo ""
echo "Please run 'make verify-with-proof' before pushing your changes."
echo "This will:"
echo " 1. Run lint and tests"
echo " 2. Upload test logs to a Gist"
echo " 3. Add Tested-By and Test-Gist trailers to your commit"
echo ""
exit 1
fi

echo ""
echo "✅ All verification checks passed!"
38 changes: 38 additions & 0 deletions CONTRIBUTING_GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,44 @@ This policy exists to:
- PRs that look like low-effort AI slop will be closed.
- Repeated violators may be banned from the project.



### Verification Workflow

To ensure contributors have actually run tests locally, we use a verification system.

**Prerequisites:**
- [GitHub CLI (`gh`)](https://cli.github.com/) installed and authenticated
- At least one commit on your branch
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The proof is based on the commit’s tree SHA, but the doc only lists “at least one commit” as a prerequisite. Since uncommitted changes won’t be represented by the tree SHA (and can cause CI verification failures/confusion), consider documenting that contributors should commit all changes / have a clean working tree before running make verify-with-proof (or update this if the Makefile is changed to enforce it).

Suggested change
- At least one commit on your branch
- At least one commit on your branch
- A clean working tree (all changes you want verified are staged and committed)

Copilot uses AI. Check for mistakes.

**For all contributors:**
Copy link
Member

Choose a reason for hiding this comment

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

we do not have to spell out how the validation is done (it just makes it easier for LLM to cheat)

- Your commit must include a `Tested-By:` trailer showing you ran tests.

**For non-trusted contributors (not OWNER/MEMBER/COLLABORATOR):**
- You must also include a `Test-Gist:` trailer with a link to your test logs.
- The Gist contains your commit SHA, proving the tests were run on that specific commit.
- This proves you actually ran tests, not just copied a line.

**How to do this:**

Before pushing your PR, run:
```bash
make verify-with-proof
```

This command will:
1. Run lint and tests locally (fails if tests fail).
2. Upload your test logs to a GitHub Gist (includes commit SHA).
3. Add `Tested-By:` and `Test-Gist:` trailers to your commit.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

This section says the Gist contains the commit SHA, but both make verify-with-proof and the CI verifier use the tree SHA (HEAD^{tree}) for validation. Please update the wording (and the step list) to consistently refer to tree SHA, otherwise contributors will produce the wrong proof format.

Copilot uses AI. Check for mistakes.

Then push with:
```bash
git push --force-with-lease
```
Comment on lines 134 to 138
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

If a contributor runs make verify-with-proof to get the trailers, then makes additional commits, the Test-Gist will reference an old commit hash that doesn't match the PR head. The workflow checks for a Gist and validates it's accessible, but as noted in another comment, it doesn't verify the commit hash in the Gist description matches the PR's current commit.

This creates a loophole where contributors could:

  1. Run verify-with-proof on a simple/empty change
  2. Get the Tested-By and Test-Gist trailers
  3. Amend or add commits with actual changes
  4. Push, and the workflow would pass

The documentation (line 156) instructs users to push with --force-with-lease after running the command, but doesn't warn against making additional commits afterward. Consider adding a warning in the documentation about this limitation.

Copilot uses AI. Check for mistakes.

**Important:** If you modify your code after running `make verify-with-proof`, you must run it again. The CI will attempt to verify that the Gist's tree SHA matches your PR.


Comment on lines +132 to +141
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The new "Verification Workflow" section doesn't document the actual enforcement requirements described in the PR (required Tested-By: trailer, and Test-Gist: + what it contains for new contributors/trust rules). As written, contributors won't know what trailers are required or when the Gist is necessary.

Copilot uses AI. Check for mistakes.
## Pull Request Limits for New Contributors

To ensure high-quality code reviews and long-term codebase stability, we limit the number of simultaneous open PRs for new contributors.
Expand Down
58 changes: 58 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,61 @@ repro-check:
$(MAKE) clean
$(MAKE) build-all-platforms
shasum -b -a 256 --strict --check ./sha256sum.combined.txt



Comment on lines +260 to +261
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

There are multiple consecutive blank lines added here. Please remove extras to keep spacing consistent with the rest of the Makefile.

Suggested change

Copilot uses AI. Check for mistakes.
# Test with log capture - runs tests once and captures output
# Fails if tests fail (no masking with || true)
.PHONY: test-with-log
test-with-log:
@echo "Running tests and capturing logs..."
@$(MAKE) test 2>&1 | tee test.log; \
EXIT_CODE=$${PIPESTATUS[0]}; \
if [ $$EXIT_CODE -ne 0 ]; then \
echo "❌ Tests failed. See test.log for details."; \
exit $$EXIT_CODE; \
fi; \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

test-with-log only checks PIPESTATUS[0] (exit of $(MAKE) test) and will still report success even if tee fails (e.g., disk full / permission issues), which means you may upload an incomplete/missing log. Consider using set -o pipefail for the pipeline and/or explicitly checking tee’s exit status so log capture failures fail the target.

Suggested change
@$(MAKE) test 2>&1 | tee test.log; \
EXIT_CODE=$${PIPESTATUS[0]}; \
if [ $$EXIT_CODE -ne 0 ]; then \
echo "❌ Tests failed. See test.log for details."; \
exit $$EXIT_CODE; \
fi; \
@set -o pipefail; \
$(MAKE) test 2>&1 | tee test.log; \
TEST_EXIT_CODE=$${PIPESTATUS[0]}; \
TEE_EXIT_CODE=$${PIPESTATUS[1]}; \
if [ $$TEST_EXIT_CODE -ne 0 ]; then \
echo "❌ Tests failed. See test.log for details."; \
exit $$TEST_EXIT_CODE; \
fi; \
if [ $$TEE_EXIT_CODE -ne 0 ]; then \
echo "❌ Log capture failed (tee exited with $$TEE_EXIT_CODE). Test results may not be fully recorded."; \
exit $$TEE_EXIT_CODE; \
fi; \

Copilot uses AI. Check for mistakes.
echo "✅ Tests passed."

# Verify PR with proof for AI policy compliance
# This target runs lint and tests, uploads logs to a Gist, and adds trailers to the commit.
# Required for new contributors to prove tests were actually run locally.
#
# Prerequisites:
# - GitHub CLI (gh) installed and authenticated: https://cli.github.com/
# - At least one commit on your branch
.PHONY: verify-with-proof
verify-with-proof: lint test-with-log
Copy link
Member

Choose a reason for hiding this comment

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

please move to a script instead of doing in Makefile

Copy link
Member

Choose a reason for hiding this comment

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

make sure to run linter for scripts

@# Check that gh CLI is available
@if ! command -v gh >/dev/null 2>&1; then \
echo "❌ GitHub CLI (gh) not found. Install from: https://cli.github.com/"; \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The check for GitHub CLI uses command -v which is good, but the error message could be more helpful by suggesting how to authenticate after installation. Consider adding: "After installing, run 'gh auth login' to authenticate."

Suggested change
echo "❌ GitHub CLI (gh) not found. Install from: https://cli.github.com/"; \
echo "❌ GitHub CLI (gh) not found. Install from: https://cli.github.com/"; \
echo " After installing, run 'gh auth login' to authenticate."; \

Copilot uses AI. Check for mistakes.
exit 1; \
fi
@# Check that there's a commit to amend
@if ! git rev-parse --verify HEAD >/dev/null 2>&1; then \
echo "❌ No commit to amend. Please commit your changes first, then run 'make verify-with-proof'."; \
exit 1; \
fi
@echo "✅ Lint and tests passed. Uploading proof to Gist..."
@# Use tree SHA - it represents the code content and doesn't change when amending commit message
@TREE_SHA=$$(git rev-parse HEAD^{tree}); \
echo "Tree SHA: $$TREE_SHA" > test.log.tmp; \
echo "---" >> test.log.tmp; \
cat test.log >> test.log.tmp; \
mv test.log.tmp test.log; \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The Makefile creates a temporary file test.log.tmp, writes content to it, and then moves it to test.log. However, if the script fails between lines 300-303 (e.g., disk full, permissions issue), test.log.tmp will be left behind. Consider adding cleanup with a trap or checking if test.log.tmp exists before creating it.

Copilot uses AI. Check for mistakes.
GIST_URL=$$(gh gist create test.log -d "Test logs for Jaeger tree $$TREE_SHA" --public); \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

gh gist create ... --public will publish test logs in a publicly listed gist, which may unintentionally expose local paths/usernames or other sensitive output. Consider using a secret gist by default (and/or making visibility configurable) while keeping the URL in the trailer for CI verification.

Suggested change
GIST_URL=$$(gh gist create test.log -d "Test logs for Jaeger tree $$TREE_SHA" --public); \
GIST_URL=$$(gh gist create test.log -d "Test logs for Jaeger tree $$TREE_SHA"); \

Copilot uses AI. Check for mistakes.
if [ -z "$$GIST_URL" ]; then \
echo "❌ Failed to create Gist. Make sure 'gh' CLI is authenticated."; \
echo " test.log kept for manual inspection."; \
exit 1; \
fi; \
NAME=$$(git config user.name); \
EMAIL=$$(git config user.email); \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Tested-By is populated from git config user.name / user.email without validation. If either value is unset/empty, this will amend the commit with an invalid trailer (e.g., Tested-By: <>). Please add checks that both are non-empty (and ideally that the email looks like an email) before amending.

Suggested change
EMAIL=$$(git config user.email); \
EMAIL=$$(git config user.email); \
if [ -z "$$NAME" ]; then \
echo "❌ git config user.name is not set or empty. Please configure it before running 'make verify-with-proof'."; \
exit 1; \
fi; \
if [ -z "$$EMAIL" ]; then \
echo "❌ git config user.email is not set or empty. Please configure it before running 'make verify-with-proof'."; \
exit 1; \
fi; \
if ! echo "$$EMAIL" | grep -Eq '^[^@[:space:]]+@[^@[:space:]]+\.[^@[:space:]]+'; then \
echo "❌ git config user.email ('$$EMAIL') does not look like a valid email address."; \
echo " Please set a valid email before running 'make verify-with-proof'."; \
exit 1; \
fi; \

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Tested-By trailer is constructed from git config user.name / user.email, but these can be empty (common in fresh environments), producing an invalid trailer like Tested-By: <>. Add a check that both values are non-empty (and fail with an actionable message) before amending the commit.

Suggested change
EMAIL=$$(git config user.email); \
EMAIL=$$(git config user.email); \
if [ -z "$$NAME" ] || [ -z "$$EMAIL" ]; then \
echo "❌ Git user.name and/or user.email are not set."; \
echo " Please configure your git identity, for example:"; \
echo " git config --global user.name \"Your Name\""; \
echo " git config --global user.email \"you@example.com\""; \
echo " Then re-run: make verify-with-proof"; \
exit 1; \
fi; \

Copilot uses AI. Check for mistakes.
git commit --amend --no-edit \
--trailer "Tested-By: $$NAME <$$EMAIL>" \
--trailer "Test-Gist: $$GIST_URL"; \
rm -f test.log; \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The long shell block in verify-with-proof doesn’t enable set -e (and uses ; separators), so a failure in gh gist create (after the explicit check), git commit --amend, or rm can be masked by subsequent echo commands, potentially making the target succeed even though the commit wasn’t amended. Please add set -e (and ideally set -o pipefail) at the start of the block or use && chaining/check exit codes so failures propagate correctly.

Copilot uses AI. Check for mistakes.
echo "✅ Commit amended with Tested-By and Test-Gist trailers."; \
echo " Gist URL: $$GIST_URL"; \
echo ""; \
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

verify-with-proof runs a long \-continued shell script without set -e / error checks. If gh gist create or git commit --amend fails (e.g., auth/signing hooks), the recipe can continue, delete test.log, and print the success message anyway. Consider enabling set -euo pipefail for this block (or checking exit codes) and only removing test.log / printing success after the amend succeeds.

Copilot uses AI. Check for mistakes.
echo "Now run: git push --force-with-lease"
Loading