build(channels): swap source-SHA in beta label for a three-word slug
All checks were successful
Notify chart dev on beta cut / notify-chart-dev (push) Successful in 5s

Beta on-page labels now read
"v<X.Y.Z>-beta · <date> · word-word-word" instead of the previous
"… · g<short-sha>" form. Words are easier to recognise at a glance
than a 7-char SHA — testers can confirm "I'm running the
ranch-chamber-cake build" without consulting git log.

The slug is computed deterministically from the source commit's
full SHA (three 4-hex chunks modulo the wordlist length, looking
up shared/build-words.txt). Same source state → same slug, so the
existing chore-commit recursion fix carries over: a re-cut on
unchanged source produces an identical label and no spurious
embedded commit.

Wordlist (shared/build-words.txt): 283 short, neutral, English nouns
— flora, fauna, materials, landscape features. 283 ^ 3 ≈ 22M
combinations, more than enough that two simultaneous beta cuts on
different branches don't collide in practice.

Full provenance is still available — the binary's `--version` output
prints `git describe` (tag-distance-gSHA), and the chart appVersion
carries the full SHA written by notify-chart-bump.sh on push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-07 12:11:23 -05:00
parent 7c72ca3b1d
commit 1dafc97d8e
2 changed files with 341 additions and 22 deletions

View file

@ -131,21 +131,22 @@ _validate_semver() {
case "$_v3" in '' | *[!0-9]*) _bad ;; esac
}
# Walk backwards from HEAD until a non-auto-commit is found, return its
# short SHA. Auto-commits are recognised by their canonical commit-
# message prefixes:
# Walk backwards from HEAD until a non-auto-commit is found, return the
# resolved git ref (e.g. "HEAD~2"). Auto-commits are recognised by their
# canonical commit-message prefixes:
#
# - "chore(embedded): cut v<X.Y.Z>-beta" (beta auto-commit, build:993)
# - "release: v<X.Y.Z> lockstep" (stable auto-commit, build:986)
#
# Used by compute_build_label to embed a stable source-SHA into beta
# labels without triggering the embedded-commit recursion: HEAD shifts
# when embedded bytes change, but the source SHA returned here stays
# the same as long as the underlying source hasn't moved.
# Used by the build-label helpers to derive a stable identifier (short
# SHA, three-word slug) from the underlying source state. The source
# ref is invariant across the auto-commit step (HEAD shifts when
# embedded bytes change), so a re-run on the same source state produces
# the same identifiers and no spurious commit.
#
# Falls back to plain `HEAD` short SHA when the walk doesn't find a
# non-auto-commit in the first 32 commits (defensive cap).
_source_commit_short_sha() {
# Defensive cap: stops walking after 32 commits and returns whatever
# ref was reached.
_source_commit_ref() {
_i=0
_ref="HEAD"
while [ "$_i" -lt 32 ]; do
@ -159,7 +160,40 @@ _source_commit_short_sha() {
esac
break
done
echo "$_ref"
}
# Three-word slug derived deterministically from the source commit's
# full SHA. Same source state → same slug; survives embedded auto-
# commits by deferring to _source_commit_ref.
#
# Three 4-hex-char chunks of the SHA are taken modulo the wordlist
# length (shared/build-words.txt). 283 words × 3 = 22M combinations,
# enough that a tester eyeballing two beta builds can tell at a glance
# whether they're running the same source.
#
# Format: "word-word-word" (lowercase, hyphen-separated).
_source_commit_slug() {
_ref=$(_source_commit_ref)
_full_sha=$(git -C "$root_dir" rev-parse "$_ref" 2>/dev/null || echo "0000000000000000")
_words_file="$root_dir/shared/build-words.txt"
if [ ! -f "$_words_file" ]; then
# Fall back to plain short SHA if the wordlist is missing
# (e.g. running from a checkout that predates this feature).
git -C "$root_dir" rev-parse --short=7 "$_ref" 2>/dev/null || echo "unknown"
return
fi
_wc=$(awk 'END{print NR}' "$_words_file")
_h1=$(echo "$_full_sha" | cut -c 1-4)
_h2=$(echo "$_full_sha" | cut -c 5-8)
_h3=$(echo "$_full_sha" | cut -c 9-12)
_n1=$(( (0x$_h1 % _wc) + 1 ))
_n2=$(( (0x$_h2 % _wc) + 1 ))
_n3=$(( (0x$_h3 % _wc) + 1 ))
_w1=$(awk -v n="$_n1" 'NR==n{print; exit}' "$_words_file")
_w2=$(awk -v n="$_n2" 'NR==n{print; exit}' "$_words_file")
_w3=$(awk -v n="$_n3" 'NR==n{print; exit}' "$_words_file")
echo "${_w1}-${_w2}-${_w3}"
}
# Compute build label and channel. Reads positional args:
@ -218,17 +252,19 @@ compute_build_label() {
alpha | beta)
channel="$_arg"
_date=$(date -u +"%Y-%m-%d")
# Resolve the *source* SHA — the most recent commit that is
# not itself an embedded-files auto-commit. This stays stable
# across the build's auto-commit step (build:971-995) which
# advances HEAD by one when embedded bytes change. Using
# HEAD directly here would create an infinite loop: each cut
# would update the embedded label with the new chore-commit
# SHA, which would in turn require another chore commit.
# The source SHA is invariant across embedded auto-commits
# so the second cut on the same source state is a no-op.
_src_sha=$(_source_commit_short_sha)
build_label="v${_next_stable}-${channel} · ${_date} · g${_src_sha}"
# Three-word slug derived from the *source* SHA — see
# _source_commit_slug. Two builds carrying the same slug
# were cut from the same source state, so a tester can
# glance at the on-page label and confirm "yes, this is
# the cobalt-otter-meadow build I was emailed about" without
# having to look up a SHA. The slug is invariant across
# the embedded auto-commit step (build:971-995) so a re-cut
# on unchanged source produces the same slug, no spurious
# commit. Full source SHA is available via the binary's
# `--version` output and the chart appVersion for any case
# where exact provenance matters.
_slug=$(_source_commit_slug)
build_label="v${_next_stable}-${channel} · ${_date} · ${_slug}"
_emit_build_label_sidecar "$_tool"
return 0
;;

283
shared/build-words.txt Normal file
View file

@ -0,0 +1,283 @@
acorn
agate
alder
almond
amber
anchor
angle
antler
apple
apricot
arbor
arch
arrow
ashen
aspen
atlas
attic
auburn
aurora
azure
badge
bagel
ballad
balm
banner
barn
basil
basin
basket
beacon
beam
bear
beech
beetle
berry
biscuit
blanket
blossom
boat
bonnet
book
boulder
brass
bread
breeze
brick
bridge
brook
broom
bubble
bucket
buckle
button
cabin
cable
cactus
cake
candle
canvas
canyon
carpet
cart
cedar
chair
chalk
chamber
chapel
charm
cherry
chime
cinder
citrus
clay
cliff
cloud
clover
cluster
coal
coast
cobble
cocoa
coin
comet
compass
copper
cottage
cove
cradle
crane
crater
creek
crescent
cricket
crystal
daisy
dawn
deer
denim
diamond
ditch
dock
dolphin
dome
drift
drum
dune
dusk
eagle
ember
emerald
fable
falcon
feather
fence
fern
fiddle
finch
flag
flame
flask
flax
fleece
flint
flute
foam
forest
fork
fort
fountain
fox
garden
garnet
gear
ginger
glacier
glade
glass
globe
gold
granite
grass
grove
hammer
harbor
harp
harvest
hazel
hearth
heron
hill
hinge
hive
holly
hook
horizon
horn
hut
indigo
iris
iron
island
ivory
ivy
jade
jasper
juniper
kayak
kelp
kettle
key
kiln
kite
lake
lamp
lantern
lark
laurel
leaf
ledge
lemon
lens
lilac
lily
linen
locust
log
loom
lotus
maple
marble
marigold
marsh
mast
meadow
melon
mesa
meteor
mint
mirror
mist
mitten
moon
moor
moss
mug
nectar
nest
niche
nickel
nimbus
oak
oasis
ocean
onyx
opal
orbit
orchard
otter
owl
oyster
paddle
palm
panel
parrot
patio
pearl
pebble
pelican
pepper
perch
pier
pillow
pine
pipe
plank
plaza
pocket
pond
poplar
port
prairie
prism
puddle
quarry
quartz
quill
quilt
rabbit
raft
rain
ranch
raven
reed
reef
ribbon
ridge
river
robin
rock
rope
saddle
sage
sail
salt
sand
sapphire
sash
satin
scarf
sea
seal
seed
shawl
shield
shore
shrub
silver
silo
slope
smoke
snail
snow
spire