Skip to content

release: 0.3.0-rc1 - #49

Merged
donislawdev merged 1 commit into
mainfrom
release/v0.3.0-rc1
Sep 3, 2026
Merged

release: 0.3.0-rc1#49
donislawdev merged 1 commit into
mainfrom
release/v0.3.0-rc1

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Your decision and yours alone - immutable rule 12. This only prepares the
commit. Nothing here tags or publishes anything.

Before 1.0 the minor is where a breaking change goes, and this release has
several: six formats have different bytes under Go 1.27, a .tar.gz could not
be built at all until it started measuring its own gzip framing, a log advances
through time, a GIF moves, and a CSV quotes only the fields that need it. Each
is listed under ### Breaking with whether there is a way back to the old
bytes.

The hyphen is load bearing

v0.3.0rc1 would not have been a release candidate. The workflow marks a
prerelease when the TAG NAME carries a hyphen:

case "${GITHUB_REF_NAME}" in
  *-*) flags+=(--prerelease) ;;
esac

Without it the candidate becomes the latest stable release, which is what the
README badge resolves to. The changelog heading has to match the same string,
because the workflow greps for a section naming exactly what the binary prints.

Both of the release gate's own conditions were run locally and pass: the
changelog has a section for 0.3.0-rc1, and 0 lines are left under
[Unreleased].

Three things moved with the version

None would have been caught by a targeted run.

  • The Windows resource in both binaries. A guard reads the COMPILED
    resource rather than the script beside it, which is what catches a .syso
    nobody regenerated. Both scripts updated, both recompiled with windres.
    FILEVERSION takes four numbers and cannot hold rc1, so the numeric fields
    stay 0,3,0,0 while the string fields carry the whole version - the half the
    guard compares.
  • The about screen, which draws the version. Tree diff is one line.
  • The changelog link definitions, which had no entry for the new section
    and still compared [Unreleased] from v0.2.0. Nothing guards those.

The ritual

tools/release-check.py, on a clean tree: 178 of 178 checks passed, nine
phases, six shells.

Phase 6 is the one worth reading: 24 of 24 formats built on Windows, WSL and
macOS, byte for byte identical across all three
. Phase 7 does the same for a
recipe. Seven checks were skipped, each with a stated reason - this machine
cannot run a foreign architecture, and the window is opened once by phase 4
rather than twice.

Sixteen notes are printed for you to read before tagging: four formats are new
since v0.2.0 (avif, jxl, tiff, webp), eight have different bytes, and
seven minimums moved.

A dead check found while running it

Phase 5 asks whether every byte change is declared under ### Breaking -
immutable rule 3, the one about other people's CI. It read only the
[Unreleased] section. But the release workflow refuses a tag while anything
is left under [Unreleased], so by the time the ritual runs for real that
section is empty, find returns -1, and every format reports as undeclared.

Two gates that cannot both be satisfied. The output contradicted itself -
"8 changes, all described" beside "NOT DECLARED: csv, docx, gif, log, png,
pptx, targz, xlsx". All eight are in fact named under ### Breaking. Fixed in
tools/ to read the section this release ships. That is the worst kind of dead
check: it shouts loudest exactly when it is useless, and the false alarm looks
identical to a broken untouchable rule.

After this merges

CI has to be green on the resulting main commit before the tag - the release
workflow asks for that by name. Then the tag is yours to push:

git tag v0.3.0-rc1 && git push origin v0.3.0-rc1

That opens an empty draft. The build is handed over for signing, and you press
publish.

🤖 Generated with Claude Code

The owner's decision, and his alone - immutable rule 12. Before 1.0 the
minor is where a breaking change goes, and this release has several: six
formats have different bytes under Go 1.27, a .tar.gz could not be built
at all until it started measuring its own gzip framing, a log advances
through time, a GIF moves, and a CSV quotes only the fields that need it.
Each one is listed under Breaking with whether there is a way back.

The suffix is spelled with a HYPHEN and that is not cosmetic. The release
workflow marks a release as a prerelease when the TAG NAME carries one, so
v0.3.0rc1 would have gone out as the latest stable release - which is what
the README badge resolves to. The changelog heading has to match the same
string, because the workflow greps for a section naming exactly what the
binary prints before it builds anything.

Both of the release gate's own conditions were run here and pass: the
changelog has a section for 0.3.0-rc1, and nothing is left under
[Unreleased].

Three things moved with the version, and none of them would have been
found by a targeted run.

The Windows resource compiled into both binaries carries the version, and
a guard reads the COMPILED resource rather than the script beside it -
which is what catches a .syso nobody regenerated. Both scripts updated and
both recompiled with windres. FILEVERSION takes four numbers and cannot
hold "rc1", so the numeric fields stay 0,3,0,0 and the string fields carry
the whole version, which is the half the guard compares.

The about screen draws the version, so its stored picture and widget tree
were refreshed. The tree diff is one line.

And the changelog's link definitions at the bottom had no entry for the
new section and still compared [Unreleased] from v0.2.0. Nothing guards
those, so that one came from reading rather than from a red run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit 3b9ecfc into main Sep 3, 2026
18 checks passed
@donislawdev
donislawdev deleted the release/v0.3.0-rc1 branch September 3, 2026 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant