|
| 1 | +# UCP PR Triage & Review Routing Automation |
| 2 | + |
| 3 | +This directory houses the modular, configuration-driven Python rules engine designed to automate pull request ingestion triage, blocked/resume lifecycles, dynamically scoped organizational reviews verification, and inactivity stale-PR scans. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Directory Directory Tree Layout |
| 8 | + |
| 9 | +``` |
| 10 | +scripts/routing/ |
| 11 | +├── UCP_PR_REVIEW_ROUTING.yml # Centralized rules configuration mapping |
| 12 | +├── pr-triage-automation.py # Real-time webhook trigger runner (run by GHA workflow) |
| 13 | +├── pr-cron-stale-abandon.py # Daily stale/abandon inactivity scan runner (run by cron GHA workflow) |
| 14 | +├── validate-routing.py # Pretty CLI validation tool checking YAML syntax and org group existence |
| 15 | +├── test_routing.py # Pretty unit test suite executing mocked verifications locally |
| 16 | +└── triage/ |
| 17 | + ├── github_api.py # Encapsulates dynamic PyGithub API calls and organization caching |
| 18 | + ├── models.py # Standard shared dataclasses and strict LABEL_ Constants |
| 19 | + └── rules.py # Core abstract BaseRule class and concrete check implementations |
| 20 | +``` |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +## 1. Centralized Review Routing Configuration |
| 25 | + |
| 26 | +All folder-to-reviewer-mappings and label states are decoupled completely from the execution codebase and stored in [**`UCP_PR_REVIEW_ROUTING.yml`**](./UCP_PR_REVIEW_ROUTING.yml). This allows developers to flexibly configure or update rules without editing Python modules. |
| 27 | + |
| 28 | +### Configuration Rule Structure: |
| 29 | +```yaml |
| 30 | +routing_rules: |
| 31 | + - name: "Core Protocol & Spec" |
| 32 | + patterns: |
| 33 | + - "schemas/**/*.json" |
| 34 | + - "spec/**/*.md" |
| 35 | + review_requirements: |
| 36 | + # Maps fully qualified GitHub team handles to approval thresholds and status labels: |
| 37 | + "@Universal-Commerce-Protocol/tech-council": |
| 38 | + threshold: "majority" # Require TC majority approval or status:tc-majority-approved |
| 39 | + needs_review_label: "gov:needs-tc-review" |
| 40 | + approved_label: "gov:tc-approved" |
| 41 | + "@Universal-Commerce-Protocol/maintainers": |
| 42 | + threshold: 1 |
| 43 | + needs_review_label: "status:review-needed-maintainers" |
| 44 | + approved_label: "gov:maintainer-approved" |
| 45 | +``` |
| 46 | +
|
| 47 | +* **`patterns`**: List of path glob filters determining if modifications in the PR match this rule. |
| 48 | +* **`review_requirements`**: Maps dynamic, fully qualified organization team handles to: |
| 49 | + * `threshold`: Integer count (e.g. `1`, `2`) or `"majority"`. |
| 50 | + * `needs_review_label`: Staging label applied when approvals are below the threshold. |
| 51 | + * `approved_label`: Calming tint approval label applied dynamically once approvals are satisfied. |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +## 2. Core Rules Scaffolding (`triage/rules.py`) |
| 56 | + |
| 57 | +Triage logic is organized into specialized rule subclasses inheriting from `BaseRule`: |
| 58 | + |
| 59 | +1. **`FileRoutingRule`**: Compiles modified files against the configuration and dynamically applies the corresponding `needs_review_label` and removes the `approved_label`. |
| 60 | +2. **`ReviewerApprovalRule`**: Tracks active `pygithub` approvals against the resolved organization team members list: |
| 61 | + * **Superpower Override**: If a designated superpower user (like Amit `amithanda`) approves, all TC and GC rules are satisfied instantly, transitioning the PR to `gov:approved` / `status:ready-to-merge`. |
| 62 | + * **Label Security Guardrail**: Restricts `gov:tc-approved` and `status:tc-majority-approved` application. If applied by an unauthorized user outside Tech Council or DevOps, the script revokes the label with an automated warning comment. |
| 63 | + * **SDK Relaxed Mode**: Repositories matching `sdk` or `meeting-minutes` automatically default team thresholds to `1` to expedite SDK review cycles. |
| 64 | +3. **`LabelLifecycleRule`**: Resolves blocked feedback loops. Clears `Label.LABEL_BLOCKED` and restores `Label.LABEL_UNDER_REVIEW` when the author pushes a new commit or comments on the PR. |
| 65 | +4. **`StalePRRule`**: Scans active timestamps: |
| 66 | + * Under-review PRs inactive for 30 days are labeled `status:stale-review` and `status:needs-triage`. |
| 67 | + * Blocked PRs inactive for 37 days are labeled `status:abandon-candidate`. |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## 3. Local Dry-Run & Validation Utilities |
| 72 | + |
| 73 | +### YAML and Taxonomy Validation: |
| 74 | +Developers making changes to `UCP_PR_REVIEW_ROUTING.yml` can test and validate their configurations locally using: |
| 75 | +```bash |
| 76 | +export GH_TOKEN="your_github_personal_access_token" |
| 77 | +uv run .github/workflows/scripts/routing/validate-routing.py |
| 78 | +``` |
| 79 | +This utility checks: |
| 80 | +1. **YAML Syntax**: Verifies structure correctness. |
| 81 | +2. **Taxonomy Matcher**: Cross-references labels with `.github/labels.yml` to prevent styling typos. |
| 82 | +3. **Dynamic Org Team Check**: Dynamically calls the API to verify that all configured dynamic handles actually exist in the active organization (gracefully skipped with a warning on local forks). |
| 83 | + |
| 84 | +### Triage dry-runs: |
| 85 | +You can evaluate the rules engine output on any PR locally without committing live updates to GitHub by appending the `--dry-run` option flag: |
| 86 | +```bash |
| 87 | +export GH_TOKEN="your_token" |
| 88 | +uv run .github/workflows/scripts/routing/pr-triage-automation.py --dry-run |
| 89 | +``` |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +## 4. Local Unit Testing |
| 94 | + |
| 95 | +The rules engine is backed by a mock-based unit test suite verifying all edge conditions. Execute tests locally using: |
| 96 | +```bash |
| 97 | +export GH_TOKEN="your_token" |
| 98 | +uv run .github/workflows/scripts/routing/test_routing.py |
| 99 | +``` |
| 100 | +This outputs high-visibility boxed **Test Run Summary Results** with counts of executed, passed, failed, and errored test cases. |
0 commit comments