Solveig

Making a release

How a feature ships, and the checks a claim has to pass before it is believed, are in method.md. This is the release itself.

Four files in one commit, a tag, and a page on GitHub that the tree cannot record. Written down because it is a ritual with verification steps in it, and because three of those steps were learned by getting them wrong.

First, look at what the release adds

git diff --name-status vX.Y.Z..HEAD    # the release before this one

Read the list. It is the only view here that asks what a stretch of work added to the tree, and every other check answers a different question: expect.sol counts files of a kind, the link checker walks markdown, site.sh reads published pages, and make test compiles what the Makefile names.

It is here because of programs/:= — 117 kilobytes, byte-identical to pascal.sol, committed by accident on 2026-08-31 and found on 2026-09-02 with one command, having survived two days and fifty-four commits inside a directory four checks inspect. make dist archives HEAD, so a stray in the tree is a stray in the tarball. method.md has the rule.

The four files, in one commit titled Release X.Y.Z

   
solum/include/solum/common.h SOLUM_VERSION. All four binaries report it, and make dist names the tarball from it
README.md a new Status paragraph above the previous one; the old ones stay
docs/CHANGELOG.md a ## X.Y.Z — DATE heading above the loose entries that accumulated since the last one
index.md its Status block, since 0.39.0 — it had said 0.3.0 for thirty-five releases because nothing pointed at it

The loose entries below the heading keep their own commit hashes and dates. The release heading is a summary of them, not a replacement.

Verify compatibility rather than asserting it

Every release since 0.36.0 has, and it is the part of the notes a reader cannot check for themselves — which makes it the part most worth checking.

git archive vX.Y.Z | tar -x -C /tmp/prev     # the release before this one
( cd /tmp/prev && make )

Then compile every example with both compilers and compare the bytes, and run each .sob on both machines both ways round.

Run both machines from the same working directory. Four examples read the filesystem and will differ for that reason alone, which looks exactly like an incompatibility and is not.

And run both compilers from the same directory too, which is a second reason and was learnt in 0.40.0. An @include records the library’s path in the chunk, so the previous release unpacked elsewhere resolves lib/ relative to itself and writes a different name for identical code. That reported five examples differing when none did. Copy the old solas beside the new one rather than running it where it was unpacked.

examples/system.sol always differs, because it prints how long things took. That one is read, not compared.

When extensions were touched, load the previous release’s bundle on the new build. That is the ABI question and it is not the bytecode question — the two can disagree, and 0.39.0 is the release where they did.

Then the tag

Annotated, named vX.Y.Z, with a one-line message in the form Solveig X.Y.Z -- the headline.

git tag -a v0.39.0 -m "Solveig 0.39.0 -- measured against another language, and the three things it found"
git push origin main && git push origin v0.39.0

Then the page, and the three fixups it needs

The release body is the changelog entry, with three changes that the journal recorded the first time each was needed — it said two until 0.41.0, having been written when there were two and never counted again, which is the stale sentence this page warns about happening to this page. All three are cases of something that stops being right by being moved rather than by being wrong.

Strip the count markers. A number written with one of them — the comment notation programs.md describes, which tells the checker to recount the figure in front of it — is a live number. A release page is a historical statement that nothing recounts and nothing on GitHub would correct.

The markers are not quoted here on purpose. A comment that says recount the number before me means that wherever it appears, and prose explaining the notation is not an exception; writing one into a sentence about it is how the build was broken the first time this was written down.

Unwrap the paragraphs. GitHub renders release notes with hard line breaks, so the 79-column wrapping every document here is written to comes out as a literal break after every line — a narrow ragged column inside a wide box, breaking mid-sentence. 0.40.0’s page had 41 of them across 8 paragraphs and was published that way before anybody looked at it. Join the lines within each paragraph and leave the blank lines between them.

This is the one fixup that cannot be found by reading the markdown, because the markdown is correct. Open the page.

It has a mechanical form as well, and 0.41.0 used it: fetch the published page and count. <br> inside the body is the fault, and the paragraph count is what it should be instead — fourteen paragraphs and zero <br> there, against 0.40.0’s forty-one breaks across eight. That reads the renderer’s output, which is the artefact and is the point of it, and it would say nothing about a fault that produced no <br>; it fails on the one that has actually happened, which is the standard the rest of these checks are held to.

Absolutise the links, pinned at the tag. [NET.md](/Solveig/docs/NET.html) is right inside docs/ and a 404 from /releases/tag/vX.Y.Z. Rewrite to https://github.com/hansolovkarlsson/Solveig/blob/vX.Y.Z/docs/NET.md — at the tag rather than at main, so the page goes on describing what this release shipped after the documents move underneath it.

Then fetch each one and check it answers 200, rather than trusting the string you built. The tag has to exist on the remote before they can, which is why this comes after the push; 0.41.0 had six and all six answered.

Attach dist/solveig-X.Y.Z.tar.gz from make dist, which archives HEAD — check that HEAD is the release commit the tag points at.

What the document checker does not cover

Run site.sh first, after pushing and once Pages has rebuilt — a minute or so. It holds every published page against the source at origin/main: headings that stopped being headings, links that lost the site’s baseurl, anchors that name nothing. Both fault classes it exists for are markdown that is correct and publishes wrong, so nothing that reads the file can see them, and one of them stood for ten days and twenty releases.

The checker itself recounts prose in docs/ and reads README.md and index.md for claims. It does not know that a version number in prose is a claim, and _config.yml is not a document to it at all — which is how the site’s description sat at 123 messages and called Solveig the Solum language, inverting the 0.36.0 rename, until somebody read it. A release is a good moment to read the front page as a stranger would.