Skip to content

Let a release candidate install beside the release - #21

Merged
JDS300 merged 10 commits into
mainfrom
feat/rc-app-identity
Aug 15, 2026
Merged

Let a release candidate install beside the release#21
JDS300 merged 10 commits into
mainfrom
feat/rc-app-identity

Conversation

@JDS300

@JDS300 JDS300 commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Gear Lever holds one entry per app identity, and a candidate presented the same one as the live release: same productName, same desktopName. Only one could be integrated at a time.

A candidate now builds as Loremaster RC with its own desktop entry, application id, executable name and AppImage filename. These are build-time -c. overrides only — package.json is untouched, so local builds and full releases are byte-identical to before.

Shared settings, on purpose

userData comes from package.json name, not productName, so a candidate shares the live settings directory by default. That is deliberate: bugs surface against real data.

To make that safe, an RC snapshots the shared state once per candidate to spins-loremaster-rc-backups/<version>/ before anything can write, and never blocks startup if that fails. The snapshot covers the Electron settings and the engine-owned progress dataweekly_boss_kills.json and adventure_journal.sqlite3 with its WAL sidecars — which the Python engine writes into that same shared directory. Those are the genuinely irreplaceable files; review caught that the original design missed them.

Note for future edits: overriding extraMetadata.name or extraMetadata.productName would move userData and silently split the config. The spec and plan both record this constraint.

Verified against a real build

A local electron-builder --linux run with the RC flags produced Loremaster-RC-0.4.0-rc.1-x86_64.AppImage containing loremaster-rc.desktop with Name=Loremaster RC and StartupWMClass=loremaster-rc, against loremaster.desktop / Name=Loremaster in the live build. The packaged package.json inside the asar confirms name is still spins-loremaster and productName is absent — so the settings directory is genuinely shared.

Quality gate ALL PASS. Desktop suite passes (test:updates is Windows-only and fails on Linux on every commit; CI runs it only in the windows-latest job).

Spec: docs/superpowers/specs/2026-08-14-rc-identity-and-windows-exe-removal-design.md
Plan: docs/superpowers/plans/2026-08-14-rc-identity-and-windows-exe-removal.md

🤖 Generated with Claude Code

JDS300 and others added 10 commits August 14, 2026 16:57
Gear Lever holds one entry per app identity, and a candidate presents the
same one as the release: same productName, same desktopName. Only one can be
integrated at a time.

Records the build-time overrides that give a candidate its own identity, the
finding that userData comes from package.json name rather than productName
(so shared settings are the default, not something to arrange), and the
first-launch backup that makes sharing them safe.

Also records dropping the Windows executable from what a release publishes,
including the copy of it inside SpinUI-Manual.zip and the quality gate
assertions that currently require it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two pull requests: the candidate identity and its first-launch backup, then
dropping the Windows executable from what a release publishes.

Records the parts that have to move together -- the workflow literals the
quality gate asserts on cannot be edited in a separate commit from the gate,
or the tree is red in between -- and the shell quoting the artifactName
override needs, which bash would otherwise expand to nothing.

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

A partial copy left mkdirSync's target directory behind, and that same
directory doubled as the already-backed-up marker, so a crashed attempt
permanently masked the fact the real snapshot never completed. Stage
copies in a sibling <version>.partial directory and rename it into place
only once every file has copied.
…out it

The round-1 regression tests both passed against the pre-fix code, so
neither proved anything: one never triggered a copy failure at all, and
the other forced mkdirSync to throw before any directory could exist.
Add a test that makes the second of four source files a directory so
copyFileSync throws EISDIR after one file has already copied, then
assert the marker directory does not exist and a repaired retry
succeeds. Verified by temporarily reverting rc-backup.ts to its
pre-fix state and confirming this test fails there.
The RC backup only covered four Electron-owned JSON files, but the Python
engine writes weekly_boss_kills.json and adventure_journal.sqlite3 into the
same shared userData directory -- irreplaceable raid progress and kill
history that a release candidate could silently corrupt with zero
protection. Extend RC_BACKUP_FILES to also cover the journal's WAL/SHM
sidecars, since the engine runs in WAL mode and the bare .sqlite3 file can
miss the tail of an unclean shutdown.

Also close two smaller gaps found in the same review:
- Scope the staging directory by pid so two simultaneous launches (this
  runs before requestSingleInstanceLock) can no longer interleave and
  produce a snapshot that's missing a file but reported as complete.
- Use statSync().isDirectory() instead of existsSync() for the
  already-backed-up marker, so a stray file left at that path can't
  permanently suppress the backup.

Document all of this in RELEASING.md so whoever cuts a release knows a
candidate shares live data and how to recover if it goes wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JDS300
JDS300 merged commit 64a90d1 into main Aug 15, 2026
6 checks passed
@JDS300
JDS300 deleted the feat/rc-app-identity branch August 15, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant