mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
Reopen v6 release control as pre-RC hold
This commit is contained in:
parent
9ba7915c7c
commit
7e0f84a40f
11 changed files with 143 additions and 31 deletions
|
|
@ -6,7 +6,7 @@
|
|||
"control_plane_doc": "docs/release-control/internal/CONTROL_PLANE.md",
|
||||
"control_plane_schema": "docs/release-control/control_plane.schema.json",
|
||||
"active_profile_id": "v6",
|
||||
"active_target_id": "v6-ga-promotion",
|
||||
"active_target_id": "v6-rc-cut",
|
||||
"profiles": [
|
||||
{
|
||||
"id": "v6",
|
||||
|
|
@ -30,16 +30,16 @@
|
|||
"id": "v6-rc-cut",
|
||||
"profile_id": "v6",
|
||||
"kind": "release",
|
||||
"status": "completed",
|
||||
"summary": "Drive Pulse v6 to a governed RC cut by clearing prerelease blockers, proving migration safety, and exercising the pre-RC high-risk gates.",
|
||||
"status": "active",
|
||||
"summary": "Hold Pulse v6 on the governed prerelease-cut target until RC publication is explicitly judged ready, while continuing to clear prerelease blockers, prove migration safety, and exercise the pre-RC high-risk gates.",
|
||||
"completion_rule": "rc_ready"
|
||||
},
|
||||
{
|
||||
"id": "v6-rc-stabilization",
|
||||
"profile_id": "v6",
|
||||
"kind": "stabilization",
|
||||
"status": "completed",
|
||||
"summary": "Keep Pulse v6 simple, accurate, and cleanly readable around the proven monitoring-and-alerting RC floor, using resource-change intelligence only where it materially improves investigation or safe AI flows while broader control-plane expansion stays out of the default target until it is proven.",
|
||||
"status": "planned",
|
||||
"summary": "Once a real governed RC exists, keep Pulse v6 simple, accurate, and cleanly readable around that proven monitoring-first floor while broader control-plane expansion stays out of the default target until it is proven.",
|
||||
"completion_rule": "manual",
|
||||
"proof_scope": "none"
|
||||
},
|
||||
|
|
@ -47,8 +47,8 @@
|
|||
"id": "v6-ga-promotion",
|
||||
"profile_id": "v6",
|
||||
"kind": "release",
|
||||
"status": "active",
|
||||
"summary": "Promote Pulse v6 from a validated RC to governed GA with exercised promotion proof, rollback clarity, and only GA-blocking decisions remaining.",
|
||||
"status": "planned",
|
||||
"summary": "Promote Pulse v6 from a validated, actually shipped RC to governed GA with exercised promotion proof, rollback clarity, and only GA-blocking decisions remaining.",
|
||||
"completion_rule": "release_ready"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -232,18 +232,18 @@ user language should update the control plane.
|
|||
## Current State
|
||||
|
||||
1. v6 is the current active release profile.
|
||||
2. `v6-ga-promotion` is the current active engineering target.
|
||||
2. `v6-rc-cut` is the current active engineering target.
|
||||
It is a release target, so default slice selection should stay centered on
|
||||
exercised prerelease lineage, release-pipeline proof, rollback metadata,
|
||||
and the remaining GA-blocking documentation inputs until `release_ready`
|
||||
is actually met.
|
||||
prerelease blockers, RC-publication judgment, and the remaining proof needed
|
||||
before a real governed RC should actually be cut.
|
||||
3. `v6-product-lane-expansion` remains planned and is still blocked on the
|
||||
broader surfaced product case being proven.
|
||||
Its candidate-lane surface remains available in `available_candidate_lane_queue`
|
||||
plus the linked `candidate_lanes` and `coverage_gaps`.
|
||||
4. `v6-rc-stabilization` is complete and remains the predecessor
|
||||
stabilization target for the current v6 line.
|
||||
5. `v6-rc-cut` is complete and remains the predecessor release-cut target.
|
||||
4. `v6-ga-promotion` remains planned and must stay dormant until a real
|
||||
governed RC has actually shipped and been validated.
|
||||
5. `v6-rc-stabilization` remains planned and should only become active once
|
||||
the first governed RC exists.
|
||||
6. Its files remain under `docs/release-control/v6/`.
|
||||
7. The existing v6 control surfaces are still live, but they now sit underneath
|
||||
an evergreen Pulse control plane rather than pretending to be the whole
|
||||
|
|
|
|||
|
|
@ -13,10 +13,13 @@ Use this as the final gate before cutting a Pulse v6 pre-release.
|
|||
## Current Status
|
||||
- Automated command-driven checks completed on 2026-03-06 are marked `[x]` below.
|
||||
- `status.json` is no longer overclaiming threshold-unmet gates as passed.
|
||||
- There are no remaining `rc-ready` high-risk blockers; `rc_ready` now derives
|
||||
true in `status.json`.
|
||||
- The active target is back on `v6-rc-stabilization`, so GA rehearsal remains
|
||||
intentionally out of scope until promotion is resumed explicitly.
|
||||
- There are no remaining `rc-ready` high-risk release-gate blockers, but
|
||||
`rc_ready` is intentionally held false in `status.json` by the open decision
|
||||
`rc-publication-judgment` until the current candidate is explicitly judged
|
||||
ready for a real governed RC.
|
||||
- The active target is back on `v6-rc-cut`, so GA rehearsal remains
|
||||
intentionally out of scope until a real RC has actually shipped and
|
||||
promotion is resumed explicitly.
|
||||
- The remaining release-ready blocker is `rc-to-ga-promotion-readiness`, which
|
||||
stays blocked until a later stable `6.0.0` candidate completes a matching
|
||||
`Release Dry Run` rehearsal with the canonical promotion artifact envelope:
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ Pulse v6 is ready when these outcomes land together:
|
|||
customer exposure.
|
||||
|
||||
Pulse v6 is still a bridge release toward that unified operations layer, while
|
||||
the current active target remains governed GA promotion rather than broad new
|
||||
platform execution.
|
||||
the current active target remains governed prerelease-cut readiness with an
|
||||
explicit RC-publication hold rather than broad new platform execution.
|
||||
For this profile, "bridge release" means the product should stop reading as a
|
||||
monitoring tool growing sideways and instead make the mixed-estate operator
|
||||
surface legible through canonical resources, investigation context, governed
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
# Prerelease-to-GA Promotion Readiness Blocked Record
|
||||
|
||||
- Date: `2026-04-04`
|
||||
- Gate: `rc-to-ga-promotion-readiness`
|
||||
- Result: `blocked`
|
||||
|
||||
## Blocking Facts
|
||||
|
||||
1. No Pulse v6 prerelease has shipped yet.
|
||||
2. The repository contains accidental prerelease git tag history (`v6.0.0-rc.1`),
|
||||
but those tags were never published and do not count as shipped prerelease lineage.
|
||||
3. The selected remote ref `origin/pulse/v6-release` is still behind the current
|
||||
local governed branch state, so `Release Dry Run` would exercise stale remote
|
||||
control-plane metadata instead of the intended candidate.
|
||||
4. The governed release profile in `docs/release-control/control_plane.json`
|
||||
currently declares both `prerelease_branch` and `stable_branch` as
|
||||
`pulse/v6-release`.
|
||||
5. The active control-plane target is still `v6-rc-cut`, not
|
||||
`v6-ga-promotion`.
|
||||
6. The active local `pulse/v6-release` branch currently reports `VERSION=6.0.0-rc.1`, so the
|
||||
working line is still prerelease and there is not yet a governed local stable
|
||||
`6.0.0` candidate.
|
||||
7. There is still no governed `Prerelease-to-GA Rehearsal Record` proving a successful
|
||||
non-publish `Release Dry Run` for the eventual stable `6.0.0` candidate.
|
||||
8. `docs/releases/RELEASE_NOTES_v6.md` and
|
||||
`docs/release-control/v6/internal/V5_MAINTENANCE_SUPPORT_POLICY.md` still leave the
|
||||
GA announcement dates as placeholders because no real prerelease lineage or GA-ready
|
||||
rehearsal has locked them yet:
|
||||
- `v6` GA date placeholder: `[v6-ga-date]`
|
||||
- `v5` end-of-support placeholder: `[v5-eos-date]`
|
||||
9. There is still no governed `Release Dry Run` artifact or rehearsal record
|
||||
exercising stable inputs for:
|
||||
- `version=6.0.0`
|
||||
- no governed `promoted_from_tag` exists yet because no prerelease has shipped
|
||||
- the artifact-owned candidate stable tag for that rehearsal
|
||||
- the artifact-owned promotion channel for that rehearsal
|
||||
- the artifact-owned promoted prerelease tag for that rehearsal
|
||||
- the artifact-owned rollback target for that stable candidate
|
||||
- no exact `ga_date` is locked yet because the GA notice is still pending
|
||||
- no exact `v5_eos_date` is locked yet because the GA notice is still pending
|
||||
- an explicit `rollback_version`
|
||||
- the exact derived rollback command that artifact will publish
|
||||
|
||||
## Why The Gate Cannot Be Cleared Yet
|
||||
|
||||
The blocker is no longer missing governance text. The remaining problem is that
|
||||
the control plane still holds v6 on the pre-GA prerelease line, the working
|
||||
version is still prerelease (`6.0.0-rc.1`), and there is still no exercised
|
||||
`Release Dry Run` record proving the eventual stable `6.0.0`
|
||||
candidate is ready for GA-style promotion. Until that rehearsal exists, stable
|
||||
users would still be the first real cohort for the final promotion path.
|
||||
|
||||
## Required Unblock Steps
|
||||
|
||||
1. Promote the active target from `v6-rc-cut` to
|
||||
`v6-ga-promotion` only when that change is actually intended.
|
||||
2. Push the governed `pulse/v6-release` branch state that is intended to become the
|
||||
stable `6.0.0` candidate, including the eventual `VERSION=6.0.0`
|
||||
change and release-control records, to `origin/pulse/v6-release`.
|
||||
3. Ship the first real prerelease through the governed prerelease release path and record
|
||||
its exact published prerelease tag plus rollback target and exact derived
|
||||
rollback command.
|
||||
4. Run `Release Dry Run` from `pulse/v6-release` using that published prerelease as
|
||||
`promoted_from_tag` with:
|
||||
- `version=6.0.0`
|
||||
- an artifact-owned candidate stable tag matching that rehearsal
|
||||
- an artifact-owned promotion channel matching that rehearsal
|
||||
- an artifact-owned promoted prerelease tag matching that rehearsal
|
||||
- an artifact-owned rollback target for the stable candidate
|
||||
- the exact planned GA and v5 end-of-support dates for the publish notice
|
||||
- an explicit `ga_date` chosen for that rehearsal
|
||||
- an explicit stable `rollback_version`
|
||||
- the exact derived rollback command that artifact will publish
|
||||
- an explicit `v5_eos_date` chosen for that rehearsal
|
||||
5. Capture the `rc-to-ga-rehearsal-summary` artifact and run URL.
|
||||
6. Materialize the final rehearsal record from that artifact without
|
||||
hand-repairing any missing candidate tag, promoted prerelease tag, rollback
|
||||
target, rollback command, or GA/EOS metadata.
|
||||
7. Change the gate from `blocked` only if the rehearsal passes and the rollout
|
||||
inputs remain explicit.
|
||||
|
|
@ -2698,7 +2698,7 @@
|
|||
"status": "partial",
|
||||
"completion": {
|
||||
"state": "bounded-residual",
|
||||
"summary": "First-session UX reached the RC floor with the runtime wizard reduced to welcome and security, direct handoff into Infrastructure Operations install, and setup completion retained only as a separate preview surface; broader polish and parity work remain intentionally outside the current stabilization target.",
|
||||
"summary": "First-session UX reached the RC floor with the runtime wizard reduced to welcome and security, direct handoff into Infrastructure Operations install, and setup completion retained only as a separate preview surface; broader polish and parity work remain intentionally outside the current prerelease target.",
|
||||
"tracking": [
|
||||
{
|
||||
"kind": "lane-followup",
|
||||
|
|
@ -3624,6 +3624,12 @@
|
|||
"kind": "file",
|
||||
"evidence_tier": "local-rehearsal"
|
||||
},
|
||||
{
|
||||
"repo": "pulse",
|
||||
"path": "docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-04-04.md",
|
||||
"kind": "file",
|
||||
"evidence_tier": "local-rehearsal"
|
||||
},
|
||||
{
|
||||
"repo": "pulse",
|
||||
"path": "docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-rehearsal-blocked-2026-03-26.md",
|
||||
|
|
@ -4216,7 +4222,23 @@
|
|||
}
|
||||
],
|
||||
"work_claims": [],
|
||||
"open_decisions": [],
|
||||
"open_decisions": [
|
||||
{
|
||||
"id": "rc-publication-judgment",
|
||||
"summary": "Pulse v6 prerelease publication remains an explicit product judgment: even if the machine-derived RC floor looks close, `rc_ready` stays blocked until the current candidate is explicitly judged ready for a real governed RC and not inferred from accidental `6.0.0-rc.1` lineage or checklist completion.",
|
||||
"owner": "project-owner",
|
||||
"blocking_level": "rc-ready",
|
||||
"status": "open",
|
||||
"opened_at": "2026-04-04",
|
||||
"lane_ids": [
|
||||
"L1",
|
||||
"L8",
|
||||
"L9",
|
||||
"L12"
|
||||
],
|
||||
"subsystem_ids": []
|
||||
}
|
||||
],
|
||||
"source_of_truth_file": "docs/release-control/v6/internal/SOURCE_OF_TRUTH.md",
|
||||
"resolved_decisions": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ func TestReleaseControlPlaneFilesExist(t *testing.T) {
|
|||
if !ok || activeTargetID == "" {
|
||||
t.Fatalf("%s missing active_target_id", jsonRel)
|
||||
}
|
||||
if activeTargetID != "v6-rc-stabilization" && activeTargetID != "v6-ga-promotion" && activeTargetID != "v6-product-lane-expansion" {
|
||||
if activeTargetID != "v6-rc-cut" && activeTargetID != "v6-rc-stabilization" && activeTargetID != "v6-ga-promotion" && activeTargetID != "v6-product-lane-expansion" {
|
||||
t.Fatalf("%s has unexpected active_target_id %q", jsonRel, activeTargetID)
|
||||
}
|
||||
|
||||
|
|
@ -557,8 +557,8 @@ func TestReleaseControlPlaneFilesExist(t *testing.T) {
|
|||
if status, _ := targetsByID[activeTargetID]["status"].(string); status != "active" {
|
||||
t.Fatalf("%s active_target_id %q is not marked active", jsonRel, activeTargetID)
|
||||
}
|
||||
if status, _ := targetsByID["v6-rc-cut"]["status"].(string); status != "completed" {
|
||||
t.Fatalf("%s v6-rc-cut must remain completed", jsonRel)
|
||||
if status, _ := targetsByID["v6-rc-cut"]["status"].(string); status != "active" && status != "completed" {
|
||||
t.Fatalf("%s v6-rc-cut must remain either active or completed", jsonRel)
|
||||
}
|
||||
if rule, _ := targetsByID["v6-rc-stabilization"]["completion_rule"].(string); rule != "manual" {
|
||||
t.Fatalf("%s v6-rc-stabilization must keep completion_rule=manual", jsonRel)
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ def build_blocked_record(*, record_date: str) -> str:
|
|||
if version_is_prerelease:
|
||||
why_lines = [
|
||||
"The blocker is no longer missing governance text. The remaining problem is that",
|
||||
"the control plane still treats v6 as the prerelease-stabilization line, the working",
|
||||
"the control plane still holds v6 on the pre-GA prerelease line, the working",
|
||||
f"version is still prerelease (`{version}`), and there is still no exercised",
|
||||
f"`Release Dry Run` record proving the eventual stable `{stable_version}`",
|
||||
"candidate is ready for GA-style promotion. Until that rehearsal exists, stable",
|
||||
|
|
@ -273,7 +273,7 @@ def build_blocked_record(*, record_date: str) -> str:
|
|||
else:
|
||||
why_lines = [
|
||||
"The blocker is no longer missing governance text. The remaining problem is that",
|
||||
"the control plane still treats v6 as the prerelease-stabilization line, and there is",
|
||||
"the control plane still holds v6 on the pre-GA prerelease line, and there is",
|
||||
f"still no exercised `Release Dry Run` record proving the exact `{version}`",
|
||||
"candidate is ready for GA-style promotion. Until that rehearsal exists, stable",
|
||||
"users would still be the first real cohort for the final promotion path.",
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn(promotion_metadata_envelope(), normalize_ws(runbook))
|
||||
|
||||
def test_blocked_record_tracks_current_target_and_candidate_version(self) -> None:
|
||||
blocked = read("docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-03-28.md")
|
||||
blocked = read("docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-04-04.md")
|
||||
self.assertIn("origin/pulse/v6-release", blocked)
|
||||
self.assertIn("VERSION=6.0.0-rc.1", blocked)
|
||||
self.assertIn("artifact-owned candidate stable tag", blocked)
|
||||
|
|
@ -248,10 +248,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn("artifact-owned rollback target", blocked)
|
||||
self.assertIn("Materialize the final rehearsal record from that artifact without", blocked)
|
||||
self.assertIn("hand-repairing any missing candidate tag, promoted prerelease tag, rollback", blocked)
|
||||
self.assertIn("The active control-plane target is `v6-ga-promotion`", blocked)
|
||||
self.assertIn("The active control-plane target is still `v6-rc-cut`, not", blocked)
|
||||
matrix = read("docs/release-control/v6/internal/HIGH_RISK_RELEASE_VERIFICATION_MATRIX.md")
|
||||
self.assertIn(promotion_metadata_envelope(), normalize_ws(matrix))
|
||||
expected = blocked_record.build_blocked_record(record_date="2026-03-28")
|
||||
expected = blocked_record.build_blocked_record(record_date="2026-04-04")
|
||||
self.assertEqual(blocked, expected)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from datetime import datetime, timezone
|
||||
import os
|
||||
import tempfile
|
||||
|
|
@ -1354,9 +1355,15 @@ class StatusAuditTest(unittest.TestCase):
|
|||
],
|
||||
)
|
||||
|
||||
control_plane = copy.deepcopy(status_audit.DEFAULT_CONTROL_PLANE)
|
||||
control_plane["targets_by_id"]["v6-rc-cut"]["status"] = "completed"
|
||||
|
||||
with mock.patch.dict(os.environ, {"PULSE_REPO_ROOT_PULSE": str(pulse)}, clear=False), mock.patch(
|
||||
"status_audit.load_subsystem_rules",
|
||||
return_value=[],
|
||||
), mock.patch(
|
||||
"status_audit.DEFAULT_CONTROL_PLANE",
|
||||
control_plane,
|
||||
):
|
||||
report = audit_status_payload(payload)
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class SubsystemLookupTest(unittest.TestCase):
|
|||
self.assertEqual(result["status_audit_errors"], [])
|
||||
self.assertIn(
|
||||
result["control_plane"]["active_target"]["id"],
|
||||
{"v6-rc-stabilization", "v6-ga-promotion", "v6-product-lane-expansion"},
|
||||
{"v6-rc-cut", "v6-rc-stabilization", "v6-ga-promotion", "v6-product-lane-expansion"},
|
||||
)
|
||||
self.assertEqual(result["scope"]["control_plane_repo"], "pulse")
|
||||
self.assertEqual(result["status_summary"]["lane_count"], 16)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue