Skip to content

ci: trunk guard — only trunk may merge into main - #1933

Closed
khaliqgant wants to merge 2 commits into
mainfrom
ci/trunk-guard
Closed

khaliqgant wants to merge 2 commits into
mainfrom
ci/trunk-guard

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Brings relay in line with the org trunk policy (agentrelay.com#182), already live in agentrelay.com, cloud, relayfile, relayfile-cloud, relaycast and relay-desktop. relay already has a trunk branch but no guard, so it was the last repo still taking feature PRs straight into main.

Approved by Khaliq, who chose adopting the policy here over re-running the 61 CI checks that the 22:13Z org queue clear cancelled on #1930.

What this does

Ports .github/workflows/trunk-guard.yml verbatim from relayfile-cloud: any PR into main whose head is not trunk fails the check.

What this deliberately does NOT do

relayfile-cloud also restricted CI to the trunk → main PR (de298c0), which was a one-line change because it has a single ci.yml job. relay has 19 workflows triggered on pull_request with heterogeneous filters, and they do not take one uniform edit:

  • already pull_request: branches: [main], so a trunk-based PR skips them: node-compat, package-validation
  • unfiltered pull_request, so they would still run on feature → trunk PRs: large-files, rust-ci, test, plus path-filtered codegen-models, relay-evals
  • auto-fix workflows that push formatting commits back to feature branches: prettier-fmt-fix, rust-fmt-fix — restricting these to trunk changes developer-facing behaviour
  • pull_request_target with path filters: relayflow-pr-proof-broker
  • pull_request: types: [closed]: cancel-on-merge

Deciding which of those should stop running on feature PRs belongs to the policy owner, not this PR. Filed as a follow-up for @claude-agentrelay-com-efef42 (agentrelay.com lead). The guard is independently useful and safe to land first.

Verification

No code paths touched; the workflow is byte-identical to relayfile-cloud's apart from one comment line. It will self-demonstrate: this PR's own head is not trunk, so Trunk guard is expected to FAIL here — that failing check is the proof it works. Merge it from main with that check red, or retarget via trunk once relay's merge train is set up.

🤖 Generated with Claude Code


Note

Low Risk
Adds CI-only branch policy enforcement with no application or runtime code changes.

Overview
Adds a Trunk guard GitHub Actions workflow that runs on every pull request targeting main and fails unless the PR head is the trunk branch from this repository.

The job uses a combined check on github.head_ref and github.event.pull_request.head.repo.full_name so a fork branch also named trunk cannot bypass the rule. On violation it emits a workflow error telling contributors to retarget the PR to trunk.

Reviewed by Cursor Bugbot for commit a6d6fc8. Bugbot is set up for automated code reviews on this repo. Configure here.


Agent Relay sessions

  • claude session 03e53226 · opened via gh pr create · last active 2026-10-08

Brings relay in line with the org policy already live in agentrelay.com,
cloud, relayfile, relayfile-cloud, relaycast and relay-desktop
(agentrelay.com#182): feature branches merge into `trunk`, and only the
trunk -> main PR targets main.

This is the guard half only. relay has 19 workflows triggered on
pull_request with heterogeneous filters, so restricting CI to the
trunk -> main PR is a separate per-workflow decision for the policy
owner — see the PR body.

Approved by Khaliq.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T23:14:22.369394Z 6651d1c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 56532ad8-fa89-419d-834a-1c32791397c9
📥 Commits

Reviewing files that changed from the base of the PR and between 6651d1c and a6d6fc8.

📒 Files selected for processing (1)
  • .github/workflows/trunk-guard.yml
 __________________________________________________________________________________________________________________________________________________________________________________________________________________
< In software, we rarely have meaningful requirements. Even if we do, the only measure of success that matters is whether our solution solves the customer's shifting idea of what their problem is. - Jeff Atwood >
 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
📝 Walkthrough

Walkthrough

Adds a GitHub Actions workflow for pull requests targeting main. The workflow skips pull requests from trunk and fails other pull requests with an error that instructs authors to retarget them to trunk.

Changes

Trunk guard

Layer / File(s) Summary
Head branch check
.github/workflows/trunk-guard.yml
The workflow skips pull requests from trunk. For other head branches, it emits an error and fails.

Priority: ➖ Normal

Estimated code review effort: 1 (Trivial) | ~4 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 6651d

The guard fails non-trunk pull requests to main as intended. No concrete permissions-related impact is established, so no merge-blocking risk is identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check Passed The title clearly and concisely describes the added CI guard that permits only trunk-based merges into main.
Description check Passed The description provides a detailed summary, scope, rationale, workflow behavior, limitations, and verification notes. It does not use the template's exact Test Plan, RelayFlow Proof, or Screenshots h…
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the branch at dawn,
“From trunk,” it says, “you may pass on.”
Other paths receive a gentle sign,
To retarget before crossing the line.
Then hops away, its guard job done.

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment thread .github/workflows/trunk-guard.yml Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6651d1c71a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/trunk-guard.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔇 Additional comments (1)
.github/workflows/trunk-guard.yml-9-12 (1)

9-12: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Add an explicit permissions block to the workflow.

The workflow has no permissions: block, so the job uses the default GITHUB_TOKEN scopes. The job only runs echo and exit 1. It needs no token access. Set permissions: {} to apply least privilege and clear the zizmor excessive-permissions warning.

🔒️ Proposed fix
 jobs:
   only-trunk-merges-to-main:
     if: github.head_ref != 'trunk'
     runs-on: ubuntu-latest
+    permissions: {}
     timeout-minutes: 1

As per the retrieved learning: apply least privilege and default permissions to the minimum scopes required. The PR states the file is byte-identical to relayfile-cloud, so consider applying the same fix there.

Sources: Learnings, Linters/SAST tools


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1984965c-0fcf-460f-91b7-b009579bc74e
📥 Commits

Reviewing files that changed from the base of the PR and between cb32953 and 6651d1c.

📒 Files selected for processing (1)
  • .github/workflows/trunk-guard.yml

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6651d1c. Configure here.

Comment thread .github/workflows/trunk-guard.yml Outdated
Devin, Codex and Cursor Bugbot all flagged the same hole: github.head_ref
is only the branch NAME, with no repository identity, so a pull request
from a fork whose head branch is also called `trunk` skipped the job and
passed the guard without its commits ever going through this repo's
protected trunk.

Require github.event.pull_request.head.repo.full_name == github.repository
as well as the branch name.

NOTE: the same hole exists in the guard already merged in agentrelay.com,
cloud, relayfile, relayfile-cloud, relaycast and relay-desktop, which this
file was copied from. Flagged to the policy owner for the same fix.

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

Copy link
Copy Markdown
Member Author

Fixed in
6651d1c → the new head. Devin, Codex and Cursor Bugbot all landed on the same real hole, and they are right: github.head_ref carries only the branch name, with no repository identity, so a fork PR whose head branch is also called trunk skipped the job entirely and sailed past the guard without its commits ever passing through this repo's protected trunk.

The job condition now requires both:

if: >-
  github.event.pull_request.head.repo.full_name != github.repository
  || github.head_ref != 'trunk'

so the guard runs (and fails) for any fork PR regardless of branch name, and only a same-repo trunk head skips it. The error message now names the repository too.

This matters beyond this PR: the file was copied verbatim from relayfile-cloud, so the identical hole is already merged and live in agentrelay.com, cloud, relayfile, relayfile-cloud, relaycast and relay-desktop. Each needs the same two-line fix. Flagged to the policy owner (agentrelay.com#182) rather than fixed piecemeal here.

Reminder for reviewers: this PR's own only-trunk-merges-to-main check is expected to fail, since its head is ci/trunk-guard and not trunk. That red check is the guard working.

@khaliqgant

Copy link
Copy Markdown
Member Author

Closing as superseded — and the version that landed is better than mine.

While this was open, #1932 merged ci: run CI only for trunk PRs and main pushes (649a3c91) plus ci: harden trunk gating (9024ae34) into main. Between them they cover both halves of the policy for relay, and the guard there is stronger than the one in this PR on three counts:

  1. It triggers on pull_request_target rather than pull_request, so the workflow is loaded from the base branch and a PR cannot edit the guard to pass itself. That hole was in mine and no reviewer caught it.
  2. It sets permissions: {}.
  3. It already carries the fork-identity check that Devin, Codex and Cursor Bugbot flagged here, which I had just pushed as a6d6fc80.

It also restricted CI across all 19 pull_request-triggered workflows, which I had deliberately left out of this PR as a judgement call for the policy owner. That decision has now been made in code.

The one thing worth carrying forward: the fork bypass I fixed here still exists in the guard merged in agentrelay.com, cloud, relayfile, relayfile-cloud, relaycast and relay-desktop, which relay's original copy came from. Those six still compare github.head_ref alone, and several also use plain pull_request. Raised with the policy owner separately — this is the real finding from this PR, so it should not get lost in the close.

@khaliqgant khaliqgant closed this Oct 8, 2026
@khaliqgant
khaliqgant deleted the ci/trunk-guard branch October 8, 2026 23:32
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.

1 participant