From 9d3b31a7b965b522ca363beccccc9f47bc05eb4e Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sun, 6 Sep 2026 16:48:17 +0100 Subject: [PATCH] fix(release): bind forward 6.4.4 checkpoint to release train The held regression candidate needs an honest forward beta above published 6.4.3-rc.1. Bind only 6.4.4 to release/v6.4 without capturing patch 40 or weakening candidate checks. Exercise the actual release and rehearsal branch-policy shell and retain historical rollback mapping. Change-source: pulse-maintainer (cherry picked from commit 64dba483d05ca8f30fb85e44a650b5a9d02748c9) --- docs/release-control/control_plane.json | 5 +++ .../v6/internal/RELEASE_PROMOTION_POLICY.md | 13 ++++++ scripts/release_control/control_plane.py | 13 +++++- .../control_plane_audit_test.py | 45 +++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/docs/release-control/control_plane.json b/docs/release-control/control_plane.json index ebef138a8..b9448b2d6 100644 --- a/docs/release-control/control_plane.json +++ b/docs/release-control/control_plane.json @@ -23,6 +23,11 @@ "prerelease_branch": "release/v6.4", "stable_branch": "release/v6.4" }, + { + "version_prefix": "6.4.4", + "prerelease_branch": "release/v6.4", + "stable_branch": "release/v6.4" + }, { "version_prefix": "6.5.", "prerelease_branch": "release/v6.5", diff --git a/docs/release-control/v6/internal/RELEASE_PROMOTION_POLICY.md b/docs/release-control/v6/internal/RELEASE_PROMOTION_POLICY.md index 689d1d3e3..e1aad0f8a 100644 --- a/docs/release-control/v6/internal/RELEASE_PROMOTION_POLICY.md +++ b/docs/release-control/v6/internal/RELEASE_PROMOTION_POLICY.md @@ -287,6 +287,19 @@ without the other lanes changing the candidate underneath it. moving `main` can no longer invalidate the compiler's exact-SHA binding between dispatch and compilation, which is what failed run 33579042375. Earlier `6.4.x` versions keep their historical `main` mapping. +8. The forward regression checkpoint `v6.4.4-beta.1` uses the same + `release/v6.4` line, with an explicit `6.4.4` mapping for beta, RC and + eventual stable. This is a maturity reset, not new feature scope: a + `6.4.3-beta.N` would sort below the published `v6.4.3-rc.1`. + `v6.4.4-beta.1` advances both that preview and stable `v6.4.1`; + `v6.4.1` remains the rollback target. Later qualification proceeds through + `v6.4.4-rc.N` and exact same-version stable promotion, including a fresh + 72-hour clean RC soak. Beta time does not count. Mapping is preparation, + not readiness or publication authority: failed candidate checks still + require repair or an evidence-based disposition under the existing gates. + Land the mapping and resolver contract on canonical main and backport it + to the release line before taking a fresh bound packet. Unlisted patches + and new product work retain their existing mapping and scope. ## Paid Pro Artifact Lineage diff --git a/scripts/release_control/control_plane.py b/scripts/release_control/control_plane.py index 1f3aa8188..74a2c2062 100644 --- a/scripts/release_control/control_plane.py +++ b/scripts/release_control/control_plane.py @@ -442,7 +442,18 @@ def legacy_release_line_for_version( reverse=True, ) for line in legacy_release_lines: - if normalized_version.startswith(line["version_prefix"]): + prefix = line["version_prefix"] + # A complete patch version binds only that version, not e.g. 6.4.40. + # Trailing-dot prefixes continue to bind the whole minor/major line. + if prefix.endswith("."): + matches = normalized_version.startswith(prefix) + else: + matches = ( + normalized_version == prefix + or normalized_version.startswith(prefix + "-") + or normalized_version.startswith(prefix + "+") + ) + if matches: return line return None diff --git a/scripts/release_control/control_plane_audit_test.py b/scripts/release_control/control_plane_audit_test.py index 094e5e1e3..4b4a2c8c3 100644 --- a/scripts/release_control/control_plane_audit_test.py +++ b/scripts/release_control/control_plane_audit_test.py @@ -1,3 +1,8 @@ +import os +from pathlib import Path +import re +import tempfile +import textwrap import shlex import subprocess import sys @@ -239,6 +244,46 @@ class ControlPlaneAuditTest(unittest.TestCase): "pulse/release-5.1.25", ) + def test_forward_patch_train_uses_actual_control_plane(self) -> None: + for version in ("6.4.4-beta.1", "v6.4.4-beta.2", "6.4.4-rc.1", + "6.4.4", "v6.4.4+build.1", "6.4.3-rc.1", "6.4.3"): + with self.subTest(version=version): + self.assertEqual(release_branch_for_version(version), "release/v6.4") + for version in ("6.4.1", "6.4.2", "6.4.5-beta.1", "6.4.40-beta.1", + "6.4.30", "6.3.20", "6.6.0-beta.1"): + with self.subTest(version=version): + self.assertEqual(release_branch_for_version(version), "main") + self.assertEqual(release_branch_for_version("6.5.10-beta.1"), "release/v6.5") + + def test_forward_patch_workflow_branch_contract(self) -> None: + # Execute the real branch-policy shell only, never dispatch a workflow. + for workflow in ("create-release.yml", "release-dry-run.yml"): + content = (REPO_ROOT / ".github/workflows" / workflow).read_text() + match = re.search( + r"(?ms)^ - name: Resolve required release branch\n" + r".*?^ run: \|\n((?: [^\n]*\n|\n)+)", content + ) + self.assertIsNotNone(match) + script = textwrap.dedent(match.group(1)) + for branch in ("main", "release/v6.4"): + with self.subTest(workflow=workflow, branch=branch), tempfile.TemporaryDirectory() as tmp: + output = os.path.join(tmp, "output") + result = subprocess.run( + ["bash", "-euo", "pipefail", "-c", script], + cwd=REPO_ROOT, capture_output=True, text=True, + env={**os.environ, "GITHUB_OUTPUT": output, + "VERSION_INPUT": "6.4.4-beta.1", + "WORKFLOW_OUTPUT_1": "6.4.4-beta.1", + "WORKFLOW_OUTPUT_2": branch}, + ) + rejects = workflow == "create-release.yml" and branch == "main" + self.assertEqual(result.returncode, 1 if rejects else 0, result.stderr) + if rejects: + self.assertIn("must run from release/v6.4", result.stdout) + else: + self.assertRegex(Path(output).read_text(), + r"^required_branch<<([^\n]+)\nrelease/v6.4\n\1\n$") + def test_audit_flags_stale_active_target(self) -> None: report = audit_control_plane_payload( VALID_PAYLOAD,