|
| 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