Skip to content

fix(pr-agent): classify timeouts per ATTEMPT, not on total job time - #39

Open
yakimoto wants to merge 1 commit into
mainfrom
fix/pr-agent-per-attempt-timeout
Open

fix(pr-agent): classify timeouts per ATTEMPT, not on total job time#39
yakimoto wants to merge 1 commit into
mainfrom
fix/pr-agent-per-attempt-timeout

Conversation

@yakimoto

@yakimoto yakimoto commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Re-syncs this repo to wave-foundation-public#72, which landed after the inline pr-agent lane was adopted here. Tracked as wave-pen#417.

The defect

The adopted template stamped AGENT_START once, before attempt 1, then compared total job time — attempt 1 + the 45s backoff + attempt 2 — against STEP_BUDGET_S=360, a budget its own comment calls per-attempt.

Two healthy-but-slow attempts (~180s each, ~405s together) therefore reported:

pr-agent TIMED OUT … A hang, NOT a rate limit.

…sending the next reader to debug a hang that never happened. The else-branch lied the other way, asserting 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 before acting.

The fix

Stamp each attempt separately and classify on the longest attempt, with if: always() end stamps so an attempt killed by its step timeout still records one — exactly the case the classifier exists to catch. Total wall time is still reported as context but no longer decides the verdict.

case now before
180s + 180s (405s total) failed after 2 attempts TIMED OUT
attempt killed at ~358s TIMED OUT

Not urgent, not ignorable

The defect is in a message, not behaviour — the lane still retries, still renders NEUTRAL, still never blocks a PR. But that verdict step exists precisely because "a confidently wrong cause is worse than no cause", so a classifier that can misname a hang defeats its own purpose.

Job id pr_agent and every on: trigger unchanged — the job id is the check-run context and branch protection matches on it.

Refs wave-av/wave-pen#417, wave-av/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

Low Risk
CI diagnostic messaging only; retry, check-run identity, and non-blocking advisory behavior are unchanged.

Overview
Fixes the pr-agent verdict so hang vs rate-limit is decided from per-attempt duration, not wall-clock job time.

The workflow used to stamp one AGENT_START and compare total time (attempt 1 + 45s backoff + attempt 2) against the 360s per-attempt budget. Two slow-but-healthy ~180s attempts (~405s together) were therefore labeled a hang. Each attempt now gets start/end stamps (if: always() so a timeout-killed step still records an end), and the classifier uses the longest attempt plus 15s slack. Wall time is still logged but no longer drives the verdict. Retry and NEUTRAL/non-blocking behavior are unchanged.

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

Review in cubic

Summary by Sourcery

Classify pr-agent failures using per-attempt timeout durations so slow retries are not incorrectly reported as hangs while preserving advisory, retry, and non-blocking behavior.

Bug Fixes:

  • Correct timeout classification in the pr-agent workflow by evaluating the longest individual attempt rather than total job duration.
  • Ensure attempts that are terminated by step timeouts are included in timeout diagnostics.

Enhancements:

  • Retain total wall-clock duration as contextual logging while making per-attempt duration the basis for verdicts.

Re-syncs this repo to wave-foundation-public#72, which landed after the inline
lane was adopted here.

THE DEFECT. The adopted template stamped AGENT_START once, before attempt 1,
then compared TOTAL job time — attempt 1 + the 45s backoff + attempt 2 —
against STEP_BUDGET_S=360, a budget its own comment calls PER-ATTEMPT. Two
healthy-but-slow attempts (~180s each, ~405s together) therefore reported

  "pr-agent TIMED OUT ... A hang, NOT a rate limit."

sending the next reader to debug a hang that never happened; the else-branch
lied the other way, asserting 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 before
acting.

THE FIX. Stamp each attempt separately and classify on the LONGEST attempt,
with if: always() end stamps so an attempt killed BY its step timeout still
records one — exactly the case the classifier exists to catch. Total wall time
is still reported as context but no longer decides the verdict.

NOT URGENT, NOT IGNORABLE. The defect is in a MESSAGE, not in behaviour: the
lane still retries, still renders NEUTRAL, still never blocks a PR. But that
verdict step exists precisely because "a confidently wrong cause is worse than
no cause", so shipping a classifier that can misname a hang defeats its purpose.

Job id pr_agent and every on: trigger unchanged — the job id is the check-run
context and branch protection matches on it.

Refs wave-av/wave-pen#417, wave-av/wave-pen#388
@codeant-ai

codeant-ai Bot commented Aug 24, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR fbf5313 Aug 24, 2026 · 13:38 13:38

@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_34975281-3570-4902-9076-c2b6559de2d0)

@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 28 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: d0635f12-282e-41a0-a821-05327785329f

📥 Commits

Reviewing files that changed from the base of the PR and between 438281f and fbf5313.

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

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

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Aug 24, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 24, 2026

Copy link
Copy Markdown

Approvability

Verdict: Would Approve

Macroscope's review found this PR approvable — This is a narrowly scoped CI diagnostic fix that measures each pr-agent attempt separately and leaves retry behavior, advisory status, and production workflows unchanged. The sole modified workflow is owned by the author, with no schema, deployment, security, billing, or Macroscope configuration impact.

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.

@gitar-bot

gitar-bot Bot commented Aug 24, 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

Refactors PR-agent timeout classification to evaluate per-attempt duration rather than total job time, preventing false timeout reports on slow retries. 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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix pr-agent timeout classification to use per-attempt duration

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

Grey Divider

AI Description

• Stamp start/end timestamps per retry attempt (including always-run end stamps).
• Classify “TIMED OUT” using the longest attempt, not total wall-clock time.
• Keep total wall time in messaging for context without driving the verdict.
Diagram

graph TD
  J["pr_agent job"] --> A1["Attempt 1 run"] --> BO["Backoff 45s"] --> A2["Attempt 2 run"] --> V["Verdict (per-attempt)"] --> R[["Check message (neutral/ok)"]
  A1 --> V
Loading
High-Level Assessment

The per-attempt stamping approach is the most reliable option given GitHub Actions doesn’t expose a step-level timed_out signal. Using always-run end stamps and classifying on the longest attempt correctly distinguishes step-timeout kills from fast upstream failures while keeping wall time as contextual output.

Files changed (1) +44 / -6

Bug fix (1) +44 / -6
pr-agent.ymlRecord per-attempt timing and fix timeout verdict logic +44/-6

Record per-attempt timing and fix timeout verdict logic

• Replaces a single job-level start stamp with per-attempt start/end stamps (including always-run end stamps). Updates the verdict step to compute attempt durations, pick the longest attempt, and apply a small slack when comparing to the per-attempt step budget; total wall time remains informational only.

.github/workflows/pr-agent.yml

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Remediation recommended

1. Missing changelog for verdict text 📘 Rule violation § Compliance
Description
This PR changes user-visible GitHub Actions warning text for the pr-agent verdict, but
CHANGELOG.md has no Unreleased entry documenting the change. This violates the requirement to
document user-facing changes in the Unreleased section.
Code

.github/workflows/pr-agent.yml[R217-220]

+            echo "::warning::pr-agent TIMED OUT — the longest attempt ran ${LONGEST}s against a ${STEP_BUDGET_S}s per-attempt budget (attempt 1 ${A1}s, attempt 2 ${A2}s), so it 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)."
+          echo "::warning::pr-agent failed after 2 attempts (attempt 1 ${A1}s, attempt 2 ${A2}s, ${ELAPSED}s wall including the 45s backoff — NEITHER attempt reached the ${STEP_BUDGET_S}s per-attempt 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)."
Evidence
The workflow now emits updated, user-visible verdict warnings (shown in GitHub Actions logs/check
annotations), which qualifies as a user-facing change; however, CHANGELOG.md contains only the `##
[Unreleased]` header and no entries documenting this change.

Rule 2497952: Document user-facing changes in Unreleased section of CHANGELOG.md
.github/workflows/pr-agent.yml[216-220]
CHANGELOG.md[1-7]

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 PR changes user-visible workflow output (the `::warning::pr-agent ...` verdict messages) but there is no corresponding entry under `## [Unreleased]` in the root `CHANGELOG.md`.

## Issue Context
Compliance requires documenting user-facing changes in the `Unreleased` section.

## Fix Focus Areas
- CHANGELOG.md[7-7]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 1 rule
✅ REVIEW.md
Review mode: ⚖️ Balanced: This changes CI workflow timeout classification and retry-path behavior across several hunks; it is localized but has enough subtle control-flow and timing semantics to warrant a complete 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 on lines +217 to +220
echo "::warning::pr-agent TIMED OUT — the longest attempt ran ${LONGEST}s against a ${STEP_BUDGET_S}s per-attempt budget (attempt 1 ${A1}s, attempt 2 ${A2}s), so it 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)."
echo "::warning::pr-agent failed after 2 attempts (attempt 1 ${A1}s, attempt 2 ${A2}s, ${ELAPSED}s wall including the 45s backoff — NEITHER attempt reached the ${STEP_BUDGET_S}s per-attempt 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)."

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Missing changelog for verdict text 📘 Rule violation § Compliance

This PR changes user-visible GitHub Actions warning text for the pr-agent verdict, but
CHANGELOG.md has no Unreleased entry documenting the change. This violates the requirement to
document user-facing changes in the Unreleased section.
Agent Prompt
## Issue description
The PR changes user-visible workflow output (the `::warning::pr-agent ...` verdict messages) but there is no corresponding entry under `## [Unreleased]` in the root `CHANGELOG.md`.

## Issue Context
Compliance requires documenting user-facing changes in the `Unreleased` section.

## Fix Focus Areas
- CHANGELOG.md[7-7]

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

@bito-code-review

Copy link
Copy Markdown

The pull request modifies the warning messages emitted by the pr-agent GitHub Action, which constitutes a user-facing change. According to the project's compliance requirements, such changes must be documented in the ## [Unreleased] section of CHANGELOG.md. Please add an entry to CHANGELOG.md describing these updates to the pr-agent verdict messages.

@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

🍒 Ready to be cherry-picked — ✅ Merged (0) · ☑ Fixed (1)

Grey Divider

🔗 Fix PR: #40

This fix PR was closed automatically. Its branch is preserved so you can cherry pick the changes into the original PR.

Prompt for coding agent

This is an automated fix prepared on a separate branch (#40). It is NOT applied to this PR.
To use it: review Fix PR #40 (https://github.com/wave-av/wave-modules/pull/40), evaluate each change critically against your local context, and cherry-pick the changes that are correct into this branch. Do not accept them blindly.
Process — 1 fixed
  • ☑ Fixed: Missing changelog for verdict text

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

Sorry @yakimoto, you have reached your weekly rate limit of 250000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates the pr-agent workflow’s diagnostic classifier to measure the longest individual attempt rather than total job duration, preventing retries and backoff from causing false timeout/hang messages while preserving existing retry and non-blocking behavior.

Sequence diagram for per-attempt pr-agent timeout classification

sequenceDiagram
    participant Workflow
    participant Agent as PR-Agent
    participant Classifier

    Workflow->>Workflow: stamp attempt 1 start
    Workflow->>Agent: run attempt 1
    Workflow->>Workflow: stamp attempt 1 end
    alt attempt 1 failed
        Workflow->>Workflow: sleep 45s
        Workflow->>Workflow: stamp attempt 2 start
        Workflow->>Agent: run attempt 2
        Workflow->>Workflow: stamp attempt 2 end
    end
    Workflow->>Classifier: calculate A1, A2, and LONGEST
    alt LONGEST >= STEP_BUDGET_S - 15
        Classifier-->>Workflow: report TIMED OUT and render NEUTRAL
    else longest attempt below budget
        Classifier-->>Workflow: report failed after attempts and render NEUTRAL
    end
Loading

Flow diagram for longest-attempt verdict selection

flowchart TD
    A[PR-Agent attempt durations recorded] --> B[Calculate A1 and A2]
    B --> C[Select LONGEST attempt]
    C --> D{LONGEST >= STEP_BUDGET_S - 15?}
    D -->|Yes| E[TIMED OUT: likely hang]
    D -->|No| F[Failed after attempts: likely rate limit]
    E --> G[Render NEUTRAL; do not block PR]
    F --> G
Loading

File-Level Changes

Change Details Files
Track and classify timeout duration independently for each PR-Agent attempt.
  • Replace the single job-level start timestamp with attempt 1 start/end and conditional attempt 2 start/end stamps.
  • Use if: always() for end stamps so timeout-killed attempts remain measurable.
  • Classify using the longest attempt with 15 seconds of timeout slack, while retaining total wall time for diagnostics only.
.github/workflows/pr-agent.yml
Preserve advisory retry and non-blocking workflow behavior while correcting failure messaging.
  • Keep the existing retry, triggers, job identity, and NEUTRAL outcome behavior unchanged.
  • Update timeout and upstream-error warnings to report per-attempt durations and avoid attributing healthy slow attempts to hangs.
.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

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

Labels

size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant