Repository navigation
Description
What the reading does today
scripts/review-queue.py renders one row per open rant (pending / in_progress) so a cycle can tell whether a rant has already become work. The pairing is read from the rant entry's own progress field — the place the evolution prompt's R5 writes it at take-up (issue #N), and the only per-installation record that holds the whole history.
The predicate is a substring search:
_PROGRESS_ISSUE = re.compile(r"(?i)\bissues?\s*#(\d+)((?:\s*(?:,|/|&|\band\b)\s*#\d+)*)")\b is a word boundary, not a line anchor: the words issue/issues followed by a number, anywhere in the field, are read as this rant's issue.
The defect
progress is free text, and a cycle writes sentences into it, not only handles. A sentence that mentions some other issue — most often as the reason this rant is not being taken up yet — is read as this rant's handle.
Reproduced on 98468f4 with the tool's own function, no reimplementation:
>>> _progress_issues("R5 is closed this cycle because issue #2633 is open, "
... "and a rant becomes work only by opening its issue first.")
[2633]
>>> _progress_issues("issue #2637")
[2637]
The first field states no pairing. It says a different issue is open, which is precisely why this rant has none, and it is the note a cycle writes when the gate is closed. The reading answers #2633 and the row renders as though the rant had been filed against it.
Why it is not noise
This row is the only reading a cycle consults to decide whether a rant has already become work — R5's fail-closed rule, where "no issue" is the condition for opening one. A false pair therefore tells the next cycle that the rant was already filed, so that cycle neither opens its issue nor takes the rant, and the rant stops moving without anyone deciding to stop it.
It also silently disables the second reading. open_rant_rows consults the issue side (Origin: rant <ts>) only when the entry names no number (scripts/review-queue.py:672), so a narrated number does not merely add a wrong pairing — it prevents the fallback from running on a row the fallback would have answered.
And the sibling instrument cannot catch it: check-issue-links.py asks the opposite question (does an open issue have a PR that claims it) and reports nothing about a rant with no issue at all. Two readings over one wrong pair, both clean.
What the fix must satisfy
- A handle is a statement, not a mention. The form R5 defines —
issue #N— counts when it begins a line (after optional leading whitespace and markdown emphasis), and not when it appears inside a sentence. - The continuation forms stay readable:
issue #1/#2,issue #1, #2,issue #1 and #2— these are how entries spell several numbers, and they are unchanged. - A
progressthat names no number is unchanged: no pairing, and the issue-side fallback still runs, as it does today. - The comment above the predicate tells the truth. It currently calls the pattern "Anchored" while the pattern is not anchored at all.
Acceptance, measured in both directions:
- a
progresswhose sentence narrates another issue ⇒ no pair, and the issues-side reading is consulted; - a
progressthat states the handle ⇒ pair, exactly as today; - a
progresswith no number ⇒ unchanged.
What the change re-reads
The rule changes which existing entries pair, so the ledger is re-measured under it rather than assumed. On this host, over 13 rants in the ledger:
| rows | |
|---|---|
| paired by the substring rule | 12 |
| paired by the anchored rule | 2 |
| lose their pairing | 10 |
| never paired | 1 |
Every one of the 10 is completed, so no row the queue renders changes on the day this lands: the queue renders only pending / in_progress, and the ledger has none. The two that keep their pairing are the two most recent, and both spell the handle exactly the way R5 says to write it (issue #2459 closed; PR #2460 merged …, issue #2637 → PR #2640 …).
The 10 that lose it are entries written before the pairing moved into progress, in the shape of a report on work already finished (Merged. Issue #2565 closed by PR #2566 …, … Issue #1114 CLOSED COMPLETED …). Two consequences, both stated rather than discovered later:
- those rows are history, so the pairing they lose is not read by anything;
- if such an entry is ever returned to
in_progress(R6's correction path), its pairing now falls to the issue-sideOrigin:fallback. For an issue filed after the convention moved that line does not exist, so the row would printno issue yet. That is the failure this row exists to prevent, and it is the price of no longer reading mentions as handles. A cycle writing the handle in the form R5 defines keeps it.
Activity
pm25coder commentedon Oct 30, 2026
Handled by #2659
emrg: a rant's issue handle is a statement, not a number a sentence m…