Skip to content

The upstream fast-forward (ruleset off → push → ruleset on) lives only in a Bot's head: write the runbook and verify the restore #815

Closed
Birfy/FlyBot
#37

Description

@Birfy

What is wrong

ThinkFlowLab/FlyBot main is protected by ruleset 25173372 ("main merge queue"): enforcement: active, bypass_actors: [], current_user_can_bypass: never, with merge_queue (SQUASH) and required_status_checks rules on refs/heads/main. With no bypass actor a push of the fork's main to upstream main is refused — right for protection, fatal for a fast-forward.

The only route used so far is: set the ruleset to disabled, push the fork's main to upstream, set it back to active. That sequence lives in one Bot's head. Nothing in the repository records it, nothing checks that the ruleset came back, and a failure between the two flips leaves main unprotected while the next release PR is prepared. Nor is there a record of the call that works from this machine, where git push to github.com:443 does not connect and only api.github.com answers.

AGENTS.md describes upstream main's merge queue (section "Merge gates") but never says how the fork's commits reach it; the daily-release row names only the release itself.

Expected behaviour

A checked-in runbook another Bot can follow without asking, a read-only check that answers "is the fork in sync and is the ruleset active?", and a sync path whose last step is the restore verified rather than assumed.

The release flow this runbook has to describe (owner, 2026-10-23)

One ruleset window per version. The fast-forward and the tag travel in the same window, and the upstream release is created inside it — never a second window later. Every release leaves the fork ahead again (measured today: fork f20cfa37, upstream 6f9b60c7, ahead_by: 3, behind_by: 0), so this repeats with every version.

The six steps, in this order:

  1. the release pull request lands on the fork and main's Test is green;
  2. the tag goes on the release commit, not on the tip;
  3. the fork's Release workflow produces the four assets; check the sha256 of each;
  4. re-run the dry-run and its five gates;
  5. open the window: PUT the ruleset to disabled → fast-forward with force: false → restore in finally and read the ruleset back;
  6. create the release upstream and upload the same asset bytes.

Two facts measured on 2026-10-23 go into the runbook verbatim, not paraphrased:

  • a ruleset update is PUT; PATCH returns 404 every time;
  • under the billing lock, both the tag run and main's Test are zero-step, 4-second failures — that is the lock, not a failed release, and there is nothing to cancel.

Stop rule: if the fast-forward ancestry no longer holds, do not guess — stop and report to Chief.

Acceptance

  1. docs/release-runbook.md exists, is linked from AGENTS.md, and covers in order: the preflight reads (upstream ruleset id and state, both main SHAs, the commits to push); the disable call; the fast-forward; the restore call; a post-check that reads the ruleset back and asserts enforcement: active, bypass_actors: [], current_user_can_bypass: never.
  2. The runbook names the write path this machine actually has (api.github.com, ref update; git push fails) and the permission needed (admin on the upstream repo), including the exact error to expect without it.
  3. The failure path is written out: what to do when the push fails after the disable, so the ruleset is never left off, and one command that proves the restore.
  4. A read-only check runs without side effects: node .github/scripts/upstream-sync.mjs --check prints both main SHAs, ahead/behind/equal, and the ruleset state; it exits 0 only when the SHAs are equal and the ruleset is active with empty bypass, otherwise nonzero naming the failed condition.
  5. .github/scripts/upstream-sync.test.mjs pins the exit codes for: equal + active (0); behind (nonzero); ruleset inactive (nonzero); non-empty bypass (nonzero); ruleset read 404 (nonzero, message names the id).
  6. Nothing in --check writes to either repository.
  7. The runbook contains the six release steps above, the "one window per version" rule, and the two measured facts verbatim (PUT vs PATCH 404; zero-step 4-second failures are the billing lock).
  8. --check prints the ahead_by/behind_by numbers it read, not only a boolean.

Kill conditions

  • A runbook that says "disable the ruleset" without the restore step and the read-back.
  • Automation that can push while leaving the ruleset off — no finally/trap equivalent.
  • Leaving the runbook only in a pull-request body or in a Bot's memory.
  • Claiming the fast-forward is git push from this machine without having tested it.
  • Requiring a permission Release does not have, without saying so up front.
  • Changing the upstream ruleset's design: the empty bypass_actors is a deliberate owner decision, not a bug.
  • A second ruleset window for the same version, or a tag that reaches upstream without the fast-forward in the same window.

Out of scope

Activity

  1. added
    documentationImprovements or additions to documentation
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 23, 2026
  2. Birfy commented on Oct 23, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Release

    Claiming this one. Branch feat/815-upstream-sync in Birfy/FlyBot, pull request #37 at a30f77d2.

    What is in it: docs/release-runbook.md (the six steps, one window per version, the disable/fast-forward/restore sequence with the read-back, the write path and the permission, the failure path, and the two measured facts quoted verbatim), node .github/scripts/upstream-sync.mjs --check (both main SHAs, ahead_by/behind_by, the ruleset row; exit 0 only when the mains are equal and the ruleset is active with an empty bypass list), its test file, the AGENTS.md link, and one changelog fragment.

    Run on the tip: node --test .github/scripts/upstream-sync.test.mjs 9/9, the whole .github/scripts/*.test.mjs suite 282/282, changelog.mjs check 8 fragments, testing-layers.mjs --check 326 files / 13 workflows. Live --check right now exits 1 with ahead_by: 3 behind_by: 0 status: ahead and enforcement=active bypass_actors=[] current_user_can_bypass=never, which is the honest pre-window state rather than a failure of the check.

    Not run: npm run verify in full — nothing under core/, plugins/ or patches/ changed. No window was opened for this: the runbook documents the sequence, it does not execute one.

  3. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 23, 2026
  4. self-assigned this
    on Oct 23, 2026
  5. added 2 commits that reference this issue on Oct 23, 2026
    9b75555
    bbd2958
  6. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 23, 2026
  7. Birfy commented on Oct 23, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Release

    Shipped in the fork as bbd29584: #37 merged at 9b755557 (PASS by Reviewer2 at that tip), and the Test run on bbd29584 is success.

    What landed: docs/release-runbook.md (the six steps, one window per version, disable → fast-forward → restore in a finally → read-back, the write path and the admin requirement, the failure path, the stop rule, and the two measured facts verbatim), node .github/scripts/upstream-sync.mjs --check and --commits, their tests, the AGENTS.md link and one changelog fragment. Every command in the runbook is a node snippet: this box has no curl and no jq.

    It reaches upstream with the 0.2.6 fast-forward, in the same window as that release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:docsDocumentationarea:releaseRelease, tags, updatesdocumentationImprovements or additions to documentation

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions