This is the canonical checklist for Shirowwww/Achievement-Watcher-Next. Other
project documents link here instead of duplicating the release process.
git status --short --branch and preserve unrelated changes.CHANGELOG.md entry may
stay together; unrelated docs, dependency refreshes and CI fixes get separate
commits.git diff --cached, then commit.fix: sync dependency lockfilefeat: add controller UI navigationdocs: clarify notification setupchore: update runtime and dependenciesUse a new SemVer version. A published version is immutable: never replace its installer to force an update, because clients only update to a higher version.
Update the same version in all of these places:
app/package.jsonapp/package-lock.json (root package entries)watchdog/package.jsonwatchdog/package-lock.json (root package entries)CHANGELOG.md: move relevant Unreleased entries under the dated versionRELEASE_NOTES.md: title, highlights and installer filenameThe README release badge is dynamic and does not require a manual version edit.
Do not hand-edit generated app/dist/latest.yml; the build creates it.
Do this for every release, not only ones that “feel” doc-worthy - stale docs accumulate silently between releases otherwise:
README.md “What it does” list: add a phrase for any genuinely new capability this release
ships (a new source, a new tool, a reliability/data-safety change). Bug fixes belong in
CHANGELOG.md, not this list. If nothing list-worthy shipped, leave it alone.docs/comparison.md: only touch a row if this release changed the behavior it describes. Do
not re-verify the whole table on every release - that is a separate, occasional audit (see below),
not a per-release step.%APPDATA%\Achievement Watcher 3.0 split from the legacy %APPDATA%\Achievement Watcher
is the precedent), grep the whole repo for the old form - public docs, .github/ISSUE_TEMPLATE/,
and the local CLAUDE.md/AGENTS.md notes all drift independently and have shipped stale paths
before.[text](path#anchor) added or touched this release actually
resolves (a moved/renamed file or a renamed heading breaks the anchor silently). A quick script
that resolves every relative link in every tracked .md file against the filesystem is cheap
insurance - see git history around 2026-08-05 for one.Do this only when asked, or when a competing project’s fork clearly changed - not every release.
When you do it: check the competitor’s actual source (main.js/preload.js/renderer HTML,
fetched via gh api repos/<owner>/<repo>/contents/<path> -H "Accept: application/vnd.github.raw"),
not just their README. A README is a marketing summary and routinely under-describes real
features. A missing README mention is not evidence of a missing feature - grep the code before
downgrading a ✅ to a ❌.
From the repository root in PowerShell:
git diff --check
Push-Location app
npm ci
npm test
npm audit --omit=dev
Pop-Location
Push-Location watchdog
npm ci
npm test
npm audit --omit=dev
Pop-Location
The app suite includes locale completeness. If a native optional dependency is unavailable on the current machine, record the exact limitation; do not silently skip a failed check.
Before release, confirm the four package and lockfile root versions match:
node -e "for (const p of ['app/package.json','app/package-lock.json','watchdog/package.json','watchdog/package-lock.json']) console.log(p, require('./' + p).version)"
Build on Windows from app/:
Push-Location app
npm run build
Pop-Location
Expected files:
app/dist/Achievement.Watcher.Setup.<version>.exeapp/dist/Achievement.Watcher.Setup.<version>.exe.blockmapapp/dist/latest.ymlCheck that latest.yml names the exact installer and version. Verify its SHA-512
against the built installer:
$version = (Get-Content app/package.json | ConvertFrom-Json).version
$installer = "app/dist/Achievement.Watcher.Setup.$version.exe"
$expected = [Convert]::ToBase64String(
[Security.Cryptography.SHA512]::HashData([IO.File]::ReadAllBytes($installer))
)
Get-Content app/dist/latest.yml
$expected
Also smoke-test the packaged runtime and the affected feature path. For runtime inspection, temporarily use Electron as Node and always remove the variable:
$env:ELECTRON_RUN_AS_NODE = '1'
& 'app/dist/win-unpacked/Achievement Watcher.exe' -e "console.log(process.versions)"
Remove-Item Env:ELECTRON_RUN_AS_NODE
The build runs npm prune --omit=dev in watchdog/. Restore its development
dependencies afterward with npm install from watchdog/.
main to origin and wait for .github/workflows/test.yml to pass.$version = (Get-Content app/package.json | ConvertFrom-Json).version
$target = git rev-parse HEAD
gh release create "v$version" `
"app/dist/Achievement.Watcher.Setup.$version.exe" `
"app/dist/Achievement.Watcher.Setup.$version.exe.blockmap" `
"app/dist/latest.yml" `
--repo Shirowwww/Achievement-Watcher-Next `
--target $target `
--title "Achievement Watcher Next $version" `
--notes-file RELEASE_NOTES.md
latest.yml, and
that the public manifest is downloadable.node tools/site/release-data.js
git add docs/data/release.json
git commit -m "docs: publish <version> on the site"
The file is read from the site’s own origin rather than from the GitHub API, so a visitor never spends an unauthenticated rate limit to see a version number. Missing or stale, the pages fall back to their own wording; they never show a wrong version.
The product was renamed to Achievement Watcher Next in 3.9.0, but several identifiers deliberately
kept their historical value because an existing install is keyed on them. Changing any of these
breaks upgrades rather than merely renaming something, and test/core/branding.test.js pins them:
| Identifier | Value | Why it stays |
|---|---|---|
appId (AppUserModelID) |
io.github.shirowwww.achievement.watcher |
Windows matches toasts and taskbar pins against it |
executableName |
Achievement Watcher |
Fixes the .exe name, install directory and uninstaller filename; the autostart registry value stores that full path, and the Watchdog spawns the app by it |
app.setName() |
Achievement Watcher |
Names the autostart registry value and the main log file |
| Installer artifact | Achievement.Watcher.Setup.<version>.exe |
Referenced by latest.yml; published releases are immutable |
updaterCacheDirName |
achievement-watcher-updater |
Existing partially downloaded updates live there |
| Legacy data folders | Achievement Watcher 3.0, Achievement Watcher |
Import sources for the one-way migration; never renamed or deleted |
The repository was renamed Achievement-Watcher-3.0 → Achievement-Watcher-Next for 3.9.0. Clients
released before 3.9.0 shipped an app-update.yml naming the old repository, so they reach new
releases only through GitHub’s permanent rename redirect. That redirect was verified end to end
against electron-updater’s own HTTP stack - the releases.atom feed, the latest.yml channel file
and the installer asset all follow the 301 transparently.
Never create a new repository at Shirowwww/Achievement-Watcher-3.0. Doing so takes over the old
path, kills the redirect and permanently strands every client older than 3.9.0. Releases must also
stay on this repository: moving them to a different owner or repo breaks the same path.
An updater check is only meaningful from an installed lower version to the newly published higher version.
latest.yml, installer and
blockmap.npm start).Do not claim auto-update success from source mode, an unpacked build, or a same-version asset replacement.
git status --short --branch is clean and synchronized.