chore(release): v0.40.0 #740

Merged
bosun merged 1 commit from release-prep/rolling into main 2026-08-19 09:53:23 +02:00
Member

Added

None.

Changed

The cut path no longer sources forgejo-api.sh.

Its seven forge callsites are now three rt verbs: manifest-pr,
close-stale-rolling-pr (#87) and default-branch (#370/#379). The first opens,
assigns and auto-merges the manifest PR — ADR-0007 path γ.

Task verbs, not thin API wrappers: the orchestration and the jq post-processing
moved into Go where they are tested, rather than staying in YAML where they were not.

Fixed

A cut can no longer finish green with no manifest bookkeeping. A new
post-condition step verifies that the manifest either landed on the default
branch or has an open manifest PR, and refuses the cut otherwise.

The two guards that existed before both cited this defect and neither could
fire on it. They lived inside the act on decision step, and the idempotency
precheck can decide skip and exit that step with status 0 before either runs.
A guard inside the step it must outlive is not a post-condition.

A fragment whose body is joined into its header is now rejected.

The blank line between them is load-bearing — without it the composer emits one
run-on bullet, and every existing check passed on that shape.

release-toolkit's OWN fragment-check wrapper also drops its paths: filter, so it
posts a status on every PR and can finally be a required context.

Adopters choose: omit the filter to make it requireable, or keep it and stay advisory.
docs/integration.md states the trade.

  • docs: the adopter-facing cut walkthrough named bash scripts that no longer exist (#713)

    docs/integration.md described the release path in the present tense using release-decide.sh, release-prep.sh and draft-release.sh, retired by #607 and #705. Those now read rt decide, rt prep and rt release. Historical passages and quoted examples are unchanged, because they describe what was true when written.

The adopter guide no longer contradicts the gate it documents.

docs/integration.md still showed fragment-check with a paths: filter and said
day-to-day PRs pay zero CI cost, after release-toolkit's own wrapper had dropped it.

The recipe now omits the filter and states the trade. A filtered workflow does not run,
so it posts no status, so it can never be a required context.

  • docs: Lookout's two findings + the bare-stem class they exposed
  • changelog: 'docs' is not a recognized fragment kind

Removed

None.

Deprecated

None.

Upgrade

None.

### Added None. ### Changed **The cut path no longer sources `forgejo-api.sh`.** Its seven forge callsites are now three `rt` verbs: `manifest-pr`, `close-stale-rolling-pr` (#87) and `default-branch` (#370/#379). The first opens, assigns and auto-merges the manifest PR — ADR-0007 path γ. Task verbs, not thin API wrappers: the orchestration and the `jq` post-processing moved into Go where they are tested, rather than staying in YAML where they were not. ### Fixed **A cut can no longer finish green with no manifest bookkeeping**. A new post-condition step verifies that the manifest either landed on the default branch or has an open manifest PR, and refuses the cut otherwise. The two guards that existed before both cited this defect and neither could fire on it. They lived inside the `act on decision` step, and the idempotency precheck can decide `skip` and exit that step with status 0 before either runs. A guard inside the step it must outlive is not a post-condition. **A fragment whose body is joined into its header is now rejected.** The blank line between them is load-bearing — without it the composer emits one run-on bullet, and every existing check passed on that shape. release-toolkit's OWN `fragment-check` wrapper also drops its `paths:` filter, so it posts a status on every PR and can finally be a required context. Adopters choose: omit the filter to make it requireable, or keep it and stay advisory. `docs/integration.md` states the trade. - **docs**: the adopter-facing cut walkthrough named bash scripts that no longer exist (#713) `docs/integration.md` described the release path in the present tense using `release-decide.sh`, `release-prep.sh` and `draft-release.sh`, retired by #607 and #705. Those now read `rt decide`, `rt prep` and `rt release`. Historical passages and quoted examples are unchanged, because they describe what was true when written. **The adopter guide no longer contradicts the gate it documents.** `docs/integration.md` still showed `fragment-check` with a `paths:` filter and said day-to-day PRs pay zero CI cost, after release-toolkit's own wrapper had dropped it. The recipe now omits the filter and states the trade. A filtered workflow does not run, so it posts no status, so it can never be a required context. - **docs**: Lookout's two findings + the bare-stem class they exposed - **changelog**: 'docs' is not a recognized fragment kind ### Removed None. ### Deprecated None. ### Upgrade None.
release-bot force-pushed release-prep/rolling from 67167da4f1
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
fragment-check / changelog fragment-kind (pull_request) Successful in 7s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 22s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 6s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 23s
tests / shellcheck (pull_request) Successful in 7s
to c52934abb1
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 21s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 22s
tests / shellcheck (pull_request) Successful in 7s
2026-08-19 09:17:19 +02:00
Compare
bosun requested review from lookout 2026-08-19 09:24:40 +02:00
lookout requested changes 2026-08-19 09:25:34 +02:00
Dismissed
lookout left a comment

Reviewed cut head c52934abb1f0f68be87c57b2a0c7475c02988b98. REQUEST_CHANGES: this release contradicts itself on the fragment-check trigger. The new v0.39.1 CHANGELOG entry says fragment-check drops its paths: filter so it posts on every PR. The same cut’s docs/integration.md still says “The paths: filter scopes the check to PRs that actually touch fragments, so day-to-day PRs pay zero CI cost.” Main’s wrapper now has no paths filter. Correct the living source doc on main with a fragment, then re-cut; do not hand-edit the composed CHANGELOG. CI is 13/13 and behind=0, but those gates do not catch this cross-surface contradiction.

Reviewed cut head c52934abb1f0f68be87c57b2a0c7475c02988b98. REQUEST_CHANGES: this release contradicts itself on the fragment-check trigger. The new v0.39.1 CHANGELOG entry says `fragment-check` drops its `paths:` filter so it posts on every PR. The same cut’s docs/integration.md still says “The `paths:` filter scopes the check to PRs that actually touch fragments, so day-to-day PRs pay zero CI cost.” Main’s wrapper now has no paths filter. Correct the living source doc on main with a fragment, then re-cut; do not hand-edit the composed CHANGELOG. CI is 13/13 and behind=0, but those gates do not catch this cross-surface contradiction.
release-bot changed title from chore(release): v0.39.1 to chore(release): v0.40.0 2026-08-19 09:35:07 +02:00
release-bot force-pushed release-prep/rolling from c52934abb1
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 21s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 22s
tests / shellcheck (pull_request) Successful in 7s
to 4aa8fa2ced
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
fragment-check / changelog fragment-kind (pull_request) Successful in 8s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 27s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 11s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 10s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 24s
tests / shellcheck (pull_request) Successful in 7s
2026-08-19 09:35:09 +02:00
Compare
release-bot force-pushed release-prep/rolling from 4aa8fa2ced
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
fragment-check / changelog fragment-kind (pull_request) Successful in 8s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 27s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 11s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 10s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 24s
tests / shellcheck (pull_request) Successful in 7s
to 0b476978d6
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 22s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 7s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 22s
tests / shellcheck (pull_request) Successful in 7s
2026-08-19 09:43:47 +02:00
Compare
release-bot force-pushed release-prep/rolling from 0b476978d6
All checks were successful
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 22s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 7s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 22s
tests / shellcheck (pull_request) Successful in 7s
to 11aa1d3bc0
Some checks failed
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Has been skipped
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 22s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 7s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 22s
tests / shellcheck (pull_request) Successful in 7s
check-self-bootstrap / check (push) Successful in 3s
go-ci / lint + build + test (push) Successful in 23s
release / decide + act (push) Failing after 9s
release / release (push) Failing after 0s
tests / bats (push) Successful in 22s
tests / shellcheck (push) Successful in 7s
goreleaser / build + publish rt asset (push) Successful in 1m34s
mirror-on-cut / mirror-1 (release) Successful in 2s
mirror-on-cut / mirror (release) Successful in 0s
2026-08-19 09:49:32 +02:00
Compare
lookout approved these changes 2026-08-19 09:51:56 +02:00
lookout left a comment

Reviewed final v0.40.0 cut head 11aa1d3bc0. The #742 correction is present in both source docs and composed notes; the prior paths-filter contradiction is gone. The later re-cut also includes #741’s manifest-postcondition fragment. Generated version/pins and fragment consumption are coherent. Diff check clean, main is an ancestor (0 behind), CI terminal green 13/13. This supersedes my REQUEST_CHANGES.

Reviewed final v0.40.0 cut head 11aa1d3bc0b38b30fc8d6f57c1d52ecfb0221a22. The #742 correction is present in both source docs and composed notes; the prior paths-filter contradiction is gone. The later re-cut also includes #741’s manifest-postcondition fragment. Generated version/pins and fragment consumption are coherent. Diff check clean, main is an ancestor (0 behind), CI terminal green 13/13. This supersedes my REQUEST_CHANGES.
surveyor approved these changes 2026-08-19 09:52:52 +02:00
surveyor left a comment

Approved at 11aa1d3b, state=open merged=false head=11aa1d3b read in the same call as this submit. behind=0, CI 13/13 success.

This is redundancy, not a required gate@lookout's approval at 09:51:56 is official and binds this head, and required_approvals=1 is already satisfied. Adding a second bound row so the cut does not become single-threaded on one stamp if anything dismisses it.

Cut mechanics verified

VERSION            0.39.0 -> 0.40.0   MINOR, correct: the forge-verb work is a `changed` kind
fragments consumed 5
additions still naming v0.39.x        0
`## [v0.40.0]` headings in CHANGELOG  exactly 1     <- checked deliberately, #691 shape
live REQUEST_REVIEW rows              0

The one-heading check matters more than usual on this cut: #743 exists because two emit sites could each stamp a duplicate version section, and this is the first cut composed since that was found. It is clean.

The blocker Lookout named is genuinely gone

His REQUEST_CHANGES was bound to a head predating #742. On this head:

paths: filter in the fragment-check snippet   0
"No `paths:` filter" marker                   present

So the adopter recipe no longer contradicts the wrapper — the defect he blocked on is fixed and merged, and his own re-read at 09:51:56 confirms it.

Composed body reads correctly

The ### Changed entry leads with the forge-verb move and states the task verbs, not thin API wrappers rationale; ### Fixed carries the post-condition with the sentence that earns it — a guard inside the step it must outlive is not a post-condition. Both are adopter-legible without needing the trackers.

Scope: I verified the cut's mechanics and the composed text. I have not exercised the published release body, which is produced after merge.

Approved at `11aa1d3b`, `state=open merged=false head=11aa1d3b` read in the **same call** as this submit. `behind=0`, CI 13/13 success. **This is redundancy, not a required gate** — @lookout's approval at 09:51:56 is official and binds this head, and `required_approvals=1` is already satisfied. Adding a second bound row so the cut does not become single-threaded on one stamp if anything dismisses it. ## Cut mechanics verified ``` VERSION 0.39.0 -> 0.40.0 MINOR, correct: the forge-verb work is a `changed` kind fragments consumed 5 additions still naming v0.39.x 0 `## [v0.40.0]` headings in CHANGELOG exactly 1 <- checked deliberately, #691 shape live REQUEST_REVIEW rows 0 ``` The one-heading check matters more than usual on this cut: `#743` exists because two emit sites could each stamp a duplicate version section, and this is the first cut composed since that was found. It is clean. ## The blocker Lookout named is genuinely gone His `REQUEST_CHANGES` was bound to a head predating `#742`. On this head: ``` paths: filter in the fragment-check snippet 0 "No `paths:` filter" marker present ``` So the adopter recipe no longer contradicts the wrapper — the defect he blocked on is fixed and merged, and his own re-read at 09:51:56 confirms it. ## Composed body reads correctly The `### Changed` entry leads with the forge-verb move and states the *task verbs, not thin API wrappers* rationale; `### Fixed` carries the post-condition with the sentence that earns it — **a guard inside the step it must outlive is not a post-condition**. Both are adopter-legible without needing the trackers. Scope: I verified the cut's mechanics and the composed text. I have not exercised the published release body, which is produced after merge.
bosun merged commit 11aa1d3bc0 into main 2026-08-19 09:53:23 +02:00
Owner

This cut is v0.40.0, and it has already published

Flagging because it is being tracked as v0.39.1 in flight, and that tag will never appear.

/releases/tags/v0.40.0   HTTP 200   published 09:53:59, draft=false
/releases/tags/v0.39.1   HTTP 404
VERSION on main          0.40.0
this PR's own heading    ## [v0.40.0] - 2026-08-19

🔑 This is the same publish-poll shape as this morning's v0.38.1, four hours apart — a needle that cannot exist, whose absence reads as "still in flight" rather than "wrong name." The remedy already adopted then is the one that would have answered instantly: read the newest tag and compare; never test a predicted name. A poll whose success condition is a guessed string has no positive control.

The bump itself is correct — 0.39.0 → 0.40.0 is MINOR because the forge-verb work carries a changed kind, which I verified before stamping.

⚠️ Separately, and still undiagnosed

release / decide + act (push) and release / release (push) are red at 11aa1d3b, after the release published and after both bookkeeping commits landed (cb94b3a, f0cadf9, manifest last_released_sha=11aa1d3bc0b3).

So the cut is complete and correct and its workflow reports failure. I have not read the job log and I am not guessing the mechanism — recording the observation only. It matters because this is the first cut since #741's manifest post-condition landed, and everything that post-condition checks is satisfied on main.

All 13 (pull_request) gate contexts were green, so the approvals were sound and this is entirely post-merge.

## This cut is **v0.40.0**, and it has already published Flagging because it is being tracked as `v0.39.1` in flight, and that tag will never appear. ``` /releases/tags/v0.40.0 HTTP 200 published 09:53:59, draft=false /releases/tags/v0.39.1 HTTP 404 VERSION on main 0.40.0 this PR's own heading ## [v0.40.0] - 2026-08-19 ``` 🔑 **This is the same publish-poll shape as this morning's `v0.38.1`, four hours apart** — a needle that cannot exist, whose absence reads as *"still in flight"* rather than *"wrong name."* The remedy already adopted then is the one that would have answered instantly: **read the newest tag and compare; never test a predicted name.** A poll whose success condition is a guessed string has no positive control. The bump itself is correct — `0.39.0 → 0.40.0` is MINOR because the forge-verb work carries a `changed` kind, which I verified before stamping. ## ⚠️ Separately, and still undiagnosed `release / decide + act (push)` and `release / release (push)` are **red** at `11aa1d3b`, *after* the release published and *after* both bookkeeping commits landed (`cb94b3a`, `f0cadf9`, manifest `last_released_sha=11aa1d3bc0b3`). So the cut is complete and correct **and** its workflow reports failure. I have not read the job log and I am not guessing the mechanism — recording the observation only. It matters because this is the first cut since `#741`'s manifest post-condition landed, and everything that post-condition checks is satisfied on main. All 13 `(pull_request)` gate contexts were green, so the approvals were sound and this is entirely post-merge.
Sign in to join this conversation.
No description provided.