From feaa758d8653eb92cf84d8f04daedc09cf4e0286 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 2 Oct 2026 09:29:42 -0700 Subject: [PATCH 1/6] quest: plan the branch flip (dev to main, main to release) Co-Authored-By: Claude Opus 5.5 --- quest/m0/README.md | 1 + quest/m0/branch-flip.md | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 quest/m0/branch-flip.md diff --git a/quest/m0/README.md b/quest/m0/README.md index a07461f573..9f03fe4520 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -39,6 +39,7 @@ Published API or wire breaks still land on dev; each quest's Plan says so. ## Required +- [Branch flip](/quest/m0/branch-flip.md) - `dev` becomes the default `main` trunk and today's `main` becomes `release`, where publishing runs - [IETF FIN semantics](/quest/m0/ietf-fin-not-cancel.md) - a request stream FIN stops updates without cancelling, and REQUEST_UPDATE on a subscribe is parsed - [JS unknown bidi](/quest/m0/ietf-unknown-bidi.md) - an unknown moq-transport bidi stream type closes the session in JS, as in Rust - [moq-lite early streams](/quest/m0/lite-early-streams.md) - a lite uni stream that arrives before SETUP is held until SETUP lands, and a second SETUP is fatal diff --git a/quest/m0/branch-flip.md b/quest/m0/branch-flip.md new file mode 100644 index 0000000000..b40854bbaa --- /dev/null +++ b/quest/m0/branch-flip.md @@ -0,0 +1,71 @@ +# [M] Trunk is main, releases ship from release + +## Goal + +`dev` is renamed `main` and is the default branch: the trunk, where breaking +changes are allowed and every quest and PR lands. Today's `main` is renamed +`release`, a permanent branch where release-plz and all publishing run. Code +reaches `release` when a release is cut, by merging `main` in; urgent fixes in +between are cherry-picked. AGENTS, CONTRIBUTING, and the quest tree describe +that model, with no `dev` left. + +## Plan + +Facts (2026-10-02): + +- Publishing on push: `release-rs` (release-plz), `release-js`, `release-py`, + and `release-go` trigger on `main`; `release-kt-lib` and `release-swift-lib` + on `main` and `dev`. Everything else publishes from tags and is unaffected. +- `cache.yml` and `swift.yml` warm caches on `main`; `obs`, `platform`, and + `quest` push filters name `main` and `dev`. Nightly and interop crons run on + the default branch. +- Rulesets: "main" targets `~DEFAULT_BRANCH` (linear history, Check and Test + required), so it moves to trunk. "dev" targets `refs/heads/dev` (PRs only, + squash or merge commit) and does not follow a rename. +- Renaming `main` makes GitHub retarget its 35 open PRs to `release`; `dev`'s 11 + follow it to the new `main`. +- `sh/changed.sh` falls back to `origin/main`, which stays right for trunk. + `quest` hardcodes `main` as the milestone base, which becomes right too. +- 76 quest files say "on `dev`"; AGENTS.md (API, Development, quest sections), + CONTRIBUTING.md, `py/AGENTS.md`, and `quest/README.md` encode the split. +- `nix run github:moq-dev/moq`, `npx skills add moq-dev/moq`, and docs "edit" + links follow the default branch. + +Decided (2026-10-02): + +- ✅ Flip, over keeping `dev` with continuous sync or dropping a stable branch. +- ✅ A release is cut by merging `main` into `release` (merge commit), by + hand. Rejected: resetting `release` to `main`, and backport-only flow. +- ✅ Backports are manual cherry-pick PRs into `release`; automate later if + it hurts. +- ✅ After each publish, a workflow merges `release` back into `main`, so trunk + carries the published versions and CHANGELOGs. Rejected: running the + release-plz PR on `main` and publishing from `release`. +- ✅ Trunk is the default branch. User-facing installs in docs pin `release` + (`github:moq-dev/moq/release`); all branch-triggered publishing, including + kt and swift libs, runs only from `release`. Nightly and edit links stay on + trunk. +- ✅ A new `release` ruleset (no deletion or force push, Check and Test + required, PRs only, merge commits allowed); the `dev` ruleset is deleted. +- ✅ Open PRs that land on `release` through the rename are retargeted to the + new `main`, except any explicitly meant as backports. +- ✅ One mechanical wording sweep: "on `dev`" goes from quests (trunk allows + breaks), the rules describe trunk, `release`, and backports, and + `dev`-only process quests are deleted. +- ✅ One quest, m0, right after dev-sync lands. + +Sequence: + +1. [dev-sync](/quest/m1/dev-sync.md) (#4720) lands. +2. A PR on `dev`: the wording sweep, trunk stops publishing, cache and push + filters updated, the back-merge workflow, docs pin `release`. +3. A small PR on `main`: publish triggers and cache filters move to + `release`. +4. Admin, back to back, run by the agent only after the maintainer's + go-ahead in chat: rename `main` to `release`, rename `dev` to `main`, set + the default branch, create the `release` ruleset, delete the `dev` + ruleset, retarget open PRs. Dry-run first and show the PR list. +5. Verify: a no-op push to `main` publishes nothing; release-plz runs on + `release`; the back-merge opens on the first publish. + +Public API: none. Wire: none. Contributors see the new branch model. From 4c0d80fb764cd181313fbcb03163546cb7382a76 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 2 Oct 2026 09:31:01 -0700 Subject: [PATCH 2/6] quest: correct the branch-flip counts Co-Authored-By: Claude Opus 5.5 --- quest/m0/branch-flip.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/quest/m0/branch-flip.md b/quest/m0/branch-flip.md index b40854bbaa..85db17ed25 100644 --- a/quest/m0/branch-flip.md +++ b/quest/m0/branch-flip.md @@ -22,12 +22,13 @@ Facts (2026-10-02): - Rulesets: "main" targets `~DEFAULT_BRANCH` (linear history, Check and Test required), so it moves to trunk. "dev" targets `refs/heads/dev` (PRs only, squash or merge commit) and does not follow a rename. -- Renaming `main` makes GitHub retarget its 35 open PRs to `release`; `dev`'s 11 +- Renaming `main` makes GitHub retarget its 35 open PRs to `release`; `dev`'s 10 follow it to the new `main`. - `sh/changed.sh` falls back to `origin/main`, which stays right for trunk. `quest` hardcodes `main` as the milestone base, which becomes right too. -- 76 quest files say "on `dev`"; AGENTS.md (API, Development, quest sections), - CONTRIBUTING.md, `py/AGENTS.md`, and `quest/README.md` encode the split. +- 52 quest files on `main` (40 on `dev`) say "on `dev`"; AGENTS.md (API, + Development, quest sections), CONTRIBUTING.md, `py/AGENTS.md`, and + `quest/README.md` encode the split. - `nix run github:moq-dev/moq`, `npx skills add moq-dev/moq`, and docs "edit" links follow the default branch. From ad0fc4893ce7f286f3838997a126d6c8ec577cc3 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 2 Oct 2026 09:35:41 -0700 Subject: [PATCH 3/6] quest: keep cache writers on trunk in the branch flip Co-Authored-By: Claude Opus 5.5 --- quest/m0/README.md | 5 +++-- quest/m0/branch-flip.md | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/quest/m0/README.md b/quest/m0/README.md index 9f03fe4520..222dcc4449 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -17,8 +17,9 @@ done. moq.pro tracks this repository as a submodule rather than a release, so no release quest gates this milestone. The Pronto GPU integration lives in moq.pro. -Relay hardening: IETF interop leads the ranking, since only those quests -block Seattle. IETF stream types came from m1 in the 2026-09-30 audit +The branch flip ranks first: it is a short cutover that every later PR +targets. Relay hardening: IETF interop leads the rest, since only those +quests block Seattle. IETF stream types came from m1 in the 2026-09-30 audit because a session ended by legal input is exactly what Seattle would hit. The DoS hardening from an external review on 2026-09-29, verified against `main`, stays in m0 as security work. Its quests describe fixes, not exploits. diff --git a/quest/m0/branch-flip.md b/quest/m0/branch-flip.md index 85db17ed25..7901984b28 100644 --- a/quest/m0/branch-flip.md +++ b/quest/m0/branch-flip.md @@ -16,17 +16,18 @@ Facts (2026-10-02): - Publishing on push: `release-rs` (release-plz), `release-js`, `release-py`, and `release-go` trigger on `main`; `release-kt-lib` and `release-swift-lib` on `main` and `dev`. Everything else publishes from tags and is unaffected. -- `cache.yml` and `swift.yml` warm caches on `main`; `obs`, `platform`, and - `quest` push filters name `main` and `dev`. Nightly and interop crons run on - the default branch. +- `cache.yml` and `swift.yml` warm caches on `main` on both branches. PRs + read caches only from the default branch, so writers stay on trunk and + need no change. `obs`, `platform`, and `quest` push filters name `main` and + `dev`. Nightly and interop crons run on the default branch. - Rulesets: "main" targets `~DEFAULT_BRANCH` (linear history, Check and Test required), so it moves to trunk. "dev" targets `refs/heads/dev` (PRs only, squash or merge commit) and does not follow a rename. -- Renaming `main` makes GitHub retarget its 35 open PRs to `release`; `dev`'s 10 +- Renaming `main` makes GitHub retarget its ~35 open PRs to `release`; `dev`'s 10 follow it to the new `main`. - `sh/changed.sh` falls back to `origin/main`, which stays right for trunk. `quest` hardcodes `main` as the milestone base, which becomes right too. -- 52 quest files on `main` (40 on `dev`) say "on `dev`"; AGENTS.md (API, +- About 50 quest files on `main` (40 on `dev`) say "on `dev`"; AGENTS.md (API, Development, quest sections), CONTRIBUTING.md, `py/AGENTS.md`, and `quest/README.md` encode the split. - `nix run github:moq-dev/moq`, `npx skills add moq-dev/moq`, and docs "edit" @@ -58,10 +59,11 @@ Decided (2026-10-02): Sequence: 1. [dev-sync](/quest/m1/dev-sync.md) (#4720) lands. -2. A PR on `dev`: the wording sweep, trunk stops publishing, cache and push - filters updated, the back-merge workflow, docs pin `release`. -3. A small PR on `main`: publish triggers and cache filters move to - `release`. +2. A PR on `dev`: the wording sweep, trunk stops publishing, `obs`, + `platform`, and `quest` push filters name `main` and `release`, the + back-merge workflow, docs pin `release`. +3. A small PR on `main`: publish triggers move to `release`; cache writers + keep `main`, so they stop running there. 4. Admin, back to back, run by the agent only after the maintainer's go-ahead in chat: rename `main` to `release`, rename `dev` to `main`, set the default branch, create the `release` ruleset, delete the `dev` From 70fb543fba3c6ac04ede3699aa4d3e4277c1b26a Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 2 Oct 2026 09:39:45 -0700 Subject: [PATCH 4/6] quest: cover chained publishers, back-merge history, and release-plz PRs Co-Authored-By: Claude Opus 5.5 --- quest/m0/branch-flip.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/quest/m0/branch-flip.md b/quest/m0/branch-flip.md index 7901984b28..f0f7dbd973 100644 --- a/quest/m0/branch-flip.md +++ b/quest/m0/branch-flip.md @@ -16,13 +16,18 @@ Facts (2026-10-02): - Publishing on push: `release-rs` (release-plz), `release-js`, `release-py`, and `release-go` trigger on `main`; `release-kt-lib` and `release-swift-lib` on `main` and `dev`. Everything else publishes from tags and is unaffected. +- `workflow_run` chains (`release-go` after Release Go FFI, plus `py`, + `kt-lib`, `swift-lib`, `brew`, `winget`) run the default branch's + workflow file and check out its head unless the checkout names a ref, so + after the flip they would build trunk. - `cache.yml` and `swift.yml` warm caches on `main` on both branches. PRs read caches only from the default branch, so writers stay on trunk and need no change. `obs`, `platform`, and `quest` push filters name `main` and `dev`. Nightly and interop crons run on the default branch. - Rulesets: "main" targets `~DEFAULT_BRANCH` (linear history, Check and Test - required), so it moves to trunk. "dev" targets `refs/heads/dev` (PRs only, - squash or merge commit) and does not follow a rename. + required), so it moves to trunk and forbids a merge-commit back-merge. + "dev" targets `refs/heads/dev` (PRs only, squash or merge commit) and does + not follow a rename. - Renaming `main` makes GitHub retarget its ~35 open PRs to `release`; `dev`'s 10 follow it to the new `main`. - `sh/changed.sh` falls back to `origin/main`, which stays right for trunk. @@ -56,19 +61,29 @@ Decided (2026-10-02): `dev`-only process quests are deleted. - ✅ One quest, m0, right after dev-sync lands. +Open: + +- Trunk's linear-history rule rejects the back-merge's merge commit. + Recommended: squash-merge the back-merge PR; each cut's merge commit on + `release` advances the merge base, so the next cut stays clean. + Alternative: drop linear history from trunk's ruleset. + Sequence: 1. [dev-sync](/quest/m1/dev-sync.md) (#4720) lands. 2. A PR on `dev`: the wording sweep, trunk stops publishing, `obs`, - `platform`, and `quest` push filters name `main` and `release`, the + `platform`, and `quest` push filters name `main` and `release`, + `workflow_run` publishers check out `release` or the triggering tag, the back-merge workflow, docs pin `release`. 3. A small PR on `main`: publish triggers move to `release`; cache writers keep `main`, so they stop running there. 4. Admin, back to back, run by the agent only after the maintainer's go-ahead in chat: rename `main` to `release`, rename `dev` to `main`, set the default branch, create the `release` ruleset, delete the `dev` - ruleset, retarget open PRs. Dry-run first and show the PR list. + ruleset, retarget open PRs. Release-plz PRs (such as #4596) stay on + `release`. Dry-run first and show the PR list with each PR's class. 5. Verify: a no-op push to `main` publishes nothing; release-plz runs on - `release`; the back-merge opens on the first publish. + `release`; a chained publish builds `release` while trunk differs; the + first back-merge lands on `main`, not just opens. Public API: none. Wire: none. Contributors see the new branch model. From 8c3bcf1dc574ac96d027e81c1e90305904933009 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 2 Oct 2026 09:43:25 -0700 Subject: [PATCH 5/6] quest: name the unpinned workflow_run checkouts Co-Authored-By: Claude Opus 5.5 --- quest/m0/branch-flip.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/quest/m0/branch-flip.md b/quest/m0/branch-flip.md index f0f7dbd973..8b046e7efa 100644 --- a/quest/m0/branch-flip.md +++ b/quest/m0/branch-flip.md @@ -16,10 +16,11 @@ Facts (2026-10-02): - Publishing on push: `release-rs` (release-plz), `release-js`, `release-py`, and `release-go` trigger on `main`; `release-kt-lib` and `release-swift-lib` on `main` and `dev`. Everything else publishes from tags and is unaffected. -- `workflow_run` chains (`release-go` after Release Go FFI, plus `py`, - `kt-lib`, `swift-lib`, `brew`, `winget`) run the default branch's - workflow file and check out its head unless the checkout names a ref, so - after the flip they would build trunk. +- `workflow_run` chains run the default branch's workflow file, and a + checkout without a ref takes its head. `release-py`, `release-kt-lib`, and + the `release-swift-lib` build pin the triggering `head_sha`; `release-go`, + `release-brew`, and the other `release-swift-lib` checkouts do not, so after + the flip they would build trunk. - `cache.yml` and `swift.yml` warm caches on `main` on both branches. PRs read caches only from the default branch, so writers stay on trunk and need no change. `obs`, `platform`, and `quest` push filters name `main` and @@ -73,7 +74,7 @@ Sequence: 1. [dev-sync](/quest/m1/dev-sync.md) (#4720) lands. 2. A PR on `dev`: the wording sweep, trunk stops publishing, `obs`, `platform`, and `quest` push filters name `main` and `release`, - `workflow_run` publishers check out `release` or the triggering tag, the + unpinned `workflow_run` checkouts pin the triggering `head_sha`, the back-merge workflow, docs pin `release`. 3. A small PR on `main`: publish triggers move to `release`; cache writers keep `main`, so they stop running there. From bd23a658a6546f3ea49528770119f33ad45ddec8 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 2 Oct 2026 09:48:54 -0700 Subject: [PATCH 6/6] quest: recommend merge-commit back-merges over squash Co-Authored-By: Claude Opus 5.5 --- quest/m0/branch-flip.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/quest/m0/branch-flip.md b/quest/m0/branch-flip.md index 8b046e7efa..b687084433 100644 --- a/quest/m0/branch-flip.md +++ b/quest/m0/branch-flip.md @@ -65,9 +65,10 @@ Decided (2026-10-02): Open: - Trunk's linear-history rule rejects the back-merge's merge commit. - Recommended: squash-merge the back-merge PR; each cut's merge commit on - `release` advances the merge base, so the next cut stays clean. - Alternative: drop linear history from trunk's ruleset. + Recommended: drop linear history from trunk's ruleset, so the back-merge + lands as a merge commit and advances the merge base; ordinary PRs still + squash. Rejected: squash-merging the back-merge, which leaves the base at + the last cut, so a second publish before the next cut conflicts. Sequence: @@ -85,6 +86,6 @@ Sequence: `release`. Dry-run first and show the PR list with each PR's class. 5. Verify: a no-op push to `main` publishes nothing; release-plz runs on `release`; a chained publish builds `release` while trunk differs; the - first back-merge lands on `main`, not just opens. + back-merges of two publishes without a cut between both land on `main`. Public API: none. Wire: none. Contributors see the new branch model.