Handle update_pull_request update-branch soft 422s as non-fatal in safe outputs#32246
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
update_pull_request update-branch soft 422s as non-fatal in safe outputs
There was a problem hiding this comment.
Pull request overview
Fixes intermittent safe_outputs failures by treating specific benign pulls.updateBranch 422 responses as non-fatal during update_pull_request processing.
Changes:
- Add a classifier to detect “already up-to-date” and “merge conflict”
updateBrancherrors and continue processing without throwing. - Add tests covering the two non-fatal
updateBranchscenarios (no-op and “continue with title/body update”). - Document best-effort
update-branchsemantics for these soft failure cases.
Show a summary per file
| File | Description |
|---|---|
| docs/src/content/docs/reference/safe-outputs-pull-requests.md | Documents that two specific updateBranch outcomes are treated as best-effort (warning + continue). |
| actions/setup/js/update_pull_request.test.cjs | Adds test coverage for the two newly non-fatal updateBranch error paths. |
| actions/setup/js/update_pull_request.cjs | Introduces isNonFatalUpdateBranchError and updates updateBranch error handling to warn+continue for soft 422s. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 3/3 changed files
- Comments generated: 1
| const hasStatus = typeof error === "object" && error !== null && "status" in error; | ||
| if (hasStatus && error.status !== 422) { |
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnose + /tdd — this is a targeted bug fix with new regression tests.
Key Themes
- Root cause addressed correctly: the fix introduces a dedicated classifier
isNonFatalUpdateBranchErrorrather than burying the logic inline, which is exactly the right seam for future changes. - Regression test coverage is good: both known-benign messages are exercised, including the "continues processing" scenario (title update after merge-conflict soft-fail). One missing edge case noted inline.
- Classifier boundary: the implicit pass-through for errors without a
statusfield is correct behaviour but should carry a comment to prevent accidental "cleanup" later.
Positive Highlights
- ✅ Clean extraction of
isNonFatalUpdateBranchErroras a named, testable predicate - ✅ Warning message clearly distinguishes
(non-fatal)from fatal errors — good observability - ✅ Documentation updated in the same PR, including the exact GitHub messages users may see
- ✅ PR description includes a post-mortem — clear root cause and fix rationale
Verdict
Approving — the two inline comments are suggestions only (a missing boundary test and a clarifying comment). Neither blocks correctness.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 5.1M
| // These should not fail safe output processing. | ||
| const message = getErrorMessage(error).toLowerCase(); | ||
| return ( | ||
| message.includes("there are no new commits on the base branch") || message.includes("merge conflict between base and head") |
There was a problem hiding this comment.
[/tdd] The classifier allows errors without a status property (plain Error objects) to bypass the early-return guard and still match on message alone. In production the GitHub API always provides an HTTP status, so this is unlikely in practice.
A brief comment would prevent future readers from "fixing" it inadvertently:
// Errors without a `status` field (e.g. plain Error objects from tests or
// unexpected throws) also reach the message check. If the message matches a
// known benign condition they are treated as non-fatal.
const message = getErrorMessage(error).toLowerCase();| }); | ||
| expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("branch from base (non-fatal)")); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
[/tdd] The two new tests cover the two known-benign 422 messages, but there's no test verifying that a different 422 message remains fatal. That boundary condition is the riskiest part of the classifier — without it, a future edit could accidentally widen the non-fatal set.
Consider adding:
it("should re-throw a 422 with an unrecognised message as fatal", async () => {
const err = Object.assign(new Error("Unprocessable Entity: unknown reason"), { status: 422 });
mockGithub.rest.pulls.updateBranch.mockRejectedValueOnce(err);
const handler = await updatePRModule.main({ update_branch: true });
const result = await handler({ pull_request_number: 100 });
expect(result.success).toBe(false);
expect(mockCore.warning).not.toHaveBeenCalledWith(expect.stringContaining("(non-fatal)"));
});
🧪 Test Quality Sentinel ReportTest Quality Score: 100/100✅ Excellent test quality
Test Classification Details
Test AnalysisBoth new tests verify behavioral contracts of the soft-422 / non-fatal error handling introduced in this PR: Test 1 — Test 2 — Mocking targets ( Language SupportTests analyzed:
Verdict
📖 Understanding Test ClassificationsDesign Tests (High Value) verify what the system does:
Implementation Tests (Low Value) verify how the system does it:
Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.
|
|
@copilot review all comments and reviews |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Reviewed all comments/reviews and applied the requested follow-up in |
Bug Fix
PR Sous Chefwas intermittently failing insafe_outputsbecauseupdate_pull_requesttreated benignpulls.updateBranchresponses as hard failures. Specifically, “no new commits on base” and “merge conflict between base and head” were counted as failed safe outputs, causing avoidable workflow failures.What was the bug?
update_pull_requestwithupdate_branch: truethrew on allupdateBranchfailures.safe_outputsexited non-zero despite successful agent/detection execution and no actionable defect.How did you fix it?
- Handler error classification (
actions/setup/js/update_pull_request.cjs)isNonFatalUpdateBranchError(error)classifier.There are no new commits on the base branchmerge conflict between base and head- Behavior change in update flow
non-fatal)- Tests (
actions/setup/js/update_pull_request.test.cjs)- Docs (
docs/src/content/docs/reference/safe-outputs-pull-requests.md)update-branchbest-effort semantics for these two soft responses.Testing