1. Executive Summary
Over the last 24 hours the PR Review Agent completed 22 runs: 19 succeeded and 3 failed (13.6% failure rate). The single fully-logged failure (run #599) reveals two compounding errors during the post-review branch-update step for petry-projects/markets/pull/99: a GitHub GraphQL API 504 Gateway Timeout followed immediately by a 403 "user doesn't have permission to update head repository". Together these cause review-one-pr.sh to exit non-zero, which the batch loop treats as session-fatal, aborting the entire run. The 403 is a structural permission gap — the bot PAT (DON_PETRY_BOT_PETRY_PROJECT_PAT) cannot push to PR head branches it doesn't own. Logs for runs #595 and #596 are unavailable, but their timing (two consecutive failures at 13:45 and 14:52, followed by a clean run at 15:46) is consistent with the same transient-plus-permission pattern. Urgency: DEGRADED — the pipeline recovers and retries, but any PR requiring a rebase will reliably abort the batch session.
2. Failure Breakdown
| Failure Category |
Affected Runs |
Example Error Message |
| Permission / auth error (403) |
#599 (confirmed), #595/#596 (probable) |
{"message":"user doesn't have permission to update head repository","status":"403"} |
| Timeout / infrastructure (504) |
#599 (confirmed) |
HTTP 504: 504 Gateway Timeout (https://api.github.com/graphql) |
| Review dismiss failure (422) |
#599 (warning, non-fatal) |
gh: Validation Failed (HTTP 422) — failed to dismiss prior agent review 4125073439 |
| Unknown / log unavailable |
#595, #596 |
(logs not retrieved for runs 25380169511 and 25383796920) |
3. Error Patterns
Pattern A — 403 on branch update ("update head repository")
Exact error:
{"message":"user doesn't have permission to update head repository",
"documentation_url":"https://docs.github.com/rest/pulls/pulls#update-a-pull-request-branch",
"status":"403"}
Step / script: Review each PR (cascade) → scripts/review-one-pr.sh, in the "Branch is BEHIND, requesting rebase…" code path (REST PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch).
Root cause: The donpetry-bot PAT does not have write access to the source/head repository of the PR. For PRs opened from branches inside petry-projects/* repos where the bot is not a collaborator with push rights — or for any fork-based PR — GitHub's API rejects branch-update requests with 403. The review itself succeeded (APPROVED was posted); only the rebase request failed. However review-one-pr.sh propagates this non-zero exit to the batch loop, which classifies it as session-fatal and aborts all remaining candidates.
Pattern B — 504 Gateway Timeout on GraphQL
Exact error:
HTTP 504: 504 Gateway Timeout (https://api.github.com/graphql)
Step / script: Same step as Pattern A — the review-one-pr.sh rebase path, just before the 403. The 504 is from a GraphQL call (likely checking branch status or issuing the update mutation) and reflects transient GitHub API instability at that moment (17:44 UTC on May 5). It does not cause the abort by itself, but it precedes and likely compounds the 403 handling.
Pattern C — 422 on prior review dismissal (non-fatal warning)
Exact error:
cleanup: failed to dismiss prior agent review 4125073439 on
https://github.com/petry-projects/markets/pull/99 —
duplicates will stack until resolved. API said: gh: Validation Failed (HTTP 422)
Step / script: review-one-pr.sh, in the pre-post cleanup that dismisses the previous bot review before submitting a new one.
Root cause: The review 4125073439 is in a state that cannot be dismissed via the API — most likely it was already dismissed, submitted as a non-pending review, or the PR was merged/closed between enumeration and review. The script correctly emits a ::warning:: and continues, so this does not cause the failure, but stale duplicate reviews will accumulate on the PR.
4. Token Scope Analysis
Scopes currently confirmed present (from gh auth status in run #599):
| Token |
Account |
Protocol |
Notes |
DON_PETRY_BOT_PETRY_PROJECT_PAT (github_pat_11CDFSYKQ0…) |
donpetry-bot |
HTTPS |
Active, authenticated |
The workflow's permissions: block grants:
contents: read
pull-requests: write
checks: read
These are the workflow-token permissions; the actual API calls use the PAT, which operates under its own OAuth scopes.
Scopes that appear missing or insufficient:
| Scope / Permission |
Evidence of Gap |
Recommendation |
Write access to petry-projects/* head branches |
403 on PUT .../update-branch for markets/pull/99 |
Add donpetry-bot as a collaborator with Write or Maintain role on each repo in petry-projects org, OR grant it org-level member write rights; alternatively, give petry-projects org owner the bot with a role that allows branch updates |
| GraphQL API stability |
504 on api.github.com/graphql |
Not a scope issue — transient infrastructure; retry logic in the script would mitigate |
| Pull request review dismiss |
422 on dismiss of review 4125073439 |
Possibly a timing/state issue rather than a scope gap; verify the review state before attempting dismissal |
5. Recommendations
-
[CRITICAL] Make the rebase/branch-update step non-fatal in review-one-pr.sh
The 403 on branch update should be a warning, not a session-abort. The review was already posted successfully; failing to request a rebase is cosmetic. Change the branch-update call to use || true or an explicit rc check that treats the 403/non-zero exit as a ::warning:: and continues. The current code propagates the error as exit code 1, which the batch loop treats as session-fatal and halts all remaining PRs.
- File:
scripts/review-one-pr.sh — branch-update / rebase-request section
- Why: A permanent permission gap (bot cannot push to head repos it doesn't own) is causing every batch that encounters a behind-branch PR to abort early
- Expected impact: Eliminates the session-abort failure mode; the rebase warning will be visible in logs but the queue will complete
- Urgency: CRITICAL
-
[HIGH] Grant donpetry-bot write access to petry-projects repos (or use a token that has it)
If requesting rebases is a desired feature (not just cosmetic), the bot's PAT must have push access to the head branch. Either:
-
Add donpetry-bot as a collaborator with Write role in each petry-projects repo, or
-
Create a scoped installation token (GitHub App) with contents: write on petry-projects — this is more secure than a broad PAT
-
File: GitHub repo/org settings + secrets management
-
Why: Without write access the 403 is permanent; every PR that falls behind main will fail to get the auto-rebase
-
Expected impact: Restores auto-rebase functionality; removes the root cause of the 403
-
Urgency: HIGH
-
[HIGH] Retrieve and inspect logs for runs 💡 Per-Tier MCP Tool Gating with Selective LSP Tool Exposure #595 and 💡 Token Cost Observatory LSP Metrics: Measuring the ROI of Semantic Code Intelligence #596
Two consecutive failures around 13:45–14:52 UTC on May 5 have no available logs. Without them, the diagnosis is incomplete. Retrieve them via gh run view 25380169511 --log and gh run view 25383796920 --log to confirm whether they share the same 403/504 root cause or represent a separate issue.
- File: Health-check / monitoring script
- Why: The pattern (two failures, then recovery) could indicate a different transient error (rate limit, infra blip) that may recur
- Urgency: HIGH
-
[MEDIUM] Add retry logic around the GraphQL/branch-update call
The 504 Gateway Timeout is transient but precedes the 403 in the failure chain. A short exponential-backoff retry (2–3 attempts) around the branch-update REST call would absorb GitHub API blips without propagating them as failures.
- File:
scripts/review-one-pr.sh — branch-update section
- Why: Transient 504s from
api.github.com/graphql are expected occasionally; retrying is safer than failing
- Expected impact: Reduces transient-infrastructure failures; does not fix the 403
- Urgency: MEDIUM
-
[MEDIUM] Guard review dismissal against already-dismissed or terminal-state reviews
Before calling gh pr review dismiss, check the current state of the review. If it is already dismissed, in a merged PR, or in a non-dismissable state, skip the dismiss call silently rather than emitting a ::warning:: about stacking duplicates.
- File:
scripts/review-one-pr.sh — pre-post cleanup section
- Why: The 422 will recur on any PR that gets reviewed twice in short succession or where the prior review state has changed; the warning is noisy and the stacked duplicates will confuse PR authors
- Urgency: MEDIUM
-
[LOW] Surface log-retrieval failures in the health report
Runs 💡 Per-Tier MCP Tool Gating with Selective LSP Tool Exposure #595 and 💡 Token Cost Observatory LSP Metrics: Measuring the ROI of Semantic Code Intelligence #596 show (log unavailable) in the report. The health-check script should distinguish between "run failed and log was fetched" vs "run failed and log could not be retrieved," and should alert (e.g., via ::warning::) when failure logs are missing so the operator knows the diagnosis is incomplete.
- File: Health-check / log-fetch script
- Urgency: LOW
6. Health Score
Health: 7/10 — Pipeline is mostly functional but a structural 403 permission gap causes every batch that encounters a behind-branch PR to abort early, and two additional failures remain undiagnosed due to missing logs.
1. Executive Summary
Over the last 24 hours the PR Review Agent completed 22 runs: 19 succeeded and 3 failed (13.6% failure rate). The single fully-logged failure (run #599) reveals two compounding errors during the post-review branch-update step for
petry-projects/markets/pull/99: a GitHub GraphQL API 504 Gateway Timeout followed immediately by a 403 "user doesn't have permission to update head repository". Together these causereview-one-pr.shto exit non-zero, which the batch loop treats as session-fatal, aborting the entire run. The 403 is a structural permission gap — the bot PAT (DON_PETRY_BOT_PETRY_PROJECT_PAT) cannot push to PR head branches it doesn't own. Logs for runs #595 and #596 are unavailable, but their timing (two consecutive failures at 13:45 and 14:52, followed by a clean run at 15:46) is consistent with the same transient-plus-permission pattern. Urgency: DEGRADED — the pipeline recovers and retries, but any PR requiring a rebase will reliably abort the batch session.2. Failure Breakdown
{"message":"user doesn't have permission to update head repository","status":"403"}HTTP 504: 504 Gateway Timeout (https://api.github.com/graphql)gh: Validation Failed (HTTP 422)— failed to dismiss prior agent review41250734393. Error Patterns
Pattern A — 403 on branch update ("update head repository")
Exact error:
Step / script:
Review each PR (cascade)→scripts/review-one-pr.sh, in the "Branch is BEHIND, requesting rebase…" code path (RESTPUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch).Root cause: The
donpetry-botPAT does not have write access to the source/head repository of the PR. For PRs opened from branches insidepetry-projects/*repos where the bot is not a collaborator with push rights — or for any fork-based PR — GitHub's API rejects branch-update requests with 403. The review itself succeeded (APPROVED was posted); only the rebase request failed. Howeverreview-one-pr.shpropagates this non-zero exit to the batch loop, which classifies it as session-fatal and aborts all remaining candidates.Pattern B — 504 Gateway Timeout on GraphQL
Exact error:
Step / script: Same step as Pattern A — the
review-one-pr.shrebase path, just before the 403. The 504 is from a GraphQL call (likely checking branch status or issuing the update mutation) and reflects transient GitHub API instability at that moment (17:44 UTC on May 5). It does not cause the abort by itself, but it precedes and likely compounds the 403 handling.Pattern C — 422 on prior review dismissal (non-fatal warning)
Exact error:
Step / script:
review-one-pr.sh, in the pre-post cleanup that dismisses the previous bot review before submitting a new one.Root cause: The review
4125073439is in a state that cannot be dismissed via the API — most likely it was already dismissed, submitted as a non-pending review, or the PR was merged/closed between enumeration and review. The script correctly emits a::warning::and continues, so this does not cause the failure, but stale duplicate reviews will accumulate on the PR.4. Token Scope Analysis
Scopes currently confirmed present (from
gh auth statusin run #599):DON_PETRY_BOT_PETRY_PROJECT_PAT(github_pat_11CDFSYKQ0…)donpetry-botThe workflow's
permissions:block grants:contents: readpull-requests: writechecks: readThese are the workflow-token permissions; the actual API calls use the PAT, which operates under its own OAuth scopes.
Scopes that appear missing or insufficient:
petry-projects/*head branchesPUT .../update-branchformarkets/pull/99donpetry-botas a collaborator withWriteorMaintainrole on each repo inpetry-projectsorg, OR grant it org-level member write rights; alternatively, givepetry-projectsorg owner the bot with a role that allows branch updatesapi.github.com/graphql41250734395. Recommendations
[CRITICAL] Make the rebase/branch-update step non-fatal in
review-one-pr.shThe 403 on branch update should be a warning, not a session-abort. The review was already posted successfully; failing to request a rebase is cosmetic. Change the branch-update call to use
|| trueor an explicitrccheck that treats the 403/non-zero exit as a::warning::and continues. The current code propagates the error as exit code 1, which the batch loop treats as session-fatal and halts all remaining PRs.scripts/review-one-pr.sh— branch-update / rebase-request section[HIGH] Grant
donpetry-botwrite access topetry-projectsrepos (or use a token that has it)If requesting rebases is a desired feature (not just cosmetic), the bot's PAT must have push access to the head branch. Either:
Add
donpetry-botas a collaborator withWriterole in eachpetry-projectsrepo, orCreate a scoped installation token (GitHub App) with
contents: writeonpetry-projects— this is more secure than a broad PATFile: GitHub repo/org settings + secrets management
Why: Without write access the 403 is permanent; every PR that falls behind main will fail to get the auto-rebase
Expected impact: Restores auto-rebase functionality; removes the root cause of the 403
Urgency: HIGH
[HIGH] Retrieve and inspect logs for runs 💡 Per-Tier MCP Tool Gating with Selective LSP Tool Exposure #595 and 💡 Token Cost Observatory LSP Metrics: Measuring the ROI of Semantic Code Intelligence #596
Two consecutive failures around 13:45–14:52 UTC on May 5 have no available logs. Without them, the diagnosis is incomplete. Retrieve them via
gh run view 25380169511 --logandgh run view 25383796920 --logto confirm whether they share the same 403/504 root cause or represent a separate issue.[MEDIUM] Add retry logic around the GraphQL/branch-update call
The 504 Gateway Timeout is transient but precedes the 403 in the failure chain. A short exponential-backoff retry (2–3 attempts) around the branch-update REST call would absorb GitHub API blips without propagating them as failures.
scripts/review-one-pr.sh— branch-update sectionapi.github.com/graphqlare expected occasionally; retrying is safer than failing[MEDIUM] Guard review dismissal against already-dismissed or terminal-state reviews
Before calling
gh pr review dismiss, check the current state of the review. If it is already dismissed, in a merged PR, or in a non-dismissable state, skip the dismiss call silently rather than emitting a::warning::about stacking duplicates.scripts/review-one-pr.sh— pre-post cleanup section[LOW] Surface log-retrieval failures in the health report
Runs 💡 Per-Tier MCP Tool Gating with Selective LSP Tool Exposure #595 and 💡 Token Cost Observatory LSP Metrics: Measuring the ROI of Semantic Code Intelligence #596 show
(log unavailable)in the report. The health-check script should distinguish between "run failed and log was fetched" vs "run failed and log could not be retrieved," and should alert (e.g., via::warning::) when failure logs are missing so the operator knows the diagnosis is incomplete.6. Health Score
Health: 7/10 — Pipeline is mostly functional but a structural 403 permission gap causes every batch that encounters a behind-branch PR to abort early, and two additional failures remain undiagnosed due to missing logs.