fix(loop): refresh branches before merge, watch post-merge main CI #239

Merged
logan merged 1 commits from feat/238-merge-leg-refresh-watch into main 2026-09-03 13:53:45 +00:00
2 changed files with 44 additions and 7 deletions
+30 -7
View File
@@ -163,7 +163,22 @@ Merge when, and only when, **all** hold:
- the protection contexts `CI / check` and `CI / e2e` are green on the - the protection contexts `CI / check` and `CI / e2e` are green on the
PR's head, read from the API, not from the PR page's badge; PR's head, read from the API, not from the PR page's badge;
- the PR reports mergeable; - the PR reports mergeable;
- the critique leg ran and no open blocker stands. - the critique leg ran and no open blocker stands;
- the branch is **not behind `origin/main`** — the protection's
`block_on_outdated_branch: true` refuses it anyway; never
`force_manually_merged` around it.
**Refresh before every merge.** In the loop worktree: fetch, then
`git merge origin/main` on the PR branch, push. A textual conflict
stops the leg there — as diff text, not as a failed merge click: hunks
the loop authored are resolved by the loop; anything else is left with
`⟦loop⟧` comment for a human, never forced. After any refresh push,
re-poll the PR's own required contexts on the **new head** before
merging.
**Merges happen one at a time**, each re-reading state — the previous
merge moved `main`, and the next PR's mergeability is recomputed at
its own turn.
``` ```
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \ curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
@@ -172,12 +187,20 @@ curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-d '{"Do":"merge","merge_message_field":"default","force_manually_merged":false}' -d '{"Do":"merge","merge_message_field":"default","force_manually_merged":false}'
``` ```
Afterwards: `scripts/issue.sh list --state open` and check the footer **Afterwards watch the `push` run on `main`** — the CI the merge
took. Close stragglers with `issue.sh close`, naming the merge commit. started. A red main after a loop merge is a **halt**: comment what is
`unclaim.yml` handles the label; it is not instant; reopening does not known on the offending PR, mark the state file, stop taking new issues.
restore it. Merging fans out to nothing (releases are the manual That run is the only thing between a clean textual merge of
`release.yml`, which the loop never runs) — the criticism stands before independently-written PRs and a self-contradicting main; no
the merge because nothing stands after it. mergeability check sees it. Only a green main lets the tick proceed (to
footer verification, below).
Footer verification: `scripts/issue.sh list --state open` and check
the footer took. Close stragglers with `issue.sh close`, naming the
merge commit. `unclaim.yml` handles the label; it is not instant;
reopening does not restore it. Merging fans out to nothing (releases
are the manual `release.yml`, which the loop never runs) — the
criticism stands before the merge because nothing stands after it.
## Rails — the loop's absolute rules ## Rails — the loop's absolute rules
@@ -140,6 +140,20 @@ from a concurrent session is caught before the first edit.
- **Only PRs the loop opened.** A collaborator's PR is never merged, never - **Only PRs the loop opened.** A collaborator's PR is never merged, never
commented on for pressure, never touched. commented on for pressure, never touched.
- **Every branch is refreshed against main before its merge**, in the
loop worktree — the refresh is where a textual conflict surfaces, as
diff text: hunks the loop authored are resolved there, anything else
is left to a human with a `⟦loop⟧` comment. The protection's
`block_on_outdated_branch` makes the refresh mandatory for adopted
(pre-loop) branches: behind `main`, a PR cannot merge at all.
Required contexts are re-polled on the refreshed head.
- **Merges are one at a time**, each re-reading state — the previous
merge moved `main`, and the next PR's mergeability is recomputed at
its own turn.
- **Post-merge, the `push` run on `main` is watched.** A red main after
a loop merge halts the loop. That run is the only guard against the
class no mergeability check sees: two PRs touching the same file,
merging cleanly, contradicting each other.
- The gate is the protection rule itself, read from the API: contexts - The gate is the protection rule itself, read from the API: contexts
`CI / check*` and `CI / e2e*` green, PR mergeable. (Required approvals `CI / check*` and `CI / e2e*` green, PR mergeable. (Required approvals
is 0 today; if a second person changes protection rules, the merge is 0 today; if a second person changes protection rules, the merge