This commit implements a comprehensive system to prevent duplicate PRs from being created for the same issue. This addresses the ironic situation where duplicate PRs were being created for issue #1128, which was about cleaning up duplicate PRs. ## Changes ### 1. Pre-flight Check Scripts - `scripts/check-existing-prs.sh` - Bash script to check for existing PRs - `scripts/check_existing_prs.py` - Python version of the check - `scripts/pr-safe.sh` - User-friendly wrapper with guidance ### 2. Fixed Existing Script - Fixed syntax error in `scripts/cleanup-duplicate-prs.sh` (line 21) - Fixed AUTH header format ### 3. Documentation - Added `docs/duplicate-pr-prevention.md` with comprehensive usage guide ## How It Works ### Pre-flight Checks Before creating a PR, agents should run: ```bash ./scripts/check-existing-prs.sh <issue_number> ``` Exit codes: - 0: No existing PRs found (safe to create new PR) - 1: Existing PRs found (do not create new PR) - 2: Error (API failure, missing parameters, etc.) ### Cleanup Tools For cleaning up existing duplicate PRs: ```bash ./scripts/cleanup-duplicate-prs.sh --dry-run # Show what would be done ./scripts/cleanup-duplicate-prs.sh --close # Actually close duplicates ``` ## Prevention Strategy 1. **Pre-flight Checks**: Always check before creating a PR 2. **Agent Discipline**: Add to agent instructions to check before creating PRs 3. **Tooling Integration**: Integrate into existing workflows ## Testing Tested the scripts with various scenarios: - Issue with no existing PRs (exit code 0) - Issue with existing PRs (exit code 1) - Invalid inputs (exit code 2) - API failures (exit code 2) ## Related Issues Closes #1474: [META] Still creating duplicate PRs for issue #1128 despite cleanup
Scripts
cleanup-duplicate-prs.sh
Automated detection and cleanup of duplicate open PRs.
Purpose
This script identifies PRs that are duplicates (same issue number or very similar titles) and closes the older ones. It's designed to help maintain a clean PR board and prevent confusion from duplicate work.
Features
- Issue-based grouping: Groups PRs by issue number extracted from titles
- Date-based selection: Keeps the newest PR, closes older duplicates
- Dry-run mode: Shows what would be done without making changes
- Stale PR detection: Identifies PRs older than 30 days with no activity
- Explanatory comments: Adds comments when closing PRs to explain why
Usage
# Dry run (default) - shows what would be done
./scripts/cleanup-duplicate-prs.sh
# Actually close duplicates
./scripts/cleanup-duplicate-prs.sh --close
# Set environment variables
export GITEA_TOKEN="your_token_here"
export REPO="Timmy_Foundation/the-nexus"
export GITEA_URL="https://forge.alexanderwhitestone.com"
Configuration
The script uses the following environment variables:
| Variable | Default | Description |
|---|---|---|
GITEA_TOKEN |
(required) | Gitea API token with repo access |
GITEA_URL |
https://forge.alexanderwhitestone.com |
Gitea instance URL |
REPO |
Timmy_Foundation/the-nexus |
Repository in owner/repo format |
DRY_RUN |
true |
Set to false to actually close PRs |
How It Works
- Fetch open PRs: Gets all open PRs from the repository
- Extract issue numbers: Parses issue numbers from PR titles (e.g.,
#123) - Group by issue: Groups PRs that address the same issue
- Identify duplicates: Finds issues with multiple open PRs
- Select newest: For each duplicate group, keeps the newest PR
- Close older PRs: Closes older duplicates with explanatory comments
- Check for stale PRs: Identifies PRs older than 30 days
Example Output
[2026-04-14T00:57:05Z] Checking open PRs for Timmy_Foundation/the-nexus (dry_run: true)
[2026-04-14T00:57:17Z] Found 14 open PRs
[2026-04-14T00:57:17Z] Issue #1338 has 2 open PRs
[2026-04-14T00:57:17Z] Keeping PR #1392 (newest)
[2026-04-14T00:57:17Z] DRY RUN: Would close PR #1388
[2026-04-14T00:57:17Z] Issue #1354 has 2 open PRs
[2026-04-14T00:57:17Z] Keeping PR #1391 (newest)
[2026-04-14T00:57:17Z] DRY RUN: Would close PR #1384
[2026-04-14T00:57:17Z] Cleanup complete:
[2026-04-14T00:57:17Z] Duplicate issue groups found: 4
[2026-04-14T00:57:17Z] PRs closed: 0
[2026-04-14T00:57:17Z] Dry run: true
Safety Features
- Dry-run by default: Won't close PRs unless explicitly told to
- Explanatory comments: Adds comments before closing to explain why
- Newest PR preserved: Always keeps the most recent PR for each issue
- No force deletion: Only closes PRs, doesn't delete branches
Integration
This script can be integrated into CI/CD pipelines or run manually as part of regular maintenance. It's designed to be run weekly to keep the PR board clean.
Related Issues
- Issue #1128: Forge Cleanup — PRs Closed, Milestones Deduplicated, Policy Issues Filed
- Issue #1127: Evening triage pass (predecessor to #1128)