ci: gate PR bodies on the template's HTML comment rules - #36551
ci: gate PR bodies on the template's HTML comment rules#36551mateo-berri wants to merge 5 commits into
Conversation
Greptile SummaryThe PR adds a CI gate that validates pull request bodies using a checker sourced exclusively from the base revision.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; the validator now comes solely from the base revision and the PR-controlled fallback has been removed.
|
| Filename | Overview |
|---|---|
| .github/workflows/pr-body-template.yml | Checks out and executes only the base revision’s validator, fully addressing both previously reported trust-boundary bypasses. |
| .github/scripts/check_pr_body.py | Implements deterministic pull request body validation for the template’s machine-checkable rules. |
| tests/test_litellm/test_github_check_pr_body.py | Covers compliant bodies, formatting violations, placeholders, fenced content, HTML comments, and QA runbook handling. |
| tests/code_coverage_tests/check_workflow_startup_safety.py | Exempts GitHub Actions object-filter syntax from multiplication detection without weakening arithmetic checks. |
Reviews (5): Last reviewed commit: "fix: ignore fenced code blocks when chec..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Ellipsis false positives everywhere
- Restricted the
...placeholder match to bullet lines (- ...), which is the only form the template uses, so bare...in prose or omitted output no longer trips the check.
- Restricted the
- ✅ Fixed: Fenced headings break parsing
- Made
split_sectionstrack fenced code-block state via a scan and ignore heading-looking lines that appear inside fences so quoted template snippets no longer conjure fake sections.
- Made
Or push these changes by commenting:
@cursor push 0f42d30c34
Preview (0f42d30c34)
diff --git a/.github/scripts/check_pr_body.py b/.github/scripts/check_pr_body.py
--- a/.github/scripts/check_pr_body.py
+++ b/.github/scripts/check_pr_body.py
@@ -25,6 +25,7 @@
HEADING_PATTERN: Final = re.compile(r"^#{2,6}\s+(?P<title>.+?)\s*$")
BULLET_PATTERN: Final = re.compile(r"^\s*(?:[-*+]|\d+[.)])\s+(?P<text>.*)$")
LABEL_PATTERN: Final = re.compile(r"^[^-*+].*:\s*$")
+FENCE_PATTERN: Final = re.compile(r"^\s*(?:```|~~~)")
@dataclass(frozen=True, slots=True)
@@ -48,9 +49,21 @@
def split_sections(body: str) -> tuple[Section, ...]:
lines: Final = tuple(body.split("\n"))
- headings: Final = tuple(
- (index, match.group("title")) for index, line in enumerate(lines) if (match := HEADING_PATTERN.match(line))
- )
+
+ def scan(
+ acc: tuple[bool, tuple[tuple[int, str], ...]],
+ item: tuple[int, str],
+ ) -> tuple[bool, tuple[tuple[int, str], ...]]:
+ in_fence, headings = acc
+ index, line = item
+ if FENCE_PATTERN.match(line):
+ return (not in_fence, headings)
+ if in_fence:
+ return acc
+ match: Final = HEADING_PATTERN.match(line)
+ return (in_fence, (*headings, (index, match.group("title")))) if match else acc
+
+ _, headings = reduce(scan, tuple(enumerate(lines)), (False, ()))
ends: Final = tuple(index for index, _ in headings[1:]) + (len(lines),)
return tuple(Section(title=title, lines=lines[start + 1 : end]) for (start, title), end in zip(headings, ends))
@@ -92,8 +105,9 @@
def is_placeholder_line(line: str) -> bool:
bullet_match: Final = BULLET_PATTERN.match(line)
- content: Final = bullet_match.group("text").strip() if bullet_match else line.strip()
- return content == "..." or any(token in line for token in PLACEHOLDER_TOKENS)
+ if bullet_match and bullet_match.group("text").strip() == "...":
+ return True
+ return any(token in line for token in PLACEHOLDER_TOKENS)
def check_placeholders(sections: tuple[Section, ...]) -> tuple[Violation, ...]:
diff --git a/tests/test_litellm/test_github_check_pr_body.py b/tests/test_litellm/test_github_check_pr_body.py
--- a/tests/test_litellm/test_github_check_pr_body.py
+++ b/tests/test_litellm/test_github_check_pr_body.py
@@ -109,3 +109,28 @@
violations = checker.check_body(body, ("litellm/main.py",))
assert len(violations) == 1
assert violations[0].section == "QA runbook"
+
+
+def test_bare_ellipsis_in_proof_output_is_not_a_placeholder(checker):
+ body = (
+ "## Screenshots / Proof of Fix\n\n"
+ "```\n"
+ "$ curl http://localhost:4000/v1/chat/completions ...\n"
+ "{\"id\": \"chatcmpl-abc\",\n"
+ " ...\n"
+ " \"usage\": {\"prompt_tokens\": 5}}\n"
+ "```\n"
+ )
+ assert checker.check_body(body, ()) == ()
+
+
+def test_headings_inside_fenced_block_do_not_open_new_sections(checker):
+ body = (
+ "## Screenshots / Proof of Fix\n\n"
+ "Quoted template excerpt:\n\n"
+ "```markdown\n"
+ "## Caveats (if any)\n\n"
+ "Some prose paragraph that is not a bullet at all\n"
+ "```\n"
+ )
+ assert checker.check_body(body, ()) == ()You can send follow-ups to the cloud agent here.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit e02c331. Configure here.

TLDR
Problem this solves:
How it solves it:
User Flow
Before: a contributor writes the Caveats section as prose and leaves the QA runbook in, and nothing tells them until a maintainer reads it
After: the same PR gets an immediate failing check naming each violated rule
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Both runs captured at 5d9f28c against PR #36543's real description and changed-file list. The before input is that PR's description exactly as it stood before its bullets fix, which a reviewer had to catch by hand; the after input is the description live right now
On this PR itself the check skips with a notice, since the judging copy always comes from the base branch and the base branch gains the script only when this merges; the captured runs above are the proof
Type
🚄 Infrastructure
Caveats (if any)
Final Attestation
Note
Medium Risk
New CI gate that runs on every human PR and can fail on description formatting; false positives would block contributors until they reformat or apply the skip label. No production runtime impact.
Overview
Adds a CI check that enforces the machine-checkable rules buried in HTML comments in
pull_request_template.md, which rendered PRs and agent harnesses normally strip away.The new workflow validates that TLDR and Caveats use short bullets (≤14 words), leftover placeholders like
<blah>/...are gone, and the QA runbook section is removed unless the PR touchestests/e2e/. It re-runs on body edits, skips bots and theignore-pr-body-templatelabel, and always judges from the base branch copy of the script so a PR cannot weaken the gate.Also exempts GitHub's
labels.*.nameobject-filter syntax from the workflow startup-safety arithmetic checker, which the new workflow needs.Reviewed by Cursor Bugbot for commit e02c331. Bugbot is set up for automated code reviews on this repo. Configure here.