Repository navigation
Three PR collisions merged cleanly today: nothing asks what the merged tree contains #16155
Description
Three collisions in one session, none detectable
Every collision check we have is textual — git merge-tree, the mergeable flag, path-filtered workflows. All three of today's collisions merged with zero conflicts:
| PRs | mechanism | what merged cleanly | |
|---|---|---|---|
| referential | #16144 / #16149 | one PR deletes a directory the other cites three times and argues its design by analogy to | no file in common at all |
| arithmetic | #16151 / #16141 | one declares reach floor=6000, growth=400; the other adds 29 files to the sweep |
shared file, disjoint hunks, 6406 > 6400 |
| resurrection | #16130 / #16149 | a branch that merged base between a file being added and deleted re-adds it | new file mode 100644, deletion silently reverted |
The third is the sharpest: a deletion is where the textual check is weakest, because re-adding a file conflicts with nothing. #16130 was green, reviewed, both its own bugs fixed — and merging it would have undone a completed site separation, with the only signal being two new file lines in a diff nobody re-reads once it is green.
The question none of them asks
A textual merge check answers can these combine. It never answers should the result hold.
All three would have been caught by asking what does the merged tree contain — cheaply, without running the suite:
referential does any path this PR cites still exist in the merged tree?
arithmetic do the declared counts still match the merged population?
resurrection does the merged tree contain a path base deleted?
The mechanisms differ enough that one detector probably will not cover all three, but they share that question, and today nothing asks it.
The resurrection case is mechanical and worth doing first
It is fully decidable from refs, needs no checkout, and has an exact criterion:
files added by the branch relative to merge-base ∩ files deleted on base since merge-base ≠ ∅
Where merge-base and head agree, git sees one side deleted, one unchanged and the deletion wins — safe. Only a branch that added the path fights the deletion.
This distinction is load-bearing and easy to get wrong. The obvious proxy — does the branch contain the deleted path — flagged 6 of 7 open PRs today, including the one unblocking every session's pushes, where the true criterion flags exactly one. A proxy validated on the case it was derived from can disagree everywhere else, and this one fails toward alarm.
Acceptance criteria
- A check reports when a PR's merged result would re-add a path deleted on base since the branch's merge-base, using the add-relative-to-merge-base criterion — not "the branch contains the path"
- The check names the paths and states which side introduced them, so the reader can tell a resurrection from a legitimate re-add
- A test proves the false-positive case: a branch that merely carries a file unchanged since its merge-base is not flagged, with a fixture built from the fix(guards): bind the hooksPath guard to what it examined (#16150) #16151 shape
- A test proves the true-positive case, built from the fix(kb): the summary evaluator must see more than the opening 200 characters (#16110) #16130 shape — merge-base 0, head 2, base 0
- The check distinguishes no resurrection found from could not compare (shallow clone, missing merge-base) and never reports the second as the first
- Whether the referential and arithmetic cases fold into the same check, or need their own, is decided and written down rather than left open
Related
- guard(migrations): nothing detects two branches claiming the same Alembic revision id #15866, guard(migrations): two open PRs can chain off the same parent, and per-PR CI cannot see it #15899 — the same family scoped to Alembic revisions: two branches colliding on a shared counted thing with no textual conflict. Prior art for the general case rather than duplicates of it.
- refactor(site): remove the root-page copy, its source is now one repo (#16145, #16140) #16149, fix(kb): the summary evaluator must see more than the opening 200 characters (#16110) #16130, feat(site): social preview cards for the two skills marketplaces (#16143) #16144, fix(guards): bind the hooksPath guard to what it examined (#16150) #16151, fix(guards): the hooksPath guard could not read a single git hook (#16139) #16141 — today's three instances
- ci: a merge to Dev_new_gui can land with no required context on the head it merges #15107 — a merge landing with no required context on the head; adjacent, about the merge commit rather than its content
Activity