Promote v6 control plane to GA readiness

This commit is contained in:
rcourtman
2026-03-28 22:13:22 +00:00
parent fb3b72f6e8
commit 95cba22a96
6 changed files with 136 additions and 16 deletions
+3 -3
View File
@@ -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-rc-stabilization",
"active_target_id": "v6-ga-promotion",
"profiles": [
{
"id": "v6",
@@ -38,7 +38,7 @@
"id": "v6-rc-stabilization",
"profile_id": "v6",
"kind": "stabilization",
"status": "active",
"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.",
"completion_rule": "manual",
"proof_scope": "none"
@@ -47,7 +47,7 @@
"id": "v6-ga-promotion",
"profile_id": "v6",
"kind": "release",
"status": "planned",
"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.",
"completion_rule": "release_ready"
},
@@ -222,24 +222,24 @@ user language should update the control plane.
## Current State
1. v6 is the current active release profile.
2. `v6-rc-stabilization` is the current active engineering target.
It is an intentionally human-held stabilization target with `proof_scope:
none`, and default slice selection should stay centered on the monitoring
floor unless the user explicitly overrides it or the governed target
changes again.
2. `v6-ga-promotion` 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.
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-ga-promotion` remains planned and is still blocked on exercised
promotion proof when that target is resumed.
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.
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
long-term system.
8. Until the explicit post-GA branch cutover happens, both prerelease and
stable v6 promotions resolve to `pulse/v6` via `control_plane.json`.
stable v6 promotions resolve to `pulse/v6-release` via `control_plane.json`.
9. `AGENT_VALUES.md` is the evergreen values-only entry point; prompts should
stay close to that layer and delegate detailed behavior to the governed
control-plane and profile-specific files.
@@ -0,0 +1,77 @@
# Prerelease-to-GA Promotion Readiness Blocked Record
- Date: `2026-03-28`
- 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 `v6-ga-promotion`, so stable or GA
promotion is now the governed objective for Pulse v6.
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 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. 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`.
2. 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.
3. 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
4. Capture the `rc-to-ga-rehearsal-summary` artifact and run URL.
5. 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.
6. Change the gate from `blocked` only if the rehearsal passes and the rollout
inputs remain explicit.
@@ -616,6 +616,11 @@
"path": "docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-03-26.md",
"kind": "file"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-03-28.md",
"kind": "file"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-rehearsal-blocked-2026-03-26.md",
@@ -2945,6 +2950,12 @@
"kind": "file",
"evidence_tier": "local-rehearsal"
},
{
"repo": "pulse",
"path": "docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-03-28.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",
@@ -238,7 +238,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-26.md")
blocked = read("docs/release-control/v6/internal/records/rc-to-ga-promotion-readiness-blocked-2026-03-28.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)
@@ -247,9 +247,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)
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-26")
expected = blocked_record.build_blocked_record(record_date="2026-03-28")
self.assertEqual(blocked, expected)
+34 -3
View File
@@ -153,6 +153,21 @@ def work_claim(
}
def non_completed_target_id(*preferred_ids: str) -> str:
candidate_ids = preferred_ids or ("v6-ga-promotion", "v6-product-lane-expansion")
for target_id in candidate_ids:
target = status_audit.DEFAULT_CONTROL_PLANE["targets_by_id"].get(target_id)
if target and str(target.get("status", "")).strip() != "completed":
return target_id
for target_id, target in status_audit.DEFAULT_CONTROL_PLANE["targets_by_id"].items():
if (
str(target.get("profile_id", "")).strip() == status_audit.ACTIVE_PROFILE_ID
and str(target.get("status", "")).strip() != "completed"
):
return target_id
raise AssertionError("expected at least one non-completed control-plane target for test fixtures")
def base_payload(
*,
lane_status: str = "target-met",
@@ -1174,6 +1189,21 @@ class StatusAuditTest(unittest.TestCase):
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.active_target_blocking_levels",
return_value=["repo-ready", "rc-ready", "release-ready"],
), mock.patch.object(
status_audit,
"ACTIVE_TARGET",
{
**status_audit.ACTIVE_TARGET,
"id": non_completed_target_id("v6-product-lane-expansion", "v6-ga-promotion"),
"kind": "feature",
"summary": "Exercise work-claim expiry without coupling the unit test to the live release target.",
"completion_rule": "manual",
"proof_scope": "none",
"blocking_levels": ["repo-ready", "rc-ready", "release-ready"],
},
):
report = audit_status_payload(
payload,
@@ -1194,13 +1224,14 @@ class StatusAuditTest(unittest.TestCase):
write_file(pulse, "docs/lane-proof.md")
write_file(pulse, "docs/proof_test.go")
write_file(pulse, "docs/hybrid_test.go")
target_id = non_completed_target_id("v6-ga-promotion", "v6-product-lane-expansion")
payload = base_payload(
work_claims=[
work_claim(
claim_id="claim-lane-broad",
agent_id="agent-1",
target_id="v6-rc-stabilization",
target_id=target_id,
work_kind="lane",
work_id="L1",
claimed_at="2026-03-13T09:00:00Z",
@@ -1210,7 +1241,7 @@ class StatusAuditTest(unittest.TestCase):
work_claim(
claim_id="claim-gate-narrow",
agent_id="agent-2",
target_id="v6-rc-stabilization",
target_id=target_id,
work_kind="release-gate",
work_id="g1",
claimed_at="2026-03-13T09:20:00Z",
@@ -1220,7 +1251,7 @@ class StatusAuditTest(unittest.TestCase):
work_claim(
claim_id="claim-gate-duplicate",
agent_id="agent-3",
target_id="v6-rc-stabilization",
target_id=target_id,
work_kind="release-gate",
work_id="g1",
claimed_at="2026-03-13T09:40:00Z",