fix(push_branch): retry transient push errors and surface full stderr/stdout (#573)
* fix(push_branch): retry transient push errors and surface full stderr/stdout issue #571 motivated three small improvements to `mcp__pullfrog__push_branch`: 1. classify push errors into `concurrent-push` / `transient` / `unknown`. - `concurrent-push` extends the existing `fetch first` / `non-fast-forward` matcher to also catch the server-side `cannot lock ref` form (the case #571 reports). all three route to the same fetch + integrate + retry recovery message; copy now mentions concurrent push as a likely cause. - `transient` covers RPC failed, early EOF, connection reset, dns flake, HTTP 5xx, HTTP/2 stream not closed, and unexpected sideband disconnect. these are retried in-tool with 2s + 5s backoff before surfacing the error. push is idempotent so verbatim retry is safe. - `unknown` (auth/permission/protected-branch/4xx) is rethrown unchanged — retrying these wastes time and noise. 2. surface stdout alongside stderr in `$git` failure messages and include the exit code. previously only `stderr.trim()` was forwarded, which could be empty in rare HTTPS failure modes (the agent on issue #571's run saw a one-line `failed to push some refs` and had nothing to diagnose with). 3. unit tests for the classifier covering all three branches plus the concurrent-push-wins-over-transient ordering. does not introduce auto fetch+rebase+retry inside the tool — that path is blocked under shell=disabled, can leave the working tree mid-conflict, and would create unwanted merge commits. the recovery message keeps the agent in the loop. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(push_branch): retry 429, jitter backoff, downgrade retry log to info - treat HTTP 429 (rate-limit / abuse detection) as transient — GitHub occasionally surfaces it on git push, where it is retry-safe unlike 401/403/404 - add ±25% jitter to backoff so concurrent agents hit by the same upstream blip don't retry in lockstep - log retries with log.info instead of log.warning to match retry.ts convention; a successful retry shouldn't leave a yellow GHA annotation behind in the job summary --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
This commit is contained in:
committed by
pullfrog[bot]
parent
e58299740d
commit
b6e2c61d30
+13
-2
@@ -154,8 +154,19 @@ export async function $git(
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
const stderr = result.stderr.trim();
|
||||
log.info(`git ${subcommand} failed: ${stderr}`);
|
||||
throw new Error(`git ${subcommand} failed: ${stderr}`);
|
||||
const stdout = result.stdout.trim();
|
||||
// stderr is the primary channel for git diagnostics, but in rare cases
|
||||
// (e.g. some HTTPS smart-protocol failures) the only useful detail is
|
||||
// on stdout — without it the agent / operator sees an empty error.
|
||||
// include exit code so we can distinguish e.g. signal-killed (1 with
|
||||
// empty output) from a genuine git-level rejection.
|
||||
const detail =
|
||||
stderr && stdout
|
||||
? `${stderr}\n--- stdout ---\n${stdout}`
|
||||
: stderr || stdout || "(no output)";
|
||||
const message = `git ${subcommand} failed (exit ${result.exitCode}): ${detail}`;
|
||||
log.info(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user