Arc Forge Labs Docs v2026.10.2

Release policy

Dictate offers stable releases for everyday use, beta releases for testing the next stable release, and a dev channel for building from master. This page explains those choices, how versions are numbered, and what a release has been checked for.

The structure follows OpenClaw's release policy (docs/reference/RELEASING.md and docs/install/development-channels.md), adapted to a desktop app.

This is the target design, not what ships today. Only the version numbering has started: 2026.10.1 was the first release numbered YYYY.M.PATCH. The rest is planned and tracked in #145, and What changes from today lists every gap. Until it lands, release-versioning.md is the procedure releases follow.

Release channels

Channel What you get
Stable The regular release: the GitHub release marked Latest, npm latest, and the Microsoft Store.
Beta A candidate on npm beta and a GitHub prerelease. It may be a prerelease or a final version awaiting promotion.
Dev The moving head of master, built from source. For development only; it may be incomplete or broken.

Every channel is the same app. There is no separate beta app: a user switches channel on the install they have, and the choice is saved in config.yaml as update_channel. Beta carries what is heading to the next stable release, and nothing else: unfinished or shelved features are in no release, beta or stable, switched off or otherwise. Meetings, shelved as P3 in VISION.md, was removed from the app before 2026.10.1 (#143) and is kept on the archive/meeting-2026-10-03 branch (#140).

On upgrade, a saved update_channel: unstable from the old prerelease lane becomes beta, and the old dictate config set-update-channel command is replaced by dictate update --channel. The old value is migrated, not kept as an alias.

None of the commands below exist yet; they are planned in #145. Today the channel is fixed by the build, as described in release-versioning.md.

dictate update --channel stable
dictate update --channel beta
dictate update --channel dev
  • --channel updates and saves the choice after a successful update. A refused or failed update keeps the previous channel.
  • Beta installs the newest of npm beta and latest by version order. An older beta never replaces a newer stable release.
  • Dev switches to a source checkout of master, builds it and reinstalls.
  • --tag <version> installs one exact version once without changing the saved channel, for example dictate update --tag 2026.10.1-beta.2.
  • Installing an older version than the one running asks for confirmation.
  • dictate update --dry-run shows the channel, target version and planned steps without changing anything; dictate update status shows the channel, where that choice came from, the install kind, the current version and whether an update is available.
  • Microsoft Store installs are stable only. The Store controls their updates.

The app's settings show the same channel choice for direct installs.

OpenClaw's fourth channel, extended-stable, is a maintenance line for its Gateway servers. Dictate has no equivalent need and does not offer it.

Version naming

Release Version example
Regular final 2026.10.1
Beta prerelease 2026.10.1-beta.1
Regular correction 2026.10.1-1

Versions use year.month.patch, without zero-padding. The patch is a release number within the month, starting at 1, not a day of the month: the highest stable patch already tagged in that month (UTC) plus 1, so it can be more than 31 (after 2026.10.31 comes 2026.10.32). python scripts/calver.py next prints it. 2026.10.1 (released 3 October 2026) was the first version numbered this way. Git tags add v, as in v2026.10.1. Release tags are annotated and signed, and the publication workflow refuses lightweight, unsigned or unverified tags.

Published versions and release tags are never replaced. A fix gets a new version: a correction (-1) for a repackage of the same code, or the next patch for new code.

Version order

Version order is PEP 440 order applied to the public version. A beta is older than the final with the same base number, and a correction is newer:

2026.10.1-beta.1 < 2026.10.1-beta.2 < 2026.10.1 < 2026.10.1-1 < 2026.10.2-beta.1

Every update decision uses this order: whether an update is available, which of npm beta and latest the beta channel installs, and whether an install is a downgrade that needs confirmation.

npm's semver order is different and never makes these decisions. npm reads 2026.10.1-1 as a prerelease, so it ranks the correction below 2026.10.1 and even below 2026.10.1-beta.2. The app therefore never asks npm for the "newest" version: it reads the exact versions that the latest and beta dist-tags point at and compares them in PEP 440 order, and the release workflow always publishes with an explicit --tag.

Package formats map the public version as follows. The Python and MSI strings sort in the order above; the npm strings are only names (see above).

Format Final 2026.10.1 Beta 2026.10.1-beta.2 Correction 2026.10.1-1
Python (PEP 440) 2026.10.1 2026.10.1b2 2026.10.1.post1
npm (named by dist-tag) 2026.10.1 2026.10.1-beta.2 2026.10.1-1
MSI (WiX) 26.10.1100.0 26.10.1002.0 26.10.1101.0

MSI versions are numeric: major YY and minor M, each at most 255, then a build number of at most 65535. Windows Installer compares only those three fields and ignores a fourth, so the order lives in the build number: patch × 1000 + slot, where the slot is N for beta N (1–99), 100 for the final and 100 + K for correction K. The fourth field is always 0. Every later build therefore has a higher version than an earlier one, and the patch can reach 65 in a month before the build number runs out. The first release on this map jumps the build number from today's P to P × 1000 + 100, which is still higher than every earlier MSI.

Release cadence

Releases go to beta first and move to stable after validation. Promotion does not change the version: the final version is published to the beta channel, validated, then promoted by moving npm latest, marking the GitHub release Latest and submitting the same MSIX to the Store. A maintainer can publish straight to stable when needed, and it still has to meet the stable validation below. npm beta must always be at least as new as latest.

A release is cut from master onto a release/YYYY.M.P branch. Beta and final builds of that release come from that branch with pinned release tooling, and fixes reach it from master by cherry-pick.

Release validation

Stable publication needs stable validation. A final version first published to beta still has to meet it, and beta validation cannot qualify a stable release.

Blocking checks for stable:

  • The Linux and Windows test matrix.
  • The packaged builds: .deb, MSI and NSIS installers, and MSIX.
  • The frozen-engine cold-start smoke: time to handshake, and the first authenticated /api/state.
  • The hosted Windows user install smoke.
  • The Windows release gate: install, launch, version check and uninstall on the release VM.
  • Upgrade from the previous stable release on Windows and Linux.
  • Release metadata and package integrity checks.

Beta needs the test matrix, the packaged builds and the cold-start smoke.

Every failed check needs an explicit release-lead decision: blocker or flake. Rerun a flake on the same release commit at most twice, file its fix on master, and keep the original failure on record. A skipped, cancelled or missing check is not a pass.

Dependency advisories never block or delay a release. Release evidence records every advisory, and the fix ships through master after publication. Only a known-malware finding stops publication.

The health of master CI does not gate a release. Validation and publication run from the release branch.

Packages and apps can become available at different times

A published release does not mean every package is ready. The Microsoft Store review, Windows signing and the npm publish can finish separately from the GitHub release. A beta may ship without some installers; its release notes say so, for example "no Windows installer for this beta".

Release notes and verification

The changelog describes user-facing changes. Each GitHub release also carries:

  • dictate-<version>-release-manifest.json: the release commit, version and channel, and every published file with its SHA-256.
  • dictate-<version>-dependency-evidence.zip: the locked Python and npm dependencies and the advisory report for that commit.
  • dictate-<version>-postpublish-evidence.json: the checks run against the published files after upload.

These identify the tested version and the files that shipped. Later documentation fixes can improve the release notes without rebuilding or replacing packages.

What changes from today

Each row is tracked in #145.

Today Target
scripts/calver.py and scripts/sync_release_version.py pick and check the next YYYY.M.PATCH count, and the tag checks accept any patch from 1 with an optional -N. They reject -beta.N The tooling and docs generate and check YYYY.M.PATCH, corrections -N and betas -beta.N
The update check ranks a same-base prerelease above the final (_compare_prerelease in src/dictate/update_status.py), so 2026.10.1-beta.2 would count as newer than 2026.10.1 PEP 440 version order: betas below the final, corrections above it
Prerelease channel named unstable: npm unstable, tags vYYYY.M.D-unstable.<run>.<attempt> npm beta, tags vYYYY.M.P-beta.N
The channel is fixed by the build (release_channel() in src/dictate/version.py reads the version string); the UI refuses to change it; dictate config set-update-channel stable|unstable saves a choice; there is no dictate update command The channel is a saved setting on any direct install; dictate update --channel, --tag, --dry-run and update status; a saved unstable becomes beta on upgrade
A separate side-by-side beta app was planned (#131) One app; no separate beta app
Releases tagged from master release/YYYY.M.P branches with pinned tooling
Unsigned annotated tags Signed annotated tags, verified before publishing
Release assets are packages and install scripts Packages plus release manifest, dependency evidence and post-publish evidence
MSI YY.M.P.N (_msi_safe_version in scripts/sync_release_version.py): the release counter is the build number (2026.10.2 is 26.10.2.0) and a correction -N goes in the fourth field, which Windows Installer ignores, so a correction compares equal to its final; an unstable build keeps the MSI number of the commit it was built from MSI YY.M.(P × 1000 + slot).0: betas slot 1–99, the final 100, corrections 101 and up