Step 9 inspected latest.json and stopped there. That proves the file is well
formed, not that it installs: the TLS download, signature verification,
installer execution and relaunch are exercised by no test at all — cargo check
and cargo test only prove that code compiles. A regression there surfaces at
update time, on a user's machine, and automatic updates are a Base+ feature.
Written as a manual checklist rather than automation because it needs two real
desktop environments. The value is in the preconditions, which are what make
the difference between running the test and only appearing to:
- Test from a machine still on the PREVIOUS version. check() compares
release.version > current strictly, so testing on the machine that just
built the release shows "up to date" and the checklist gets ticked as
"nothing to update" — the silent skip it exists to prevent, now with a
paper trail claiming it passed.
- A Base+ key on each machine, with any activation.token from another machine
removed. Auto-update is entitlement-gated: useUpdater dispatches NOT_ENTITLED
before check() ever runs, and a token bound to a different machine_id
silently resolves the edition back to Free.
- A live polkit agent on Linux, and record which prompt actually appeared —
install_deb cascades pkexec -> zenity/kdialog -> terminal sudo, so without an
agent the app appears to hang instead of failing.
The two target flows are written separately because they genuinely differ: on
Windows downloadAndInstall never returns (the process exits and NSIS takes
over, so readyToInstall/installing never render), while on Linux the app stays
alive through a polkit prompt and a restart control.
The doc states what the test does NOT prove: `tar` stays unexercised. Its
vulnerable path is only reached by install_appimage and the macOS .app.tar.gz
branch; our .deb goes through `pkexec dpkg -i` and our NSIS path launches the
.exe. That corrects #315's own premise.
The failure path is documented as an incident playbook rather than a
"rollback", because it is not one. Republishing the previous latest.json stops
propagation, but check()'s strict comparison means users already on the broken
version are never offered the older one — the real fix is a vN+1.
rpm is recorded as known-broken, not unverified, and tracked in #320.
Resolves#315
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follow-up on the review of #232:
- .claude/ is tracked (rules + skills) and cannot affect the frontend build,
so a change confined to it no longer queues a 1m43s job for nothing.
- The release skill's pre-flight step named check.yml, which this PR deletes.
The reasoning still holds — the check workflows only run on PRs, never on
main, so a merged tip has never been seen by CI — only the filename moved.
Its dated changelog entry is left alone.
Resolves#232
Lessons from the v0.13.0 release (session fdda84cb):
- Step 0: revalidate the tip locally before tagging — check.yml never
runs on main, and ensure .claude/worktrees/ is empty (vitest recurses)
- Step 9: verify the published release — 7 expected artifacts and
latest.json content (drives auto-update); status=success is not enough
- Rule: tagging publishes externally via the updater JSON — confirm
with Max before tagging
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Avoids the false 'empty FR changelog' diagnosis during the version-migration
step of the /release skill. Committing a WIP edit orphaned from session 5466da98.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>