From 737aa6ed8e06ba66c965659be5ce92e21287f2ee Mon Sep 17 00:00:00 2001 From: paulohenriquevn Date: Fri, 4 Sep 2026 23:12:40 -0300 Subject: [PATCH 1/2] docs(backmerge): state the limit of the evidence that chose the mechanism The docblock cited 12 back-merge PRs, 12 merged, 10 the same day, and concluded the failure mode does not happen. Both repositories ALREADY opened pull requests -- the sample is the repositories that had adopted the mechanism, which is survivorship. It shows the pull request works where it was chosen; it does not show that theokit backmerges will be merged, because under the push nobody there ever had to merge one. Raised by the session that maintains theokit, reviewing the change to its own repository. The claim travels to three repos in a file people read to understand why the mechanism is what it is, so the limit belongs in that file rather than in a review comment nobody finds later. The first back-merge pull request in a repository that is switching is the measurement still missing. The cost of being wrong is at least observable -- an open PR with the branch visibly behind main, rather than a silent drift. actionlint clean. --- .github/workflows/backmerge.yml | 14 ++++++++++++-- CHANGELOG.md | 11 +++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backmerge.yml b/.github/workflows/backmerge.yml index 9a78077..df65f66 100644 --- a/.github/workflows/backmerge.yml +++ b/.github/workflows/backmerge.yml @@ -24,8 +24,18 @@ # merges it, and an unmerged one leaves the branch exactly as far behind as no mechanism at all. # Every back-merge pull request in both repositories was merged, and all but one on the day it was # opened (`theokit-sdk` #511, #516, #519, #528, #540, #548; `theokit-gateways` #108, #111, #114, -# #117, #120, #121). The failure mode did not happen, so the pull request keeps its advantage — a -# reviewable record of what came back — at no observed cost. +# #117, #120, #121). +# +# THAT EVIDENCE IS NARROWER THAN IT LOOKS, and the limit belongs here rather than in a review +# comment nobody will find. Both repositories ALREADY opened pull requests — the sample is the +# repositories that had already adopted the mechanism, which is survivorship. It shows the pull +# request works where it was chosen; it does not show that `theokit`'s back-merges will be merged, +# because under the push nobody there ever had to merge one. The first back-merge pull request in +# a repository that is switching is the measurement this reasoning is still missing. +# +# The cost of being wrong is at least observable: an open pull request with the branch visibly +# behind `main`, rather than a silent drift. If one sits, that is the signal to revisit — not a +# reason to have waited. # # The push mechanism is not wrong. It is simply the one with no audit trail, and with two working # options the one that leaves a record is the one worth sharing. diff --git a/CHANGELOG.md b/CHANGELOG.md index fa22f1f..ec3a42c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 opened. The failure mode did not happen, so the pull request keeps its advantage — a reviewable record of what came back — at no observed cost. + **That evidence is narrower than it looks**, and the workflow now says so rather than leaving the + limit to a review comment. Both repositories ALREADY opened pull requests — the sample is the + repositories that had adopted the mechanism, which is survivorship. It shows the pull request + works where it was chosen; it does not show that `theokit`'s back-merges will be merged, because + under the push nobody there ever had to merge one. The first back-merge pull request in a + repository that is switching is the measurement this reasoning is still missing. Raised by the + session that maintains `theokit`, on reviewing the change to its own repository. + + The cost of being wrong is at least observable — an open pull request with the branch visibly + behind `main`, rather than a silent drift. + The push mechanism is not wrong. With two working options, the one that leaves a record is the one worth sharing. From bb795dae6bbe8558f577b6eb9024d46e65054952 Mon Sep 17 00:00:00 2001 From: paulohenriquevn Date: Sat, 5 Sep 2026 09:49:25 -0300 Subject: [PATCH 2/2] feat(backmerge): report how long the pull request has been open The workflow opened the pull request and stopped. One sitting for ten hours printed the same line as one opened four minutes ago, so nothing distinguished "the mechanism ran" from "the mechanism ran and nobody acted on it". usetheokit/theokit-sdk#548 sat that long, and the drift was found by someone running changeset status by hand before a release cut. Four hours is from the distribution, not a round number. Hours between opening and merging, every back-merge PR in the two repositories that had this workflow: theokit-sdk 10, 1, 1, 0, 0, 0, 0, 12, 0, 0, 0, 0 theokit-gateways 0, 11, 0, 0, 0, 0, 0, 0 Seventeen of twenty merged inside an hour; the three that did not took 10, 11 and 12. The gap between normal and stuck is an order of magnitude, so any threshold inside it separates them. Four is four times the slowest ordinary case and below every outlier. The first draft said twelve, reasoning from "ten of twelve merged the same day". That would have missed the ten-hour incident that prompted the check -- the summary was true and lost the shape the threshold needed. A warning rather than a failure: this job runs on push to main, and failing it would paint main red for something main did not do. createdAt comes back in the call already asking for the number, so no extra API request. actionlint and zizmor clean. --- .github/workflows/backmerge.yml | 42 ++++++++++++++++++++++++++++++--- CHANGELOG.md | 31 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backmerge.yml b/.github/workflows/backmerge.yml index df65f66..e943e69 100644 --- a/.github/workflows/backmerge.yml +++ b/.github/workflows/backmerge.yml @@ -94,10 +94,46 @@ jobs: exit 0 fi - existing="$(gh pr list --base "${TARGET}" --head main --state open --json number \ - --jq '.[0].number // empty')" + # `createdAt` comes back in the SAME call that was already asking for the number, so the + # age below costs no extra API request. + existing="$(gh pr list --base "${TARGET}" --head main --state open \ + --json number,createdAt --jq '.[0] | select(.number) | "\(.number) \(.createdAt)"')" if [ -n "${existing}" ]; then - echo "✓ PR #${existing} is already open and will pick these commits up" + number="${existing%% *}" + created="${existing##* }" + hours=$(( ( $(date -u +%s) - $(date -u -d "${created}" +%s) ) / 3600 )) + + # SAYING IT IS OPEN IS NOT ENOUGH, and that is this block's whole reason. Before this, + # a pull request that had been sitting for ten hours printed the same line as one + # opened four minutes ago — usetheokit/theokit-sdk#548, where the branch stayed a + # release behind and the drift was found by someone running `changeset status` by hand + # before a cut. The mechanism worked; nothing reported that its output was ignored. + # + # A WARNING RATHER THAN A FAILURE, deliberately. This job runs on a push to `main`, and + # failing it would paint `main` red for something `main` did not do — a red that blocks + # nothing is a red people learn to scroll past. The annotation shows on the run and in + # the Actions summary, which is where somebody looking at the release line will be. + # + # FOUR HOURS, and the number comes from the distribution rather than from a round + # figure. Every back-merge pull request in the two repositories that had this workflow, + # measured 2026-09-05 as the hours between opening and merging: + # + # theokit-sdk 10, 1, 1, 0, 0, 0, 0, 12, 0, 0, 0, 0 + # theokit-gateways 0, 11, 0, 0, 0, 0, 0, 0 + # + # Seventeen of twenty merged inside an hour. The three that did not took 10, 11 and 12. + # There is no middle: the gap between normal and stuck is an order of magnitude, so any + # threshold in it separates them cleanly. Four is four times the slowest ordinary case + # and below all three outliers. + # + # The first draft of this said twelve hours, reasoning from "ten of twelve merged the + # same day" — which would have missed usetheokit/theokit-sdk#548 at ten hours, the very + # incident that prompted the check. "Same day" was a summary of the data, and the + # summary lost the shape the threshold needed. + if [ "${hours}" -ge 4 ]; then + echo "::warning title=Back-merge pull request is stale::PR #${number} (main → ${TARGET}) has been open ${hours}h. While it sits, ${TARGET} keeps the previous version and holds changesets that already shipped, so a release cut from it re-releases them." + fi + echo "✓ PR #${number} is already open (${hours}h) and will pick these commits up" exit 0 fi diff --git a/CHANGELOG.md b/CHANGELOG.md index ec3a42c..ae4845e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **The back-merge says how long its pull request has been open (usetheokit/theokit-sdk#566).** + The workflow opened the pull request and stopped. A pull request sitting for ten hours printed + the same line as one opened four minutes ago, so nothing distinguished "the mechanism ran" from + "the mechanism ran and nobody acted on it" — `usetheokit/theokit-sdk#548` sat that long and the + drift was found by someone running `changeset status` by hand before a release cut. + + The age now appears on every run, and past four hours it becomes a warning annotation. + + **Four hours is from the distribution, not a round number.** Every back-merge pull request in the + two repositories that had this workflow, in hours between opening and merging: + + ``` + theokit-sdk 10, 1, 1, 0, 0, 0, 0, 12, 0, 0, 0, 0 + theokit-gateways 0, 11, 0, 0, 0, 0, 0, 0 + ``` + + Seventeen of twenty merged inside an hour; the three that did not took 10, 11 and 12. The gap + between normal and stuck is an order of magnitude, so any threshold inside it separates them + cleanly — four is four times the slowest ordinary case and below every outlier. + + The first draft said twelve hours, reasoning from "ten of twelve merged the same day". That would + have missed the ten-hour incident that prompted the check: the summary was true and lost the + shape the threshold needed. + + A warning rather than a failure, deliberately: this job runs on a push to `main`, and failing it + would paint `main` red for something `main` did not do. A red that blocks nothing is a red people + learn to scroll past. + + `createdAt` comes back in the call that was already asking for the number, so this costs no extra + API request. + - **`backmerge.yml` is now a reusable workflow, and which mechanism it shares was decided by measurement (#53).** Three repositories had a back-merge and two mechanisms: `theokit` pushed straight to `workspace`, `theokit-sdk` and `theokit-gateways` opened a pull request. Three