Repository navigation
gate(policy): wire pr_budget.t27 into a pull_request_target gate -- draft the 21st open PR, promote held drafts, exempt queen-* bees #10074
Description
#10070 writes the PR budget down as a t27 card, specs/policy/pr_budget.t27: at most 20 open non-draft PRs per author, the excess held as drafts, drafts promoted in order as slots free. Nothing reads the card yet. This issue wires it into a gate.
Measured 2026-10-18: open PRs are dmitrii-f-t27 128 (all 128 from forks), gHashTag 163 (131 of them on queen-* branches), dependabot 5. Because the fork PRs carry a read-only token on pull_request, the gate runs on pull_request_target and checks out only the base branch's gen/c/policy (no PR code is built or run).
Owner decision 2026-10-18: PRs on queen-* branches are exempt -- they do not count against the cap and are never drafted -- but only when the branch lives in gHashTag/t27 itself, so a fork cannot claim the exemption by naming a branch queen-1. Everyone else is capped, the owner's hand-opened PRs included.
Shape, following the issue-shape gate (needs-boundary.yml + gen/c/policy/issue_shape.c): the logic is in the card, lowered by t27c gen-c; the workflow is a one-line C main around the generated function plus gh calls. The workflow feeds the card one line for the event PR and one line per other open PR of the same author (oldest first); the card prints the actions (draft N / ready N). A held draft carries the label pr-budget-held, so a draft its author made on purpose is never promoted. No existing PR is closed; existing ready PRs are not touched until their own next event.
Boundary
- specs/policy/pr_budget.t27
- gen/c/policy/pr_budget.c
- .trinity/seals/policy_PolicyPrBudget.json
- .github/workflows/pr-budget.yml
- specs/claw/gates.t27
- specs/claw/gates_pins.t27
- gen/c/claw/gates.c
- docs/claw/README.md
- scripts/trinity-pellis-pipeline/fix_bugs.py
- scripts/trinity-pellis-pipeline/fix_bugs_v2.py
User Scenarios
- Given dmitrii-f-t27 holds 20 or more open ready PRs, when they open one more, then the gate converts it to a draft, labels it
pr-budget-heldand comments that it is held byspecs/policy/pr_budget.t27until a slot frees. - Given an author holds 17 ready PRs and 5 held drafts, when one ready PR merges, then the gate promotes the 4 oldest held drafts to ready, so the author is back at 20.
- Given a PR on a
queen-*branch of gHashTag/t27, when it is opened, then it is never drafted and does not count against the cap. - Given a fork PR on a branch named
queen-1, when it is opened by an author at the cap, then it is drafted like any other PR.
Requirements
- FR-001: The card MUST define the exemption as branch prefix
queen-with the head repository equal to the base repository, with tests for both sides. - FR-002: The card MUST expose one function that reads the event line and the author's other open PRs and writes the drafts and promotions, using
admit()andpromoted(). - FR-003:
gen/c/policy/pr_budget.cMUST be the uneditedt27c gen-coutput of the card, with its seal. - FR-004: The workflow MUST run on
pull_request_target, MUST NOT check out or run PR code, and MUST stay under 40 added lines. - FR-005: The gate MUST NOT close any PR.
Success Criteria
- The card's tests pass on the Railway lab with
t27c test-report, 0 vacuous, and the harness from the workflow, compiled on the lab againstgen/c/policy/pr_budget.c, printsdraft Nfor a 21st PR: exit 0. - Negative control: the same harness with
OPEN_CAPraised to 200 prints nothing for that PR, and a forkqueen-1row is still drafted. - The PR changes 10 files, 0 outside the Boundary;
.github/workflows/pr-budget.ymladds at most 40 lines, and the foreign-line total shrinks (the two orphaned one-off scriptsscripts/trinity-pellis-pipeline/fix_bugs*.py, 99 lines, zero references, are deleted). - The TRI CLAW inventory
specs/claw/gates.t27carries the newpr-budgetrow (67 workflows, census 62/3/2), its tests pass on the lab,gen/c/claw/gates.cis its regenerated output, and the claw-drift harness printsmatrix is current(negative control: without the row it prints DRIFT). - 0 existing PRs are closed.
Activity
gate(policy): pr_budget.t27 imports line_end from text_lines instead …
gate(policy): pr-budget.yml -- plumbing around the generated budget() (…
gate(policy): wire pr_budget.t27 into a pull_request_target gate -- d…