feat: freshen-channel helper and channel-discipline protocol
Add ./freshen-channel <tool> <channel> at the repo root for the
"drag alpha/beta forward to current stable" workflow. The script
uses a temporary git worktree at the latest <tool>-v* tag so the
main worktree's HEAD is never touched — no checkout, no stash, no
race against in-progress dev. Build runs inside the worktree, the
resulting <tool>_<channel>.html is copied back into the main
repo's website/releases/, worktree is removed.
The on-page label of a freshened build is `<channel> · <today> ·
<stable-tag-sha>` — the SHA pins which stable was the source, so
anyone debugging can `git checkout <sha>` to reproduce.
Smoke-tested:
./freshen-channel archive alpha → archive_alpha.html with
"alpha · 2026-04-27 · ea385b5"
./freshen-channel transmittal beta → transmittal_beta.html with
"beta · 2026-04-27 · ea385b5"
./freshen-channel foobar alpha → usage error
./freshen-channel archive stable → usage error
AGENTS.md gains a "Channel discipline (MUST rules)" subsection
codifying the protocol the build system can't enforce:
1. Stable doesn't regress — files are immutable; bump for fixes.
2. No backports — bump and let users update pins.
3. Alpha/beta are mutable — never pin in production.
4. Stale-channel rule — after every stable release, freshen alpha
and beta so neither is older than current stable. NOT optional.
5. Hotfix path — direct stable cut allowed, no beta soak required;
freshen alpha + beta after.
6. Beta soak (recommended) — a few days exposure before promoting.
Plus a "Freshen helper" subsection documenting the script.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
699069e538
commit
d122804bdb
4 changed files with 18930 additions and 2 deletions
36
AGENTS.md
36
AGENTS.md
|
|
@ -167,16 +167,48 @@ Three channels:
|
||||||
- **Beta**: mutable. `sh tool/build.sh --release beta` overwrites `website/releases/<tool>_beta.html` in place. No tag. The on-page label is `beta · <date> · <sha>` so the source is recoverable from git via the SHA.
|
- **Beta**: mutable. `sh tool/build.sh --release beta` overwrites `website/releases/<tool>_beta.html` in place. No tag. The on-page label is `beta · <date> · <sha>` so the source is recoverable from git via the SHA.
|
||||||
- **Alpha**: mutable, analogous. `sh tool/build.sh --release alpha`.
|
- **Alpha**: mutable, analogous. `sh tool/build.sh --release alpha`.
|
||||||
|
|
||||||
Stable releases do **not** automatically clobber `<tool>_alpha.html` / `<tool>_beta.html` — those keep whatever was last built into them. To freshen alpha to current stable, `git checkout v<X>.<Y>.<Z> && sh tool/build.sh --release alpha`.
|
Stable releases do **not** automatically clobber `<tool>_alpha.html` / `<tool>_beta.html` — those keep whatever was last built into them. Use `./freshen-channel <tool> <channel>` (see "Freshen helper" below) to drag a channel forward to current stable; never `git checkout` the main worktree by hand for this.
|
||||||
|
|
||||||
After cutting a stable release, run `git push --tags` to publish the tag.
|
After cutting a stable release, run `git push --tags` to publish the tag.
|
||||||
|
|
||||||
The "skip if no source change since last tag" guard for stable releases compares **HEAD** to the latest tag — uncommitted working-tree changes are invisible. If you edit a tool and want a stable release to actually fire, commit the change first; otherwise the build prints `no source changes since <tool>-vX.Y.Z — skipping` and exits 0. Alpha and beta channel builds always rebuild (no skip check).
|
The "skip if no source change since last tag" guard for stable releases compares **HEAD** to the latest tag — uncommitted working-tree changes are invisible. If you edit a tool and want a stable release to actually fire, commit the change first; otherwise the build prints `no source changes since <tool>-vX.Y.Z — skipping` and exits 0. Alpha and beta channel builds always rebuild (no skip check).
|
||||||
|
|
||||||
Agents must **never** write to `website/releases/` or `website/index.html` directly — always go through `--release`.
|
Agents must **never** write to `website/releases/` or `website/index.html` directly — always go through `--release` or `./freshen-channel`.
|
||||||
|
|
||||||
`landing/build.sh --release <version>` additionally writes `website/index.html` (the root URL of zddc.varasys.io).
|
`landing/build.sh --release <version>` additionally writes `website/index.html` (the root URL of zddc.varasys.io).
|
||||||
|
|
||||||
|
### Channel discipline (MUST rules)
|
||||||
|
|
||||||
|
The build system does not enforce these. Treating channels carelessly defeats the point of having three. Be disciplined.
|
||||||
|
|
||||||
|
1. **Stable doesn't regress.** No known-broken features that worked in the previous stable. If you ship `v0.0.5` with a bug, the path forward is `v0.0.6` with a fix — never edit `v0.0.5` in place. Stable files are immutable.
|
||||||
|
2. **No backports.** Don't try to patch an older stable version. Always cut a new stable at a higher version. Users pinned to the old version stay pinned by their own choice; they can move forward when they want.
|
||||||
|
3. **Alpha and beta are mutable.** Document this anywhere you invite users to test them. Pinning `?v=alpha` (or `_alpha.html`) in a production deployment is a mistake; it gets rebuilt without notice.
|
||||||
|
4. **Stale-channel rule.** Users tracking alpha (or beta) MUST never see a build older than current stable. After every stable release, run `./freshen-channel <tool> alpha` and `./freshen-channel <tool> beta` so each channel is at-least-current. This is not optional.
|
||||||
|
5. **Hotfix path.** For critical bugs: fix on `main`, cut a new stable (no beta soak required), then freshen alpha + beta. Tag the commit message `fix:` or include "hotfix" so the intent is visible in `git log`.
|
||||||
|
6. **Beta soak before promoting (recommended).** Give a beta a few days of exposure before cutting the same code as stable. Not enforced; use judgment for trivial changes.
|
||||||
|
|
||||||
|
### Freshen helper
|
||||||
|
|
||||||
|
`./freshen-channel <tool> <channel>` rebuilds the alpha or beta channel of a tool from its current stable tag. Use it after every stable release (rule 4 above) and any other time alpha/beta has fallen behind stable.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./freshen-channel archive alpha
|
||||||
|
./freshen-channel transmittal beta
|
||||||
|
```
|
||||||
|
|
||||||
|
What it does:
|
||||||
|
|
||||||
|
1. Finds the latest `<tool>-v*` tag.
|
||||||
|
2. Creates a temporary git worktree at that tag — does **not** touch the main worktree's HEAD or working tree.
|
||||||
|
3. Runs `<tool>/build.sh --release <channel>` inside the worktree.
|
||||||
|
4. Copies the resulting `<tool>_<channel>.html` into the main repo's `website/releases/`.
|
||||||
|
5. Removes the worktree.
|
||||||
|
|
||||||
|
The on-page label of the freshened build is `<channel> · <today> · <stable-tag-sha>` — the SHA pins which stable was used as the source, recoverable via `git checkout`.
|
||||||
|
|
||||||
|
Note: the build pipeline used is the one **at the tag**, not on `main`. That is intentional (pure reproducibility). If you have made build-system improvements since stable was cut and want the freshen to use them, cut a new stable first.
|
||||||
|
|
||||||
### Bootstrap zips
|
### Bootstrap zips
|
||||||
|
|
||||||
`build.sh` regenerates three downloadable zips into `website/` on every invocation:
|
`build.sh` regenerates three downloadable zips into `website/` on every invocation:
|
||||||
|
|
|
||||||
98
freshen-channel
Executable file
98
freshen-channel
Executable file
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# =============================================================================
|
||||||
|
# freshen-channel — rebuild a tool's alpha or beta channel from its current
|
||||||
|
# stable tag, so users tracking that channel are never on code older than
|
||||||
|
# current stable.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./freshen-channel <tool> <channel>
|
||||||
|
# tool archive | transmittal | classifier | mdedit | landing
|
||||||
|
# channel alpha | beta
|
||||||
|
#
|
||||||
|
# Why this exists:
|
||||||
|
# Stable releases do NOT automatically clobber alpha/beta files (see
|
||||||
|
# AGENTS.md "Channel discipline" rule 4). After cutting stable v0.0.5,
|
||||||
|
# users pinned to alpha may be on an older build than current stable —
|
||||||
|
# that violates the stale-channel rule. Run this to drag alpha (or
|
||||||
|
# beta) forward to whatever stable currently is.
|
||||||
|
#
|
||||||
|
# What it does:
|
||||||
|
# 1. Finds the latest <tool>-v* tag.
|
||||||
|
# 2. Creates a temporary git worktree at that tag — does NOT touch
|
||||||
|
# your current branch or working tree.
|
||||||
|
# 3. Runs <tool>/build.sh --release <channel> inside the worktree.
|
||||||
|
# 4. Copies the resulting <tool>_<channel>.html into the main repo's
|
||||||
|
# website/releases/.
|
||||||
|
# 5. Removes the worktree.
|
||||||
|
#
|
||||||
|
# The on-page label of the freshened build will be
|
||||||
|
# `<channel> · <today> · <stable-tag-sha>` — the SHA encodes which
|
||||||
|
# stable was used as the source, so anyone debugging can `git checkout`
|
||||||
|
# that exact commit.
|
||||||
|
#
|
||||||
|
# Note: the build pipeline used is the one AT THE TAG, not the latest
|
||||||
|
# main. That is intentional — pure reproducibility. If you have made
|
||||||
|
# build-system improvements since stable was cut and want the freshen
|
||||||
|
# to use them, cut a new stable that includes those changes first.
|
||||||
|
# =============================================================================
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
TOOL="${1:-}"
|
||||||
|
CHANNEL="${2:-}"
|
||||||
|
|
||||||
|
case "$TOOL" in
|
||||||
|
archive | transmittal | classifier | mdedit | landing) ;;
|
||||||
|
*)
|
||||||
|
echo "usage: $0 <tool> <channel>" >&2
|
||||||
|
echo " tool: archive | transmittal | classifier | mdedit | landing" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$CHANNEL" in
|
||||||
|
alpha | beta) ;;
|
||||||
|
*)
|
||||||
|
echo "usage: $0 <tool> <channel>" >&2
|
||||||
|
echo " channel: alpha | beta (stable is what you are freshening FROM)" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
REPO=$(cd "$(dirname "$0")" && pwd)
|
||||||
|
|
||||||
|
# Find the latest stable tag for the tool.
|
||||||
|
LATEST_TAG=$(git -C "$REPO" tag --list "${TOOL}-v*" --sort=-v:refname | head -1)
|
||||||
|
if [ -z "$LATEST_TAG" ]; then
|
||||||
|
echo "error: no stable tag found for ${TOOL} (looking for ${TOOL}-v*)" >&2
|
||||||
|
echo " cut a stable release first: sh ${TOOL}/build.sh --release [version]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Temporary detached worktree at the stable tag. Cleaned up on exit.
|
||||||
|
WT=$(mktemp -d)
|
||||||
|
cleanup() {
|
||||||
|
git -C "$REPO" worktree remove --force "$WT" >/dev/null 2>&1 || true
|
||||||
|
rm -rf "$WT"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
echo "Freshening ${TOOL} ${CHANNEL} from ${LATEST_TAG}"
|
||||||
|
git -C "$REPO" worktree add --quiet --detach "$WT" "$LATEST_TAG"
|
||||||
|
|
||||||
|
# Build in the worktree. The tool's build.sh writes the channel artifact
|
||||||
|
# to "$WT/website/releases/<tool>_<channel>.html"; we then copy it into
|
||||||
|
# the main worktree.
|
||||||
|
sh "$WT/${TOOL}/build.sh" --release "$CHANNEL"
|
||||||
|
|
||||||
|
SRC="$WT/website/releases/${TOOL}_${CHANNEL}.html"
|
||||||
|
DST="$REPO/website/releases/${TOOL}_${CHANNEL}.html"
|
||||||
|
if [ ! -f "$SRC" ]; then
|
||||||
|
echo "error: build did not produce $SRC" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$REPO/website/releases"
|
||||||
|
cp "$SRC" "$DST"
|
||||||
|
echo "Wrote $DST"
|
||||||
|
echo "Done. ${CHANNEL} channel for ${TOOL} now reflects ${LATEST_TAG}."
|
||||||
|
echo "Commit the change: git add $DST && git commit"
|
||||||
7800
website/releases/archive_alpha.html
Normal file
7800
website/releases/archive_alpha.html
Normal file
File diff suppressed because it is too large
Load diff
10998
website/releases/transmittal_beta.html
Normal file
10998
website/releases/transmittal_beta.html
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue