Skip to content

fix(review): a false-positive empty-files guardrail hit locks a clean PR into manual-review forever #7599

Description

@JSONbored

A PR that never actually touches a guarded path can get permanently stuck with the manual-review
label and "merge withheld" — traced live to several JSONbored/awesome-claude PRs that sat held for
8-9 hours despite touching nothing in hardGuardrailGlobs.

Root cause

isGuardrailHit (packages/loopover-engine/src/signals/change-guardrail.ts) is deliberately
fail-safe:

export function isGuardrailHit(changedPaths: string[], hardGuardrailGlobs: string[]): boolean {
  if (hardGuardrailGlobs.length === 0) return false;
  return changedPaths.length === 0 || changedPathsHittingGuardrail(changedPaths, hardGuardrailGlobs).length > 0;
}

When the changed-files list is empty, it treats that as a hit rather than risk missing a guarded
path (#1062). That's the right call when the diff is genuinely unknown. The problem is upstream:
fetchAndStorePullRequestFilesForReview (src/github/backfill.ts) can legitimately return [] for
a PR that has real files, because GitHub can return a clean, successful, empty files: [] response
for a PR whose diff it hasn't finished computing yet (common right after a PR is opened or pushed) —
this is not a rate limit (the shared client in src/github/client.ts already retries those
internally), so nothing currently retries it.

Once that empty result trips the guardrail fail-safe, planAgentMaintenanceActions
(src/settings/agent-actions.ts) applies the manual-review label — and by deliberate design
(#stale-disposition-label-cleanup), that label is never auto-removed, since the planner can't
tell a bot-applied guardrail hold apart from a maintainer's own manual hold using the same label.
So a single transient empty-files read permanently locks an otherwise-clean PR into "held for manual
review, merge denied" until a human notices.

Confirmed live: JSONbored/awesome-claude#5176, #5298, #5162, #5092, #5164 — all CONTRIBUTOR-
association repeat contributors (not first-timers, not bots), all carrying manual-review, all
denied merge repeatedly for 8-9+ hours with "manual-review label \"manual-review\" is present on the live PR — merge not executed". Their actual changed files (checked via the GitHub API) never
touch any of the repo's configured hardGuardrailGlobs (.github/workflows/**, .loopover.yml,
codecov.yml, etc.) at all.

Fix: retry the inline file fetch once, after a short delay, when the first attempt comes back
empty, before accepting it and computing the guardrail check against it. This doesn't touch the
fail-safe (a genuinely persistent failure still degrades to [] → still a hit) or the "never
auto-remove manualReview" invariant (left alone, intentionally) — it just makes the empty result far
less likely to be a false positive in the first place.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.maintainer-onlyOwner-only work — yields no Gittensor points.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions