feat: release flow — install-from-tag, release.yml, and -dev versions (#83) #90

Merged
dan-claude-bot merged 6 commits from feat/release-flow into main 2026-07-18 22:24:34 +00:00

6 commits

Author SHA1 Message Date
dan-claude-bot
3122729877 fix: release.yml triggers on every tag, so a mismatch fails loudly
A shape-filtered trigger silently skips the tags it doesn't match — a
habitual v0.7.0 would mint nothing and say nothing. rig's release.yml
(heavy-duty/rig#40) already triggers on '**' and lets the tag==VERSION
assert be the loud gate; align box to that.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:18:23 +00:00
dan-claude-bot
3cac269b5b docs: the three install channels and the release ritual (#83)
README: the installer's default is the latest release — reproducible,
resolved off the releases/latest redirect, failing loudly rather than
silently handing out main — with the pinned (BOX_REF=<tag>) and dev
(BOX_REF=main) channels beside it. CONTRIBUTING: a release is a PR,
then a tag — the release PR bumps VERSION and stamps the changelog,
the bare X.Y.Z tag on the merge commit triggers release.yml, and
main's VERSION bumps to X.Y.(Z+1)-dev immediately after, because the
versioned layout names install trees after VERSION. Plus the
CHANGELOG entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:18:23 +00:00
dan-claude-bot
646c458e25 test(release): the extraction, the resolution, and every channel — offline (#83)
test/release.sh, wired into CI beside the other suites and never
touching the network: release-notes.sh is driven against a fixture
changelog carrying every boundary (Unreleased must not leak, the
version is matched whole so 0.7.0 never grabs 0.7.0-rc1, a missing
or empty section refuses) AND against the real CHANGELOG.md, so the
header format cannot drift under release.yml. latest_release_tag is
extracted from install.sh and driven against a shim curl serving
canned redirects — including the no-releases redirect and a network
failure. And the three channels are REAL install.sh runs against
throwaway roots with the shim standing in for GitHub: the default
resolves and downloads the tag tarball (never a branch), a pin skips
the probe, BOX_REF=main falls back tag → branch in that order, and a
failed resolution refuses loudly having downloaded nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:17:51 +00:00
dan-claude-bot
7044f8e0c1 chore: bump main to 0.6.1-dev — a dev install must not impersonate 0.6.0 (#83)
0.6.0 is released; the versioned layout names install trees after
the tree's own VERSION file — so a main install at VERSION 0.6.0
would land in versions/0.6.0, impersonating the released tree: the
converging no-op then refuses to update it, and BOX_REINSTALL=1
silently swaps a release tree for a dev one under the same name. A
-dev version keeps dev installs side by side with releases. The
post-release bump is now part of the release ritual (CONTRIBUTING).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:17:51 +00:00
dan-claude-bot
6d4996e532 feat(install): three channels — the latest release by default, tags before branches (#83)
curl | bash used to hand out whatever main was at that second: the
release was a bookmark, not a package. install.sh now resolves the
latest release tag by following GitHub's releases/latest redirect
(one HEAD request via -w '%{redirect_url}' — no API, no token, no
rate-limit pain) and downloads that tag's tarball. A failed
resolution REFUSES with the way out (BOX_REF) — it never hangs and
never silently falls back to main. A set BOX_REF is tried as a tag
first (the pinned channel), then as a branch (the dev channel), so
one knob yields three channels: default = latest release,
BOX_REF=0.6.0 = pinned, BOX_REF=main = dev. The resolution happens
AFTER the confirm — even a redirect probe is network the operator
has not yet said yes to — and INSTALLED_FROM records the resolved
tag, so a caller can assert it got what it asked for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:17:51 +00:00
dan-claude-bot
dd51b06f3a feat(release): publish the tagged version's changelog section (#83)
On a bare X.Y.Z tag push (the 0.6.0 tag set the no-'v' precedent),
release.yml asserts the tag names the tree's own VERSION — a mismatch
fails loudly and creates NOTHING — then publishes the GitHub release
with that version's CHANGELOG.md section as the body: the curated
prose, not the generated PR list. No assets, because for a pure-bash
tree GitHub's source tarball for the tag IS the package.

The extraction lives in .github/scripts/release-notes.sh, a file of
its own so test/release.sh drives the same code against fixtures and
the real changelog — it refuses a missing or empty section, so a tag
whose release ritual was skipped fails before anything is created.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 22:17:51 +00:00