diff --git a/src/docs/Capabilities/release-management/design.md b/src/docs/Capabilities/release-management/design.md index ad7a1c6..f02ce15 100644 --- a/src/docs/Capabilities/release-management/design.md +++ b/src/docs/Capabilities/release-management/design.md @@ -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` 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`, @@ -114,10 +145,10 @@ release-paths: ## Release notes -The GitHub Release **name** is the version; the **body** depends on the trigger: -`# ` + description (merged PR), `# ` + remainder -(direct push), or `# ` + 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 @@ -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) | diff --git a/src/docs/Capabilities/release-management/index.md b/src/docs/Capabilities/release-management/index.md index cc4d98a..de03cc5 100644 --- a/src/docs/Capabilities/release-management/index.md +++ b/src/docs/Capabilities/release-management/index.md @@ -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. diff --git a/src/docs/Capabilities/release-management/spec.md b/src/docs/Capabilities/release-management/spec.md index 62829a3..1f5252f 100644 --- a/src/docs/Capabilities/release-management/spec.md +++ b/src/docs/Capabilities/release-management/spec.md @@ -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 @@ -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. @@ -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. diff --git a/src/docs/Capabilities/vscode-extension-framework/design.md b/src/docs/Capabilities/vscode-extension-framework/design.md index feb5197..c78343c 100644 --- a/src/docs/Capabilities/vscode-extension-framework/design.md +++ b/src/docs/Capabilities/vscode-extension-framework/design.md @@ -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 diff --git a/src/docs/Capabilities/vscode-extension-framework/spec.md b/src/docs/Capabilities/vscode-extension-framework/spec.md index cfb5f8b..2e99954 100644 --- a/src/docs/Capabilities/vscode-extension-framework/spec.md +++ b/src/docs/Capabilities/vscode-extension-framework/spec.md @@ -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. - **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. @@ -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. diff --git a/src/docs/Coding-Standards/Natural-Language.md b/src/docs/Coding-Standards/Natural-Language.md index 5d9f4b1..3e947fc 100644 --- a/src/docs/Coding-Standards/Natural-Language.md +++ b/src/docs/Coding-Standards/Natural-Language.md @@ -76,7 +76,7 @@ Prefer specific, observable language over vague intent. | Prefer | Avoid | | --- | --- | | The workflow fails when the version label is missing. | There may be some issues with labels. | -| Add `release:none` to documentation-only PRs. | Make sure docs PRs are handled correctly. | +| Add `release:skip` to documentation-only PRs. | Make sure docs PRs are handled correctly. | | The agent reads `AGENTS.md` before editing files. | The agent should probably look at the instructions. | Use **MUST**, **SHOULD**, and **MAY** only when a sentence is intentionally normative. If a rule is optional, say what trade-off decides it. @@ -155,7 +155,7 @@ A good error message includes: Prefer: ```text -Release label is missing. Add exactly one of release:major, release:minor, release:patch, or release:none. +Release label is missing. Add exactly one of release:patch, release:minor, release:major, or release:skip. Add release:pre-release only with a bump label. ``` Avoid: diff --git a/src/docs/Ways-of-Working/Automation-Labels.md b/src/docs/Ways-of-Working/Automation-Labels.md index 6629af6..47c5112 100644 --- a/src/docs/Ways-of-Working/Automation-Labels.md +++ b/src/docs/Ways-of-Working/Automation-Labels.md @@ -46,20 +46,24 @@ signal. ## Every set is namespaced There is no reserved unprefixed vocabulary. Every label set an automation reads is -namespaced, including the release bump set: +namespaced, including the release set: -```text -release:major release:none -release:minor -release:patch -``` +| Label | Instruction | +| --- | --- | +| `release:patch` | Publish a patch release. | +| `release:minor` | Publish a minor release. | +| `release:major` | Publish a major release. | +| `release:pre-release` | Publish a prerelease from the open pull request. | +| `release:skip` | Validate the change without publishing a release. | -`release:major`, `release:minor`, `release:patch`, and `release:none` are read by +These labels are read by [release management](../Capabilities/release-management/spec.md) and by nothing else. +Exactly one bump label or `release:skip` records the release decision. +`release:pre-release` is an optional mode used with exactly one bump label, never +with `release:skip`. -Reserving bare words would be the weaker mechanism, because it depends on a documented -prohibition rather than on the label's own name — and for the release set specifically, -the prohibition is not the only thing at stake. +Reserving bare words would be weaker because it depends on a documented +prohibition rather than making ownership visible in the label itself. ## Automation ignores what it does not own diff --git a/src/docs/Ways-of-Working/PR-Format.md b/src/docs/Ways-of-Working/PR-Format.md index 2b1b834..04054be 100644 --- a/src/docs/Ways-of-Working/PR-Format.md +++ b/src/docs/Ways-of-Working/PR-Format.md @@ -40,8 +40,12 @@ Pull requests in the MSX ecosystem double as **release notes**. The description | Feature | 🚀 | `release:minor` | New features or enhancements | | Patch | 🩹 | `release:patch` | Small fixes or improvements | | Fix | 🪲 | `release:patch` | Bugfixes (patch-level release impact) | -| Docs | 📖 | `release:none` | Documentation changes only | -| Maintenance | ⚙️ | `release:none` | CI/CD, build configs, AI/agent files, internal upkeep | +| Docs | 📖 | `release:skip` | Documentation changes only | +| Maintenance | ⚙️ | `release:skip` | CI/CD, build configs, AI/agent files, internal upkeep | + +`release:pre-release` is a release mode, not a change type. Apply it alongside +exactly one of `release:patch`, `release:minor`, or `release:major` when an open +pull request must publish a prerelease. Never combine it with `release:skip`. ### Detecting the change type