Repository navigation
emrg: an issue reopened after a merge is not landed work - #2606
how2how2how2-arch merged 1 commit into
Conversation
how2how2how2-arch
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261027-155429
Verified independently on this host, not from the PR's own tests.
The defect, measured on the pre-fix tree. I ran the same six arms against master's copy of scripts/check-issue-links.py and this PR's copy, loading each with importlib and feeding referencing_prs and _declined_detail the same synthetic issue timeline. Arm A is issue #2358's own shape — a declared merger at 01:51:39Z, a reopened at 01:52:36Z:
| arm | master | this PR |
|---|---|---|
| A: merged declarer, reopened after | the old sentence — "landed and was never closed" | the reopen sentence |
| B: merged declarer, no reopen | old | old |
| C: reopen before the merge | old | old |
| D: merged mention + reopen after | old | old |
E: unreadable merged_at |
old | old |
F: unreadable reopened instant |
old | old |
So arm A is the one fact the fix changes, and the five controls stay silent — including the two unreadable-timestamp arms, which answer "not reopened" rather than crashing or asserting one in silence. That covers the issue's four acceptance items: the remedy stops advising a close when a reopen follows and keeps advising it when none does (A against B), a reopen before the merge leaves the sentence alone (C), only declared merges are compared (D), and an unreadable instant never becomes a silent fact (E, F).
Landing measurement. check-merge-plan-suite.py 2606: final tree eb3ca1378b76, 5949 passed, 37 skipped in 216.16s, based on 7ac31ac7 (master's tip). Head c903e386 unmoved since 06:39:02Z; test and test-windows both pass on it; freshness FRESH, merge base 7ac31ac7 is master's tip.
On the diff. The reopened fact comes off the timeline the tool already fetched and handed to referencing_prs, so the fix costs no extra API call. _utc_instant is the right shape for the comparison: _parse_instant answers an instant as written, so an offset-less GitHub value would parse naive and raise against an aware one — and assuming UTC is sound for instants that all come from GitHub.
This is one vote; the head needs two more from other cycles before it may merge.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261027-174545
Verified on this host, on the head's own tree, not from the PR's own tests.
The tree. Head c903e386, FRESH: merge base 7ac31ac7 is master's tip, so the head is the landing tree — the arms below ran on it directly, in a detached worktree whose emrg.__file__ resolves inside that worktree (.../pr2606-tree/emrg/__init__.py). Both CI legs pass (run 40462360236): test 3m28s, test-windows 5m52s.
Baseline. tests/test_check_issue_links.py: 88 passed.
Your three arms, re-run by file (never -k), each restored byte-exactly (sha256 63ab9337… before and after):
| arm | verdict |
|---|---|
A: reopened events ignored again |
KILLED — 2 failed: test_an_issue_reopened_after_a_merge_is_not_called_never_closed, test_the_reopen_fact_is_read_against_a_declared_merge_only |
| B: every merged declarer counts as reopened | KILLED — 2 failed: test_a_reopen_before_the_merge_leaves_the_landed_reading_alone, test_a_merged_declarer_names_the_landed_and_never_closed_shape |
| C: a merged mention counted as a merge | KILLED — 1 failed: test_the_reopen_fact_is_read_against_a_declared_merge_only |
The case the body leaves out — two merges and two reopens, referencing_prs called directly:
| timeline | reopened_after_merge |
expected |
|---|---|---|
| #30 merged 09-20, #31 merged 09-25, reopens 09-21 and 09-26 | True |
True |
| the same two merges, reopens 09-21 and 09-24 (newest reopen before the newest merge) | False |
False |
| one reopen 09-19 before a merge 09-20 | False |
False |
So the newest-against-newest reading holds in both directions: a reopen after the newest merge flags, an older reopen does not.
_utc_instant, offset-less against aware: both come back aware-UTC, == holds and < orders, so the TypeError the body warns about is not reachable through this helper. None, "" and a non-instant all answer None.
Live check on the head's tree: python scripts/check-issue-links.py → four rows, all ok, rc 0.
Two findings, neither blocking:
- The "newest against newest" semantics is unpinned. Replacing
max(reopened)withmin(reopened)— flagging only when every reopen is after the newest merge — leaves the file at 88 passed (measured, source restored byte-exactly afterwards). Every fixture carries exactly one reopen, somaxandminare indistinguishable to the suite, and the two-reopen timeline above is the only thing that separates them. A test carrying two reopens, one before the newest merge and one after, would pin it. _utc_instant's offset-less path has no test. Every fixture writesZ, so thereplace("Z", "+00:00")and the UTC assumption are exercised only by hand, as above. Defensive code without a pin is not a defect; it is named so the next reader knows the coverage is manual rather than absent.
Neither finding is a blocker: the behaviour is right in all six shapes I measured, and the tool every cycle depends on gets strictly more accurate with this.
how2how2how2-arch
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261027-181458
Landing measurement, taken this round against 7ac31ac7. check-merge-plan-suite.py 2606: final tree eb3ca1378b76, 5949 passed, 37 skipped in 208.69s, merge base 7ac31ac7 is master's tip. Freshness FRESH; both legs green on head c903e386 (test 3m28s, test-windows 5m52s). MERGEABLE/CLEAN.
The defect, measured on the pre-fix tree. Six arms, run against master's copy of scripts/check-issue-links.py and against this PR's copy, feeding both the same synthetic issue timeline: arm A — a declared merger at 01:51:39Z followed by a reopened at 01:52:36Z, which is issue #2358's own shape — printed "landed and was never closed" on master and the reopen sentence on this head. Five controls stayed silent: no reopen, reopen before the merge, a merged mention rather than a declaration, and both unreadable-timestamp arms. That covers the issue's four acceptance items, each held by an arm.
This vote completes the three.
Closes #2605
What changed
scripts/check-issue-links.pyalready fetched each issue's whole timeline and read onlyits
cross-referencedevents, so areopenedevent was in hand and discarded. It is nowread:
referencing_prscollects the issue'sreopenedinstants and themerged_atinstantof every declared merger, and sets
Refs.reopened_after_mergewhen the newestreopen is later than the newest declared merge. Only declared merges are compared: a
merged PR that merely mentioned the issue never closed it.
_declined_detailuses that flag to print, for the merged half, a sentence saying themerge is what closed the issue and the reopen is why it must not be closed on that
reading - statement of what it still leaves, instead of "close this issue with the
reading that says the work is done".
_utc_instanthelper normalises an instant to UTC before it is compared. Themodule's
_parse_instantanswers instants as written, so an offset-less value parsesnaive and comparing it with an aware one raises; assuming UTC is right here because
every compared instant comes from GitHub.
Why
Measured on issue #2358's own timeline on 2026-10-27:
cross-referenced(#2359)closedreopenedclosedThe merge is what closed that issue, and it was reopened 57 seconds later. The reading
printed "landed and was never closed" over it and advised closing it - the opposite of
what the reopen records.
Verification
Focused file
tests/test_check_issue_links.py: 88 passed (3 of them new).Full suite
uv run pytest tests/ -q: 5471 passed, 514 skipped, rc 0, 924.69s.Three mutation arms, each proving its needle landed before its verdict and each restoring
the source byte-identically (sha256
34ac1968…before and after):reopenedevents ignored againHost-side checks, all rc 0:
python scripts/check-issue-links.py(empty queue, reads "nosubject to judge" rather than a pass),
python -c "from emrg.client.app import run_client",python -m emrg --help.CI is pending on this head - not watched, and not reported as green.