diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d006e62..e4c0dcd5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **A fix for a regression on stable can skip the 72-hour wait, like a security fix.** Garry, asked whether non-security breakage on stable may bypass the twice-weekly cadence and the floor, answered on 2026-10-28: "Regression". Until now a fix for something the last stable release did right, and the current one breaks, waited up to 72 hours on every team install. Now `--regression "#n: worked in v: why"` with `--ref` passes the floor once the tool ties it to the release, and the claim is public in the tag and the notes. ### Added -- **Regression cuts in `scripts/release-cut.ts`: `--regression ""` with `--ref `** (workflow input `regression`). The reason must name exactly one earlier stable release `v` and at least one `#`. The cut is refused with `REFUSED_REGRESSION`, red on every trigger, unless `v` is an annotated release tag strictly behind the current stable (the current stable itself never counts), `#` carries the `regression` label (a maintainer's declaration; a `security` label does not count, nor the reverse) and a released commit references `#`. That the behavior really worked in `v` stays attested. The record matches a security cut. The sanitized reason and evidence go into the tag (`Regression:` / `Verified:`), the notes (`(regression release)`, `**Regression release:** …`) and the plan (`REGRESSION:`, `RIDING_ALONG:`); a half-done cut finishes from its tag; an unmoved regression tag must verify again. +- **Regression cuts in `scripts/release-cut.ts`: `--regression ""` with `--ref `** (workflow input `regression`). The reason must name exactly one earlier stable release `v` and at least one `#`. The cut is refused with `REFUSED_REGRESSION`, red on every trigger, unless `v` is an annotated release tag strictly behind the current stable with a published GitHub Release (the current stable itself never counts, nor a tag a half-done cut left behind), `#` carries the `regression` label (a maintainer's declaration; a `security` label does not count, nor the reverse) and a released commit references `#`. That the behavior really worked in `v` stays attested. The record matches a security cut. The sanitized reason and evidence go into the tag (`Regression:` / `Verified:`), the notes (`(regression release)`, `**Regression release:** …`) and the plan (`REGRESSION:`, `RIDING_ALONG:`); a half-done cut finishes from its tag; an unmoved exception tag must verify again with the reason it recorded (this now holds for security cuts too). ### Changed - **One exception per cut.** `--security` and `--regression` together are `REFUSED_REGRESSION`. A regression cut passes a dated floor only; `FLOOR_UNKNOWN` (an undatable or unpublished stable) still yields to a security cut alone. The floor counts from any exception release, so the next scheduled cut may be held. `REFUSED_FLOOR` names both ways past it. diff --git a/docs/releases.md b/docs/releases.md index 8a0210218..de38b8ff4 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -159,7 +159,7 @@ A security cut that stopped half-way after moving `stable` is finished by the next run with the reason and evidence its tag recorded, without checking them again. A tag that records a security cut but never moved `stable` gets no such pass: the cut is refused (`REFUSED_SECURITY`) until it runs again -with `security` and `ref` and verifies. The floor then counts from the security +with `security`, `ref` and the reason its tag recorded, and verifies. The floor then counts from the security release, and the next regular cut's notes list only the versions after it. ## Regression releases @@ -180,15 +180,18 @@ The reason must name exactly one release `v` and at least one `#`. All of these must hold, or the cut is `REFUSED_REGRESSION` with what it checked: -1. `v` is an annotated release tag strictly behind the current `stable`, - an earlier stable release team installs actually ran. The current stable +1. `v` is an annotated release tag strictly behind the current `stable` + whose GitHub Release is published (not a draft): an earlier stable release + team installs actually ran, which a tag left behind by a half-done cut is + not. The current stable itself does not count, since a fix for something it still does right needs no exception. 2. `#` carries the `regression` label (a `security` label does not count, nor the reverse) and a commit in `stable`..`ref` references it (the squash subject `(#)` does). -**Verified:** the release tag and its place behind `stable`, the label, and +**Verified:** the release tag, its place behind `stable` and its published +release, the label, and that the labelled issue or PR is in the released span. **Attested, not verified:** that the behavior really worked in `v` and that this change restores it; the label and the public reason carry that claim. @@ -199,7 +202,8 @@ rides along (`RIDING_ALONG:`), and the sanitized reason and evidence go into the tag message (`Regression:` / `Verified:`) and the notes (``**Regression release:** `` (verified: )``). A half-done regression cut finishes from its tag's record, and a regression tag that never -moved `stable` is refused until it runs again with `regression` and verifies. +moved `stable` is refused until it runs again with `regression` and the reason +its tag recorded, and verifies. Unlike a security cut, a regression cut does not pass `FLOOR_UNKNOWN`. The floor counts from the regression release, so the next scheduled cut may be held. @@ -307,8 +311,8 @@ is exercised against real check runs before it merges. | `REFUSED_FLOOR: stable moved to v at