Commit cc886b9
authored
feat(review): report a superseded pull request as superseded, not unlinked (#10205)
* feat(review): detect a pull request superseded by a merged rival
A contributor whose linked issue is closed by a rival PR that merged first is
currently told "No linked issue detected — link it explicitly in the PR body".
They did link one, correctly, and the advice cannot work: re-linking a closed
issue changes nothing. metagraphed#8886 linked issue #8829 at 09:22:36; rival
#8881 merged at 09:30:24 and the issue closed one second later, and from then on
every evaluation produced the same unactionable hold.
confirmedNoOpenLinkedIssue collapses gaming (citing an already-dead issue to
clear linkedIssueGateMode: block) with supersession (linking a genuinely open
issue that a rival then closed). Only the first is a linking failure.
The two separate on facts already in hand, with no new GitHub call: the issue's
closed_at postdates the PR's created_at, and a MERGED sibling citing the same
issue landed in the window ending at that close. GitHub's issue payload already
carries closed_at (#4528) and the linked-issue pass already fetches every linked
issue -- it discarded everything but a boolean. The rival comes from our own
pull_requests rows; the duplicate machinery cannot see it, because that keys on
OPEN siblings and the rival stopped being one when it merged.
Every uncertain case resolves to "not superseded": a supersession verdict closes
a contributor's PR, so a missing timestamp, an unparseable date, a still-open
issue, or an absent rival all leave the existing disposition alone. Ordering is
pinned on both axes (ascending issue number, then ascending PR number within one
issue) because a result that closes a PR must not depend on database row order.
Refs #10168
* feat(db): read merged pull requests in a time window for supersession checks
The supersession resolver (#10168) needs the rivals that merged between a PR's
creation and its linked issue's close. Selects only number/merged_at/linked
issues, bounded by the window and capped, ordered by ascending merge time so the
cap keeps the earliest merges -- the resolver elects the earliest qualifying
rival, so dropping those would mis-name the cause.
Reads pull_requests rather than the purpose-shaped recent_merged_pull_requests
table: that table is no longer written -- its newest row on the Orb is three
weeks old -- so a rival that merged minutes ago is absent from it entirely, and a
check reading it would silently never fire.
Refs #10168
* feat(review): report a superseded pull request as superseded, not unlinked
Splits the confirmedNoOpenLinkedIssue verdict in two. A PR that cited an
already-dead issue keeps reading as missing_linked_issue -- that is the gaming
case the guardrail exists for. A PR whose genuinely-open issue a rival closed
after it opened now gets its own code, its own message naming the rival, and an
action a contributor can actually take.
The two facts are proven before the split fires: the issue outlived this PR's
creation, and a merged sibling citing it landed in the window ending at its
close. resolveLinkedIssueHasOpenReference already fetched every linked issue and
discarded all but a boolean, so the closure facts now ride out of that same pass
-- no extra GitHub call. The rival comes from a bounded pull_requests read.
Wired through all four gate-evaluating call sites via the shared
resolveLinkedIssueAdvisoryContext, so the sweep, the webhook path, the heavy
re-review and authorized PR actions cannot disagree about it. Applied to BOTH
advisory twins (src/rules/advisory.ts and the engine's gate-advisory.ts), each
with its own suite, since a fix to only one side is exactly the drift that pair
is kept apart to expose.
linked_issue_superseded rides the same linkedIssueGateMode knob it was split out
of: a repo that opted into block already asked for this PR to be acted on, and
splitting the message must not quietly change WHETHER it is acted on -- only what
the contributor is told and which code the ledger records. It joins
CONCRETE_EVIDENCE_BLOCKER_CODES on the same footing as its sibling (two recorded
timestamps and a merged PR's own linked-issue set; no AI judgment) and
CONFIGURED_GATE_BLOCKER_SIGNAL_CODES so its reversals record under their own id.
Gated OFF by LOOPOVER_SUPERSEDED_CLOSE: recognising supersession CLOSES the pull
request, so it ships dark until shadow-checked against the held backlog.
Closes #10168
* test(review): cover the supersession wiring end to end
Drives the split through reReviewStoredPullRequest rather than against
buildPullRequestAdvisory directly, so the whole seam is proven: the linked-issue
verification pass carrying closure facts out, the bounded merged-rival read, the
flag, and the finding the contributor actually sees. Reproduces the Orb's own
collision -- PR 8886 citing issue 8829, rival 8881 merging behind it, the issue
closing one second later -- and pins that the feature is byte-identical with the
flag off.
The candidate-window derivation moves into the pure module beside the resolver
it serves, so 'can this even be superseded' is one tested unit instead of
branches stranded in processors.ts that only an integration test could reach.
Refs #101681 parent c7645de commit cc886b9
19 files changed
Lines changed: 930 additions & 46 deletions
File tree
- packages/loopover-engine
- src/advisory
- test
- src
- db
- queue
- review
- rules
- settings
- test/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
195 | 195 | | |
196 | 196 | | |
197 | 197 | | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
198 | 204 | | |
199 | 205 | | |
200 | 206 | | |
| |||
223 | 229 | | |
224 | 230 | | |
225 | 231 | | |
226 | | - | |
| 232 | + | |
227 | 233 | | |
228 | 234 | | |
229 | 235 | | |
| |||
298 | 304 | | |
299 | 305 | | |
300 | 306 | | |
| 307 | + | |
| 308 | + | |
301 | 309 | | |
302 | 310 | | |
303 | 311 | | |
| |||
314 | 322 | | |
315 | 323 | | |
316 | 324 | | |
317 | | - | |
318 | | - | |
319 | | - | |
320 | | - | |
321 | | - | |
322 | | - | |
323 | | - | |
324 | | - | |
325 | | - | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
326 | 348 | | |
327 | 349 | | |
328 | 350 | | |
| |||
Lines changed: 73 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
5116 | 5116 | | |
5117 | 5117 | | |
5118 | 5118 | | |
| 5119 | + | |
| 5120 | + | |
| 5121 | + | |
| 5122 | + | |
| 5123 | + | |
| 5124 | + | |
| 5125 | + | |
| 5126 | + | |
| 5127 | + | |
| 5128 | + | |
| 5129 | + | |
| 5130 | + | |
| 5131 | + | |
| 5132 | + | |
| 5133 | + | |
| 5134 | + | |
| 5135 | + | |
| 5136 | + | |
| 5137 | + | |
| 5138 | + | |
| 5139 | + | |
| 5140 | + | |
| 5141 | + | |
| 5142 | + | |
| 5143 | + | |
| 5144 | + | |
| 5145 | + | |
| 5146 | + | |
| 5147 | + | |
| 5148 | + | |
| 5149 | + | |
| 5150 | + | |
| 5151 | + | |
| 5152 | + | |
| 5153 | + | |
| 5154 | + | |
5119 | 5155 | | |
5120 | 5156 | | |
5121 | 5157 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
735 | 735 | | |
736 | 736 | | |
737 | 737 | | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
738 | 744 | | |
739 | 745 | | |
740 | 746 | | |
| |||
0 commit comments