chore(release): bump version to 2026.3.1

This commit is contained in:
Agent 2026-03-01 21:14:09 +00:00
parent 94a5d28d26
commit e7cafed424
52 changed files with 82 additions and 82 deletions

View File

@ -2,31 +2,31 @@
Docs: https://docs.openclaw.ai
## 2026.2.27
## 2026.3.1
### Changes
- Android/Nodes parity: add `system.notify`, `photos.latest`, `contacts.search`/`contacts.add`, `calendar.events`/`calendar.add`, and `motion.activity`/`motion.pedometer`, with motion sensor-aware command gating and improved activity sampling reliability. (#29398) Thanks @obviyus.
- Android/Nodes: add `camera.list`, `device.permissions`, `device.health`, and `notifications.actions` (`open`/`dismiss`/`reply`) on Android nodes, plus first-class node-tool actions for the new device/notification commands. (#28260) Thanks @obviyus.
- Discord/Thread bindings: replace fixed TTL lifecycle with inactivity (`idleHours`, default 24h) plus optional hard `maxAgeHours` lifecycle controls, and add `/session idle` + `/session max-age` commands for focused thread-bound sessions. (#27845) Thanks @osolmaz.
- Web UI/i18n: add German (`de`) locale support and auto-render language options from supported locale constants in Overview settings. (#28495) thanks @dsantoreis.
- Web UI/Cron i18n: localize cron page labels, filters, form help text, and validation/error messaging in English and zh-CN. (#29315)
- Discord/Thread bindings: replace fixed TTL lifecycle with inactivity (`idleHours`, default 24h) plus optional hard `maxAgeHours` lifecycle controls, and add `/session idle` + `/session max-age` commands for focused thread-bound sessions. (#27845) Thanks @osolmaz.
- Android/Nodes: add `camera.list`, `device.permissions`, `device.health`, and `notifications.actions` (`open`/`dismiss`/`reply`) on Android nodes, plus first-class node-tool actions for the new device/notification commands. (#28260) Thanks @obviyus.
- Android/Nodes parity: add `system.notify`, `photos.latest`, `contacts.search`/`contacts.add`, `calendar.events`/`calendar.add`, and `motion.activity`/`motion.pedometer`, with motion sensor-aware command gating and improved activity sampling reliability. (#29398) Thanks @obviyus.
- Android/Gateway capability refresh: add live Android capability integration coverage and node canvas capability refresh wiring, plus runtime hardening for A2UI readiness retries, scoped canvas URL normalization, debug diagnostics JSON, and JavaScript MIME delivery. (#28388) Thanks @obviyus.
- Feishu/Doc permissions: support optional owner permission grant fields on `feishu_doc` create and report permission metadata only when the grant call succeeds, with regression coverage for success/failure/omitted-owner paths. (#28295) Thanks @zhoulongchao77.
- Feishu/Docx tables + uploads: add `feishu_doc` actions for Docx table creation/cell writing (`create_table`, `write_table_cells`, `create_table_with_values`) and image/file uploads (`upload_image`, `upload_file`) with stricter create/upload error handling for missing `document_id` and placeholder cleanup failures. (#20304) Thanks @xuhao1.
- Feishu/Reactions: add inbound `im.message.reaction.created_v1` handling, route verified reactions through synthetic inbound turns, and harden verification with timeout + fail-closed filtering so non-bot or unverified reactions are dropped. (#16716) Thanks @schumilin.
- Feishu/Chat tooling: add `feishu_chat` tool actions for chat info and member queries, with configurable enablement under `channels.feishu.tools.chat`. (#14674)
- Feishu/Doc permissions: support optional owner permission grant fields on `feishu_doc` create and report permission metadata only when the grant call succeeds, with regression coverage for success/failure/omitted-owner paths. (#28295) Thanks @zhoulongchao77.
- Memory/LanceDB: support custom OpenAI `baseUrl` and embedding dimensions for LanceDB memory. (#17874) Thanks @rish2jain and @vincentkoc.
### Fixes
- Agents/Sessions list transcript paths: handle missing/non-string/relative `sessions.list.path` values and per-agent `{agentId}` templates when deriving `transcriptPath`, so cross-agent session listings resolve to concrete agent session files instead of workspace-relative paths. (#24775) Thanks @martinfrancois.
- Android/Voice screen TTS: stream assistant speech via ElevenLabs WebSocket in Talk Mode, stop cleanly on speaker mute/barge-in, and ignore stale out-of-order stream events. (#29521) Thanks @gregmousseau.
- Web UI/Cron: include configured agent model defaults/fallbacks in cron model suggestions so scheduled-job model autocomplete reflects configured models. (#29709)
- CLI/Cron: clarify `cron list` output by renaming `Agent` to `Agent ID` and adding a `Model` column for isolated agent-turn jobs. (#26259)
- Cron/Delivery: disable the agent messaging tool when `delivery.mode` is `"none"` so cron output is not sent to Telegram or other channels. (#21808)
- CLI/Cron: clarify `cron list` output by renaming `Agent` to `Agent ID` and adding a `Model` column for isolated agent-turn jobs. (#26259)
- Feishu/Reply media attachments: send Feishu reply `mediaUrl`/`mediaUrls` payloads as attachments alongside text/streamed replies in the reply dispatcher, including legacy fallback when `mediaUrls` is empty. (#28959)
- Slack/User-token resolution: normalize Slack account user-token sourcing through resolved account metadata (`SLACK_USER_TOKEN` env + config) so monitor reads, Slack actions, directory lookups, onboarding allow-from resolution, and capabilities probing consistently use the effective user token. (#28103) Thanks @Glucksberg.
- Agents/Sessions list transcript paths: handle missing/non-string/relative `sessions.list.path` values and per-agent `{agentId}` templates when deriving `transcriptPath`, so cross-agent session listings resolve to concrete agent session files instead of workspace-relative paths. (#24775) Thanks @martinfrancois.
- Feishu/Reaction notifications: add `channels.feishu.reactionNotifications` (`off | own | all`, default `own`) so operators can disable reaction ingress or allow all verified reaction events (not only bot-authored message reactions). (#28529)
- Feishu/Outbound session routing: stop assuming bare `oc_` identifiers are always group chats, honor explicit `dm:`/`group:` prefixes for `oc_` chat IDs, and default ambiguous bare `oc_` targets to direct routing to avoid DM session misclassification. (#10407) Thanks @Bermudarat.
- Feishu/Group session routing: add configurable group session scopes (`group`, `group_sender`, `group_topic`, `group_topic_sender`) with legacy `topicSessionMode=enabled` compatibility so Feishu group conversations can isolate sessions by sender/topic as configured. (#17798)

View File

@ -21,8 +21,8 @@ android {
applicationId = "ai.openclaw.android"
minSdk = 31
targetSdk = 36
versionCode = 202602270
versionName = "2026.2.27"
versionCode = 202603010
versionName = "2026.3.1"
ndk {
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")

View File

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.27</string>
<string>2026.3.1</string>
<key>CFBundleVersion</key>
<string>20260227</string>
<string>20260301</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>

View File

@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.27</string>
<string>2026.3.1</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>20260227</string>
<string>20260301</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>

View File

@ -17,8 +17,8 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.27</string>
<string>2026.3.1</string>
<key>CFBundleVersion</key>
<string>20260227</string>
<string>20260301</string>
</dict>
</plist>

View File

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.27</string>
<string>2026.3.1</string>
<key>CFBundleVersion</key>
<string>20260227</string>
<string>20260301</string>
<key>WKCompanionAppBundleIdentifier</key>
<string>$(OPENCLAW_APP_BUNDLE_ID)</string>
<key>WKWatchKitApp</key>

View File

@ -15,9 +15,9 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.27</string>
<string>2026.3.1</string>
<key>CFBundleVersion</key>
<string>20260227</string>
<string>20260301</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>

View File

@ -92,8 +92,8 @@ targets:
- CFBundleURLName: ai.openclaw.ios
CFBundleURLSchemes:
- openclaw
CFBundleShortVersionString: "2026.2.27"
CFBundleVersion: "20260227"
CFBundleShortVersionString: "2026.3.1"
CFBundleVersion: "20260301"
UILaunchScreen: {}
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: false
@ -148,8 +148,8 @@ targets:
path: ShareExtension/Info.plist
properties:
CFBundleDisplayName: OpenClaw Share
CFBundleShortVersionString: "2026.2.27"
CFBundleVersion: "20260227"
CFBundleShortVersionString: "2026.3.1"
CFBundleVersion: "20260301"
NSExtension:
NSExtensionPointIdentifier: com.apple.share-services
NSExtensionPrincipalClass: "$(PRODUCT_MODULE_NAME).ShareViewController"
@ -179,8 +179,8 @@ targets:
path: WatchApp/Info.plist
properties:
CFBundleDisplayName: OpenClaw
CFBundleShortVersionString: "2026.2.27"
CFBundleVersion: "20260227"
CFBundleShortVersionString: "2026.3.1"
CFBundleVersion: "20260301"
WKCompanionAppBundleIdentifier: "$(OPENCLAW_APP_BUNDLE_ID)"
WKWatchKitApp: true
@ -203,8 +203,8 @@ targets:
path: WatchExtension/Info.plist
properties:
CFBundleDisplayName: OpenClaw
CFBundleShortVersionString: "2026.2.27"
CFBundleVersion: "20260227"
CFBundleShortVersionString: "2026.3.1"
CFBundleVersion: "20260301"
NSExtension:
NSExtensionAttributes:
WKAppBundleIdentifier: "$(OPENCLAW_WATCH_APP_BUNDLE_ID)"
@ -237,5 +237,5 @@ targets:
path: Tests/Info.plist
properties:
CFBundleDisplayName: OpenClawTests
CFBundleShortVersionString: "2026.2.27"
CFBundleVersion: "20260227"
CFBundleShortVersionString: "2026.3.1"
CFBundleVersion: "20260301"

View File

@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.2.27</string>
<string>2026.3.1</string>
<key>CFBundleVersion</key>
<string>202602270</string>
<string>202603010</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@ -37,16 +37,16 @@ Notes:
# APP_BUILD must be numeric + monotonic for Sparkle compare.
# Default is auto-derived from APP_VERSION when omitted.
BUNDLE_ID=ai.openclaw.mac \
APP_VERSION=2026.2.27 \
APP_VERSION=2026.3.1 \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
scripts/package-mac-app.sh
# Zip for distribution (includes resource forks for Sparkle delta support)
ditto -c -k --sequesterRsrc --keepParent dist/OpenClaw.app dist/OpenClaw-2026.2.27.zip
ditto -c -k --sequesterRsrc --keepParent dist/OpenClaw.app dist/OpenClaw-2026.3.1.zip
# Optional: also build a styled DMG for humans (drag to /Applications)
scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.27.dmg
scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.3.1.dmg
# Recommended: build + notarize/staple zip + DMG
# First, create a keychain profile once:
@ -54,13 +54,13 @@ scripts/create-dmg.sh dist/OpenClaw.app dist/OpenClaw-2026.2.27.dmg
# --apple-id "<apple-id>" --team-id "<team-id>" --password "<app-specific-password>"
NOTARIZE=1 NOTARYTOOL_PROFILE=openclaw-notary \
BUNDLE_ID=ai.openclaw.mac \
APP_VERSION=2026.2.27 \
APP_VERSION=2026.3.1 \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
scripts/package-mac-dist.sh
# Optional: ship dSYM alongside the release
ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenClaw-2026.2.27.dSYM.zip
ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenClaw-2026.3.1.dSYM.zip
```
## Appcast entry
@ -68,7 +68,7 @@ ditto -c -k --keepParent apps/macos/.build/release/OpenClaw.app.dSYM dist/OpenCl
Use the release note generator so Sparkle renders formatted HTML notes:
```bash
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/OpenClaw-2026.2.27.zip https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/OpenClaw-2026.3.1.zip https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml
```
Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/changelog-to-html.sh)) and embeds them in the appcast entry.
@ -76,7 +76,7 @@ Commit the updated `appcast.xml` alongside the release assets (zip + dSYM) when
## Publish & verify
- Upload `OpenClaw-2026.2.27.zip` (and `OpenClaw-2026.2.27.dSYM.zip`) to the GitHub release for tag `v2026.2.27`.
- Upload `OpenClaw-2026.3.1.zip` (and `OpenClaw-2026.3.1.dSYM.zip`) to the GitHub release for tag `v2026.3.1`.
- Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`.
- Sanity checks:
- `curl -I https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml` returns 200.

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/acpx",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw ACP runtime backend via acpx",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/bluebubbles",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw BlueBubbles channel plugin",
"type": "module",
"openclaw": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/copilot-proxy",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw Copilot Proxy provider plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/diagnostics-otel",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw diagnostics OpenTelemetry exporter",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/diffs",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw diff viewer plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/discord",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Discord channel plugin",
"type": "module",
"openclaw": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/feishu",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Feishu/Lark channel plugin (community maintained by @m1heng)",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/google-gemini-cli-auth",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw Gemini CLI OAuth provider plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/googlechat",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw Google Chat channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/imessage",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw iMessage channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/irc",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw IRC channel plugin",
"type": "module",
"openclaw": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/line",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw LINE channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/llm-task",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw JSON-only LLM task plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/lobster",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
"type": "module",
"openclaw": {

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/matrix",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Matrix channel plugin",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/mattermost",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Mattermost channel plugin",
"type": "module",
"openclaw": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/memory-core",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw core memory search plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/memory-lancedb",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw LanceDB-backed long-term memory plugin with auto-recall/capture",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/minimax-portal-auth",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw MiniMax Portal OAuth provider plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/msteams",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Microsoft Teams channel plugin",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/nextcloud-talk",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Nextcloud Talk channel plugin",
"type": "module",
"openclaw": {

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/nostr",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Nostr channel plugin for NIP-04 encrypted DMs",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/open-prose",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenProse VM skill pack plugin (slash command + telemetry).",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/signal",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw Signal channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/slack",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw Slack channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/synology-chat",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "Synology Chat channel plugin for OpenClaw",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/telegram",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw Telegram channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/tlon",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Tlon/Urbit channel plugin",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/twitch",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Twitch channel plugin",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/voice-call",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw voice-call plugin",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/whatsapp",
"version": "2026.2.27",
"version": "2026.3.1",
"private": true,
"description": "OpenClaw WhatsApp channel plugin",
"type": "module",

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/zalo",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Zalo channel plugin",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.2.27
## 2026.3.1
### Changes

View File

@ -1,6 +1,6 @@
{
"name": "@openclaw/zalouser",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "OpenClaw Zalo Personal Account plugin via zca-cli",
"type": "module",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "openclaw",
"version": "2026.2.27",
"version": "2026.3.1",
"description": "Multi-channel AI gateway with extensible messaging integrations",
"keywords": [],
"homepage": "https://github.com/openclaw/openclaw#readme",

View File

@ -13,15 +13,15 @@ describe("collectAppcastSparkleVersionErrors", () => {
});
it("requires lane-floor builds on and after lane-floor cutover", () => {
const xml = `<rss><channel>${makeItem("2026.2.27", "202602270")}</channel></rss>`;
const xml = `<rss><channel>${makeItem("2026.3.1", "202603010")}</channel></rss>`;
expect(collectAppcastSparkleVersionErrors(xml)).toEqual([
"appcast item '2026.2.27' has sparkle:version 202602270 below lane floor 2026022790.",
"appcast item '2026.3.1' has sparkle:version 202603010 below lane floor 2026030190.",
]);
});
it("accepts canonical stable lane builds on and after lane-floor cutover", () => {
const xml = `<rss><channel>${makeItem("2026.2.27", "2026022790")}</channel></rss>`;
const xml = `<rss><channel>${makeItem("2026.3.1", "2026030190")}</channel></rss>`;
expect(collectAppcastSparkleVersionErrors(xml)).toEqual([]);
});