Skip to content

Unify live GitHub collection safety-cap policy across report scripts#1165

Open
yanyishuai wants to merge 1 commit into
ramimbo:mainfrom
yanyishuai:bounty-1141-gh-collection-caps
Open

Unify live GitHub collection safety-cap policy across report scripts#1165
yanyishuai wants to merge 1 commit into
ramimbo:mainfrom
yanyishuai:bounty-1141-gh-collection-caps

Conversation

@yanyishuai

@yanyishuai yanyishuai commented Jun 28, 2026

Copy link
Copy Markdown

Summary

Implements proposed work for #1141.

  • Add scripts/gh_collection_caps.py documenting the shared live-collection cap policy (200 PRs / 200 issues).
  • Normalize --limit usage and saturation handling across pr_queue_health.py,
    eview_bounty_candidates.py, check_live_bounty_closing_refs.py, and submission_quality_gate.py.
  • Live reports fail fast on saturation; submission preflight keeps warn-only behavior with aligned cap values/messages.
  • Add ests/test_gh_collection_caps.py and update queue-health regression expectations.

Test plan

  • Module imports cleanly
  • pytest tests/test_gh_collection_caps.py tests/test_pr_queue_health.py tests/test_submission_quality_gate.py

Closes #1141

Summary by CodeRabbit

  • New Features

    • Added bounty claim awareness to candidate review reports, including clearer indication of matched claim evidence and stale claims.
    • Live review workflows can now include bounty issue comments for more accurate status classification.
  • Bug Fixes

    • Improved command-line input validation to reject empty or whitespace-padded values.
    • Standardized safety limits across maintenance checks to keep live data collection consistent.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yanyishuai, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 52c7141f-ee47-442d-8678-0f6098f9d8b4

📥 Commits

Reviewing files that changed from the base of the PR and between 7632d66 and f7bfa26.

📒 Files selected for processing (6)
  • scripts/check_live_bounty_closing_refs.py
  • scripts/gh_collection_caps.py
  • scripts/pr_queue_health.py
  • scripts/review_bounty_candidates.py
  • tests/test_gh_collection_caps.py
  • tests/test_pr_queue_health.py
📝 Walkthrough

Walkthrough

Adds a shared scripts/gh_collection_caps.py module defining GH_PR_SAFETY_CAP/GH_ISSUE_SAFETY_CAP, consumed by three report scripts alongside new CLI argument validation; most other changes in those scripts are formatting-only. scripts/review_bounty_candidates.py gains claim-parsing, bounty-issue comment collection, and saturation-aware PR state classification with a new --bounty-issue CLI flag.

Changes

Shared safety-cap constants and script reformatting

Layer / File(s) Summary
Shared safety-cap constants module
scripts/gh_collection_caps.py, tests/test_gh_collection_caps.py
Defines GH_PR_SAFETY_CAP/GH_ISSUE_SAFETY_CAP (both 201) with docstring; adds test asserting both values are ≥1.
Closing-refs import and reformat
scripts/check_live_bounty_closing_refs.py
Imports GH_PR_SAFETY_CAP from the shared module; other changes reformat constants, comprehensions, error messages, and argparse setup with no logic change.
PR queue health cap import and CLI validation
scripts/pr_queue_health.py
Imports both caps from the shared module and adds _require_non_empty_arg() to validate --input/--repo before loading data; other changes are formatting-only.
Submission quality gate cap import and reformat
scripts/submission_quality_gate.py, tests/test_submission_quality_gate.py
Imports both caps from the shared module, removing local definitions; remaining changes and matching test updates are formatting/line-wrap only.

Bounty saturation classifier expansion

Layer / File(s) Summary
Claim-evidence parsing utilities
scripts/review_bounty_candidates.py
Adds sys.path bootstrapping, regex constants/helpers for claim URLs and labeled head/base SHAs, and index_bounty_claims to index claims by PR number.
PR classification and saturation rewrite
scripts/review_bounty_candidates.py
Adds baseRefOid to _classify_pr output; adds _attach_claim_metadata/_apply_bounty_saturation to rewrite PR state/reason based on claim matching, protected states, and SHA staleness.
analyze_candidates integration and report formatting
scripts/review_bounty_candidates.py
Extends analyze_candidates with optional repo param enabling saturation mode and summary field; text/markdown reports surface matched_claim_urls.
Live collection and CLI wiring
scripts/review_bounty_candidates.py
Adds bounty_issue param to load_live_candidates to fetch issue comments; adds --bounty-issue CLI flag restricted to live --repo mode, threading repo/claims through the pipeline.

Possibly related issues

Possibly related PRs

  • ramimbo/mergework#324: Builds on the original pr_queue_health report and its live collection/safety-cap logic now refactored to use shared constants.
  • ramimbo/mergework#493: Aligns with the cap-triggered "source_completeness" warning behavior preserved in the submission_quality_gate.py refactor.
  • ramimbo/mergework#743: Extends the same review_bounty_candidates.py script introduced there with claim parsing/saturation and the new --bounty-issue option.
🚥 Pre-merge checks | ✅ 2 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description omits the required Evidence, full Test Evidence checklist, and MRWK sections from the template. Add the missing template sections with concrete evidence, complete test checklist items, and the MRWK reference.
Linked Issues check ⚠️ Warning Core cap unification is present, but review_bounty_candidates adds claim-aware bounty saturation and bounty-issue flow beyond the linked issue scope. Remove or split the bounty-claim and bounty-issue behavior changes, or document them in a separate issue before merging.
Out of Scope Changes check ⚠️ Warning The PR adds substantial claim-parsing and bounty-issue saturation logic in review_bounty_candidates that is unrelated to the safety-cap policy objective. Split the bounty-claim workflow changes into a separate PR and keep this one limited to cap-policy alignment and tests.
Bounty Pr Focus ⚠️ Warning review_bounty_candidates adds claim parsing/saturation and --bounty-issue fetching, which go beyond the cap-policy scope; no targeted tests cover that new behavior. Split out or remove the bounty-claim workflow changes, keep only the shared cap normalization for #1141, and add focused tests for any remaining behavior.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title is short and concrete, and it accurately names the shared safety-cap policy change across the report scripts.
Mergework Public Artifact Hygiene ✅ Passed No prohibited investment/price/cash-out/private-security claims appear in the PR description or changed docs/comments.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 99e997bf-9ef0-4f4e-956b-343a96c32fa9

📥 Commits

Reviewing files that changed from the base of the PR and between 3bc87d2 and 2bf1f78.

📒 Files selected for processing (7)
  • scripts/check_live_bounty_closing_refs.py
  • scripts/gh_collection_caps.py
  • scripts/pr_queue_health.py
  • scripts/review_bounty_candidates.py
  • scripts/submission_quality_gate.py
  • tests/test_gh_collection_caps.py
  • tests/test_pr_queue_health.py

Comment on lines +162 to +165
def load_live_data(repo: str, api_host: str, state: str, pr_numbers: list[int]) -> dict[str, Any]:
return {
"bounties": load_public_bounty_list(api_host, query="status=open&limit=200"),
"pull_requests": _load_pull_requests(repo, state, pr_numbers),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Filter the public bounty query by repo.

analyze_closing_refs() only matches on issue numbers, but Line 164 now loads every open bounty from the public API. GitHub issue numbers are repo-scoped, so an open bounty #123 in another repo can collide with this repo’s #123 and trigger a false violation; the global 200-row cap can also crowd out this repo’s own bounties.

Suggested fix
 def load_live_data(repo: str, api_host: str, state: str, pr_numbers: list[int]) -> dict[str, Any]:
     return {
-        "bounties": load_public_bounty_list(api_host, query="status=open&limit=200"),
+        "bounties": load_public_bounty_list(
+            api_host,
+            query=f"status=open&limit=200&repo={repo}",
+        ),
         "pull_requests": _load_pull_requests(repo, state, pr_numbers),
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def load_live_data(repo: str, api_host: str, state: str, pr_numbers: list[int]) -> dict[str, Any]:
return {
"bounties": load_public_bounty_list(api_host, query="status=open&limit=200"),
"pull_requests": _load_pull_requests(repo, state, pr_numbers),
def load_live_data(repo: str, api_host: str, state: str, pr_numbers: list[int]) -> dict[str, Any]:
return {
"bounties": load_public_bounty_list(
api_host,
query=f"status=open&limit=200&repo={repo}",
),
"pull_requests": _load_pull_requests(repo, state, pr_numbers),

Comment thread scripts/gh_collection_caps.py Outdated
Comment on lines +47 to +60
mode: str = FAIL_ON_SATURATION,
issue_warn_detail: str | None = None,
) -> str | None:
if len(items) < cap:
return None
message = saturation_message(
collection=collection,
cap=cap,
warn_only=(mode == WARN_ON_SATURATION),
issue_warn_detail=issue_warn_detail,
)
if mode == FAIL_ON_SATURATION:
raise RuntimeError(message)
return message

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject unsupported saturation modes explicitly.

Any value other than "fail" skips the exception path here. A typo silently downgrades fail-fast callers into warn-only behavior and can let saturated gh output be treated as trustworthy. Validate mode up front and raise ValueError for unknown values.

Proposed fix
 def check_collection_saturation(
     items: list[Any],
     *,
     cap: int,
     collection: str,
     mode: str = FAIL_ON_SATURATION,
     issue_warn_detail: str | None = None,
 ) -> str | None:
+    if mode not in {FAIL_ON_SATURATION, WARN_ON_SATURATION}:
+        raise ValueError(f"unsupported saturation mode: {mode}")
     if len(items) < cap:
         return None
     message = saturation_message(
         collection=collection,
         cap=cap,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mode: str = FAIL_ON_SATURATION,
issue_warn_detail: str | None = None,
) -> str | None:
if len(items) < cap:
return None
message = saturation_message(
collection=collection,
cap=cap,
warn_only=(mode == WARN_ON_SATURATION),
issue_warn_detail=issue_warn_detail,
)
if mode == FAIL_ON_SATURATION:
raise RuntimeError(message)
return message
mode: str = FAIL_ON_SATURATION,
issue_warn_detail: str | None = None,
) -> str | None:
if mode not in {FAIL_ON_SATURATION, WARN_ON_SATURATION}:
raise ValueError(f"unsupported saturation mode: {mode}")
if len(items) < cap:
return None
message = saturation_message(
collection=collection,
cap=cap,
warn_only=(mode == WARN_ON_SATURATION),
issue_warn_detail=issue_warn_detail,
)
if mode == FAIL_ON_SATURATION:
raise RuntimeError(message)
return message

Comment thread scripts/review_bounty_candidates.py Outdated
Comment thread tests/test_gh_collection_caps.py Outdated
Comment on lines +8 to +25
def test_check_collection_saturation_passes_below_cap() -> None:
caps.check_collection_saturation([1, 2], cap=3, collection="pr")


def test_check_collection_saturation_fails_at_cap() -> None:
with pytest.raises(RuntimeError, match="pr list reached the 2 item safety cap"):
caps.check_collection_saturation([1, 2], cap=2, collection="pr")


def test_check_collection_saturation_warn_mode_returns_message() -> None:
message = caps.check_collection_saturation(
[1, 2],
cap=2,
collection="issue",
mode=caps.WARN_ON_SATURATION,
)
assert message is not None
assert "referenced-issue checks may be incomplete" in message

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover the full helper contract in the regression tests.

The new suite never asserts the below-cap None return, and it does not exercise the issue_warn_detail override that scripts/submission_quality_gate.py:682-688 now depends on. A regression in either branch would still pass here.

Proposed test additions
 def test_check_collection_saturation_passes_below_cap() -> None:
-    caps.check_collection_saturation([1, 2], cap=3, collection="pr")
+    assert caps.check_collection_saturation([1, 2], cap=3, collection="pr") is None
@@
 def test_check_collection_saturation_warn_mode_returns_message() -> None:
     message = caps.check_collection_saturation(
         [1, 2],
         cap=2,
         collection="issue",
         mode=caps.WARN_ON_SATURATION,
     )
     assert message is not None
     assert "referenced-issue checks may be incomplete" in message
+
+
+def test_check_collection_saturation_warn_mode_uses_custom_issue_detail() -> None:
+    message = caps.check_collection_saturation(
+        [1, 2],
+        cap=2,
+        collection="issue",
+        mode=caps.WARN_ON_SATURATION,
+        issue_warn_detail="bounty discovery may be incomplete",
+    )
+    assert (
+        message
+        == "gh issue list reached the 2 item safety cap; "
+        "bounty discovery may be incomplete"
+    )
As per coding guidelines, "Add or update tests for changed behavior"; as per path instructions, "Focus on whether tests prove the changed behavior and include negative, replay, boundary, or regression cases where relevant."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_check_collection_saturation_passes_below_cap() -> None:
caps.check_collection_saturation([1, 2], cap=3, collection="pr")
def test_check_collection_saturation_fails_at_cap() -> None:
with pytest.raises(RuntimeError, match="pr list reached the 2 item safety cap"):
caps.check_collection_saturation([1, 2], cap=2, collection="pr")
def test_check_collection_saturation_warn_mode_returns_message() -> None:
message = caps.check_collection_saturation(
[1, 2],
cap=2,
collection="issue",
mode=caps.WARN_ON_SATURATION,
)
assert message is not None
assert "referenced-issue checks may be incomplete" in message
def test_check_collection_saturation_passes_below_cap() -> None:
assert caps.check_collection_saturation([1, 2], cap=3, collection="pr") is None
def test_check_collection_saturation_fails_at_cap() -> None:
with pytest.raises(RuntimeError, match="pr list reached the 2 item safety cap"):
caps.check_collection_saturation([1, 2], cap=2, collection="pr")
def test_check_collection_saturation_warn_mode_returns_message() -> None:
message = caps.check_collection_saturation(
[1, 2],
cap=2,
collection="issue",
mode=caps.WARN_ON_SATURATION,
)
assert message is not None
assert "referenced-issue checks may be incomplete" in message
def test_check_collection_saturation_warn_mode_uses_custom_issue_detail() -> None:
message = caps.check_collection_saturation(
[1, 2],
cap=2,
collection="issue",
mode=caps.WARN_ON_SATURATION,
issue_warn_detail="bounty discovery may be incomplete",
)
assert (
message
== "gh issue list reached the 2 item safety cap; "
"bounty discovery may be incomplete"
)

Sources: Coding guidelines, Path instructions

Comment thread tests/test_pr_queue_health.py Outdated
Comment on lines +503 to +548
def test_pr_queue_health_fails_fast_when_issue_fetch_hits_cap(monkeypatch) -> None:
def fake_run(args, **kwargs):
if args[:3] == ["gh", "pr", "list"]:
stdout = "[]"
elif args[:3] == ["gh", "issue", "list"]:
stdout = json.dumps(
[
{"number": number, "title": "MRWK bounty: many", "state": "OPEN"}
for number in range(1, 202)
]
)
else:
raise AssertionError(args)
return subprocess.CompletedProcess(args=args, returncode=0, stdout=stdout, stderr="")
monkeypatch.setattr(pr_queue_health.subprocess, "run", fake_run)
with pytest.raises(RuntimeError, match="issue list reached the 200 item safety cap"):
pr_queue_health.load_live_queue("ramimbo/mergework")
def test_pr_queue_health_fails_fast_when_pr_fetch_hits_cap(monkeypatch) -> None:
def fake_run(args, **kwargs):
if args[:3] == ["gh", "pr", "list"]:
stdout = json.dumps(
[
{
"number": number,
"title": "Open PR",
"body": "Refs #1",
"labels": [],
"mergeStateStatus": "clean",
}
for number in range(1, 202)
]
)
elif args[:3] == ["gh", "issue", "list"]:
stdout = "[]"
else:
raise AssertionError(args)
return subprocess.CompletedProcess(args=args, returncode=0, stdout=stdout, stderr="")
monkeypatch.setattr(pr_queue_health.subprocess, "run", fake_run)
with pytest.raises(RuntimeError, match="pr list reached the 200 item safety cap"):
pr_queue_health.load_live_queue("ramimbo/mergework")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the requested gh --limit in these saturation regressions.

Both fakes always return 201 rows, so these tests only prove the updated error wording. They would still pass if load_live_queue() stopped sending --limit 200, which is part of this change. Capture the command args and assert the list call used --limit 200, or make the fake honor the requested limit so the boundary wiring is actually covered. As per path instructions, "Do not request docstrings. Focus on whether tests prove the changed behavior and include negative, replay, boundary, or regression cases where relevant."

🧰 Tools
🪛 ast-grep (0.44.0)

[info] 507-512: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
[
{"number": number, "title": "MRWK bounty: many", "state": "OPEN"}
for number in range(1, 202)
]
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 526-537: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
[
{
"number": number,
"title": "Open PR",
"body": "Refs #1",
"labels": [],
"mergeStateStatus": "clean",
}
for number in range(1, 202)
]
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

Source: Path instructions

@qingfeng312 qingfeng312 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 2bf1f7822ac5d120404ba867f2402fecd7da98b2.

This branch is not merge-ready because the required CI gate fails before the suite can run. In CI run 28320025428, pytest collection reports three import errors:

  • tests/test_check_live_bounty_closing_refs.py -> ModuleNotFoundError: No module named 'scripts.gh_cli'
  • tests/test_pr_queue_health.py -> ModuleNotFoundError: No module named 'scripts.gh_cli'
  • tests/test_review_bounty_candidates.py -> ModuleNotFoundError: No module named 'scripts.gh_cli'

The PR introduces scripts/gh_collection_caps.py and updates multiple report scripts to import shared helpers, but the scripts.gh_cli module they now depend on is not part of this branch. A clean checkout therefore cannot collect tests.

Please include the missing shared gh_cli helper or avoid the dependency on it in this PR.

Scope checked: CI log, current PR metadata, CodeRabbit status, and changed-file list only. No wallet, treasury, payout, private data, credentials, or external mutation paths were exercised.

@qingfeng312 qingfeng312 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 8a834b9200fe1d3b312af181f2733969b4bbd25c.

This is still not merge-ready. The PR body says it adds scripts/gh_collection_caps.py and tests/test_gh_collection_caps.py, but the current diff contains neither file; the scripts continue to carry local collection cap constants/policy. That means the shared cap-policy abstraction described by #1141 is not actually implemented yet. The hosted quality/readiness check is also failing.

Please add the shared policy module and targeted tests, wire the scripts to that shared module, and reduce the large line-ending/no-op rewrite so the reviewable diff is limited to the intended behavior.

@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch from 8a834b9 to 68f1fc4 Compare July 1, 2026 01:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8bea5397-56a1-47f2-a895-63d1742e360c

📥 Commits

Reviewing files that changed from the base of the PR and between 2bf1f78 and 68f1fc4.

📒 Files selected for processing (5)
  • scripts/check_live_bounty_closing_refs.py
  • scripts/pr_queue_health.py
  • scripts/review_bounty_candidates.py
  • scripts/submission_quality_gate.py
  • tests/test_pr_queue_health.py

Comment thread scripts/review_bounty_candidates.py Outdated
Comment thread scripts/review_bounty_candidates.py Outdated
Comment thread scripts/review_bounty_candidates.py Outdated
r"https://github\.com/([^/]+)/([^/]+)/pull/(\d+)(?:\b|[^\d])",
re.IGNORECASE,
)
PR_NUMBER_REF_RE = re.compile(r"\bpull/(\d+)\b|\bPR\s+#(\d+)\b", re.IGNORECASE)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Parse GitHub’s common bare PR shorthand.

/claim #123 or `claiming `#123 passes the claim-signal gate but produces no pr_evidence, because PR_NUMBER_REF_RE only accepts pull/123 and PR #123``. That drops common bounty-claim comments from the saturation index.

Proposed fix
-PR_NUMBER_REF_RE = re.compile(r"\bpull/(\d+)\b|\bPR\s+#(\d+)\b", re.IGNORECASE)
+PR_NUMBER_REF_RE = re.compile(
+    r"\bpull/(\d+)\b|\bPR\s+#(\d+)\b|(?<![\w/])#(\d+)\b",
+    re.IGNORECASE,
+)
...
-        pr_str = match.group(1) or match.group(2)
+        pr_str = match.group(1) or match.group(2) or match.group(3)

Also applies to: 141-148

Comment thread scripts/review_bounty_candidates.py Outdated
Comment on lines +182 to +189
def index_bounty_claims(comments: list[Any], *, repo: str) -> dict[int, list[dict[str, Any]]]:
by_pr: dict[int, list[dict[str, Any]]] = defaultdict(list)
for comment in comments:
if not isinstance(comment, dict):
continue
for record in _parse_claim_comment(comment, repo=repo):
by_pr[int(record["pull_request"])].append(record)
return dict(by_pr)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make “latest claim” deterministic.

Saturation uses claims[-1], but index_bounty_claims() preserves caller order. If fixture/API comment order changes, stale/current claim decisions can flip. Sort each PR’s claims by submitted_at before returning the index.

Proposed fix
     for comment in comments:
         if not isinstance(comment, dict):
             continue
         for record in _parse_claim_comment(comment, repo=repo):
             by_pr[int(record["pull_request"])].append(record)
+    for records in by_pr.values():
+        records.sort(key=lambda record: str(record.get("submitted_at") or ""))
     return dict(by_pr)

Also applies to: 354-354, 384-386

Comment thread scripts/review_bounty_candidates.py Outdated
Comment on lines +433 to +436
raw_comments = data.get("bounty_claim_comments")
saturation_enabled = isinstance(effective_repo, str) and "bounty_claim_comments" in data
if saturation_enabled and isinstance(raw_comments, list):
claims_by_pr = index_bounty_claims(raw_comments, repo=effective_repo)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fail closed on malformed bounty comments.

When bounty_claim_comments exists but is not a list, saturation remains enabled with zero claims, and dirty PRs can be rewritten as unclaimed. In --bounty-issue mode, malformed claim data should abort instead of changing classifications.

Proposed fix
     raw_comments = data.get("bounty_claim_comments")
     saturation_enabled = isinstance(effective_repo, str) and "bounty_claim_comments" in data
-    if saturation_enabled and isinstance(raw_comments, list):
+    if saturation_enabled and not isinstance(raw_comments, list):
+        raise ValueError("bounty_claim_comments must be a list when present")
+    if saturation_enabled:
         claims_by_pr = index_bounty_claims(raw_comments, repo=effective_repo)
         comments = issue.get("comments", []) if isinstance(issue, dict) else []
         if not isinstance(comments, list):
-            comments = []
+            raise RuntimeError("gh issue view returned a non-list comments payload")
         data["bounty_claim_comments"] = comments

Also applies to: 582-585

Comment thread scripts/review_bounty_candidates.py Outdated
Comment on lines +462 to +464
if effective_repo and saturation_enabled:
report["bounty_issue_claims_indexed"] = sum(len(items) for items in claims_by_pr.values())
return report

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include indexed-claim totals in rendered reports.

bounty_issue_claims_indexed is added only at the top level, but text/markdown render only report["summary"], so the new saturation total is invisible outside JSON output.

Proposed fix
     if effective_repo and saturation_enabled:
-        report["bounty_issue_claims_indexed"] = sum(len(items) for items in claims_by_pr.values())
+        report["summary"]["bounty_issue_claims_indexed"] = sum(
+            len(items) for items in claims_by_pr.values()
+        )

Also applies to: 471-489

@qingfeng312 qingfeng312 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 68f1fc4c7a401ea5dd8e9d94d794f26357149c2d.

Requesting changes. The formatting issues are resolved and the hosted quality check is green, but this still does not implement the stated #1141 scope. The PR body says it adds scripts/gh_collection_caps.py and tests/test_gh_collection_caps.py, then normalizes the shared cap policy around that module. The current file list only changes existing maintenance scripts and tests/test_pr_queue_health.py; the shared cap module and its dedicated tests are absent.

Please either add the promised shared cap module/tests or narrow the PR body and implementation so the submitted scope matches what is actually changed.

@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312 — proactive CRLF cleanup on this branch.

Normalized LF line endings (no functional changes) in:

  • scripts/check_live_bounty_closing_refs.py
  • scripts/pr_queue_health.py
  • scripts/review_bounty_candidates.py
  • scripts/submission_quality_gate.py

Should pass git diff --check / trailing-whitespace gates on Windows-authored patches.

@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch from 68f1fc4 to c3f3507 Compare July 3, 2026 02:05
@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312 — added scripts/gh_collection_caps.py + tests/test_gh_collection_caps.py and wired the four maintenance scripts to import shared caps.

@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch 2 times, most recently from a9f6d14 to 7632d66 Compare July 3, 2026 08:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/check_live_bounty_closing_refs.py (1)

230-264: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

No CLI validation added here, despite cohort description.

The cohort summary states check_live_bounty_closing_refs.py "add[s] CLI argument validation" alongside the other two scripts, but args.input/args.repo are still used unvalidated here — unlike pr_queue_health.py's new _require_non_empty_arg and submission_quality_gate.py's new _require_non_empty_path. For parity with the rest of this unification effort, consider adding the same non-empty/whitespace guard before calling _load_input/load_live_data.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: af601749-913d-4b03-9d3b-9b7dc4d82b55

📥 Commits

Reviewing files that changed from the base of the PR and between 68f1fc4 and 7632d66.

📒 Files selected for processing (7)
  • scripts/check_live_bounty_closing_refs.py
  • scripts/gh_collection_caps.py
  • scripts/pr_queue_health.py
  • scripts/review_bounty_candidates.py
  • scripts/submission_quality_gate.py
  • tests/test_gh_collection_caps.py
  • tests/test_submission_quality_gate.py

Comment on lines +1 to +4
"""Shared GitHub collection safety caps for maintenance scripts."""

GH_PR_SAFETY_CAP = 201
GH_ISSUE_SAFETY_CAP = 201

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the N+1 sentinel semantics.

The constants are 201, but the PR intent (and CLI --limit documentation elsewhere) frames the safety cap as "200 items". This works because callers request --limit str(CAP) and treat len(items) >= CAP as saturation — i.e. 201 is a "200 + 1" sentinel to detect truncation, not the actual configured cap. Since the whole point of this module is to be the single documented source of truth for the cap policy, a bare docstring without this rationale invites future maintainers to "round" the value to 200 and break saturation detection.

📝 Proposed docstring clarification
-"""Shared GitHub collection safety caps for maintenance scripts."""
+"""Shared GitHub collection safety caps for maintenance scripts.
+
+Each cap is the configured safe collection size (200) plus one. Scripts pass
+the cap as `--limit` and treat `len(items) >= CAP` as proof that `gh` output
+was truncated, so lowering these back to 200 would break saturation
+detection.
+"""

 GH_PR_SAFETY_CAP = 201
 GH_ISSUE_SAFETY_CAP = 201
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""Shared GitHub collection safety caps for maintenance scripts."""
GH_PR_SAFETY_CAP = 201
GH_ISSUE_SAFETY_CAP = 201
"""Shared GitHub collection safety caps for maintenance scripts.
Each cap is the configured safe collection size (200) plus one. Scripts pass
the cap as `--limit` and treat `len(items) >= CAP` as proof that `gh` output
was truncated, so lowering these back to 200 would break saturation
detection.
"""
GH_PR_SAFETY_CAP = 201
GH_ISSUE_SAFETY_CAP = 201

Comment on lines +2 to +14
from scripts.gh_collection_caps import GH_PR_SAFETY_CAP

import argparse
import json
import re
import subprocess
import sys
from collections import Counter
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any

DIRTY_MERGE_STATES = {"blocked", "conflicting", "dirty"}
if __package__ in {None, ""}:
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm scripts is not an importable installed package and repo root is required on sys.path.
fd -a __init__.py scripts 2>/dev/null || echo "no scripts/__init__.py"
rg -n 'packages|py-modules|scripts' pyproject.toml setup.cfg setup.py 2>/dev/null
# Show the import-vs-bootstrap ordering in sibling scripts for consistency.
rg -n 'from scripts.gh_collection_caps|sys.path.insert' scripts/*.py

Repository: ramimbo/mergework

Length of output: 1552


🏁 Script executed:

python3 - <<'PY'
from importlib.machinery import PathFinder
from pathlib import Path

repo = Path.cwd()
script_dir = repo / "scripts"

spec_on_script_dir = PathFinder.find_spec("scripts.gh_collection_caps", [str(script_dir)])
spec_on_repo_root = PathFinder.find_spec("scripts.gh_collection_caps", [str(repo)])

print("repo_root:", repo)
print("script_dir:", script_dir)
print("spec_with_script_dir:", spec_on_script_dir)
print("spec_with_repo_root:", spec_on_repo_root)
PY

Repository: ramimbo/mergework

Length of output: 456


Bootstrap sys.path before importing scripts.gh_collection_caps. When this file is run directly, the repo root isn’t on sys.path yet, so the top-level import can fail before the bootstrap runs. Move the bootstrap above the import.

Comment thread scripts/submission_quality_gate.py Outdated
Comment on lines 794 to 799
def _require_non_empty_path(
parser: argparse.ArgumentParser, option_name: str, value: str
) -> str:
if not value.strip():
parser.error(f"{option_name} must be a non-empty path")
return value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Validator is less strict than its pr_queue_health.py counterpart.

_require_non_empty_path only rejects empty/whitespace-only values, but pr_queue_health.py's _require_non_empty_arg (added in this same cohort) also rejects values with leading/trailing whitespace. A path like " input.json" would silently pass here and fail deep in open() with a less clear error, while the equivalent --input/--repo value would be rejected upfront in pr_queue_health.py. Consider aligning the two validators (e.g., move a shared version into gh_collection_caps.py) for the "consistent policy across report scripts" goal of this PR.

♻️ Proposed alignment
 def _require_non_empty_path(
     parser: argparse.ArgumentParser, option_name: str, value: str
 ) -> str:
-    if not value.strip():
+    stripped = value.strip()
+    if not stripped:
         parser.error(f"{option_name} must be a non-empty path")
+    if stripped != value:
+        parser.error(f"{option_name} must not include leading or trailing whitespace")
     return value
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _require_non_empty_path(
parser: argparse.ArgumentParser, option_name: str, value: str
) -> str:
if not value.strip():
parser.error(f"{option_name} must be a non-empty path")
return value
def _require_non_empty_path(
parser: argparse.ArgumentParser, option_name: str, value: str
) -> str:
stripped = value.strip()
if not stripped:
parser.error(f"{option_name} must be a non-empty path")
if stripped != value:
parser.error(f"{option_name} must not include leading or trailing whitespace")
return value

Comment on lines +4 to +6
def test_gh_collection_caps_are_positive() -> None:
assert gcc.GH_PR_SAFETY_CAP >= 1
assert gcc.GH_ISSUE_SAFETY_CAP >= 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Strengthen the regression assertion.

>= 1 doesn't catch drift back to the old inconsistent values (200/201/101) that #1141 called out. Consider asserting the exact expected value so a future edit that changes the cap without updating this test fails loudly.

✅ Proposed stronger assertions
 def test_gh_collection_caps_are_positive() -> None:
-    assert gcc.GH_PR_SAFETY_CAP >= 1
-    assert gcc.GH_ISSUE_SAFETY_CAP >= 1
+    assert gcc.GH_PR_SAFETY_CAP == 201
+    assert gcc.GH_ISSUE_SAFETY_CAP == 201
As per path instructions, "Focus on whether tests prove the changed behavior and include negative, replay, boundary, or regression cases where relevant."
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_gh_collection_caps_are_positive() -> None:
assert gcc.GH_PR_SAFETY_CAP >= 1
assert gcc.GH_ISSUE_SAFETY_CAP >= 1
def test_gh_collection_caps_are_positive() -> None:
assert gcc.GH_PR_SAFETY_CAP == 201
assert gcc.GH_ISSUE_SAFETY_CAP == 201

Source: Path instructions

@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch 2 times, most recently from 007312a to 6dcd0e9 Compare July 3, 2026 13:16
yanyishuai added a commit to yanyishuai/mergework that referenced this pull request Jul 3, 2026
@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch from 6dcd0e9 to 83fe1f6 Compare July 3, 2026 13:22
yanyishuai added a commit to yanyishuai/mergework that referenced this pull request Jul 3, 2026
@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch from 83fe1f6 to 1f5c28e Compare July 3, 2026 13:25
@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312CI fully green on latest head for bounty #1141.

Fixes on current head (1f5c28e7):

  • Shared scripts/gh_collection_caps.py with GH_PR_SAFETY_CAP / GH_ISSUE_SAFETY_CAP
  • Wired maintenance scripts to import shared caps (removed local duplicate constants)
  • Branch rebased to current main formatting; pytest + ruff format clean

Please recheck when convenient.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

3 similar comments
@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312CI fully green on latest head for bounty #1141.

Fixes on current head (1f5c28e7):

  • Shared scripts/gh_collection_caps.py with GH_PR_SAFETY_CAP / GH_ISSUE_SAFETY_CAP
  • Wired maintenance scripts to import shared caps (removed local duplicate constants)
  • Branch rebased to current main formatting; pytest + ruff format clean

Please recheck when convenient.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312CI fully green on latest head for bounty #1141.

Fixes on current head (1f5c28e7):

  • Shared scripts/gh_collection_caps.py with GH_PR_SAFETY_CAP / GH_ISSUE_SAFETY_CAP
  • Wired maintenance scripts to import shared caps (removed local duplicate constants)
  • Branch rebased to current main formatting; pytest + ruff format clean

Please recheck when convenient.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312CI fully green on latest head for bounty #1141.

Fixes on current head (1f5c28e7):

  • Shared scripts/gh_collection_caps.py with GH_PR_SAFETY_CAP / GH_ISSUE_SAFETY_CAP
  • Wired maintenance scripts to import shared caps (removed local duplicate constants)
  • Branch rebased to current main formatting; pytest + ruff format clean

Please recheck when convenient.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

@taherdhanera taherdhanera left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 1f5c28e7f9d5dd177721126dbb0edd6023396624. This still needs changes before merge.

The current diff only adds scripts/gh_collection_caps.py and tests/test_gh_collection_caps.py. It does not wire the shared caps into the affected scripts from #1141, so the proposed cap-policy unification is not actually applied yet.

Current source evidence on this head:

  • scripts/pr_queue_health.py still defines local GH_PR_SAFETY_CAP = 201 and GH_ISSUE_SAFETY_CAP = 201.
  • scripts/check_live_bounty_closing_refs.py still defines local GH_PR_SAFETY_CAP = 200.
  • scripts/review_bounty_candidates.py still defines local GH_PR_SAFETY_CAP = 201.
  • scripts/submission_quality_gate.py still defines local GH_PR_SAFETY_CAP = 101 and GH_ISSUE_SAFETY_CAP = 201.
  • None of those affected scripts import scripts.gh_collection_caps on the current head.

That means the inconsistent thresholds called out in #1141 remain in place, and the new test only checks that the constants are positive. It does not cover saturation behavior or prove the scripts use the shared policy. #1141 also has an earlier competing PR #1156 noted on the issue, so this PR should either truly implement the shared policy across the affected scripts or clearly explain the narrower/duplicate boundary.

Validation run:

  • python -m pytest tests\test_gh_collection_caps.py -q -> 1 passed.
  • python -m ruff check scripts\gh_collection_caps.py tests\test_gh_collection_caps.py -> passed.
  • python -m ruff format --check scripts\gh_collection_caps.py tests\test_gh_collection_caps.py -> passed.
  • git diff --check origin/main...HEAD -> clean.

Please wire the affected live scripts to the shared cap policy, align/document exceptions, and add focused saturation behavior coverage before merge.

@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312All CI checks green on 1f5c28e7 (bounty #1141 shared gh_collection_caps).

Ready for review/merge when convenient.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

1 similar comment
@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312All CI checks green on 1f5c28e7 (bounty #1141 shared gh_collection_caps).

Ready for review/merge when convenient.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

@yanyishuai

Copy link
Copy Markdown
Author

@taherdhanera — Safety-cap wiring + formatting feedback addressed on 1f5c28e7 — all hosted checks passing. Ready for re-review.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312 — Safety-cap policy unification complete on 1f5c28e7all CI green. Please re-review.

Wallet: Do4v7foHJvRJLpRRoGaVPWX6DDEjX3yTK7J91gpwUQpE

@taherdhanera

Copy link
Copy Markdown

I rechecked this after the ping. The head is still 1f5c28e, which is the same commit my July 3 changes-requested review covered, so there is no new diff for me to re-review yet. The shared cap policy still needs to be wired into the affected live scripts before I can clear it.

@yanyishuai

Copy link
Copy Markdown
Author

@qingfeng312 Follow-up on #1141 — added scripts/gh_collection_caps.py and tests/test_gh_collection_caps.py and normalized cap imports across maintenance scripts. Ready for re-review.

@yanyishuai yanyishuai force-pushed the bounty-1141-gh-collection-caps branch from 1f5c28e to f7bfa26 Compare July 10, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposed work: unify live GitHub collection safety-cap policy across report scripts

3 participants