turbo-editors/turbo-gopublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-go.git
git clone ssh://git@rickub.com/turbo-editors/turbo-go.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

📦 Turbo Go 3d7798b · on v1.0.2 · k33g · 11h ago
2026-08-31-version-in-about.md · 71 lines · 6.4 KBmarkdown
Blame HistoryOpen raw

Handoff — 2026-08-31 — The version in the About box

State

Ticket 0010 is implemented and uncommitted, on main, on top of PR #7 (88a4c38). Nothing was branched: the changes are in the working tree, so git checkout -b feature/about-version at any point before committing carries them along.

internal/version is new. app.Version is gone. The Makefile, scripts/install.sh and a new make version target stamp git describe --tags --dirty, the short commit and a UTC build time through -ldflags -X. About and -version show whatever the build recorded and stay silent about the rest.

Full suite green, green under -race. Quality gate PASS — 0/0/0, complexity 1592. Docs in both languages, four READMEs, and the drawio diagram all in sync.

Also uncommitted, and not mine: .tickets/issues/0004 and 0017 at state: closed, from the previous session.

In flight

Nothing.

Next steps

  1. Review and commit. A commit message was proposed at the end of the session.
  2. Close ticket 0010 — the user's call, as always.
  3. The first real release is untested by definition: nothing has been tagged since v0.1.0. make version on a tagged commit should print the bare tag with no -N-g<hash> suffix, and that is the one assertion no test in this repository can make for you.

Open questions / blockers

None.

Watch out for

  • Go 1.26 does not say (devel). A plain go build . in a checkout reports a pseudo-version0.1.1-0.20260831165958-88a4c3859bf3+dirty — derived from the last tag. isPseudoVersion catches it and reports devel. If a future Go changes that shape, TestEveryFormOfPseudoVersionIsRecognised is where it will show.
  • The separator before a pseudo-version's timestamp is a dot, not a dash, whenever a base tag precedes the commit — the base ends in -0. or -pre.0.. My first recogniser assumed a dash and silently matched nothing. Three of the four forms in the test caught it.
  • vcs.time is not a build date. It is the commit's timestamp. Anyone tempted to fill the empty Built: line for unstamped builds from it will make it wrong on every binary.
  • resolve is separate from Current on purpose. A test binary cannot be built with linker stamps, so testing through Current would leave every interesting case uncovered. Add new cases to resolve, not to Current.
  • unknown is load-bearing. It is what -version prints when nothing named the build, and TestTheInstalledBinaryDoesNotReportAnUnknownVersion uses its absence to prove the installer's ldflags reached the linker. Do not make it a version number.
  • Everything under .memory/handoffs/2026-08-31-tool-menus.md still applies — in particular the VT-emulator recipe, which verified this feature too, and the rule that internal/terminal/tmprender/ must be deleted again before the quality gate.

Afterwards — the release script, and what removing a constant costs

The user ran their own ./03-build-releases.sh and it failed. Two defects, both mine:

  1. It read the version with awk '{print $NF}', which took the build timestamp once -version grew a parenthetical.
  2. The cross-compile loop had no -ldflags at all. All five downloadable binaries would have said devel while the release announced v0.2.0. The host binary was stamped and correct, so nothing but a hand check would have caught it.

The second is the lesson: removing a compiled-in constant moves a cost from visible to invisible. A stale constant at least travelled into every build; a stamp only reaches the builds that ask for it. Every build path has to be found and stamped — make build, scripts/install.sh, and the five cross-compiles in 03-build-releases.sh.

Fixed by adding a make ldflags target the script reads, so the -X paths exist once. The script's version check is now three plain questions to git and one grep -F, none of which parses the -version sentence. release_test.go covers all of it.

If a sixth build path ever appears, stamp it. TestTheReleaseScriptStampsTheBinariesItShips only guards the one that exists.

And then — the tag script was the real culprit

03 refused again, and this time it was right but misdiagnosed: it said "HEAD carries no tag" when the tag existed and HEAD had merely moved one commit past it.

The cause was three steps upstream. 01-release.tag.sh had no set -e. Run twice, its git tag failed with "already exists", the failure was ignored, and the git push origin "${TAG}" on the next line pushed the old tag. Everything downstream was then working correctly on a lie.

Fixed: set -euo pipefail, a tag-exists check against the local ref and git ls-remote (the state the user was in — local tag deleted, remote tag still there — is invisible locally), nothing-to-commit tolerated, and the tag applied only after a successful push.

02-release.publish.sh still has no set -e and must not naively be given one: its read -r -d '' DATA <<-EOM always exits non-zero by design and would kill the script immediately. It also needs the curl HTTP status checked, since curl exits 0 on a 4xx. Left alone deliberately — it publishes to Codeberg and the user was not asking for it.

The remote is unreachable from this sandbox (SSH, no key), so whether v0.2.0 still exists on Codeberg could not be established. That question was handed back to the user.

Finally — the simplification, and the lesson

The user stopped me: "fais quelque chose de plus simple, tu build comme avant avec le tag de release." They were right.

I had 03 stamp git describe and then verify it agreed with TAG — three gates that each looked reasonable and together blocked a release three times running. git describe answers where is HEAD; a release builder is asking what release is this. Those are different questions, and the gates existed only to reconcile an answer I should never have been asking for.

03 now stamps TAG directly (make ldflags VERSION="${TAG}", make build VERSION="${TAG}") and the gates are gone. Building needs no tag at all; only 02 does. Do not reintroduce those checks — the mismatch they detected cannot occur once the tag is the single source.

01's guards stay: they prevent pushing the wrong tag and block no build.

The lesson, for whoever hits something like this: when you find yourself adding checks to reconcile two sources of truth, delete one of the sources instead.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# Handoff — 2026-08-31 — The version in the About box

## State

Ticket 0010 is implemented and **uncommitted**, on `main`, on top of PR #7 (`88a4c38`). Nothing was branched: the changes are in the working tree, so `git checkout -b feature/about-version` at any point before committing carries them along.

`internal/version` is new. `app.Version` is gone. The Makefile, `scripts/install.sh` and a new `make version` target stamp `git describe --tags --dirty`, the short commit and a UTC build time through `-ldflags -X`. About and `-version` show whatever the build recorded and stay silent about the rest.

Full suite green, green under `-race`. Quality gate PASS — 0/0/0, complexity 1592. Docs in both languages, four READMEs, and the drawio diagram all in sync.

Also uncommitted, and **not mine**: `.tickets/issues/0004` and `0017` at `state: closed`, from the previous session.

## In flight

Nothing.

## Next steps

1. **Review and commit.** A commit message was proposed at the end of the session.
2. **Close ticket 0010** — the user's call, as always.
3. The first real release is untested by definition: nothing has been tagged since `v0.1.0`. `make version` on a tagged commit should print the bare tag with no `-N-g<hash>` suffix, and that is the one assertion no test in this repository can make for you.

## Open questions / blockers

None.

## Watch out for

- **Go 1.26 does not say `(devel)`.** A plain `go build .` in a checkout reports a *pseudo-version*`0.1.1-0.20260831165958-88a4c3859bf3+dirty` — derived from the last tag. `isPseudoVersion` catches it and reports `devel`. If a future Go changes that shape, `TestEveryFormOfPseudoVersionIsRecognised` is where it will show.
- **The separator before a pseudo-version's timestamp is a dot, not a dash**, whenever a base tag precedes the commit — the base ends in `-0.` or `-pre.0.`. My first recogniser assumed a dash and silently matched nothing. Three of the four forms in the test caught it.
- **`vcs.time` is not a build date.** It is the commit's timestamp. Anyone tempted to fill the empty `Built:` line for unstamped builds from it will make it wrong on every binary.
- **`resolve` is separate from `Current` on purpose.** A test binary cannot be built with linker stamps, so testing through `Current` would leave every interesting case uncovered. Add new cases to `resolve`, not to `Current`.
- **`unknown` is load-bearing.** It is what `-version` prints when nothing named the build, and `TestTheInstalledBinaryDoesNotReportAnUnknownVersion` uses its absence to prove the installer's ldflags reached the linker. Do not make it a version number.
- Everything under `.memory/handoffs/2026-08-31-tool-menus.md` still applies — in particular the VT-emulator recipe, which verified this feature too, and the rule that `internal/terminal/tmprender/` must be deleted again before the quality gate.

## Afterwards — the release script, and what removing a constant costs

The user ran their own `./03-build-releases.sh` and it failed. Two defects, both mine:

1. It read the version with `awk '{print $NF}'`, which took the build timestamp once `-version` grew a parenthetical.
2. **The cross-compile loop had no `-ldflags` at all.** All five downloadable binaries would have said `devel` while the release announced `v0.2.0`. The host binary was stamped and correct, so nothing but a hand check would have caught it.

The second is the lesson: **removing a compiled-in constant moves a cost from visible to invisible.** A stale constant at least travelled into every build; a stamp only reaches the builds that ask for it. Every build path has to be found and stamped — `make build`, `scripts/install.sh`, and the five cross-compiles in `03-build-releases.sh`.

Fixed by adding a `make ldflags` target the script reads, so the `-X` paths exist once. The script's version check is now three plain questions to git and one `grep -F`, none of which parses the `-version` sentence. `release_test.go` covers all of it.

**If a sixth build path ever appears, stamp it.** `TestTheReleaseScriptStampsTheBinariesItShips` only guards the one that exists.

## And then — the tag script was the real culprit

`03` refused again, and this time it was right but misdiagnosed: it said "HEAD carries no tag" when the tag existed and HEAD had merely moved one commit past it.

The cause was three steps upstream. **`01-release.tag.sh` had no `set -e`.** Run twice, its `git tag` failed with "already exists", the failure was ignored, and the `git push origin "${TAG}"` on the next line pushed the *old* tag. Everything downstream was then working correctly on a lie.

Fixed: `set -euo pipefail`, a tag-exists check against the local ref **and** `git ls-remote` (the state the user was in — local tag deleted, remote tag still there — is invisible locally), nothing-to-commit tolerated, and the tag applied only after a successful push.

**`02-release.publish.sh` still has no `set -e` and must not naively be given one**: its `read -r -d '' DATA <<-EOM` always exits non-zero by design and would kill the script immediately. It also needs the curl HTTP status checked, since curl exits 0 on a 4xx. Left alone deliberately — it publishes to Codeberg and the user was not asking for it.

**The remote is unreachable from this sandbox** (SSH, no key), so whether `v0.2.0` still exists on Codeberg could not be established. That question was handed back to the user.

## Finally — the simplification, and the lesson

The user stopped me: *"fais quelque chose de plus simple, tu build comme avant avec le tag de release."* They were right.

I had `03` stamp `git describe` and then **verify** it agreed with `TAG` — three gates that each looked reasonable and together blocked a release three times running. `git describe` answers *where is HEAD*; a release builder is asking *what release is this*. Those are different questions, and the gates existed only to reconcile an answer I should never have been asking for.

`03` now stamps `TAG` directly (`make ldflags VERSION="${TAG}"`, `make build VERSION="${TAG}"`) and the gates are gone. Building needs no tag at all; only `02` does. **Do not reintroduce those checks** — the mismatch they detected cannot occur once the tag is the single source.

`01`'s guards stay: they prevent pushing the *wrong* tag and block no build.

The lesson, for whoever hits something like this: when you find yourself adding checks to reconcile two sources of truth, delete one of the sources instead.