fix(review): keep a fetched file distinguishable from an omitted one under thin budget - #8711
Conversation
…under thin budget
review-grounding.ts guarantees a file whose content is successfully fetched is
never rendered as the empty '(no content available)' placeholder a never-fetched
file uses. fetchFullFileContents broke that: when the remaining share fell below
MIN_SAMPLE_CHARS, sampleHeadAndTail returned '' and the fetched file was pushed as
{ text: '', truncated: true } -- identical to an unreadable file, so the AI
reviewer couldn't tell 'fetched but budget exhausted' from 'never fetched'. Sample
at the MIN_SAMPLE_CHARS floor for a genuinely-fetched file so it always carries a
minimal, distinguishing head+tail (a small bounded overrun only on the last thin
file). A never-fetched file (text == null) still skips/omits, unchanged. Updates
the existing 1MB-fixture test to assert the corrected, distinguishable output.
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8711 +/- ##
==========================================
+ Coverage 90.56% 90.59% +0.03%
==========================================
Files 96 97 +1
Lines 22490 22589 +99
Branches 3884 3921 +37
==========================================
+ Hits 20367 20465 +98
Misses 1945 1945
- Partials 178 179 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-26 00:23:29 UTC
Review summary Nits — 3 non-blocking
Flagged checks (non-blocking)
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
…of approving into a silent stall (#8758) (#8783) PR #8711 exposed the seam: a non-required third-party check concluded action_required, GitHub reported mergeable_state "unstable", and ORB's four surfaces disagreed — the unified comment said Manual Review, approve fired ("gate satisfied and CI is green"), the ready-to-merge label landed, the merge silently self-suppressed (mergeableClean requires "clean"), and the only record of why lived in an internal audit_events row. A human had to notice. - heldForManualReview gains a mergeableState === "unstable" term: no approve, no ready-to-merge label, manual-review label + comment naming the culprit non-required check(s) (threaded via the CI aggregate's nonRequiredFailingDetails; generic wording when unitemized). - New 1f fallback mirrors 1d so the hold stays visible on repos where review_state_label autonomy isn't acting but merge is. - The executor live-rechecks mergeable_state before posting an approve (denied on live dirty/unstable, fails open on ambiguous reads), the same #3863-shaped guard merge-adjacent actions already had. - dirty/behind/blocked semantics deliberately unchanged: dirty stays the close path, behind stays the rebase rail's, blocked stays approvable (the approval itself can be the unblocking act). Co-authored-by: loopover-orb[bot] <296761690+loopover-orb[bot]@users.noreply.github.com>
…umed by planner, comment, and labels (#8800) * refactor(disposition): one shared mergeable-state interpretation consumed by planner, comment, and labels (#8759) The #8711 incident's root CLASS: four surfaces each re-derived their own meaning for GitHub's raw mergeable_state string (comment {dirty,behind, unstable}; merge !== clean; approve {dirty}; hold-label none pre-#8758). #8758 unified the predicates; this removes the class: - src/settings/pr-disposition.ts (new, pure, dependency-free): assessMergeableState — THE single raw-string interpretation point — and derivePrDisposition (heldForManualReview / wouldApprove / wouldMerge / commentMergeStateHeld), each semantic documented as a contract. - agent-actions.ts derives the disposition once and consumes its fields for heldForManualReview, the approve gate, and canMerge; isConflict routes through assessMergeableState. Byte-identical: all 308 existing planner tests pass unchanged. - unified-comment.ts's readiness gains mergeStateHeld — resolved by processors.ts via isCommentMergeStateHeld so the self-contained renderer consumes a boolean instead of re-deriving meaning (zero-import contract intact); legacy callers keep the byte-identical raw-string fallback. - test/unit/pr-disposition-invariants.test.ts: the cross-surface invariant suite — module invariants over the full state matrix, planner-vs- disposition agreement per state, renderer-vs-disposition agreement through the bridge boolean, and the legacy-fallback equivalence pin. * test(disposition): include the #8759 mergeStateHeld field in the merge-facts exact-shape expectation
What & why
Closes #8646.
review-grounding.ts's header guarantees (twice) that a file whose content is successfully fetched is "NEVER rendered as pure 'omitted' content again, no matter how large it grows" (#7465-class fix).fetchFullFileContentsbroke that: when cumulativeusedlands withinMIN_SAMPLE_CHARS(400) ofFILE_CONTENT_BUDGET(96,000), the computedshareis too small,sampleHeadAndTailreturns""by design, and the fetched file gets pushed as{ text: "", truncated: true }— the same empty placeholder a never-fetched file uses.Consequence: the AI reviewer can't distinguish "fetched but budget ran out" from "never fetched" — both render identically, undermining the omission-tracking guarantee.
test/unit/review-grounding.test.tseven asserted this broken behavior as correct.The fix
Sample at the
MIN_SAMPLE_CHARSfloor for a genuinely-fetched file —sampleHeadAndTail(text, Math.max(share, MIN_SAMPLE_CHARS))— so a fetched file always yields a minimal, non-empty head+tail with the omission marker. This is a small, bounded overrun of the overall budget only on the last thin file, not a per-file unbounded cost. A never-fetched file (text == null) still skips/omits, unchanged.Tests
src/huge.tsfixture test (was asserting{ text: "", truncated: true }) to assert the corrected behavior: the fetched file istruncatedbut non-empty, carries genuine content (z), and includes the omission marker — verifiably distinguishable from a never-fetched file's empty placeholder.Verified bug-catching: reverting the fix (back to the empty-placeholder degrade) fails the updated test.
Validation
test/unit/review-grounding.test.ts: 50 tests pass; typecheck clean; 100% of changed lines/branches covered.main, mergeable-clean.