Skip to content

ci: adopt the inline pr-agent lane — a public repo cannot call a private reusable workflow - #65

Open
yakimoto wants to merge 6 commits into
mainfrom
ci/adopt-inline-pr-agent
Open

ci: adopt the inline pr-agent lane — a public repo cannot call a private reusable workflow#65
yakimoto wants to merge 6 commits into
mainfrom
ci/adopt-inline-pr-agent

Conversation

@yakimoto

@yakimoto yakimoto commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

User description

User description

User description

User description

User description

Part of the fan-out tracked in wave-pen#388, proven first on wave-certify#44 where pr_agent returned success.

The defect

pr-agent.yml here calls wave-av/wave-foundation/.github/workflows/reusable-pr-agent.yml@main, and wave-foundation is private. GitHub does not permit a public repository to call a reusable workflow from a private one, so the uses: never resolves: the run dies before any job is created — conclusion: failure, total_count: 0, no log, and no check run on the head sha at all.

That is worse than a normal failure. There is nothing to click through to. Every PR on this repo has been carrying a red check that reports nothing, and external contributors see it.

Measured on this repo today — the last 5 pr-agent runs:

failure, failure, failure, failure, failure

Measured across the org on 2026-08-22: 7 public repos / 176 runs / 100% failure; 9 private repos / zero failures. A clean 16/16 split on visibility alone. Three competing hypotheses were each tested and refuted — missing OPENAI_KEY (present in both populations), a dead pinned ref (150ffae2 resolves, file exists at it), and @main vs a pinned sha (wave-realtime-edge pins @main and fails, wave-pen pins @main and works).

The fix already existed and was never adopted

wave-foundation-public/.github/workflows/pr-agent.yml is an inline copy of the same lane with no reference to the private repo. Its own header says it was written for exactly this. This PR adopts it verbatim.

So this is an adoption gap, not a design gap.

Why now, and not when #388 was filed

#388 named two blockers, and both are cleared as of wave-foundation-public#71:

  1. The shared concurrency key. The template carried pr-agent-${{ github.event.pull_request.number || … }}, shared between pull_request and issue_comment, so any bot comment cancelled a live review ~10s in (wave-pen#386). It now keys on github.event_name.
  2. Missing step-level timeouts. The template now carries 6.

Fanning out before those landed would have traded a red-with-no-log lane for a cancelled-on-every-comment lane — a different failure, not a fix.

Verified before opening this

  • The template is genuinely self-contained. Its only two wave-foundation/ mentions are in comments, not in a uses:. Checked rather than assumed, since that is the whole property this depends on.
  • The job id stays pr_agent. A job's id is its check-run context and branch protection matches on (context, app_id), so nothing needs touching on the protection side.
  • The workflow parses, and the source was read from a fresh clone of wave-foundation-public's default branch — not from a local checkout that might be parked on another branch.

The receipt is this PR, not the diff

A red lane and a working lane are indistinguishable until one actually runs — that is the whole reason 176 failures went unexamined. So the proof is pr-agent going green on this PR. If it does, the remaining 27 repos get the same change with evidence behind it. If it does not, we learn that here, on one low-traffic repo, instead of across the org's entire public surface.

Proven before fanning out. wave-certify#44 took this exact change first and its pr_agent run returned success on the pull_request event — a job with a real log, where the broken form produced no job at all. The other repos were not changed on hope.

Refs wave-pen#388

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Note

Medium Risk
CI now holds OPENAI_KEY in-repo and adds fork/slash-command gating plus retry/verdict logic. A gate bug could spend the LLM key or skip reviews; it does not change product auth or data handling.

Overview
Replaces the unusable uses: of private wave-foundation reusable pr-agent with a self-contained inline workflow. Public repos cannot call private reusable workflows, so the old job never started (red check, no log).

The new lane runs Qodo Merge (The-PR-Agent/pr-agent@v0.42.0) with event-scoped concurrency so comments no longer cancel in-flight push reviews, a fail-closed fork gate on / commands, 6-minute attempts with a 45s retry, and a verdict step that classifies timeout/cancel/429 and stays neutral so an advisory reviewer cannot block the PR. CONFIG__AI_TIMEOUT is 300s so the agent can fall back before the step is killed.

Changelog documents the fork-gate, timeout, and attempt-2 stamp arithmetic fixes. Maintainer /review on fork PRs is now declined instead of running on the org key.

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

Review in cubic

Summary by Sourcery

Restore functional, non-blocking pull-request reviews in public repositories by adopting the inline pr-agent workflow and strengthening its safety and failure handling.

Bug Fixes:

  • Replace the unusable private reusable-workflow reference with a self-contained inline pr-agent workflow for public repositories.
  • Prevent fork-based slash commands from using the repository's AI credentials by adding a fail-closed fork check.
  • Ensure reviewer timeouts, cancellations, and transient failures are classified accurately without blocking pull requests.

Enhancements:

  • Improve pr-agent reliability with event-specific concurrency, bounded attempts, retry backoff, and fallback model handling.

CI:

  • Reconfigure the pr-agent CI lane to run directly in the public repository with protected pull-request and comment-trigger behavior.

Documentation:

  • Document the fork-gating, timeout-budget, and retry-related fixes in the changelog.

PR Type

Bug fix, Enhancement


Description

  • Replaced private workflow reference with inline public version

  • Fixed concurrency grouping to prevent job collisions

  • Added fork detection and handling for issue comments

  • Implemented timeout management and retry logic

  • Added detailed outcome classification for advisory reviews


Diagram Walkthrough

flowchart TD
  A["Concurrency Group"] --> B["Fork Gate Check"]
  B --> C{"Fork?"}
  C -->|Yes| D["Skip with warning"]
  C -->|No| E["Attempt 1 (6min)"]
  E --> F["Stamp Start"]
  E --> G["Run PR-Agent"]
  G --> H["Stamp End"]
  H --> I{"Success?"}
  I -->|Yes| J["Exit"]
  I -->|No| K["Backoff 45s"]
  K --> L["Attempt 2 (6min)"]
  L --> M["Stamp Start"]
  L --> N["Run PR-Agent"]
  N --> O["Stamp End"]
  O --> P["Verdict Classification"]
  P --> Q["Report Outcome"]
Loading

File Walkthrough

Relevant files
Enhancement
pr-agent.yml
Rewrote pr-agent workflow with inline implementation         

.github/workflows/pr-agent.yml

  • Replaced private workflow reference with inline public version
  • Added detailed concurrency grouping with event name and PR/issue
    context
  • Implemented fork detection via pulls endpoint API call
  • Added timeout management with per-attempt budgeting
  • Implemented retry logic with 45s backoff between attempts
  • Added comprehensive outcome classification for advisory reviews
+325/-6 
Documentation
CHANGELOG.md
Updated changelog with workflow improvements                         

CHANGELOG.md

  • Documented fork handling improvements
  • Noted timeout configuration changes
  • Added details about retry logic implementation
  • Clarified handling of upstream failures
+24/-0   

@codeant-ai

codeant-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed f6bb396 Aug 24, 2026 · 14:36 14:37
✅ Incremental review completed 5c00a8b Aug 24, 2026 · 13:35 13:36
✅ Reviewed your PR c65ca81 Aug 23, 2026 · 22:31 22:31

@cursor

cursor Bot commented Aug 23, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_1df86b51-910d-487b-bd14-3ff0f8c45cf1)

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 27 minutes.

View limit details

Limit details: You’ve used the included review currently available. Your 91 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8984b280-8559-4cb4-aaa8-b2c647c9fa86

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd455f and 545cf0f.

📒 Files selected for processing (2)
  • .github/workflows/pr-agent.yml
  • CHANGELOG.md

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

@sourcery-ai

sourcery-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR replaces the broken reusable pr-agent workflow invocation with an inline, fully self-contained workflow tailored for public repositories, and updates it to match the current reusable lane behavior, including concurrency fixes, timeouts, retry logic, and non-blocking verdict classification.

Sequence diagram for PR-Agent retry and verdict classification

sequenceDiagram
    participant GitHub
    participant Lane as pr_agent job
    participant Agent as PR-Agent action
    participant Verdict as verdict step

    GitHub->>Lane: Start pull_request or trusted slash-command run
    Lane->>Agent: Run attempt 1 (6-minute timeout)
    alt attempt 1 succeeds
        Agent-->>Verdict: success
        Verdict-->>GitHub: exit 0
    else attempt 1 fails
        Agent-->>Lane: failure
        Lane->>Lane: sleep 45
        Lane->>Agent: Run retry attempt 2 (6-minute timeout)
        Agent-->>Verdict: success or failure
        Verdict-->>GitHub: classify result and remain non-blocking
    else run is cancelled
        Agent-->>Verdict: cancelled or no outcome
        Verdict-->>GitHub: classify workflow-level cancellation
    end
Loading

Flow diagram for PR-Agent event isolation and execution

flowchart TD
    Event[Pull request or issue comment event] --> Group[Concurrency key includes event name and PR number]
    Group --> Eligible{Eligible to run?}
    Eligible -->|No| Skip[Skip job]
    Eligible -->|Yes| Attempt[Run PR-Agent attempt]
    Attempt --> Result{Attempt succeeds?}
    Result -->|Yes| Pass[Verdict exits 0]
    Result -->|No| Retry[Wait 45 seconds and retry]
    Retry --> Verdict[Classify success, failure, timeout, or cancellation]
    Verdict --> NonBlocking[Advisory outcome does not block the PR]
Loading

File-Level Changes

Change Details Files
Inline and modernize the pr-agent CI workflow so public repos no longer depend on a private reusable workflow, and align behavior with the updated reusable lane.
  • Replace the uses: wave-av/wave-foundation/...reusable-pr-agent.yml job with a fully inline pr_agent job using The-PR-Agent GitHub Action and explicit env configuration.
  • Update concurrency group key to include github.event_name so pull_request and issue_comment events do not cancel each other while still superseding same-event runs.
  • Introduce a 15-minute job timeout plus 6-minute step-level timeouts for each PR-Agent attempt, ensuring retries fit within the overall budget.
  • Add guarded if: conditions so only trusted slash-command comments and non-bot, non-fork, non-draft PRs trigger the workflow.
  • Implement a two-attempt PR-Agent execution with 45s backoff and identical model/config env for both attempts, using continue-on-error for advisory behavior.
  • Add a final verdict step that classifies outcomes (success, never-ran, cancelled, timed-out, failure) and converts reviewer flakes into neutral/warning results so the advisory check never blocks merges while still surfacing workflow faults as errors.
  • Enhance workflow header comments with detailed rationale, measurements, and cross-repo context to document why the inline form exists and how drift must be managed.
.github/workflows/pr-agent.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Running ultrareview automatically — Running ultrareview automatically — This change modifies cross-cutting CI/CD infrastructure and security-sensitive authorization logic that will be replicated across dozens of repositories, warranting a deep analysis of potential side effects and concurrency issues.. I'll post findings when complete.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 23, 2026
@cubic-dev-ai

cubic-dev-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown

I can't run this ultrareview because your workspace has reached its monthly review limit. cubic has reviewed 100,145 of the 100,000 allowed lines of code this month. Reviews resume on 4 September 2026 (in 13 days). Enable flex capacity to cover overages automatically and resume reviews now. Learn how flex capacity works.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

CI: inline PR-Agent workflow to avoid private reusable-workflow calls

🐞 Bug fix ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Inline the PR-Agent workflow so public repos don’t call private reusable workflows.
• Fix concurrency grouping so comment triggers don’t cancel in-flight PR reviews.
• Add retries, step timeouts, and a non-blocking verdict step for advisory stability.
Diagram

graph TD
  A["PR / Comment event"] --> B["Concurrency group"] --> C["pr_agent job"] --> D["PR-Agent attempt 1"] --> E["Backoff 45s"] --> F["PR-Agent attempt 2"] --> G["Verdict (neutral on flake)"]
  D -->|"success"| G
  F -->|"success"| G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Public reusable workflow (wave-foundation-public)
  • ➕ Single source of truth; avoids inline drift across many repos
  • ➕ Keeps per-repo workflow files minimal
  • ➖ Still cross-repo coupling; requires making the reusable workflow public and stable
  • ➖ Repos must trust and pin a shared workflow ref; changes need coordination
2. Composite GitHub Action for PR-Agent lane
  • ➕ Encapsulates retry/verdict logic in a versioned action
  • ➕ Reused across repos without copying large YAML blocks
  • ➖ Needs action packaging, publishing, and versioning discipline
  • ➖ Still requires per-repo workflow wiring and secrets/env handling
3. Automated sync of inline mirror
  • ➕ Retains inline public-repo compatibility while preventing drift
  • ➕ Can be enforced via periodic bot PRs or CI checks
  • ➖ Adds maintenance tooling and process overhead
  • ➖ Sync automation failures can block timely updates

Recommendation: Inline adoption is the most reliable immediate fix for public repos because it removes the private reusable-workflow dependency entirely (the root failure mode). If long-term drift becomes a recurring problem, the best follow-up is to move the lane into a public reusable workflow or composite action with pinned versions, or introduce an automated sync mechanism to keep inline mirrors aligned.

Files changed (1) +166 / -6

Bug fix (1) +166 / -6
pr-agent.ymlInline PR-Agent lane with event-safe concurrency, retries, and verdicts +166/-6

Inline PR-Agent lane with event-safe concurrency, retries, and verdicts

• Replaces the call to a private reusable workflow with an inline PR-Agent job suitable for a public repository. Updates concurrency grouping to include the event name, preventing issue_comment runs from cancelling pull_request reviews. Adds step-level timeouts, a retry with backoff, and a verdict step that classifies failures/cancellations/timeouts and keeps this advisory check from blocking PRs.

.github/workflows/pr-agent.yml

@macroscopeapp

macroscopeapp Bot commented Aug 23, 2026

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR replaces a broken reusable-workflow reference with a substantial secret-bearing CI workflow that changes PR and comment execution, fork handling, concurrency, retries, and automated write-capable review behavior. The workflow is also outside the author’s CODEOWNERS ownership, so designated human review is warranted.

Not approved because:

  • Credit balance exhausted. Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 62c65cc)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
⚡ Recommended focus areas for review

Misclassified Timeout

ELAPSED is measured from AGENT_START (stamped before attempt 1) to the verdict step, so it spans attempt 1 + the 45s backoff + attempt 2, but it is compared against STEP_BUDGET_S (360s), a per-attempt budget. A realistic case — attempt 1 errors at 200s, 45s sleep, attempt 2 errors at 150s — totals 395s and is reported as "TIMED OUT ... A hang, NOT a rate limit", which is exactly the confidently-wrong cause the comment above it warns against. To measure per-attempt elapsed, the start stamp needs to be re-taken before each attempt (or the comparison made against the full worst-case budget).

ELAPSED=$(( $(date +%s) - ${AGENT_START:-$(date +%s)} ))
if [ "$ELAPSED" -ge "$STEP_BUDGET_S" ]; then
  echo "::warning::pr-agent TIMED OUT — ${ELAPSED}s against a ${STEP_BUDGET_S}s per-attempt budget, so an attempt was killed by its step timeout rather than returning an error. A hang, NOT a rate limit. Rendering NEUTRAL: an advisory reviewer must not block the PR (#3128)."
  exit 0
fi
Red On Supersede

If the run is cancelled by the concurrency group before the agent step starts (e.g. during runner/job setup, or while the stamp attempt start step runs), steps.agent.outcome is empty and the verdict step takes the "never ran" branch and exit 1 — a red check. The cancelled branch only covers the case where the agent step itself was already running. Since two pushes intentionally supersede each other via this group, a red check on a superseded run contradicts the "advisory reviewer must never block" intent stated in the same file. Consider treating cancelled()/github.event.action-driven cancellation as non-blocking, e.g. gate the

@gitar-bot

gitar-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your team has used its included automatic processing for this billing period (headroom scales with your seat count). You can still comment "Gitar review" to run one anytime, and automatic reviews resume on their own by September 1. Add seats for more headroom.
Learn more

Code Review ✅ Approved

Replaces the private reusable workflow reference with an inline PR-agent workflow to fix public repo check failures. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sourcery-ai sourcery-ai 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.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. If the workflow or pinned action is wrong, a PR-triggered job can use the repository's OPENAI_KEY and write-capable GitHub token, potentially exposing access or making automated repository changes that reverting the workflow cannot undo. The retry and concurrency changes also alter when runs execute, but their ordinary failures are recoverable.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@github-actions

Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

@qodo-code-review

qodo-code-review Bot commented Aug 23, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Job timeout defeats verdict ✓ Resolved 🐞 Bug ☼ Reliability
Description
The job has timeout-minutes: 15, but the workflow relies on a final “verdict (classify, never
block)” step to neutralize advisory failures; a job-level timeout cancels the job and prevents that
step from running. In that case, the check can remain cancelled/non-success and still block branch
protection despite the intended “never block” behavior.
Code

.github/workflows/pr-agent.yml[R56-60]

  pr_agent:
-    uses: wave-av/wave-foundation/.github/workflows/reusable-pr-agent.yml@150ffae24f63e207ab81430fa64cb2b1e5c01546 # post-#1191
-    secrets:
-      OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
+    timeout-minutes: 15
+    # Slash commands: PR-only + trusted members (cost-abuse guard). Forks skipped (no secrets there).
+    if: >-
+      ${{
Relevance

●●● Strong

The timeout can cancel the job before the always-run verdict, directly undermining the PR's
never-block advisory intent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The job sets a hard 15-minute timeout, while the workflow’s own comments and logic show it depends
on the final verdict step to “never block”; a job-level cancellation prevents any later step
(including verdict) from running.

.github/workflows/pr-agent.yml[56-58]
.github/workflows/pr-agent.yml[78-92]
.github/workflows/pr-agent.yml[148-183]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The workflow intends to always reach the final verdict step that exits 0 for advisory failures/timeouts. However, the job-level `timeout-minutes: 15` can cancel the whole job before the verdict executes, leaving a cancelled/failed check that can still block merges.

### Issue Context
Step-level timeouts already exist for the agent attempts. The remaining job-level timeout should be set high enough to reliably allow the verdict step to run even under runner slowness/queueing.

### Fix Focus Areas
- .github/workflows/pr-agent.yml[56-58]
- .github/workflows/pr-agent.yml[148-183]

### Suggested fix
- Increase the job timeout to a safer bound (e.g., 25–30 minutes) so step-level timeouts enforce the real budget but the verdict step still runs.
- Or remove the job-level timeout entirely and rely on the step-level `timeout-minutes` plus retry/backoff.
- If you keep a job timeout, ensure the sum of worst-case step timeouts + backoff + expected runner overhead stays comfortably below it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Slash-commands run on forks ✓ Resolved 🐞 Bug ⛨ Security
Description
The issue_comment path has no fork == false guard, so trusted members can trigger pr-agent on
fork PRs even though the workflow carries OPENAI_KEY and other secrets. This contradicts the
“Forks skipped” comment and can expose secrets to an action execution context tied to untrusted fork
PR content.
Code

.github/workflows/pr-agent.yml[R58-65]

+    # Slash commands: PR-only + trusted members (cost-abuse guard). Forks skipped (no secrets there).
+    if: >-
+      ${{
+        (github.event_name == 'issue_comment'
+            && github.event.issue.pull_request
+            && startsWith(github.event.comment.body, '/')
+            && contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association))
+        || (github.event_name == 'pull_request'
Relevance

● Weak

Recent precedent rejected caller-side issue_comment secret-safety guards; trusted base-context
comments do not execute fork code here.

PR-#64

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The workflow explicitly enables issue_comment runs, and the job if: block only checks
head.repo.fork == false inside the pull_request branch; the issue_comment branch lacks any
fork restriction while the workflow injects OPENAI_KEY into the action environment.

.github/workflows/pr-agent.yml[20-25]
.github/workflows/pr-agent.yml[56-69]
.github/workflows/pr-agent.yml[95-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The workflow enables `issue_comment` triggers, but the job-level `if:` only enforces `fork == false` for the `pull_request` path. For `issue_comment`, there is no fork guard, so members/owners/collaborators can run pr-agent (with secrets) against fork PRs.

### Issue Context
This is especially risky because `issue_comment` runs in the base repo context (secrets available), and the current comment says forks are skipped.

### Fix Focus Areas
- .github/workflows/pr-agent.yml[20-25]
- .github/workflows/pr-agent.yml[56-69]

### Suggested fix
Add a fork guard for the `issue_comment` path too.

Options:
1) Fetch the PR in a preliminary step (e.g., via `actions/github-script` or `gh api` using `GITHUB_TOKEN`) to determine whether `head.repo.fork` is true, store it as an output/env var, and include it in the job `if:`.
2) If you cannot reliably gate forks for comments, remove `issue_comment` trigger support (or restrict it to `pull_request` only) to avoid running with secrets on fork PRs.

Also update the comment `Forks skipped (no secrets there)` to reflect reality once the gating is correct.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: This is a behavioral CI workflow replacement with substantial logic across permissions, triggers, concurrency, retries, timeouts, secrets, and failure classification; it warrants a careful single-pass review, but the two edit sites are not dense enough to justify extended redundancy.

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/pr-agent.yml
@bito-code-review

Copy link
Copy Markdown

The job-level timeout-minutes: 15 is indeed too restrictive, as it can cancel the entire job before the final verdict step executes, potentially leaving the check in a cancelled state that blocks branch protection. To resolve this, you should increase the job-level timeout to a value that comfortably accommodates the sum of all step-level timeouts, retry logic, and runner overhead (e.g., 25–30 minutes). Alternatively, you could remove the job-level timeout entirely and rely solely on the step-level timeout-minutes to enforce the budget.

.github/workflows/pr-agent.yml

timeout-minutes: 15

@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

✅ Merged (0) · ☑ Fixed (0)

Process

  • No fixes were applied (no_fixes_applied)

…ate reusable workflow

This repo's pr-agent.yml calls
`wave-av/wave-foundation/.github/workflows/reusable-pr-agent.yml`, and
wave-foundation is PRIVATE. GitHub does not permit a PUBLIC repository to call
a reusable workflow from a private one, so the `uses:` never resolves: the run
dies before any job is created — conclusion: failure, total_count: 0, no log,
and no check run on the head sha to read. Every PR here has carried a red check
that reports nothing, and external contributors see it.

Measured across the org 2026-08-22: 7 public repos / 176 runs / 100% failure;
9 private repos / zero failures — a clean 16/16 split on visibility alone.
Three competing hypotheses (missing OPENAI_KEY, dead pinned ref, @main vs a
pinned sha) were each tested and refuted.

THE FIX already existed and was never adopted:
wave-foundation-public/.github/workflows/pr-agent.yml is an INLINE copy of the
same lane with no reference to the private repo. This adopts it verbatim.

PROVEN BEFORE FANNING OUT. wave-certify#44 took this exact change first and its
pr_agent run returned SUCCESS on the pull_request event — a job with a real log,
where the broken form produced no job at all. 27 repos were not changed on hope.

Two prerequisites named in wave-pen#388 are cleared as of
wave-foundation-public#71: the shared concurrency key that let any bot comment
cancel a live review ~10s in (wave-pen#386) now keys on github.event_name, and
the lane carries step-level timeouts.

The job id stays `pr_agent`, so the check-run context is unchanged and no
branch protection rule needs touching.

Refs wave-pen#388

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@yakimoto
yakimoto force-pushed the ci/adopt-inline-pr-agent branch from c65ca81 to 62c65cc Compare August 23, 2026 22:40
@cursor

cursor Bot commented Aug 23, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_6e3fa34e-7f7c-4e45-b40a-3d8f4876ac8a)

@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 62c65cc

…t classification

Picks up wave-foundation-public#72, which landed after this PR was opened.

The template this PR originally copied classified timeouts on TOTAL job time
(attempt 1 + 45s backoff + attempt 2) against STEP_BUDGET_S=360, a PER-ATTEMPT
budget. Two healthy-but-slow attempts (~180s each) were therefore reported as
"TIMED OUT ... A hang, NOT a rate limit", and the else-branch claimed the run
was "well inside the budget" from the same misused total. Found by qodo review
on wave-monitor#48 and confirmed against the file.

Now stamps each attempt separately and classifies on the LONGEST attempt, with
if: always() end stamps so an attempt killed BY its step timeout still records
one. Verified by dry-running both cases before the template landed.

Updated in place rather than as a follow-up PR because this has not merged yet
— cheaper, and it keeps the repo from ever carrying the defective version.

Refs wave-av/wave-pen#417, wave-av/wave-pen#388
@cursor

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_461d6c0d-c1e1-491e-bb12-fe0fbd818b95)

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Aug 24, 2026
…s step cap

Re-syncs this PR to the hardened template before it merges, so it cannot land
carrying the two defects it was opened with (wave-pen#418, wave-foundation-public#73).

1. Fork status is now RESOLVED, not assumed. The job-level `if:` refuses forks
   on the `pull_request` arm; it structurally cannot on `issue_comment`, because
   fork status is absent from that payload — measured, with a positive control:
   `issues/<n>.pull_request` carries exactly [diff_url, html_url, merged_at,
   patch_url, url], while `pulls/<n>.head.repo.fork` answers. A `fork gate` step
   asks the pulls endpoint and FAILS CLOSED: only a literal `false` proceeds; a
   404, a revoked token, a rate limit and `.head.repo = null` all skip.

   Scope: this lane runs no `actions/checkout`, so fork code is never fetched or
   executed and no exfiltration path existed. The durable defect was the comment
   claiming "Forks skipped (no secrets there)" — true of one arm, false of the
   other, and exactly what would mislead whoever adds a checkout step later.

2. CONFIG__AI_TIMEOUT 600 -> 300, in both env blocks. 600s inside a 360s step is
   unreachable: the runner killed the step first, so pr-agent never reached its
   own timeout and never fell back to CONFIG__FALLBACK_MODELS.

3. A latent classifier bug the gate exposed: `stamp attempt 2 end` runs under
   `if: always()`, so when attempt 2 never ran the arithmetic subtracted from
   zero and reported a 1787580408-second attempt as a confident TIMED OUT. Fixed
   at the arithmetic; the verdict also gains an explicit `skipped` branch.

The job id stays `pr_agent`, so the check-run context is unchanged and no branch
protection rule needs touching.

Refs wave-pen#418, wave-pen#417, wave-pen#388

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

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_bbeca419-d6ea-4696-bb6f-33d1901f4b71)

…ce of a true

Review of this wave found the fail-closed gate had a fail-OPEN consumer. Two
reviewers flagged it independently, on two different repos, and they were right.

    if: steps.gate.outputs.fork != 'true'      # grants when the output is EMPTY

The gate could only fail closed if it always wrote an output. It did, on every
path — so this did not fail open today, and the implicit success() on the
consumer covers a gate that errors outright. But the safety rested on an
argument rather than on the structure, and it is the very argument this change
exists to delete: absence must not read as permission.

Two independent changes, so neither carries the invariant alone:

  - the gate now assigns a shell variable that STARTS at `true` and writes ONCE
    at the end, so no future edit adding an early exit can emit nothing;
  - the consumer requires `== 'false'`, an explicit affirmative, so an empty or
    missing output skips the agent.

Also braces both sides of the A2 subtraction in the verdict step. The bare
`ATTEMPT2_START` was CORRECT — POSIX arithmetic expansion evaluates a bare name
as a variable, verified identical (180 == 180) — but a reviewer read it as a
literal token and filed it High. An expression that reads wrong on 27 repos gets
re-filed on 27 repos, so it is normalised rather than defended.

RECEIPTS. actionlint clean; zizmor clean; shellcheck clean. The gate was driven
through all six branches plus the reviewers' no-output scenario: only a literal
`false` reaches AGENT RUNS. The verdict was re-run across all six states and is
unchanged on the five that already worked.

LIVE: wave-av/api-spec merged the previous revision and its pull_request run
executed `fork gate (issue_comment only) -> success` in production, then ran the
agent — so the gate does not wrongly refuse a legitimate same-repo PR.

Upstream: wave-av/wave-foundation-public#73. Refs wave-pen#418, wave-pen#417.

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

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d337fb48-b344-4955-ab74-9ec18d73348e)

…mber space

Review found a SECOND concurrency collision, on a different axis from the one
this template already documents (wave-pen#386).

`issue_comment` fires for ISSUES as well as PRs, and GitHub draws both from ONE
number sequence. So a comment on Issue #30 and a `/review` on PR #30 entered the
same concurrency group. Concurrency is evaluated at WORKFLOW level, BEFORE the
job-level `if:` runs — so the Issue comment cancelled the PR review already in
flight, and was then skipped itself, having done nothing.

That is the identical shape as the #386 defect the block above exists to fix,
one axis over: a run that will not review taking the lane from the run that
would have. #386 separated the two EVENTS; it did not separate the two number
spaces inside one event.

    pull_request        PR 433   -> pr-agent-pull_request-pr-433
    issue_comment on PR  30      -> pr-agent-issue_comment-pr-30
    issue_comment on ISSUE 30    -> pr-agent-issue_comment-issue-30

The last two used to be one group. actionlint and zizmor clean.

Upstream: wave-av/wave-foundation-public#73. Refs wave-pen#418, wave-pen#417.

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

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_54cb06d8-3aed-43ea-9a32-232b4971261a)

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Aug 24, 2026
A reviewer flagged the missing entry on wave-modules#41. 25 of the 28 repos in
this wave keep the same Keep-a-Changelog convention, so the entry lands in all
of them rather than only the repo whose review happened to catch it — fixing the
reported instance and leaving the class is the pattern this wave keeps undoing.

The change IS user-visible, which is why it belongs here: a maintainer's
`/review` on a fork PR is now declined with a warning instead of silently
running, so contributors on forks see different behaviour.

Refs wave-pen#418, wave-av/wave-foundation-public#73

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

cursor Bot commented Aug 24, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5a0f38c1-fc7b-4f5d-af9f-0491088ab425)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant