• v0.30.2 fea688f024

    Release v0.30.2
    All checks were successful
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 3s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / changelog fragment-kind (pull_request) Successful in 3s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    register-check / register-drift check (pull_request) Successful in 4s
    register-check / check (pull_request) Successful in 0s
    tests / bats (pull_request) Successful in 1m55s
    tests / shellcheck (pull_request) Successful in 7s
    check-self-bootstrap / check (push) Successful in 3s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 1m55s
    tests / shellcheck (push) Successful in 7s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-07-07 18:56:16 +02:00 | 1061 commits to main since this release

    Added

    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.sh bakes 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 old resolve-ref step emitted that value unconditionally as the checkout target; actions/checkout@v4 then failed trying to fetch a not-yet-created tag.

    Each resolve-ref step now scope-detects the consumer wrapper's uses:@<ref> value. When it is main, REF is overridden to main regardless 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_TAG value. @vX.Y.Z pins remain a frozen source-of-record per #336. Empirical anchor: v0.30.1 cut failed at 7a1e63cb. No action required for adopters. Closes #460.

    Removed

    None.

    Deprecated

    None.

    Upgrade

    None.

    Downloads
  • v0.30.1 7a1e63cbe7

    v0.30.1
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / changelog fragment-kind (pull_request) Successful in 3s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    register-check / register-drift check (pull_request) Successful in 4s
    register-check / check (pull_request) Successful in 0s
    tests / bats (pull_request) Successful in 1m55s
    tests / shellcheck (pull_request) Successful in 7s
    check-self-bootstrap / check (push) Successful in 3s
    release / decide + act (push) Failing after 38s
    release / release (push) Failing after 0s
    tests / bats (push) Successful in 1m56s
    tests / shellcheck (push) Successful in 8s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    quartermaster released this 2026-07-07 16:34:07 +02:00 | 1064 commits to main since this release

    Added

    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/*.yml pin uses:@main instead of a versioned tag. Every push to main runs against the current scripts substrate; no drift class can accumulate.

    Adopter behavior is unchanged. Adopters continue to pin uses:@vX.Y.Z to a released tag. The cut-tag artifact retains its baked BUILD_BAKED_TOOLKIT_REF value, so @vX.Y.Z remains a frozen source-of-record.

    Closes the shared substrate of the fix-cut-bootstrap-through-own-gate class. Class 1 (check-self-bootstrap firing red between cuts) closes via a PINNED_REF == 'main' early-exit in check-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 @main stay 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
  • v0.30.0 071e318888

    Release v0.30.0
    All checks were successful
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / changelog fragment-kind (pull_request) Successful in 3s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    register-check / register-drift check (pull_request) Successful in 4s
    register-check / check (pull_request) Successful in 0s
    tests / bats (pull_request) Successful in 1m55s
    tests / shellcheck (pull_request) Successful in 7s
    check-self-bootstrap / check (push) Successful in 3s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 1m55s
    tests / shellcheck (push) Successful in 8s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-07-07 12:59:26 +02:00 | 1069 commits to main since this release

    Added

    Cut-time preflight verifies the composed CHANGELOG section is gate-clean before the release fires. A new scripts/compose-verify.sh runs at the top of the cut case in reusable-release.yml. It scans the target section with register-check --stdin and changelog-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-bootstrap no 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.sh now scaffolds all 6 standard CHANGELOG sections in the composed body. When fragments produce only a subset (e.g., only Added + Fixed), the missing sections are filled with None. before the section transitions into CHANGELOG.md. Aligns compose to the documented changelog-body-check check-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
  • v0.29.0 56bf27b44a

    Release v0.29.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 3s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / changelog fragment-kind (pull_request) Successful in 3s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    register-check / register-drift check (pull_request) Failing after 4s
    register-check / check (pull_request) Failing after 0s
    tests / bats (pull_request) Successful in 1m52s
    tests / shellcheck (pull_request) Successful in 7s
    check-self-bootstrap / check (push) Failing after 3s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 1m52s
    tests / shellcheck (push) Successful in 8s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-07-07 09:55:05 +02:00 | 1081 commits to main since this release

    Added

    Compose-time register-scrub on commit-subject → CHANGELOG bullets. Chamber-name attributions in commit subjects used to ride through cc_categorize_commits_since into 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-existing register-check --stdin FATAL in release-prep.sh stays 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-check now 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 #NNN with a word-boundary guard. It catches every ref shape — trailer, bolded scope, inline close-verb, comma-separated multi-refs. #4380 still does not match fragment #438. No action required. Closes #441.

    • release-prep: Bundle 1 v0.28.1 must-fix — target section + dedup + register-scrub [#440 #441 #442]
    Downloads
  • v0.28.0 f759909bcb

    Release v0.28.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
    changelog-body-check / check (pull_request) Successful in 0s
    check-self-bootstrap / check (pull_request) Has been skipped
    fragment-check / changelog fragment-kind (pull_request) Successful in 6s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 8s
    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 1m51s
    tests / shellcheck (pull_request) Successful in 7s
    check-self-bootstrap / check (push) Failing after 4s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 1m51s
    tests / shellcheck (push) Successful in 12s
    mirror-on-cut / mirror-1 (release) Successful in 1s
    mirror-on-cut / mirror (release) Successful in 0s
    Stable

    release-bot released this 2026-07-06 21:12:56 +02:00 | 1086 commits to main since this release

    v0.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 by register-check sometimes legitimately use pattern words in adopter-relevant contexts — a role title in a doc, a program name in a script. A trailing # register-check: allow marker suppresses that one line's match (<!-- register-check: allow --> in Markdown and HTML). No action required. Scrubbing the pattern remains first-resort; .register-allowlist at file scope is second-resort; the line marker is the last resort. The --stdin scan called from release-prep.sh ignores 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.md past the file-scan gate. The compose step read subjects the file scan never touched. Two new modes close the gap. --git-log RANGE scans commit subjects at PR review time. --stdin scans arbitrary text and runs from release-prep.sh on the composed section before it lands in CHANGELOG.md, catching drift from any source. Recommended for adopters using commit-subject → CHANGELOG composition (cc_list_commits_since / cc_categorize_commits_since): set check_commit_subjects: 'true' on the reusable-register-check.yml input. Fragments-only adopters can leave the default ('false'). Closes #403.

    Fragment authors can hard-wrap paragraphs without breaking the rendered CHANGELOG. release-prep.sh now normalizes compiled sections before they transition into CHANGELOG.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-check now 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 on reusable-changelog-body-check.ymlsentence_max_words, sentence_warn_words, paren_depth_max, paragraph_max_words, paragraph_warn_words. Author-side guidance lives in docs/conventions.md "Writing adopter-facing prose". Closes #421.

    Changed

    Orphan-check now catches BREAKING CHANGE: body-footer commits. A refactor: or chore: commit whose body carries a BREAKING CHANGE: footer is bump-worthy per conventional-commits. The pre-fix range-walk in release-decide.sh keyed only on the subject and false-skipped them. release-decide.sh now delegates release-relevance to cc_bump_level_from_subject in scripts/lib/conventional-commits.sh. The helper handles subject-level feat! / fix! markers AND body-level BREAKING 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-bootstrap docstrings 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.sh with 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.sh now reads HEAD_SUBJECT early and matches it against PREP_SUBJECT_RE. When HEAD IS a prep-PR merge commit AND the version it cuts equals CHANGELOG_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) and chore(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.yml triggers on the release: published event. It fires either on immediate-mode publication, or when the operator clicks Publish on a draft.

    The in-graph mirror: job is removed from release.yml. It combined continue-on-error: true with a uses: 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 a mirror: job inside release.yml. The reusable-mirror-to-codeberg.yml docstring shows the new pattern. Closes #427.

    Removed

    None.

    Deprecated

    None.

    Upgrade

    • Optional: set check_commit_subjects: 'true' on reusable-register-check.yml if 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.ymlsentence_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 inline mirror: job in release.yml. Toolkit-self's own mirror-on-cut.yml is a working example. See #427.
    Downloads
  • v0.27.0 b36955e148

    v0.27.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / changelog fragment-kind (pull_request) Successful in 4s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    register-check / register-drift check (pull_request) Successful in 4s
    register-check / check (pull_request) Successful in 0s
    tests / bats (pull_request) Successful in 1m47s
    tests / shellcheck (pull_request) Successful in 8s
    check-self-bootstrap / check (push) Successful in 3s
    release / decide + act (push) Failing after 5s
    release / release (push) Failing after 0s
    release / mirror (push) Failing after 0s
    tests / bats (push) Successful in 1m47s
    tests / shellcheck (push) Successful in 8s
    Stable

    bosun released this 2026-07-06 14:12:12 +02:00 | 1110 commits to main since this release

    v0.27.0 hardens release-toolkit for real Forgejo and Codeberg adoption ahead of v1.0: bounded, retryable API calls with pagination; per-project publish_mode overrides; 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 set FORGEJO_API_RETRY_UNSAFE=1 (#334; v1.0.0 must-fix).

    You can now change publish_mode per project without editing your workflow: set vars.RT_PUBLISH_MODE in Forgejo → Settings → Actions → Variables, or add publish_mode: to release-toolkit.yml. Precedence, highest first: repo variable → config file → workflow with: input → the draft default (#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.Z placeholders 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 concurrency block 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.md now includes a workflow-level concurrency: block (group: release-cut-${{ github.ref }}, cancel-in-progress: false) that queues later pushes behind the in-flight cut.
    • Detection (toolkit-side): release-decide.sh fails loud when CHANGELOG.md's top-most released section is ahead of manifest.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
  • v0.26.0 512aa1a408

    Release v0.26.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
    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 4s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    register-check / register-drift check (pull_request) Successful in 4s
    register-check / check (pull_request) Successful in 0s
    tests / bats (pull_request) Successful in 1m33s
    tests / shellcheck (pull_request) Successful in 8s
    check-self-bootstrap / check (push) Failing after 3s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    tests / bats (push) Successful in 1m34s
    tests / shellcheck (push) Successful in 12s
    release / mirror (push) Successful in 2s
    Stable

    release-bot released this 2026-07-05 16:46:34 +02:00 | 1130 commits to main since this release

    Added

    CI test suite as required merge gate — new .forgejo/workflows/tests.yml runs full bats tests/ + shellcheck --severity=warning on every PR + push:main. ADR-0001 named shellcheck as CI gate; this workflow implements it. One pre-existing SC1010 false-positive in scripts/lib/forgejo-api.sh (jq variable named do) fixed by rename to merge_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.md brings 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.md is renamed to docs/cold-read-changelog.md (distinguisher-leads naming, matching its # Changelog Cold-Read Prompt heading); inbound references updated (#366).

    Mechanized pre-merge register grep-gate (scripts/register-check.sh + reusable-register-check.yml). Case-insensitive sweep across scripts/ + .forgejo/ + tests/ + docs/ + changelog.d/ + README.md + CHANGELOG.md + AGENTS.md for chamber-name attribution

    • invented-jargon vocabulary. Fails PR merges on hits; extensible
      pattern list (bash array) + .register-allowlist for 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.yml invoking frankenbit/release-toolkit/.forgejo/workflows/reusable-register-check.yml@v1.0.0 on your pull_request: branches: [main] trigger. Allow-list at .register-allowlist (glob patterns; directory-prefix dir/ 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.yml and toolkit-self's release.yml scrubbed 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_mode default flipped from immediate back to draft. 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 restores draft as the default so the advertised safety property holds by default. Adopters who value frictionless CI cadence over the per-cut Gate-3 opt in via publish_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 #252 refs (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=blocked on manifest PR), Bot user provisioning step-by-step for path α (Forgejo user + collaborator + PAT scope + whitelist + secret), and a concrete secrets: inherit wiring 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 with apt-get + sudo for install-on-demand of yq. Per-requirement table names root cause + what to do if your runner is non-Debian / rootless / ephemeral (pre-bake yq+jq+curl so 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 via tag_name + target_commitish (no git 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 virtual v0.0.0 anchor and walks from the initial commit (#368). First cut produces v0.1.0 (any feat: since root), v0.0.1 (only fix:), or mode=noop (only chore:/docs:/refactor: — chore isn't release-relevant per conventional-commits; adopter adds a feat:/fix: or drops a bump/* label to force the cut). SUPERSEDES the #355 Option B fail-loud path shipped in v0.25.0: adopters no longer need to manually tag v0.0.0 before the first cut. cc_list_commits_since gains a --root sentinel so the virtual-anchor bump-walk stays root-inclusive even when a prerelease tag exists (prerelease v-tags matched git describe --match 'v*' and would silently drop the root feat: from the bump walk despite step-2 seeing it). A feat!: / BREAKING CHANGE at root bumps to v1.0.0 by default; set pre_v1_breaking_to_minor: true for the pre-1.0 opt-in that downgrades to v0.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, ratify ceremony) scrubbed from CHANGELOG.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.0 pins 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). Extended scripts/hooks/update-doc-version-refs.sh to walk examples/**/*.yml + examples/README.md on every future cut so the drift can't recur; the hook silently no-ops when a consumer doesn't have an examples/ directory (#311).

    Mirror workflow: empty-TAG_NAME guard as Forgejo-agnostic first-check. Forgejo Actions doesn't reliably honor needs.<reusable-call-job>.outputs in a job-level if: gate (empirical: mirror fired on non-cut push:main events), causing spurious workflow failures on every non-cut push despite if: 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.0808: value too great for base). Base-10 arithmetic (10#$var) added to semver_bump as belt-and-suspenders. 20 new test cases cover every invalid-input class from the cold-read finding (#329).

    Squash-merge cut detection — PREP_SUBJECT_RE in release-decide.sh now accepts both chore(release): prepare vX.Y.Z (merge/rebase style) and chore(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 where main accepts unrestricted merges. Docs now match actual workflow behavior (#333).

    Config schema version now enforced against supported set — config_validate in scripts/lib/config.sh maintains SUPPORTED_SCHEMA_VERSIONS=(1) and rejects unsupported versions with a clear error naming what's supported. Previously any integer passed (version: 999 silently accepted). Forward-compat: when schema v2 arrives, add 2 to the array. +4 test cases (#335).

    scripts/repin.sh no longer defaults REPIN_REVIEWER to an internal reviewer username. The default is now empty and the PR review-request is skipped when unset, so an adopter running repin.sh no longer opens a re-pin PR requesting review from a user that doesn't exist in their repo. Set REPIN_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-start publish_mode example dropped so the reusable's draft default 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 cite AGENTS.md (maintainer doc) — replaced with public references to the CI gate + inlined content; ADRs no longer link alcatraz-infra#65 (cross-repo private tracker) — replaced with plain-prose "initial architecture proposal"; docs/VERSIONING.md glosses paths α/γ + mode = noop|update|cut with links to ADR-0007 / ADR-0004; docs/migration/tmux-tell.md reframed as a pointer + tmux-tell-specific notes (drops "Status: stub" language).

    scripts/release-decide.sh + README.md + docs/integration.md + examples/README.mdBREAKING for docs-honesty, no behavior regression: fresh consumer repo (no .release-toolkit-manifest.json AND no stable vX.Y.Z tag) previously failed with a terse FATAL: 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 exact git tag -a v0.0.0 && git push origin v0.0.0 command, 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 in docs/integration.md naming both anchor options; README quick-start now has a "step 0" for the tag; examples/README.md post-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. New resolve_default_branch helper in forgejo-api.sh walks 6 fallback layers: (1) RT_DEFAULT_BRANCH env var, (2) default_branch: field in release-toolkit.yml, (3) TOOLKIT_DEFAULT_BRANCH_HINT env var (workflow YAML passes ${{ github.event.repository.default_branch }}), (4) Forgejo API GET /repos/{owner}/{repo}, (5) git symbolic-ref refs/remotes/origin/HEAD, (6) fail-loud with actionable error naming all 5 preceding layers. New config_get_default_branch in config.sh exposes layer 2. Adopters on non-main default 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 in docs/integration.md § Default branch (#370). Regression tests cover all 6 layers (#370).

    scripts/lib/forgejo-api.sh + scripts/setup-bump-labels.sh — keep the FORGEJO_TOKEN out of curl's argv (visible via /proc/<pid>/cmdline + ps on 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 in reusable-mirror-to-codeberg.yml:184-193. New shared helper _forgejo_auth_config_new in the lib consolidates the mktemp+install+write dance across the 3 lib callsites; setup-bump-labels.sh inlines the same shape at script start with a trap cleanup. 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-main sweep. Path-α direct-push (2 sites) + git_push_authed helper (1 site) + repin PR base + manifest-precheck remote-read: all now use resolve_default_branch (6-layer chain) or RT_DEFAULT_BRANCH env var. Adopters on non-main default branches (trunk/master/develop/custom) no longer hit silent-corruption at any push callsite. New tests/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.json anchor precedence rule for the last_released_sha vs last_released_tag disagreement class. When both fields are set AND the local tag exists AND its SHA disagrees with last_released_sha, release-decide.sh now 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 — all git -c http.extraheader="Authorization: token $TOKEN" push argv-token exposures refactored to write the extraheader to a mktemp+chmod 600 config file scoped via GIT_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 to ps auxww + auditd audit logs. Regression test in tests/hardcoded-defaults-sweep.bats (grep-guard on git -c ... http.extraheader pattern) locks the class-axis (#381).

    scripts/draft-release.sh header comment (lines 1-8) refreshed to describe current v1.0.0 behavior: publish_mode default is draft (per #332) not immediate as 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 hardcoding main (#383).

    docs/integration.md § Dry-run preview rewritten 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 API METHOD 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 reads last_released_sha from the LOCAL manifest + walks LOCAL history); origin IS used for OWNER/REPO derivation (dry-run substitutes OWNER/REPO placeholder 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-*, and docs/walkthrough-* moved to docs/internal/. Adopter-facing root docs/ now contains only canonical adopter documentation (integration, conventions, operations, architecture, adr, migration); maintainer audits + walkthrough artifacts live under docs/internal/ with a bucket-README explaining the split (#312).

    Workflow comments scrubbed of internal incident-history — the .forgejo/workflows/*.yml docblocks 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 remaining cold-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 active cold-read/ maintainer tool stays in-repo, provenance preserved (#398).

    Downloads
  • v0.25.0 97bb807648

    Release v0.25.0
    All checks were successful
    check-self-bootstrap / check (push) Successful in 3s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    release / mirror (push) Successful in 2s
    Stable

    release-bot released this 2026-07-03 12:44:58 +02:00 | 1194 commits to main since this release

    Added

    Codeberg-mirror reusable workflow — .forgejo/workflows/reusable-mirror-to-codeberg.yml publishes each cut's release to the Codeberg mirror. Toolkit-self wires it in release.yml as a needs: release job gated on mode == '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-toolkit via scripts/dev-tools/migrate-releases-to-codeberg.sh. Idempotent + rate-limit-aware + order-preserving (#310).

    Downloads
  • v0.24.0 acb6db8521

    Release v0.24.0
    All checks were successful
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
    check-self-bootstrap / check (pull_request) Has been skipped
    changelog-body-check / check (pull_request) Successful in 0s
    fragment-check / changelog fragment-kind (pull_request) Successful in 4s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    check-self-bootstrap / check (push) Successful in 4s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    Stable

    release-bot released this 2026-07-03 10:49:44 +02:00 | 1199 commits to main since this release

    Changed

    check-self-bootstrap now skips on release-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 and push:main keep firing the check unchanged; only release-bot's release-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
  • v0.23.0 11c990bbd2

    Release v0.23.0
    Some checks failed
    changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 4s
    changelog-body-check / check (pull_request) Successful in 0s
    check-self-bootstrap / check (pull_request) Failing after 3s
    fragment-check / changelog fragment-kind (pull_request) Successful in 4s
    fragment-check / check (pull_request) Successful in 0s
    manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
    manifest-check / check (pull_request) Successful in 0s
    check-self-bootstrap / check (push) Failing after 4s
    release / decide + act (push) Successful in 6s
    release / release (push) Successful in 0s
    Stable

    release-bot released this 2026-07-02 23:41:14 +02:00 | 1206 commits to main since this release

    Post-v0.22.0 correctness fix: retire the release-notes sidecar so edits to CHANGELOG.md actually reach the Forgejo release page.

    Removed

    BREAKING: .release-toolkit-release-notes.md sidecar retired — CHANGELOG.md is the single source of truth for the release-body content. The v0.5.0-introduced sidecar (fed by fragment summary: frontmatter) was preferred by draft-release.sh over the CHANGELOG section, so edits to CHANGELOG.md were silently ignored on the release page. draft-release.sh now always reads the CHANGELOG section. release-prep.sh no 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.md file 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.sh ignores the file post-v0.23.0, so leaving it in place is a no-op (not an error). Nothing else needed to upgrade.

    Downloads