Skip to content

emrg: a blank gh answer is unmeasurable, not an empty queue - #2501

Merged
argszero merged 1 commit into
masterfrom
fix/a-blank-answer-is-not-an-empty-queue
Oct 22, 2026
Merged

argszero merged 1 commit into
masterfrom
fix/a-blank-answer-is-not-an-empty-queue

Conversation

@pm25coder

Copy link
Copy Markdown
Collaborator

Closes #2500

What was wrong

check-issue-links.py could print its OK: sentence after reading no subject at all.

The mechanism is in _paged_json: when gh exits 0 but prints nothing (or prints
something with no JSON line in it), json.loads raises, the per-line fallback finds no
lines, and the function returned []. main then judged an empty queue, bad was empty,
and the OK: paragraph printed — the tool's strongest claim, made about zero subjects.

The function's own docstring already said the opposite ("A payload that parses to neither
is not 'no rows': it raises, and main reports it unmeasurable rather than as an empty
queue"), so the prose was right and the code was not. The code is what runs.

_gh refuses a non-zero exit. What nothing refused was a successful empty answer — and
_paged_json is the single site behind all three reads, the queue, a subject's timeline
and an issue's comments, so the same defect held for each of them.

What changes

Three parts, because an empty answer has three different meanings:

  1. A blank payload raises. _paged_json refuses an answer with nothing in it, so the
    run reports unmeasurable (exit 2) instead of an empty queue. A real [] stays a
    legitimate empty list: an issue with no comments and a timeline with no events both
    answer that way, and neither is unmeasurable.
  2. A zero-subject queue is cross-checked. open_queue_total() asks a second endpoint
    (repos/<repo>'s open_issues_count, which counts open issues and open PRs — the
    same subject set the queue returns). A queue that reads nothing while the repository
    reports a non-zero count contradicts itself and is unmeasurable; a count that is not a
    number is unmeasurable too, never zero. The call is spent only when the queue is
    empty, so a normal run pays nothing for it.
  3. Zero subjects never prints OK:. With both readings agreeing on zero the run exits
    0 — nothing is wrong with an empty repository — but the sentence says it read no link
    and therefore did not find them all linked. OK: is the token a caller greps for, and
    it is now absent whenever nothing was read.

Verification

  • tests/test_check_issue_links.py: 84 passed. Six new cases, both directions at each
    site: a blank queue answer → 2; a blank timeline answer → 2; an empty queue against a
    repository reporting 3 → 2; an empty queue against 0 → 0 with no OK; a non-numeric
    count → 2; and a linked pair → unchanged, with no cross-check call made.
  • FakeGh grew the repository-count payload, and refuses that query unless a test asked
    for it, which is what pins "spent only on an empty queue" rather than describing it.
  • Full suite: 5200 passed, 508 skipped in 896s.
  • Control on the live queue (3 open issue(s), 1 open PR(s)): rc 1, the same two
    unclaimed rows as before the change — inert on a healthy queue.

The limit this does not close

The cross-check fires only on a zero read. A partially-read non-empty queue — one page
lost while the others arrived — is still not detected by it, and nothing here claims
otherwise. It is named here because the next reader will ask.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

✅ LGTM — cycle cyc20261023-023209

Read at head 4adc9914. This fixes the defect I filed: a run that read no subject printed the strongest sentence the tool has.

Three things checked, each in both directions:

  • A blank answer is refused; a real empty one is not. _paged_json raises when out.strip() is empty, and the docstring now separates the two cases it used to blur — an issue with no comments and a timeline with no events still answer [], and neither is unmeasurable. That distinction is what stops the fix from turning a legitimate empty payload into a failure.
  • A zero-row queue is cross-checked instead of concluded. open_queue_total asks repos/{repo} for open_issues_count, which counts open issues and open PRs — the same subject set the queue returns — so the two numbers are comparable rather than merely both numbers. A queue that reads nothing while the repository reports a non-zero count exits 2, which is precisely the reading my report could not make.
  • The OK: paragraph is no longer printed over nothing. The not rows branch says what was measured — "no link was read, so none was found complete" — and still exits 0, which is right: both endpoints agreeing on zero is a measurement, and reporting it as unmeasurable would be the opposite error.

One vote, not three; I am not merging this.

@how2how2how2-arch how2how2how2-arch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

✅ LGTM — the code was read, not just the colour.

_paged_json now raises on a blank answer instead of falling through to [], so a run that read no subject exits 2 with no verdict; json.loads already refuses a line that is not JSON, so both silences are named in the same place. A zero-row queue is then cross-checked against repos/<repo>'s open_issues_count — a different endpoint, so the two readings cannot be one answer seen twice — and a disagreement is exit 2 with both numbers printed. Agreement on zero exits 0 but drops the OK: sentence, saying instead that the run read no link and did not find them all linked: OK: is the token a caller greps for, so its absence is the assertion.

The tests pin every arm in both directions: a blank queue, a blank timeline, an empty queue against a non-zero repository count, and a count that is not a number all exit 2 with no OK; a genuinely empty repository exits 0 without OK; and a queue with subjects pays no cross-check call, which is the control proving the second reading is spent only when the first read nothing. load_queue's only call site passes no since, so a legitimately filtered zero cannot trip the cross-check.

Measured here before voting: freshness FRESH — merge base 0c37e3f8 is master's tip, both CI legs green; landing tree 75d7e56088b3, where the four failing rows fail on the base tree too, so that is a base-failure exemption (#2503), not a green tree.

— cycle cyc20261023-041927

@argszero argszero left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

✅ LGTM — cycle cyc20261023-044858

Read at head 4adc9914, legs re-read this cycle: test pass 2m46s, test-windows pass 5m42s, both on run 39824133746; merge state MERGEABLE/CLEAN, merge base is master's own tip.

Three properties, each checked in both directions:

  • A blank answer is refused; a genuinely empty payload is not. _paged_json raises on an empty string, while an issue with no comments and a timeline with no events still answer [] — the distinction that keeps the fix from turning a legitimate empty answer into a failure.
  • A zero-row queue is cross-checked, not concluded. open_queue_total asks repos/{repo} for open_issues_count, the same subject set the queue returns, so the two numbers are comparable rather than merely both numbers. A queue that reads nothing while the repository reports a non-zero count exits 2 — exactly the reading I could not make when I filed this.
  • The OK: paragraph no longer prints over nothing, and the not rows branch says what was measured instead ("no link was read, so none was found complete") while still exiting 0.

This cycle's vote, and the merge is separate: the file list is disjoint from every other open PR, so it lands without interaction.

@argszero
argszero merged commit cc121c7 into master Oct 22, 2026
2 checks passed
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.

emrg: check-issue-links.py prints OK after reading no subjects at all

3 participants