Today, if even one of ~50 generated action files contains a string that GitHub's secret scanning flags (e.g. SK[a-f0-9]{32} for Twilio API Keys), the entire create-app workflow PR fails — 5+ hours of agent work discarded. The original symptom was a misleading 422 from GitHub's /pulls endpoint; #1399 surfaced the real GH013 stderr; this PR turns the rejection into a graceful drop-and-continue.
Concrete motivating repro:
xg8mfm9r(twilio create-app, 2026-04-21 + retry 2026-04-23) and5ywkt9bl(replay onnext, 2026-04-27) — all deterministically producedapps/twilio/actions/delete_key.pywith a literalSK8b594707ae1b3e069cad41c8bcb3df08baked in. With this PR, that one file would be dropped and the remaining ~49 actions would still ship as a draft PR.
GitHub is the source of truth for what's flagged — we don't run any secret-pattern matching against file content ourselves.
_push_branch_with_secret_scanning_recovery(base_branch) is the new entry point used by Git.create_pr. First push runs as normal.remote: lines on token-auth pushes) is parsed by _parse_secret_scanning_offenders — a pure parser that looks for the GH013 marker and path: <file>:<line> lines. Non-GH013 failures (auth, network, branch protection, etc.) pass through unchanged with the existing strict error from #1399._squash_and_force_push(base_branch, files_to_drop) collapses every local commit into a single squash commit via git reset --soft origin/<base>, removes each offender from the index AND the working tree, commits with a message naming the dropped files, and git push --force-with-leases the rewritten branch.max_recoveries=3 so we don't loop forever.Git.create_pr appends a ## ⚠️ Secret-scanning recovery section to the PR body listing every dropped path, so the human reviewer knows what didn't ship.Git.checkpoint is unchanged — it still tolerates push failures and holds commits locally. Recovery only kicks in at the strict push inside create_pr. This preserves the fail-fast invariant from #1399 for non-GH013 failures.
apps/<app>/__init__.py registered the dropped class, the package could fail to import. Mercury convention is to keep __init__.py empty (per action_builder linting rules) so the risk is low. If it bites, a follow-up post-cleanup pass can prune dangling imports.All 39 tests in test_git_utils.py pass (24 existing + 15 new):
--force-with-lease flag set.make fmt clean; ruff check / format / imports all pass.Re-run a create-app for twilio on prod-integrator with integrator_branch=next and the same params as xg8mfm9r. Expected: workflow runs ~5h, eventually pushes successfully via recovery, opens a draft PR with apps/twilio/actions/delete_key.py (and any other flagged files) listed in the recovery note. Without merge, the same payload but integrator_branch=fix/git-push-drop-and-retry-on-secret-scanning exercises this branch directly.
xg8mfm9r (twilio create-app, reported by Venkat 2026-04-24).🤖 Generated with Claude Code