Keep pull requests current, and merge them on approval — squash unless curated #17

Closed
opened 2026-08-10 10:11:55 +00:00 by agent.teyla · 0 comments
Contributor

Two rules the steward has the substrate for today, and neither exists yet.

Keeping a PR current. A tango/<ticket> branch goes stale the moment its base moves. With merge-commits and rebase-merge turned off (#5), a stale branch cannot land at all until someone updates it by hand, so the flow stalls on a mechanical step nobody is watching for.

Merging on approval. An approved PR sits until a human returns to click merge. The click carries a decision, though, and it is one Ting/Ting#4 already made: history is curated into logical commits before the PR, so a merge must replay those commits — but only when they were actually curated. A branch that was never curated is exploratory session work ("fix: nu -> nushell" three commits after the typo), and replaying that onto the trunk publishes the noise the curation discipline exists to remove.

So: rebase when the pull request is marked curated, squash when it is not. The label is the author asserting they did the work; absent it, the safe reading is that they did not.

In scope

  • A rule that, on a push to a branch pull requests target, declares that open pull requests based on it be updated.
  • A rule that, on an approving review of a non-draft pull request, declares the merge — rebase if it carries the curated label, squash otherwise.
  • The merge declaration pins head_commit_id to the commit the review approved.
  • The review event family added to the declared webhook, since none of this is delivered today.

Out of scope

  • Performing either. The steward declares effects and performs none; it holds no Forgejo token and checks.module-evaluates fails the build if its unit grows one. Execution is the separate extension this repository has named since PR #2.
  • Deciding whether a review is authoritative. Forgejo's branch protection already owns required approvals and who may give them. Re-deciding it here would put the answer in two places.
  • Marking a PR curated. That is the author's assertion, and Ting/Ting#4 is the discipline behind it. The steward reads the label, it does not award it.
  • Update-then-merge sequencing. Each rule declares its own effect from its own event. Chaining them is an executor's concern.

Boundary

head_commit_id is the load-bearing field, not a detail. Approval is of a commit, not of a branch name — without pinning it, a push landing between the approval and the merge gets merged unreviewed, and the steward would have declared it. With it, the merge fails rather than silently taking the wrong tree.

Draft pull requests are excluded: a draft is not finished being written, and an approval on one is not a request to land it.

The merge styles the rules emit must stay inside the allow-set declared for the repos (#5 turns merge and rebase-merge off). A rule that declares an impossible merge is a rule that fails at the forge, so the two declarations must be checked against each other rather than kept consistent by hand.

The event kind for an approving review cannot be verified from here — this forge carries no webhook to read the wire name off, and its swagger does not enumerate hook event names. So the mapping belongs in the crystallized parameters, not baked into the rules: getting it wrong must be a Nix edit, not a rebuild. Same for the label name and the two styles.

Acceptance

  • An approving review on a PR carrying the curated label declares one merge effect with Do: rebase, naming the repository, the index, and the approved head commit.
  • The same review without the label declares Do: squash.
  • An approving review on a draft declares no merge at all.
  • A push to a base branch declares an update effect naming the repository and the branch; a push to any other branch declares none.
  • Nothing is performed: the effects are declared, as forgejo.comment already is.
  • The styles the rules can emit are a subset of the styles the repo declaration allows, checked mechanically rather than by eye.
  • A policy missing the new parameters is refused rather than silently gating nothing.

Context

  • Ting/Ting#4 — the curate-before-PR discipline the curated label asserts.
  • #5 / #16 — the merge-style allow-set these styles must stay inside.
  • PR #2 — "gate-rule execution is the next extension," which is what would perform these.
Two rules the steward has the substrate for today, and neither exists yet. **Keeping a PR current.** A `tango/<ticket>` branch goes stale the moment its base moves. With merge-commits and rebase-merge turned off (#5), a stale branch cannot land at all until someone updates it by hand, so the flow stalls on a mechanical step nobody is watching for. **Merging on approval.** An approved PR sits until a human returns to click merge. The click carries a decision, though, and it is one Ting/Ting#4 already made: history is curated into logical commits *before* the PR, so a merge must **replay** those commits — but only when they were actually curated. A branch that was never curated is exploratory session work ("fix: nu -> nushell" three commits after the typo), and replaying that onto the trunk publishes the noise the curation discipline exists to remove. So: **`rebase` when the pull request is marked curated, `squash` when it is not.** The label is the author asserting they did the work; absent it, the safe reading is that they did not. ## In scope - A rule that, on a push to a branch pull requests target, declares that open pull requests based on it be updated. - A rule that, on an approving review of a non-draft pull request, declares the merge — `rebase` if it carries the curated label, `squash` otherwise. - The merge declaration pins `head_commit_id` to the commit the review approved. - The review event family added to the declared webhook, since none of this is delivered today. ## Out of scope - **Performing either.** The steward declares effects and performs none; it holds no Forgejo token and `checks.module-evaluates` fails the build if its unit grows one. Execution is the separate extension this repository has named since PR #2. - **Deciding whether a review is authoritative.** Forgejo's branch protection already owns required approvals and who may give them. Re-deciding it here would put the answer in two places. - **Marking a PR curated.** That is the author's assertion, and Ting/Ting#4 is the discipline behind it. The steward reads the label, it does not award it. - **Update-then-merge sequencing.** Each rule declares its own effect from its own event. Chaining them is an executor's concern. ## Boundary `head_commit_id` is the load-bearing field, not a detail. Approval is of a *commit*, not of a branch name — without pinning it, a push landing between the approval and the merge gets merged unreviewed, and the steward would have declared it. With it, the merge fails rather than silently taking the wrong tree. Draft pull requests are excluded: a draft is not finished being written, and an approval on one is not a request to land it. The merge styles the rules emit must stay inside the allow-set declared for the repos (#5 turns `merge` and `rebase-merge` off). A rule that declares an impossible merge is a rule that fails at the forge, so the two declarations must be checked against each other rather than kept consistent by hand. **The event kind for an approving review cannot be verified from here** — this forge carries no webhook to read the wire name off, and its swagger does not enumerate hook event names. So the mapping belongs in the crystallized parameters, not baked into the rules: getting it wrong must be a Nix edit, not a rebuild. Same for the label name and the two styles. ## Acceptance - An approving review on a PR carrying the curated label declares one merge effect with `Do: rebase`, naming the repository, the index, and the approved head commit. - The same review without the label declares `Do: squash`. - An approving review on a draft declares no merge at all. - A push to a base branch declares an update effect naming the repository and the branch; a push to any other branch declares none. - Nothing is performed: the effects are declared, as `forgejo.comment` already is. - The styles the rules can emit are a subset of the styles the repo declaration allows, checked mechanically rather than by eye. - A policy missing the new parameters is refused rather than silently gating nothing. ## Context - Ting/Ting#4 — the curate-before-PR discipline the `curated` label asserts. - #5 / #16 — the merge-style allow-set these styles must stay inside. - PR #2 — "gate-rule execution is the next extension," which is what would perform these.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Ting/Jostoph#17
No description provided.