Arc Forge Labs Docs v2026.10.2

Release Versioning

The target release policy (stable, beta and dev channels; corrections and betas) is RELEASING.md. This page describes the current procedure until that lands (#145).

Dictate uses calendar versioning for public releases:

YYYY.M.N
  • YYYY.M is the month the release is cut in (UTC), without zero-padding.
  • N is the release count in that month. It is not the day.
  • N is the highest stable N already tagged in that month, plus 1. The first release of a month is 1. N can be more than 31.
  • Releases before 2026.10.1 used the day as N. Do not change those tags. The count continues from the highest one in the month.

For example, after 2026.10.20 comes 2026.10.21, after 2026.10.31 comes 2026.10.32, and the first release of November is 2026.11.1. Sort versions as numbers, part by part, never as text: as text, 2026.10.9 comes after 2026.10.32. In shell, sort -V and git tag --sort=-v:refname are correct.

Python package metadata uses the same PEP 440-compatible version.

Older releases sometimes added a correction -K (for example 2026.9.25-1) when an immutable package registry already had the version. The tooling still accepts these tags. For new code, use the next N.

The app reports the public release version through:

dictate --version

Print the next version, or check one, with (fetch the tags first):

git fetch --tags
python scripts/calver.py next
python scripts/calver.py next --today 2026-10-20
python scripts/calver.py check 2026.10.3

Synchronize release metadata before tagging with:

python scripts/sync_release_version.py            # the next version
python scripts/sync_release_version.py 2026.10.3  # must be the next version
python scripts/sync_release_version.py 2026.10.3 --check

When it writes, sync_release_version.py refuses a version that is not the next release this month (--today sets the date for a test). --check only compares the files with the version, so it works for any released version.

Release tags and GitHub milestones should use the public version with a leading v, for example:

v2026.10.3
v2026.10.32

Windows MSI installers cannot use the public CalVer string directly because WiX/MSI requires numeric major.minor.patch[.build], with major and minor at most 255. Keep tauri.conf.json's public app version as YYYY.M.N[-K], but map bundle.windows.wix.version to YY.M.N.K for MSI packaging:

2026.10.3   -> 26.10.3.0
2026.10.32  -> 26.10.32.0
2026.9.25-1 -> 26.9.25.1

After creating a v20* CalVer tag on a commit that has reached the default branch, start .github/workflows/release.yml manually with the release_tag input. The release workflow first verifies that the requested tag resolves to a commit reachable from the default branch, then runs the Linux/Windows test matrix, the hosted Windows user install smoke test, release metadata validation, Python artifact checks, and npm package validation before publishing.

The release, npm-publish and windows-signing environments need maintainer approval, so each publishing job waits in the run until approved. After the Windows installer is uploaded, the release dispatches windows-release-gate.yml, which installs that exact setup.exe on the win11-gpu VM, launches it, checks the running engine's version, and uninstalls. See windows-11.md.

GitHub release publication and Microsoft Store publication are separate lanes. Publishing a GitHub release updates the downloadable source and unsigned direct-install artifacts; it does not make an update available through the Microsoft Store. Store updates require the Store MSIX workflow in draft mode, Partner Center review, and an explicit publish/certification step. The draft refuses to build without a passing Windows release gate for the same version; skip_windows_gate is the override.

The npm package is published as @arcforgelabs/dictate and powers the hosted CDN install/update scripts. Publishing uses GitHub OIDC trusted publishing on the npm-publish environment. Do not dispatch a release until both publishers exist on the package, each with direct npm publish allowed:

Workflow file Environment
release.yml npm-publish
npm-unstable.yml npm-publish

Repository owner is arcforgelabs. Repository name is dictate. A long-lived NPM_TOKEN is not used. The June 2026 granular token expired, so latest stayed on 2026.7.4 while the GitHub release moved to 2026.9.25.

Provenance is off. npm only accepts provenance statements from public source repositories, and the repository was private when trusted publishing was set up. Both publish steps force publishConfig.provenance to false, so tags cut before that change still publish. The repository is public now, so provenance can be turned back on by removing that override and setting publishConfig.provenance to true.

The previous personal-scope package, @iamsamuelrodda/dictate, is deprecated on npm with a migration notice pointing users to @arcforgelabs/dictate. Keep it published as a compatibility landing point for old scripts; do not unpublish it unless there is a specific security or legal reason.

The current developer/bootstrap install path is:

powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@latest/install.ps1 | iex"

Npm Channels

Use npm dist-tags as update channels for the hosted developer/source bootstrap:

npm dist-tag Meaning Publisher
latest Stable developer bootstrap that points at a reviewed CalVer GitHub release tag. .github/workflows/release.yml
unstable Test bootstrap for a selected branch/SHA before it is considered stable. .github/workflows/npm-unstable.yml

The stable release workflow publishes @arcforgelabs/dictate@latest only from a validated v20* release tag reachable from the default branch. Do not publish feature work to latest.

The unstable workflow is manually dispatched. It creates a unique prerelease npm version such as 2026.7.4-unstable.123.1, patches the hosted install.ps1 and update.ps1 scripts in the npm package so they download the exact selected commit archive, and publishes that shim under the requested dist-tag (unstable by default). It does not move GitHub releases, Microsoft Store drafts, or the stable npm latest tag. The app version reported by dictate --version still comes from the selected source commit, so branch builds without a version bump can report the base CalVer while being delivered through the unstable channel.

Every unstable publish produces a durable GitHub prerelease containing the matching unsigned Windows MSI/NSIS installers and the Linux .deb, because installed direct builds cannot update from short-lived Actions artifacts. With run_tests=true, the unstable publish waits for the Linux/Windows Python matrix, the Windows user install/update/uninstall smoke, the Linux user encrypted-sync install smoke, UI build/server smoke, desktop shell compile/Rust tests, Linux/Windows desktop bundles, and npm package dry-run. Store upload/publish remains a separate guarded workflow. Paid Authenticode signing for direct-download Windows installers is a future-only lane, not a current release blocker.

Unstable is upstream of stable

The channels are ordered: everything on latest went through unstable first, or a newer build is on unstable. A Beta (unstable) user must never be offered something older than the current stable release. Two mechanisms keep that true:

  1. release.yml moves the npm unstable dist-tag forward to the stable version it just published whenever the unstable pointer is behind it (same-base prereleases and newer-base unstable builds are left alone).
  2. The app's update check resolves the Beta channel to the newer of the unstable and latest dist-tags, so installed copies get the same answer even if the pointer was not moved.

To publish an unstable bootstrap:

gh workflow run npm-unstable.yml \
  -f source_ref=feature/my-branch \
  -f npm_tag=unstable \
  -f run_tests=true

To install or update from the unstable bootstrap:

powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@unstable/install.ps1 | iex"
powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@unstable/update.ps1 | iex"

For Linux user installs that update through the in-app/npm path, set:

dictate config set-update-channel unstable
dictate config set-update-channel stable

stable is the user-facing name for npm's latest dist-tag. unstable maps to the unstable dist-tag. DICTATE_UPDATE_CHANNEL=unstable remains available for temporary process-level overrides, but the CLI setting is the supported user opt-in mechanism.

Promotion rule: once a feature set is locked, tested, and accepted, merge it to the release branch/default branch, create the normal CalVer tag, run .github/workflows/release.yml, then advance official channels such as Microsoft Store through their own guarded workflows. Never promote by retagging an unstable npm package as stable.

Windows has two supported ecosystems. Direct installs may select stable or unstable and update from the corresponding GitHub release/prerelease installer; these builds are unsigned and Windows will show the expected untrusted-publisher warning. Microsoft Store installs are stable-only and update through the Store. Do not revisit paid Windows signing until there is an explicit revenue-backed decision.

Each Windows payload contains engine/dictate-distribution.json. Direct bundles mark themselves direct and include their exact channel package version; Store MSIX bundles mark themselves store. The app uses this marker rather than guessing from Windows.