From a23ba2f5dc575da4ece2ddb6a46261c594a5f968 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sun, 20 Sep 2026 14:32:46 +0100 Subject: [PATCH] docs(release): record the private Pro pair preflight contract The create-release prepare job now verifies that pulse-enterprise declares docs/release-source-pairs/.json and that it names the frozen public commit before any draft release object exists. Record that obligation in the deployment-installability contract and add the release-promotion policy proof that guards the step and its ordering ahead of create_release. Change-source: pulse-maintainer --- .../subsystems/deployment-installability.md | 8 ++++++++ .../release_promotion_policy_test.py | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 8d2d72b974..6d221cada2 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -1932,6 +1932,14 @@ artifact-selection behaviour. workflow-run details from GitHub and poll the exact returned run ID; it must never infer its child from the newest matching workflow/branch/timestamp, because version-scoped release concurrency and manual dispatches can overlap. + Before it creates the unpublished draft, `create-release.yml` must prove the + private payload can resolve its source: the `prepare` job must read + `docs/release-source-pairs/.json` from + `rcourtman/pulse-enterprise` and fail unless that file exists and declares + `pulse_sha` equal to the frozen public commit. A missing or mismatched + declaration must stop the run before any draft release object exists, so a + private build that cannot resolve its pair never orphans another draft. The + check is a fact check only; the release steward still selects the pair. Only after public release asset validation, staged install smoke, exact public Docker publication, exact Helm OCI publication, durable convergence dispatch, and the publicly readable activation-commit marker may the diff --git a/scripts/release_control/release_promotion_policy_test.py b/scripts/release_control/release_promotion_policy_test.py index 09df57eba1..0558f5b9a1 100644 --- a/scripts/release_control/release_promotion_policy_test.py +++ b/scripts/release_control/release_promotion_policy_test.py @@ -2895,6 +2895,26 @@ class CandidatePublicationBoundaryTest(unittest.TestCase): def setUp(self) -> None: self.jobs = yaml.load(read(".github/workflows/create-release.yml"), Loader=UniqueKeyLoader)["jobs"] + def test_private_pro_pair_is_verified_before_any_draft_exists(self) -> None: + prepare_steps = self.jobs["prepare"]["steps"] + step = next( + (candidate for candidate in prepare_steps + if candidate.get("name") == "Verify the private Pro source pair is declared"), + None, + ) + self.assertIsNotNone(step, "prepare must verify the private Pro source pair before dispatch") + condition = step["if"] + self.assertIn("startsWith(github.event.inputs.version, '6.')", condition) + self.assertIn("historical_asset_backfill_only != 'true'", condition) + self.assertIn("draft_only != 'true'", condition) + self.assertEqual(step["env"]["PRIVATE_PAIR_REPOSITORY"], "rcourtman/pulse-enterprise") + script = step["run"] + self.assertIn("docs/release-source-pairs/${EXPECTED_SOURCE_SHA}.json", script) + self.assertIn("pulse_sha", script) + self.assertIn("exit 1", script) + # The draft is created only after the prepare gate can refuse the run. + self.assertIn("prepare", self.jobs["create_release"]["needs"]) + def condition(self, job: str, results: dict[str, str], *, draft: bool = False, cancelled: bool = False) -> bool: # Execute the workflow's Boolean condition with explicit job outcomes. # This intentionally fails if the workflow adds an unsupported expression.