Promote 0.2.25 to stable

This commit is contained in:
Franz Rolfsvaag 2026-07-22 10:26:20 +02:00
parent 42a5e42d08
commit 162c3b1df6
6 changed files with 53 additions and 24 deletions

View File

@ -3,8 +3,8 @@
## 0.2.25
- Added verified, snapshot-backed production deployment switching between `main` and exact `experimental-*` branches, including bundled plugin code, preserved plugin data, automatic failed-apply restoration, and graceful wrapper restarts.
- Added a guarded one-time bootstrap that completes bundled-plugin synchronization after an older stable updater installs this experimental core; metadata checks alone cannot trigger deployment.
- Completed the experimental DesignMotionHQ UX foundation and representative settings, navigation, theme, command, updates, resources, and OBS overlay workflows with a five-viewport browser/accessibility matrix.
- Added a guarded compatibility bootstrap that completes bundled-plugin synchronization if stable `0.2.24` installs an experimental branch directly; metadata checks alone cannot trigger deployment.
- Completed the DesignMotionHQ UX foundation and representative settings, navigation, theme, command, updates, resources, and OBS overlay workflows with a five-viewport browser/accessibility matrix.
- Fixed overlay editor scrolling and viewport containment, interactive pop-out preview editing, and persistent resource storage-limit saves with immediate page reconciliation.
## 0.2.24

View File

@ -29,16 +29,13 @@ new branch.
## Test an experimental branch
To enter from stable `0.2.24`, open **Admin > Settings**, select
**Experimental** as the update branch, save, and use **Update from repository**.
The older updater installs experimental core `0.2.25`; on its first startup the
new guarded bootstrap confirms that an actual core update completed, snapshots
the installation, synchronizes bundled plugin code, and restarts a second time.
Merely checking metadata cannot trigger this bootstrap. Stable `0.2.24` predates
the exact-branch picker, so it selects the newest published `experimental-*`
branch. The same guarded entry path is available again after a return to stable.
While experimental code is running, use the exact branch controls below to move
between `main` and any published experimental branch.
First update production on `main` to stable `0.2.25` through the existing
**Update from repository** action. This installs the exact-branch controls on
stable, so future switches in either direction use the same verified workflow.
If a `0.2.24` host enters Experimental before taking the stable update, the
guarded compatibility bootstrap completes bundled-plugin synchronization after
the older updater installs the newest `experimental-*` branch; expect a second
restart. Merely checking metadata cannot trigger that bootstrap.
1. Open **Admin > Updates** and find **Running code branch**.
2. Confirm the page reports the expected running branch and commit.

View File

@ -2,6 +2,36 @@
"schema_version": 1,
"channel": "stable",
"releases": [
{
"version": "0.2.25",
"ref": "refs/tags/v0.2.25",
"released_at": "2026-07-22",
"installable": true,
"rollback_safe": true,
"replaces_versions": [
"1.2.0"
],
"data_policy": "preserve",
"dependency_policy": "sync_on_restart",
"migration_notes": "Adds the shared DesignMotionHQ UX foundation, repaired overlay and resource-library workflows, and verified snapshot-backed switching between main and exact experimental branches. Existing content, settings, tokens, overlays, databases, uploads, models, feedback, secrets, plugin data, and local-only plugins are preserved.",
"plugins": {
"auto-vc": "0.1.6",
"birthday": "0.1.3",
"economy-framework": "0.2.10",
"economy-games": "0.1.7",
"expression-interaction": "0.2.1",
"lumi_ai": "0.8.5",
"moderation": "0.1.5",
"okf": "0.1.2",
"quotes": "0.1.2",
"sample-plugin": "0.1.0",
"throne_wishlist": "0.1.2",
"welcome_messages": "0.1.1"
},
"tools": {
"lumi_ai_web_search": "0.1.1"
}
},
{
"version": "0.2.24",
"ref": "refs/tags/v0.2.24",

View File

@ -5,8 +5,8 @@ const { findSafeTarget } = require("../src/services/versioning");
const root = path.join(__dirname, "..");
const releaseVersion = "0.2.25";
const stableReleaseVersion = "0.2.24";
const previousStableVersion = "0.2.23";
const previousStableVersion = "0.2.24";
const priorStableVersion = "0.2.23";
const earliestCompatibleCoreVersion = "0.1.9";
const changedPlugins = {
"auto-vc": { from: "0.1.5", to: "0.1.6", knowledge: "auto-vc" },
@ -45,19 +45,21 @@ assert.equal(packageJson.version, releaseVersion);
assert.equal(packageLock.version, releaseVersion);
assert.equal(packageLock.packages?.[""]?.version, releaseVersion);
assert.equal(coreManifest.version, releaseVersion);
assert.equal(coreManifest.channel, "experimental");
assert.equal(coreManifest.channel, "stable");
assert.equal(coreManifest.compatible_from, earliestCompatibleCoreVersion);
assert.equal(coreManifest.rollback_safe, true);
assert.deepEqual(coreManifest.replaces_versions, ["1.2.0"]);
assert.equal(releaseIndex.releases[0].version, stableReleaseVersion);
assert.equal(releaseIndex.releases[0].ref, `refs/tags/v${stableReleaseVersion}`);
assert.equal(releaseIndex.releases[0].version, releaseVersion);
assert.equal(releaseIndex.releases[0].ref, `refs/tags/v${releaseVersion}`);
assert.equal(releaseIndex.releases[1].version, previousStableVersion);
assert.equal(releaseIndex.releases[1].ref, `refs/tags/v${previousStableVersion}`);
assert.equal(releaseIndex.releases[2].version, priorStableVersion);
assert.equal(releaseIndex.releases[2].ref, `refs/tags/v${priorStableVersion}`);
assert.equal(releaseIndex.releases.at(-1).version, earliestCompatibleCoreVersion);
assert.equal(hasVersionHeading(readText("CHANGELOG.md"), releaseVersion), true);
assert.match(readText("knowledge/core/lumi-core.md"), new RegExp(`^Version: ${escapeRegex(releaseVersion)}$`, "m"));
const safeCoreTarget = findSafeTarget(stableReleaseVersion, [
const safeCoreTarget = findSafeTarget(previousStableVersion, [
...(Array.isArray(coreManifest.versions) ? coreManifest.versions : []),
coreManifest
]);
@ -88,4 +90,4 @@ assert.equal(webSearch.minimum_lumi_version, "0.2.0");
assert.equal(webSearch.minimum_lumi_ai_version, "0.8.2");
assert.equal(hasVersionHeading(readText("plugins/lumi_ai_web_search/CHANGELOG.md"), webSearch.version), true);
console.log("Release metadata verification passed: experimental core 0.2.25 over stable 0.2.24, Lumi AI 0.8.5, and synchronized package metadata.");
console.log("Release metadata verification passed: stable core 0.2.25 after 0.2.24, Lumi AI 0.8.5, and synchronized package metadata.");

View File

@ -21,7 +21,7 @@ function readJson(relativePath) {
const releaseIndex = readJson("release-index.json");
const releaseVersions = releaseIndex.releases.map((release) => release.version);
assert.deepEqual(releaseVersions, ["0.2.24", "0.2.23", "0.2.22", "0.2.21", "0.2.20", "0.2.19", "0.2.18", "0.2.17", "0.2.16", "0.2.15", "0.2.14", "0.2.13", "0.2.12", "0.2.11", "0.2.10", "0.2.9", "0.2.8", "0.2.7", "0.2.6", "0.2.5", "0.2.4", "0.2.3", "0.2.2", "0.2.1", "0.2.0", "0.1.9"]);
assert.deepEqual(releaseVersions, ["0.2.25", "0.2.24", "0.2.23", "0.2.22", "0.2.21", "0.2.20", "0.2.19", "0.2.18", "0.2.17", "0.2.16", "0.2.15", "0.2.14", "0.2.13", "0.2.12", "0.2.11", "0.2.10", "0.2.9", "0.2.8", "0.2.7", "0.2.6", "0.2.5", "0.2.4", "0.2.3", "0.2.2", "0.2.1", "0.2.0", "0.1.9"]);
assert.equal(new Set(releaseVersions).size, releaseVersions.length, "release versions must be unique");
for (const release of releaseIndex.releases) {
assert.equal(normalizeRepositoryRef(release.ref), release.ref);
@ -33,9 +33,9 @@ const currentRelease = releaseIndex.releases[0];
const packageVersion = readJson("package.json").version;
const coreManifest = readJson("update-manifest.json");
assert.equal(packageVersion, coreManifest.version);
assert.equal(coreManifest.channel, "experimental");
assert.equal(coreManifest.channel, "stable");
assert.equal(packageVersion, "0.2.25");
assert.equal(currentRelease.version, "0.2.24");
assert.equal(currentRelease.version, "0.2.25");
assert.deepEqual(currentRelease.replaces_versions, ["1.2.0"]);
for (const [pluginId, version] of Object.entries(currentRelease.plugins)) {
assert.equal(readJson(`plugins/${pluginId}/plugin.json`).version, version, `${pluginId} release catalog version`);

View File

@ -1,14 +1,14 @@
{
"name": "Lumi Core",
"version": "0.2.25",
"channel": "experimental",
"channel": "stable",
"released_at": "2026-07-22",
"compatible_from": "0.1.9",
"migration_kind": "patch",
"replaces_versions": [
"1.2.0"
],
"migration_notes": "Experimental DesignMotionHQ UX checkpoint plus safe production branch deployment controls. Existing content, settings, tokens, overlays, databases, uploads, models, feedback, secrets, plugin data, and local-only plugins are preserved. Bundled plugin code follows the selected branch.",
"migration_notes": "DesignMotionHQ UX checkpoint plus safe production branch deployment controls. Existing content, settings, tokens, overlays, databases, uploads, models, feedback, secrets, plugin data, and local-only plugins are preserved. Bundled plugin code follows the selected branch.",
"rollback_safe": true,
"requirements": [
"Node.js 18 or newer"