blaise/.claude/skills/cut-blaise-release/SKILL.md
Graeme Geldenhuys 0a61dce249 docs(skill): update cut-blaise-release with v0.12.0-cut gaps
- archive the NATIVE stage-2 binary (/tmp/fpn_blaise2), not the QBE one
- COMPILER_ID drops -SNAPSHOT at release, re-gains it at the dev-cycle bump;
  keep base version synced with project.xml or bif-coverage fails
- add fixpoint-warmcache.sh (WARMCACHE_FIXPOINT_OK) to the verification set
- rename the -pre bootstrap dir to the next cycle (not refresh-in-place)
- define the changelog/community-post range as <prev-tag>..v<X.Y.Z>
- note the CI STAGE1_TAG / rolling-bootstrap anchor bump
2026-06-24 07:44:29 +01:00

18 KiB
Raw Permalink Blame History

name description allowed-tools
cut-blaise-release Cut a new Blaise compiler release. Bumps the version in Blaise.pas + project.xml, rebuilds compiler + RTL + stage-2 binary, verifies all four self-hosting fixpoints (QBE, native, internal-assembler, warm-cache), runs the full test suite, commits, tags, archives the NATIVE stage-2 binary under releases/vX.Y.Z/ (NOT committed — releases/ is gitignored), renames + refreshes the -pre bootstrap dir to the next cycle, writes the community post + changelog, then bumps Blaise.pas + project.xml + uCompilerId.pas to the next -SNAPSHOT version. Use when asked to "cut a release", "release v0.X.Y", "tag a release", or after the user confirms a fixpoint is achieved.
Bash
Read
Edit
Write
AskUserQuestion

Cutting a Blaise release

This skill mirrors the verified release process used for v0.11.0. Run every step in order. Stop and surface the failure to the user if any step fails — never paper over a missing fixpoint.

Inputs

The user will name the release version (e.g. "0.11.0"). If they didn't, ask them via AskUserQuestion. The next-dev version is the next minor with a -SNAPSHOT suffix (0.11.0 → 0.12.0-SNAPSHOT). Confirm with the user if unsure.

Preconditions to check before starting

  1. Run git status. The working tree often carries unrelated uncommitted changes (e.g. compiler/project.xml and tools/kanban/project.xml build- option tweaks, plus many untracked .txt/.adoc working files). These are NORMAL on this repo and must NOT be swept into the release commit. The root project.xml may also carry unrelated edits — you will stage only its version line (see Step 5). If you see uncommitted changes that look like in-progress feature work, ask the user whether to proceed.
  2. The current Version constant in compiler/src/main/pascal/Blaise.pas should look like '<X.Y.Z>-SNAPSHOT' (the dev marker). If it's already a plain non-dev version, ask the user — they may have already cut this release.
  3. All commands run from the project root /data/devel/new-pascal-compiler (PasBuild requirement — sub-modules lack the <version> field).
  4. vendor/qbe/qbe must exist (the fixpoint scripts need it). The current release binary lives at the newest releases/v*-pre/blaise or releases/v*/blaise — the fixpoint scripts auto-pick it via sort -V.

Step 1 — bump version to release

Edit compiler/src/main/pascal/Blaise.pas. Find the line:

  Version = '<X.Y.Z>-SNAPSHOT';

and change it to:

  Version = '<X.Y.Z>';

Also edit project.xml in the project root. Find the line:

  <version><X.Y.Z>-SNAPSHOT</version>

and change it to:

  <version><X.Y.Z></version>

Edit compiler/src/main/pascal/uCompilerId.pas. The COMPILER_ID looks like 'blaise-<X.Y.Z>-SNAPSHOT+bif<N>'. For the release, drop the -SNAPSHOT but keep the +bif<N> tag'blaise-<X.Y.Z>+bif<N>'. Only bump the +bif<N> number if the .bif interface format actually changed this cycle (IFACE_VERSION in uUnitInterfaceIO.pas — the two must agree).

IMPORTANT (bif-coverage sync): the bif-coverage tool's TestRun_BifCoverage_NoGaps asserts the project.xml base version (with -SNAPSHOT stripped) is a substring of COMPILER_ID. So COMPILER_ID and project.xml <version> must carry the same base version at all times — here at release, and again at the dev-cycle bump in Step 7. A mismatch silently fails that test.

Step 2 — verify the QBE fixpoint (NOT the binary we ship)

The project's scripts/fixpoint.sh does a whole QBE stage-2 build: it rebuilds + installs the runtime, uses the latest release binary as stage-1 to emit stage-2 IR, assembles + links the stage-2 binary to /tmp/fp_blaise2, then emits stage-3 IR and diffs it.

NOTE: since v0.12.0 the native backend is the default, so the binary we actually archive and ship is the NATIVE stage-2 binary from Step 3 (/tmp/fpn_blaise2), NOT /tmp/fp_blaise2. This step still runs — the QBE fixpoint is a required reproducibility guard and prints the IR line count we quote in the commit message and docs — but /tmp/fp_blaise2 is only used to rebuild compiler/target/blaise below so the native fixpoints run on the released version.

./scripts/fixpoint.sh        # must print FIXPOINT_OK

Note the stage-2 IR line count it prints ("stage-2 IR: N lines") — you'll quote it in the commit message and the docs.

The verified stage-2 binary is /tmp/fp_blaise2. (If the release stage-1 was too old to reach fixpoint in one round, fixpoint.sh extends a round and the binary becomes /tmp/fp_blaise3 — read the script's output to see which.)

fixpoint.sh only updated the IR-emitting path; compiler/target/blaise may still be the old -SNAPSHOT build. Rebuild it from the fixpoint binary so the native fixpoints and the test runner use the released version:

cp compiler/target/blaise_rtl.a /tmp/blaise_rtl.a   # FindRTL looks beside --compiler
pasbuild compile -m blaise-compiler --compiler /tmp/fp_blaise2
compiler/target/blaise --help | head -1             # must print: Blaise Compiler v<X.Y.Z>

ALWAYS pass --compiler to pasbuild — without it, PasBuild falls back to FPC, which this project does not use.

Step 3 — verify the native, internal-assembler, and warm-cache fixpoints

fixpoint.sh (Step 2) only exercises the QBE backend. Three more fixpoints guard the native backend, the in-process internal assembler, and warm-cache (incremental) rebuilds. Run all three:

./scripts/fixpoint-native.sh           # must print NATIVE_FIXPOINT_OK
./scripts/fixpoint-native-internal.sh  # must print NATIVE_INTERNAL_OK
./scripts/fixpoint-warmcache.sh        # must print WARMCACHE_FIXPOINT_OK

fixpoint-native.sh writes the native stage-2 binary to /tmp/fpn_blaise2 — that is the binary we archive and ship (Step 6), because native is the default backend. Confirm it prints the right version:

/tmp/fpn_blaise2 --help | head -1      # must print: Blaise Compiler v<X.Y.Z>

If any of the four fixpoints does not print its OK line, stop. Show the user the output and do not proceed. There is no acceptable "almost fixpoint" — any divergence means the release is not reproducible.

Step 4 — run the full test suite with the fixpoint binary

Build the TestRunner with the verified stage-2 binary, then run the whole suite (do NOT use pasbuild test — build the runner and invoke it directly so you see the real total):

cp compiler/target/blaise_rtl.a /tmp/blaise_rtl.a
pasbuild test-compile -m blaise-compiler --compiler /tmp/fp_blaise2
compiler/target/TestRunner          # must print: OK (N tests, ...)

Note the test count N. If any test fails, stop — do not release.

Step 5 — commit and tag

Stage Blaise.pas whole (its only change is the version), but stage only the version-line hunk of project.xml — it frequently carries unrelated working changes that must not enter the release commit:

git add compiler/src/main/pascal/Blaise.pas
printf '%s\n' 'y' 'n' 'n' | git add -p project.xml   # stage hunk 1 (version) only
git diff --cached --stat   # MUST show exactly: Blaise.pas | 2 +-  AND  project.xml | 2 +-

If git diff --cached --stat shows project.xml with more than 2 changed lines, you swept in unrelated changes — git restore --staged project.xml and redo the git add -p. (The hunk-selection answers y n n assume the version line is the first hunk; if project.xml has a different shape, inspect with git add -p and stage only the <version> hunk.)

Commit with this pattern (HEREDOC for clean formatting):

release: v<X.Y.Z>

<one or two sentences: fixpoint verified on N lines of QBE IR (all four
fixpoints green: QBE, native, internal-asm, warm-cache), the headline themes
of the cycle, and the test count.>

Then tag:

git tag v<X.Y.Z>

Never reference Claude in the commit message. Do not push. The user pushes manually.

Step 6 — archive the release binary + refresh the -pre bootstrap binary

Archive the native stage-2 binary (/tmp/fpn_blaise2 from Step 3) — native is the default backend, so the shipped/bootstrap binary must be native, NOT the QBE /tmp/fp_blaise2:

mkdir -p releases/v<X.Y.Z>
cp /tmp/fpn_blaise2 releases/v<X.Y.Z>/blaise       # NATIVE binary from fixpoint-native.sh
chmod +x releases/v<X.Y.Z>/blaise
cp compiler/target/blaise_rtl.a releases/v<X.Y.Z>/blaise_rtl.a
releases/v<X.Y.Z>/blaise --help | head -1          # sanity: Blaise Compiler v<X.Y.Z>

Every release directory must contain both blaise and blaise_rtl.a — the bootstrap (FindRTL) looks for the archive beside the binary, so a release missing blaise_rtl.a cannot serve as a stage-1 bootstrap.

Now rename the rolling -pre directory to the next cycle and refresh its binary to this verified build, so cold-bootstrap of master stays current for the dev cycle about to open. releases/ is gitignored, so this is a plain filesystem mv (no git mv):

# The -pre dir from the previous cycle is named v<X.Y.Z>-pre. Rename it to the
# NEXT minor so the newest releases/v*-pre/ tracks the cycle we are opening.
mv releases/v<X.Y.Z>-pre releases/v<next-minor>.0-pre   # e.g. v0.12.0-pre -> v0.13.0-pre
cp /tmp/fpn_blaise2 releases/v<next-minor>.0-pre/blaise
cp compiler/target/blaise_rtl.a releases/v<next-minor>.0-pre/blaise_rtl.a
rm -f releases/v<next-minor>.0-pre/*.bak             # drop any stale backup cruft
releases/v<next-minor>.0-pre/blaise --help | head -1 # sanity check

(If no -pre directory exists yet, create releases/v<next-minor>.0-pre/ directly. The GitHub Actions CI and rolling-bootstrap.sh both look for the newest releases/v*-pre/, so the name must be the cycle just opened — not the one just released.)

Update the test count in README.adoc (the "Testing: N tests" line in the Project Status section) to match the suite total from Step 4, then commit it:

git add README.adoc
git commit -m "docs: update test count to <N> for v<X.Y.Z>"

Then build the release tarball for the GitHub Releases page — a single top-level directory containing blaise, blaise_rtl.a, README.adoc, NOTICE, and LICENSE:

STAGING=$(mktemp -d)
DIRNAME="blaise-v<X.Y.Z>-linux-x86_64"
mkdir "$STAGING/$DIRNAME"
cp releases/v<X.Y.Z>/blaise       "$STAGING/$DIRNAME/blaise"
cp releases/v<X.Y.Z>/blaise_rtl.a "$STAGING/$DIRNAME/blaise_rtl.a"
cp README.adoc                    "$STAGING/$DIRNAME/README.adoc"
cp NOTICE                         "$STAGING/$DIRNAME/NOTICE"
cp LICENSE                        "$STAGING/$DIRNAME/LICENSE"
tar -czf releases/blaise-v<X.Y.Z>-linux-x86_64.tar.gz -C "$STAGING" "$DIRNAME"
rm -rf "$STAGING"
tar -tzf releases/blaise-v<X.Y.Z>-linux-x86_64.tar.gz   # verify

Do not git add any binary files or the tarball. releases/ is gitignored and the user keeps binaries local on purpose.

Step 7 — bump to next -dev version

Edit compiler/src/main/pascal/Blaise.pas again, changing '<X.Y.Z>' to '<next-minor>.0-SNAPSHOT' (e.g. 0.11.00.12.0-SNAPSHOT). Use -SNAPSHOT in BOTH files — the older -dev suffix in Blaise.pas was retired so the two version strings stay in sync.

Also edit project.xml, changing <version> to <next-minor>.0-SNAPSHOT.

REQUIRED: also bump compiler/src/main/pascal/uCompilerId.pas back to the -SNAPSHOT form for the new base version, keeping the same +bif<N> tag → 'blaise-<next-minor>.0-SNAPSHOT+bif<N>'. This keeps COMPILER_ID in sync with project.xml's base version — otherwise bif-coverage's TestRun_BifCoverage_NoGaps fails (it checks the project base version is a substring of COMPILER_ID). Do NOT bump +bif<N> here; the .bif format hasn't changed just by opening a dev cycle.

Stage selectively again (project.xml may still carry unrelated changes):

git add compiler/src/main/pascal/Blaise.pas compiler/src/main/pascal/uCompilerId.pas
printf '%s\n' 'y' 'n' 'n' | git add -p project.xml
git diff --cached --stat   # Blaise.pas, uCompilerId.pas, project.xml — version lines only
git commit -m "chore: begin v<next-minor>.0-dev cycle"

NOTE on CI / rolling-bootstrap anchor: when this release becomes the new cold-bootstrap baseline, bump STAGE1_TAG in .github/workflows/bootstrap.yml and the rolling-bootstrap default---from anchor to v<X.Y.Z>. This is a CI-only change and typically lands in its own commit alongside the release (it does not block the release itself).

Step 8 — write community post and changelog

Write two documents at the project root. Both are untracked (do not git add them) — they are working files the user edits and publishes manually.

Both documents cover only the commits between the previous release tag and the tag being cut now — the range <prev-tag>..v<X.Y.Z>, where <prev-tag> is the most recent existing release tag before this one. Find it with git describe --tags --abbrev=0 v<X.Y.Z>^ (the latest tag reachable from the commit just before the release commit), or git tag --sort=-v:refname | head. Do NOT summarise the whole history — only this cycle's delta.

Survey the commits first (the v<X.Y.Z> tag already exists from Step 5): git log <prev-tag>..v<X.Y.Z> --oneline, and group by theme. Helpful groupings: git log <prev-tag>..v<X.Y.Z> --oneline | grep ' feat' (features), ... | grep ' fix' (fixes), ... | grep ' perf' (performance).

community-post-v<X.Y.Z>.md

A warm, readable announcement for the Blaise community (forums, mailing lists, social media). Style:

  • Not a commit-by-commit log — group changes into themes with a clear headline win up top.
  • Sprinkle emoji throughout section headings and milestone call-outs.
  • Warm, collaborative voice; British English spelling (see the global writing style guide).
  • Include tiny code examples for the headline language features — a 36 line snippet per feature is far more compelling than prose. VERIFY each snippet actually compiles before publishing: write it to /tmp/snip.pas and run compiler/target/blaise --source /tmp/snip.pas --output /tmp/snip (try both --backend qbe and --backend native for codegen-sensitive features).
  • Cover: headline win(s), language/stdlib additions, bug-fix hardening, notable compiler internals, the fixpoint line count and test count, and a closing call to action pointing at the GitHub release page.
  • End with a forward-looking line ("Onwards to v! 🙌").
  • Reference changelog-v<X.Y.Z>.md for full details.

changelog-v<X.Y.Z>.md

A technical changelog for the GitHub Releases page. Style:

  • Grouped by area (Native backend, Toolchain, Language, ARC/runtime, stdlib, Codegen, Semantic, Debugging, Performance, Tooling/CI).
  • Each entry is one concise line: what changed and why it matters. More technical than the community post.
  • A short "Examples" block under the Language section showing the new syntax is welcome (same verify-it-compiles rule applies).
  • Close with the fixpoint IR line count and the test count.

Step 9 — report to the user

Summarise:

  • Tag created (e.g. v<X.Y.Z>)
  • Stage-2 IR line count and all four OK lines: "FIXPOINT_OK / NATIVE_FIXPOINT_OK / NATIVE_INTERNAL_OK / WARMCACHE_FIXPOINT_OK"
  • Test count (N tests passing)
  • Binary path: releases/v<X.Y.Z>/blaise (note: not in git)
  • -pre bootstrap binary refreshed
  • New dev version on master
  • Last 34 commit oneliners
  • Community post: community-post-v<X.Y.Z>.md (untracked)
  • Changelog: changelog-v<X.Y.Z>.md (untracked)
  • Release tarball: releases/blaise-v<X.Y.Z>-linux-x86_64.tar.gz (upload to GitHub Releases)

Mention that the user must git push --tags themselves if they want the tag on the remote — never push automatically.

Common pitfalls

  • Unrelated changes swept into the release commit. git add project.xml stages the WHOLE file, including pre-existing build-option edits. Always git add -p and confirm git diff --cached --stat shows exactly two one-line changes before committing. (This bit the v0.11.0 cut — the commit had to be reset and redone.)
  • Archiving the QBE binary instead of the native one. Native is the default backend, so the shipped/bootstrap binary must be /tmp/fpn_blaise2 (from fixpoint-native.sh), NOT the QBE /tmp/fp_blaise2 (from fixpoint.sh). The QBE fixpoint is still required as a reproducibility guard — it just isn't the artefact.
  • Stale fixpoint binary in /tmp. A /tmp/fpn_blaise2 (or /tmp/fp_blaise3) left over from an earlier run can be days old. Always confirm the binary you archive prints the RIGHT version: <binary> --help | head -1.
  • COMPILER_ID out of sync with project.xml → bif-coverage fails. At release COMPILER_ID drops -SNAPSHOT; at the dev-cycle bump it must re-gain -SNAPSHOT for the NEW base version. The base version in COMPILER_ID must always match project.xml's <version> base, or TestRun_BifCoverage_NoGaps fails. Keep the +bif<N> tag unchanged unless the .bif format changed.
  • Forgetting to rename the -pre dir. The newest releases/v*-pre/ must name the cycle being OPENED, not the one just released. After cutting v<X.Y.Z>, mv releases/v<X.Y.Z>-pre releases/v<next-minor>.0-pre (filesystem move — releases/ is gitignored). CI and rolling-bootstrap pick the newest by name.
  • compiler/target/blaise still on the old version. fixpoint.sh does not rebuild it; do the pasbuild compile --compiler /tmp/fp_blaise2 step (Step 2) before the native fixpoints, or they run against the previous version.
  • Forgetting --compiler. Every pasbuild command must pass --compiler <blaise-binary>; without it PasBuild falls back to FPC, which is not part of this toolchain.
  • Missing blaise_rtl.a beside --compiler. When pointing --compiler at /tmp/fp_blaise2, copy compiler/target/blaise_rtl.a to /tmp/blaise_rtl.a first — FindRTL looks beside the binary, and a stale archive causes undefined-reference link errors.
  • Wrong working directory. All pasbuild commands fail outside the project root with a confusing version error. Stay at /data/devel/new-pascal-compiler.
  • Backgrounded fixpoint output drops. Run the fixpoint scripts in the foreground with a generous timeout (600000 ms). Self-built compilation of the compiler takes ~2 min per stage.