-
Release v0.30.2
StableAll checks were successfulchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 3scheck-self-bootstrap / check (pull_request) Has been skippedchangelog-body-check / check (pull_request) Successful in 0sfragment-check / changelog fragment-kind (pull_request) Successful in 3sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Successful in 4sregister-check / check (pull_request) Successful in 0stests / bats (pull_request) Successful in 1m55stests / shellcheck (pull_request) Successful in 7scheck-self-bootstrap / check (push) Successful in 3srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0stests / bats (push) Successful in 1m55stests / shellcheck (push) Successful in 7smirror-on-cut / mirror-1 (release) Successful in 1smirror-on-cut / mirror (release) Successful in 0sreleased this
2026-07-07 18:56:16 +02:00 | 1061 commits to main since this releaseAdded
None.
Changed
None.
Fixed
Cut workflow no longer fails on the not-yet-existent cut tag under the toolkit-self floating-pin discipline. Under #456, the toolkit's own wrapper pins
uses:@main. The reusable then runs from main HEAD.After a rolling-PR merge, main transiently carries
BUILD_BAKED_TOOLKIT_REF='vCUT_TAG'.release-prep.shbakes it at compose so adopters pinning the cut tag get frozen substrate. But the tag itself does not exist yet at push:main time. The oldresolve-refstep emitted that value unconditionally as the checkout target;actions/checkout@v4then failed trying to fetch a not-yet-created tag.Each
resolve-refstep now scope-detects the consumer wrapper'suses:@<ref>value. When it ismain,REFis overridden tomainregardless of the baked value. Adopters are unaffected. Their wrapper pins@vX.Y.Z, the override branch is skipped, and the baked value is trusted as before.Cut-tag artifacts still carry the baked
vCUT_TAGvalue.@vX.Y.Zpins remain a frozen source-of-record per #336. Empirical anchor: v0.30.1 cut failed at7a1e63cb. No action required for adopters. Closes #460.Removed
None.
Deprecated
None.
Upgrade
None.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
v0.30.1
StableSome checks failedchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4scheck-self-bootstrap / check (pull_request) Has been skippedchangelog-body-check / check (pull_request) Successful in 0sfragment-check / changelog fragment-kind (pull_request) Successful in 3sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Successful in 4sregister-check / check (pull_request) Successful in 0stests / bats (pull_request) Successful in 1m55stests / shellcheck (pull_request) Successful in 7scheck-self-bootstrap / check (push) Successful in 3srelease / decide + act (push) Failing after 38srelease / release (push) Failing after 0stests / bats (push) Successful in 1m56stests / shellcheck (push) Successful in 8smirror-on-cut / mirror-1 (release) Successful in 1smirror-on-cut / mirror (release) Successful in 0sreleased this
2026-07-07 16:34:07 +02:00 | 1064 commits to main since this releaseAdded
None.
Changed
None.
Fixed
Toolkit-self wrappers now float on
@main, eliminating the between-cut manual-rc-tag intervention window. The toolkit's own.forgejo/workflows/*.ymlpinuses:@maininstead of a versioned tag. Every push tomainruns against the current scripts substrate; no drift class can accumulate.Adopter behavior is unchanged. Adopters continue to pin
uses:@vX.Y.Zto a released tag. The cut-tag artifact retains its bakedBUILD_BAKED_TOOLKIT_REFvalue, so@vX.Y.Zremains a frozen source-of-record.Closes the shared substrate of the
fix-cut-bootstrap-through-own-gateclass. Class 1 (check-self-bootstrap firing red between cuts) closes via aPINNED_REF == 'main'early-exit incheck-self-bootstrap.sh. Class 3 (compose-scaffold-carrying tooling unavailable from a stale pinned tag) closes via a symmetric skip in the auto-re-pin path (#283). Wrappers already at@mainstay there.Adopter drift-detection stays intact via the tag-pin discipline. That discipline is orthogonal to
check-self-bootstrap.yml, a toolkit-self-only workflow. No action required for adopters. Closes #456.Removed
None.
Deprecated
None.
Upgrade
None.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
Release v0.30.0
StableAll checks were successfulchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4scheck-self-bootstrap / check (pull_request) Has been skippedchangelog-body-check / check (pull_request) Successful in 0sfragment-check / changelog fragment-kind (pull_request) Successful in 3sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Successful in 4sregister-check / check (pull_request) Successful in 0stests / bats (pull_request) Successful in 1m55stests / shellcheck (pull_request) Successful in 7scheck-self-bootstrap / check (push) Successful in 3srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0stests / bats (push) Successful in 1m55stests / shellcheck (push) Successful in 8smirror-on-cut / mirror-1 (release) Successful in 1smirror-on-cut / mirror (release) Successful in 0sreleased this
2026-07-07 12:59:26 +02:00 | 1069 commits to main since this releaseAdded
Cut-time preflight verifies the composed CHANGELOG section is gate-clean before the release fires. A new
scripts/compose-verify.shruns at the top of thecutcase inreusable-release.yml. It scans the target section withregister-check --stdinandchangelog-body-check.The preflight blocks the cut when either gate flags drift or density. Closes the gap surfaced by PR#438 (v0.28.0), where a manifest-only recovery skipped the auto push:main trigger and workflow_dispatch fired the cut against a CHANGELOG that never went through compose. No action required for adopters — the preflight is wired into the reusable workflow. Closes #439.
Changed
None.
Fixed
check-self-bootstrapno longer fires red on the rolling-PR-merge push:main event. The auto-re-pin substrate (#283) runs INSIDE the cut workflow that fires on that same push, so the pin-drift check saw pre-cut state and reported a false-positive RED. The check now detects a HEAD subject matching the release-prep merge shape and skips with an informative message.Discipline preserved for every OTHER push:main. A hand-edit to
release.yml, an orphaned tag, or any content edit outside the rolling-PR flow still fires the check. Closes the "check-self-bootstrap RED on every substrate cut" class (empirical: PR#438 v0.28.0 arc + PR#443/#446 Bundle 1/2 sprint). No action required. Closes #445.release-prep.shnow scaffolds all 6 standard CHANGELOG sections in the composed body. When fragments produce only a subset (e.g., onlyAdded+Fixed), the missing sections are filled withNone.before the section transitions intoCHANGELOG.md. Aligns compose to the documentedchangelog-body-checkcheck-2 intent ("add each even if the body is 'None.'").Closes the compose-vs-check-2 divergence surfaced by the v0.30.0 rolling PR, which would otherwise block at cut-time via the compose-verify preflight. Idempotent: existing sections pass through verbatim; running twice produces identical output. Non-standard sections (e.g., a fragment producing
### Internal) are preserved after the standard set. No action required. Closes #453.- register-check: pivot to allowlist entry ([reviewer] 3682 catch)
- tests: scrub chamber-names from compose-verify.bats fixture ([reviewer] 3680 Finding B)
- compose-verify: fold review 3676 non-blockers (version-newest coupling + dry-run parity) [#446]
Removed
None.
Deprecated
None.
Upgrade
None.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Release v0.29.0
StableSome checks failedchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 3scheck-self-bootstrap / check (pull_request) Has been skippedchangelog-body-check / check (pull_request) Successful in 0sfragment-check / changelog fragment-kind (pull_request) Successful in 3sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Failing after 4sregister-check / check (pull_request) Failing after 0stests / bats (pull_request) Successful in 1m52stests / shellcheck (pull_request) Successful in 7scheck-self-bootstrap / check (push) Failing after 3srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0stests / bats (push) Successful in 1m52stests / shellcheck (push) Successful in 8smirror-on-cut / mirror-1 (release) Successful in 1smirror-on-cut / mirror (release) Successful in 0sreleased this
2026-07-07 09:55:05 +02:00 | 1081 commits to main since this releaseAdded
Compose-time register-scrub on commit-subject → CHANGELOG bullets. Chamber-name attributions in commit subjects used to ride through
cc_categorize_commits_sinceinto CHANGELOG bullets past the register-check file-scan gate. The bullet is now scrubbed at emission — every REGISTER_PATTERNS match is replaced with[reviewer]— so the composed CHANGELOG is clean at source. The pre-existingregister-check --stdinFATAL inrelease-prep.shstays wired as a defense-in-depth layer for fragment-side drift the emission scrub doesn't cover. No action required. Closes #442.Fixed
changelog-body-checknow targets the newest versioned section instead of the empty[Unreleased]placeholder above it. On a composed CHANGELOG the transition step inserts the freshly-versioned[vX.Y.Z]section BELOW a still-empty[Unreleased]heading. The pre-fix check took the first## [and passed vacuously. The check now prefers the newest## [vX.Y.Z]heading and falls back to[Unreleased]only when no versioned section exists. No action required. Closes #440.Fragment-vs-commit dedup now matches
(closes #NNN)/(fixes #NNN)bullet formats. A CC bullet composed from a commit subject carrying(closes #NNN)/(fixes #NNN)/Closes #NNN.used to escape the trailer-only dedup and appear alongside its fragment prose.The dedup key is now a bare
#NNNwith a word-boundary guard. It catches every ref shape — trailer, bolded scope, inline close-verb, comma-separated multi-refs.#4380still does not match fragment#438. No action required. Closes #441.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
Release v0.28.0
StableSome checks failedchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7schangelog-body-check / check (pull_request) Successful in 0scheck-self-bootstrap / check (pull_request) Has been skippedfragment-check / changelog fragment-kind (pull_request) Successful in 6sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 8smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Successful in 6sregister-check / check (pull_request) Successful in 0stests / bats (pull_request) Successful in 1m51stests / shellcheck (pull_request) Successful in 7scheck-self-bootstrap / check (push) Failing after 4srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0stests / bats (push) Successful in 1m51stests / shellcheck (push) Successful in 12smirror-on-cut / mirror-1 (release) Successful in 1smirror-on-cut / mirror (release) Successful in 0sreleased this
2026-07-06 21:12:56 +02:00 | 1086 commits to main since this releasev0.28.0 tightens release-note hygiene and release-cut reliability. Register-check now covers commit subjects and composed text. CHANGELOG output is normalized and readability-gated. Orphan-check handles breaking-change footers and legitimate prep-PR merges. Codeberg mirroring is decoupled from the cut workflow.
Added
Per-line exemption for
register-check. Files scanned byregister-checksometimes legitimately use pattern words in adopter-relevant contexts — a role title in a doc, a program name in a script. A trailing# register-check: allowmarker suppresses that one line's match (<!-- register-check: allow -->in Markdown and HTML). No action required. Scrubbing the pattern remains first-resort;.register-allowlistat file scope is second-resort; the line marker is the last resort. The--stdinscan called fromrelease-prep.shignores markers, so composed content stays gated. Closes #396.Register-check now scans commit subjects and composed CHANGELOG content. Chamber-name attributions in commit subjects used to ride into
CHANGELOG.mdpast the file-scan gate. The compose step read subjects the file scan never touched. Two new modes close the gap.--git-log RANGEscans commit subjects at PR review time.--stdinscans arbitrary text and runs fromrelease-prep.shon the composed section before it lands inCHANGELOG.md, catching drift from any source. Recommended for adopters using commit-subject → CHANGELOG composition (cc_list_commits_since/cc_categorize_commits_since): setcheck_commit_subjects: 'true'on thereusable-register-check.ymlinput. Fragments-only adopters can leave the default ('false'). Closes #403.Fragment authors can hard-wrap paragraphs without breaking the rendered CHANGELOG.
release-prep.shnow normalizes compiled sections before they transition intoCHANGELOG.md. Blank lines stay paragraph boundaries. Structural markdown — lists, headers, blockquotes, fenced code — passes through verbatim. Continuation lines within a paragraph join with a single space. A hyphen-word rejoin heuristic catches breaks inside hyphenated words. If a residual[a-z]- [a-z]pattern survives, the step fails loud on the offending source so the fragment can be fixed before release-prep ships. No action required. Hard-wrapping fragments at ~72 columns continues to work. Closes #420.CHANGELOG bodies get a readability check.
changelog-body-checknow flags long sentences, deep parenthetical nesting, and long paragraphs on newly-composed sections before the release-prep PR reaches operator eyes. Defaults: sentence fails at 30 words (warns at 25), paragraph fails at 100 words (warns at 75), parenthetical nesting fails above depth 2. Historical sections stay out of scope — the check targets the newest version block only. Recommended for adopters wanting different thresholds: override via new inputs onreusable-changelog-body-check.yml—sentence_max_words,sentence_warn_words,paren_depth_max,paragraph_max_words,paragraph_warn_words. Author-side guidance lives indocs/conventions.md"Writing adopter-facing prose". Closes #421.Changed
Orphan-check now catches
BREAKING CHANGE:body-footer commits. Arefactor:orchore:commit whose body carries aBREAKING CHANGE:footer is bump-worthy per conventional-commits. The pre-fix range-walk inrelease-decide.shkeyed only on the subject and false-skipped them.release-decide.shnow delegates release-relevance tocc_bump_level_from_subjectinscripts/lib/conventional-commits.sh. The helper handles subject-levelfeat!/fix!markers AND body-levelBREAKING CHANGE:footers, so orphan-check uses the same rules as the release-cut bump logic. Any future addition to the bump-worthy type set (for example,perf:) propagates automatically. No action required. Closes #424.check-self-bootstrapdocstrings clarify their scope. The script and workflow headers now open with "Pin-drift detection, NOT content validation." A red on a PR that touches a compose-script means the mechanism is firing correctly. The pinned ref needs to advance to catch main up at the next cut. It does not mean something is wrong with the code. Content-quality gates (shellcheck, bats, register-check) are named explicitly so a reader triaging a failure knows where content-questions live. No action required. Closes #432.Fixed
Orphan-check no longer false-fires on a legitimate cut — v1.0.0 must-fix follow-up to #417. Release-toolkit's own v0.27.0 cut on PR#406 tripped
release-decide.shwith the exact fail-loud the detector was designed to emit. The detector's signature —semver_compare(CHANGELOG_TOP, manifest.last_released) == 1— matches both an orphan-post-cancellation AND a legitimate cut-in-progress. Both have CHANGELOG advanced, manifest not yet updated, and no tag yet. The distinguisher is action-context, not signature.release-decide.shnow readsHEAD_SUBJECTearly and matches it againstPREP_SUBJECT_RE. When HEAD IS a prep-PR merge commit AND the version it cuts equalsCHANGELOG_TOP, the check skips — that is the legitimate cut-about-to-fire path. Any other HEAD shape (a bystander push after a cancelled cut) still fires the fail-loud. Both prep-merge subject styles are handled:chore(release): prepare vX.Y.Z(merge / rebase) andchore(release): vX.Y.Z(squash-merge using the PR title). Three new regression tests lock the behavior. No action required. Closes #417.Codeberg mirror no longer runs inside the cut workflow. A new
.forgejo/workflows/mirror-on-cut.ymltriggers on therelease: publishedevent. It fires either on immediate-mode publication, or when the operator clicks Publish on a draft.The in-graph
mirror:job is removed fromrelease.yml. It combinedcontinue-on-error: truewith auses:job, which Forgejo Actions correctly rejects per the GitHub Actions schema. Mirror-outages, PAT-rotations, and Codeberg-side timeouts no longer cascade into cut-failure. Mirror-failure never touches the cut workflow's job graph.Adopter action required only if you fork the toolkit's workflow files and mirror to Codeberg from your own copy. Move the mirror job into a separate
release: published-triggered workflow rather than amirror:job insiderelease.yml. Thereusable-mirror-to-codeberg.ymldocstring shows the new pattern. Closes #427.Removed
None.
Deprecated
None.
Upgrade
- Optional: set
check_commit_subjects: 'true'onreusable-register-check.ymlif you compose CHANGELOG entries from commit subjects. Default remains'false'for fragments-only adopters. See #403. - Optional: override readability thresholds via new inputs on
reusable-changelog-body-check.yml—sentence_max_words,sentence_warn_words,paren_depth_max,paragraph_max_words,paragraph_warn_words. Defaults follow the "Writing adopter-facing prose" convention. See #421. - Required if you fork the toolkit's workflow files and mirror releases to Codeberg from your own copy: move the mirror job into a separate
release: published-triggered workflow rather than an inlinemirror:job inrelease.yml. Toolkit-self's ownmirror-on-cut.ymlis a working example. See #427.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Optional: set
-
v0.27.0
StableSome checks failedchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4scheck-self-bootstrap / check (pull_request) Has been skippedchangelog-body-check / check (pull_request) Successful in 0sfragment-check / changelog fragment-kind (pull_request) Successful in 4sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Successful in 4sregister-check / check (pull_request) Successful in 0stests / bats (pull_request) Successful in 1m47stests / shellcheck (pull_request) Successful in 8scheck-self-bootstrap / check (push) Successful in 3srelease / decide + act (push) Failing after 5srelease / release (push) Failing after 0srelease / mirror (push) Failing after 0stests / bats (push) Successful in 1m47stests / shellcheck (push) Successful in 8sreleased this
2026-07-06 14:12:12 +02:00 | 1110 commits to main since this releasev0.27.0 hardens release-toolkit for real Forgejo and Codeberg adoption ahead of v1.0: bounded, retryable API calls with pagination; per-project
publish_modeoverrides; Codeberg proven end-to-end; stale docstring pins cleaned up; and protection against cut cancellation.Added
Forgejo API calls are now bounded and retryable: a 5s connect timeout and 30s total timeout on every call (env-tunable via
FORGEJO_API_CONNECT_TIMEOUT_S/FORGEJO_API_TIMEOUT_S), with exponential backoff on 429 / 5xx / network errors. Tag and PR-list lookups now paginate, fixing missed results on repos with more than one page of tags or PRs. Retries are safety-gated: 429 is retried on any method, but 5xx / network errors are retried only for idempotent methods — a POST / PATCH is not retried by default (the server may have partially applied it) unless you setFORGEJO_API_RETRY_UNSAFE=1(#334; v1.0.0 must-fix).You can now change
publish_modeper project without editing your workflow: setvars.RT_PUBLISH_MODEin Forgejo → Settings → Actions → Variables, or addpublish_mode:torelease-toolkit.yml. Precedence, highest first: repo variable → config file → workflowwith:input → thedraftdefault (#332). Invalid values fail loud with the resolution chain shown (#408; v1.0.0 must-fix).Integration docs now collect the per-project override options in one table, with worked examples for config-file, repo-variable, and one-off dispatch overrides (#409).
Changed
release-toolkit is now demonstrated end-to-end on Codeberg's shared runners: the tic-tac-toe companion demo runs the full flow from fresh-repo bootstrap through cut to a draft release. The README and integration docs now describe Codeberg as proven rather than assumed, link the demo, and add a short "Running on Codeberg" note covering how to enable Actions and Releases and the no-token fallback (#412).
Fixed
Reusable workflow docstring examples now use
@vX.Y.Zplaceholders instead of concrete pins that had drifted stale; the copy-paste-ready refs in the docs and examples stay pinned and are kept fresh by the post-cut version-ref hook (#407).Cut-cancellation recovery. If another push lands while a cut is queued, the cut can be cancelled after the CHANGELOG section moved but before the release tag exists — leaving an orphaned
## [vX.Y.Z]section that the next run would otherwise re-emit as a duplicate entry.Recommended: add the new
concurrencyblock from integration.md to your consumer workflow to prevent the cancellation. If you skip it, cuts stay safe — the toolkit fails loud on the state divergence — but an interrupted cut may need manual recovery.- Prevention (adopter-side): the consumer wiring template in
docs/integration.mdnow includes a workflow-levelconcurrency:block (group: release-cut-${{ github.ref }},cancel-in-progress: false) that queues later pushes behind the in-flight cut. - Detection (toolkit-side):
release-decide.shfails loud whenCHANGELOG.md's top-most released section is ahead ofmanifest.last_released_version— the orphan signature — and lists two recovery options (finish the pending release and update the manifest, or move the section back under[Unreleased]). Auto-heal is out of scope for v1.0.0.
Closes #417.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Prevention (adopter-side): the consumer wiring template in
-
Release v0.26.0
StableSome checks failedchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4schangelog-body-check / check (pull_request) Successful in 0scheck-self-bootstrap / check (pull_request) Has been skippedfragment-check / changelog fragment-kind (pull_request) Successful in 4sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0sregister-check / register-drift check (pull_request) Successful in 4sregister-check / check (pull_request) Successful in 0stests / bats (pull_request) Successful in 1m33stests / shellcheck (pull_request) Successful in 8scheck-self-bootstrap / check (push) Failing after 3srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0stests / bats (push) Successful in 1m34stests / shellcheck (push) Successful in 12srelease / mirror (push) Successful in 2sreleased this
2026-07-05 16:46:34 +02:00 | 1130 commits to main since this releaseAdded
CI test suite as required merge gate — new
.forgejo/workflows/tests.ymlruns fullbats tests/+shellcheck --severity=warningon every PR + push:main. ADR-0001 named shellcheck as CI gate; this workflow implements it. One pre-existing SC1010 false-positive inscripts/lib/forgejo-api.sh(jq variable nameddo) fixed by rename tomerge_do(#330).docs/SECURITY.md— new page documenting the release-toolkit trust boundary + the audit-scope gap between consumer-pinning and secondary-checkout script execution. Honest disclosure of the current state; architectural fix targets v1.1.0 (#336).docs/VERSIONING.md— new page memorializing the v1.0.0 SemVer stability contract: v1.x preserves backward compat on workflow inputs, config schema, published script CLI, and documented behavior contracts; breaking changes require v2.0.0; deprecation cycle spans a minor before removal; tag immutability commitment; post-v1.0.0 cadence shift to boring/settled infrastructure (#338).New
docs/cold-read-adopter.mdbrings the full-project adopter-simulation cold-read prompt in-repo (previously BookStack-only) — the prompt used to prime outside-model reviews of the whole toolkit.docs/cold-read-prompt.mdis renamed todocs/cold-read-changelog.md(distinguisher-leads naming, matching its# Changelog Cold-Read Promptheading); inbound references updated (#366).Mechanized pre-merge register grep-gate (
scripts/register-check.sh+reusable-register-check.yml). Case-insensitive sweep acrossscripts/+.forgejo/+tests/+docs/+changelog.d/+README.md+CHANGELOG.md+AGENTS.mdfor chamber-name attribution- invented-jargon vocabulary. Fails PR merges on hits; extensible
pattern list (bash array) +.register-allowlistfor load-bearing exceptions. Closes #392.
Empirical rationale: manual per-round sweeps don't stay closed under continued PR throughput. Round-4 alone saw n≥3 same-day re-seedings across concurrent-authorship PRs — careful authors crediting reviewers inline is the recurring failure mode. Manual sweep as a discipline is falsified at this cadence; CI grep-gate is the fix by construction.
Adopter wiring: drop
.forgejo/workflows/register-check.ymlinvokingfrankenbit/release-toolkit/.forgejo/workflows/reusable-register-check.yml@v1.0.0on yourpull_request: branches: [main]trigger. Allow-list at.register-allowlist(glob patterns; directory-prefixdir/for recursive allow) opts out load-bearing exceptions like compiled-changelog historical records or maintainer archaeology.Changed
Adopter-visible header comments tightened — top-of-file comment blocks in
reusable-mirror-to-codeberg.ymland toolkit-self'srelease.ymlscrubbed of internal-review vocabulary ("substrate-of-record", "dogfoods its own new mechanic") + shortened. Consumer wiring pattern + design references preserved; canonical vocabulary in ADRs untouched. Set J adopter-hygiene sweep (#313).BREAKING (v1.0.0-blocking):
publish_modedefault flipped fromimmediateback todraft. Cold-read finding: README advertised "gates every release behind a human" but v0.10.0 immediate-default let the merge of the rolling PR publish directly, no Gate-3 click. The fix restoresdraftas the default so the advertised safety property holds by default. Adopters who value frictionless CI cadence over the per-cut Gate-3 opt in viapublish_mode: 'immediate'at the workflow_call layer. Adopters currently relying on the immediate-default must explicitly opt in when bumping to this release. (#332)docs/integration.md— tighten the version-file-basename scope note: the two#252refs (now closed as superseded) point at the broader-ecosystem tracker #337, which covers TOML (pyproject/Cargo), Maven, Helm, and other release-please-shaped ecosystems. Also names a workable escape hatch for unlisted ecosystems today (version_files: []+ consumer hook).docs/integration.md— new Token Decision Matrix (3 yes/no questions → adopter's token setup with minimum scope), Sample failure modes section naming the 4 concrete errors adopters actually hit (Repository not found,push_whitelist pre-flight FAIL, case-sensitive push-denied,mergeable=blockedon manifest PR), Bot user provisioning step-by-step for path α (Forgejo user + collaborator + PAT scope + whitelist + secret), and a concretesecrets: inheritwiring example. Retires the "token model is complicated" trust-dent surfaced by external cold-read round 2 (#356).README.md+docs/integration.md— new Runner requirements section naming explicitly what the toolkit assumes: Debian/Ubuntu-family runner withapt-get+sudofor install-on-demand ofyq. Per-requirement table names root cause + what to do if your runner is non-Debian / rootless / ephemeral (pre-bakeyq+jq+curlso the install step is a no-op). Configurable install commands + auto-skip logic named as v1.1.0+ target. Retires the "workflow installs deps with apt-get/sudo, not universally safe" trust-dent surfaced by external cold-read round 2 (#357).docs/integration.md— new Tag creation section documenting the mechanism explicitly: Forgejo release API creates the tag viatag_name+target_commitish(nogit tag && git push --tags); tag points at the rolling-PR merge commit; idempotency behavior enumerated (same-tag-same-commit skips; same-tag-different-commit fails loud; same-tag-on-Forgejo-missing-local skips via pre-check); cross-variant status per Forgejo/Codeberg/Gitea. Retires the "delegated tag creation needs proof" trust-dent surfaced by external cold-read round 2 (#359).scripts/release-decide.sh+scripts/lib/conventional-commits.sh+docs/integration.md+README.md+examples/README.md+ tests — BREAKING for the fresh-repo failure surface, no regression for repos with a real anchor: fresh consumer repo (no manifest, no stable tag) now bootstraps from a virtualv0.0.0anchor and walks from the initial commit (#368). First cut producesv0.1.0(anyfeat:since root),v0.0.1(onlyfix:), ormode=noop(onlychore:/docs:/refactor:— chore isn't release-relevant per conventional-commits; adopter adds afeat:/fix:or drops abump/*label to force the cut). SUPERSEDES the #355 Option B fail-loud path shipped in v0.25.0: adopters no longer need to manually tagv0.0.0before the first cut.cc_list_commits_sincegains a--rootsentinel so the virtual-anchor bump-walk stays root-inclusive even when a prerelease tag exists (prerelease v-tags matchedgit describe --match 'v*'and would silently drop the root feat: from the bump walk despite step-2 seeing it). Afeat!:/BREAKING CHANGEat root bumps tov1.0.0by default; setpre_v1_breaking_to_minor: truefor the pre-1.0 opt-in that downgrades tov0.1.0. Advanced adopters can still hand-write.release-toolkit-manifest.json; that path is unchanged.docs/VERSIONING.md+docs/SECURITY.md— retire the "as of v1.0.0" / "target v1.1.0" future-tense-in-current-adopter-surface leaks external cold-read round 3 flagged. VERSIONING.md rewritten to describe current pre-1.0 state (Option A) + a "will commit at v1.0.0" preamble frames the effective-at-v1.0.0 sections without ambiguity. SECURITY.md's architectural-fix section labeled "Planned for v1.1.0" with an explicit callout that it describes not-yet-shipped work (Option B — preserves the fix-target framing). Example pin swapped from@v1.0.0(aspirational) →@v0.25.0(current). Tag-immutability section reframed as "already active" (the discipline was already in force pre-1.0, not a v1.0.0 addition). Adopters reading v0.25.0 docs no longer confuse future contract with current guarantee (#369).Adopter-facing docs read register-clean — internal review-process language (per-reviewer attributions,
ratifyceremony) scrubbed fromCHANGELOG.md+docs/adr/*.md, so the project no longer reads as internally process-driven. Technical content and issue/ADR anchors are preserved and the documented cold-read editorial discipline is kept + glossed; only the internal-review narration is removed. Third register-audit delta after #352 + #362 (#372).README.md+docs/integration.md— new Positioning section declaring release-toolkit Codeberg-primary (source, releases, and tags at codeberg.org/frankenbit/release-toolkit) and targeting Forgejo Actions as its CI runtime, with a runtime-compatibility list (Forgejo Actions tested; Gitea Actions untested; Codeberg where a runner is available). This is the definitive answer to the adopter positioning question external cold-read raised (rounds 2–4): where the project lives (Codeberg) and what CI runtime it targets (Forgejo Actions) are stated as separate facts so neither is ambiguous. The maintainers develop on a self-hosted Forgejo and mirror code + releases to Codeberg (#358, #397).Fixed
examples/README.md+examples/{go,node}-project/.forgejo/workflows/*.yml— bumped stale@v0.16.0pins to the current released tag@v0.25.0, closing the trust-dent surfaced by external cold-read round 2 (adopters landing on stale copy-paste refs). Extendedscripts/hooks/update-doc-version-refs.shto walkexamples/**/*.yml+examples/README.mdon every future cut so the drift can't recur; the hook silently no-ops when a consumer doesn't have anexamples/directory (#311).Mirror workflow: empty-TAG_NAME guard as Forgejo-agnostic first-check. Forgejo Actions doesn't reliably honor
needs.<reusable-call-job>.outputsin a job-levelif:gate (empirical: mirror fired on non-cut push:main events), causing spurious workflow failures on every non-cut push despiteif: mode == 'cut'. Script-level guard exits cleanly on empty TAG_NAME + sidesteps the outputs-based gating question; wrong-commit class was already closed by the tag-guard (#322).SemVer parser now enforces the full SemVer 2.0.0 grammar — leading zeros in numeric identifiers, empty prerelease/build identifiers, and leading/trailing dots are rejected at parse time. Previous regex accepted these invalid values and then crashed on bump (bash-octal interpretation of
1.2.08→08: value too great for base). Base-10 arithmetic (10#$var) added tosemver_bumpas belt-and-suspenders. 20 new test cases cover every invalid-input class from the cold-read finding (#329).Squash-merge cut detection —
PREP_SUBJECT_REinrelease-decide.shnow accepts bothchore(release): prepare vX.Y.Z(merge/rebase style) andchore(release): vX.Y.Z(Forgejo squash-merge style using PR title). Layer 2 branch-source-check remains the belt-and-suspenders gate. Adopters following standard Git squash-merge conventions were silently losing their release cut before this fix (#331).Token docs cleanup — removed all references to the unimplemented
release_token(path β) that shipped in docs but never wired in the workflow. Rewrote the no-token path (γ) section: dedicated PAT (path α) is operationally required for repos with required-checks branch protection, not merely optional. Path (γ) fits only repos wheremainaccepts unrestricted merges. Docs now match actual workflow behavior (#333).Config schema version now enforced against supported set —
config_validateinscripts/lib/config.shmaintainsSUPPORTED_SCHEMA_VERSIONS=(1)and rejects unsupported versions with a clear error naming what's supported. Previously any integer passed (version: 999silently accepted). Forward-compat: when schema v2 arrives, add 2 to the array. +4 test cases (#335).scripts/repin.shno longer defaultsREPIN_REVIEWERto an internal reviewer username. The default is now empty and the PR review-request is skipped when unset, so an adopter runningrepin.shno longer opens a re-pin PR requesting review from a user that doesn't exist in their repo. SetREPIN_REVIEWER=<user>to request a specific reviewer. (#340)README.md+docs/integration.md+docs/SECURITY.md+docs/events-logging.md+docs/VERSIONING.md+docs/migration/tmux-tell.md+ ADRs 0001/0002/0003 — v1.0.0 pre-cut Cold-Read delta (#353): quick-startpublish_modeexample dropped so the reusable'sdraftdefault applies (was contradicting the "gates every release" claim, same class as #332); ADR-0003 now records the v1.0.0 supersession — the 2026-06-26 immediate-default amendment is retired, and the default-record matches the shipped code; adopter-facing docs no longer citeAGENTS.md(maintainer doc) — replaced with public references to the CI gate + inlined content; ADRs no longer linkalcatraz-infra#65(cross-repo private tracker) — replaced with plain-prose "initial architecture proposal";docs/VERSIONING.mdglosses paths α/γ +mode = noop|update|cutwith links to ADR-0007 / ADR-0004;docs/migration/tmux-tell.mdreframed as a pointer + tmux-tell-specific notes (drops "Status: stub" language).scripts/release-decide.sh+README.md+docs/integration.md+examples/README.md— BREAKING for docs-honesty, no behavior regression: fresh consumer repo (no.release-toolkit-manifest.jsonAND no stablevX.Y.Ztag) previously failed with a terseFATAL: no stable vX.Y.Z tag found; docs implied fresh repos "just worked" — external cold-read round 2 named this as the biggest trust-dent for first-user experience (#355). Fixed by (a) making the error actionable — names the exactgit tag -a v0.0.0 && git push origin v0.0.0command, points at the docs section, mentions the manifest hand-write escape hatch — and (b) documenting the anchor requirement upfront: new "Bootstrapping a new consumer repo" section indocs/integration.mdnaming both anchor options; README quick-start now has a "step 0" for the tag;examples/README.mdpost-copy checklist ends with the bootstrap command. Behavior unchanged: no code path that previously succeeded now fails; only the failure surface (message + docs coverage) improved..forgejo/workflows/reusable-release.yml+scripts/lib/forgejo-api.sh+scripts/lib/config.sh+docs/integration.md— layered default-branch resolution replaces the hardcoded"main"at the manifest follow-up PR path. Newresolve_default_branchhelper in forgejo-api.sh walks 6 fallback layers: (1)RT_DEFAULT_BRANCHenv var, (2)default_branch:field inrelease-toolkit.yml, (3)TOOLKIT_DEFAULT_BRANCH_HINTenv var (workflow YAML passes${{ github.event.repository.default_branch }}), (4) Forgejo APIGET /repos/{owner}/{repo}, (5)git symbolic-ref refs/remotes/origin/HEAD, (6) fail-loud with actionable error naming all 5 preceding layers. Newconfig_get_default_branchin config.sh exposes layer 2. Adopters on non-maindefault branches (trunk/master/develop/custom) previously hit a silent-corruption class: manifest PR opened against a nonexistent branch or one with unrelated content. Full docs indocs/integration.md § Default branch (#370). Regression tests cover all 6 layers (#370).scripts/lib/forgejo-api.sh+scripts/setup-bump-labels.sh— keep theFORGEJO_TOKENout of curl's argv (visible via/proc/<pid>/cmdline+pson some systems). All 6 previously-argv-exposing curl invocations refactored to use a 0600-mode auth config file (install -m600,-K "$auth_config"), matching the pattern already established inreusable-mirror-to-codeberg.yml:184-193. New shared helper_forgejo_auth_config_newin the lib consolidates the mktemp+install+write dance across the 3 lib callsites;setup-bump-labels.shinlines the same shape at script start with atrapcleanup. Closes the argv-vs-config-file inconsistency between the two curl surfaces on the same substrate (#371)..forgejo/workflows/reusable-release.yml+scripts/release-prep.sh+scripts/repin.sh+scripts/manifest-precheck.sh— semantic-class sweep for hardcoded default-branch references missed by #370's literal-mainsweep. Path-α direct-push (2 sites) +git_push_authedhelper (1 site) + repin PR base + manifest-precheck remote-read: all now useresolve_default_branch(6-layer chain) orRT_DEFAULT_BRANCHenv var. Adopters on non-maindefault branches (trunk/master/develop/custom) no longer hit silent-corruption at any push callsite. Newtests/hardcoded-defaults-sweep.bats(7 grep-guards) locks the class-axis so future edits can't reintroduce the anti-patterns (#379).scripts/release-decide.sh+docs/integration.md—.release-toolkit-manifest.jsonanchor precedence rule for thelast_released_shavslast_released_tagdisagreement class. When both fields are set AND the local tag exists AND its SHA disagrees withlast_released_sha,release-decide.shnow fails loud with an actionable error naming both SHAs + three resolution options (tag authoritative / manifest authoritative / re-bootstrap by deleting manifest). Shallow clones and tags-pruned setups where the tag isn't local are treated as "cannot verify" — the check is skipped rather than erroring, so those adopters aren't blocked. Docs section explains the three divergence scenarios (force-push after cut / manual tag surgery / cross-repo mirror desync) + why v1.0.0 ships fail-loud rather than silent-manifest-wins (#380)..forgejo/workflows/reusable-release.yml+scripts/release-prep.sh— allgit -c http.extraheader="Authorization: token $TOKEN" pushargv-token exposures refactored to write the extraheader to amktemp+chmod 600config file scoped viaGIT_CONFIG_GLOBAL. Same semantic class as #371 (curl argv-token → config file) and alcatraz-infra#89 (token in git remote URL): grep-sweep-missed-sibling because those sweeps targeted their specific literal patterns, not the semantic class of "token visible outside process env." Token now stays in the mode-0600 file + env (process-owned), never in argv — invisible tops auxww+ auditd audit logs. Regression test intests/hardcoded-defaults-sweep.bats(grep-guard ongit -c ... http.extraheaderpattern) locks the class-axis (#381).scripts/draft-release.shheader comment (lines 1-8) refreshed to describe current v1.0.0 behavior:publish_modedefault isdraft(per #332) notimmediateas the pre-#332 header claimed; the "cut path merges to main" phrasing now points at the consumer's resolved default branch (per #370) rather than hardcodingmain(#383).docs/integration.md § Dry-run previewrewritten to accurately describe the three-script dry-run flow (release-decide.sh/release-prep.sh/draft-release.sh) — what mutates locally (release-prep.sh transitions CHANGELOG + bumps version_files in place), what emits summaries (draft-release.sh Forgejo APIMETHOD POST / URL / BODY), what skips (git remote writes + Forgejo API calls). Corrects the pre-fix "still needs an origin remote" note: origin is NOT needed for the git-log walk (release-decide.sh readslast_released_shafrom the LOCAL manifest + walks LOCAL history); origin IS used for OWNER/REPO derivation (dry-run substitutesOWNER/REPOplaceholder if missing) + Forgejo API calls (dry-run summarizes). Remote-less clones work for dry-run (#385).- security+workflow: semantic-class sweep for hardcoded-main + argv-token (#379 + #381) (#386)
- workflow: move #370 env vars to correct step + wire config_get + docstrings
- tests: preserve v0.10.0+ default marker in mutation-guard fixture
Internal
Docs reclassify —
docs/cold-read/,docs/drift-audit-*, anddocs/walkthrough-*moved todocs/internal/. Adopter-facing rootdocs/now contains only canonical adopter documentation (integration, conventions, operations, architecture, adr, migration); maintainer audits + walkthrough artifacts live underdocs/internal/with a bucket-README explaining the split (#312).Workflow comments scrubbed of internal incident-history — the
.forgejo/workflows/*.ymldocblocks and inline comments no longer carry accumulated bug-archaeology (probe-iteration logs, dated cross-repo incident recoveries, version-specific cut-failure narration, internal review-vocabulary). Load-bearing invariants and issue/ADR anchors are kept; only the narration is removed, so the reusable workflows read as auditable machinery rather than internal history (#340).Script, workflow, and test comment blocks scrubbed of internal review-role attributions (per-reviewer names + short hex-id tags). Round-3 (#372) closed the prose surface (README + docs/ + ADRs + CHANGELOG); this closes the sibling code-comment surface —
scripts/*.sh+.forgejo/workflows/*.yml+tests/*.bats— that an adopter reads when auditing the toolkit. Load-bearing technical invariants and issue/ADR anchors are kept; only the attribution narration is removed, with no behavior change (one test fixture genericized behavior-preservingly, mutation-verified green). (#384)AGENTS.md(contributor pre-flight doc) scrubbed of internal review-role attributions + invented-jargon — the last manual surface in the register-audit sweep (sibling to #384). Per-reviewer name+hex attributions removed from the disciplines (the guidance content is kept), the historical convergence-arc section trimmed of its internal narration (catch-count table + forward-going guidance kept), and a functional reviewer-identity default genericized. Round-3 held this doc as maintainer-surface; round-4 external reached it, so it's covered now (#391).docs/internal/— moved the point-in-time maintainer archaeology (the 2026-06-27 consumer-walkthrough audit and documentation-drift audit) out of the source tree to the maintainers' internal wiki, and cleaned the remainingcold-read/framework of internal-review-role names so a source-tree browser sees a neutral external-doc-review framework. Part of the Codeberg-primary shift: source-tree visibility is now the adopter-facing surface, so point-in-time internal-process artifacts are archived off-tree while the activecold-read/maintainer tool stays in-repo, provenance preserved (#398).Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- invented-jargon vocabulary. Fails PR merges on hits; extensible
-
Release v0.25.0
Stablereleased this
2026-07-03 12:44:58 +02:00 | 1194 commits to main since this releaseAdded
Codeberg-mirror reusable workflow —
.forgejo/workflows/reusable-mirror-to-codeberg.ymlpublishes each cut's release to the Codeberg mirror. Toolkit-self wires it inrelease.ymlas aneeds: releasejob gated onmode == 'cut'. Idempotent (GET-before-POST) + warn-not-fail on Codeberg errors (#309).Historical mirror script for Codeberg (dev-tool). One-shot migration of existing releases to
codeberg.org/FrankenBit/release-toolkitviascripts/dev-tools/migrate-releases-to-codeberg.sh. Idempotent + rate-limit-aware + order-preserving (#310).Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)
-
Release v0.24.0
StableAll checks were successfulchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4scheck-self-bootstrap / check (pull_request) Has been skippedchangelog-body-check / check (pull_request) Successful in 0sfragment-check / changelog fragment-kind (pull_request) Successful in 4sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0scheck-self-bootstrap / check (push) Successful in 4srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0sreleased this
2026-07-03 10:49:44 +02:00 | 1199 commits to main since this releaseChanged
check-self-bootstrapnow skips onrelease-prep/*rolling PRs. The rolling PR is the surface where "the mechanism that would fix the drift IS the drift" — the check fires genuine content-differences that aren't actionable AT that PR (the next cut resolves them via #283 auto-re-pin). Regular feature PRs andpush:mainkeep firing the check unchanged; only release-bot'srelease-prep/head branches are exempted. Per operator directive: zero-failing-runs on release PRs as a general goal. Closes #304.Fixed
- ci: harden #304 rolling-PR exemption per Surveyor 41ce
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Release v0.23.0
StableSome checks failedchangelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4schangelog-body-check / check (pull_request) Successful in 0scheck-self-bootstrap / check (pull_request) Failing after 3sfragment-check / changelog fragment-kind (pull_request) Successful in 4sfragment-check / check (pull_request) Successful in 0smanifest-check / manifest-vs-tag consistency (pull_request) Successful in 5smanifest-check / check (pull_request) Successful in 0scheck-self-bootstrap / check (push) Failing after 4srelease / decide + act (push) Successful in 6srelease / release (push) Successful in 0sreleased this
2026-07-02 23:41:14 +02:00 | 1206 commits to main since this releasePost-v0.22.0 correctness fix: retire the release-notes sidecar so edits to
CHANGELOG.mdactually reach the Forgejo release page.Removed
BREAKING:
.release-toolkit-release-notes.mdsidecar retired — CHANGELOG.md is the single source of truth for the release-body content. The v0.5.0-introduced sidecar (fed by fragmentsummary:frontmatter) was preferred bydraft-release.shover the CHANGELOG section, so edits toCHANGELOG.mdwere silently ignored on the release page.draft-release.shnow always reads the CHANGELOG section.release-prep.shno longer writes a sidecar and deletes any legacy committed sidecar on the first post-#301 cut so the file stops shipping stale content.summary:frontmatter on fragments becomes inert (safe to leave or strip). Closes #301.Upgrade
If your repo has committed a
.release-toolkit-release-notes.mdfile from a pre-v0.23.0 cut, delete it:git rm .release-toolkit-release-notes.md && git commit -m "chore: drop retired sidecar per release-toolkit#301"draft-release.shignores the file post-v0.23.0, so leaving it in place is a no-op (not an error). Nothing else needed to upgrade.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
Source code (ZIP)