Pulse/docs/release-control/internal/CONTROL_PLANE.md
2026-04-11 13:23:07 +01:00

289 lines
16 KiB
Markdown

# Pulse Release Control Plane
This directory is the evergreen governance layer for Pulse release control.
It is not tied to one release line. It defines the long-lived control-plane
model that active and future release profiles reuse.
## Purpose
1. Keep one canonical governance system across releases.
2. Keep one canonical values layer for agent behavior instead of pushing
detailed operating burden into prompts.
3. Separate evergreen control-plane rules from release-specific profile state.
4. Make the active release profile explicit and machine-resolvable.
5. Make the active engineering target explicit so the objective can change
without replacing the control plane itself.
6. Prevent future releases from cloning a new `v7`, `v8`, or `v9` governance
stack when the system itself should remain continuous.
## Control-Plane Rules
1. Governance is continuous.
Repo hardening, subsystem ownership, proof routing, drift guards, and
readiness assertion mechanics are permanent parts of Pulse engineering.
2. Release profiles are temporary.
Open decisions, manual release gates, migration obligations, and
release-specific readiness concerns belong to the active profile, not to the
evergreen control plane.
3. There must be one active profile at a time.
`control_plane.json` is the machine authority for which release profile is
currently active and where its files live.
The active profile also owns the governed prerelease and stable release
branches for that line, so workflows and local release tooling must resolve
branch requirements from the control plane instead of hard-coding `main`.
When an older maintenance line still needs governed release or demo-routing
support after the active profile has moved on, that branch mapping must stay
explicit in `control_plane.json` as a legacy release-line override rather
than being inferred from the active profile or smuggled into one workflow.
4. Profile changes must reuse the same control-plane machinery.
Future releases should switch or add profiles rather than fork the guardrail
system.
5. The active target must never be implicit.
`control_plane.json` must always declare the current target, even when the
target changes from release hardening to stabilization, polish, or feature
delivery.
6. The control plane must stay self-policing.
Audit scripts, completion guards, and repo tests must derive the active
profile from `control_plane.json` instead of hard-coding version-specific
paths.
7. A completed active target must not linger.
When a machine-derivable target completion rule is satisfied,
`control_plane_audit.py --check` must fail until the current target is
marked complete and a new active target is promoted.
8. Executable proof runs must follow the active target.
Local hooks and CI should resolve readiness-assertion proof scope from the
active target completion rule instead of hard-coding only repo-ready or
future-phase release-ready proof surfaces.
Manual targets may declare an explicit proof scope when they should still
pull machine proofs, or `none` when the hold is intentionally human-owned
and should not emit derivation warnings.
9. High-risk closure must respect evidence quality.
A passed release gate is not enough by itself. `status.json.release_gates`
must declare the minimum evidence tier needed for closure, and the audit
layer must treat lower-tier evidence as rehearsal only rather than as
high-confidence completion.
10. Direction changes must be normalized.
When the user states a durable product truth or changes the current product
priority, the agent must classify that direction as a readiness assertion,
release gate, open decision, or active-target update instead of leaving it
as informal chat.
Treat casual statements about consistency, seamlessness, drift, bypass
resistance, or things that "should always be true" as candidate governance
input, not only explicit "add an assertion" requests.
11. Lane coverage gaps must be normalized.
When an agent discovers a durable product surface that is materially
underrepresented by the current lane map, it must record that gap in the
active profile instead of burying it inside a broad lane, lane followup, or
assertion.
Repeated or durable coverage gaps must be promoted into a lane split,
lane addition, or active-target update once the right owning surface is
clear.
When that promotion path is clear enough to name a `candidate_lane`, the
candidate should also point at the owning control-plane target so future
lane-expansion work has an explicit destination rather than only prose.
12. Work claims are the audit trail for active governed slices.
Before coding begins on a governed slice, record a `work_claim` against
the lane, blocker, coverage gap, candidate lane, or other governed item
being worked on.
The claim records what is being worked on and prevents accidental overlap
if work is resumed or handed off.
Claims carry expiry so abandoned claims age out instead of freezing the
map indefinitely; remove expired claims when encountered.
13. Lane work must follow canonical v6 modernization rules.
Choosing the right lane is not enough by itself. Once a lane or candidate
lane is selected, the agent must check that surface against the canonical
v6 architecture rather than only improving the local pre-v6 or legacy path.
That includes preferring the unified resource model where it is the
canonical shape, removing or isolating legacy host-era terminology and
compatibility shims from primary paths, using canonical v6 types and APIs,
and updating subsystem ownership plus proof routing when runtime ownership
changes.
When canonicalization makes an old internal path obsolete, the same slice
should retire the superseded code, docs, fixtures, and helpers rather than
leaving parallel legacy-primary implementations behind.
This is not limited to code the slice directly replaced: if work lands on
clearly obsolete old-way internals inside the governed surface, cleanup
should pull that retirement forward instead of leaving known legacy clutter
behind for a future pass.
Any retained legacy behavior must be named as a boundary-only exception,
and the slice should carry proof or guardrails showing that the supported
boundary still works after the broader old path is removed.
These modernization rules are retrospective as well as forward-looking:
existing lanes that were previously improved in a legacy-shaped way must be
re-judged against the canonical v6 model when they are touched again, and
they must not be treated as complete simply because earlier local work
landed.
14. Prompt guidance should stay principle-led.
Prompt-like guidance should express posture, values, and entry-point
heuristics, while the control plane, active profile, subsystem contracts,
audits, and guardrails carry the detailed operating rules.
If an agent still needs repeated procedural prompt wording to behave, the
canonical system should be strengthened instead of growing the prompt.
15. Governance routing should be quiet by default.
Resolving the entry bundle, mapping a request into a lane or governed slice,
and deciding whether a claim or contract update is required are normally
internal behaviors. Surface those mechanics only when they materially affect
blockers, scope, cross-repo impact, or the user's next decision.
16. Use worktrees when a mutating slice needs physical isolation.
Claims and lookup tools reduce logical overlap, but they do not isolate
hooks, formatters, staged reads, or unrelated dirty state. Use a dedicated
worktree when a slice requires that isolation — a shared checkout is fine
for normal single-session work.
The canonical helper trio is `worktree_base.py` to ensure the clean landing
worktree exists, `worktree_claim.py` to start isolated work, and
`worktree_finish.py` to land a finished isolated slice back onto the clean
base-branch worktree.
17. Lane-expansion targets must steer slice selection.
When the active target is a lane-expansion or bridge-foundation target and
`status_audit.py --pretty` exposes an `available_candidate_lane_queue`,
agents should choose from that queue before local cleanup, bounded
stabilization residue, or presentation-only polish unless the user
explicitly overrides the priority or a release-blocking surface needs
immediate containment.
Support work is still valid, but it should normally advance the selected
candidate lane or the active blocker rather than displacing it.
18. Candidate-lane work must be first-class, not prose-only.
If `status.json.candidate_lanes` and `status.json.coverage_gaps` are the
real remaining work, the control-plane entrypoint, lookup helpers, and
current-state text must treat them as primary governed surfaces rather than
forcing agents to infer them indirectly from broad audit output.
19. Shared-checkout claim flow must be executable.
If `status.json.work_claims` is the governed overlap boundary, the control
plane must expose a first-class shared-checkout helper for reserving,
renewing, and releasing exactly one governed slice instead of leaving that
path as prose-only guidance.
Support-only slices still need that claim boundary, and any same-lane
residual must be normalized before the claim is replaced or released.
20. Customer-surface quality is a stop gate, not a polish note.
If a customer-facing surface is still prototype-grade, confusing, or
untrustworthy in real browser use, the control plane should steer the
agent toward redesigning the owning model or boundary instead of
continuing narrow same-shape iteration on that weak baseline.
21. Frontend slices require browser proof at the browser layer.
For frontend or UI changes, a build and code-level tests are not enough to
call the slice progress. The changed surface must be exercised in
Playwright after the current build, and that browser inspection should
inform whether the slice is acceptable, still prototype-grade, or needs a
deeper redesign.
## Canonical Files
1. `docs/release-control/AGENT_VALUES.md`
Stable human values layer for agent behavior.
1. `docs/release-control/CONTROL_PLANE.md`
Stable human governance for the evergreen control plane.
2. `docs/release-control/control_plane.json`
Machine-readable control-plane state, including the active profile and
active target.
3. `docs/release-control/control_plane.schema.json`
Machine-readable contract for `control_plane.json`.
## Active Profile Rule
The active release profile owns:
1. profile-specific source of truth
2. live lane and readiness state
3. active release gates and open decisions
4. subsystem registry and contracts
5. release-specific runbooks and checklists
6. the governed prerelease and stable release branches for that profile
The evergreen control plane owns:
1. the profile selection mechanism
2. the active engineering target selection mechanism
3. the continuous governance model
4. the requirement that tooling resolves the active profile instead of assuming
one fixed version forever
## Active Target Rule
The control plane must always declare one active target.
Targets may change over time, for example:
1. release hardening
2. post-release stabilization
3. polish
4. a named feature initiative
5. maintenance or reliability work
The target should change without rebuilding the governance system.
Agents should update the active target when direction changes, and the audits
should fail when a target with a machine-derivable completion rule is already
complete but still marked active.
Durable truths that are not target changes should be normalized into
readiness assertions, release gates, or open decisions rather than copied into
the target text.
Active targets narrow execution focus; they do not define the entire product
map.
If the active profile discovers product scope that the current lane taxonomy
does not model cleanly, that gap should still be recorded even when it sits
outside the active target floor.
For release targets, `rc_ready` means a governed prerelease build can be
cut, while `release_ready` means stable or GA promotion readiness after
prerelease validation.
Executable readiness proof runs should follow that same phase boundary: the
active target should pull in only the readiness blocking levels required for
its completion rule, not future-phase proof suites by default.
When a target is intentionally human-held, declare that explicitly in
`control_plane.json` rather than relying on a derivation failure to signal it.
Do not wait for a special governance prompt before checking whether informal
user language should update the control plane.
## Current State
1. v6 is the current active release profile.
2. `v6-rc-cut` is the current active engineering target.
It is a release target, so default slice selection should stay centered on
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-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
long-term system.
8. Until the explicit post-GA branch cutover happens, both prerelease and
stable v6 promotions resolve to `pulse/v6-release` via `control_plane.json`.
9. Legacy maintenance releases that still feed governed automation outside the
active v6 line must also resolve through `control_plane.json`.
Right now the remaining `5.1.x` stable maintenance line resolves to `main`
via an explicit legacy release-line override, so stable demo automation can
validate the real shipped stable tag without pretending that v5 tags belong
to `pulse/v6-release`.
10. `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.
11. `python3 scripts/release_control/control_plane.py --agent-entrypoint --pretty`
is the executable entrypoint for that ordered guidance bundle; agents
should prefer it over reconstructing the bundle ad hoc.
That entrypoint should lead with lightweight startup files plus derived
commands such as `status_audit.py --pretty` and targeted lookup helpers.
`status_lookup.py` is the id-scoped helper for candidate lanes, coverage
gaps, lanes, assertions, gates, followups, and work claims.
`work_claim.py` is the shared-checkout claim helper for reserving or
releasing exactly one governed slice before mutation.
`worktree_base.py` ensures the canonical clean landing worktree exists for
the base branch.
`worktree_claim.py` is the canonical helper for pairing a mutating claim
with a dedicated worktree when a slice requires physical isolation.
`worktree_finish.py` is the canonical helper for landing an isolated
finished slice back onto the base branch worktree.
It should not require agents to ingest raw `status.json` or `registry.json`
in full at startup unless the current task genuinely needs to drill into
those surfaces.
11. The control plane now treats prototype-grade customer surfaces as a real
routing concern.
Agents should not let lane continuity or queue selection trap them into
repeated improvement of a baseline that is still obviously below the
product bar; they should step back to the owning redesign or canonical
boundary first.