chore(release): v0.1.0 #2

Merged
alex merged 3 commits from release-prep/v0.1.0 into main 2026-06-24 15:13:25 +02:00

First release.

Added

  • The first release of release-toolkit — a shared, Forgejo-native
    release pipeline you reference instead of copy-pasting release
    machinery into every repo. It cuts releases, transitions your
    CHANGELOG, and runs a CI check that catches version-number drift.
    Consumers reference
    frankenbit/release-toolkit/.forgejo/workflows/_*.yml@v0.1.0 to
    adopt the full release flow — with human-approval gates at each
    stage — without copy-pasting the machinery. See
    docs/integration.md for the 4-step adoption
    guide and docs/conventions.md for the
    conventional-commits + changelog.d/ fragment formats.

  • Two ways to write your CHANGELOG, mixable: conventional commits
    (feat: / fix: / feat!: / etc.) read from git log, and
    hand-written changelog.d/<id>.<kind>.md fragment files. Use
    either or both; the toolkit merges them so a section heading never
    appears twice. See
    ADR-0002 for
    the both-mechanisms reasoning.

  • Multi-stage release flow with human-approval gates at each
    stage: workflow_dispatch → release-prep PR → Forgejo draft release
    → operator clicks Publish → release:published event → deploy.
    Four reusable Forgejo Actions workflows (_release-prep.yml +
    _release-draft.yml + _release-publish.yml +
    _manifest-check.yml) implement the four gates. See
    ADR-0003 for the
    design reasoning.

  • Built-in manifest-vs-tag CI check via _manifest-check.yml
    runs on every PR and catches the usual release-prep mistakes:
    version numbers out of sync across files, a missing [Unreleased]
    heading, or a manifest left behind the latest tag. The "manifest

    = tag, not strict equality" disposition (see
    scripts/manifest-check.sh --help) prevents false-positives on
    every legitimate release-prep PR.

  • Dry-run + operator preview: --dry-run skips remote mutations
    (no git push, no Forgejo PR creation) but DOES perform local
    CHANGELOG transitioning + version_file bumps + fragment deletion,
    so you can preview exactly what a release will do — the full git
    diff and the API payload — before anything runs. See
    docs/operations.md for the operator-preview
    disposition.

The toolkit ships as bash scripts plus 4 reusable Forgejo Actions
workflows; 179 unit + integration tests cover the substrate,
shellcheck-clean throughout.

Fixed

  • changelog_transition now merges sections by kind so a section
    heading never appears twice in a new release section when both the
    [Unreleased] content and the fragments contribute the same kind.

Internal

  • Extracted the version-section extraction logic into a reusable
    library helper for use by both release-prep.sh and
    draft-release.sh.
First release. ### Added - **The first release of release-toolkit** — a shared, Forgejo-native release pipeline you reference instead of copy-pasting release machinery into every repo. It cuts releases, transitions your CHANGELOG, and runs a CI check that catches version-number drift. Consumers reference `frankenbit/release-toolkit/.forgejo/workflows/_*.yml@v0.1.0` to adopt the full release flow — with human-approval gates at each stage — without copy-pasting the machinery. See [docs/integration.md](docs/integration.md) for the 4-step adoption guide and [docs/conventions.md](docs/conventions.md) for the conventional-commits + `changelog.d/` fragment formats. - **Two ways to write your CHANGELOG, mixable**: conventional commits (`feat:` / `fix:` / `feat!:` / etc.) read from git log, and hand-written `changelog.d/<id>.<kind>.md` fragment files. Use either or both; the toolkit merges them so a section heading never appears twice. See [ADR-0002](docs/adr/0002-conventional-commits-from-day-one.md) for the both-mechanisms reasoning. - **Multi-stage release flow with human-approval gates** at each stage: workflow_dispatch → release-prep PR → Forgejo draft release → operator clicks Publish → `release:published` event → deploy. Four reusable Forgejo Actions workflows (`_release-prep.yml` + `_release-draft.yml` + `_release-publish.yml` + `_manifest-check.yml`) implement the four gates. See [ADR-0003](docs/adr/0003-multi-stage-operator-gate.md) for the design reasoning. - **Built-in manifest-vs-tag CI check** via `_manifest-check.yml` — runs on every PR and catches the usual release-prep mistakes: version numbers out of sync across files, a missing `[Unreleased]` heading, or a manifest left behind the latest tag. The "manifest >= tag, not strict equality" disposition (see `scripts/manifest-check.sh --help`) prevents false-positives on every legitimate release-prep PR. - **Dry-run + operator preview**: `--dry-run` skips remote mutations (no git push, no Forgejo PR creation) but DOES perform local CHANGELOG transitioning + version_file bumps + fragment deletion, so you can preview exactly what a release will do — the full git diff and the API payload — before anything runs. See [docs/operations.md](docs/operations.md) for the operator-preview disposition. The toolkit ships as bash scripts plus 4 reusable Forgejo Actions workflows; 179 unit + integration tests cover the substrate, shellcheck-clean throughout. ### Fixed - `changelog_transition` now merges sections by kind so a section heading never appears twice in a new release section when both the `[Unreleased]` content and the fragments contribute the same kind. ### Internal - Extracted the version-section extraction logic into a reusable library helper for use by both `release-prep.sh` and `draft-release.sh`.
Generated by release-toolkit release-prep.sh.

Tracker: frankenbit/release-toolkit#1
Owner

Tone + audience pass — Herald (release-narrative lens)

Two threads per the operator's feedback: (A) concrete over abstract + (B) scrub internal references — the CHANGELOG should stand on its own; an outside adopter shouldn't need to know our internal projects/chambers. Proposed rewrites:

(B) Internal references to remove (the external-leaks)

  • "preserved from tmux-tell's pattern" (Multi-stage bullet) → drop the attribution; an adopter doesn't know tmux-tell. Describe the flow on its own merits.
  • "the deprecation policy follows the tmux-tell pattern" (SemVer policy) → "post-1.0, deprecated surfaces get a two-minor-cycle support floor before removal." (Keep the policy, drop the tmux-tell credit.)
  • "Closes frankenbit/cellblock#157 by adoption when cellblock migrates" (manifest-check bullet) → remove entirely. cellblock is an internal project; its migration-tracking belongs on the internal tracker, not the public CHANGELOG. The feature stands on its own.
  • "for the frankenbit organisation" (first bullet) → drop, or → "for any project" — the org name isn't meaningful to an external reader.
  • "scripts/lib/changelog.sh::changelog_merge_sections unifies the outputs" → drop the internal function-name (implementation detail) → "…so the same section heading never appears twice."
  • ADR/docs links (docs/integration.md, docs/adr/…) are fine — they're the toolkit's OWN docs (self-contained). Surveyor's self-sufficiency pass will confirm.

(A) Concrete-over-abstract rewrites

  • "Initial release-toolkit substrate for the frankenbit organisation — shared release-cut + CHANGELOG transitioning + manifest-vs-tag CI + Forgejo-native reusable workflows."
    "The first release of release-toolkit — a shared, Forgejo-native release pipeline you reference instead of copy-pasting release machinery into every repo. It cuts releases, transitions your CHANGELOG, and runs a CI check that catches version-number drift."
  • "adopt the multi-stage operator-gated release flow without copy-pasting the machinery"
    "adopt the full release flow — with human-approval gates at each stage — without copy-pasting the machinery."
  • "Two CHANGELOG sources, composable … for the both-axes-coverage reasoning."
    "Two ways to write your CHANGELOG, mixable: conventional commits (feat: / fix: / …) read from git log, and hand-written changelog.d/ fragment files. Use either or both; the toolkit merges them so a section heading never appears twice."
  • "Built-in manifest-vs-tag CI check … catches version_files drift + missing [Unreleased] sections + manifest-behind-tag regressions on every PR."
    "A built-in CI check that runs on every PR and catches the usual release-prep mistakes: version numbers out of sync across files, a missing [Unreleased] heading, or a manifest left behind the latest tag."
  • "Per the operator-preview-beats-fully-read-only-check disposition"
    "so you can preview exactly what a release will do — the full git diff and the API payload — before anything runs."
  • SemVer policy: "if the substrate shape needs to shift""if the toolkit's interfaces need to change."

Structural note (optional, from the tone-lens)

The granular scripts/lib: per-file bullets at the bottom of ### Added read as raw commit-subjects (internal file paths: config.sh, forgejo-api.sh, …). Consider folding them into the feature narrative above, or trimming to a line — an adopter cares what the toolkit does, not its internal file layout. (Structure-call; flagging from the tone-lens.)

Net: keep the necessary dev vocabulary (conventional-commits, CI, SemVer — the audience IS release-tool adopters), but frame it concretely + cut the internal-project references so the toolkit stands on its own.

— Herald

## Tone + audience pass — Herald (release-narrative lens) Two threads per the operator's feedback: **(A) concrete over abstract** + **(B) scrub internal references** — the CHANGELOG should stand on its own; an outside adopter shouldn't need to know our internal projects/chambers. Proposed rewrites: ### (B) Internal references to remove (the external-leaks) - **"preserved from tmux-tell's pattern"** (Multi-stage bullet) → drop the attribution; an adopter doesn't know tmux-tell. Describe the flow on its own merits. - **"the deprecation policy follows the tmux-tell pattern"** (SemVer policy) → "post-1.0, deprecated surfaces get a two-minor-cycle support floor before removal." (Keep the policy, drop the tmux-tell credit.) - **"Closes frankenbit/cellblock#157 by adoption when cellblock migrates"** (manifest-check bullet) → **remove entirely.** cellblock is an internal project; its migration-tracking belongs on the internal tracker, not the public CHANGELOG. The feature stands on its own. - **"for the frankenbit organisation"** (first bullet) → drop, or → "for any project" — the org name isn't meaningful to an external reader. - **"scripts/lib/changelog.sh::changelog_merge_sections unifies the outputs"** → drop the internal function-name (implementation detail) → "…so the same section heading never appears twice." - ADR/docs links (`docs/integration.md`, `docs/adr/…`) are fine — they're the toolkit's OWN docs (self-contained). Surveyor's self-sufficiency pass will confirm. ### (A) Concrete-over-abstract rewrites - *"Initial release-toolkit substrate for the frankenbit organisation — shared release-cut + CHANGELOG transitioning + manifest-vs-tag CI + Forgejo-native reusable workflows."* → **"The first release of release-toolkit — a shared, Forgejo-native release pipeline you reference instead of copy-pasting release machinery into every repo. It cuts releases, transitions your CHANGELOG, and runs a CI check that catches version-number drift."** - *"adopt the multi-stage operator-gated release flow without copy-pasting the machinery"* → **"adopt the full release flow — with human-approval gates at each stage — without copy-pasting the machinery."** - *"Two CHANGELOG sources, composable … for the both-axes-coverage reasoning."* → **"Two ways to write your CHANGELOG, mixable: conventional commits (`feat:` / `fix:` / …) read from git log, and hand-written `changelog.d/` fragment files. Use either or both; the toolkit merges them so a section heading never appears twice."** - *"Built-in manifest-vs-tag CI check … catches version_files drift + missing [Unreleased] sections + manifest-behind-tag regressions on every PR."* → **"A built-in CI check that runs on every PR and catches the usual release-prep mistakes: version numbers out of sync across files, a missing `[Unreleased]` heading, or a manifest left behind the latest tag."** - *"Per the operator-preview-beats-fully-read-only-check disposition"* → **"so you can preview exactly what a release will do — the full git diff and the API payload — before anything runs."** - SemVer policy: *"if the substrate shape needs to shift"* → **"if the toolkit's interfaces need to change."** ### Structural note (optional, from the tone-lens) The granular `scripts/lib:` per-file bullets at the bottom of `### Added` read as raw commit-subjects (internal file paths: `config.sh`, `forgejo-api.sh`, …). Consider folding them into the feature narrative above, or trimming to a line — an adopter cares what the toolkit *does*, not its internal file layout. (Structure-call; flagging from the tone-lens.) **Net:** keep the necessary dev vocabulary (conventional-commits, CI, SemVer — the audience IS release-tool adopters), but frame it concretely + cut the internal-project references so the toolkit stands on its own. — Herald
surveyor left a comment

Self-sufficiency pass (Surveyor) — CHANGELOG should stand alone for an outside adopter

My axis is the operator's point 2: does every reference make sense to a stranger who has no context on our internal projects/trackers? The audience here is someone evaluating/adopting release-toolkit from outside — they can follow links into this repo (ADRs, docs/, scripts/) but have zero context on tmux-tell, cellblock, alcatraz-infra, or our Forgejo trackers. Three concrete fixes + two notes. (Herald has the parallel tone/abstraction pass; where they overlap I flag but defer to that pass.)

3 internal references to fix (hard self-sufficiency)

1. SemVer policy — "the tmux-tell pattern"

Post-1.0 the deprecation policy follows the tmux-tell pattern: two-minor-cycle floor for deprecated surfaces.

tmux-tell is an internal project a stranger can't look up — and naming it adds nothing, because the policy itself is the substance. Rewrite:

Post-1.0, deprecated surfaces keep a two-minor-cycle floor before removal.

2. Added / release flow — "preserved from tmux-tell's pattern"

Multi-stage operator-gated release flow preserved from tmux-tell's pattern: workflow_dispatch → release-prep PR → Forgejo draft release → operator clicks Publish → release:published event → consumer's deploy.

The flow is already fully self-describing; the provenance attribution is internal-only. Drop the clause:

Multi-stage operator-gated release flow: workflow_dispatch → release-prep PR → Forgejo draft release → operator clicks Publish → release:published event → consumer's deploy.

3. Manifest-check — "Closes frankenbit/cellblock#157 … when cellblock migrates"

…manifest-behind-tag regressions on every PR. Closes [frankenbit/cellblock#157] by adoption when cellblock migrates. The "manifest >= tag…" disposition…

This is internal cross-project coordination — cellblock, #157, and "when cellblock migrates" are all invisible/meaningless to an external adopter, and a CHANGELOG documents what this release does, not which of our other repos will adopt it. The downstream-adoption link belongs on cellblock#157, not here. Remove the sentence:

…manifest-behind-tag regressions on every PR. The "manifest ≥ tag, not strict equality" rule (see scripts/manifest-check.sh --help) prevents false-positives on every legitimate release-prep PR.

2 notes

4. (Herald-adjacent) the "-disposition / -reasoning / -justification" framings — "both-axes-coverage reasoning", "operator-review-at-multiple-points justification", "operator-preview-beats-fully-read-only-check disposition". These are self-sufficient (each links to this repo's own ADR/docs), so not a hard fail — but a stranger hits a compound-noun-shorthand before the link tells them what it means. Plainer reads better, e.g. "See ADR-0003 for why the operator reviews at each gate." Flagging for the tone pass; defer to Herald.

5. (out of this diff, same axis) the repo description — "Built per alcatraz-infra#65" is the first thing an external visitor sees on the repo page, and alcatraz-infra#65 is an internal tracker. Worth a generic replacement there too whenever the description is next touched.

Everything else is genuinely self-sufficient — the Keep-a-Changelog/SemVer links, the @v0.1.0 pin guidance, the in-repo docs/+scripts/ references, and "for the frankenbit organisation" (honest provenance, and the consumer paths are frankenbit-scoped anyway). Net: drop two tmux-tell mentions + one cross-project cellblock#157 sentence and the CHANGELOG stands fully on its own.

## Self-sufficiency pass (Surveyor) — CHANGELOG should stand alone for an outside adopter My axis is the operator's point 2: does every reference make sense to a stranger who has no context on our internal projects/trackers? The audience here is **someone evaluating/adopting release-toolkit from outside** — they can follow links *into this repo* (ADRs, docs/, scripts/) but have zero context on tmux-tell, cellblock, alcatraz-infra, or our Forgejo trackers. Three concrete fixes + two notes. (Herald has the parallel tone/abstraction pass; where they overlap I flag but defer to that pass.) ### 3 internal references to fix (hard self-sufficiency) **1. SemVer policy — "the tmux-tell pattern"** > Post-1.0 the deprecation policy follows **the tmux-tell pattern**: two-minor-cycle floor for deprecated surfaces. `tmux-tell` is an internal project a stranger can't look up — and naming it adds nothing, because the *policy itself* is the substance. Rewrite: > Post-1.0, deprecated surfaces keep a **two-minor-cycle floor** before removal. **2. Added / release flow — "preserved from tmux-tell's pattern"** > Multi-stage operator-gated release flow **preserved from tmux-tell's pattern**: workflow_dispatch → release-prep PR → Forgejo draft release → operator clicks Publish → release:published event → consumer's deploy. The flow is already fully self-describing; the provenance attribution is internal-only. Drop the clause: > Multi-stage operator-gated release flow: workflow_dispatch → release-prep PR → Forgejo draft release → operator clicks Publish → release:published event → consumer's deploy. **3. Manifest-check — "Closes frankenbit/cellblock#157 … when cellblock migrates"** > …manifest-behind-tag regressions on every PR. **Closes [frankenbit/cellblock#157] by adoption when cellblock migrates.** The "manifest >= tag…" disposition… This is internal cross-project coordination — `cellblock`, `#157`, and "when cellblock migrates" are all invisible/meaningless to an external adopter, and a CHANGELOG documents *what this release does*, not which of our other repos will adopt it. The downstream-adoption link belongs on cellblock#157, not here. Remove the sentence: > …manifest-behind-tag regressions on every PR. The "manifest ≥ tag, not strict equality" rule (see `scripts/manifest-check.sh --help`) prevents false-positives on every legitimate release-prep PR. ### 2 notes **4. (Herald-adjacent) the "-disposition / -reasoning / -justification" framings** — "both-axes-coverage reasoning", "operator-review-at-multiple-points justification", "operator-preview-beats-fully-read-only-check disposition". These *are* self-sufficient (each links to this repo's own ADR/docs), so not a hard fail — but a stranger hits a compound-noun-shorthand before the link tells them what it means. Plainer reads better, e.g. *"See ADR-0003 for why the operator reviews at each gate."* Flagging for the tone pass; defer to Herald. **5. (out of this diff, same axis) the repo description** — "Built per **alcatraz-infra#65**" is the first thing an external visitor sees on the repo page, and `alcatraz-infra#65` is an internal tracker. Worth a generic replacement there too whenever the description is next touched. Everything else is genuinely self-sufficient — the Keep-a-Changelog/SemVer links, the `@v0.1.0` pin guidance, the in-repo `docs/`+`scripts/` references, and "for the frankenbit organisation" (honest provenance, and the consumer paths are frankenbit-scoped anyway). Net: drop two `tmux-tell` mentions + one cross-project `cellblock#157` sentence and the CHANGELOG stands fully on its own.
Per Herald 73190 + Surveyor 73192 reviews on PR #2. Both reviewers
flagged the same two threads:

## (B) Internal-reference scrub

- "from tmux-tell's pattern" attribution → dropped; workflow is
  self-describing
- "deprecation policy follows the tmux-tell pattern" → rewritten to
  "post-1.0, deprecated surfaces get a two-minor-cycle support floor
  before removal." Same policy; no project-internal attribution.
- "Closes frankenbit/cellblock#157 by adoption when cellblock migrates"
  → removed entirely. cellblock-migration tracking belongs on
  cellblock#157, not in a public CHANGELOG.
- "for the frankenbit organisation" → dropped
- "scripts/lib/changelog.sh::changelog_merge_sections unifies the
  outputs" → "the toolkit merges them so a section heading never
  appears twice" (drop internal function-name)
- SemVer-policy: "if the substrate shape needs to shift" → "if the
  toolkit's interfaces need to change"

## (A) Concrete-over-abstract rewrites

Each feature bullet rewritten per Herald's specific before→after
language. Examples:
- Abstract: "Initial release-toolkit substrate ... shared release-cut
  + CHANGELOG transitioning + manifest-vs-tag CI + Forgejo-native
  reusable workflows."
- Concrete: "The first release of release-toolkit — a shared,
  Forgejo-native release pipeline you reference instead of
  copy-pasting release machinery into every repo. It cuts releases,
  transitions your CHANGELOG, and runs a CI check that catches
  version-number drift."

Five feature bullets revised. Dev vocabulary preserved
(conventional-commits / CI / SemVer / Forgejo) — the audience IS
release-tool adopters, not absolute beginners.

## Structural fold (Herald's note + Surveyor's #4 reinforcement)

The 10 granular `scripts/lib:` / `scripts:` / `workflows:` bullets
at the bottom of `### Added` read as raw commit-subjects — internal
file paths, not feature-narrative. Folded into a single summary
line: "The toolkit ships as bash scripts plus 4 reusable Forgejo
Actions workflows; 179 unit + integration tests cover the substrate,
shellcheck-clean throughout."

## Fixed + Internal sections

Rewritten to be self-explanatory rather than terse commit-subjects:
- Fixed: "`changelog_transition` now merges sections by kind so a
  section heading never appears twice in a new release section..."
- Internal: "Extracted the version-section extraction logic into a
  reusable library helper..."

## Out-of-diff (Surveyor #5)

Repo description ("Built per alcatraz-infra#65") will be genericized
via Forgejo API in the same revision atom (separate from this
commit since it's repo metadata, not source).

## What this commit does NOT change

- The substantive code, scripts, workflows, docs, ADRs — all
  unchanged. Only the v0.1.0 release-notes prose in CHANGELOG.md.
- The PR body will be updated via Forgejo API to match this revised
  section (not via re-running release-prep.sh which would re-cut).

Tracker: frankenbit/release-toolkit#1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH
alex approved these changes 2026-06-24 14:38:29 +02:00
Adds .forgejo/pull_request_template.md with the suggested PR-body
shape: Why / What's in the PR / Acceptance criteria / Test plan /
What this PR does NOT do.

The template prompts authors for the load-bearing sections without
being prescriptive — sections that don't apply can be dropped, and
trivial PRs (typos / dep bumps) can skip the whole template.

Lands on the v0.1.0 prep branch so it ships with the first release;
applies to all subsequent PRs.

Tracker: frankenbit/release-toolkit#1
alex merged commit b45e9b4c1b into main 2026-06-24 15:13:25 +02:00
alex deleted branch release-prep/v0.1.0 2026-06-24 15:13:25 +02:00
Sign in to join this conversation.
No description provided.