A simple, small calculator placed discreetly near the Windows Taskbar or anywhere on the screen
0

Configure Feed

Select the types of activity you want to include in your feed.

docs: design spec for progressive build number in version field 4

Editor-safe (.rc2 isolation) VERSIONINFO whose 4th field is a
committed, progressive build counter incremented by a pre-build
MSBuild target. Persists across machines via git.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Marco Maroni (Jul 17, 2026, 12:32 PM +0200) 4516eb08 17c64e04

+194
+194
docs/superpowers/specs/2026-07-17-build-number-versioning-design.md
··· 1 + # Progressive Build Number in Version Field 4 — Design 2 + 3 + **Date:** 2026-07-17 4 + **Status:** Approved (brainstorming) 5 + 6 + ## Goal 7 + 8 + Turn the 4th version field (`revision`) of the executable's `VERSIONINFO` into a 9 + **progressive build counter** that increments on every build, so two `.exe` files can be 10 + compared at a finer granularity than the semantic `major.minor.build` (currently `3.0.1`) 11 + to confirm they came from the same build. 12 + 13 + - Fields 1–3 (`3.0.1`) stay **manual/semantic** and remain the only part shown in the 14 + About dialog. 15 + - Field 4 is **not** shown in the About dialog; it is only a build fingerprint visible in 16 + the file's Windows "Details" property page and the binary `FILEVERSION`. 17 + - The counter is **committed to the repository** so it keeps progressing when the project 18 + is built from different machines. 19 + 20 + ## Approach 21 + 22 + Approach **B** (editor-safe): the entire `VS_VERSION_INFO` block is moved out of the 23 + Visual Studio resource-editor–managed area into a hand-maintained `.rc2` file that uses 24 + preprocessor macros. The counter lives in a committed plain-text file and is regenerated 25 + into a header by a pre-build MSBuild target. 26 + 27 + ## Files 28 + 29 + ### `version.h` (committed) — semantic version, single source of truth 30 + ```c 31 + #pragma once 32 + 33 + #define VER_MAJOR 3 34 + #define VER_MINOR 0 35 + #define VER_BUILD 1 36 + 37 + // Per-build counter. version_build.h is regenerated on every build by the 38 + // IncrementBuildCounter MSBuild target (BeforeTargets="ResourceCompile"), so it always 39 + // exists at compile time. The #ifndef is a safety fallback only. 40 + #include "version_build.h" 41 + #ifndef VER_BUILDNUM 42 + # define VER_BUILDNUM 0 43 + #endif 44 + ``` 45 + The manual version is edited **here** from now on, not in the VS resource editor. 46 + 47 + ### `BuildCounter.txt` (committed) — the counter's source of truth 48 + A single integer on one line. Seeded at `1` to match the current `3.0.1.1`. This is the 49 + file that travels between machines; each build rewrites it with the incremented value. 50 + 51 + ### `version_build.h` (gitignored, generated) — derived, never committed 52 + Regenerated every build from `BuildCounter.txt`: 53 + ```c 54 + #define VER_BUILDNUM <n> 55 + ``` 56 + Derived artifact → excluded from git (added to `.gitignore`). 57 + 58 + ### `TaskbarCalculator.rc2` (committed) — the hand-maintained version resource 59 + Holds the whole `VS_VERSION_INFO` block, built from the macros. Skeleton: 60 + ```c 61 + #include "version.h" 62 + 63 + #define VER_STRINGIZE2(x) #x 64 + #define VER_STRINGIZE(x) VER_STRINGIZE2(x) 65 + #define VER_VERSION_STR \ 66 + VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." \ 67 + VER_STRINGIZE(VER_BUILD) "." VER_STRINGIZE(VER_BUILDNUM) 68 + 69 + LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT 70 + 71 + VS_VERSION_INFO VERSIONINFO 72 + FILEVERSION VER_MAJOR,VER_MINOR,VER_BUILD,VER_BUILDNUM 73 + PRODUCTVERSION VER_MAJOR,VER_MINOR,VER_BUILD,VER_BUILDNUM 74 + FILEFLAGSMASK 0x3fL 75 + #ifdef _DEBUG 76 + FILEFLAGS 0x1L 77 + #else 78 + FILEFLAGS 0x0L 79 + #endif 80 + FILEOS 0x40004L 81 + FILETYPE 0x2L 82 + FILESUBTYPE 0x0L 83 + BEGIN 84 + BLOCK "StringFileInfo" 85 + BEGIN 86 + BLOCK "040004b0" 87 + BEGIN 88 + VALUE "CompanyName", "Marco Maroni" 89 + VALUE "FileDescription", "Taskbar Calculator" 90 + VALUE "FileVersion", VER_VERSION_STR 91 + VALUE "InternalName", "TaskbarCalculator.exe" 92 + VALUE "LegalCopyright", "Copyright (C) 2001-2019" 93 + VALUE "OriginalFilename", "TaskbarCalculator.exe" 94 + VALUE "ProductName", "Taskbar Calculator" 95 + VALUE "ProductVersion", VER_VERSION_STR 96 + END 97 + END 98 + BLOCK "VarFileInfo" 99 + BEGIN 100 + VALUE "Translation", 0x400, 1200 101 + END 102 + END 103 + ``` 104 + `rc.exe` supports `#`-stringize and adjacent string-literal concatenation, so 105 + `VER_VERSION_STR` compiles to e.g. `"3.0.1.42"`. (The stale `LegalCopyright` year is 106 + preserved verbatim — out of scope here.) 107 + 108 + ### `TaskbarCalculator.rc` (edited) — wiring, editor-safe 109 + 1. Remove the `VS_VERSION_INFO` block (current lines ~21–57, the `// Version` comment 110 + through the closing `END`) from the neutral-resources section. 111 + 2. Register the include in the (currently empty) `TEXTINCLUDE 3` block so the editor 112 + preserves it: 113 + ``` 114 + 3 TEXTINCLUDE 115 + BEGIN 116 + "#include ""TaskbarCalculator.rc2""\r\n" 117 + "\0" 118 + END 119 + ``` 120 + 3. Add the real include in the bottom `#ifndef APSTUDIO_INVOKED` region ("Generated from 121 + the TEXTINCLUDE 3 resource"): 122 + ```c 123 + #ifndef APSTUDIO_INVOKED 124 + #include "TaskbarCalculator.rc2" 125 + #endif // not APSTUDIO_INVOKED 126 + ``` 127 + When VS opens the `.rc` it defines `APSTUDIO_INVOKED`, so it skips the include and never 128 + parses `.rc2`/`version.h`/macros — the editor stays happy. `rc.exe` (no `APSTUDIO_INVOKED`) 129 + compiles the include normally. 130 + 131 + ### `TaskbarCalculator.vcxproj` (edited) — the increment target 132 + ```xml 133 + <Target Name="IncrementBuildCounter" BeforeTargets="ResourceCompile"> 134 + <PropertyGroup> 135 + <BuildCounterFile>$(MSBuildProjectDirectory)\BuildCounter.txt</BuildCounterFile> 136 + <BuildNumberHeader>$(MSBuildProjectDirectory)\version_build.h</BuildNumberHeader> 137 + </PropertyGroup> 138 + <ReadLinesFromFile File="$(BuildCounterFile)" Condition="Exists('$(BuildCounterFile)')"> 139 + <Output TaskParameter="Lines" ItemName="_BuildCounterLines" /> 140 + </ReadLinesFromFile> 141 + <PropertyGroup> 142 + <_OldBuildCounter>@(_BuildCounterLines)</_OldBuildCounter> 143 + <_OldBuildCounter Condition="'$(_OldBuildCounter)' == ''">0</_OldBuildCounter> 144 + <_NewBuildCounter>$([MSBuild]::Add($(_OldBuildCounter), 1))</_NewBuildCounter> 145 + </PropertyGroup> 146 + <WriteLinesToFile File="$(BuildCounterFile)" Lines="$(_NewBuildCounter)" Overwrite="true" /> 147 + <WriteLinesToFile File="$(BuildNumberHeader)" Lines="#define VER_BUILDNUM $(_NewBuildCounter)" Overwrite="true" /> 148 + <Message Importance="high" Text="TaskbarCalculator build number: $(_NewBuildCounter)" /> 149 + </Target> 150 + ``` 151 + Runs before every `ResourceCompile`; rewriting `version_build.h` makes it newer than the 152 + compiled resource, so `rc.exe` recompiles and the fresh number is baked into the `.exe`. 153 + The counter is shared across Debug and Release (one file in the project directory). 154 + 155 + ### `.gitignore` (edited) 156 + Add `version_build.h` (generated). `BuildCounter.txt` stays **tracked**. 157 + 158 + ### `AboutDialog.cpp` — unchanged 159 + Already reads only `major.minor.build` from the runtime `FILEVERSION`, so it keeps showing 160 + `3.0.1`; the new field 4 stays invisible there. It reflects `version.h` automatically. 161 + 162 + ## Constraints / accepted side effects 163 + 164 + - **16-bit fields:** `VERSIONINFO` numbers are `0..65535`; a simple counter fits for a very 165 + long time. (This is why field 4 is a plain counter, not an encoded timestamp.) 166 + - **Always-dirty counter:** every build rewrites `BuildCounter.txt`, so it shows as 167 + modified in `git status`. It is committed together with real work; on another machine a 168 + `pull` continues the sequence. Building on two machines before syncing can cause a trivial 169 + conflict on `BuildCounter.txt`, resolved by keeping the higher number. 170 + - **VS fast up-to-date check:** command-line `msbuild` (the documented build path) always 171 + runs the target and increments. The VS IDE "fast up-to-date check" may occasionally skip 172 + a no-op build without incrementing — acceptable, since the counter only needs to advance 173 + on real builds. 174 + - **First build / fresh clone:** `version_build.h` is absent until the first build, but the 175 + target generates it before `ResourceCompile`, so the first build succeeds. Nothing but 176 + `rc.exe` (at build time) ever parses `version.h`/`.rc2`, so a missing generated header 177 + never affects the C++ IntelliSense or the resource editor. 178 + 179 + ## Testing 180 + 181 + - Engine regression: `tests\build-and-run.cmd` → 16/16 (unaffected; no engine change). 182 + - Build twice with `msbuild ... /p:Configuration=Release /p:Platform=x64` and confirm the 183 + `.exe` "Details" tab shows the version's 4th field incrementing (e.g. `3.0.1.2` then 184 + `3.0.1.3`), and that `BuildCounter.txt` advances in lockstep. 185 + - Confirm the About dialog still shows `3.0.1` (field 4 not shown). 186 + - Confirm opening/closing the `.rc` in the VS resource editor leaves `.rc2` and the macros 187 + untouched. 188 + 189 + ## Out of scope (YAGNI) 190 + 191 + - Showing the build number in the About dialog. 192 + - Timestamp/date encoding, git-hash embedding, or CI integration. 193 + - Fixing the stale `LegalCopyright` year or other version strings. 194 + - Bumping the semantic version — that remains a manual edit in `version.h`.