Max's decision on PR #416, after its review measured who still read the
key. tauri-plugin-updater looks up "{os}-{arch}-{installer}" first, and
every version that ships the updater (v0.2.1 and later, plugin 2.10.0,
tauri-utils 2.8.2 in each tag's Cargo.lock) carries its bundle-type
marker, so .deb and .rpm installs never reach the generic key. Its last
readers were the AppImage installs v0.4.3 to v0.4.5 (v0.4.2 targets a
route that answers 404, earlier versions read GitHub) and binaries run
outside a package; none of them can install the .deb it served, and the
updater wrote that .deb over the running file. They now get
TargetsNotFound when checking: an error on the Updates card, nothing
installed.
The key leaves the script, scripts/check-latest-json.jq and
verify-release.sh together; the test compares the three lists. Docs fix
the readers named by #416 (v0.4.2 is not one) and the CHANGELOG entry
says what changes for those AppImage installs.
Refs #320
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups of PR #416. The check that release.yml runs before
deleting the published manifest moves from an inline jq program to
scripts/check-latest-json.jq, which release.yml applies with
`jq -e --arg version ... -f`. scripts/build-latest-json.test.mjs now runs
that same filter on every pull request: on the script's output, on a
wrong version, on nine broken manifests and on the shape published up to
v0.17.1. It also compares the key lists of the script, of the filter and
of verify-release.sh, so a list left behind shows before the tag.
Two new cases pin the version argument: 0.18.0 must come out as 0.18.0
(the other fixtures pass 0.17.1, the version the bundle names carry, so a
script that ignored its argument passed them all), and an empty version
must fail. Both mutations survived the review's pass.
No change to what the manifest holds: still four keys.
Refs #320
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
latest.json only had linux-x86_64, built from the .deb. An .rpm install
looks up linux-x86_64-rpm first, then fell back on linux-x86_64,
downloaded the .deb and refused it (InvalidUpdaterFormat).
- scripts/build-latest-json.sh writes linux-x86_64-deb,
linux-x86_64-rpm, linux-x86_64 (the .deb, as a fallback) and
windows-x86_64 from the release assets. It fails unless it finds
exactly one .deb, one .rpm and one -setup.exe, each with a non-empty
.sig. The JSON comes from jq alone, notes read with --rawfile, file
names encoded with @uri like the upload step.
- scripts/build-latest-json.test.mjs (vitest) runs it under
bash -e -o pipefail on a temporary assets dir; check-frontend
installs jq.
- release.yml calls the script, writes the notes output under a random
delimiter, checks the manifest with jq before the registry DELETE,
and names the .rpm instead of an AppImage in the release notes.
- verify-release.sh requires the four keys, the extension of each URL
and the minisign file: of each signature, and exits 1 on any failure.
- Docs: release skill, qa-update-cycle section 3 (rpm procedure, keys,
reason and removal condition of the fallback), architecture, README,
CHANGELOG, STATE.
Resolves#320
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The /actions/tasks endpoint ignores `limit` and returns the whole list,
newest first, so `?limit=5` promised five rows it does not give. The
skill now selects the release.yml run of the tag directly (python3, as
verify-release.sh does), checked on v0.17.1: run 508, `v20.20.2`.
Also clarify check-frontend.yml's concurrency comment: same setup as
check-rust.yml, not the same group.
Refs #376
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node 20 reached end of life on 2026-04-30. check-frontend.yml, audit.yml
and release.yml now install Node.js 24 through NodeSource's setup_24.x,
as check-rust.yml has done since #375. Each step still prints
`node --version` in the job log.
The concurrency comment of check-rust.yml claimed the key cancels
obsolete runs on force-push. la-compagnie-maximus docs/forgejo-ops.md
measured the opposite on Forgejo 8.0.3: no pull_request run was
cancelled by a later one, and push runs are cancelled without the key.
The comment now states that measurement; the keys stay (harmless,
meaningful where honoured), and check-frontend.yml and audit.yml point
to it.
release.yml names the trust it places in NodeSource: the script runs as
root in the job container, whose Node.js then runs `npx tauri build`
with TAURI_SIGNING_PRIVATE_KEY. Kept on purpose, also documented in
docs/architecture.md, whose release.yml section described two jobs
(windows-latest, ubuntu-22.04) where there is one containerised job
cross-compiling Windows with cargo-xwin.
release.yml only runs on a tag, so Node 24 there is proven at the next
release: the /release skill now reads `node --version` from the release
run's log (step 9), and STATE.md lists it under the checks due at the
next release.
No CHANGELOG entry: CI only.
Resolves#376
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0021 records the catch-up rules and amends ADR 0003 and 0005, whose
claims about secondary profiles were false. architecture.md, CLAUDE.md
and the migration rule describe the two migration paths; the guide tells
where the pre-update copy lives and how to remove a database a deletion
on Linux left behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- issue-license (new): issue licenses by hand through maximus-api's admin
endpoint (testers, gifts, refund compensation, admin account); the admin
key and the issued keys never enter the transcript. Third-party
beneficiaries are named by their Logto id, never by email: the
repository is public.
- release step 0: when a release tightens edition gating, tester keys must
be entered before the tag, since an install without a key receives that
version and then nothing more.
- release step 9: scripts/verify-release.sh checks the 7 assets by suffix
and the content of latest.json, instead of a script rewritten each time.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`downloadAndInstall` is one call that downloads AND installs
(updater.rs:723-729), and the `Finished` event is a no-op in
useUpdater.ts:119-121. `READY_TO_INSTALL` is therefore dispatched only
after `dpkg -i` returns, so the pkexec prompt opens while the card still
reads "Téléchargement en cours…".
The checklist told the tester to tick `readyToInstall` before the prompt.
That ordering neutralized the polkit precondition the page exists to
enforce: with no agent the app hangs in `downloading`, not in
`readyToInstall`, so a tester following the checklist files "the download
stalls" instead of "no polkit agent" — the exact false trace this page is
written to prevent.
Also states plainly that at `readyToInstall` the .deb is already on disk
and the button only calls `relaunch()`; the label misleads on this target.
Three smaller corrections from the same review:
- The update triggers are four manual sites, not two: UpdateCard idle /
upToDate refresh / error retry, plus ErrorPage.tsx:82 — which only
detects and offers no download path. The load-bearing claim, "no
automatic check in the app", was already right.
- "Si ça casse" now spells out that DELETE and PUT hit different API
prefixes (/api/v1/packages/ vs /api/packages/, release.yml:217-219,
which carries a comment about exactly this). Replaying both against one
URL 404s at the worst possible moment.
- The SKILL.md changelog entry moves back into date order.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>