Skip to content

Commit 6e30367

Browse files
docs: specify workflow lifecycle candidate
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 73cd63d commit 6e30367

3 files changed

Lines changed: 328 additions & 0 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
title: Process-PSModule workflow lifecycle candidate design
3+
description: Candidate design for Process-PSModule event routing, stamped artifacts, recovery release notes, and concurrency isolation.
4+
---
5+
6+
# Process-PSModule workflow lifecycle candidate design
7+
8+
**Status:** This is a candidate design for discussion. It is not approved and does not change the reusable workflow or its caller contract.
9+
10+
This design describes one way to meet the [workflow lifecycle candidate specification](process-workflow-lifecycle-specification.md). It follows the [spec/design separation](https://msx.no/docs/Ways-of-Working/Spec-Driven-Development/#what-a-design-is); detailed implementation choices remain free until the candidate is approved.
11+
12+
## Confirmed implementation baseline
13+
14+
The confirmed reusable workflow runs a `Plan` job, enriches one settings object with the resolved version, and passes that object to downstream jobs. The module build action accepts the resolved version and prerelease identity; the publish workflow consumes the built artifact and uses the resolved full version for the release.
15+
16+
The version resolver treats non-pull-request events, including `workflow_dispatch` and `schedule`, as events without a release decision. The workflow's concurrency identity contains either the pull request number or the Git ref, and its runs are not canceled. The documented scenario matrix also identifies closed-pull-request cleanup as non-stable behavior.
17+
18+
These facts establish the starting point. They do not implement the scheduled validation or manual recovery behavior proposed below.
19+
20+
## Candidate event routing
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.
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. |
32+
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+
35+
## Candidate artifact and version boundary
36+
37+
Version resolution is the boundary between planning and release-capable work. The candidate carries one immutable release record through build, test, and publication:
38+
39+
| Record field | Purpose |
40+
| --- | --- |
41+
| Commit identity | Binds validation, artifact, and release to one source revision. |
42+
| Resolved stable version and prerelease identity | Defines the only version permitted in the built artifact. |
43+
| Event route and mutation class | Restricts each downstream stage to its authorized behavior. |
44+
| Release-note range | Identifies the merged pull requests eligible for a recovery release note. |
45+
46+
The build stage stamps the resolved version into the module artifact. Before any package, tag, or release becomes visible, the publication stage verifies that the artifact version and prerelease identity equal the immutable release record. A mismatch stops publication; it is not corrected by retagging or by recalculating a version after the artifact is built.
47+
48+
## Candidate manual recovery
49+
50+
A manual recovery route accepts only a selected default-branch commit. It first determines whether a stable publication already covers that commit and returns a no-op when one exists.
51+
52+
For a missing publication, the route identifies the last published stable version and its associated default-branch commit. It then queries merged pull requests targeting the default branch between that publication boundary and the selected commit. The release-note reconstruction uses that ordered, de-duplicated result rather than the manual-dispatch event payload, which has no pull-request context.
53+
54+
The recovery route validates the selected commit using the same release gates as a default-branch push. It produces a stable release only after the artifact/version boundary succeeds. This keeps recovery notes traceable even when the normal main-push run was missed or interrupted.
55+
56+
## Candidate scheduled validation
57+
58+
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+
## Candidate concurrency isolation
61+
62+
Closed-pull-request cleanup and default-branch publication use independent concurrency identities:
63+
64+
| Lifecycle route | Concurrency scope | Mutable resources |
65+
| --- | --- | --- |
66+
| Closed pull request | Pull request number | Prereleases associated with that pull request. |
67+
| Default-branch push or manual recovery | Default-branch ref | The stable release and its versioned artifacts. |
68+
| Scheduled validation | Published-version read scope | No release-related mutable resources. |
69+
70+
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.
71+
72+
## Candidate verification strategy
73+
74+
The lifecycle contract is exercised with event payload fixtures and publication fakes before credentials are used:
75+
76+
| Candidate behavior | Verification |
77+
| --- | --- |
78+
| Event routing | One fixture for each supported event and pull-request activity. |
79+
| Version boundary | A mismatched artifact fixture that proves publication stops. |
80+
| Recovery release notes | Merged-pull-request query fixtures covering an empty range, one pull request, and multiple pull requests. |
81+
| 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. |
83+
84+
## Decisions requiring approval
85+
86+
The candidate does not decide the following:
87+
88+
- Whether a recovery release always uses the normal next patch version or permits an explicit version input.
89+
- Which source is authoritative when a PowerShell Gallery publication and GitHub release disagree about the last published stable version.
90+
- 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.
92+
- The exact caller triggers and concurrency expression, which remain subject to [PSModule/Process-PSModule#514](https://github.com/PSModule/Process-PSModule/issues/514).
93+
94+
## Related
95+
96+
- [Candidate specification](process-workflow-lifecycle-specification.md) — behavior and acceptance criteria.
97+
- [Scenario matrix](scenario-matrix.md) — established job-level routing reference.
98+
- [Process-PSModule caller workflow candidate](process-workflow-fleet-standard.md) — candidate event and concurrency contract.
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
title: Process-PSModule workflow lifecycle candidate specification
3+
description: Candidate behavior-driven requirements for Process-PSModule caller event routing, recovery releases, validation, and cleanup.
4+
---
5+
6+
# Process-PSModule workflow lifecycle candidate specification
7+
8+
**Status:** This is a candidate for discussion. It is not an approved workflow standard and does not change the candidate caller contract in [Process-PSModule caller workflow candidate](process-workflow-fleet-standard.md).
9+
10+
## Problem and outcome
11+
12+
Module repositories need each workflow event to have one safe, observable lifecycle outcome. A failed or missed publication needs a recoverable path; published artifacts need continuing validation; and pull-request activity must not create an accidental stable release.
13+
14+
This candidate defines the behavior required from a Process-PSModule workflow lifecycle. Its requirements follow [spec-driven development](https://msx.no/docs/Ways-of-Working/Spec-Driven-Development/) and use [Given / When / Then scenarios](https://msx.no/docs/Ways-of-Working/Spec-Driven-Development/#behavioral-scenarios) as the acceptance contract.
15+
16+
## Scope
17+
18+
The candidate covers dispatch recovery, scheduled validation, pull-request validation and prerelease evaluation, closed-pull-request cleanup, and stable publication after a default-branch push.
19+
20+
It does not approve a caller layout, change module build or publication implementation, define label names, or prescribe release-note presentation. Those choices remain in the caller candidate, the existing versioning guidance, and the companion [candidate design](process-workflow-lifecycle-design.md).
21+
22+
## Confirmed implementation baseline
23+
24+
The reusable workflow has a single planning decision that enriches downstream settings with a resolved version and release decision. It also serializes runs by pull request number or ref without canceling a running workflow.
25+
26+
The version resolver treats non-pull-request events, including `workflow_dispatch` and `schedule`, as events without a pull request and does not create a release decision. The existing workflow supports pull-request validation, prerelease publication, default-branch publication, and closed-pull-request prerelease cleanup. Scheduled published-artifact validation and manual recovery publication are not confirmed behavior.
27+
28+
The following requirements describe desired behavior, not a claim that the confirmed implementation already satisfies it.
29+
30+
## Functional requirements
31+
32+
### FR1 — Manual dispatch MUST provide a safe recovery release {#fr1}
33+
34+
A default-branch manual dispatch MUST either publish one recoverable stable release after all required validation succeeds or report that the selected commit is already covered by a stable publication. It MUST NOT create a duplicate stable publication.
35+
36+
#### Behavioral scenarios {#fr1-scenarios}
37+
38+
```gherkin
39+
Scenario: Recover a missing stable publication
40+
Given the default branch contains a validated commit without a stable publication
41+
When a maintainer dispatches the workflow for that commit
42+
Then the workflow publishes one stable artifact and release for the commit
43+
And the release notes identify merged pull requests since the previous published version
44+
45+
Scenario: Repeat a completed recovery dispatch
46+
Given a stable publication already covers the selected default-branch commit
47+
When a maintainer dispatches the workflow again
48+
Then the workflow reports that no recovery release is required
49+
And it does not create another artifact, tag, or release
50+
```
51+
52+
### FR2 — Scheduled runs MUST validate published artifacts without publishing {#fr2}
53+
54+
A scheduled run MUST validate the latest published stable artifact and its published documentation against the repository's configured checks. It MUST NOT create, replace, or delete a package, tag, release, or prerelease.
55+
56+
#### Behavioral scenarios {#fr2-scenarios}
57+
58+
```gherkin
59+
Scenario: Validate the latest published artifact
60+
Given a stable module version and its documentation are published
61+
When the scheduled workflow runs
62+
Then the workflow validates that published version
63+
And it reports the validated version and result
64+
And it creates no release-related artifact
65+
```
66+
67+
### FR3 — Pull-request delivery events MUST run validation only {#fr3}
68+
69+
An `opened`, `reopened`, or `synchronize` pull-request event targeting the default branch MUST run the configured validation for the pull request. It MUST NOT create a stable publication.
70+
71+
#### Behavioral scenarios {#fr3-scenarios}
72+
73+
```gherkin
74+
Scenario: Validate a synchronized pull request
75+
Given a pull request targets the default branch
76+
When a new commit synchronizes the pull request
77+
Then the workflow reports the configured validation result on that pull request
78+
And it does not publish a stable version
79+
```
80+
81+
### FR4 — Label changes MUST re-evaluate prerelease eligibility {#fr4}
82+
83+
A `labeled` or `unlabeled` pull-request event targeting the default branch MUST re-evaluate prerelease eligibility from the complete current label set. A prerelease publication MUST occur only when the pull request is eligible and every required validation succeeds.
84+
85+
#### Behavioral scenarios {#fr4-scenarios}
86+
87+
```gherkin
88+
Scenario: Add prerelease eligibility
89+
Given a validated pull request has no prerelease eligibility
90+
When a prerelease label is added
91+
Then the workflow re-evaluates the pull request
92+
And it publishes at most one eligible prerelease version
93+
94+
Scenario: Remove prerelease eligibility
95+
Given a pull request has prerelease eligibility
96+
When its prerelease label is removed
97+
Then the workflow re-evaluates the pull request as ineligible
98+
And it does not create a new prerelease version
99+
```
100+
101+
### FR5 — Closed pull requests MUST clean up only their prereleases {#fr5}
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.
104+
105+
#### Behavioral scenarios {#fr5-scenarios}
106+
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
112+
And no stable artifact, tag, or release is created
113+
```
114+
115+
### FR6 — Default-branch pushes MUST authorize stable publication after validation {#fr6}
116+
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.
118+
119+
#### Behavioral scenarios {#fr6-scenarios}
120+
121+
```gherkin
122+
Scenario: Publish a merged pull request
123+
Given a merged pull request has an unambiguous release intent
124+
And its merge commit is pushed to the default branch
125+
When all required validation gates succeed
126+
Then the workflow publishes the resulting stable version
127+
And the publication is associated with the pushed commit
128+
```
129+
130+
### FR7 — Published artifacts MUST match the resolved version {#fr7}
131+
132+
Every prerelease or stable publication MUST contain the version and prerelease identity resolved for its workflow run. A version mismatch MUST fail publication before the release is made visible.
133+
134+
#### Behavioral scenarios {#fr7-scenarios}
135+
136+
```gherkin
137+
Scenario: Reject an incorrectly stamped artifact
138+
Given a workflow resolves a release version
139+
And the built artifact reports a different version
140+
When publication is attempted
141+
Then publication fails
142+
And no release is made visible for that artifact
143+
```
144+
145+
## Non-functional requirements
146+
147+
### NFR1 — Lifecycle mutations MUST be idempotent {#nfr1}
148+
149+
Retrying the same event for the same commit and resolved version MUST produce no more than one package, tag, and release for that version.
150+
151+
#### Behavioral scenarios {#nfr1-scenarios}
152+
153+
```gherkin
154+
Scenario: Retry a publication after an interrupted run
155+
Given a publication for a resolved version was interrupted
156+
When the workflow retries the same event
157+
Then it completes the missing work or reports the completed work
158+
And it does not duplicate the package, tag, or release
159+
```
160+
161+
### NFR2 — Closed-pull-request cleanup and default-branch publication MUST be isolated {#nfr2}
162+
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.
164+
165+
#### Behavioral scenarios {#nfr2-scenarios}
166+
167+
```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
174+
```
175+
176+
### NFR3 — Each lifecycle outcome MUST be auditable {#nfr3}
177+
178+
Every run MUST report its event category, resolved version or validated published version, release decision, and terminal outcome before the run completes.
179+
180+
#### Behavioral scenarios {#nfr3-scenarios}
181+
182+
```gherkin
183+
Scenario: Inspect a scheduled validation result
184+
Given a scheduled validation has completed
185+
When a maintainer inspects the workflow result
186+
Then the result identifies the validated published version
187+
And it identifies whether validation passed or failed
188+
And it identifies that no release mutation occurred
189+
```
190+
191+
## Cross-cutting acceptance criteria
192+
193+
### AC1 — Verifies: [FR1](#fr1), [FR6](#fr6), [FR7](#fr7), [NFR1](#nfr1)
194+
195+
```gherkin
196+
Scenario: Recover release notes after a missed main-push publication
197+
Given merged pull requests exist after the last published stable version
198+
And the selected default-branch commit has no stable publication
199+
When a maintainer dispatches a recovery release
200+
Then the published artifact matches the resolved version
201+
And the release notes identify the merged pull requests in that unreleased range
202+
And a retry creates no duplicate publication
203+
```
204+
205+
### AC2 — Verifies: [FR2](#fr2), [FR5](#fr5), [NFR2](#nfr2), [NFR3](#nfr3)
206+
207+
```gherkin
208+
Scenario: Non-stable lifecycle events remain non-mutating
209+
Given a scheduled validation and a closed-pull-request cleanup overlap a main-push release
210+
When all three runs complete
211+
Then the scheduled run reports validation without a release mutation
212+
And the cleanup affects only the closed pull request's prereleases
213+
And the main-push run is the only run that can publish the stable release
214+
```
215+
216+
## Impact
217+
218+
This candidate aims to reduce time to restore a missed publication and reduce change failure risk by separating validation, cleanup, prerelease, and stable-release authority. Its domain signal is the count of duplicate, missing, or incorrectly stamped published versions per release cycle; the target is zero.
219+
220+
## Dependencies and constraints
221+
222+
Approval of the caller event and concurrency contract in [PSModule/Process-PSModule#514](https://github.com/PSModule/Process-PSModule/issues/514) is required before this candidate becomes an implementation commitment. The candidate depends on repository credentials that can query pull requests and publish module artifacts. It retains the caller candidate's default-branch and fork boundaries.
223+
224+
## Related
225+
226+
- [Candidate design](process-workflow-lifecycle-design.md) — proposed routing and recovery approach.
227+
- [Scenario matrix](scenario-matrix.md) — established job-level routing reference.
228+
- [Process-PSModule caller workflow candidate](process-workflow-fleet-standard.md) — candidate caller event and concurrency contract.

docs/zensical.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ nav = [
4747
{"Scenario matrix" = "reference/scenario-matrix.md"},
4848
{"Framework test IDs" = "reference/framework-test-ids.md"},
4949
{"Dependencies" = "reference/dependencies.md"},
50+
{"Workflow lifecycle candidate specification" = "reference/process-workflow-lifecycle-specification.md"},
51+
{"Workflow lifecycle candidate design" = "reference/process-workflow-lifecycle-design.md"},
5052
]},
5153
{"Specification" = [
5254
"specification/index.md",

0 commit comments

Comments
 (0)