One finding, two merged implementations. #3164 said the required suite runs under -x, so a red check names one failure of an unknown number. Both of these landed for it, 50 minutes apart:
| pull request |
merged |
squash |
what it adds |
| #3167 |
2026-09-03T03:35Z-ish |
cd095d6 |
tests/session_truncation.py + a pytest_configure hook in tests/conftest.py - an in-process terminal-summary section |
| #3166 |
2026-09-03T04:25Z |
b8c237a |
scripts/report_truncated_test_run.py + a test-lint.yml step - parses the log, writes the job summary and an annotation |
#3166 carries Closes #3164; #3167 says Refs #3164. So the issue is closed and the pair is invisible from it.
Why all four duplicate keys missed it
Measured against the keys AGENTS.md > PR Workflow step 1 documents:
| key |
why it was silent |
claim (closingIssuesReferences) |
only #3166 claimed. The claim set has one member, so there is nothing to collide on. |
| added paths |
disjoint. tests/session_truncation.py, tests/test_session_truncation_is_reported.py against scripts/report_truncated_test_run.py, tests/test_truncated_test_run_is_reported.py. |
| changelog slug |
3167-session-truncation-is-reported against 3164-a-red-check-says-how-much-it-never-reached. Two names for one subject - the #2820/#2822 shape. |
| weak key (2 shared words AND a shared edited pre-existing test) |
the conjunction is false: #3167 edits tests/conftest.py and docs/contributing.md, #3166 edits .github/workflows/test-lint.yml. No pre-existing test in common. |
The claim key's silence is the structural one, and it is not an accident of this pair: step 1 itself prescribes Refs #N as the correct spelling for a deliberate second implementation, so the documented spelling for an intended duplicate is exactly the spelling that hides an unintended one.
They compose - main is not broken
Verified rather than assumed, because the two now sit in one pipeline: #3167 prints the subtraction into the log and #3166 parses that same log.
Reconstructed a truncated session log carrying #3167's section above the counts line and ran #3166's script over it: truncated, 34547 of 46583 items executed, 12036 never ran (25.8%), exit 0, annotation emitted. #3167's heading (session truncated: 34547 of 46583 collected tests ran) matches neither pattern it could have collided with - _COLLECTED requires collected N items and the heading says collected tests, and _SUMMARY_LINE requires a trailing in <n>s. #3166's _EXECUTED_LABELS also correctly counts xfailed/xpassed/error and excludes deselected/warnings/rerun, so a complete run with xfails is not misreported as truncated.
What is actually duplicated
Not the surface - the arithmetic. The subtraction now has two owners:
They agree on the run above. The surface where they can diverge: #3166 falls back to collected when pytest prints / N deselected without a following / M selected token, while the plugin always uses the post-deselection count - so a deselecting run would state two different "never ran" numbers on two surfaces, with nothing to say which is right. Not observed on this suite; recorded as the drift surface, not as a defect.
Bounded remedy if it is wanted, and it is this repository's own convention ("give the vocabulary one owner and delegate to it, rather than a second inline copy that reports differently"): have the script prefer the line the plugin already states and keep the arithmetic as the fallback. Small, and optional.
Negative result: do not add the obvious intake key
Recorded so a later cycle does not rebuild it. The tempting fix is to widen the claim key to cross-references: an issue with two or more pull requests open at the same time referencing it, where at most one claims the close. Measured over 27 issues in #2709-#3164 against the 80 most recently updated closed/merged pull requests (#3077-#3168):
That is the threshold this repository already names: a finding on a third of the queue is boilerplate, and a gate authors click through teaches them to click through it. The 25 false selections are epics (#2977, #2940), cross-cutting tripwire issues (#2963), the 476-file draft (#2848), and one recurring shape worth naming - the pull request that exposed the bug (#3161 references #3164; #2708 references #2709), which is never a duplicate of the fix.
It is also strictly disjoint from the existing key rather than a superset: the claim key catches #3132 (#3135 + #3136) and #3094 (#3095 + #3097), and "at most one claims" excludes both by construction.
Narrowed - at most 3 referencing pull requests, excluding pull requests that reference 3 or more issues, excluding pull requests already named in the issue body - it selects about 4 pairs, 2 genuine (about 50%), and still recovers #3164 and #3069. That version is defensible as an advisory line in a health sweep, not as a check. Even then it cannot reach the more common shape in this window: 2 of the 5 genuine pairs were sequential (#3066/#3086 on #2935, #2981/#3077 on #2963), and no simultaneity key sees those at all.
Suggested disposition
No revert: both changes are correct and their surfaces are complementary (one is available to every local pytest run, the other reaches a reviewer without downloading a 6.4 MB log). The actionable residue is the one-owner cleanup above. The intake-key half should stay closed with the numbers attached, which is the point of filing this rather than leaving it in a merge comment.
Refs #3164, #3166, #3167.
One finding, two merged implementations. #3164 said the required suite runs under
-x, so a red check names one failure of an unknown number. Both of these landed for it, 50 minutes apart:cd095d6tests/session_truncation.py+ apytest_configurehook intests/conftest.py- an in-process terminal-summary sectionb8c237ascripts/report_truncated_test_run.py+ atest-lint.ymlstep - parses the log, writes the job summary and an annotation#3166 carries
Closes #3164; #3167 saysRefs #3164. So the issue is closed and the pair is invisible from it.Why all four duplicate keys missed it
Measured against the keys AGENTS.md > PR Workflow step 1 documents:
closingIssuesReferences)tests/session_truncation.py,tests/test_session_truncation_is_reported.pyagainstscripts/report_truncated_test_run.py,tests/test_truncated_test_run_is_reported.py.3167-session-truncation-is-reportedagainst3164-a-red-check-says-how-much-it-never-reached. Two names for one subject - the #2820/#2822 shape.tests/conftest.pyanddocs/contributing.md, #3166 edits.github/workflows/test-lint.yml. No pre-existing test in common.The claim key's silence is the structural one, and it is not an accident of this pair: step 1 itself prescribes
Refs #Nas the correct spelling for a deliberate second implementation, so the documented spelling for an intended duplicate is exactly the spelling that hides an unintended one.They compose -
mainis not brokenVerified rather than assumed, because the two now sit in one pipeline: #3167 prints the subtraction into the log and #3166 parses that same log.
Reconstructed a truncated session log carrying #3167's section above the counts line and ran #3166's script over it:
truncated, 34547 of 46583 items executed, 12036 never ran (25.8%), exit 0, annotation emitted. #3167's heading (session truncated: 34547 of 46583 collected tests ran) matches neither pattern it could have collided with -_COLLECTEDrequirescollected N itemsand the heading sayscollected tests, and_SUMMARY_LINErequires a trailingin <n>s. #3166's_EXECUTED_LABELSalso correctly countsxfailed/xpassed/errorand excludesdeselected/warnings/rerun, so a complete run with xfails is not misreported as truncated.What is actually duplicated
Not the surface - the arithmetic. The subtraction now has two owners:
collectedislen(session.items)(post-deselection),startedis counted atpytest_runtest_logfinish.collected N itemsline, minus the sum of the executed labels on the summary line.They agree on the run above. The surface where they can diverge: #3166 falls back to
collectedwhen pytest prints/ N deselectedwithout a following/ M selectedtoken, while the plugin always uses the post-deselection count - so a deselecting run would state two different "never ran" numbers on two surfaces, with nothing to say which is right. Not observed on this suite; recorded as the drift surface, not as a defect.Bounded remedy if it is wanted, and it is this repository's own convention ("give the vocabulary one owner and delegate to it, rather than a second inline copy that reports differently"): have the script prefer the line the plugin already states and keep the arithmetic as the fallback. Small, and optional.
Negative result: do not add the obvious intake key
Recorded so a later cycle does not rebuild it. The tempting fix is to widen the claim key to cross-references: an issue with two or more pull requests open at the same time referencing it, where at most one claims the close. Measured over 27 issues in #2709-#3164 against the 80 most recently updated closed/merged pull requests (#3077-#3168):
That is the threshold this repository already names: a finding on a third of the queue is boilerplate, and a gate authors click through teaches them to click through it. The 25 false selections are epics (#2977, #2940), cross-cutting tripwire issues (#2963), the 476-file draft (#2848), and one recurring shape worth naming - the pull request that exposed the bug (#3161 references #3164; #2708 references #2709), which is never a duplicate of the fix.
It is also strictly disjoint from the existing key rather than a superset: the claim key catches #3132 (#3135 + #3136) and #3094 (#3095 + #3097), and "at most one claims" excludes both by construction.
Narrowed - at most 3 referencing pull requests, excluding pull requests that reference 3 or more issues, excluding pull requests already named in the issue body - it selects about 4 pairs, 2 genuine (about 50%), and still recovers #3164 and #3069. That version is defensible as an advisory line in a health sweep, not as a check. Even then it cannot reach the more common shape in this window: 2 of the 5 genuine pairs were sequential (#3066/#3086 on #2935, #2981/#3077 on #2963), and no simultaneity key sees those at all.
Suggested disposition
No revert: both changes are correct and their surfaces are complementary (one is available to every local pytest run, the other reaches a reviewer without downloading a 6.4 MB log). The actionable residue is the one-owner cleanup above. The intake-key half should stay closed with the numbers attached, which is the point of filing this rather than leaving it in a merge comment.
Refs #3164, #3166, #3167.