diff --git a/.github/workflows/silent-revert-canary.yml b/.github/workflows/silent-revert-canary.yml index f9d77539c..13d2588ee 100644 --- a/.github/workflows/silent-revert-canary.yml +++ b/.github/workflows/silent-revert-canary.yml @@ -4,11 +4,11 @@ name: silent-revert-canary # another recently-merged commit had just added. # # On 2026-08-15 three squash merges each landed a tree that dropped work a -# sibling PR had merged minutes earlier (#2633 dropped #2632, #2639 dropped -# #2635, #2641 dropped #2639). Every check stayed green through all three, -# because each reverting squash removed the code AND its tests in the same -# commit -- no suite can fail for a behavior whose tests are gone. Two issues -# sat CLOSED as COMPLETED with their fixes absent from main. +# sibling PR had merged minutes earlier (#2633 dropped both #2644 and #2642, +# #2639 dropped #2635, #2641 dropped #2639). Every check stayed green through +# all three, because each reverting squash removed the code AND its tests in +# the same commit -- no suite can fail for a behavior whose tests are gone. +# Two issues sat CLOSED as COMPLETED with their fixes absent from main. # # DETECTION, NOT PREVENTION -- and deliberately so. # diff --git a/scripts/check-silent-revert.sh b/scripts/check-silent-revert.sh index 2fb9d7a42..09e857a95 100755 --- a/scripts/check-silent-revert.sh +++ b/scripts/check-silent-revert.sh @@ -13,9 +13,19 @@ # WHY THIS EXISTS (#2691) # ---------------------- # On 2026-08-15, three squash merges each landed a tree that dropped work a -# sibling PR had merged minutes earlier. #2633 dropped #2632's rollups (853 -# lines); #2639 dropped #2635's report-ordering fix (346); #2641 dropped -# #2639's guard work (451) -- destructive_guard.py went 1643 -> 1424 lines. +# sibling PR had merged minutes earlier. #2633 dropped #2644's finding rollups +# (853 lines) AND #2642's aliased GraphQL merge evidence (301) in one squash -- +# plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh went 2178 -> +# 1700 lines with every `rollup` and `graphql` marker at zero; #2639 dropped +# #2635's report-ordering fix (346); #2641 dropped #2639's guard work (451) -- +# destructive_guard.py went 1643 -> 1424 lines. +# +# Every PARENTHESIZED line count above is a blame attribution: the number of +# lines the reverting squash deleted that `git blame` credits to that one +# culprit commit. It is not the squash's diffstat (cc58cbc53 removed 1165 +# lines in total) and not the number of lines the culprit added. The bare +# `2178 -> 1700` and `1643 -> 1424` figures are a different measurement -- +# whole-file line counts before and after. # # Every check stayed green through all three, and that is the point: each # reverting squash removed the code AND the tests covering it in the same @@ -49,10 +59,10 @@ # chosen. # # Curated marker strings (#2691's own suggestion 3). Catches only what -# somebody pre-registered. Nobody had registered #2632, #2635 or #2639 -- -# registration happens after you already know a fix matters, which is exactly -# the knowledge the incident destroys. It also decays: the list is only as -# fresh as the last person who remembered to append to it. +# somebody pre-registered. Nobody had registered #2644, #2642, #2635 or +# #2639 -- registration happens after you already know a fix matters, which +# is exactly the knowledge the incident destroys. It also decays: the list is +# only as fresh as the last person who remembered to append to it. # # Merge-base staleness (the PR's branch point vs. what landed since). # Tested and REJECTED on evidence: it exonerates all three real incidents, @@ -65,10 +75,12 @@ # open, so it fires on almost everything. # # What is left is content: blame the lines a merge deleted and see who had just -# added them. Measured over the last 500 first-parent commits of main, the three -# known incidents score 853 / 451 / 346 lines against a single recent commit, -# and the highest verified-legitimate commit scores well below the threshold -# below. The separation is what makes the canary livable. +# added them. Measured over the last 500 first-parent commits of main, the +# three known incidents score 853 / 451 / 346 lines against a single recent +# commit -- plus a fourth attribution of 301 lines on the SAME #2633 squash, +# whose deletions trace to two different culprits and are reported separately. +# The highest verified-legitimate commit scores well below the threshold below. +# The separation is what makes the canary livable. # # FALSE-POSITIVE STRATEGY (the whole design rests on this) # ------------------------------------------------------- @@ -101,8 +113,10 @@ # THE RESIDUAL FALSE POSITIVE, MEASURED RATHER THAN ASSUMED # --------------------------------------------------------- # At these settings, over the last 500 first-parent commits of main, the canary -# fires 5 times -- 1%. Three are the confirmed incidents. The other two are both -# real, and neither is a bug in the detector: +# fires on 5 commits -- 1%. (Six findings, not five: #2633's squash deleted +# content from two different culprits and each attribution is reported on its +# own.) Three of the five commits are the confirmed incidents. The other two +# are both real, and neither is a bug in the detector: # # 6f0a31109 (#2640, 390 lines) is the manual RESTORE of #2633's revert. To # put back what #2633 dropped it had to delete what #2633 had added, so a diff --git a/scripts/silent-revert-acknowledged.txt b/scripts/silent-revert-acknowledged.txt index a35ed716f..56e2c0bd4 100644 --- a/scripts/silent-revert-acknowledged.txt +++ b/scripts/silent-revert-acknowledged.txt @@ -26,9 +26,10 @@ # the canary's first fire is a NEW event rather than known backlog. # #2640 is the manual restore of the content #2633's squash dropped. Putting -# #2632's rollups back required deleting what #2633 had put in their place, so -# a large removal of very recent content is precisely what this commit is. -6f0a3110942375fb292112f5df5e2c39bbf56cb0 #2640 restores #2632's rollups; the removal undoes #2633's revert +# #2644's finding rollups and #2642's aliased GraphQL merge evidence back +# required deleting what #2633 had put in their place, so a large removal of +# very recent content is precisely what this commit is. +6f0a3110942375fb292112f5df5e2c39bbf56cb0 #2640 restores #2644's rollups and #2642's GraphQL merge evidence; the removal undoes #2633's revert # #2135's author noticed main move under the PR, merged origin/main into the # branch rather than force-pushing, and argued the disposition explicitly in the diff --git a/scripts/silent-revert-incidents.txt b/scripts/silent-revert-incidents.txt index ce9d08d52..50d765990 100644 --- a/scripts/silent-revert-incidents.txt +++ b/scripts/silent-revert-incidents.txt @@ -30,11 +30,30 @@ fires f603880dad4003477ec7cac27654fce92c75eec8 #2639 dropped #2635 (346 lines), # went to zero occurrences. #2618 was left CLOSED as COMPLETED. fires 9239f1541b1b15a2a183ad6b4067e577f931e3f1 #2641 dropped #2639 (451 lines), 10 minutes later -# Found by this canary's own calibration run, NOT by the #2691 audit: the same -# night, #2633's squash dropped #2632's finding rollups (853 lines). #2640 had -# to restore them by hand. Nobody had filed it, which is the clearest argument -# for automating the detection. -fires cc58cbc53fbbb2cca68e071507b82d3e3ff896ff #2633 dropped #2632's rollups (853 lines) -- unfiled until now +# The third merge from the same night, surfaced by this canary's own +# calibration run rather than by the #2691 audit. #2633's squash dropped work +# from TWO siblings at once, and the canary reports each culprit separately: +# +# 853 lines traced to bfb66beb8 (#2644, finding rollups and handoff plans) +# 301 lines traced to eda5ae5ed (#2642, aliased GraphQL merge evidence) +# +# Both numbers are blame attributions -- the count of lines cc58cbc53 deleted +# that `git blame` credits to that one culprit. Neither is the squash's +# diffstat (it removed 1165 lines in total) and neither is what the culprit +# itself added. plugins/repo-fleet-hygiene/skills/audit/scripts/audit-fleet.sh +# went 2178 -> 1700 lines with every `rollup` and `graphql` marker at zero; +# #2640 restored both surfaces by hand. +# +# This event was NOT unfiled. Issue #2656 recorded it, pinned to the same +# commits, roughly 23 hours before this canary merged -- from the test-coverage +# angle (the hand restore left `bare-repo-with-working-tree` uncovered) rather +# than as a silent revert. What the canary adds is detection speed and +# automation, not discovery. +# +# An earlier version of this row named #2632 as the victim. That was wrong: +# #2632 never merged (closed unmerged, no merge commit, not an ancestor of +# main) and its diff contains no rollup work at all. +fires cc58cbc53fbbb2cca68e071507b82d3e3ff896ff #2633 dropped #2644's rollups (853 blamed lines) and #2642's GraphQL merge evidence (301); already recorded in #2656 # --- Verified-legitimate commits that must stay quiet ------------------------- # The closest sub-threshold miss in 500 commits: 129 lines of a doc section that