Closes #29.
package.json is the version. Everything else should derive it, and two of the copies had already drifted.
The two that were wrong
The from-source bundle. app/Uplink/service.ts wrote CFBundleShortVersionString as a literal 0.1.0, and infoPlist() takes no argument, so no caller could pass one in. An install from a 0.1.4 tree reported itself as 0.1.0 to macOS and to anyone reading Get Info. It interpolates pkg.version now, which is what app/Desktop/launcher.ts already did.
The homepage's download button. buddy uplink:release wrote resources/data/release.json and left a comment asking whoever was releasing to commit it. The site therefore advertised whatever someone last remembered to commit, which is how it sat on 0.1.0 while the tree built 0.1.1.
The release command commits and pushes it itself now, before gh release create. That ordering is the point: gh cuts the tag from the remote tip, so any later and the tagged commit does not contain its own release metadata. It is skipped when nothing changed, so re-running a release for the same version is not an error.
The part that keeps it fixed
Neither drift was noticed, because nothing compared them. bun run release:check compares package.json against release.json and asserts the Info.plist carries the same version, and the lint job runs it.
Checked both directions. With release.json rolled back to 0.1.0, as it actually was:
The app version is not consistent:
resources/data/release.json says 0.1.0, package.json says 0.1.4.
The homepage download button reads release.json, so it is advertising a different build.
`buddy uplink:release` writes and commits that file: run a release, or put the version back.and on the current tree:
Version 0.1.4 agrees across package.json, release.json and the Info.plist.The failure names the likely cause rather than just the mismatch, because the usual one is a version bumped without a release yet, and "the homepage is still advertising the previous build" is the thing worth knowing.
A note on where it lives
.github/scripts/ did not exist. package.json already had declarations:check and deps:lockfile:check pointing into it, both inherited from the framework scaffold and both pointing at files this app does not have. I followed the naming convention rather than inventing one; the dead scripts are #39's territory.
Verification
- the check passes on this tree and fails on the real historical drift, shown above
- 139 unit tests pass, lint and typecheck clean
bun build --compilestill produces a binary, andinfoPlist()now reports 0.1.4
🤖 Generated with Claude Code