Repository navigation
fix(workflows): repair squad-heartbeat 403 on fork PR close - #2155
Merged
tamirdresher merged 3 commits intoOct 2, 2026
Merged
Conversation
Fork PRs trigger GITHUB_TOKEN read-only enforcement under the plain
pull_request event, so the 'Ralph - Repair orphaned squad labels' step's
issues.addLabels call 403s whenever a fork PR is closed (observed in
runs 37045805429 and 37036858649).
Switch the pull_request trigger to pull_request_target in all 5 synced
copies of squad-heartbeat.yml. This is safe here specifically because:
- the checkout step has no ref: override, so it always checks out the
base ref, never the fork head
- no script in this workflow reads or executes fork-sourced content;
the label-repair and triage steps only operate on repo-side state
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
🟠 Impact Analysis — PR #2155Risk tier: 🟠 HIGH 📊 Summary
🎯 Risk Factors
📦 Modules Affectedci-workflows (1 file)
root (2 files)
squad-cli (1 file)
squad-sdk (1 file)
templates (1 file)
This report is generated automatically for every PR. See #733 for details. |
Contributor
🛫 PR Readiness Check
PR Scope: 🔧 Infrastructure
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 3 commits — consider squashing before review |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | Changeset file found |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ❌ | Copilot threads resolved | 1 unresolved Copilot thread(s) — fix and resolve before merging |
| ❌ | CI passing | 12 check(s) still running |
Files Changed (6 files, +45 −5)
| File | +/− |
|---|---|
.changeset/2155-heartbeat-fork-pr-labels.md |
+10 −0 |
.github/workflows/squad-heartbeat.yml |
+7 −1 |
.squad-templates/workflows/squad-heartbeat.yml |
+7 −1 |
packages/squad-cli/templates/workflows/squad-heartbeat.yml |
+7 −1 |
packages/squad-sdk/templates/workflows/squad-heartbeat.yml |
+7 −1 |
templates/workflows/squad-heartbeat.yml |
+7 −1 |
Total: +45 −5
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The required changeset is missing, causing the Changelog Gate to fail.
2 open findings
What changed in this PR
Switches Squad Heartbeat to pull_request_target, enabling label repairs after fork PRs close while continuing to execute base-branch code only.
Changes:
- Replaces
pull_requestwithpull_request_target. - Synchronizes the trigger and safety rationale across all workflow copies.
| File | Description |
|---|---|
.github/workflows/squad-heartbeat.yml |
Updates the active workflow trigger. |
.squad-templates/workflows/squad-heartbeat.yml |
Updates the canonical template. |
templates/workflows/squad-heartbeat.yml |
Synchronizes the root template. |
packages/squad-cli/templates/workflows/squad-heartbeat.yml |
Synchronizes the CLI template. |
packages/squad-sdk/templates/workflows/squad-heartbeat.yml |
Synchronizes the SDK template. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| # Safe here because the checkout step below has no `ref:` override, so it | ||
| # always checks out the BASE ref — no fork code or fork-controlled input | ||
| # is ever read or executed by this job. | ||
| pull_request_target: |
| pull_request: | ||
| # pull_request_target (not pull_request): GITHUB_TOKEN is forced read-only | ||
| # for `pull_request` events triggered by a fork, so write steps below | ||
| # (addLabels/createComment) 403 on fork PR closes (#2080-class bug). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Root cause
.github/workflows/squad-heartbeat.yml(and its 4 synced template copies) use:For
pull_requestevents triggered by a fork PR, GitHub forcesGITHUB_TOKENto read-only regardless of the workflow''s declaredpermissions:block. The "Ralph — Repair orphaned squad labels" step callsissues.listForRepothenissues.addLabels, which 403s whenever a fork PR closes.Evidence: failed runs 37045805429 and 37036858649, both triggered by the
pull_request: closedevent from fork PR #2150 (headfb31ec7e), failing atPOST /issues/2074/labelswith403 Resource not accessible by integration.Fix
Switch the trigger from
pull_requesttopull_request_targetin all 5 synced copies:.github/workflows/squad-heartbeat.ymltemplates/workflows/squad-heartbeat.ymlpackages/squad-cli/templates/workflows/squad-heartbeat.ymlpackages/squad-sdk/templates/workflows/squad-heartbeat.yml.squad-templates/workflows/squad-heartbeat.ymlpull_request_targetruns with base-repo token permissions regardless of fork origin, fixing the 403.Why this is safe
pull_request_targetis normally risky because it can combine elevated permissions with untrusted fork code execution. That risk does not apply here:actions/checkoutstep has noref:override anywhere in the file — it always checks out the base ref, never the fork head.issues.listForReposcan, and the triage script (ralph-triage.js) is base-repo-owned code.scripts/security-review.mjs(which specifically flagspull_request_target+ fork-head checkout as a warning) reports zero findings against this change:Validation
yaml.safe_load), confirmedon:triggers are nowissues,pull_request_target,workflow_dispatch.node scripts/security-review.mjs origin/dev HEAD→ no findings.Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com