From 8b9d20d3c02e02a098d051a45191de9ec1d3031a Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:31:28 -0400 Subject: [PATCH 1/8] Stop publishing the Windows executable --- .github/workflows/build-loremaster.yml | 24 ++++-------------------- tools/release_quality_gate.py | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-loremaster.yml b/.github/workflows/build-loremaster.yml index d7bc644..6a4a89e 100644 --- a/.github/workflows/build-loremaster.yml +++ b/.github/workflows/build-loremaster.yml @@ -481,12 +481,6 @@ jobs: name: SpinUI-UI path: package/ui-component - - name: Download verified Loremaster component - uses: actions/download-artifact@v6 - with: - name: Loremaster-Windows - path: package/loremaster-component - - name: Assemble staged manual release shell: pwsh run: | @@ -495,9 +489,6 @@ jobs: Copy-Item -Force package/ui-component/SpinUI-UI.zip package/SpinUI-UI.zip Copy-Item -Force package/ui-component/SpinUI-Update.json package/SpinUI-Update.json Expand-Archive -LiteralPath package/SpinUI-UI.zip -DestinationPath $manualPackage -Force - New-Item -ItemType Directory -Force -Path dist-electron-release | Out-Null - Copy-Item -Force package/loremaster-component/Loremaster.exe dist-electron-release/Loremaster.exe - Copy-Item -Force dist-electron-release/Loremaster.exe $manualPackage - name: Verify staged release package run: > @@ -518,8 +509,7 @@ jobs: $lines = foreach ($file in @( 'package/SpinUI-Manual.zip', 'package/SpinUI-UI.zip', - 'package/SpinUI-Update.json', - 'dist-electron-release/Loremaster.exe')) { + 'package/SpinUI-Update.json')) { $hash = (Get-FileHash -Algorithm SHA256 $file).Hash.ToLower() "$hash $(Split-Path -Leaf $file)" } @@ -539,7 +529,6 @@ jobs: with: name: SpinUI-Windows-tools path: | - dist-electron-release/Loremaster.exe package/SpinUI-UI.zip package/SpinUI-Update.json package/SHA256SUMS.txt @@ -569,8 +558,7 @@ jobs: 'package/SpinUI-Manual.zip', 'package/SpinUI-UI.zip', 'package/SpinUI-Update.json', - 'package/SHA256SUMS.txt', - 'dist-electron-release/Loremaster.exe' + 'package/SHA256SUMS.txt' ) # Built as a joined array rather than a here-string, whose closing # marker has to sit at column zero and so cannot be indented inside @@ -597,11 +585,9 @@ jobs: "", "**Linux** -- download ``$appImage``, ``chmod +x`` it, and run it. There is no self-update on Linux, so new builds always come from this page.", "", - "**Windows** -- ``Loremaster.exe`` is the portable build. It is unsigned, so antivirus machine-learning heuristics sometimes flag it; see the README's Troubleshooting section.", - "", "**Skins** -- extract ``SpinUI-Manual.zip`` for the classic ``spinui_reloaded`` skin, the optional ``spinui_glass`` skin, layout profiles and the manual guide.", "", - "Check what you downloaded against ``Loremaster-Linux-SHA256SUMS.txt`` (Linux) or ``SHA256SUMS.txt`` (Windows and skins).", + "Check what you downloaded against ``Loremaster-Linux-SHA256SUMS.txt`` (Linux) or ``SHA256SUMS.txt`` (skins).", "", "---" ) @@ -656,13 +642,11 @@ jobs: package/SpinUI-UI.zip package/SpinUI-Update.json package/SHA256SUMS.txt - dist-electron-release/Loremaster.exe --clobber package-linux-release: name: Assemble complete Linux release - # Linux artifacts are attached to the release standalone (mirroring how - # Loremaster.exe is published as its own release asset) rather than + # Linux artifacts are attached to the release standalone rather than # folded into SpinUI-Manual.zip, which is the Windows-specific manual # install bundle referenced by that package's own release notes text. # This job never touches package-windows-release's steps or outputs. diff --git a/tools/release_quality_gate.py b/tools/release_quality_gate.py index dab96ba..19fe8fa 100644 --- a/tools/release_quality_gate.py +++ b/tools/release_quality_gate.py @@ -224,7 +224,6 @@ "UI_Spin_qeynos_LO1.ini", "README.md", "INSTALL.md", - "Loremaster.exe", } @@ -350,8 +349,6 @@ def check_loremaster_release_pipeline() -> None: "actions/download-artifact@v6", "electron-builder --win portable --x64 --publish never", "-c.extraMetadata.version=$version", - "dist-electron-release/Loremaster.exe", - "Copy-Item -Force dist-electron-release/Loremaster.exe $manualPackage", ) missing = [value for value in required if value not in workflow] if missing: @@ -361,13 +358,26 @@ def check_loremaster_release_pipeline() -> None: "dist/Loremaster.exe", "--specpath build/spec loremaster/loremaster.py", "LOREMASTER-NEXT-SHA256.txt", + # The Windows executable is built and tested in CI but never published: + # it is this repository's code compiled for a platform the fork does + # not test, and nobody should be installing it from here. Retired + # rather than deleted so an upstream sync cannot quietly restore it. + "Copy-Item -Force dist-electron-release/Loremaster.exe $manualPackage", ) present = [value for value in retired if value in workflow] + # build-loremaster legitimately stages its own freshly built exe with + # "Copy-Item -Force dist-electron-release/Loremaster.exe $component" so + # it can hash it and upload the Loremaster-Windows artifact; that one + # sanctioned reference is exempt. Any other appearance of the same path + # means the executable has crept back into a release-publishing step. + if re.search(r"(? None: package_root / "UI_Spin_qeynos_LO1.ini", f"{kind}/UI_Spin_qeynos_LO1.ini", ) - _check_windows_executable(package_root / "Loremaster.exe") if kind == "installer": _check_windows_executable(package_root / "SpinUIInstaller.exe") print( From 173cfa5e0145dd7fcd5edd22b4074fb0b4466ad3 Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:35:59 -0400 Subject: [PATCH 2/8] Fix hole in retired-executable guard: exempt the sanctioned copy by exact text, not a broad -Force lookbehind --- tools/release_quality_gate.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/release_quality_gate.py b/tools/release_quality_gate.py index 19fe8fa..1ef2cce 100644 --- a/tools/release_quality_gate.py +++ b/tools/release_quality_gate.py @@ -365,12 +365,15 @@ def check_loremaster_release_pipeline() -> None: "Copy-Item -Force dist-electron-release/Loremaster.exe $manualPackage", ) present = [value for value in retired if value in workflow] - # build-loremaster legitimately stages its own freshly built exe with - # "Copy-Item -Force dist-electron-release/Loremaster.exe $component" so - # it can hash it and upload the Loremaster-Windows artifact; that one - # sanctioned reference is exempt. Any other appearance of the same path - # means the executable has crept back into a release-publishing step. - if re.search(r"(? Date: Fri, 14 Aug 2026 21:44:19 -0400 Subject: [PATCH 3/8] Widen retired-executable guard from one path shape to the bare filename Gating on dist-electron-release/Loremaster.exe only caught that path shape; a downloaded Loremaster-Windows artifact staged elsewhere and referenced directly (e.g. package/loremaster-component/Loremaster.exe) slipped past untouched. Check the bare Loremaster.exe filename instead, exempting by exact text only the four build-and-verify lines inside the build-loremaster job. --- tools/release_quality_gate.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/tools/release_quality_gate.py b/tools/release_quality_gate.py index 1ef2cce..728624d 100644 --- a/tools/release_quality_gate.py +++ b/tools/release_quality_gate.py @@ -365,16 +365,32 @@ def check_loremaster_release_pipeline() -> None: "Copy-Item -Force dist-electron-release/Loremaster.exe $manualPackage", ) present = [value for value in retired if value in workflow] - # build-loremaster legitimately stages its own freshly built executable so - # it can hash it and upload the Loremaster-Windows artifact. That one line - # is exempt by exact text; any other appearance of the path means the - # executable has crept back into a release-publishing step. - SANCTIONED_EXE_STAGING = ( - "Copy-Item -Force dist-electron-release/Loremaster.exe $component" + # Gating on one path shape (dist-electron-release/Loremaster.exe) only + # catches that shape: a downloaded Loremaster-Windows artifact staged at + # package/loremaster-component/Loremaster.exe and referenced straight + # from a publish step would slip past it untouched. So this checks the + # bare filename instead, and exempts the handful of lines that + # legitimately build and verify the Windows executable inside the + # build-loremaster job -- build it, smoke-test it, stage it for the + # Loremaster-Windows workflow artifact, and checksum that artifact. + # Everything else that mentions Loremaster.exe is a publishing path. + # Adding a line here should be a deliberate decision that a new + # reference is genuinely build-and-verify, never a reflex to silence + # this check. + SANCTIONED_EXE_LINES = ( + " $lore = Start-Process -FilePath " + "'dist-electron-release/win-unpacked/Loremaster.exe' " + "-PassThru -Wait -WindowStyle Hidden", + " Copy-Item -Force dist-electron-release/Loremaster.exe $component", + " $file = Join-Path $component 'Loremaster.exe'", + ' Set-Content -Path $checksum -Value "$hash Loremaster.exe`n" ' + "-NoNewline", ) - residual = workflow.replace(SANCTIONED_EXE_STAGING, "") - if "dist-electron-release/Loremaster.exe" in residual: - present.append("dist-electron-release/Loremaster.exe") + residual = workflow + for sanctioned in SANCTIONED_EXE_LINES: + residual = residual.replace(sanctioned, "") + if "Loremaster.exe" in residual: + present.append("Loremaster.exe") if present: fail("release workflow publishes a retired artifact: " + ", ".join(present)) print( From c263dc3b1c912c013688e2f0b0b0d52870689173 Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:00:16 -0400 Subject: [PATCH 4/8] Stop telling people to download an executable this repo no longer ships --- README.md | 17 ++++++----------- docs/RELEASING.md | 7 ++++--- installer/INSTALL-MANUAL.md | 32 ++++++++++++++++---------------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 34f543a..787c2da 100644 --- a/README.md +++ b/README.md @@ -374,9 +374,9 @@ EverQuest draws a countdown and a beneficial/detrimental plate on the same buff 1. Download and extract **`SpinUI-Manual.zip`** from the [latest release](https://github.com/itsspin/spinips/releases/latest). 2. Fully close EverQuest, then copy `spinui_reloaded`, `spinui_glass`, or both included folders to `\uifiles\`. 3. Keep your existing character UI INI for a skin-only update. If you want a complete layout, select the matching resolution and Combat Focus, Social Focus, or Hybrid profile and back up the existing character UI file before replacing it. -4. Run the included **`Loremaster.exe`**, select **`/loadskin spinui_glass 1`** or **`/loadskin spinui_reloaded 1`**, and type **`/log on`** once in game. +4. Select **`/loadskin spinui_glass 1`** or **`/loadskin spinui_reloaded 1`** and type **`/log on`** once in game. On Linux, run Loremaster from the AppImage in the same release; on Windows, this repository builds and tests `Loremaster.exe` in CI but does not publish it, so build it from source if you want it there. -Releases intentionally ship the manual package and standalone Loremaster only; the Windows installer is not built or published as a release option. +Releases intentionally ship the manual package and the Linux AppImage and tar.gz. The Windows installer is not built or published as a release option, and the Windows executable, while built and tested in CI, is not published either. Packaged releases require no Python installation. Running Loremaster from source requires Python 3.10+; the application otherwise uses the standard library, with Lore Lens calling Windows-provided OCR integration. @@ -385,7 +385,7 @@ Packaged releases require no Python installation. Running Loremaster from source
Show the manual installation guide -Download **`SpinUI-Manual.zip`** from the same release. It contains both UI skins, Loremaster, layouts, and a standalone [manual guide](installer/INSTALL-MANUAL.md). +Download **`SpinUI-Manual.zip`** from the same release. It contains both UI skins, layouts, and a standalone [manual guide](installer/INSTALL-MANUAL.md). 1. If the skin folder you are updating already exists, rename or move it out of the way; do not merge a new release into a retired file tree. 2. Copy `spinui_glass`, `spinui_reloaded`, or both into `\uifiles\` so each installed folder contains its own `EQUI.xml`. @@ -394,16 +394,12 @@ Download **`SpinUI-Manual.zip`** from the same release. It contains both UI skin 5. A manual profile replaces that entire character UI INI, including its window and chat preferences. Apply one only after making the backup in the previous step. 6. Name the preset `UI___.ini`, preserving the character's existing `LO1`, `LO2`, `LO3`, or other suffix. Example: `UI_Spin_qeynos_LO1.ini`. 7. Copy that optional character UI file beside `eqgame.exe`. Do **not** replace the separate `__.ini` file or `eqclient.ini`. -8. Launch EverQuest and use `/loadskin spinui_glass 1` for Midnight Frost or `/loadskin spinui_reloaded 1` for Vellum & Ember. Run the packaged `Loremaster.exe` if desired and type `/log on` in game. +8. Launch EverQuest and use `/loadskin spinui_glass 1` for Midnight Frost or `/loadskin spinui_reloaded 1` for Vellum & Ember. On Linux, run Loremaster from the AppImage in the release and type `/log on` in game; on Windows, build `Loremaster.exe` from source if you want it, since this repository doesn't publish it. **Rollback:** restore your character UI backup and select `/loadskin default_modern 1`.
-### Windows security note - -SpinUI's unsigned `Loremaster.exe` may trigger SmartScreen or heuristic antivirus warnings. Release executables are built publicly by GitHub Actions from this repository. Verify downloads against the release's **`SHA256SUMS.txt`** with `Get-FileHash -Algorithm SHA256 `. - ## Trust by design - **The UI is normal EQ skin content:** SIDL XML, TGA textures, and layout INIs. @@ -420,7 +416,7 @@ This architecture supports a transparent non-injecting workflow. As with any com ### Running and controlling the overlay -1. Take `Loremaster.exe` from `SpinUI-Manual.zip`, download the standalone release executable, or run it from source. +1. On Linux, run the AppImage from the release. On Windows, this repository builds and tests `Loremaster.exe` in CI but does not publish it, so build it from source. 2. Type `/log on` in game. Loremaster follows the newest standard EQ log it can find; **Settings → Change EverQuest Folder** or **CHANGE / LOCATE LOG** can point it to an EverQuest root or `Logs` directory. 3. Click the **Rune Seed** to unfold the full ledger; use **SEED** in the masthead to collapse it again. The transition fades the current surface, performs one atomic geometry/layout swap, then reveals the destination—avoiding a frame-by-frame child-widget reflow. Reduced motion switches instantly. Full and compact positions are remembered separately. 4. DPS is the only default Rune Seed metric. Pin additional ledger sections with ✦ to build an optional four-item carousel, then use the mouse wheel over the seed to rotate it. @@ -489,7 +485,7 @@ pnpm test:skin-updates pnpm build ``` -Every UI release builds and publishes the portable Electron `Loremaster.exe` with its hidden parser engine. No installer or parallel legacy executable is produced. See the [live milestone and validation gates](docs/LOREMASTER_MILESTONE_2.md). +Every UI release builds and tests the portable Electron `Loremaster.exe` with its hidden parser engine in CI, but this fork does not publish it; the Linux AppImage and tar.gz are what ship. No installer or parallel legacy executable is produced. See the [live milestone and validation gates](docs/LOREMASTER_MILESTONE_2.md). Loremaster's **Settings → SpinUI Update Center** can check the official release, download and verify the portable app, then replace and relaunch it with automatic @@ -536,7 +532,6 @@ python3 tools/release_quality_gate.py | Symptom | Fix | |---|---| -| SmartScreen or antivirus warns about an executable | Verify it against release `SHA256SUMS.txt` and the public Actions build. Use the Manual package if preferred. | | Skin does not load | Confirm `uifiles\spinui_glass\EQUI.xml` or `uifiles\spinui_reloaded\EQUI.xml`, then use the matching `/loadskin 1` command. | | Layout did not apply | Close EverQuest completely, restore/reapply the intended character UI file, and relaunch. | | Layout does not fit | Restore your character UI backup, then select the exact or nearest validated screen profile from the manual package. | diff --git a/docs/RELEASING.md b/docs/RELEASING.md index f4846a6..8124541 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -67,9 +67,10 @@ candidate was cut from and tested at. ## What a Linux install actually updates The Linux AppImage has **no self-update**. The app updater replaces a portable -Windows `Loremaster.exe` and does nothing on Linux, so a new AppImage is always -a manual download. The SpinUI **skin** updater does run on Linux, and it -follows full releases only, exactly like the app updater. +Windows `Loremaster.exe` -- which CI still builds and tests on every +qualifying run but no longer publishes -- and does nothing on Linux, so a new +AppImage is always a manual download. The SpinUI **skin** updater does run on +Linux, and it follows full releases only, exactly like the app updater. That is why testing a candidate on Linux is a manual install by design. diff --git a/installer/INSTALL-MANUAL.md b/installer/INSTALL-MANUAL.md index 54f50b6..a3c45a6 100644 --- a/installer/INSTALL-MANUAL.md +++ b/installer/INSTALL-MANUAL.md @@ -2,10 +2,10 @@ This package does not require the installer. It contains both complete skins: the classic **Vellum & Ember** `spinui_reloaded` skin and the optional -**Midnight Frost** `spinui_glass` skin, plus Spin's Loremaster, the optional -character-layout profiles, and alternate chat presets. Seven validated screen -profiles cover 1920×1080, 2048×1080, 2560×1080, 2560×1440, 3440×1440, -3840×1600, and 3840×2160. +**Midnight Frost** `spinui_glass` skin, plus the optional character-layout +profiles and alternate chat presets. Spin's Loremaster ships separately -- +see step 4. Seven validated screen profiles cover 1920×1080, 2048×1080, +2560×1080, 2560×1440, 3440×1440, 3840×1600, and 3840×2160. > **Safest layout option:** install either skin and keep your current > character UI INI. Combat Focus, Social Focus, and Hybrid are @@ -13,13 +13,6 @@ profiles cover 1920×1080, 2048×1080, 2560×1080, 2560×1440, 3440×1440, > including its window and chat preferences. Make a byte-exact backup before > applying one so the original arrangement is easy to restore. -> **Antivirus note:** the unsigned `Loremaster.exe` can trip machine-learning -> heuristics (commonly `Wacatac!ml` or "suspicious PE"). Every release -> publishes a `SHA256SUMS.txt`; verify a download with -> `Get-FileHash -Algorithm SHA256 ` in PowerShell before restoring it -> from quarantine. The executable is built in public by GitHub Actions from -> the repository source. - ## 1. Close EverQuest completely EverQuest rewrites character UI files when it exits. Do not copy or replace an @@ -109,7 +102,11 @@ matching filename as permission to overwrite it without a backup. ## 4. Run Spin's Loremaster -Move `Loremaster.exe` anywhere you prefer, then run it. In EverQuest, type: +This package does not include `Loremaster.exe`. On Linux, download the +AppImage from the release and run it instead -- see section 5 below. On +Windows, this repository builds and tests `Loremaster.exe` in every CI run +but does not publish it, so build it from source if you want it there. Once +it's running, in EverQuest type: ```text /log on @@ -142,8 +139,10 @@ confirm it. The shortcut, Hover Scan, wiki network access, high-contrast palette, reduced motion, and text scale are configurable through **SETTINGS**. Lore Lens never injects into or reads memory from `eqgame.exe`. -To start Loremaster with Windows without showing it before the game launches, -create a shortcut in `shell:startup` whose target is: +This applies only if you've built `Loremaster.exe` yourself, since this +repository doesn't publish it. To start Loremaster with Windows without +showing it before the game launches, create a shortcut in `shell:startup` +whose target is: ```text "C:\path\to\Loremaster.exe" --wait-for-eq @@ -190,8 +189,9 @@ in the payload (for example, a plain source checkout with no release artifacts), `--install` still installs the skin and reports the layout result normally; it just skips Loremaster with an explicit message telling you to grab the Linux build from the releases page, rather than silently -doing nothing or falling back to the Windows exe under Wine. `Loremaster.exe` -is only ever installed on Windows. When the native build is found, +doing nothing or falling back to a Windows exe under Wine -- there is no exe +to fall back to, since this repository builds and tests `Loremaster.exe` in +CI but never publishes it. When the native build is found, `--desktop-shortcut` and `--startup-shortcut` write a freedesktop `.desktop` entry (to your Desktop folder and `~/.config/autostart`, respectively) whose `Exec=` points straight at the installed AppImage, instead of a Windows `.lnk`. From f2f6b44b8bc07914467f42a19fbb0f27845ec74e Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:03:00 -0400 Subject: [PATCH 5/8] Fix hole in Windows executable removal docs: LOREMASTER_MILESTONE_2.md still claimed publication --- docs/LOREMASTER_MILESTONE_2.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/LOREMASTER_MILESTONE_2.md b/docs/LOREMASTER_MILESTONE_2.md index 5ed7596..cce1fd6 100644 --- a/docs/LOREMASTER_MILESTONE_2.md +++ b/docs/LOREMASTER_MILESTONE_2.md @@ -36,8 +36,9 @@ versioned UTF-8 JSONL and is the portable desktop shipped with UI releases. `Loremaster.exe` is the CI-built Electron desktop and contains its private headless parser engine. The workflow smoke-tests the protocol, strict TypeScript build, production renderer, bundled engine, and portable executable -before the same binary is copied into the manual ZIP and attached to the UI -release. The legacy Python GUI remains source/reference code and is not +on every qualifying run, but the binary is not published: it is not copied +into the manual ZIP and not attached to the UI release. It can still be built +from source. The legacy Python GUI remains source/reference code and is not published as a competing executable. The release also publishes `SpinUI-UI.zip`, `SpinUI-Update.json`, and From 4ee7675ce972e1df8cd01f34b922f7236d10b3c7 Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:04:41 -0400 Subject: [PATCH 6/8] Say the Windows executable is no longer published --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e999676..bd72c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,11 @@ that came with it. it and the live release at the same time instead of treating them as one app. It shares the live settings on purpose, so bugs show up against real data, and it copies those settings aside once per candidate before it can touch them. +- **The Windows executable is no longer published here** — releases carry the + Linux build and the skins. The executable is still built and tested on every + run, so a change that breaks it is still caught, but it is not offered for + download from this fork. Anyone already running the Windows build will see + its updater fail rather than find a new one. ### Removed From 4dca2f83a404a087e0e95c2651936d0f06e431b4 Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:17:08 -0400 Subject: [PATCH 7/8] Stop shipping internal planning docs in the UI/manual packages The UI-package assembly step copied the entire docs/ tree, including docs/superpowers/specs and docs/superpowers/plans -- this project's own internal design specs and implementation plans -- into SpinUI-UI.zip and, by extension, SpinUI-Manual.zip. Both are published and delivered to end users. Strip docs/superpowers from the staged package, and add a gate assertion (with a clear failure message) so a staged release package containing docs/superpowers fails release_quality_gate.py's --packages-only check. Also, smaller fixes surfaced by the same review: - LOREMASTER_MILESTONE_2.md still claimed Loremaster "is the portable desktop shipped with UI releases" three lines from the top -- ships inside SpinUI-Manual.zip itself, so a user reads a false publication claim on the first screen. Corrected to say the Linux AppImage ships and the Windows portable build is CI-tested but not published, and scoped the Settings Update Center description off "can update Loremaster" since the Windows updater has no published asset to find. - The package-linux-release comment still called SpinUI-Manual.zip "the Windows-specific manual install bundle"; it now holds skins, layouts, and docs, with its own INSTALL.md pointing Linux users at the AppImage. - package-windows-release's needs: build-loremaster is now the only thing making a broken Windows build block a release; commented it as a gating, not component, dependency so it doesn't look like dead weight. - The retired-artifact gate's bare-filename check now names the offending line(s) so resolving an upstream merge conflict is faster. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build-loremaster.yml | 14 +++++++++-- docs/LOREMASTER_MILESTONE_2.md | 12 ++++++--- tools/release_quality_gate.py | 35 +++++++++++++++++++++++--- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-loremaster.yml b/.github/workflows/build-loremaster.yml index 6a4a89e..d87a0de 100644 --- a/.github/workflows/build-loremaster.yml +++ b/.github/workflows/build-loremaster.yml @@ -177,6 +177,10 @@ jobs: } Copy-Item -Recurse -Force layouts/profiles (Join-Path $packageLayouts 'profiles') Copy-Item -Recurse -Force docs (Join-Path $uiPackage 'docs') + # docs/superpowers holds this project's own internal design specs + # and implementation plans, not user-facing documentation -- strip + # it before the tree is compressed and shipped to end users. + Remove-Item -Recurse -Force (Join-Path $uiPackage 'docs/superpowers') Copy-Item -Force UI_Spin_qeynos_LO1.ini $uiPackage Copy-Item -Force README.md $uiPackage Copy-Item -Force installer/INSTALL-MANUAL.md (Join-Path $uiPackage 'INSTALL.md') @@ -457,6 +461,11 @@ jobs: package-windows-release: name: Assemble complete Windows release + # needs: build-loremaster is a gating dependency, not a component one: + # this job downloads nothing from build-loremaster (the portable + # Windows build is never published), but keeping it here means a broken + # Windows build still blocks a release -- the one invariant this whole + # change exists to preserve. needs: [detect-changes, build-ui, build-loremaster] # Named events rather than "not push": these jobs stage and publish a # release, so they belong to the two events that produce one. Pull @@ -647,8 +656,9 @@ jobs: package-linux-release: name: Assemble complete Linux release # Linux artifacts are attached to the release standalone rather than - # folded into SpinUI-Manual.zip, which is the Windows-specific manual - # install bundle referenced by that package's own release notes text. + # folded into SpinUI-Manual.zip, which is the manual install bundle for + # skins, layouts and docs; its own INSTALL.md directs Linux users to the + # AppImage instead. # This job never touches package-windows-release's steps or outputs. # # needs: package-windows-release is a sequencing dependency, not a diff --git a/docs/LOREMASTER_MILESTONE_2.md b/docs/LOREMASTER_MILESTONE_2.md index cce1fd6..7519c0f 100644 --- a/docs/LOREMASTER_MILESTONE_2.md +++ b/docs/LOREMASTER_MILESTONE_2.md @@ -4,7 +4,9 @@ Loremaster is now a live Electron + React + TypeScript application, not a fixture player. Electron supervises a private headless parser engine over -versioned UTF-8 JSONL and is the portable desktop shipped with UI releases. +versioned UTF-8 JSONL and is the portable desktop this project builds. The +Linux AppImage is what ships with UI releases; the Windows portable build is +CI-tested but not published. ## Live path delivered @@ -25,9 +27,11 @@ versioned UTF-8 JSONL and is the portable desktop shipped with UI releases. group participation can count. Difficulty and Solo/Group mode come from the logged instance-entry line, with an explicit fallback only for unknown tiers. - A Settings Update Center that displays the running version, checks the - official release, verifies portable and skin downloads, and can update - Loremaster, Reloaded, and Glass independently. App relaunch has automatic - rollback; skin replacement is isolated and blocked while EverQuest runs. + official release, and verifies and updates the Reloaded and Glass skins + independently, with automatic relaunch rollback and skin replacement + isolated and blocked while EverQuest runs. The Windows app updater still + looks for a `Loremaster.exe` release asset, but this fork publishes none, + so it has nothing to find. - A single portable Windows test build containing the React application and its hidden parser engine. No installer target is produced. diff --git a/tools/release_quality_gate.py b/tools/release_quality_gate.py index 728624d..4542979 100644 --- a/tools/release_quality_gate.py +++ b/tools/release_quality_gate.py @@ -390,7 +390,15 @@ def check_loremaster_release_pipeline() -> None: for sanctioned in SANCTIONED_EXE_LINES: residual = residual.replace(sanctioned, "") if "Loremaster.exe" in residual: - present.append("Loremaster.exe") + # Name the offending line(s) so someone resolving an upstream merge + # knows what to look at, rather than just that the bare filename + # matched somewhere in the workflow. + offending_lines = [ + line.strip() for line in residual.splitlines() if "Loremaster.exe" in line + ] + present.append( + "Loremaster.exe (unsanctioned line(s): " + " | ".join(offending_lines) + ")" + ) if present: fail("release workflow publishes a retired artifact: " + ", ".join(present)) print( @@ -893,8 +901,16 @@ def _sha256(path: Path) -> str: return digest.hexdigest() -def _compare_packaged_tree(source: Path, packaged: Path, label: str) -> int: +def _compare_packaged_tree( + source: Path, packaged: Path, label: str, exclude_prefixes: tuple[str, ...] = () +) -> int: source_files = _tree_files(source) + if exclude_prefixes: + source_files = { + relative: path + for relative, path in source_files.items() + if not relative.startswith(exclude_prefixes) + } package_files = _tree_files(packaged) missing = sorted(set(source_files) - set(package_files)) extra = sorted(set(package_files) - set(source_files)) @@ -981,8 +997,21 @@ def check_staged_package(kind: str, package_root: Path) -> None: package_layouts / "profiles", f"{kind}/layouts/profiles", ) + # docs/superpowers holds this project's own internal design specs and + # implementation plans, not user-facing documentation, so it must never + # be staged into a release package. Check this explicitly -- and before + # the tree comparison below -- so a regression fails with a message that + # names the real problem instead of a generic "unexpected files" diff. + staged_superpowers = package_root / "docs" / "superpowers" + if staged_superpowers.exists(): + fail( + f"{kind} package ships internal planning docs at " + f"{staged_superpowers}: docs/superpowers holds this project's " + "own specs and plans and must not ship to end users" + ) docs_files = _compare_packaged_tree( - REPO / "docs", package_root / "docs", f"{kind}/docs" + REPO / "docs", package_root / "docs", f"{kind}/docs", + exclude_prefixes=("superpowers/",), ) _check_same_file( REPO / "README.md", package_root / "README.md", f"{kind}/README.md") From 1673dc99c420a80e2da7cdeb92a2007357ed0b75 Mon Sep 17 00:00:00 2001 From: JDS300 <70587798+JDS300@users.noreply.github.com> Date: Fri, 14 Aug 2026 22:24:15 -0400 Subject: [PATCH 8/8] Make docs/superpowers strip conditional on its existence. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The planning documents in docs/superpowers are transient — they will plausibly be deleted from the repo once their work is complete. When that happens, the cleanup step must not fail or print errors under any ErrorActionPreference setting. Use Test-Path to gate the removal. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build-loremaster.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-loremaster.yml b/.github/workflows/build-loremaster.yml index d87a0de..06bd60f 100644 --- a/.github/workflows/build-loremaster.yml +++ b/.github/workflows/build-loremaster.yml @@ -180,7 +180,13 @@ jobs: # docs/superpowers holds this project's own internal design specs # and implementation plans, not user-facing documentation -- strip # it before the tree is compressed and shipped to end users. - Remove-Item -Recurse -Force (Join-Path $uiPackage 'docs/superpowers') + # Conditional because these are transient planning documents: once + # they are deleted from the repo, their absence must not fail or + # noisily error the build. + $stagedPlans = Join-Path $uiPackage 'docs/superpowers' + if (Test-Path $stagedPlans) { + Remove-Item -Recurse -Force $stagedPlans + } Copy-Item -Force UI_Spin_qeynos_LO1.ini $uiPackage Copy-Item -Force README.md $uiPackage Copy-Item -Force installer/INSTALL-MANUAL.md (Join-Path $uiPackage 'INSTALL.md')