Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/pr-budget.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: PR budget (at most 20 open ready PRs per author; the excess waits as a draft)
# The rule is specs/policy/pr_budget.t27 (#10067, #10074); gen/c/policy/pr_budget.c is its t27c gen-c output
# (L2). This file is plumbing: it feeds the generated budget() one line for the event PR and one per other
# open PR of the same author, oldest first, and does what budget() prints. pull_request_target, because most
# PRs come from forks: only the base branch's gen/c/policy is checked out, no PR code runs. Nothing is ever
# closed. queen-* branches of this repository are exempt (owner, 2026-10-18).
on:
pull_request_target:
types: [opened, reopened, ready_for_review, converted_to_draft, closed]
permissions:
contents: read
pull-requests: write
jobs:
budget:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with: { sparse-checkout: gen/c/policy, sparse-checkout-cone-mode: false }
- name: Draft the PR over the cap, or promote held drafts, as the generated budget() prints
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ACT: ${{ github.event.action }}
DRAFT: ${{ github.event.pull_request.draft }}
FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
N: ${{ github.event.pull_request.number }}
REF: ${{ github.event.pull_request.head.ref }}
U: ${{ github.event.pull_request.user.login }}
run: |
printf '#include "gen/c/policy/pr_budget.c"\n#include <stdio.h>\nint main(void){static char b[1<<20],o[1<<16];budget((uint8_t*)b,fread(b,1,sizeof b,stdin),(uint8_t*)o,sizeof o-1);fputs(o,stdout);return 0;}\n' > budget.c
cc -O1 -w -o budget budget.c
k=C; case "$ACT" in opened|reopened|ready_for_review) [ "$DRAFT" = true ] || k=O;; esac
x=0; if [ "$FORK" = true ]; then x=1; fi
{ echo "$k $x $N $REF"; gh pr list --state open --limit 1000 --json number,isDraft,isCrossRepository,headRefName,labels,author | jq -r --arg u "$U" --argjson n "$N" 'def l: sub("^app/";"") | sub("\\[bot\\]$";""); sort_by(.number)[] | select((.author.login | l) == ($u | l) and .number != $n) | "\(if .isDraft then (if any(.labels[]; .name == "pr-budget-held") then "H" else "D" end) else "R" end) \(if .isCrossRepository then 1 else 0 end) \(.number) \(.headRefName)"'; } > rows.txt
./budget < rows.txt | while read -r a p; do
gh label create pr-budget-held --force -c ededed -d "Held as a draft by specs/policy/pr_budget.t27"
if [ "$a" = draft ]; then gh pr ready "$p" --undo && gh pr edit "$p" --add-label pr-budget-held && gh pr comment "$p" -b "Held as a draft by the PR budget (specs/policy/pr_budget.t27, #10074): this author already has 20 open pull requests ready for review (OPEN_CAP). Nothing is closed: this one is marked ready again when a slot frees, oldest first. Batching port waves per ring or crate needs fewer PRs."
else gh pr ready "$p" && gh pr edit "$p" --remove-label pr-budget-held && gh pr comment "$p" -b "A slot under the PR budget freed (specs/policy/pr_budget.t27, #10074): marked ready for review again."; fi || echo "::warning::pr-budget: $a $p failed"
done
33 changes: 33 additions & 0 deletions .trinity/seals/policy_PolicyPrBudget.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"built_by": "t27c-bootstrap@0.5.2+063da049b",
"closure": [
{
"spec": "specs/policy/text_lines.t27",
"spec_hash": "sha256:13c90895a4a44c760cce6b3b65f72f937298633062f040f8489486108ab0c2d1"
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:fdcf881cdcf97e03e270355a355fa1e075ccb720f044dd6d903a1ce39d5da5dd",
"gen_hash_rust": "sha256:59e76374ca756c3b813dc92b400c3bd9ea3a67c777cf876fc891f1a33fd272f0",
"gen_hash_verilog": "sha256:d76482675c3cfef75499c65522e0c006eed0f98bb692e60b4930b9d3e92bbded",
"gen_hash_zig": "sha256:142b6bd176982f9f316a0eb0d3a3d113a9931efcfca598641d3e2f0d250bce18",
"module": "PolicyPrBudget",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-18T02:19:46Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:7b5bc32463219d8e7740cf4880a5a72b94484e08fed8e326505fad158b8d81b0",
"spec_path": "specs/policy/pr_budget.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 19,
"total": 19,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"zig": "0.16.0"
}
}
4 changes: 2 additions & 2 deletions docs/claw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ behind one front door, with the gate logic in `.t27` specs. Epic

## The inventory spec and how to regenerate it

`specs/claw/gates.t27` holds three parallel `[66]str`-shaped arrays — `NAMES`
`specs/claw/gates.t27` holds three parallel `[67]str`-shaped arrays — `NAMES`
(workflow file stems), `CLASSES` (0 blocking / 1 soft / 2 reporting), `EVENTS`
(trigger keys, comma-joined). Rows stay alphabetical and aligned; tests pin
the alignment, the census (61/3/2), pairwise name uniqueness, and the
the alignment, the census (62/3/2), pairwise name uniqueness, and the
merge-critical rows.

The class rule is mechanical, so the regeneration is too:
Expand Down
Loading
Loading