You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/content/reference/process-workflow-lifecycle-design.md
+41-20Lines changed: 41 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,18 +19,19 @@ These facts establish the starting point. They do not implement the scheduled va
19
19
20
20
## Candidate event routing
21
21
22
-
The planner classifies the caller event before build and publication work begins. Each route produces one of three mutation classes: validation only, prerelease mutation, or stable-release mutation.
22
+
The planner classifies the caller event before build and publication work begins. Each route produces one of three mutation classes: validation only, prerelease mutation, or stable-release mutation. The candidate also assigns an explicit mutability mode, because callers cannot safely infer it from labels alone.
23
23
24
-
| Event | Candidate route | Mutation class | Required result |
25
-
| --- | --- | --- | --- |
26
-
|`workflow_dispatch` on the default branch | Recovery release | Stable release or explicit no-op | Rebuild and validate the selected commit; reconstruct the unreleased release notes. |
27
-
|`schedule`| Published-artifact validation | Validation only | Validate the latest published stable artifact and its documentation. |
28
-
| Pull request `opened`, `reopened`, `synchronize`| Pull-request CI | Validation only | Report configured validation on the pull request. |
29
-
| Pull request `labeled`, `unlabeled`| Prerelease evaluation | Prerelease or validation only | Re-evaluate the full label set and publish only an eligible prerelease. |
30
-
| Pull request `closed`| Pull-request cleanup | Prerelease cleanup only | Remove only prereleases owned by the closed pull request. |
31
-
| Push to the default branch | Stable release | Stable release | Resolve merged-pull-request intent when applicable, then publish after required gates. |
|`workflow_dispatch` on the default branch | Recovery release | Stable release or explicit no-op | Never cancel | Rebuild and validate the selected commit; reconstruct the unreleased release notes. |
27
+
|`schedule`| Published-artifact validation | Validation only | May cancel only another explicitly read-only validation; never cancel a default-branch release | Validate the latest published stable artifact and its documentation. |
28
+
| Pull request `opened`, `reopened`, `synchronize`| Pull-request CI | Explicitly read-only | May cancel a superseded run in the same read-only CI domain | Report configured validation on the pull request. |
29
+
| Pull request `labeled`, `unlabeled`| Prerelease evaluation | Potential prerelease mutation | Never cancel | Re-evaluate the full label set and publish only an eligible prerelease. |
30
+
| Merged pull request `closed`| Post-merge close | Validation only | May cancel only when explicitly routed as read-only | Do not clean up; the successful main-push release owns promotion cleanup. |
31
+
| Abandoned pull request `closed`| Pull-request cleanup | Prerelease cleanup only | Never cancel | Remove only prereleases owned by the abandoned pull request. |
32
+
| Push to the default branch | Stable release | Stable release | Never cancel | Resolve merged-pull-request intent when applicable, then publish and perform promotion cleanup after required gates. |
32
33
33
-
The classifier records the route, mutable resource scope, commit identity, and release decision in the plan result. Downstream jobs consume that record rather than infer the event again.
34
+
The classifier records the route, mutability mode, mutable resource scope, commit identity, and release decision in the plan result. Downstream jobs consume that record rather than infer the event again.
34
35
35
36
## Candidate artifact and version boundary
36
37
@@ -57,29 +58,49 @@ The recovery route validates the selected commit using the same release gates as
57
58
58
59
The scheduled route resolves the latest published stable version as an input, not as a version to create. It validates the downloaded package and its published documentation with the checks appropriate to a published consumer artifact. Its plan record sets the mutation class to validation only, so publication and cleanup stages cannot run.
59
60
61
+
## Candidate close behavior
62
+
63
+
A merged pull-request close performs no prerelease cleanup because the default-branch push is the release authority. After its stable release succeeds, that main-push route owns promotion cleanup. An abandoned pull-request close runs only pull-request-scoped prerelease cleanup because no main push will occur.
64
+
65
+
The alternative of never cleaning up on a close requires scheduled garbage collection and leaves abandoned prereleases available until that collection runs. This candidate selects abandoned-close cleanup instead; it does not change the existing unapproved caller contract.
66
+
60
67
## Candidate concurrency isolation
61
68
62
-
Closed-pull-request cleanup and default-branch publication use independent concurrency identities:
69
+
Cancellation is conditional, not globally disabled. Only an explicit read-only mode may cancel a superseded run. A route that can mutate an external release resource never cancels and is never canceled.
| Explicitly read-only pull-request CI | Pull request number and read-only mode | Superseded runs may cancel | None. |
74
+
| Prerelease evaluation and abandoned-close cleanup | Pull request number and mutation mode | Never cancel | Prerelease artifacts associated with that pull request. |
75
+
| Default-branch push and manual recovery | Default-branch ref and mutation mode | Never cancel | Stable package, GitHub Release, tag, uploads, Pages, and promotion cleanup. |
76
+
| Scheduled validation | Published-version read domain | May cancel only another explicit scheduled validation | None. |
77
+
78
+
Concurrency selection occurs before the reusable workflow can produce a planner output. The candidate therefore requires either an explicit event mode at the caller boundary or separate read-only and mutation workflow domains. Caller-level label inspection is insufficient: repository settings can make a label event release-capable. Manual and scheduled runs must not share a cancellation domain that can cancel a default-branch release.
79
+
80
+
Cleanup receives a pull-request-scoped artifact set and MUST NOT perform a broad prerelease deletion while a stable release can be active. Any future broad cleanup needs a separately approved exclusive scope; it cannot share the abandoned-close route.
The routes do not cancel one another. Cleanup receives a pull-request-scoped artifact set and MUST NOT perform a broad prerelease deletion while a stable release can be active. Any future broad cleanup needs a separately approved exclusive scope; it cannot share the closed-pull-request route.
84
+
Cancellation after an external mutation begins can leave the release lifecycle partially complete:
85
+
86
+
| Mutation path | Cancellation hazard |
87
+
| --- | --- |
88
+
| PowerShell Gallery publication | The package can publish before the GitHub Release, release-asset upload, or pull-request comment completes. |
89
+
| Prerelease cleanup | Cleanup can delete only part of a prerelease tag and release set. |
90
+
| Default-branch release | A main release can stop between package publication, tag or GitHub Release creation, uploads, comments, Pages deployment, and promotion cleanup. |
91
+
| Manual or scheduled run sharing a ref | A cancellation domain keyed only by ref can interrupt a main release with a manual dispatch or scheduled validation. |
71
92
72
93
## Candidate verification strategy
73
94
74
95
The lifecycle contract is exercised with event payload fixtures and publication fakes before credentials are used:
75
96
76
97
| Candidate behavior | Verification |
77
98
| --- | --- |
78
-
| Event routing | One fixture for each supported event and pull-request activity. |
99
+
| Event routing | One fixture for each supported event and pull-request activity, including merged and abandoned close outcomes. |
79
100
| Version boundary | A mismatched artifact fixture that proves publication stops. |
80
101
| Recovery release notes | Merged-pull-request query fixtures covering an empty range, one pull request, and multiple pull requests. |
81
102
| Scheduled validation | A published-version fixture that proves no release mutation is requested. |
82
-
|Concurrency isolation|Overlapping closed-pull-request and default-branch-push runs that prove neither run cancels or broadens the other's scope. |
103
+
|Conditional cancellation|A superseded read-only CI fixture plus release-capable label, cleanup, main-push, manual, and scheduled fixtures that prove mutation-capable runs do not cancel. |
83
104
84
105
## Decisions requiring approval
85
106
@@ -88,7 +109,7 @@ The candidate does not decide the following:
88
109
- Whether a recovery release always uses the normal next patch version or permits an explicit version input.
89
110
- Which source is authoritative when a PowerShell Gallery publication and GitHub release disagree about the last published stable version.
90
111
- Which consumer-facing checks comprise scheduled published-artifact validation.
91
-
- Whether removing prerelease eligibility cleans up existing prereleases immediately or leaves cleanup to pull-request closure.
112
+
- Whether removing prerelease eligibility cleans up existing prereleases immediately or leaves them until the abandoned-close cleanup route.
92
113
- The exact caller triggers and concurrency expression, which remain subject to [PSModule/Process-PSModule#514](https://github.com/PSModule/Process-PSModule/issues/514).
### FR5 — Closed pull requests MUST clean up only their prereleases {#fr5}
101
+
### FR5 — Closed pull requests MUST route cleanup by close outcome {#fr5}
102
102
103
-
A closed pull request MUST clean up only prerelease artifacts associated with that pull request when cleanup is enabled. It MUST NOT authorize or create a stable publication.
103
+
A merged pull request close MUST NOT perform prerelease cleanup. A successful default-branch release for the merge MUST own promotion cleanup. An abandoned pull request close MUST run cleanup-only behavior for prerelease artifacts associated with that pull request when cleanup is enabled. Neither close outcome MUST authorize or create a stable publication.
104
104
105
105
#### Behavioral scenarios {#fr5-scenarios}
106
106
107
107
```gherkin
108
-
Scenario: Close a pull request with prereleases
109
-
Given a closed pull request owns prerelease artifacts
110
-
When the cleanup workflow completes
111
-
Then the pull request's prerelease artifacts are removed according to configuration
108
+
Scenario: Merge a pull request with prereleases
109
+
Given a merged pull request owns prerelease artifacts
110
+
When its close event is processed
111
+
Then the close event does not clean up prerelease artifacts
112
+
And the successful default-branch release owns promotion cleanup
113
+
114
+
Scenario: Abandon a pull request with prereleases
115
+
Given an unmerged closed pull request owns prerelease artifacts
116
+
When its close event is processed
117
+
Then the workflow runs cleanup only for that pull request's prerelease artifacts
112
118
And no stable artifact, tag, or release is created
113
119
```
114
120
115
121
### FR6 — Default-branch pushes MUST authorize stable publication after validation {#fr6}
116
122
117
-
A push to the default branch MUST publish a stable version only after all required build, test, quality, and publication gates succeed. When the pushed commit is the merge commit of a pull request, the stable-release decision MUST use that pull request's release intent.
123
+
A push to the default branch MUST publish a stable version only after all required build, test, quality, and publication gates succeed. When the pushed commit is the merge commit of a pull request, the stable-release decision MUST use that pull request's release intent. A successful stable release for a merged pull request MUST own promotion cleanup.
118
124
119
125
#### Behavioral scenarios {#fr6-scenarios}
120
126
@@ -125,6 +131,7 @@ Scenario: Publish a merged pull request
125
131
When all required validation gates succeed
126
132
Then the workflow publishes the resulting stable version
127
133
And the publication is associated with the pushed commit
134
+
And the successful release performs promotion cleanup
128
135
```
129
136
130
137
### FR7 — Published artifacts MUST match the resolved version {#fr7}
@@ -158,19 +165,29 @@ Scenario: Retry a publication after an interrupted run
158
165
And it does not duplicate the package, tag, or release
159
166
```
160
167
161
-
### NFR2 — Closed-pull-request cleanup and default-branch publication MUST be isolated {#nfr2}
168
+
### NFR2 — Cancellation MUST be conditional on an explicit mutability mode {#nfr2}
162
169
163
-
A closed-pull-request cleanup and a default-branch push MUST use distinct concurrency identities and MUST NOT cancel each other. Cleanup MUST remain limited to its pull request's prerelease artifacts while a default-branch push publishes a stable version.
170
+
A run explicitly classified as read-only pull-request CI SHOULD cancel a superseded `opened`, `reopened`, or `synchronize` run for the same pull request. A run that may publish to the PowerShell Gallery, create or upload a GitHub Release or tag, deploy Pages, or clean up prereleases MUST NOT be canceled. Caller-level label inspection MUST NOT determine mutability because repository settings can change the release decision. Manual and scheduled runs sharing a ref MUST NOT cancel a default-branch release.
164
171
165
172
#### Behavioral scenarios {#nfr2-scenarios}
166
173
167
174
```gherkin
168
-
Scenario: Cleanup and stable publication overlap
169
-
Given a pull request closes while another pull request is pushed to the default branch
170
-
When both workflow runs start
171
-
Then neither run cancels the other
172
-
And cleanup does not remove artifacts outside the closed pull request
173
-
And stable publication completes independently
175
+
Scenario: Supersede read-only pull-request CI
176
+
Given an explicitly read-only pull-request CI run is in progress
177
+
When a newer synchronize event starts the same read-only route
178
+
Then the older run may be canceled
179
+
And no release-related resource is mutated by either run
180
+
181
+
Scenario: Preserve a release-capable run
182
+
Given a run may publish, deploy, or clean up prereleases
183
+
When a newer event starts
184
+
Then the release-capable run is not canceled
185
+
And the newer event does not infer safety from pull-request labels alone
186
+
187
+
Scenario: Keep main release independent from manual and scheduled runs
188
+
Given a default-branch release is in progress
189
+
When a manual dispatch or scheduled validation starts for the same ref
190
+
Then neither event cancels the default-branch release
174
191
```
175
192
176
193
### NFR3 — Each lifecycle outcome MUST be auditable {#nfr3}
@@ -202,15 +219,16 @@ Scenario: Recover release notes after a missed main-push publication
0 commit comments