Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 44 additions & 12 deletions src/docs/Capabilities/release-management/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,55 @@ Two consequences follow, and they are the point of the model:

## Version computation

- The bump comes from the PR label (`release:major` / `release:minor` / `release:patch` / `release:none`).
Exactly one is required; **no default** is applied. A missing label, multiple
SemVer labels, or a SemVer label alongside `release:none` are all **rejected**, so
the version is always a decision someone made. For `workflow_dispatch`, the
bump is an input.
Release automation reads only labels in its `release:` namespace:

| Label | Meaning | Valid combination |
| --- | --- | --- |
| `release:patch` | Resolve the next patch version. | Exactly one bump label. |
| `release:minor` | Resolve the next minor version. | Exactly one bump label. |
| `release:major` | Resolve the next major version. | Exactly one bump label. |
| `release:pre-release` | Publish the open pull request as a prerelease. | With exactly one bump label. |
| `release:skip` | Run validation without resolving or publishing a version. | Alone. |

Exactly one bump label or `release:skip` is required; **no default** is applied.
`release:pre-release` is an optional mode label, not a bump. A missing decision,
multiple bump labels, `release:skip` with another release label, or
`release:pre-release` without one bump label is **rejected**, so the outcome is
always a decision someone made. Bare `major`, `minor`, and `patch` labels are
ignored.

- **First release** starts from a baseline (`v0.1.0` or `v1.0.0`). Pre-`1.0.0`
breaking changes are `release:minor` per [SemVer §4](https://semver.org/#spec-item-4);
`release:major` is never auto-detected pre-`1.0.0`.
- The tag is created on the commit now at the head of the release branch —
squash, merge-commit, and rebase strategies alike.

### Optional ad hoc releases

The standard release path is a labeled pull request merged into a release
branch. `workflow_dispatch` is an optional extension, not part of the minimum
implementation. An implementation SHOULD omit it unless its product has a real
need to release already-reviewed content outside the merge flow.

Where an ad hoc path exists, it requires an explicit bump, source ref, release-note
summary, and reason. It resolves the source ref to an immutable commit and enters
the same Resolve → Build → Test → Publish pipeline as a merged pull request. It
does not infer a bump, bypass validation, rebuild an existing version, or make a
direct push into a release interface.

Do not create an empty pull request to manufacture a release. It contains no
artifact-affecting change and makes the review trail imply a change that did not
happen. Retrying failed validation or publication is not an ad hoc release
either: rerun the existing release with the same artifact and version under the
[recovery rule](#the-pipeline).

## Prereleases

- **Branch-level** — a prerelease-type branch publishes on every push, using the
branch name as the identifier: `v1.3.0-dev.1`, `v1.3.0-dev.2`, …
- **PR-level** — a prerelease label on an open PR publishes
- **PR-level** — `release:pre-release` alongside exactly one bump label on an open PR publishes
`v<base>-<identifier>.<counter>`: `base` is the next version from the PR's bump
label, `identifier` is the normalised branch name, and `counter`
label, `identifier` is the normalized branch name, and `counter`
auto-increments per push.
- Artifact-specific conventions replace the SemVer suffix where they exist
(`-alpha.N` for npm, `.devN` for Python). Release candidates use `-rc.N`,
Expand All @@ -114,10 +145,10 @@ release-paths:

## Release notes

The GitHub Release **name** is the version; the **body** depends on the trigger:
`# <PR title>` + description (merged PR), `# <first commit line>` + remainder
(direct push), or `# <summary>` + collected history (dispatch). The same note is
handed to [Downstream Release Propagation](../downstream-release-propagation/design.md).
The GitHub Release **name** is the version. Its **body** is the pull request title
and description for the standard merge path, or the required release-note summary
for an optional ad hoc dispatch. The same note is handed to
[Downstream Release Propagation](../downstream-release-propagation/design.md).

## Release output

Expand Down Expand Up @@ -212,7 +243,8 @@ release, and its runs are serialised like any other.
| Surface | Where |
| --- | --- |
| Release branches + type | `.github/release.config.yml` |
| Bump label / prerelease / RC | PR label, or `workflow_dispatch` input |
| Release decision / prerelease / RC | `release:` PR label |
| Optional ad hoc release | `workflow_dispatch` inputs |
| Path filter | `.github/release.config.yml` |
| Prerelease cleanup toggle | release config / workflow input |
| Publishing targets | reusable-workflow input + GitHub environment; see [Publishing Targets](design-publishing-targets.md) |
Expand Down
7 changes: 4 additions & 3 deletions src/docs/Capabilities/release-management/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ description: How a source change becomes a versioned, immutable artifact, driven

Turning a merged change into a versioned, immutable artifact — a container
image, a GitHub Action or reusable workflow, a language package, a Terraform
module — paired with a GitHub Release and a git tag, driven entirely by
pull-request labels. No release CLI, no hand-edited version file, no tagging
ritual.
module — paired with a GitHub Release and a git tag, normally driven by
pull-request labels. An implementation may add a GitHub-native ad hoc release
path when its product needs one. No release CLI, no hand-edited version file,
no tagging ritual.

<!-- INDEX:START -->

Expand Down
21 changes: 12 additions & 9 deletions src/docs/Capabilities/release-management/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,21 @@ this capability governs the release. If no, there is nothing to release.
## Requirements

- **Semantic versioning.** Versions follow [SemVer 2.0.0](https://semver.org/) (`vMAJOR.MINOR.PATCH`), derived automatically — never written by hand.
- **Label-driven bump, stated explicitly.** The bump level is a pull-request label — `release:major` / `release:minor` / `release:patch` / `release:none`. Exactly one bump label MUST be present, and there is **no default**: an unlabelled pull request is not releasable, and the release fails closed rather than assuming the smallest bump. Requiring the label makes the versioning decision a reviewed decision instead of an omission. Conventional commit messages are **not** required.
- **A release per merge.** One merged PR to a release branch is one release, and the PR review gate is the release gate. Direct pushes and manual dispatch also release.
- **Namespaced release decision, stated explicitly.** Release automation reads only the `release:` namespace. Exactly one of `release:patch`, `release:minor`, `release:major`, or `release:skip` MUST be present, and there is **no default**: an unlabeled pull request is not releasable, and the release fails closed rather than assuming the smallest bump. `release:pre-release` MAY accompany exactly one bump label on an open pull request and MUST NOT be combined with `release:skip`. Bare `patch`, `minor`, and `major` labels are not release decisions. Requiring an owned label makes versioning a reviewed decision instead of an omission. Conventional commit messages are **not** required.
- **A release per merge.** One merged PR carrying `release:patch`, `release:minor`, or `release:major` to a release branch is one release, and the PR review gate is the release gate. `release:skip` validates without publishing. This pull-request path is the required release interface.
- **Ad hoc release is optional.** An implementation MAY expose `workflow_dispatch` when its product needs an ad hoc release outside the merge flow; implementations are not required to support it. A dispatch MUST require an explicit release decision and release-note context, and MUST use the same version, build, validation, immutability, and publication controls as a merged pull request. A direct push MUST NOT be an ad hoc release interface, and an empty pull request MUST NOT be created solely to trigger a release.
- **Version before build.** The version MUST be resolved before the artifact is built, so the version is part of the artifact's identity rather than a label attached afterwards.
- **Build once.** The artifact MUST be built exactly once and MUST NOT be altered after it is built. The same bytes flow through validation and publishing. Rebuilding to publish means the tested artifact and the published artifact are different artifacts.
- **Stable and prerelease.** Every release is either **stable** (the latest version to adopt) or a **prerelease** (testable, not promoted to latest). A prerelease MUST be obtainable from an open pull request and/or from a prerelease branch.
- **Stable and prerelease.** Every release is either **stable** (the latest version to adopt) or a **prerelease** (testable, not promoted to latest). A prerelease MUST be obtainable from an open pull request carrying `release:pre-release` and a bump label and/or from a prerelease branch.
- **Serialised releases.** Only one release process runs against a given version of the codebase (the same ref) at a time. A release mutates shared, version-anchored state — the tag, the version counter, the published artifact — so overlapping runs on the same ref MUST NOT race, and an in-flight release is never interrupted.
- **A single production authority.** Exactly one branch is in charge of the production (stable) version, so consumers get one unambiguous latest stable release and two branches can never publish competing production releases.
- **Notes from the contributor's own words.** The GitHub Release name is the version; its body is assembled from material the contributor already wrote (PR title + description, or commit message, or collected history). The PR description is therefore written for consumers.
- **Only artifact-affecting changes release.** A change that does not flow into the artifact (documentation, CI config) MUST NOT produce a release — though validation still runs on every merge.
- **Notes from the contributor's own words.** The GitHub Release name is the version; its body comes from the pull request title and description, or from the required release-note context of an optional ad hoc dispatch. The PR description is therefore written for consumers.
- **Only artifact-affecting changes release.** A change that does not flow into the artifact (documentation, CI config) MUST carry `release:skip` and MUST NOT produce a release — though validation still runs on every merge.
- **Immutable references.** Consumers pin to the most immutable reference available — a container digest or a commit SHA — never a mutable tag.
- **Publish through a target contract.** Every publishing destination is reached through the same [publishing-target contract](design-publishing-targets.md), so the release process stays one process regardless of how many destinations a repository has. Adding a destination supplies a contract and a publish step; it MUST NOT change the release process.
- **All-or-nothing across targets.** Where a repository publishes one artifact to more than one destination, a version MUST NOT end up present on some destinations and absent from others. Partial publication is a failure, reported as one, and resumed by completing the remaining destinations with the same immutable artifact and version.
- **Recovery distinguishes retries from changed output.** Retrying validation or publication of unchanged bytes MUST reuse their artifact and version. A correction that changes the bytes MUST create a new versioned artifact; an existing version is never overwritten or reused.
- **Standard GitHub primitives only.** Pull requests, labels, comments, and workflow dispatch — no external tooling beyond `gh` and GitHub Actions.
- **Standard GitHub primitives only.** Pull requests, labels, comments, and, where implemented, workflow dispatch — no external tooling beyond `gh` and GitHub Actions.

### Consumer update policies

Expand All @@ -62,10 +63,11 @@ Because versions are semantic, immutable, and published once, a consumer can ado

## Success criteria

- Merging a labelled PR to a release branch produces a GitHub Release, a git tag, and (where one exists) a published artifact, with no manual step.
- The version bump matches the PR's label every time; a missing, conflicting, or ambiguous label set is **rejected**, never guessed.
- Merging a PR with exactly one namespaced bump label to a release branch produces a GitHub Release, a git tag, and (where one exists) a published artifact, with no manual step.
- The version bump matches the PR's `release:` label every time; a missing, conflicting, ambiguous, or bare label set is **rejected**, never guessed.
- An open pull request carrying `release:pre-release` and exactly one bump label publishes a prerelease without promoting it to latest.
- The artifact that consumers download is byte-identical to the artifact that passed validation.
- A documentation-only merge produces no new version but still runs its CI checks.
- A documentation-only merge carrying `release:skip` produces no new version but still runs its CI checks.
- Two release runs for the same ref never overlap; the second waits for the first to finish rather than racing it.
- Only the single production branch ever publishes a stable release.
- A version that reaches one publishing target reaches all of them, or the release is reported as failed.
Expand All @@ -76,5 +78,6 @@ Because versions are semantic, immutable, and published once, a consumer can ado
- [Design](design.md) — how these requirements are delivered.
- [Publishing Targets](design-publishing-targets.md) — the contract each destination documents.
- [Documentation Model](../../Ways-of-Working/Documentation-Model.md) — why this spec holds only the why and the what.
- [Automation Labels](../../Ways-of-Working/Automation-Labels.md) — why release labels are owned by the `release:` namespace.
- [PR Format](../../Ways-of-Working/PR-Format.md) — the change-type labels that drive the bump.
- [Dependency Updates](../dependency-updates/spec.md) — update PRs are artifact-affecting and release through this capability.
16 changes: 8 additions & 8 deletions src/docs/Capabilities/vscode-extension-framework/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,16 @@ pass before the release stage runs, alongside a green test result.
Versioning is [Release Management](../release-management/design.md) applied to a
VSIX artifact — this framework does not re-implement it:

- The bump is the PR label (`release:major` / `release:minor` / `release:patch` / `release:none`,
defaulting to `release:patch`); multiple SemVer labels, or a SemVer label with
`release:none`, are rejected.
- The release decision is exactly one of `release:patch`, `release:minor`,
`release:major`, or `release:skip`, with no default. Multiple bump labels and
`release:skip` with another release label are rejected.
- The version is computed once and stamped into the manifest; it is never
hand-edited.
- A prerelease is requested by a `Prerelease` label on an open pull request (or
a prerelease branch), producing a prerelease VSIX that is never promoted to
latest. When such a build is also published to the VS Code Marketplace, it goes
out with `@vscode/vsce publish --pre-release` and an odd minor-version number,
the Marketplace's pre-release-channel convention.
- A prerelease is requested by `release:pre-release` alongside one bump label on
an open pull request (or by a prerelease branch), producing a prerelease VSIX
that is never promoted to latest. When such a build is also published to the
VS Code Marketplace, it goes out with `@vscode/vsce publish --pre-release` and
an odd minor-version number, the Marketplace's pre-release-channel convention.

## Publishing and distribution

Expand Down
6 changes: 3 additions & 3 deletions src/docs/Capabilities/vscode-extension-framework/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ framework itself.
- **Tested on a real host.** The extension is tested against a real VS Code host, across the host versions and operating systems the extension declares as supported. Tests exercise the exact bundle that ships — never a separately compiled copy.
- **A static quality gate.** Every change is linted and type-checked, and the pipeline holds on any error. Quality is validated at pull-request time, not after merge.
- **Built once, shipped once.** The version is computed once, stamped into the manifest, and the same packaged VSIX is what is tested and what is published. Build, test, and release MUST NOT diverge.
- **Label-driven, semantic versioning.** Versioning follows [Release Management](../release-management/spec.md): the bump is a pull-request label (`release:major` / `release:minor` / `release:patch` / `release:none`, defaulting to `release:patch`), the version is [SemVer](https://semver.org/), and it is derived automatically — never hand-edited in the manifest.
- **Label-driven, semantic versioning.** Versioning follows [Release Management](../release-management/spec.md): exactly one of `release:patch`, `release:minor`, `release:major`, or `release:skip` records the release decision with no default; `release:pre-release` MAY accompany one bump label. The version is [SemVer](https://semver.org/) and is derived automatically — never hand-edited in the manifest.
Comment thread
MariusStorhaug marked this conversation as resolved.
- **An installable artifact on every release.** Each release produces an installable VSIX attached to its [GitHub Release](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases), together with an immutable reference. A user MUST be able to install a specific released version without a marketplace account.
- **Optional marketplace publication.** Where configured, the same VSIX is also published to an extension marketplace (the VS Code Marketplace and/or Open VSX). Marketplace publication is opt-in and MUST NOT be a prerequisite for the GitHub-Release install path.
- **A prerelease from an open pull request.** A prerelease VSIX MUST be obtainable from an open pull request for testing before merge, without being promoted to the latest stable version.
Expand All @@ -72,9 +72,9 @@ framework itself.
## Success criteria

- Creating a repository from the template and pushing a first change yields a green build, a passing test run, and a packaged VSIX with no configuration written.
- A labelled pull request merged to a release branch produces a GitHub Release carrying an installable VSIX whose version matches the label's bump — a conflicting or ambiguous label set is rejected, never guessed.
- A labeled pull request merged to a release branch produces a GitHub Release carrying an installable VSIX whose version matches the label's bump — a conflicting or ambiguous label set is rejected, never guessed.
- The tests that gate the release exercise the exact VSIX that is released, on every supported host version and operating system.
- A documentation-only or CI-only change runs its checks but produces no new version.
- A documentation-only or CI-only change carrying `release:skip` runs its checks but produces no new version.
- A user installs any released version straight from its GitHub Release with no marketplace account; where marketplace publishing is enabled, that same version also appears in the marketplace.
- An open pull request can publish a prerelease VSIX for testing that never becomes the latest stable version, and it is cleaned up when the pull request closes.
- Adopting the framework in a new extension is a short caller plus a settings file — the pipeline itself is never copied into the repository.
Expand Down
Loading
Loading