Repository navigation
tests.yml and release.yml: a push to a release/v<major>.<minor> branch that holds this change runs the suite as a push to main does, so a patch's commit there gets its push run with no dispatch, a fast-forward of that branch to main's commit no longer cancels main's run on it, and the release header says how a fix is back-ported (#850) - #852
Conversation
…nch runs the suite as a push to main does, so each commit a release branch takes carries the push run a patch's tag needs, a fast-forward of that branch to main's commit no longer cancels main's run on it, and the release header says how a fix is back-ported (#850) Brett Heap ruled at 2026-10-17T19:52:26Z: "back-port to release/v1.1 as 1.1.1, open the issue". This is two of #850's work items. tests.yml runs on `push: branches: [main, 'release/v*']`. The glob matches release.yml's own `release/v<major>.<minor>`, since a branch filter's `*` matches no `/`. The header that lists the triggers says so. The concurrency group gains `github.ref` beside the commit. A group spans the repository, and a push run's `github.sha` is the commit the push left its branch at. Without the ref, a fast-forward of `release/v1.1` to main's commit lands in the group `tests-push-<sha>` that main's push run holds, and `cancel-in-progress: true` cancels that run if it is still going. A pull request keeps one group, since its ref is its own. release.yml changes in comments only. Step 1 of RELEASING counts a release branch's push run, and says a fast-forwarded commit already has main's. The dispatch stays for a commit no finished green run tested. RELEASING now carries the release-branch and back-port procedure. With comment lines removed, the file is byte-identical, and PyYAML and repo_shape.load_yaml load the same value from both. tests/test_workflow_concurrency.py holds the trigger and the matching branch names. Two more rows of earlier spellings must be refused: the group without the ref, and the ref in place of the commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis PR enables Sequence diagram for release branch back-port and test gatingsequenceDiagram
participant Main
participant ReleaseBranch as release/vX.Y
participant Tests as tests.yml
participant Release as release.yml
Main->>Tests: push commit
Tests-->>Main: tests run for commit
Main->>ReleaseBranch: fast-forward or cherry-pick back-port
ReleaseBranch->>Tests: push commit
Tests-->>ReleaseBranch: tests run for commit
Release->>Tests: verify finished green run for commit
Release->>ReleaseBranch: tag patch release
Flow diagram for selecting a tested release commitflowchart TD
Commit[Release commit]
PushRun{Finished green push run?}
Dispatch[workflow_dispatch tests.yml]
Tag[Tag patch release]
Commit --> PushRun
PushRun -->|yes| Tag
PushRun -->|no| Dispatch
Dispatch -->|green| Tag
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-17T20:38:53Z) RV852's review of #852 (head RV852: review of PR #852 at head 417951cVerdict: READY. There is no MUST item. The four SHOULDs make one short fix round, and the PR cannot land before the v1.1.0 tag anyway. The head did not move during the review. Main moved twice (to 6f64eea, then 190133d), and the head merges cleanly with both. CI at 20:37Z: SonarCloud passed, The brief's questions1. The concurrency group
2. Step 1's claim is true. The gate at release.yml:407-409 lists
It has no branch filter, so main's push run counts for a tag on
The last three rows lead to SHOULD-2. 3. release.yml changes only comments.
4. The procedure text (release.yml:60-72)
5. The tests
6. Interactions: actionlint 1.7.12 on all four workflows exits 0 with no output. The merges are clean (see Merges). Other checks
SHOULDSHOULD-1: a push runs the tests.yml that the pushed commit holds.
SHOULD-2: step 1 does not say which run decides (release.yml:33-34, and "that commit's tests.yml run" at 71-72).
SHOULD-3: SonarCloud python:S9073 is open at tests/test_workflow_concurrency.py:178 (new code, "Split this composite assertion into separate assertions"). Line 156's composite assert comes from 024ea55 and is not new. Fix: split line 178 into two asserts: SHOULD-4: mutant M3 survives (tests/test_workflow_concurrency.py:190 checks only
NITNIT-1: "names exactly" overstates the pattern (tests.yml:20-21, and the PR body). With the test's own matcher, NIT-2: the fast-forward route has no caveat (release.yml:66-70). It is stated without #850's open ruleset question. Add: "unless a NIT-3: two small layout and wording slips.
NIT-4: no test pins the #850 text (mutants M6 and M7 survive). Add NIT-5: the PR body miscounts. It says the header test pins "five" phrases; it has six asserts. TestsEvery run used a stub
Mutants
Merges
Lane: openreposhape-3 (openRepoShape-3) |
…olds, so a release branch runs the suite only once it holds this change, and after a fast-forward the branch's own push run decides; the trigger, the header and the pull_request line are pinned (RV852, #850) The fix round on RV852's review of #852 (issuecomment-6240653347). It changes release.yml in comments only: with comment lines removed it is byte-identical to origin/main's, and PyYAML and repo_shape.load_yaml load the same value from both. SHOULD-1. GitHub runs the version of a workflow that the pushed commit holds. tests.yml's header, release.yml's procedure and the test docstring now say a release branch runs the suite only once it holds this change: `release/v1.1` from its first fast-forward past it, or a cherry-pick of it; `release/v1.0` never has, and still needs a dispatch. SHOULD-2. Step 1 now says that once the branch holds #850's tests.yml, a fast-forward also starts the branch's own push run on that commit, and that newer run decides: tag once it has finished green. The procedure's last bullet names step 1 in place of "that commit's tests.yml run". SHOULD-3. The composite assert in `branch_filter_matches` is two asserts. SHOULD-4. `on["pull_request"] is None` is pinned, so a branch filter on pull_request, which would leave a cherry-pick back-port's pull request to `release/v<X.Y>` with no run, fails. NIT-1. "names exactly" is "takes each of them": `release/v*` also matches `release/v`, `release/v1` and `release/vnext`. NIT-2. The fast-forward route carries the caveat: it stands unless a `release/*` ruleset requires a pull request, which waits on Brett Heap's word in #850; then every back-port is the cherry-pick. NIT-3. A `#` separator line sits before the release-branch section, and the group sentence in tests.yml and the test's GROUP comment say a pull request's group is its ref and its number. NIT-4. test_the_header_says_how_to_release pins the #850 heading and the two new sentences in release.yml; a new test pins the two #850 blocks and the push rule in tests.yml. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-17T21:48:22Z) RV852's re-check of #852's fix round ( RV852 re-check: PR #852 at head 900ffe7Verdict: READY. F852's commit takes all four SHOULDs and all five NITs, and the new text is true. Every requested mutant is now killed. There is no MUST and no SHOULD left, only three optional NITs. The head did not move during the re-check. Main is now 9f235da, one landing past 190133d. The commit's identity is Brett Heap service@opensoft.dev, and it ends with two Co-Authored-By lines, Sonnet's then Opus's. CI at 900ffe7 is all green: 1. Each item, and whether the new text holds
2. release.yml is still comments only (against origin/main 190133d)
3. Mutants at 900ffe7Each run covered the two workflow test files plus
M10 and M11 are comment wording only. Pinning them is optional and not required. 4. Proposed titleUse this exactly:
5. Merges (main fetched fresh, now 9f235da)
6. TestsEvery run used the stub
NIT (optional; none blocks landing)
Lane: openreposhape-3 (openRepoShape-3) |
release/v<major>.<minor> branch runs the suite as a push to main does, so each commit a release branch takes carries the push run a patch's tag needs, a fast-forward of that branch to main's commit no longer cancels main's run on it, and the release header says how a fix is back-ported (#850)release/v<major>.<minor> branch that holds this change runs the suite as a push to main does, so a patch's commit there gets its push run with no dispatch, a fast-forward of that branch to main's commit no longer cancels main's run on it, and the release header says how a fix is back-ported (#850)
|
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 1 hour and 52 minutes by commenting |



Part of #850. This is a draft on the v1.1.1 milestone, and it lands only after the v1.1.0 tag.
The ruling
Brett Heap, at 2026-10-17T19:52:26Z, in lane openRepoShape-3's session:
This PR covers two of #850's work items: the
tests.ymltrigger and the procedure inrelease.yml's RELEASING section. Therelease/*ruleset,release/v1.1itself and thev1.2.0milestone stay outside it.What changed, and why
.github/workflows/tests.ymlruns on pushes to release branches:push: branches: [main, 'release/v*'].release.ymlreleases a commit only once apushorworkflow_dispatchrun oftests.ymlpassedtestson that very commit. Until now, every commit onrelease/v1.0needed a dispatch by hand (the v1.0.1 notes: "A push torelease/v1.0runs no CI by itself").tests.ymlits own commit holds (GitHub: "Each workflow run will use the version of the workflow that is present in the associated commit SHA or Git ref of the event"). Sorelease/v1.1, cut at v1.1.0, runs the suite from its first fast-forward past this change, or a cherry-pick of it.release/v1.0never has, and still needs a dispatch.release/v*takes each of them: everyrelease/v<major>.<minor>thatrelease.yml's build job accepts (for branch in main "release/v${VERSION%.*}"), and any other one-segment name that startsrelease/v, such asrelease/vnext. In a branch filter, GitHub's*"matches zero or more characters, but does not match the/character".The concurrency group gains
${{ github.ref }}beside the commit. See the next section for the reason..github/workflows/release.yml, comments only:release/v<X.Y>branch, which no push run tests". It now says:release/v<X.Y>branch started (once the branch holds Release branching: main stays the trunk; release/vX.Y branches carry tagged releases and their patches; v1.1.1 back-ported to release/v1.1 #850'stests.yml), counts when it finished green;tests.yml, the fast-forward also starts the branch's own push run on that commit, and that newer run decides: tag once it has finished green;release/v<X.Y>is cut at the tagv<X.Y>.0;release/*ruleset requires a pull request (that ruleset waits on Brett Heap's word, Release branching: main stays the trunk; release/vX.Y branches carry tagged releases and their patches; v1.1.1 back-ported to release/v1.1 #850), and then every back-port is the cherry-pick. Otherwise it is a PR torelease/v<X.Y>ofgit cherry-pick -x <squash>;tests.ymlits own commit holds, so a release branch runs it only once it holds Release branching: main stays the trunk; release/vX.Y branches carry tagged releases and their patches; v1.1.1 back-ported to release/v1.1 #850's change:release/v1.1from its first fast-forward past it, or a cherry-pick of it;release/v1.0never has, and still needs a dispatch;release/v<X.Y>once step 1 holds for that commit: its tests.yml runs have all finished, and the newest is green.tests/test_workflow_concurrency.pyholds the trigger and the new group (see Tests).README.md, docs/cli.md and docs/handbook.html state neither
tests.yml's triggers nor a release-branch procedure (git grepover the three finds notests.yml,release/vor back-port text), so none of them changes. AGENTS.md is untouched. No statement goes stale.docs/release-notes/v1.0.1.mdsays "A push torelease/v1.0runs no CI by itself", in v1.0.1's own "Before tagging" record, and that is still true: a push runs thetests.ymlits own commit holds, andrelease/v1.0's (8cda139) still sayspush: branches: [main]. The file is left as it is.Concurrency: yes, it could cancel, so the group changed
The finding. With the old group,
${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.sha }}, a push torelease/v1.1could cancel a run of main's, and main's could cancel one of the release branch's. Three facts lead there:tests-push-<sha>.github.shafor a push is the "Tip commit pushed to the ref" (GitHub's events docs).So a fast-forward of
release/v1.1to main's commit X makes a run intests-push-X, which is the group of main's push run on X. Withcancel-in-progress: true, it cancels main's run if that run is still going, so a back-port made right after a landing would hit this. That is #592's silent failure again: main's run endscancelled. The cut ofrelease/v1.1at the v1.1.0 tag also pushes a commit main has, though main's run on it has finished by then, since the tag waits for it.The other way round, main's push cancels a release branch's run on the same commit only if main takes a commit after the release branch took it. The model never does that, but the old group allowed it.
The change. The group is now
${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}-${{ github.event.pull_request.number || github.sha }}. The ref sits beside the commit, never in its place, as #592's old fallback was.refs/pull/<n>/merge, its own, so a newer push to a PR still cancels its older run.Once the branch holds this change, a fast-forwarded commit therefore gets two push runs, main's and the release branch's. The release gate counts both, because it filters on
head_sha, path and event, not on branch. Neither may still be running, and the newest finished one decides.The other workflows
.github/workflows/holds two files,tests.ymlandrelease.yml. No other workflow there has abranches: [main]push trigger.release.ymlisworkflow_dispatchonly, and it should not follow: "an upload is an act Brett Heap approves", and its tests pin that (test_a_dispatch_with_a_version_is_the_only_trigger).templates/assembly-root/.github/workflows/validate.ymlandtemplates/family-root/.github/workflows/validate.yml, run onpull_requestandworkflow_dispatchonly. They run in a scaffolded project and not here, so they should not follow either.This PR changes none of them.
Tests
The
teststhat read either workflow, fromgit grep -n -E 'tests\.yml|release\.yml' tests/:tests/test_workflow_concurrency.pyandtests/test_release_workflow.py;tests/test_install_sh.py::test_it_installs_the_git_filter_repo_ci_runs.The other hits name the files in docstrings and comments only.
tests/test_repo_hygiene.pyreads every tracked file, workflows included, andtests/test_shape_advisory.py::test_only_the_three_tools_read_the_advisoryreads every file under.github/. None of them pinnedbranches: [main]or tests.yml's trigger header.test_the_header_says_how_to_releasepins nine release.yml header phrases: the six it had on main, none of them in the lines this PR changes, and three that fix round 1 adds for #850's section (see below).New in
tests/test_workflow_concurrency.py:test_a_push_to_main_or_to_a_release_branch_runs_the_suite: the push trigger is exactly{"branches": ["main", "release/v*"]}, read withrepo_shape.load_yaml, and the triggers are push, pull_request and workflow_dispatch.test_which_branchs_push_runs_the_suite: the rowsmain,release/v1.1andrelease/v1.0run.l3-850-release-branchesanddocs/release-notes-v1.0.0do not. Matching follows GitHub's rule for*.test_a_run_is_grouped_by_its_event_its_ref_and_its_commit) requires the ref.EARLIER_GROUPSgains two spellings that must be refused:sha-without-the-ref, main's group until now, refused with the Release branching: main stays the trunk; release/vX.Y branches carry tagged releases and their patches; v1.1.1 back-ported to release/v1.1 #850 reason;ref-without-the-commit, the ref in place of the commit, refused with tests.yml: a push run on main is never cancelled by the next landing, so every landing gets its own Linux result #592's reason.At 417951c, every run was made under a stub
ghfirst on PATH that exits 97, withGH_TOKEN=bogusand an emptyGH_CONFIG_DIR. HereS=/workspace/projects/openRepoShape-worktrees/_lane3-scratch/W850-scratch, andPis/usr/bin/python3 -m pytest -q -p no:cacheprovider -o tmp_path_retention_policy=failed:release.ymlchanges in comments only. At 417951c,origin/main:.github/workflows/release.ymlwas compared withHEAD's (fix round 1 repeats this at its own head, below):Both hunks lie in the header, above
on:.Mutants
'release/v*', so the trigger isbranches: [main]. The result is3 failed, 10 passed:${{ github.ref }}. The result is1 failed, 12 passed:github.ref, so the push run of a release branch fast-forwarded to main's commit shares a group with main's push run on that commit and cancels it (Release branching: main stays the trunk; release/vX.Y branches carry tagged releases and their patches; v1.1.1 back-ported to release/v1.1 #850)".The file was restored after each mutant and checked by sha256.
Merges
git merge-tree --write-tree HEAD origin/mainatfc8672073ce231d508f922b99e0cd9ae5e90d088is clean, and givesbedbd3a9f84db5ebfb4f67b117bf49fd7946acfa, HEAD's own tree, since main is HEAD's parent.tests.yml,release.ymlortests/test_workflow_concurrency.py(gh pr list --state open --limit 100 --json number,headRefOid,files), so there is no other merge to run.The ruleset question, which is Brett Heap's
GitHub cannot fast-forward a branch through a pull request. A ruleset on
release/*that requires a pull request for every change therefore rules out the fast-forward back-port. The choice is between:git cherry-pick -x.That choice is Brett Heap's, and this PR makes no ruleset.
Fix round 1 (RV852)
RV852 reviewed the head 417951c (issuecomment-6240653347): READY, no MUST. One new commit,
900ffe70b6b340b52d00b9ee68b2830f546d6eee, takes its four SHOULDs and five NITs. It changestests.yml,release.ymland the two test files, andrelease.ymlstill changes in comments only.tests.ymlits own commit holdstests.yml's header block (lines 16-25) says a release branch runs the suite only once it holds this change:release/v1.1from its first fast-forward past it, or a cherry-pick of it;release/v1.0never has, and still needs a dispatch.release.ymlhas a procedure bullet that says the same (lines 77-80). The docstring oftest_which_branchs_push_runs_the_suitesays the rows test only the pattern (tests/test_workflow_concurrency.py:211-219). The v1.0.1 sentence above is corrected: it does not go stale.release.yml:28-40): once the branch holds #850'stests.yml, the fast-forward also starts the branch's own push run on that commit, and that newer run decides; tag once it has finished green. The procedure's last bullet now says "once step 1 holds for that commit: its tests.yml runs have all finished, and the newest is green" (lines 81-83), in place of "that commit's tests.yml run".tests/test_workflow_concurrency.py:183-184: the composite assert inbranch_filter_matchesis two asserts,assert not set(pattern) & set("?+[]!"), patternandassert "**" not in pattern, pattern.tests/test_workflow_concurrency.py:199:assert on["pull_request"] is None, on["pull_request"].tests.yml:20-21saysrelease/v*"takes each of them, and any other name of one segment that startsrelease/v", where it claimed to name exactly the release branches. The body's bullet on the pattern says the same now.release.yml:74-76: the fast-forward stands unless arelease/*ruleset requires a pull request (that ruleset waits on Brett Heap's word, #850); then every back-port is the cherry-pick. I wrote "waits on Brett Heap's word" and not "Brett Heap's word", since #850 leaves the ruleset to him and he has not ruled on it.release.yml:63: a#separator line before the new section.tests.yml:39: "A pull request's group is its ref and its number." The test'sGROUPcomment (tests/test_workflow_concurrency.py:51) says the same.tests/test_release_workflow.py:597-601:test_the_header_says_how_to_releasepins# RELEASE BRANCHES AND BACK-PORTS (#850), the push-rule bullet and step 1's "that newer run decides". A new test,test_the_header_says_why_a_release_branch_runs_it_and_when(tests/test_workflow_concurrency.py:225), pins the two #850 blocks and the push rule intests.yml, because M7 changestests.yml, which the release test does not read.Tests, at
900ffe70, under the same stubgh,GH_TOKEN=bogusand an emptyGH_CONFIG_DIR(S=/workspace/projects/openRepoShape-worktrees/_lane3-scratch/F852-scratch):release.ymlis still comments only, at900ffe70, againstorigin/main(190133d) and against the PR's merge base (fc86720):tests.ymlhas two non-comment changes against main,/on/push/branchesand/concurrency/group, and none against 417951c.Mutants, each run on the two workflow test files, the file restored from a copy and its sha256 checked after:
'release/v*'3 failed, 67 passedtest_a_push_to_main_or_to_a_release_branch_runs_the_suite,test_which_branchs_push_runs_the_suite[release-v1.1]and[release-v1.0]pull_request: branches: [main]1 failed, 69 passedtest_a_push_to_main_or_to_a_release_branch_runs_the_suite(assert {'branches': ['main']} is None)1 failed, 69 passedtest_the_header_says_how_to_release1 failed, 69 passedtest_the_header_says_why_a_release_branch_runs_it_and_whenLane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Run CI on supported release branches, isolate their test runs from main, and document the back-port release procedure.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests: