* Rename desktop app from Cline Code to Cline Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Format touched Rust test assertions Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
7.1 KiB
Desktop Experimental Branch & Beta Channel
How experimental desktop features are developed on the desktop-experimental
branch, shipped to users as Cline Beta, and graduated into main.
The release mechanics (workflow internals, secrets) live in
.github/workflows/desktop-publish.yml
and the publish-desktop skill
(.cline/skills/publish-desktop/SKILL.md);
this doc is the process.
What the beta channel is
The beta is a separate app, not a mode of the stable app:
- Product name
Cline Beta, bundle identifierbot.cline.app.beta(stable isCline/bot.cline.app) — set bysrc-tauri/tauri.beta.conf.json, which is layered overtauri.release.conf.jsonat build time. - Both apps install and run side by side, so people can compare beta features against stable directly.
- Each channel polls its own auto-update feed: stable installs poll the
rolling
desktop-latestrelease, beta installs poll the rollingdesktop-betarelease. The feed URL is compiled into the binary, so a beta install only ever receives beta builds and vice versa. Never delete either rolling release. - Yes, the names are asymmetric:
desktop-latestis the stable feed. Do not rename it todesktop-stable— the URL is baked into every stable binary ever shipped and the updater has no fallback endpoint, so renaming (or deleting) the release silently strands every existing install on a dead feed forever. Renaming would mean maintaining both feeds for as long as any pre-rename install exists, i.e. permanently. Same applies todesktop-betaonce the first beta ships. - Both apps share
~/.cline(provider credentials, global settings, hub daemon — the hub is multi-client by design, same as running the CLI and the app together). A beta that requires a newer hub build can trigger the hub-update-required flow in the stable app or vice versa; that's expected version skew, not a bug in itself.
Users join the beta by downloading the beta DMG from its GitHub release (announced on Slack). There is no auto-downgrade: leaving the beta means deleting the beta app (stable was never touched). Beta users get the stable version of a graduated feature through the normal stable release of the stable app they still have installed.
Branch model
desktop-experimental is a long-lived branch where experimental features
bake before graduating to main.
- Feature PRs target
desktop-experimentaland are merged there to iterate. Keep the feature's original PR againstmainopen as a draft — it accumulates the follow-up work done on the experimental branch and documents intent to graduate. - Graduation = a fresh (or the updated draft) PR against
maincontaining the feature plus everything learned on the experimental branch. Treat it as a normalmainPR: full review, tests, no experimental scaffolding. - Sync direction is one-way: merge
mainintodesktop-experimentalregularly — at minimum after every stable desktop release — so the branch never drifts far. Never mergedesktop-experimentalintomainwholesale. - Merge-conflict policy when syncing
mainin:package.json/src-tauri/tauri.conf.jsonversions: keep the branch's beta version (see versioning rule below for when to bump its base).CHANGELOG.md: keep both sides' sections, newest version first — stable and beta sections interleave by recency.- Feature code: main wins for anything that graduated; resolve toward main's reviewed form.
Versioning & tags
- Beta versions are prereleases of the next stable version: stable
0.0.13→ betas0.0.14-beta.1,0.0.14-beta.2, … - Tag format:
desktop-vX.Y.Z-beta.N, tagged on adesktop-experimentalcommit. Stable tags (desktop-vX.Y.Z, no suffix) stay onmain; the workflow enforces both shapes and each channel's branch ancestry. - When a stable release ships with a version ≥ the current beta base, bump
the base for the next beta (stable
0.0.14out → next beta is0.0.15-beta.1). A beta must never share itsX.Y.Zbase with an already-shipped stable. - Semver keeps the channels ordered:
0.0.14-beta.Nsorts above stable0.0.13and below the eventual0.0.14.
Cutting a beta release
Manual, like stable — no nightly automation. Short form (the
publish-desktop skill walks through it):
-
On
desktop-experimental: mergemainin, bump both version files to the new beta version, prepend a## X.Y.Z-beta.Nsection toCHANGELOG.md, commit, push. -
Tag
desktop-vX.Y.Z-beta.Non that commit and push the tag. -
Dispatch from
mainwith the beta channel:gh workflow run desktop-publish.yml --ref main \ -f git_tag=desktop-vX.Y.Z-beta.N \ -f channel=beta \ -f confirm_publish=publish -
Approve the
PublishDesktopenvironment gate; the workflow builds, signs, and notarizes the beta bundle, creates a prerelease GitHub release, refreshesdesktop-beta/latest.json, and posts to Slack — same announcement path as stable, marked as beta.
Why dispatch from main when the code is on desktop-experimental?
Security invariant: the workflow run executes main's copy of
desktop-publish.yml and only the checkout points at the beta tag (the
validate job pins the tag to desktop-experimental ancestry). The
signing-secret gates — the github.ref == main check and the
PublishDesktop environment's main-only deployment-branch policy — stay
exactly as they are for stable, and a workflow file edited on
desktop-experimental can never reach the signing secrets. Do not add
desktop-experimental to the PublishDesktop deployment-branch policy.
One thing dispatch-from-main does not cover: the build job checks out the
tag and runs its build scripts (dependency install hooks, build:sdk,
Tauri's beforeBuildCommand, build.rs) with the signing secrets in scope
— true for stable and beta alike. The control is the PublishDesktop
required-reviewer approval: approving a publish means vouching for the
code the tag points at, not just for the release happening. That is why
desktop-experimental must keep main-grade merge controls (branch
protection, maintainer-only pushes) — anyone who can land code there can get
it executed alongside the signing keys once a publish of it is approved.
Guardrails worth knowing about
- The updater comparator is a plain semver "newer than". Feed separation is
the entire safety story: a beta manifest on
desktop-latestwould auto-update every stable install onto the beta. The workflow guards this three ways: the stable channel rejects prerelease tags, the feed target is derived fail-closed from the channel (and cross-checked in the release job), and the build asserts the compiled binary embeds exactly its own channel's feed URL before anything is signed into a release. tauri.beta.conf.jsonmust exist on the tagged commit (the build checks out the tag), so keep it present on bothmainanddesktop-experimental.