Skip to content

Commit c22dd38

Browse files
Define workflow trigger and cleanup guarantees
1 parent 3505ca4 commit c22dd38

1 file changed

Lines changed: 215 additions & 0 deletions

File tree

  • docs/content/specification/workflow-triggers
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
title: Workflow triggers - Spec
3+
description: Preserve production runs, replace obsolete pull-request work, and clean up owned prereleases after closure.
4+
---
5+
6+
# Workflow triggers - Spec
7+
8+
Process-PSModule admits work into independent production, pull-request, and closure tracks. Production runs retain accepted work; pull-request runs favor the latest revision; closure stops obsolete work before optionally removing its prereleases. Module repositories select events and provide credentials, not scheduling algorithms.
9+
10+
## Problem
11+
12+
One scheduling policy cannot safely serve both production publication and rapid pull-request updates. Replacing queued production work loses delivery intent; retaining obsolete pull-request work delays feedback. Cleanup that races a publisher can leave orphaned prereleases or remove another pull request's output.
13+
14+
## Outcomes and impact
15+
16+
- **Outcome:** Accepted production events reach a terminal result without replacement by later events, while obsolete pull-request runs yield to current work.
17+
- **DORA:** Shorter feedback lead time without increasing publication failures caused by overlapping runs.
18+
- **Domain signal:** Zero production events evicted within supported queue capacity, zero overlapping production pipelines, and zero owned GitHub prereleases remaining after successful enabled cleanup.
19+
20+
## Users and jobs
21+
22+
Module maintainers publish accepted changes reliably. Contributors receive feedback for the latest pull-request state. Repository operators choose whether to retain prereleases and can identify failed, canceled, or capacity-rejected work.
23+
24+
## Scope
25+
26+
**In scope:** Event admission, track isolation, production serialization, pull-request supersession, closure cancellation, prerelease cleanup, and the minimum caller contract.
27+
28+
**Out of scope:** Version-label policy, quality gates, module build logic, merge-queue integration, and changing the production branch model.
29+
30+
## Non-goals
31+
32+
- Unlimited durable event storage or guaranteed completion despite platform outages, failures, manual cancellation, or timeouts.
33+
- Debouncing that prevents every obsolete run from starting.
34+
- Deleting or unlisting PowerShell Gallery packages, or treating cancellation as rollback of an external publication.
35+
36+
## Functional requirements
37+
38+
### FR1 - Central scheduling policy {#fr1}
39+
40+
The framework MUST own track selection, queue policy, and cancellation rules. A caller MUST NOT need concurrency expressions, a dispatcher job, or separate jobs for each track. Callers MUST subscribe to the required events and supply the established credentials and permissions. Cleanup configuration MUST use the existing repository settings contract.
41+
42+
#### Caller scenario
43+
44+
```gherkin
45+
Scenario: Use the standard caller
46+
Given a caller with the required event subscriptions, permissions, and secrets
47+
And no caller concurrency block or scheduling inputs
48+
When supported events invoke the framework
49+
Then the framework selects their tracks without additional caller logic
50+
```
51+
52+
### FR2 - Retain and serialize production work {#fr2}
53+
54+
Each push to the repository default branch, normally `main`, MUST enter the production track. All supported stable-publication entry points MUST share its serialization boundary. At most one production pipeline MUST execute at a time, from planning and version resolution through its final enabled stage.
55+
56+
A later event MUST NOT cancel a running production pipeline or replace an accepted pending production event within supported capacity. Pending events MUST execute sequentially in admission order, subject to the platform ordering boundary in the design. Each run MUST retain its triggering revision; it MUST NOT silently build a newer branch tip after waiting. Admission does not bypass important-file, release-label, or quality gates.
57+
58+
#### Production scenarios
59+
60+
```gherkin
61+
Scenario: Preserve a production burst
62+
Given production run A is executing
63+
When pushes B and C are admitted in that order
64+
Then A is not canceled by either push
65+
And B and C remain pending
66+
And B executes after A terminates
67+
And C executes after B terminates
68+
And each run uses its own triggering revision
69+
70+
Scenario: Share the production authority
71+
Given a production push run is executing
72+
When a supported manual stable-publication run is admitted
73+
Then the manual run waits in the same production track
74+
```
75+
76+
### FR3 - Supersede obsolete pull-request work {#fr3}
77+
78+
An update to an open pull request MUST request cancellation of older running work and replace older pending work for that pull request. Different pull requests MUST NOT cancel one another. Once updates stop and cancellation settles, only the latest eligible state proceeds; obsolete work MUST NOT begin another publication after failing a freshness check.
79+
80+
The rule applies to revision updates and supported release-intent changes, including label removal. Cancellation MAY allow already-started steps or external requests to finish. A burst therefore guarantees latest-state convergence, not exactly one workflow start.
81+
82+
#### Supersession scenarios
83+
84+
```gherkin
85+
Scenario: Push three revisions rapidly
86+
Given revision A is running for pull request 42
87+
When revisions B and C arrive in that order before A finishes
88+
And no later updates arrive
89+
Then cancellation is requested for superseded work
90+
And only C proceeds after cancellation settles
91+
And production runs and pull request 43 are unaffected
92+
93+
Scenario: Remove prerelease intent
94+
Given a pull-request run is eligible to publish a prerelease
95+
When the prerelease label is removed before publication begins
96+
Then the older run is superseded
97+
And its publication eligibility is rechecked before any new publication
98+
```
99+
100+
### FR4 - Close, stop, then clean {#fr4}
101+
102+
Both merged and abandoned pull requests MUST enter the closure track. Closure MUST request cancellation of running and pending activity belonging to the closing pull-request lifecycle, then wait for that activity to reach a terminal state before deleting prereleases. Closure MUST NOT build, test, publish a module, deploy a site, or authorize a stable release.
103+
104+
Closure runs MUST NOT be canceled by production pushes, pull-request updates, or later closure events. A merge's closure and default-branch push MUST remain separate operations. Closing obsolete activity is independent of whether prerelease deletion is enabled.
105+
106+
#### Closure scenarios
107+
108+
```gherkin
109+
Scenario: Close during prerelease publication
110+
Given a pull-request activity run is publishing a prerelease
111+
When that pull request closes
112+
Then the closure run requests cancellation of that activity
113+
And it waits for the activity to terminate before deleting prereleases
114+
And it reports incomplete cleanup if termination cannot be confirmed
115+
116+
Scenario: Merge creates two independent operations
117+
Given a prerelease pull request is merged
118+
When its closure event and default-branch push are admitted
119+
Then closure stops obsolete pull-request work and evaluates cleanup
120+
And the push enters the production queue
121+
And neither operation cancels the other
122+
```
123+
124+
### FR5 - Optional, exact, repeatable cleanup {#fr5}
125+
126+
Automatic cleanup MUST be enabled by default and MUST be disableable through the established cleanup setting. Enabled cleanup MUST enumerate all GitHub prerelease releases and tags provably owned by the closing pull-request lifecycle, including recoverable partial publication records. It MUST preserve stable releases, other pull requests' resources, and resources from a later reopening.
127+
128+
Cleanup MUST work without the prerelease label still being present, without important-file changes, and without the source branch still existing. Repeating successful cleanup MUST succeed with nothing left to delete. Ambiguous ownership, incomplete enumeration, or an unresolved deletion MUST be reported, not presented as successful complete cleanup.
129+
130+
#### Cleanup scenarios
131+
132+
```gherkin
133+
Scenario: Retain prereleases by configuration
134+
Given automatic cleanup is disabled
135+
When a pull request closes
136+
Then obsolete activity is stopped
137+
And no prerelease release or tag is deleted
138+
And retention is reported as intentional
139+
140+
Scenario: Clean beyond the first page
141+
Given a closed pull request owns prereleases spanning multiple result pages
142+
And its prerelease label and source branch have been removed
143+
When enabled cleanup succeeds
144+
Then all its owned GitHub prerelease releases and tags are absent
145+
And stable releases and other pull requests' resources are unchanged
146+
147+
Scenario: Reopen while cleanup is pending
148+
Given a closure run is pending for an earlier pull-request lifecycle
149+
When the pull request reopens and produces another prerelease
150+
Then the earlier closure neither cancels the reopened activity nor deletes its output
151+
```
152+
153+
## Non-functional requirements
154+
155+
### NFR1 - Maximum supported queue capacity {#nfr1}
156+
157+
The production track MUST use the platform's maximum supported pending-run capacity, with no smaller framework or caller limit. Capacity and ordering guarantees MUST be documented precisely. Overflow or expiry MUST remain visible as unsuccessful admission or execution; it MUST NOT be described as successful delivery.
158+
159+
#### Capacity scenario
160+
161+
```gherkin
162+
Scenario: Fill the production queue
163+
Given one production run is active
164+
When pending runs fill the documented maximum capacity
165+
Then every admitted pending run is retained
166+
When another run exceeds capacity
167+
Then the platform's rejection or cancellation is visible
168+
And no retained run is silently replaced by the framework
169+
```
170+
171+
### NFR2 - Bounded and diagnosable cleanup {#nfr2}
172+
173+
Every executing closure run MUST record its pull request, lifecycle boundary, cleanup decision, targeted run IDs, and resource outcomes without exposing secrets. Waiting for canceled activity MUST have a finite documented deadline. Deadline expiry MUST fail cleanup without beginning deletion; enumeration and deletion failures MUST produce a failing result with enough identity to retry safely.
174+
175+
### NFR3 - Narrow authority {#nfr3}
176+
177+
Cancellation and deletion MUST be restricted to the caller repository and verified framework-owned work. Cleanup MUST NOT execute untrusted pull-request code with write credentials. Unsupported credential contexts MUST fail or explicitly skip privileged work; they MUST NOT appear to have cleaned resources.
178+
179+
## Acceptance criteria
180+
181+
```gherkin
182+
# AC1 - Verifies: FR2, FR3, FR4, FR5, NFR2
183+
Scenario: Production burst during pull-request closure
184+
Given one production run and one prerelease pull-request run are active
185+
When two more production pushes arrive and the pull request closes
186+
Then all three production runs remain serialized and are not superseded
187+
And the closing pull-request activity is canceled before cleanup deletes anything
188+
And successful enabled cleanup leaves no owned GitHub prereleases
189+
And the closure run records what was stopped and removed
190+
191+
# AC2 - Verifies: FR1, FR4, FR5, NFR3
192+
Scenario: Clean an abandoned change with the standard caller
193+
Given the standard caller and default cleanup settings
194+
And an abandoned pull request owns several prereleases
195+
When it closes without merging
196+
Then cleanup runs without a special caller job
197+
And no stable release is created
198+
And only resources with verified ownership are deleted
199+
```
200+
201+
## Constraints and assumptions
202+
203+
- **Constraint:** Platform scheduling and cancellation are asynchronous. Cancellation does not undo PowerShell Gallery uploads or already-accepted API requests.
204+
- **Constraint:** The caller controls event delivery, filtering, permissions, and any outer concurrency policy. The framework cannot recover events the caller never invokes.
205+
- **Constraint:** Guarantees apply within documented platform queue and runtime limits, not to arbitrary external cancellation or failed quality gates.
206+
- **Assumption:** A repository has one production caller for a module. Independent modules or unrelated workflows are not coordinated unless they share an explicit framework identity.
207+
208+
## Dependencies
209+
210+
- [Framework spec](../spec.md) - release authority and publication requirements.
211+
- [Settings](../../reference/settings.md) - authorable cleanup policy.
212+
213+
## Where this connects
214+
215+
- [Design](design.md) - scheduling boundaries, platform limits, and cleanup implementation.

0 commit comments

Comments
 (0)