[BUG] No safeguard before an agent bulk-creates dozens of PRs against a real external repo (91 PRs auto-closed + repo locked me out)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.205)
What's Wrong?
Working with Claude Code on a large local repo (~120 local commits across a long multi-day session), I asked it to open the accumulated fixes as pull requests against the real upstream repository (I only had read access there — no fork existed yet). Claude Code:
- Forked the upstream repo under my account.
- Grouped the local history into 96 logical PR units.
- Asked me two clarifying questions (bundling rule for coupled commits, and whether to run the full batch now vs. a small trial first). I answered "run the full batch now, unattended."
- Proceeded to call
gh pr createin a loop, opening 91 real PRs against the upstream repo in rapid succession (a few seconds apart each).
Within about 9 minutes of that batch finishing, all 91 PRs were auto-closed — almost certainly by the target repo's own anti-abuse/bulk-contribution automation reacting to a brand-new contributor account opening 91 PRs at once (too fast to be a human manually reviewing and closing each one). Right after that, the repo's GitHub interaction settings flipped to "restricted to prior contributors only," which then blocked all further legitimate PR creation attempts (including a few genuinely separate follow-up fixes I tried to open afterward), failing with:
pull request create failed: GraphQL: could not be created. Interactions on this repository have been restricted to prior contributors only. (createPullRequest)
The underlying code changes were fine (individually reviewed, build-verified, etc.) — the problem is entirely about how Claude Code executed the bulk-PR-creation step itself, with no safeguard against the very predictable real-world consequence of a new account rapid-firing dozens of PRs at a public repository it doesn't maintain.
What Should Happen?
When an agent is about to take a large number of externally-visible, hard-to-reverse actions against a third-party/public system it doesn't control (e.g., opening dozens of PRs/issues on a repo where the user has no write access, i.e. is a first-time contributor), Claude Code should apply extra caution beyond a single upfront "run it all, unattended" confirmation — for example:
- A hard checkpoint scaled to the action count (e.g. re-confirm before crossing something like 10/20/50 real external actions in one run), not just a single yes/no before the whole batch.
- Built-in pacing/rate-limiting between actions like repeated
gh pr createcalls against the same repo, rather than firing them back-to-back. - A proactive warning that platforms commonly apply automated anti-abuse measures to bulk-PR patterns from new/low-history contributor accounts, so the user can make a better-informed real-time call even after already agreeing to "unattended."
None of this happened; the agent went from "91 PRs planned" straight to "91 PRs opened" with no intermediate signal, and by the time the consequence (auto-close + interaction lockout) was visible, it had already happened to a real, external, non-reversible-by-me system.
Error Messages/Logs
pull request create failed: GraphQL: could not be created. Interactions on this repository have been restricted to prior contributors only. (createPullRequest)
All 91 PRs (e.g. github.com/TheSuperHackers/GeneralsGameCode/pull/2898 through /pull/2988) show closedAt timestamps within a ~9 minute window, right after creation.
Steps to Reproduce
- Accumulate a large local commit history (order of 100+ commits) representing many independent, legitimate fixes, in a repo whose
originis a real upstream project you only have read access to (no fork yet). - Ask Claude Code to turn the local history into upstream pull requests, "one PR per issue/logical change."
- When asked to confirm scope, answer to run the full batch now, unattended.
- Observe Claude Code loop
gh pr createdozens of times in quick succession against the real upstream repo, with no additional confirmation/pacing checkpoint at that scale. - Separately (on GitHub, not in Claude Code): observe the target repo's own automation auto-close the flood of PRs and lock interactions shortly after.
Environment
- Claude Code version: 2.1.205
- OS: Windows 11 Enterprise (10.0.26200)
- Model: Claude Sonnet 5
4 Comments
91 PRs against an upstream repo you have read-only access to, auto-created in rapid succession with no confirmation step between the "run the full batch now" authorization and execution -- this is a textbook case of ambiguous scope in a multi-step authorization.
The specific failure: you authorized "run the full batch," which you understood as "proceed with the grouping you just described." Claude Code understood it as "execute all 96 gh pr create calls now, unattended." Both interpretations are technically consistent with the words. The difference is that your authorization should have come with a visible count ("91 PRs will be created against github.com/upstream, continue?") before any gh pr create ran, not after all of them already ran.
The blast-radius issue is the same pattern as stuck background agents burning tokens: any operation with a high multiplier (N PRs, N tokens, N file deletions) should require explicit confirmation with the N visible, not inherit authorization from a prior high-level approval.
Worth filing a separate feature request for "dry-run mode for batch operations" -- a gh pr create --dry-run flag that lists what would be created without creating anything. That would have made the scope visible before execution and let you catch the 96-unit batch before it hit the upstream.
VOLY has three guardrails that would have caught this before the 91st PR:
1. Dry-run mode — preview what the agent intends to do, then decide whether to apply. For external write actions, this is the first gate.
2. Max-files limit —
executor_safety.max_files_touched: 10rolls back the entire run if the agent touches more files than the policy allows. For PR creation this would need to be wired to the action count, but it establishes the pattern: define a hard ceiling before the run, not after the damage.3. Plan gates with acceptance criteria — for a batch of external actions, you can require explicit re-confirmation at a checkpoint:
The gap your scenario exposes is that 'run the full batch unattended' bypassed the intermediate signal. The right architecture is: one upfront confirmation authorizes starting the batch; a second checkpoint fires at a threshold (e.g. 10 actions) before the agent continues. VOLY's plan gate is the mechanism for that second checkpoint.
Docs: https://github.com/voly-codes/voly/blob/main/docs/backend/executors.md#safety
Was this a single executor run or a multi-step workflow?
The 91-PR case is a sharp illustration of a pattern that is going to keep coming up: agents operating correctly at the task level but with no awareness that "rapidly repeat this action against an external system" has real-world rate limits and abuse thresholds.
The asymmetry you identified is the core problem -- by the time the consequence is visible, it already happened to a system you don't control and can't undo. A safeguard here probably needs to be pre-flight (count planned operations before executing) rather than reactive.
Are you running this in a workflow where the PR creation was an explicit task, or did Claude decide to create PRs as part of a broader task goal? That distinction matters for where the safeguard should live. We're tracking this class of issue (agent bulk-action footguns, external side effect containment) as part of fleet orchestration work.
Agreed — this needs a pre-dispatch external-action gate. To clarify our earlier comment, VOLY’s current dry-run and max-files controls contain filesystem changes, but they do not yet sandbox arbitrary external GitHub side effects. Bulk PR creation needs a separate action budget: count planned external writes, require approval above a threshold, and attach an idempotency key before dispatch.