mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Promote v6 control plane to GA readiness
This commit is contained in:
@@ -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.
|
||||
|
||||
+77
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user