Skip to content

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

Open
yakimoto wants to merge 5 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#82
yakimoto wants to merge 5 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

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


Note

Medium Risk
CI-only, but the job holds OPENAI_KEY and PR write access; fork gating, concurrency, and fail-closed classification are security- and cost-sensitive.

Overview
Makes the advisory pr-agent check actually run on this public repo. GitHub cannot call a private reusable workflow from a public one, so the old uses: never created a job (red check, no log).

The workflow is now an inline copy of the foundation lane: event- and PR/issue-keyed concurrency so comments no longer cancel in-flight reviews, a fail-closed fork gate for / commands (job if: cannot see fork status on issue_comment), and a 6-minute attempt with one retry.

Failures are classified (timeout vs 429 vs cancel vs skipped) and rendered neutral so a flaked reviewer cannot block the PR. CONFIG__AI_TIMEOUT is 300s so it sits inside the step budget. Fork /review is now declined with a warning.

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

Summary by Sourcery

Adopt a self-contained PR-Agent workflow for public repositories with safer triggering and resilient, non-blocking review execution.

Bug Fixes:

  • Run the PR-Agent lane inline so public repositories receive working review checks instead of failing when calling a private reusable workflow.
  • Prevent fork-based slash-command reviews from using the repository's AI credentials by validating fork status and failing closed.
  • Ensure advisory PR-Agent failures, timeouts, and cancellations do not block pull requests.

Enhancements:

  • Improve review execution reliability with event-aware concurrency, per-attempt timeouts, retry backoff, and explicit outcome classification.

Review in cubic


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


CodeAnt-AI Description

Make PR-Agent reviews run reliably in public repositories

What Changed

  • Runs PR-Agent directly in this public repository instead of calling a private reusable workflow, so pull requests receive an actual review check and log
  • Prevents comment events from cancelling an in-progress review triggered by a pull request update
  • Retries failed reviews once after a short delay and reports temporary reviewer failures without blocking the pull request
  • Limits each review attempt and distinguishes reviewer failures, timeouts, and cancelled runs

Impact

✅ Working PR-Agent checks for public-repository pull requests
✅ Fewer reviews cancelled by unrelated PR comments
✅ Fewer pull requests blocked by advisory reviewer outages

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.


PR Type

Bug fix


Description

  • Replaced private workflow reference with inline pr-agent configuration

  • Added fork gate validation for issue_comment events

  • Implemented retry logic with timeout management

  • Enhanced concurrency handling and error classification


Diagram Walkthrough

flowchart LR
  A["pr-agent.yml"] --> B["Fork gate validation"]
  A --> C["Retry logic with timeouts"]
  A --> D["Enhanced concurrency management"]
  A --> E["Error classification improvements"]
Loading

File Walkthrough

Relevant files
Enhancement
pr-agent.yml
Adopt inline pr-agent workflow with enhanced reliability 

.github/workflows/pr-agent.yml

  • Replaced reference to private reusable workflow with inline
    configuration
  • Added fork validation for issue_comment events
  • Implemented two-attempt retry mechanism with backoff
  • Enhanced timeout handling and error classification
  • Updated concurrency grouping strategy
+325/-6 
Documentation
CHANGELOG.md
Update changelog with pr-agent workflow improvements         

CHANGELOG.md

  • Documented fix for pr-agent workflow failures
  • Added details about fork validation improvements
  • Recorded changes to timeout configurations
  • Noted improvements in error handling
+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 f441640 Aug 24, 2026 · 14:11 14:12
✅ Reviewed your PR be83043 Aug 23, 2026 · 22:33 22:33

@sourcery-ai

sourcery-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

Replaces the broken reusable pr-agent workflow reference with an inline, self-contained workflow copied from wave-foundation-public, and updates it to match the current reusable lane including concurrency, timeouts, retry, and verdict handling so the pr-agent check runs reliably on this public repo.

Sequence diagram for the inline PR-Agent review lane

sequenceDiagram
    participant GitHub
    participant Workflow
    participant Agent as PR_Agent
    participant Router as LLM_Router
    participant Verdict

    GitHub->>Workflow: trigger pull_request or issue_comment
    Workflow->>Workflow: evaluate job if condition
    Workflow->>Agent: run PR-Agent attempt 1
    Agent->>Router: request review
    alt attempt 1 succeeds
        Agent-->>Verdict: success
    else attempt 1 fails
        Workflow->>Workflow: backoff before retry
        Workflow->>Agent: run PR-Agent retry attempt 2
        Agent->>Router: request review
        Agent-->>Verdict: success or failure
    end
    Verdict->>Verdict: classify outcome
    Verdict-->>GitHub: advisory result, workflow faults remain visible
Loading

Flow diagram for PR-Agent workflow eligibility and concurrency

flowchart TD
    Trigger["pull_request or issue_comment"] --> EventKey["Concurrency key includes event_name"]
    EventKey --> Eligible{"Job eligibility conditions pass?"}
    Eligible -- No --> Skip["Skip job"]
    Eligible -- Yes --> Run["Run inline pr_agent job"]
    Run --> Review["PR-Agent attempt with 6-minute timeout"]
    Review --> Retry{"Attempt fails?"}
    Retry -- Yes --> Backoff["Sleep 45 seconds"]
    Backoff --> RetryAttempt["PR-Agent retry with 6-minute timeout"]
    RetryAttempt --> Verdict["Always run verdict"]
    Retry -- No --> Verdict
    Verdict --> Result["Classify success, cancellation, timeout, or failure"]
Loading

File-Level Changes

Change Details Files
Inline and modernize the pr-agent GitHub Actions workflow so public repos no longer depend on a private reusable workflow and the lane behaves like the current reusable implementation.
  • Replace the previous uses: wave-av/wave-foundation/...reusable-pr-agent.yml@... job invocation with a fully inline pr_agent job based on The-PR-Agent/pr-agent@v0.42.0 and the org’s OPENAI dispatch config.
  • Adjust concurrency group to include the GitHub event name so pull_request and issue_comment events do not cancel each other while still superseding multiple pushes for the same PR.
  • Add a guarded if: expression to run only on trusted slash commands for issue_comment events and on non-bot, non-draft, non-fork pull_request events.
  • Introduce a 15-minute job-level timeout plus 6-minute step-level timeouts for each pr-agent attempt to distinguish hangs from normal failures and keep both attempts within the overall budget.
  • Add environment configuration for pr-agent (models, router base URL, token limits, GitHub token, auto_review/improve flags, suggestion thresholds) identical across the main run and retry.
  • Implement a 45-second backoff and a second pr-agent attempt when the first attempt fails, both with continue-on-error so failures can be classified later.
  • Add a "verdict" step that always runs, classifies outcomes (never ran, cancelled, timed out, genuine failure) using step outcomes and elapsed time, and ensures pr-agent is advisory-only by emitting errors/warnings but not blocking the PR.
.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

@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_2cf04571-a67c-483a-80fa-0bd0ea373b8f)

@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: 3b0324c6-8672-4256-a099-0b2ed88c71ed

📥 Commits

Reviewing files that changed from the base of the PR and between bc8c0e4 and d702122.

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

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

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

Copy link
Copy Markdown

PR Summary by Qodo

CI: inline pr-agent workflow for public repos (avoid private reusable uses)

🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Replace private reusable-workflow uses: with an inline, self-contained pr-agent job.
• Prevent issue-comment runs from cancelling in-flight PR reviews via event-scoped concurrency.
• Add retry, step timeouts, and a verdict step so the advisory reviewer never blocks PRs.
Diagram

graph TD
  Evt(["GitHub event"]) --> Cg["Concurrency group"] --> Gate{"Job if"} --> Agent[["PR-Agent run"]] --> Retry[["Retry + backoff"]] --> Verdict["Verdict step"] --> Check[("Check result")]
  Gate -- "skip" --> Check
  subgraph Legend
    direction LR
    _evt(["Event"]) ~~~ _dec{"Decision"} ~~~ _act[["Action step"]] ~~~ _chk[("Check run")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Host a public reusable workflow
  • ➕ Avoids inline duplication across public repos
  • ➕ Centralizes updates (reduces mirror drift risk)
  • ➖ Requires creating/maintaining a public workflow repo and migration work
  • ➖ Still needs careful permissions/secrets design for OSS constraints
2. Make the existing private workflow accessible
  • ➕ Keeps a single workflow source of truth
  • ➕ No per-repo workflow duplication
  • ➖ Not feasible under GitHub’s public→private reusable-workflow restriction
  • ➖ May conflict with org security posture (making wave-foundation public/shared)
3. Reduce the workflow to a minimal wrapper
  • ➕ Smaller surface area to review and maintain
  • ➕ Less chance of divergence from upstream behavior
  • ➖ May lose hard-won operational behavior (timeouts, retry, verdict classification)
  • ➖ Would reintroduce failure modes this PR explicitly addresses

Recommendation: Adopting the inline workflow is the correct immediate fix because it removes the invalid public→private reusable-workflow dependency and restores observable check runs. Longer-term, consider a public reusable workflow to eliminate mirror drift, but this PR appropriately prioritizes reliability and OSS constraints.

Files changed (1) +166 / -6

Other (1) +166 / -6
pr-agent.ymlInline pr-agent lane with event-scoped concurrency, retry, and verdict +166/-6

Inline pr-agent lane with event-scoped concurrency, retry, and verdict

• Replaces the private reusable-workflow reference with an inline job so the workflow can run in a public repo. Adds an event-name concurrency key, strict job gating for slash commands and non-fork PRs, step-level timeouts, a backoff+retry attempt, and a verdict step that classifies failures and prevents an advisory reviewer 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 turns an inert public-to-private workflow reference into an active, secret-backed PR automation lane. Its external action execution, PR write permissions, comment and fork gating, retries, and concurrency behavior create a substantial CI, security, and cost surface requiring human review.

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 1b14c32)

Here are some key observations to aid the review process:

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

Misclassification

ELAPSED is measured from AGENT_START (stamped before attempt 1) to the verdict step, so it covers attempt 1 + the 45s backoff + attempt 2, yet it is compared against STEP_BUDGET_S which is the per-attempt budget (360s). A realistic failure path — attempt 1 errors after ~200s, 45s sleep, attempt 2 errors after ~200s — yields ELAPSED ≈ 445s and reports "TIMED OUT ... an attempt was killed by its step timeout", which is exactly the confidently-wrong cause the comment above says to avoid. Either stamp/measure elapsed per attempt, or compare against 2 * STEP_BUDGET_S + 45.

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
echo "::warning::pr-agent failed after 2 attempts (45s backoff, ${ELAPSED}s total — well inside the ${STEP_BUDGET_S}s budget, so it returned an error rather than hanging) — most commonly an upstream 429/rate-limit from the LLM router. Rendering NEUTRAL: an advisory reviewer must not block the PR (#3128)."
Dead branch

The -z "$AGENT_OUTCOME" branch (the only path that exits non-zero) is effectively unreachable: steps.agent.outcome is always one of success/failure/cancelled/skipped once the step exists, and it is the first real step in the job. If the intent is to fail loudly when the reviewer never ran, the condition should also cover skipped.

if [ -z "$AGENT_OUTCOME" ]; then
  # The reviewer never ran: an earlier step failed or the job was cancelled.
  # A WORKFLOW fault, not a reviewer flake — stays loud.
  echo "::error::pr-agent never ran — an earlier step failed or the job was cancelled."
  exit 1
fi

@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

Adopts the inline PR-Agent workflow to replace the inaccessible private reusable workflow call, fixing public repository checks. 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 logic is wrong, reviews may be skipped, duplicated, or incorrectly rendered neutral, and the action may leave PR comments or suggestions behind that a revert will not remove. Those effects are bounded and can be cleaned up or rerun, though the workflow also handles a write-capable GitHub token and an API secret.


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 (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Timeout classification is inaccurate ✓ Resolved 🐞 Bug ◔ Observability
Description
The verdict step treats ELAPSED >= STEP_BUDGET_S as proof of a step timeout, but ELAPSED is
measured from a single timestamp set before attempt 1, so it includes backoff and both attempts.
This can mislabel slow-but-valid failures as “TIMED OUT,” masking real error causes and producing
misleading diagnostics.
Code

.github/workflows/pr-agent.yml[R177-180]

+          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
Relevance

●●● Strong

Single-start timestamp deterministically misclassifies retry/backoff time as step timeout; no
rejection precedent found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The workflow stamps only AGENT_START once, then later computes ELAPSED using that single
timestamp and compares it to STEP_BUDGET_S (360s). Because the job may include backoff and a
second attempt, ELAPSED can exceed 360s without any step timing out, so the classification can be
wrong.

.github/workflows/pr-agent.yml[72-77]
.github/workflows/pr-agent.yml[155-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
`AGENT_START` is stamped once at the top of the job, but the verdict logic compares total elapsed time against a **per-attempt** budget (360s). With retries and the 45s backoff, the total elapsed time can exceed 360s even when neither attempt hit its step timeout, leading to false “TIMED OUT” warnings.

### Issue Context
This affects only diagnostics (the step exits 0), but the goal of the verdict step is accurate classification; misclassification sends debugging in the wrong direction.

### Fix Focus Areas
- .github/workflows/pr-agent.yml[72-77]
- .github/workflows/pr-agent.yml[117-120]
- .github/workflows/pr-agent.yml[121-147]
- .github/workflows/pr-agent.yml[155-183]

### Proposed fix
Record a separate start timestamp for the retry attempt and classify timeouts based on the attempt that actually ran:
- Keep `AGENT_START` for attempt 1.
- Add a `stamp retry start` step immediately before `PR-Agent retry (attempt 2)` that sets `AGENT_RETRY_START`.
- In the verdict step:
 - If retry ran (e.g., `AGENT_RETRY_OUTCOME` is non-empty), compute elapsed since `AGENT_RETRY_START` when deciding whether attempt 2 timed out.
 - Otherwise compute elapsed since `AGENT_START` for attempt 1.
This keeps the timeout heuristic aligned with the step timeout budget and avoids false positives caused by backoff + multiple attempts.

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



Informational

2. Comments can cancel reviews 🐞 Bug ☼ Reliability
Description
Because the workflow triggers on every issue_comment but uses a shared issue_comment concurrency
group, any new comment (even non-/ and from untrusted users) can cancel an in-flight slash-command
review run for the same PR. This reintroduces a denial-of-service/cancellation path where harmless
comments stop the only run that actually performs the review.
Code

.github/workflows/pr-agent.yml[R51-53]

concurrency:
-  group: pr-agent-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}
+  group: pr-agent-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}
  cancel-in-progress: true
Relevance

● Weak

PR #81 repeatedly rejected closely matching concerns about shared issue-comment concurrency
cancellation.

PR-#81

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The workflow is configured to start on every issue_comment creation, but the job only runs when
the comment starts with / and is from a trusted association. Since concurrency is defined at the
workflow level with a stable issue_comment group per PR number, a non-slash comment still creates
a competing run in the same group and can cancel an in-flight slash-command run.

.github/workflows/pr-agent.yml[20-25]
.github/workflows/pr-agent.yml[51-53]
.github/workflows/pr-agent.yml[59-69]

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 uses workflow-level `concurrency` with `cancel-in-progress: true` and an `issue_comment` trigger that fires on **all comments**. Even though the job is gated to only run on slash commands from trusted associations, the **workflow run** still enters the `issue_comment` concurrency group and can cancel an in-flight slash-command run for the same PR.

### Issue Context
This makes it possible for normal chatter (or spam) comments to cancel maintainers’ `/review` runs, reducing reliability and creating an easy cancellation/DoS path for the reviewer lane.

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

### Proposed fix
Adjust concurrency so non-slash `issue_comment` runs do **not** share the same concurrency group as slash-command runs, e.g.:

Option A (recommended): add a suffix distinguishing slash commands:
```yml
concurrency:
 group: pr-agent-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}-${{ github.event_name != 'issue_comment' || startsWith(github.event.comment.body, '/') }}
 cancel-in-progress: true
```

Option B: only cancel-in-progress for PR push-triggered runs (and optionally for slash commands):
```yml
concurrency:
 group: pr-agent-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}
 cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '/')) }}
```
Either option prevents non-slash comments from canceling real review runs.

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


Grey Divider

Context sources
✅ REVIEW.md
Review mode: ⚖️ Balanced: This is a behavior-changing CI workflow with substantial new retry, timeout, concurrency, permissions, and failure-classification logic; it is not a purely localized trivial change, but the two edit sites do not justify redundant extended review.

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 Outdated
@bito-code-review

Copy link
Copy Markdown

The current verdict logic indeed uses a single AGENT_START timestamp, which causes the total elapsed time (including backoff and both attempts) to be compared against the per-attempt budget of 360 seconds. This leads to false "TIMED OUT" warnings when the total duration exceeds 360 seconds even if individual attempts were within their 6-minute limits.

To remediate this, you should implement a separate start timestamp for the retry attempt:

  1. Add a step to set AGENT_RETRY_START immediately before the retry attempt.
  2. Update the verdict logic to check if AGENT_RETRY_OUTCOME is non-empty; if so, calculate elapsed time using AGENT_RETRY_START for the second attempt, otherwise use AGENT_START for the first attempt.

.github/workflows/pr-agent.yml

- name: stamp retry start
        if: steps.agent.outcome == 'failure'
        run: echo "AGENT_RETRY_START=$(date +%s)" >> "$GITHUB_ENV"

      - name: PR-Agent retry (attempt 2)
        id: agent_retry

.github/workflows/pr-agent.yml

if [ -n "$AGENT_RETRY_OUTCOME" ]; then
            ELAPSED=$(( $(date +%s) - ${AGENT_RETRY_START:-$(date +%s)} ))
          else
            ELAPSED=$(( $(date +%s) - ${AGENT_START:-$(date +%s)} ))
          fi

…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 be83043 to 1b14c32 Compare August 23, 2026 22:39
@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_0d1ebf82-0989-464c-b342-652d05eba65f)

@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 1b14c32

@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

✅ Merged (0) · ☑ Fixed (0)

Process

  • No fixes were applied (no_fixes_applied)

@yakimoto
yakimoto enabled auto-merge August 24, 2026 13:21
…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_539fe50a-fb3d-4423-8842-f118c6559065)

@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
…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_8c60adb8-bf32-49f6-8986-9ff76cc0172c)

…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_0e165b8d-e3c0-4a13-b37f-a33fd788accf)

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_581eec2b-f411-4a55-b8a8-83fd2a68e8da)

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

Labels

Review effort 2/5 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