docs(release): record the private Pro pair preflight contract

The create-release prepare job now verifies that pulse-enterprise declares docs/release-source-pairs/<expected_source_sha>.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
This commit is contained in:
pulse-triage[bot] 2026-09-20 14:32:46 +01:00
parent f43a7a3e62
commit a23ba2f5dc
2 changed files with 28 additions and 0 deletions

View file

@ -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/<expected_source_sha>.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

View file

@ -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.