• v0.44.0 347028a2e5

    v0.44.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    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 24s
    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 / workflow-schema (pull_request) Successful in 3s
    tests / bats (pull_request) Successful in 7s
    tests / shellcheck (pull_request) Successful in 3s
    check-self-bootstrap / check (push) Successful in 6s
    go-ci / lint + build + test (push) Successful in 24s
    release / decide + act (push) Successful in 8s
    release / release (push) Successful in 0s
    tests / workflow-schema (push) Successful in 5s
    tests / bats (push) Successful in 7s
    tests / shellcheck (push) Successful in 3s
    goreleaser / build + publish rt asset (push) Successful in 1m18s
    goreleaser / publish the rt image + bake its digest (push) Failing after 3s
    goreleaser / adopters can pull the published image (push) Has been skipped
    goreleaser / adopters can fetch the published asset (push) Failing after 3s
    Stable

    release-bot released this 2026-08-25 12:02:46 +02:00 | 701 commits to main since this release

    Added

    None.

    Changed

    Duplicate-version refusal: the ## [vX.Y.Z] already present error now names what it did NOT check.

    It detects a duplicate heading and nothing more. The tag may exist. The release may have published. The section's body may be incomplete. None of that is verified.

    Its "did not tag it" clause names a likely cause, not a measured one — a hand-edited CHANGELOG reaches the same state.

    Applied at both emit sites together (#833, #659).

    Fixed

    Release prep: a second prep run for a version already in the changelog now merges into that section instead of refusing the cut.

    The transition transforms ## [Unreleased] into a version heading. It never scanned for an existing one, so a second run emitted a duplicate.

    Refusing would strand real content. The second run's Unreleased holds everything accrued since the first. Merging keeps both sets, and None. placeholders drop out.

    The existing heading keeps its date, so a tagged section is never restamped (release-toolkit#665).

    Fixed: the four PR-CI gates now build rt from the commit under test, not from main.

    Each gate checks the toolkit out a second time. That checkout resolves to main under the #456 floating-pin override. So a PR that changed a gate was graded by main's copy of it — the gate under test was never the gate that ran.

    The build arm now sources from the consumer checkout when it carries cmd/rt. That is the PR itself when the toolkit runs its own CI.

    The adopter path is unchanged. An adopter has no cmd/rt in its workspace, so it still fetches a published asset and needs no Go toolchain.

    Code comments: internal/fragments/fragments_test.go no longer names fragments.sh as the byte-authority for the changelog-kind order.

    That script went with the bash PR-CI gates (#607). Kinds is the authority now; the port's origin stays in the past tense.

    Found by re-deriving #734's scope during its AC sweep. It fixed five scripts. fragments.sh was not among them — the floor its own AC warned about.

    register-check now covers the complete built-in chamber vocabulary.

    Docs: thirteen sites named a retired script as the present-tense actor for behaviour that still happens. rt repin and rt check-self-bootstrap do the work now, including the glossary's definition of "re-pin" and two table cells citing line numbers inside a file that no longer exists.

    The behaviour was never wrong; only the actor was. That is why a tense sweep misses them — a reader asking "is this still true?" gets yes.

    Corrected five code comments that justified best-effort error handling by claiming the cut had already published.

    The code they annotate never reads publish_mode. The claim was false under the old draft default and true again under immediate, while the reasoning it supports never depended on either. The justification is now that the release object exists, which holds under both.

    Contracts + workflow comments: four contract documents named a deleted scripts/lib/*.sh file as their byte-authority. The Go packages are the authority; the bash origin stays in the past tense.

    C4, C6 and C7 now point at internal/fragments, internal/changelog and internal/forgejo. fragment-format.md also claimed the contract validates "both substrates" — there is one.

    reusable-release.yml had two: a pointer to a deleted file, and a sentence asserting other scripts/lib sources still exist. None do.

    fetch-rt.sh's HTML guard now removes the page it rejected before exiting.

    Previously the page stayed on disk. That was harmless on the default path, where the work dir is a throwaway mktemp -d. With RT_INSTALL_DIR set it was not. That directory is the adopter's persistent #606 cache, so the rejected page sat there under the release asset's own name.

    The artefact is removed, never the directory. A blanket cleanup would delete the cache #606 exists to keep.

    rt repin and rt consumer-wrappers now discover .forgejo/workflows/*.yaml adopter wrappers, not only *.yml.

    This forge executes both extensions. A .yaml adopter was invisible to Discover, so the re-pin rewrite silently did nothing rather than failing.

    The .yml-only filter was inherited from the bash oracle (scripts/lib/wrappers.sh) and faithfully ported at #705. The port was correct; the oracle was wrong.

    Docs: docs/events-logging.md had a Go heading over a bash body. The call signature, the jq escaping, the event_emit return contract and the declare -F guard all described the retired shell layer.

    The deeper defect was the opening sentence. It said the toolkit emits events during the cut path. It does not: rt check-self-bootstrap is the only command that emits anything, and the taxonomy table listed seven kinds as "currently emitted" when one is.

    That retirement was deliberate and reasoned, recorded in internal/decide. The guide now says so instead of describing behaviour a reader will not observe.

    Updated live Forgejo integration and architecture documentation after the shell client retirement; historical provenance remains, and deleted-file line coordinates are no longer presented as current anchors.

    • register: genericize newly matched reviewer credits

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Internal

    Added positive and negative Go tests for register-check allow-list glob semantics, including wildcard and recursive directory behavior.

    Removed the 55 changelog and config fixtures left orphaned when their equivalence suites were retired.

    Strengthened the workflow bake-list regression guard to count each reusable on both runtime staging loops, preventing prose occurrences from masking a missing loop entry.

    Downloads
  • v0.43.0 e6b57ce057

    v0.43.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    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 23s
    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 / workflow-schema (pull_request) Successful in 3s
    tests / bats (pull_request) Successful in 7s
    tests / shellcheck (pull_request) Successful in 3s
    check-self-bootstrap / check (push) Successful in 5s
    go-ci / lint + build + test (push) Successful in 24s
    release / decide + act (push) Successful in 8s
    release / release (push) Successful in 0s
    tests / workflow-schema (push) Successful in 3s
    tests / bats (push) Successful in 7s
    tests / shellcheck (push) Successful in 3s
    goreleaser / build + publish rt asset (push) Successful in 1m25s
    goreleaser / publish the rt image + bake its digest (push) Failing after 3s
    goreleaser / adopters can pull the published image (push) Has been skipped
    goreleaser / adopters can fetch the published asset (push) Failing after 3s
    Stable

    release-bot released this 2026-08-21 21:20:56 +02:00 | 728 commits to main since this release

    Added

    • release: publish the rt image and verify adopters can pull it
    • rt: bake-digest — the cut-time rewrite #794's coupling depends on
    • action: docker action, digest-pinned — the verified half of #794
    • prep: mirror the density verdict into the rolling PR description

    Changed

    Docs: branch-strategy.md and test-strategy.md no longer describe the retired two-line model in the present tense.

    Both already carried supersession notes. Both still opened with prose asserting the v2.x/v2/next plan as current or targeted. A header saying "superseded" does not reach a reader who starts at the first paragraph.

    branch-strategy.md now says which part survives: the single-thread cut model in §1 is still how the toolkit cuts. The multi-thread target never shipped.

    scripts/repin.sh and scripts/lib/forgejo-api.sh are retired. rt repin replaces them.

    The re-pin is now a Go verb. It validates the rc tag, builds the detached bake, then mints and pushes it. It rewrites each wrapper's pin, commits in a throwaway worktree, and opens the PR.

    Run rt repin <vX.Y.Z-rc.N>. AGENTS.md §2.5 documents it.

    ⚠️ --dry-run differs from the retired REPIN_DRY_RUN=1. The bash built the detached bake so it could be inspected. The Go reports the plan only.

    • contracts: fold validate-grammars.sh into the Go test suite (#777)

      The C4 and C6 grammars are now validated by go test, on every PR. The shell script was invoked by no workflow, so these grammars had never run under CI.

      TestC4AgainstHistoricalCorpus checks every fragment filename in git history. TestC6AgainstShippedChangelog checks every heading in the shipped changelog. Each carries accept/reject controls so a grammar that accepts everything cannot produce a clean scan.

      An empty corpus fails rather than skipping. A skip prints ok and exits 0, which a gate cannot distinguish from a pass.

    The architecture docs now say how much bash remains, not how many .sh files.

    One .sh file is left — fetch-rt.sh, 171 lines. A further 1315 lines live in workflow run: blocks across 12 files. reusable-release.yml alone carries 798.

    That bash is deliberately out of the retirement's scope. The port's subject was the script layer, not every shell command in CI. The judgement was never written down, so the docs read as if the bash were gone.

    ⚠️ "No bash files" and "no bash" are different claims. Only the first is true.

    rt: rt --version now carries build provenance. A source build reports dev (<sha>), or dev (<sha>, dirty) on a modified tree, read from Go build info. A goreleaser build still reports its tag unchanged. Previously any untagged build printed the bare string dev, so a stale binary and a broken gate were indistinguishable in CI output (closes part of #792).

    The release-toolkit action is now a docker action pinned by image digest.

    There are no inputs. version: and token: are gone. The action ref alone determines the binary, so the two can no longer disagree.

    A workflow still passing them keeps working. Actions ignores an undeclared input, so nothing breaks — the binary comes from the action ref instead.

    Run the action after actions/checkout. It installs under $GITHUB_WORKSPACE, which a later checkout wipes.

    Release cuts publish automatically again. publish_mode now defaults to immediate, reverting the #332 change that made draft the default.

    draft still works and is unchanged. Set it via RT_PUBLISH_MODE, the publish_mode: field in release-toolkit.yml, or the workflow input. The cut then creates the release unpublished, for a manual Publish click.

    Fixed

    Docs: four PR-CI gates carried a comment asserting that the @main bootstrap path builds the commit under test. It builds main.

    The gates check the toolkit out a second time, and that checkout resolves to main. So a PR that changes a gate is graded by main's copy of that gate (#728). Three of the four stated the opposite inside an otherwise-correct explanation of the cache decision; the fourth said it in different words.

    That explanation's real job — why version-tag refs are the only cacheable ones — was right and is unchanged. The false clause sat beside it as an aside, so a reader auditing the caching had no reason to check it.

    Comments only. No gate behaviour changes. The mechanism itself is #728 half B.

    The workflow parse check now covers every workflow file.

    It globbed reusable-*.yml, so it graded 5 of 16. A syntax error in any of the other 11 went unseen.

    That failure is silent by nature. A workflow which does not parse produces no run at all: no red, no status, nothing to investigate.

    Docs: secrets: inherit is required, not recommended. Omitting it is associated with a release PR that receives no CI at all.

    Measured across all four known adopters. The one repo that omits the line is the one whose rolling release PR has never received a single pull_request run.

    The PR still opens and still reads mergeable. Nothing goes red, because nothing runs, and a PR with zero checks looks like one whose checks passed.

    ⚠️ The guide said "omitting inherit degrades benignly" and labelled the line # recommended. One adopter followed that. Why it happens is not yet settled.

    Docs: arc42/07 §7.5 and arc42/05 no longer describe the Codeberg mirror as a live surface. #803 deleted both mirror workflows and left the deployment view asserting them in the present tense.

    §7.5 is kept as a dated what-was record rather than removed — the mirror was one of the two deployment-side concerns §7 exists to name. Its Substrate-verified (2026-07-08) anchor is untouched: that line is a true past-tense record and stays.

    arc42/05 listed reusable-mirror-to-codeberg.yml as a current adopter-visible building block. The live listing now matches the five reusables on disk, with the retired sixth named explicitly so a reader can tell retired from undocumented.

    Docs: README.md and docs/integration.md no longer open by calling release-toolkit "Codeberg-primary". #793 retired the mirror; both documents went on asserting Codeberg as the project's source, release and tag surface.

    The replacement states what is measurable: release-toolkit is developed on a self-hosted, LAN-only Forgejo and has no public mirror. Codeberg does not accept code that is largely LLM-generated, and a public mirror waits until there is adoption to serve.

    Codeberg as a platform for your repository is unaffected, and the docs now say so explicitly rather than leaving a reader to infer it. The 2026-07-05 companion demo is stated as a dated demonstration rather than a live guarantee — no CI arm here exercises a Codeberg target.

    docs/integration.md also linked reusable-mirror-to-codeberg.yml, a file deleted in #803.

    The workflow parse check now asserts its own coverage, not just its verdict. It previously looped a glob and printed OK. Grading 16 files and grading 5 produced byte-identical output, so narrowing the glob reddened nothing.

    It now counts what it parsed and compares that against an independent enumeration. Both sides cover *.yaml as well as *.yml, because this forge loads both — an unparseable .yaml workflow was invisible to the check.

    • changelog: split two over-long sentences in the #728 fragment
    • changelog: SectionContent distinguishes a MISSING section from an empty one
    • tests: restore main's parse arm verbatim — my own fix reverted more of #812

    Removed

    The Codeberg mirror is retired. mirror-on-cut.yml and reusable-mirror-to-codeberg.yml are deleted.

    Codeberg rejects code that is largely LLM-generated. No mirror replaces it. A public mirror waits until there is adoption to serve.

    Adopters who forked either workflow keep working — nothing here reaches a fork.

    ⚠️ The shape those files used is still right for any future mirror: a separate release: published workflow, never an in-graph mirror: job. An in-graph job trips Forgejo Actions' schema validator.

    The mirror had fired on every cut since 2026-08-17. Ten runs, all reporting success, against a repository that does not exist.

    Deprecated

    None.

    Upgrade

    None.

    Downloads
  • v0.42.0 baa31abad3

    v0.42.0
    All checks were successful
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    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 24s
    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 / workflow-schema (pull_request) Successful in 3s
    tests / bats (pull_request) Successful in 18s
    tests / shellcheck (pull_request) Successful in 3s
    check-self-bootstrap / check (push) Successful in 5s
    go-ci / lint + build + test (push) Successful in 26s
    release / decide + act (push) Successful in 9s
    release / release (push) Successful in 0s
    tests / workflow-schema (push) Successful in 3s
    tests / bats (push) Successful in 18s
    tests / shellcheck (push) Successful in 3s
    goreleaser / build + publish rt asset (push) Successful in 1m20s
    mirror-on-cut / mirror-1 (release) Successful in 2s
    mirror-on-cut / mirror (release) Successful in 0s
    goreleaser / adopters can fetch the published asset (push) Successful in 3m5s
    Stable

    release-bot released this 2026-08-20 10:42:01 +02:00 | 777 commits to main since this release

    Added

    rt repin re-pins consumer wrappers to a release-candidate tag.

    It refuses a dirty tree or an existing tag. It builds the detached bake commit at the requested base, then mints and pushes the rc tag on it. It rewrites each wrapper's uses:@<ref>, commits in a throwaway worktree, pushes and opens the PR.

    The rewrite is ported verbatim from repin.sh. A third-party uses: line, or a fork under another owner, is left alone.

    --dry-run reports what would change and writes nothing.

    • ci: ask the Forgejo engine whether it can parse the workflows (#769)

    Changed

    Port scripts/setup-bump-labels.sh to rt setup-bump-labels --owner <owner> --repo <repo>. Creates bump/{patch,minor,major} as an exclusive label group on a Forgejo repo; idempotent: already-correct labels are skipped, labels missing exclusive=true are updated. Adds ListLabels, CreateLabel, and UpdateLabel to the Forgejo client. Deletes the shell script.

    • Document the terminal bash-retirement state: scripts/fetch-rt.sh remains as the single bootstrap fetch-and-verify script because it obtains rt itself.
    • ADR-0009 and the architecture docs now name the delivery-mechanism change that would retire the residual. The stale ADR-0007 VERSIONING link is also fixed.

    Fixed

    decide: rt decide's #417 orphan walk now discriminates release plumbing from adopter work by file scope, not commit type. A bump-worthy commit touching only the changelog, the manifest or a declared version_file no longer breaks the walk. The buried-prepare skip stays reachable, so a legitimate cut is not refused. #650 fixed this in release-decide.sh, which was retired before the fix reached Go — it had been shipping on no surface at all (closes #701).

    Arm 30 in tests/workflows.bats now parses the workflow instead of scanning its raw lines.

    It previously skipped every comment line. That let release-toolkit#757 land the ${{ }} form inside a run: block comment, where the expression engine reads it before bash sees the #, and every cut was blocked.

    Skipping nothing over-corrects: a # at YAML level, or inline on a plain scalar, is stripped by the parser and is safe. The arm now walks the string scalars that survive yaml.safe_load, which is the text the engine sees.

    • Add an erratum to the v0.37.1 changelog entry whose file-scope orphan discriminator shipped only in scripts/release-decide.sh, not rt decide.

    • gates: exemptions carry an occurrence quota, not a boolean (#772)

    • changelog.d: 772 fragment described the rule the previous commit removed

    • gates: backticks cannot exempt a citation — narrow to a measured allowlist (#772)

    • setup-bump-labels: S1016 type conversions + ADR-0009 ancillary bucket

    • rt: repin never writes the operator's checkout, on any path

    • rt: repin mints the tag it demands be absent, and stops writing on --dry-run

    • tests: arm 30 parses the workflow rather than scanning raw lines

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Internal

    The #690 membership fallback's bounded window now has a negative arm.

    findPRContainingSHA asserts in its own comment that a prepare outside the window resolves to not-found and Layer 2 fails. Nothing exercised that: every membership arm placed the containing PR inside the window, so the bound could not fail where those arms ran.

    A widening window would turn a stale prepare into a spurious cut — worse than the false refusal #690 fixed, because a refusal gets investigated.

    AGENTS.md now records that rt repin is not yet the canonical re-pin path.

    scripts/repin.sh still implements the operation and the docs correctly name it canonical. The verb ships but nothing invokes it, and its forge path has never run against a real forge.

    The note is removed when #705 part B retires the script.

    • docs: repoint four Go comments at the real cli-surface contract path, and make the citation checkable (#772)

      Four comments cited contracts/cli-surface.md, which resolves from nowhere. The file is at docs/architecture/contracts/cli-surface.md, and one comment already said so.

      TestCitedDocPathsResolve now checks every repo-relative Markdown path cited in a Go comment. Two sites quote an unresolvable path as evidence; both are named in an explicit allowlist.

    Downloads
  • v0.41.0 a47f4d47db

    v0.41.0
    All checks were successful
    check-self-bootstrap / check (push) Successful in 5s
    go-ci / lint + build + test (push) Successful in 27s
    release / decide + act (push) Successful in 8s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 18s
    tests / shellcheck (push) Successful in 3s
    goreleaser / build + publish rt asset (push) Successful in 1m17s
    mirror-on-cut / mirror-1 (release) Successful in 2s
    mirror-on-cut / mirror (release) Successful in 0s
    goreleaser / adopters can fetch the published asset (push) Successful in 3m5s
    Stable

    release-bot released this 2026-08-20 02:52:33 +02:00 | 802 commits to main since this release

    Added

    rt consumer-wrappers now owns consumer-workflow discovery, and the release workflow uses it instead of sourcing the Bash helper.

    Changed

    rt manifest-postcondition now resolves the manifest-PR surface itself when --pr-state is unset. reusable-release.yml no longer sources scripts/lib/forgejo-api.sh; the flag is still honoured.

    Resolution fails closed. A lookup that cannot complete yields ungraded (exit 2), never absent (exit 1).

    This also closes a pagination hole. The old query read only the first 50 open PRs, so a busy repo could report absent while a manifest PR existed.

    rt binary-size-check replaces scripts/binary-size-check.sh (rt#720, bash retirement arc). Exit codes are tri-state: 0 pass, 1 step change exceeded, 2 could not grade. Flags --max-growth-pct, --baseline-bytes, and --attribute-ref are identical to the shell script. On a red, the merge-base attribution block is included. go-ci.yml calls rt binary-size-check directly; the shell script is deleted.

    rt check-self-bootstrap now owns pin-drift detection. The Bash script and its two private libraries are retired, and forgejo-api.sh no longer emits api_call events.

    rt prep now re-pins adopter-facing doc refs as a built-in step (rt#720, bash retirement arc). scripts/hooks/update-doc-version-refs.sh is deleted; the same rewrite logic runs as §8d UpdateDocVersionRefs inside rt prep — no consumer config change required. release-toolkit.yml no longer lists the hook in post_bump_hooks. The post_bump_hooks mechanism itself is retained for adopters.

    Fixed

    • release: an expression spelled out in a run-block comment made reusable-release.yml unusable (#762)

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Internal

    • build-bake: retire the bash marker-rewrite library.

      No action required. rt build-bake now owns the post-cut marker reset and the re-pin detached bake path, preserving the existing tree semantics while removing the duplicated sed implementation.

    • code comments: stop naming deleted bash scripts as the current authority (#734)

      Fifteen Go files carried comments asserting that a removed script is the byte-authority or behaviour-of-record. Provenance statements are kept in the past tense; role assignments now name the Go package as the authority. No behaviour change.

    • forgejo-api.sh: correct the layer-2 comment — the documented fallback is the only path (#737)

      config.sh was removed in #712, so config_get_default_branch is never defined and the inline yq read is all that runs. The comment named the dead branch as the common case. Code unchanged.

    • rt: derive the subcommand phase census instead of hand-writing it twice.

      No action required. The comment above var subcommands named five Phase 6, six Phase 6b and three Phase 7 verbs. The slice holds 5 + 8 + 4 + 1, and Phase 8 was missing.

      TestAllSubcommandsPresent now derives the breakdown, so its failure message names the phase that moved. The comment carries no counts. The total stays hand-written: it is the gate, and a new verb should cost a deliberate bump.

    Prune the orphaned internal/bake/testdata fixture corpus left behind when the differential bake oracle was retired.

    Delete scripts/dev-tools/migrate-releases-to-codeberg.sh (0 callers, one-time historical mirror script) and scripts/lib/wrappers.sh (superseded by internal/wrappers/discover.go; scripts/repin.sh repointed to rt consumer-wrappers). Delete tests/wrappers.bats alongside wrappers.sh; equivalent coverage lives in internal/wrappers/discover_test.go.

    Downloads
  • v0.40.2 41fea23c99

    v0.40.2
    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 24s
    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 23s
    tests / shellcheck (pull_request) Successful in 7s
    check-self-bootstrap / check (push) Successful in 3s
    go-ci / lint + build + test (push) Successful in 22s
    release / decide + act (push) Successful in 9s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 23s
    tests / shellcheck (push) Successful in 7s
    goreleaser / build + publish rt asset (push) Successful in 1m29s
    mirror-on-cut / mirror-1 (release) Successful in 2s
    mirror-on-cut / mirror (release) Successful in 0s
    goreleaser / adopters can fetch the published asset (push) Successful in 3m5s
    Stable

    release-bot released this 2026-08-19 12:21:11 +02:00 | 817 commits to main since this release

    Added

    None.

    Changed

    None.

    Fixed

    The FETCH arm adopters use is now exercised by this repo's CI. A tag-triggered job runs the composite bootstrap against the tag just published, executes the fetched binary, and measures the cache-hit path.

    The toolkit's own wrappers pin @main, so the #456 override sent every reusable down the BUILD arm. The FETCH arm was therefore unexercised here by construction, and it broke for every adopter at v0.34.0 with CI green throughout.

    Sentence splitting now decides where sentences end instead of pattern-matching punctuation. The old splitter broke on any ., ! or ? followed by whitespace, wherever it sat.

    That was wrong in two directions. Punctuation inside a code span or after an abbreviation split one over-long sentence into two short ones, so the density check passed prose it should have refused. Punctuation followed by a closing marker, as in the house **Lead.** Prose shape, suppressed the split and made the check refuse prose that was fine.

    The new predicate asks whether each occurrence is sentence-final. It splits at indices rather than rewriting text, so a sentence's words are never lost or double-counted by the fix itself.

    • gates: skip closers when deciding an abbreviation, and correct the policy (#738)
    • gates: abbreviations need a per-occurrence decision too (#738)

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Downloads
  • v0.40.1 c8d7307aae

    v0.40.1
    All checks were successful
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 5s
    fragment-check / changelog fragment-kind (pull_request) Successful in 11s
    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 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 5s
    go-ci / lint + build + test (push) Successful in 43s
    release / decide + act (push) Successful in 10s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 23s
    tests / shellcheck (push) Successful in 7s
    goreleaser / build + publish rt asset (push) Successful in 1m28s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-08-19 10:23:44 +02:00 | 824 commits to main since this release

    Added

    None.

    Changed

    None.

    Fixed

    A failed cut can no longer stamp the same version twice. Both changelog emit paths now refuse a ## [X.Y.Z] heading the file already carries, naming the existing section's line number.

    The state this catches is stamped-but-uncut: a cut stamps the version, fails before tagging, then the next run sees no tag and stamps again. The duplicate is dangerous rather than untidy. Everything extracting a version's section takes the first match and stops, so the second block is dropped silently while the file still parses and every gate passes.

    The post-cut manifest check can read the default branch again. Its fetch ran without credentials, so it reported the branch unreadable and refused a cut that had in fact succeeded.

    The branch read now happens in the workflow step, which holds a credential, and the result is passed to rt manifest-postcondition. The decision is unchanged. Readable-and-absent stays distinct from unreadable: only the first is evidence, so an unreachable branch still refuses to grade rather than convicting a cut.

    • release: the readable-and-absent path aborted before rt was called

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Downloads
  • v0.40.0 11aa1d3bc0

    v0.40.0
    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
    Stable

    release-bot released this 2026-08-19 09:53:59 +02:00 | 831 commits to main since this release

    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.

    Downloads
  • v0.39.0 64b6ebf8e7

    v0.39.0
    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 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 21s
    release / decide + act (push) Successful in 8s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 22s
    tests / shellcheck (push) Successful in 7s
    goreleaser / build + publish rt asset (push) Successful in 1m26s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-08-19 08:39:33 +02:00 | 844 commits to main since this release

    Added

    None.

    Changed

    • fragment-check: grades changelog density and FAILS on a violation, so a dense fragment is caught on its own PR instead of at cut time (#621)

      The density rules were already enforced, but only against the composed CHANGELOG.md. rt prep recreates that file from the fragments on every compose, so a fix applied there is regenerated away. This grades the same checks, from the same implementation, at the layer where a fix survives. Set density: 'false' to opt out. Pass the five threshold inputs to match your changelog-body-check wiring, or the two layers can disagree.

    Fixed

    • changelog-body-check: check 7 now names every over-threshold sentence, not just the longest one in each paragraph (#621)

      A second long sentence in the same paragraph used to be invisible. A fixer who cleared the named list and pushed then got a fresh FAIL that read as a new problem. Measured at eight over-threshold sentences reported as five. Verdicts are unchanged: the same text fails, and the report is now the complete work list.

    • changelog-body-check: check 3 no longer refuses a dependency pin that outranks the version being cut (#658)

      A pin naming another repository states a fact about a shipped release. Check 3 read every @vX.Y.Z as a forward reference, so a consumer whose dependencies outranked it could not cut at all. Repo identity now comes from GITHUB_REPOSITORY or FORGEJO_REPOSITORY.

      The match is a prefix, so the Actions uses: path form is still caught as a self-reference. A bare pin carrying no owner is still checked, which is the deliberate safe direction. With neither variable set the behaviour is unchanged. The PASS line now names the pins it exempted, and a FAIL names the pin rather than only its version.

    A cut-safeguard refusal now says what it refused about. The decline path emits safeguard_version and prep_sha alongside safeguard_fail.

    The workflow has named all three in its ::error:: since #663 and consumed all three as env vars, but only one was ever emitted. Operators read a prepare commit for v? … Prepare commit graded: unknown while the same log printed both values six lines above.

    • prep: --dry-run now names the working-tree paths it changed, and its help text stops calling itself preview-only (#700)

      The flag skips the branch checkout, commit, push and PR call. It still rewrites the changelog, bumps version_files, runs post_bump_hooks and deletes consumed fragments, so a run read as side-effect-free left a dirty tree. Staged paths are reported separately because git checkout -- . does not restore them. The disclosure is derived from git rather than from a fixed list.

    • ci: the positive control must survive -e when the gate refuses

    • changelog: split the #658 fragment paragraph that fails the density gate

    • prep: the staged-class undo verb deleted uncommitted operator work

    • changelog: the #700 fragment repeated the claim the PR exists to correct

    • prep: give a COMPLETE undo for staged paths, and stop claiming git writes are skipped

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Internal

    • tests: Bats CI now prints captured command output when an assertion fails, while keeping passing-test output hidden (#686)
    Downloads
  • v0.38.0 82d4aa0932

    v0.38.0
    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 / 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
    check-self-bootstrap / check (push) Successful in 3s
    go-ci / lint + build + test (push) Successful in 21s
    release / decide + act (push) Successful in 8s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 23s
    tests / shellcheck (push) Successful in 7s
    goreleaser / build + publish rt asset (push) Successful in 1m24s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-08-18 23:44:23 +02:00 | 863 commits to main since this release

    Added

    None.

    Changed

    None.

    Fixed

    • decide: --dry-run no longer skips the cut-safeguard layers, and an ungraded layer stops declaring a pass (#689)

      A dry run short-circuited both safeguard layers and the rolling-PR bump-label read, then reported mode=cut for a base a real run had refused seconds earlier. layer2=skip rendered identically to layer2=pass. The layers are reads, so they now run under --dry-run exactly as on a real run. The gate also requires an affirmative pass instead of merely "not a failure", so a layer that could not be graded declines the cut rather than clearing it.

    • docs: examples/README.md no longer hard-codes a "current" toolkit version that the release rewrite cannot update (#716)

      The setup step tells adopters to replace the uses: pin, and the release rewrite updates that pin on every cut. The parenthetical beside it named a fixed version and was updated by nothing, so it had said "v0.25.0 is current" for thirteen minor releases. It now points at the README Status section, which is the surface that actually tracks the current tag.

    • changelog: split the #689 fragment sentence that fails the density gate

    Removed

    • release path: the bash PR-CI gates and their differential oracle are gone; rt is the sole implementation (#607)

      The four gates already ran as rt subcommands, so no adopter action is needed and no gate behaviour changes. What goes is the parallel bash: eleven scripts, two now-unreferenced libs, and their bats suites. With them go the 22 equivalence tests, which existed only to prove the two implementations agreed. A fix now lands once instead of twice.

    The dead draft-release.sh component is retired — the script plus the three libraries whose only consumer it was (lib/changelog.sh, lib/config.sh, lib/semver.sh). 1,907 lines, reachable from nothing: the cut has published through rt release since the Go port landed.

    One bats test was ported to Go first. It was the only artifact verifying a claim the source makes in a comment: that scaffold and check 2 agree by construction.

    Deprecated

    None.

    Upgrade

    None.

    Downloads
  • v0.37.2 4f9ee6bed3

    v0.37.2
    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 / 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 35s
    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 2m20s
    tests / shellcheck (pull_request) Successful in 9s
    check-self-bootstrap / check (push) Successful in 3s
    go-ci / lint + build + test (push) Successful in 36s
    release / decide + act (push) Successful in 8s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 2m19s
    tests / shellcheck (push) Successful in 9s
    goreleaser / build + publish rt asset (push) Successful in 1m19s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-08-18 13:22:23 +02:00 | 873 commits to main since this release

    Added

    None.

    Changed

    None.

    Fixed

    The PR-lookup test seam no longer makes a live forge call when it is configured without its membership companion. #690 made an absent sha fall through to the membership lookup, mirroring production's 404. But in test mode with the membership seam unset, that widening reached the real forge silently. The calling arm passed either way, so nothing surfaced it. An unconfigured downstream seam is now a refusal rather than a fall-through.

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Downloads