From 3578b0044740087e7589f19bca450245f610aced Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 21:15:35 +0100 Subject: [PATCH] Preserve the published 6.4.6 packet's release-line mapping Convergence used main's unmapped fallback and rejected the immutable 6.4.6-rc.1 source. Retain its exact-version release/v6.4 policy for artifact convergence and promotion without mapping future patches or accepting unrelated source. Change-source: pulse-maintainer --- docs/release-control/control_plane.json | 5 ++++ .../release-control/internal/CONTROL_PLANE.md | 3 +++ .../control_plane_audit_test.py | 8 +++--- .../validate_artifact_release_line_test.py | 26 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/release-control/control_plane.json b/docs/release-control/control_plane.json index ce5fb0a5a0..7e67825725 100644 --- a/docs/release-control/control_plane.json +++ b/docs/release-control/control_plane.json @@ -33,6 +33,11 @@ "prerelease_branch": "release/v6.4", "stable_branch": "release/v6.4" }, + { + "version_prefix": "6.4.6", + "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/internal/CONTROL_PLANE.md b/docs/release-control/internal/CONTROL_PLANE.md index 167bb7c07f..7cf867746d 100644 --- a/docs/release-control/internal/CONTROL_PLANE.md +++ b/docs/release-control/internal/CONTROL_PLANE.md @@ -36,6 +36,9 @@ model that active and future release profiles reuse. support after the active profile has moved on, that branch mapping must stay explicit in `control_plane.json` as a legacy release-line override rather than being inferred from the active profile or smuggled into one workflow. + The selected and published `6.4.6` packet remains bound to `release/v6.4` + for convergence and exact-RC promotion. Its complete-version override does + not map future `6.4.7` or similarly prefixed `6.4.60` releases to that line. 4. Profile changes must reuse the same control-plane machinery. Future releases should switch or add profiles rather than fork the guardrail system. diff --git a/scripts/release_control/control_plane_audit_test.py b/scripts/release_control/control_plane_audit_test.py index a8f37ad9ed..7d06e042d1 100644 --- a/scripts/release_control/control_plane_audit_test.py +++ b/scripts/release_control/control_plane_audit_test.py @@ -248,10 +248,11 @@ class ControlPlaneAuditTest(unittest.TestCase): 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", "6.4.5-beta.1", "v6.4.5-beta.2", "6.4.5-rc.1", - "6.4.5", "v6.4.5+build.1"): + "6.4.5", "v6.4.5+build.1", "6.4.6-beta.1", + "v6.4.6-rc.1", "6.4.6", "v6.4.6+build.1"): 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.6-beta.1", "6.4.50-beta.1", "6.4.40-beta.1", + for version in ("6.4.1", "6.4.2", "6.4.7-rc.1", "6.4.60-rc.1", "6.4.50-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") @@ -267,7 +268,8 @@ class ControlPlaneAuditTest(unittest.TestCase): ) self.assertIsNotNone(match) script = textwrap.dedent(match.group(1)) - for version in ("6.4.4-beta.1", "6.4.5-beta.1", "6.4.5-rc.1", "6.4.5"): + for version in ("6.4.4-beta.1", "6.4.5-beta.1", "6.4.5-rc.1", "6.4.5", + "6.4.6-rc.1", "6.4.6"): for branch in ("main", "release/v6.4"): with self.subTest(workflow=workflow, branch=branch, version=version), tempfile.TemporaryDirectory() as tmp: output = os.path.join(tmp, "output") diff --git a/scripts/release_control/validate_artifact_release_line_test.py b/scripts/release_control/validate_artifact_release_line_test.py index ef5858d12c..f152f20500 100644 --- a/scripts/release_control/validate_artifact_release_line_test.py +++ b/scripts/release_control/validate_artifact_release_line_test.py @@ -9,6 +9,32 @@ import validate_artifact_release_line as validator class ValidateArtifactReleaseLineTest(unittest.TestCase): + def test_published_646_packet_uses_its_selected_line_not_main(self) -> None: + # This is the real control-plane lookup used by convergence, not an + # injected branch answer. The frozen tag need not be an ancestor of main. + source = "1abc97cbc1fd7aefa052b4607a531af3e3b97eb6" + fetched = [] + refs = {"origin/release/v6.4": "line", "origin/main": "main"} + result = validator.validate_artifact_release_line( + tag="v6.4.6-rc.1", purpose="floating-tag promotion", + fetch_refs_fn=fetched.append, + tag_exists_fn=lambda tag: tag == "v6.4.6-rc.1", + tag_commit_fn=lambda tag: source, + ref_commit_fn=refs.__getitem__, + ref_is_ancestor_fn=lambda a, b: (a, b) == (source, "line"), + ) + self.assertEqual(fetched, ["release/v6.4"]) + self.assertEqual(result["required_branch"], "release/v6.4") + with self.assertRaisesRegex(ValueError, "not reachable from origin/release/v6.4"): + validator.validate_artifact_release_line( + tag="v6.4.6-rc.1", purpose="floating-tag promotion", + fetch_refs_fn=lambda branch: None, + tag_exists_fn=lambda tag: True, + tag_commit_fn=lambda tag: "unrelated", + ref_commit_fn=refs.__getitem__, + ref_is_ancestor_fn=lambda a, b: False, + ) + def validate( self, *,