Skip to content

Read a Carried Change Set Past the Compare Endpoint's 300-File Ceiling - #2790

Merged
ptr727 merged 2 commits into
developfrom
feature/auto-2767
Oct 11, 2026
Merged

ptr727 merged 2 commits into
developfrom
feature/auto-2767

Conversation

@ptr727

@ptr727 ptr727 commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

On a pull request that changes 300 files or more, scripts/pr_review.py refused every carry, both for a coverage statement and for a file table. The compare endpoint caps its file list at 300, so changed_at returned None at both commits. status then exited 45 and told the reader to re-run, which could never help.

  • I checked whether paging gets past the cap, using read-only GETs on a 308-file comparison. Page 1 returned 300 files and page 2 returned none, whatever per_page was set to. The compare's file list does not page.
  • A tree diff does get past the cap. It reads the recursive tree of the commit and of its merge base, and compares them by path, mode and object id. On that same comparison it named exactly the 326 paths that git diff --no-renames --name-only names.
  • When both compares are at the cap, changed_at now returns Capped(merge_base), with the merge base taken from the compare it already read. carry_holds then reads both commits' change sets through tree_changed_at. Both sides are always read the same way, because the compare lists a rename once while the tree diff lists both paths.
  • When only one side is at the cap, the carry is refused as a different set. The other side's list is complete and has fewer than 300 entries, so the two sets cannot be equal.
  • A tree that GitHub truncates (at about 100,000 entries) reads as unread. No fleet repository comes close to that size.
  • scripts/README.md now describes the tree-based read.

Tests: the constructed case from the issue now passes. A 400-file promotion whose first round has a table, and whose later round has no table on an unchanged change set, carries the table. Separate cases cover a coverage statement carried past the cap, a change set that moved, the cap on either side alone, a failed or truncated tree read, a capped compare that names no merge base, and a rename. With the tree reading disabled, 4 of the new cases fail.

The local strict review took two passes. The first raised 8 findings, and I fixed 7 of them. The remaining one is filed as #2789: spec/audit.py reads trees the same way, and it is a file this change does not touch. The second pass raised nothing.

Closes on promotion: #2767

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Carry comparisons now handle changes that exceed GitHub’s 300-file comparison limit by checking the affected file sets directly when both comparisons are capped.
    • Comparisons remain undecided when tree data is missing, unreadable, truncated, or only one comparison reaches the limit.
  • Documentation
    • Clarified how carry comparisons work when both changes reach the file limit.

ptr727 and others added 2 commits October 11, 2026 03:03
The compare endpoint caps its file list at 300 on every page, measured on a
308-file comparison whose second page carried no files, so `carry_holds`
refused every carry on a pull request changing 300 files or more.

Where both commits reach that cap, the change set is now read from the trees
either side of each commit, which matched `git diff --no-renames --name-only`
on that comparison. A cap on one side only is a definite change. A tree GitHub
truncated still refuses the carry, and the digest names that as GitHub's own
ceiling rather than as a read to retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chable Ceiling Wording

The capped compare now names its merge base, so reading past the cap costs no
second compare. A truncated tree reads as unread, since GitHub's recursive-tree
ceiling is far past any fleet repository, which removes the separate digest
wording for it. Adds a statement-carry case and an earlier-side-capped case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 10:13
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: ptr727/ProjectTemplate/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5560ca8a-99a1-4a72-a197-6e4df032bbe6

📥 Commits

Reviewing files that changed from the base of the PR and between adc6c0c and 6271cc4.


📒 Files selected for processing (3)
  • scripts/README.md
  • scripts/pr_review.py
  • tests/test_pr_review.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.



📝 Walkthrough

Walkthrough

When both GitHub compare results reach the 300-file cap, the carry check now compares change sets reconstructed from commit trees. Unreadable or truncated trees leave the carry undecided.

Changes

Carried-table change detection

Layer / File(s) Summary
Represent capped compare results
scripts/pr_review.py, tests/test_pr_review.py
changed_at returns a Capped value with the merge-base SHA when the compare reaches the file cap. Compare-response fixtures and cap tests use the updated response shape.
Reconstruct changes from commit trees
scripts/pr_review.py, tests/test_pr_review.py
reread_json retries REST JSON reads. tree_at validates recursive tree responses, and cached tree_changed_at compares complete trees. Tests clear the new cache.
Apply tree fallback to carry checks
scripts/pr_review.py, tests/test_pr_review.py, scripts/README.md
carry_holds uses tree-derived path sets when both compares are capped. Tests cover matching and differing sets, unreadable trees, and other cap cases. The README documents the fallback.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes


Merge Risk: ⚪ Minimal · up to 6271c

No merge-blocking issue was established with the capped-compare fallback; merge after normal checks.

Pre-merge checks | Passed 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: reading carried change sets beyond GitHub’s 300-file compare limit.
Docstring Coverage Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 1 files. (2 skipped: 1 u…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR


🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@codecov

codecov Bot commented Oct 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.47826% with 3 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (develop@adc6c0c). Learn more about missing BASE report.

Files with missing lines Patch % Lines
scripts/pr_review.py 93.47% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #2790   +/-   ##
==========================================
  Coverage           ?   60.30%           
==========================================
  Files              ?       16           
  Lines              ?     8479           
  Branches           ?        0           
==========================================
  Hits               ?     5113           
  Misses             ?     3366           
  Partials           ?        0           
Flag Coverage Δ
python-3.13 60.30% <93.47%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The tree endpoint uses an unresolved commit SHA, and rename semantics can produce unsafe carry decisions.

2 open findings
What changed in this PR

Extends carry detection beyond GitHub’s 300-file compare limit using recursive tree comparisons.

Changes:

  • Adds capped-compare fallback logic.
  • Expands tests for large, truncated, moved, and renamed changes.
  • Documents the fallback behavior.
File Description
tests/​test_pr_review.py Tests capped comparisons and tree fallback behavior.
scripts/​README.md Documents compare-limit handling.
scripts/​pr_review.py Implements capped compares and recursive tree reads.

🧠 Review effort: Lite


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/pr_review.py
Comment thread scripts/pr_review.py
@ptr727

ptr727 commented Oct 11, 2026

Copy link
Copy Markdown
Owner Author

Answering the headline of the Copilot round on 6271cc46, which lists 2 open findings, both answered in their threads. The headline also says "rename semantics can produce unsafe carry decisions", and no listed finding carries that, so it is answered here.

No change needed. carry_holds never compares a set read one way with a set read the other way:

  • Both compares under the cap: both sets come from the compare, which names a rename once, by its new path. This is unchanged from develop.
  • Both compares at the cap: both sets come from tree_changed_at, which names a rename as a removal and an addition. test_the_tree_reading_names_both_paths_of_a_rename covers this.
  • Cap on one side only: the result is False and no tree is read, so the carry is refused, which is the fail-closed direction. test_the_cap_on_one_side_only_is_a_different_set_without_a_tree_read covers both orientations.

A rename can therefore make two readings disagree only within a single method, and that disagreement reads as a moved change set, which refuses the carry.

@ptr727

ptr727 commented Oct 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ptr727
ptr727 merged commit 74189b1 into develop Oct 11, 2026
14 checks passed
@ptr727
ptr727 deleted the feature/auto-2767 branch October 11, 2026 10:35
ptr727 added a commit that referenced this pull request Oct 11, 2026
…ilure Reporting Fixes to Main (#2816)

## Summary

Promotes these pull requests from develop to main:

- #2832 Require Every CLI Script to Answer Help and Refuse Unknown
Arguments
- #2833 Exempt nopep8 as a Leading Directive in the Prose Gate
- #2815 Bind the Workspace-Edit Rule Only to an Agent Running Inside VS
Code
- #2813 Record HomeAssistant-Config's Incidental Python as a driftNote
- #2827 Read nopep8 in Any Letter Case in the Prose Gate
- #2825 Spend a Head Refusal a Later Genuine Round on That Head Answers
- #2819 Read noqa and CSpell Directives in Any Letter Case
- #2814 Judge the Prose in Front of a Trailing noqa or pylint Directive
- #2811 Name Why a Windows Tool Install Failed and Print Docker's Status
in UTF-8
- #2808 Read a Tool Name as a Prose Gate Directive Only Where It Opens
the Comment
- #2802 Name a File the Prose Gate Cannot Decode in Its Scope Line
- #2800 Read a First-Line Comment After a Byte Order Mark as Leading in
Every Language
- #2797 Name a Python File the Prose Gate Cannot Parse in Its Scope Line
- #2794 Let the Unattended Orchestrator Retry a Transient Dispatch
Failure
- #2792 Close the Remaining Blocked-Promotion Gaps in the Unattended
Worker
- #2790 Read a Carried Change Set Past the Compare Endpoint's 300-File
Ceiling
- #2784 Let a Round on the Head Outrank pr_review.py wait's Repo-Wide
Quota Reading
- #2782 Say What pr_review.py wait --min-rounds Counts and Name It at
the Timeout
- #2778 Share the Bounded Step Reader Across the Test Harnesses
- #2776 Pin the Prose Gate's Override Wiring by Structure
- #2773 Exempt a List Item's Label Separator from the Dash Rule for
Every Marker and Bold Spelling
- #2771 Strip a List Item's Opening Label for Every Marker and Bold
Spelling in the Prose Gate
- #2768 Page pr_review.py's Changed-File List Past 100 So a Large
Promotion Can State Coverage
- #2765 Let an Attested Local Pass Cover a Head Beside a Table-Less
Copilot Round

Closes #2285
Closes #2820
Closes #2818
Closes #2809
Closes #2807
Closes #1659
Closes #2796
Closes #2798
Closes #2163
Closes #1984
Closes #2614
Closes #2767
Closes #2781
Closes #2741
Closes #2775
Closes #1665
Closes #2770
Closes #2738
Closes #2758
Closes #2739

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants