Skip to content

fix(prs): lower default page size and surface real gh errors - #2912

Open
santhiprakash wants to merge 2 commits into
Graphify-Labs:v8from
santhiprakash:fix/prs-limit-fallback-2850
Open

fix(prs): lower default page size and surface real gh errors#2912
santhiprakash wants to merge 2 commits into
Graphify-Labs:v8from
santhiprakash:fix/prs-limit-fallback-2850

Conversation

@santhiprakash

Copy link
Copy Markdown

Problem

graphify prs requests statusCheckRollup for up to 50 open PRs in one gh pr list GraphQL call. On busy repos (e.g. cilium/cilium, apache/airflow) that page times out with HTTP 504 after six retries.

When gh fails, _gh returned None for every error class and fetch_prs always raised gh CLI not found or not authenticated, so a GraphQL timeout looked like an auth problem.

Fixes #2850

Fix

  • Default page size 20 (was 50) and expose --limit / -n on graphify prs.
  • On failure, automatically retry smaller pages (20 → 10 when the requested limit is larger).
  • Split _gh_call from _gh: preserve None for soft callers, but carry GhFailure with real stderr/exit detail for fetch_prs.
  • Keep FileNotFoundError distinct (gh CLI not found…); only append gh auth login when stderr actually looks auth-related.

Test

  • uv run pytest tests/test_prs.py -q — 56 passed

Busy repos hit GitHub GraphQL 504s when statusCheckRollup is fetched for
50 PRs at once; failures were misreported as missing gh auth. Default
--limit to 20 with automatic smaller-page retries and propagate gh stderr.
Fixes Graphify-Labs#2850

@graphify-labs graphify-labs 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.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.

Formal verification. 1 change(s) tested, no difference found (not proven).


Graphify review — findings

Adds a --limit/-n flag to graphify prs and refactors gh fetching so fetch_prs retries with smaller page sizes (via new _page_limits) when a page fails. Replaces the opaque _gh return with _gh_call returning a (data, GhFailure) pair, surfacing real gh stderr and distinguishing missing-CLI from auth/network errors. Lowers the default PR page limit from 50 to 20 and adds tests covering _gh_call, _page_limits, and fetch_prs retry/error paths.

Worth a look

  • Default PR fetch limit reduced from 50 to 20graphify/prs.py:254 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • fetch_prs default limit reduced from 50 to 20 (silent behavior change)graphify/prs.py:258 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • int() on --limit argument can raise ValueError and crash cmd_prsgraphify/prs.py:766 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 312 functions depend on the 130 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 119 callees
  • new: fetch_prs() — 9 callers, 5 callees
  • new: _build_server() — 2 callers, 16 callees
  • new: cmd_prs() — 2 callers, 12 callees
  • new: _status_color() — 4 callers, 5 callees
  • new: _main() — 4 callers, 3 callees
  • new: render_dashboard() — 1 callers, 10 callees
  • new: render_pr_detail() — 1 callers, 8 callees
  • …and 5 more — each is listed as a finding

Verification — 312 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 144 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify cmd\_prs.

The verifier did not have enough to check cmd\_prs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify fetch\_prs.

The verifier did not have enough to check fetch\_prs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly RuntimeError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_gh (not a proof).

The verifier ran both versions of \_gh on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 2 grounded finding(s) anchored inline below; 11 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/prs.py
return _gh_call(*args)


def fetch_prs(repo: str | None = None, base: str | None = None, limit: int = _DEFAULT_PR_LIMIT) -> list[PRInfo]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionfetch_prs()

9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/prs.py
@@ -681,6 +738,7 @@ def triage_with_opus(prs: list[PRInfo], base: str) -> None:
def cmd_prs(argv: list[str]) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressioncmd_prs()

fans out to 12 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Reject non-numeric and non-positive --limit values with a clear CLI
error instead of an uncaught ValueError traceback.

@graphify-labs graphify-labs 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.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.

Formal verification. 1 change(s) tested, no difference found (not proven).


Graphify review — findings

Adds a --limit/-n flag to graphify prs (default now 20, down from 50) with page-size auto-retry: fetch_prs walks _page_limits and falls back to smaller pages on failure. Refactors _gh into _gh_call returning a (data, GhFailure) tuple that distinguishes missing-CLI, timeout, non-zero exit, and JSON errors, and only appends the gh auth login hint when stderr actually looks auth-related. Adds _parse_limit input validation (exits 2 on non-integer/non-positive) and test coverage for the new failure classification, page-limit chains, and retry behavior.

Worth a look

  • fetch_prs default limit changed from 50 to 20graphify/prs.py:269 · Escalate · medium · 2 independent checks
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • prs command default now fetches only 20 PRsgraphify/prs.py:753 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 317 functions depend on the 135 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 119 callees
  • new: fetch_prs() — 9 callers, 5 callees
  • new: _build_server() — 2 callers, 16 callees
  • new: cmd_prs() — 2 callers, 13 callees
  • new: _status_color() — 4 callers, 5 callees
  • new: _main() — 4 callers, 3 callees
  • new: render_dashboard() — 1 callers, 10 callees
  • new: render_pr_detail() — 1 callers, 8 callees
  • …and 5 more — each is listed as a finding

Verification — 317 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 149 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify cmd\_prs.

The verifier did not have enough to check cmd\_prs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)

Could not verify: Could not verify fetch\_prs.

The verifier did not have enough to check fetch\_prs, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly RuntimeError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_gh (not a proof).

The verifier ran both versions of \_gh on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 2 grounded finding(s) anchored inline below; 11 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/prs.py
return _gh_call(*args)


def fetch_prs(repo: str | None = None, base: str | None = None, limit: int = _DEFAULT_PR_LIMIT) -> list[PRInfo]:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressionfetch_prs()

9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Comment thread graphify/prs.py
@@ -681,6 +750,7 @@ def triage_with_opus(prs: list[PRInfo], base: str) -> None:
def cmd_prs(argv: list[str]) -> None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressioncmd_prs()

fans out to 13 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant