Compare commits

..
Author SHA1 Message Date
Saoud Rizwan c17c82b79a chore(desktop): release v0.0.28 2026-09-15 00:34:19 -07:00
Saoud Rizwan cb0b20f791 docs(skills): rewrite publish-extension for the completed SDK cutover
The ext-sdk-bundle-rollout flag reached 100%, so stable releases now ship a
standalone build of main through ext-vscode-publish-stable.yml rather than the
combined legacy+next A/B VSIX. Rewrite the skill around that path and record
what is left to retire.

Also corrects two things the old text got wrong: nightly is manual-dispatch
only (the cron was removed because PublishNightly gained required reviewers,
so unattended runs sat waiting and starved their successors), and the stable
workflow tags before it builds, so a tag-push failure fails early rather than
leaving a published-but-untagged release.
2026-09-14 23:56:07 -07:00
Saoud Rizwan 2198414245 chore(vscode): release v4.1.18 2026-09-14 23:56:01 -07:00
7 changed files with 184 additions and 141 deletions
+131 -136
View File
@@ -1,34 +1,33 @@
---
name: publish-extension
description: Use when releasing the Cline VS Code extension — stable (currently the combined legacy+next A/B VSIX via ext-vscode-ab-package), nightly (ext-vscode-publish-nightly), or a legacy-branch hotfix (ext-vscode-publish-legacy). Guides version selection, changelog, PostHog rollout-flag coordination, workflow dispatch, environment approvals, tagging, and post-publish verification, plus the eventual cutover to publishing the SDK extension standalone.
description: Use when releasing the Cline VS Code extension — stable (standalone SDK build of main via ext-vscode-publish-stable), nightly (ext-vscode-publish-nightly, manual dispatch), or an emergency legacy-branch hotfix (ext-vscode-publish-legacy). Guides version selection, changelog, workflow dispatch, environment approvals, tagging, post-publish verification, and the remaining retirement of the finished A/B rollout machinery.
---
# VS Code Extension Release
Use this skill when the user asks to release, publish, or ship the VS Code extension — stable, nightly, or a legacy hotfix — or to dial the rollout, or to cut over to the SDK extension permanently.
Use this skill when the user asks to release, publish, or ship the VS Code extension — stable, nightly, or an emergency legacy hotfix — or to retire the leftover A/B rollout machinery.
> Working directory: repo root. All workflows are dispatched from `main` (GitHub requires the workflow file on the default branch; each workflow checks out the refs it actually builds).
## The current era: combined A/B rollout
## The current era: standalone SDK extension from `main`
We are mid-migration from the legacy (npm, pre-SDK) extension to the next (SDK-based, bun) extension. Until the cutover is complete, **the stable and nightly listings ship a combined VSIX**: a small loader + two complete extensions (`next/` built from `main`, `legacy/` built from the `legacy-extension` branch). The loader picks one per window based on the PostHog flag `ext-sdk-bundle-rollout`. Deep-dive docs: `apps/vscode-rollout/README.md` (authoritative) and PR #12253 (design + runbook comments).
**The legacy → SDK migration is complete.** The PostHog flag `ext-sdk-bundle-rollout` reached 100% (verified empirically 2026-09-15: 200/200 `/decide` probes returned `true`), so every user on the combined VSIX runs the `next` (SDK, bun) bundle and the `legacy/` half is dead weight. Stable releases now ship a **plain build of `main`** through `ext-vscode-publish-stable.yml`. The combined A/B path (`ext-vscode-ab-package.yml`) is no longer used for releases and is pending deletion — see "Retiring the A/B machinery" at the bottom for what is still left to clean up and the one caveat (leave the flag at 100%).
Endgame (see "Cutover" at the bottom): once the next bundle is trusted at 100%, stable goes back to a plain build of `main` via `ext-vscode-publish-stable.yml` and all the legacy/rollout machinery is retired.
History, for context only: the A/B era ran `4.1.0``4.1.17` (JulSep 2026). Design docs remain at `apps/vscode-rollout/README.md` and PR #12253 until that directory is removed.
### The listings and the workflows
| Channel | Marketplace ID | Workflow | Trigger | Version |
|---|---|---|---|---|
| Stable (combined) | `saoudrizwan.claude-dev` | `ext-vscode-ab-package.yml` | dispatch only; `publish` input defaults false | manual input (semver, e.g. `4.1.0`) |
| Nightly (combined) | `saoudrizwan.cline-nightly` | `ext-vscode-publish-nightly.yml` | cron 12:00 UTC + dispatch | auto `<major>.<minor>.<unix-ts>` from main's `apps/vscode/package.json` |
| Legacy hotfix (standalone) | `saoudrizwan.claude-dev` | `ext-vscode-publish-legacy.yml` | dispatch | from `apps/vscode/package.json` on `legacy-extension` |
| Stable standalone (post-cutover) | `saoudrizwan.claude-dev` | `ext-vscode-publish-stable.yml` | dispatch | from `apps/vscode/package.json` on `main` |
| **Stable** | `saoudrizwan.claude-dev` | `ext-vscode-publish-stable.yml` | dispatch, from `main` | `apps/vscode/package.json` on `main`; tag `v<version>` must match |
| Nightly | `saoudrizwan.cline-nightly` | `ext-vscode-publish-nightly.yml` | **manual dispatch only** (cron deliberately removed) | auto `<major>.<minor>.<unix-ts>` from main's `apps/vscode/package.json` |
| Legacy hotfix (emergency only) | `saoudrizwan.claude-dev` | `ext-vscode-publish-legacy.yml` | dispatch | `apps/vscode/package.json` on `legacy-extension` |
All three publish paths gate on tests before publishing: nightly and ab-package run the reusable bun suite (`ext-vscode-test.yml`, tests `main`) — ab-package additionally runs the legacy branch's npm suite — and the legacy workflow inlines the npm suite. Environment gates: stable paths use `publish``Publish` environment (required reviewers approve in the Actions UI); nightly uses `PublishNightly` (branch policy only, no reviewers — a reviewer requirement would block the cron).
Stable runs the reusable bun suite (`ext-vscode-test.yml`, tests `main`) before an environment-gated publish job (`publish` environment required reviewers approve in the Actions UI). Nightly uses `PublishNightly` (branch policy only). Nightly **still builds the combined loader VSIX** (loader + `next/` + `legacy/`) until it is converted back to a plain build — that conversion is on the retirement list below.
## Golden rules (read before any release)
1. **One listing, one version line.** `claude-dev` is published from multiple workflows/branches. Every stable publish must use a version **strictly above the highest version ever published to the listing from any branch** — marketplace versions are monotonic and cannot be unpublished (supersede, never delete). Check what's live first:
1. **One listing, one version line.** `claude-dev` has been published from multiple workflows and branches. Every stable publish must use a version **strictly above the highest version ever published to the listing from any branch** — marketplace versions are monotonic and cannot be unpublished (supersede, never delete). Check what's live first:
```bash
curl -s -X POST "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery" \
@@ -37,13 +36,123 @@ All three publish paths gate on tests before publishing: nightly and ab-package
| python3 -c "import json,sys; v=json.load(sys.stdin)['results'][0]['extensions'][0]['versions'][0]; print(v['version'], v['lastUpdated'])"
```
`ext-vscode-ab-package` also enforces this automatically for `publish=true` runs: a preflight job validates the version format (plain `X.Y.Z`) and hard-fails unless it exceeds the live Marketplace version, and the publish job re-checks right before publishing (the approval wait can last days — a legacy hotfix landing in between is caught). Still run the query yourself when *choosing* the version.
**`ext-vscode-publish-stable` has no marketplace-monotonicity preflight** (that check only ever lived in the retired ab-package workflow), so this query is the only guard. The `legacy-extension` branch sits at `4.0.12`, so it cannot collide with the `4.1.x` line, but a legacy hotfix would have to be numbered above the live stable version (see the emergency section).
2. **Check the flag BEFORE any stable combined publish.** `ext-sdk-bundle-rollout` is **shared between nightly and stable** — the loader sends only a machine id to `/decide`, no channel property, so there is no per-channel targeting. If the flag is high (nightly dogfooding) and you publish stable, stable users get the next bundle at that same percentage. Verify the effective percentage empirically (no PostHog admin needed — sample `/decide` with random ids using the key inlined in any shipped loader):
2. **Tag == package version, enforced.** The workflow reads `apps/vscode/package.json`, requires the `tag` input to equal `v<that version>`, and hard-fails otherwise. Bump the version on `main` first.
3. **Changelog lives at the repo ROOT** (`CHANGELOG.md`) — not `apps/vscode/CHANGELOG.md` (doesn't exist). The workflow hard-fails unless the first `## [` heading is exactly `## [<version>]`. The section body becomes the GitHub release notes and the Slack post (Slack copy is trimmed to 3000 chars with a link out; the release body stays whole).
4. **Ask before pushing** commits or tags. **Never approve the `publish` environment gate yourself via `gh api`** — hand the maintainer the run URL to click "Review deployments".
5. **Concurrency**: the workflow groups on the tag with `cancel-in-progress: false`. A publish run left `waiting` on approval blocks every later dispatch of the same tag until cancelled (`gh run cancel <id>`).
## Stable release — the current path
### Pre-flight
```bash
# 1. What's live (rule 1) → pick <VERSION> strictly above it (normally patch bump).
# 2. Confirm main's package.json is at the *previous* published version, i.e. the repo
# reflects the live line and nothing unreleased is already bumped:
node -p "require('./apps/vscode/package.json').version"
# 3. What's in the release:
git fetch origin main --tags
git log v<PREV>..origin/main --oneline --no-merges -- apps/vscode sdk/packages
```
The CLI/SDK notes are the best starting point for the extension notes — the extension bundles `@cline/*` from source, so an SDK release in the same window ships here too. Read `sdk/CHANGELOG.md` for the matching SDK version and translate what's extension-visible; skip CLI-only and desktop-only items.
### Release prep on `main` (PR, not direct push)
- Bump `apps/vscode/package.json` → `<VERSION>`.
- Prepend `## [<VERSION>]` to root `CHANGELOG.md` with the approved notes.
- Side effect of the bump: nightly versions become `<major>.<minor>.<unix-ts>` of the new base — harmless (separate listing, still monotonic).
Optional local rehearsal of the exact packaging step (the workflow has no dry-run input, and the build runs inside the gated publish job, so this is the only pre-approval check):
```bash
bun install --frozen-lockfile && bun run build:sdk
cd apps/vscode && npx @vscode/vsce package --no-dependencies --allow-package-secrets sendgrid --out /tmp/rehearsal.vsix
# vsce runs vscode:prepublish → `bun run package` (check-types + build:webview + lint + esbuild --production),
# i.e. the same build the workflow performs. Telemetry env is NOT set locally, so expect
# telemetry to be dark in this artifact — that is fine for a build rehearsal, not for shipping.
```
### Dispatch
```bash
gh workflow run ext-vscode-publish-stable.yml --ref main \
-f release-type=release \
-f auto_create_tag_from_main=true \
-f tag=v<VERSION>
gh run list --workflow=ext-vscode-publish-stable.yml --limit 1 --json databaseId,url,status
```
What the run does, in order: test gate on `main` → publish job checks out `main`, **creates and pushes `v<VERSION>` at the tested SHA** (auto-create mode; refuses if the tag already exists elsewhere) → `bun install --frozen-lockfile` → `bun run build:sdk` → asserts the `better-sqlite3` native binary → verifies tag/version/changelog/PATs → `vsce package` → `bun run publish:marketplace` (vsce publish **and** `npx ovsx publish`, both `--no-dependencies`) → GitHub release with the `.vsix` attached → Slack post. The publish job waits for `publish` environment approval before any of that. Check what a run is waiting on:
```bash
gh api repos/cline/cline/actions/runs/<run-id>/pending_deployments
```
`release-type=pre-release` publishes to the pre-release channel of the same listing; it is a real publish, not a rehearsal.
### Post-publish
1. Verify both registries serve the new version (expect minutes-to-an-hour of Marketplace validation lag after "Published" appears in the logs):
```bash
# Marketplace: query from rule 1
curl -s "https://open-vsx.org/api/saoudrizwan/claude-dev" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['version'], d['timestamp'])"
```
2. Verify the bookkeeping landed: `git fetch --tags && git tag --list 'v<VERSION>'`, `gh release view v<VERSION>`, Slack post in the release channel. Tag creation happens **before** the build in this workflow, so a tag-push failure fails the run early (no publish) rather than leaving a published-but-untagged release. Known cause: the built commit touches `.github/workflows/**` (default token cannot create such refs). Workaround: create and push the tag yourself at `origin/main` HEAD (ask first), then re-dispatch with `auto_create_tag_from_main=false` from `main` — the workflow requires the existing tag to point at the exact SHA it tested.
3. Artifact check (`gh run download <run-id>` or the release asset): `package.json` inside is `saoudrizwan.claude-dev@<VERSION>`; `grep -c 'process.env.TELEMETRY_SERVICE_API_KEY' extension/dist/extension.js` must be **0** (a leftover literal means the build ran without its env and telemetry is silently dead). `CLINE_ROLLOUT_VARIANT` is defined to `""` for ordinary builds by `apps/vscode/esbuild.mjs`, so no literal is expected and telemetry carries no `extension_variant` — correct for a standalone build.
4. Monitor errors on `extension_version = '<VERSION>'` in `otel.otel_logs` (stable cohort is cleanly separable — nightly versions are timestamps). Metabase dashboards 17 (task error rate) and 19 (error deep dive).
## Nightly release
**Manual dispatch only.** The cron was removed on purpose: the `PublishNightly` environment made scheduled runs sit `waiting`, hold the concurrency group, and silently cancel every later scheduled run behind them. A stale nightly listing is therefore expected, not a bug.
```bash
gh workflow run ext-vscode-publish-nightly.yml --ref main # real publish
gh workflow run ext-vscode-publish-nightly.yml --ref main -f dry-run=true # artifact only
```
No changelog/version prep — the version is computed. Verify with the Marketplace query against `saoudrizwan.cline-nightly`. Until converted, nightly still ships the combined loader VSIX with a `legacy/` bundle built from `legacy-extension`.
**Red run ≠ failed publish** on this path: its tag-push step runs *after* publishing and fails whenever main's HEAD touches `.github/workflows/**`. If "Published" appears in the logs, the release went out; push the `nightly-main-<UTC ts>-<sha12>` tag manually with user credentials.
## Emergency rollback
Preferred: **ship a fixed build of `main` at a higher version** through the stable workflow above. It is the same path, fully gated, and the only rollback that keeps users on the SDK extension.
Last resort — the legacy hotfix path — still exists but is degraded: `legacy-extension` is the pre-SDK npm codebase, last touched 2026-08-18 at `4.0.12`, and a publish from it would move every user back onto code that is weeks behind. If it is ever needed:
```bash
# On legacy-extension: commit the fix, bump apps/vscode/package.json ABOVE the
# live stable version (rule 1 — e.g. 4.1.18 live -> hotfix is 4.1.19, not 4.0.13),
# add the matching `## [x.y.z]` entry to root CHANGELOG.md, push.
gh workflow run ext-vscode-publish-legacy.yml --ref main -f release-type=release
# (the branch is hardcoded to legacy-extension in the workflow)
```
The npm suite runs ungated, the publish job waits on the `publish` environment, the workflow tags and creates the GitHub release itself, and it publishes to Marketplace **and** Open VSX. On that branch use `npm`, never `bun`, and expect the old monolith layout (`apps/vscode/src/core/...`).
## Retiring the A/B machinery (still to do)
The rollout is done but the scaffolding is still in the repo. Retire it in this order, each as its own PR:
1. **Nightly → plain build of `main`**: drop the loader/`legacy-src` stitching from `ext-vscode-publish-nightly.yml` so nightly matches stable. Preserve the `|| 'default'` fallbacks for `inputs.*` while editing.
2. Delete `ext-vscode-ab-package.yml` and, once the emergency path above is judged unnecessary, `ext-vscode-publish-legacy.yml`; keep the `legacy-extension` branch for history.
3. Remove `apps/vscode-rollout/` and the rollout-only code paths in `apps/vscode/src/services/telemetry/rollout-metadata.ts` (the `extension_variant` metadata and `extension.rollout.bundle_activated` event).
4. Optionally port the marketplace-monotonicity preflight from the old ab-package workflow into `ext-vscode-publish-stable.yml` — the only automated guard for rule 1 was retired with it.
5. **Archive the PostHog flag last, and not yet.** Machines still on a combined VSIX (`≤ 4.1.17`) consult `ext-sdk-bundle-rollout` on every window load and treat a *deleted* flag as `legacy`. Leave it at 100% until `extension.rollout.bundle_activated` for combined versions flatlines, then archive. Re-verify the percentage empirically before touching it (no PostHog admin needed — the key is inlined in any shipped combined loader; download the 4.1.17 VSIX from the Marketplace `vspackage` URL, `gunzip`, `unzip`, `grep -o 'phc_[A-Za-z0-9]*' extension/extension.js`):
```bash
node -e '
const KEY = process.argv[1]; // phc_... extracted from a shipped VSIX loader
const KEY = process.argv[1];
(async () => {
let t = 0, n = 200;
for (let i = 0; i < n; i += 20) {
@@ -58,129 +167,15 @@ All three publish paths gate on tests before publishing: nightly and ab-package
})()' "$KEY"
```
Flag changes are made in the PostHog UI (Cline project). **0% is the kill switch** — the flag is two-way; there is no separate killswitch flag. Dialing down demotes machines back to legacy on their next window reload.
3. **Ask before pushing** commits or tags. Environment approvals are the maintainer's to give.
4. **Changelog lives at the repo ROOT** (`CHANGELOG.md`), on the branch being released — not `apps/vscode/CHANGELOG.md` (doesn't exist). The legacy and stable workflows hard-fail unless the first heading is exactly `## [<version>]`.
5. **Stuck concurrency groups**: `ext-vscode-ab-package` groups on the version with `cancel-in-progress: false`. Only `publish=true` runs wait on environment approval (build-only rehearsals run ungated to completion), but a publish run left `waiting` still blocks every later dispatch of the same version — cancel it (`gh run cancel <id>`) before re-dispatching.
## Stable release (combined A/B VSIX) — the current stable path
### Pre-flight
```bash
# 1. What's live, and what version comes next (must exceed it — rule 1)
# 2. Flag percentage (rule 2) — decide where it should be for this release
# 3. Legacy tip = what the non-promoted cohort will run; confirm it's the shipped hotfix line
git fetch origin main legacy-extension
git log --oneline -3 origin/legacy-extension
# 4. Cheap local rehearsal of the most likely build failure: the union manifest
# hard-fails if views/viewsContainers/configuration diverged between branches.
git show origin/main:apps/vscode/package.json > /tmp/next.json
git show origin/legacy-extension:apps/vscode/package.json > /tmp/legacy.json
node apps/vscode-rollout/scripts/gen-manifest.mjs --next /tmp/next.json --legacy /tmp/legacy.json --version <VERSION>
# Expected warnings only: engines union (takes newer) + walkthrough copy drift.
```
Release prep on `main` (PR, not direct push):
- Add `## [<VERSION>]` entry at the top of root `CHANGELOG.md`.
- Bump `apps/vscode/package.json` to `<VERSION>` so the repo reflects the published line. Side effect: nightly versions become `<major>.<minor>.<unix-ts>` of the new base — harmless (separate listing, still monotonic).
### Dispatch
```bash
gh workflow run ext-vscode-ab-package.yml --ref main \
-f version=<VERSION> -f next-ref=main -f publish=true
# (the legacy bundle always builds from the protected legacy-extension branch;
# it is deliberately not an input)
# publish=false builds an installable .vsix artifact without publishing and
# needs NO environment approval — the ungated build job uploads the artifact
# and the run completes.
gh run list --workflow=ext-vscode-ab-package.yml --limit 1
```
Preflight (version format + monotonicity) and both test suites run first, then the ungated `build` job packages and uploads the VSIX; for `publish=true` the `publish` job then **waits for `Publish` environment approval** (Actions → run → "Review deployments"). Both bundles build the exact revisions their test gates ran against (branch names are resolved once — commits landing on either branch mid-run or during the approval wait are not picked up); `publish=true` is additionally refused for any `next-ref` other than `main` (the bun gate only tests main — non-main next-refs are for build-only artifact rehearsals). Check what a run is waiting on:
```bash
gh api repos/cline/cline/actions/runs/<run-id>/pending_deployments
```
### Post-publish
1. Verify the marketplace serves the new version (query from rule 1) — expect minutes-to-an-hour of validation lag after "Published" appears in the logs. Also verify Open VSX:
```bash
curl -s "https://open-vsx.org/api/saoudrizwan/claude-dev" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['version'], d['timestamp'])"
```
2. Tag, GitHub Release (with the .vsix attached), and the Slack release-bot post happen **automatically** after a real publish (all `continue-on-error` — the publish itself already succeeded, so bookkeeping failures leave the run green). Verify they landed; the known failure is the tag push when the built commit touches `.github/workflows/**` (default token cannot create such refs — no grantable permission fixes it). Manual fallback:
```bash
git tag v<VERSION> <main-sha-built> # ask before pushing
git push origin v<VERSION>
gh release create v<VERSION> --title "v<VERSION>" --notes "<changelog section>" <path-to.vsix>
```
A real publish also **hard-fails early** if root `CHANGELOG.md` on the built main revision doesn't start with `## [<VERSION>]` — the release prep PR must be merged before dispatching.
3. Thorough artifact check (`gh run download <run-id>`): union `package.json` is `saoudrizwan.claude-dev@<VERSION>`, `next/package.json` and `legacy/package.json` carry the SAME version, `grep -c 'phc_' extension/extension.js` ≥ 1 (loader key inlined), no leftover `process.env.TELEMETRY_SERVICE_API_KEY` / `process.env.CLINE_ROLLOUT_VARIANT` literals in either bundle's dist (leftovers = a build ran without its env and telemetry is silently dead).
4. Monitor: `extension.rollout.bundle_activated` in `otel.otel_logs` filtered to `extension_version = '<VERSION>'` (stable cohort is cleanly separable — nightly versions are timestamps). Watch the next/legacy ratio and the crash-fallback rate; Metabase dashboards 17 (rollout + task error rate) and 19 (error deep dive). `extension.rollout.loader_decision` (incl. `double_failure`) is PostHog-only, not in ClickHouse.
5. Dial the flag per the rollout plan (e.g. 0% at publish → 1% → up), verifying each change with the probe from rule 2. Announce demotions ahead of time — dialing down also demotes nightly dogfooders unless they set `"cline-nightly.rollout.bundleOverride": "next"`.
### Known caveats of this path
- **`engines.vscode` unions upward** (main's floor wins, e.g. `^1.101.0` vs legacy's `^1.84.0`): users on older VS Code are never offered the combined VSIX. Fail-safe during rollout; must be resolved before 100%.
- A red run can still mean a successful publish on paths that tag (see Gotchas).
## Nightly release
Happens automatically (cron 12:00 UTC). Manual cut:
```bash
gh workflow run ext-vscode-publish-nightly.yml --ref main # real publish
gh workflow run ext-vscode-publish-nightly.yml --ref main -f dry-run=true # artifact only
gh run watch <run-id> --exit-status --interval 60
```
No changelog/version prep — the version is computed. Verify with the marketplace query against `saoudrizwan.cline-nightly`.
**Red run ≠ failed publish**: the final tag-push step fails whenever main's HEAD touches `.github/workflows/**` (default token cannot create such refs). If "Published" appears in the logs, the release went out; push the `nightly-main-<UTC ts>-<sha12>` tag manually with user credentials.
## Legacy hotfix release (and emergency full rollback)
For shipping a fix on the `legacy-extension` branch — or as the **structural rollback** from a bad combined stable VSIX: a standalone legacy publish at a higher version supersedes the combined VSIX entirely (loader and all) for every user. (For "next bundle misbehaving" you don't need this — dial the flag to 0% instead.)
```bash
# On legacy-extension: commit the fix, bump apps/vscode/package.json ABOVE the
# highest version ever published to the listing (rule 1 — including combined
# versions, e.g. combined 4.1.0 live -> hotfix is 4.1.1, not 4.0.13),
# add the matching `## [x.y.z]` entry to root CHANGELOG.md, push.
gh workflow run ext-vscode-publish-legacy.yml --ref main \
-f release-type=release
# (the branch is hardcoded to legacy-extension in the workflow; it is
# deliberately not an input)
```
npm test suite runs ungated; the publish job waits on the `Publish` environment. This workflow derives + pushes the `v<version>` tag itself and creates the GitHub release — no manual tagging. Publishes to Marketplace **and** Open VSX. The branch is the npm codebase: use `npm`, never `bun`, and expect the old monolith layout (`apps/vscode/src/core/...`).
## Cutover: retiring the A/B machinery (the endgame)
When the next bundle has held at 100% long enough to trust:
1. **Resolve the engines floor**: decide whether stranding VS Code < main's `engines.vscode` on the last combined version is acceptable, or lower main's floor first.
2. Bump `apps/vscode/package.json` on `main` above everything ever published; root `CHANGELOG.md` entry to match (both are enforced by the workflow).
3. Ship standalone from main: `gh workflow run ext-vscode-publish-stable.yml --ref main` — tests main, tags `v<version>` itself, creates the GitHub release, publishes Marketplace + Open VSX.
4. Watch the same rollout telemetry through the transition — `extension_variant` disappears from events as users leave combined builds, which is itself the adoption signal.
5. Only after the standalone version dominates: retire `legacy-extension` (keep for history), delete `ext-vscode-publish-legacy.yml` and `ext-vscode-ab-package.yml`, convert the nightly workflow back to a plain build of main, remove `apps/vscode-rollout/`, and archive the `ext-sdk-bundle-rollout` flag in PostHog (harmless to machines still on a combined VSIX: absent flag fails safe to... nothing changing until they update, but their loader treats a deleted flag as legacy — leave the flag at 100% until combined-VSIX activations flatline, then archive).
6. Update this skill: delete the combined-era sections and keep the standalone flow.
6. Update this skill: delete this section and the combined-loader notes under Nightly.
## Gotchas index
- `inputs.*` are empty strings on `schedule` events — preserve `|| 'default'` fallbacks when editing the nightly workflow.
- `bun run package` in `apps/vscode` does not build `@cline/*` workspace deps — fresh checkouts need `bun run build:sdk` first (workflows handle this).
- `bun run package` in `apps/vscode` does not build `@cline/*` workspace deps — fresh checkouts need `bun run build:sdk` first (the workflows handle this).
- Every ext workflow pins `bun-version: 1.3.14` while the root `packageManager` is `bun@1.3.13`. This is consistent across all of them and has shipped fine — don't "fix" it in one workflow alone.
- The publish job pins **Node 22** on purpose: Node 24 / npm 11 can make vsce's `npm list` detection fail with `ELSPROBLEMS` during packaging. `setup-bun` provides no Node runtime, and the publish scripts and `npx ovsx` need one.
- `gh run watch --exit-status` has returned exit 0 on a failed run. Always confirm with `gh run view <id> --json status,conclusion` before acting on a result.
- Job-level `if:` ref checks in workflow YAML are advisory (a dispatched branch runs its own copy of the file); the enforced boundary is each environment's deployment-branch policy in repo settings.
- Marketplace PATs (`VSCE_PAT`/`OVSX_PAT`) are only mounted into publish steps; neither publish workflow has an untrusted trigger surface.
- Environment-approval runs left waiting don't time out quickly — they sit for days and (for ab-package publish runs) block their version's concurrency group.
- Local forcing for manual testing: `CLINE_BUNDLE_OVERRIDE=next|legacy` env (launch VS Code fresh from a terminal) or the `<prefix>.rollout.bundleOverride` setting + reload; both report as `override` in telemetry so they don't pollute cohort data.
- Marketplace PATs (`VSCE_PAT`/`OVSX_PAT`) are only mounted into publish steps; no publish workflow has an untrusted trigger surface.
- Environment-approval runs left waiting don't time out quickly — they sit for days and block their tag's concurrency group.
- Open VSX has held a first-time publish in moderation before (logs say "Published", API 404s for hours). Verify with the API query rather than the log line.
+36
View File
@@ -1,5 +1,41 @@
# Changelog
## [4.1.18]
This is the first release built solely from the SDK extension. Earlier 4.1.x releases shipped a combined package containing both the SDK extension and the previous one, with a rollout flag choosing between them per window; that migration is now complete, so the package contains only the SDK extension and is substantially smaller to download and install.
### Added
- Images attached to a model that cannot read them are now flagged instead of silently discarded. Thumbnails get a warning badge and the composer explains that the images will be ignored, with a button to switch to an image-capable model. Previously the thumbnail looked normal and the image was replaced with a text placeholder just before the request, so there was no way to tell it had been dropped. Model info and the attachment picker also report image support accurately for models that declare text-only input without listing capabilities.
### Fixed
- A model turn that fails mid-stream with a transient provider error is now retried up to three times with backoff instead of ending the task. A single rate-limit response forwarded by a gateway previously surfaced as a failed task. A turn that has already streamed output is never retried, so nothing is duplicated.
- Terminal commands that succeed without printing anything (`git add -A` on a clean tree, for example) are now reported as empty output. They were treated as a shell-integration failure, which fed the model a snapshot of unrelated terminal scrollback prefixed with a warning that the output could not be captured, so silent commands intermittently looked like failures.
- Checkpoints no longer re-hash every untracked file on each message. In workspaces holding large untracked directories this delayed every message by seconds to minutes; a persistent per-task index now lets git skip files it has already seen.
- `run_commands` no longer hangs until its timeout after a command that backgrounds a child process. The command had finished, but the backgrounded process held the output pipes open.
- `apply_patch` no longer silently overwrites an existing file when the model uses "Add File" on a path that already exists. The file's contents were replaced with no error and no record of what was lost.
- PowerShell commands the model wrapped in another `powershell -Command "..."` are no longer parsed twice. The outer shell consumed `$_` before the inner command ran, so pipelines using it emitted an error for every item processed while still reporting success.
- Opening your home directory as a workspace no longer drives the extension host to exhaustion. Typing an `@` mention indexed every file beneath it and re-ranked the whole index on each keystroke.
- The "Supports Images" checkbox in OpenAI Compatible model settings now stays where you put it. The checkbox rendered the last committed value, and the re-sync that arrived during the save round-trip was re-emitted as a change event that wrote the old value back over your edit.
- The error shown to the model when an `editor` call omits the text to replace now names the file and explains how to recover. The previous message was terse enough that some models re-sent the identical call until the task stopped.
- Credentials are now stripped of invisible characters when saved, not only when pasted, and the cleanup covers AWS, GCP, and SAP fields and custom header values in addition to API keys.
- Cline Pass now defaults to a model from your subscription rather than a free one. Its model list contains both tiers and the default was whichever model was published most recently, so a subscriber who never picked a model could be left on the free tier.
- Cline Pass and free models now show no cost rather than the underlying market price, which is not what you are billed for.
- Model pickers now fall back to the full Recommended, Free, and Subscribed lists when the models endpoint cannot be reached. The offline fallback was a short hardcoded list with no subscription tier at all.
- Model lists for providers sharing the built-in catalog now refresh from the live catalog, so newly published models appear without an extension update, with timeouts so an unresponsive provider endpoint cannot stall the list.
- Claude Code and OpenCode no longer ask for an API key they never read. Both authenticate from their own local CLI's credentials, but they were treated as key-based providers; the workaround was storing a dummy key. A missing CLI on `PATH` now warns rather than blocking, since a configured path or bundled binary also works.
- The OpenAI Codex (ChatGPT subscription) model list no longer offers models the backend rejects, and Codex context limits are applied to every Codex model instead of being inherited from the OpenAI API catalog, which inflated both the context budget and the usage figures derived from it.
- Models served by OpenCode Go are now sent over the wire protocol each one actually speaks. Every model was sent over the OpenAI chat-completions adapter, so models on that endpoint speaking other protocols failed or misbehaved.
- Task history no longer goes blank when a task spawns many subagents. Subagent rows crowded out the tasks that created them, hiding the parent task and everything older.
- Langfuse tracing, when configured, is now limited to the Cline and Cline Pass providers. The provider was ignored, so prompts and responses sent to third-party and bring-your-own-key providers were exported too.
### Changed
- Web search is now enabled by default on models that support it, outside YOLO mode. It can still be turned off in settings, and a settings file that cannot be read leaves it disabled rather than silently on.
- The `run_commands` tool description now names the PowerShell edition in use — `Windows PowerShell (powershell.exe)` versus `PowerShell (pwsh.exe)` — quotes its guidance against that executable, and tells the model to run commands directly rather than wrapping them in another shell invocation. It also no longer describes the environment as Windows when `pwsh` is the configured shell on macOS or Linux.
- Refreshed the built-in model catalog. Adds four providers (Infer by Flow7, Melious, NaN, and Wallaby) and takes the catalog from 5,788 to 6,079 models. This is a wide refresh: the resolved default model changes for 44 providers, most of them landing on DeepSeek V4.1 Flash — among them Hugging Face, Fireworks, Requesty, Nebius, Cortecs, CrossModel, DigitalOcean, Eden AI, and OpenCode Go. Gemini and Vertex now resolve to Gemini 3.8 Flash, GitHub Copilot and Vivgrid to GPT-6 Astra, and NVIDIA to GLM 5.3 Flash. If you use a provider without pinning a model, expect a different default.
## [4.1.17]
Everything here lands through the SDK bundle, so it applies to windows running that bundle.
+12
View File
@@ -1,5 +1,17 @@
# Cline Desktop Changelog
## 0.0.28
- The app no longer gets stuck on "Desktop backend unavailable" when the backend is slow to start. The window asked for the backend's address exactly once and the shell stops waiting after about 15 seconds, so on a slower machine — where starting the hub pushed past that — you were left on the error screen until you relaunched, and the relaunch could lose the same race. Connection attempts now keep retrying and re-resolve the address each time, so a backend that has since restarted is reached at its current one
- Confirming text with a Chinese or Japanese IME no longer sends the message. The Enter that commits an in-progress composition also reached the composer, so choosing a candidate fired off a half-typed message; Enter and the arrow keys now belong to the IME while you are composing
- The model picker refreshes its list every time you open it, instead of staying frozen until you restart the app. The Recommended and Free groupings come from a live feed, so a newly promoted free model would otherwise not show up for the rest of the session
- Signing in with Cline now explains what the plan includes. The sign-in card lists the regular free model promotions, ClinePass for generous usage across open-weight models like DeepSeek, Kimi, and GLM, and that no API key is needed; the last onboarding step then shows the current free models alongside a ClinePass summary. This only appears when you sign in with Cline, not when you bring your own API key
- Cline Pass now starts you on a model from your subscription. Its model list carries both subscription and free models and the default was simply the most recently published entry, so a subscriber who never picked a model was left on a free model instead of the tier they pay for
- A turn that fails partway through with a temporary provider error is now retried up to three times instead of failing the task. A single rate-limit response passed along by a provider gateway previously ended the turn outright. A turn that has already produced output is never retried, so nothing is duplicated
- Long responses stream faster. Every chunk of a streaming reply was being handed to client-side hooks as a round trip carrying a full copy of the session, with the agent waiting on each one before continuing
- The command-running tool now tells the model which PowerShell edition it is talking to — `Windows PowerShell (powershell.exe)` versus `PowerShell (pwsh.exe)` — and to write commands directly rather than wrapping them in a second shell invocation, which was corrupting pipelines that use `$_`
- Refreshed the model catalog. Adds three providers (Infer by Flow7, Melious, and Wallaby) and takes the catalog from 5,923 to 6,079 models. The resolved default model changes for 33 providers, most of them landing on DeepSeek V4.1 Flash — among them Hugging Face, Fireworks, Requesty, Cortecs, CrossModel, DigitalOcean, Eden AI, and OpenCode Go — while NVIDIA moves to GLM 5.3 Flash and NanoGPT to GPT Astra. If you use a provider without pinning a model, expect a different default
## 0.0.27
- An expired Cline sign-in now produces one actionable error instead of two dead ends. A turn that fails before the runtime takes the prompt was reported twice — the hub's detail-less `run.failed` appended a bubble, then the send RPC resolved with the real error and overwrote the error banner underneath it — so you got a vague bubble stacked on a detailed banner. The failure now renders exactly once, upgrading in place when the detailed report arrives. Credential failures also carry a fix button: **Sign in to Cline** for the Cline provider (Settings → API Providers reads a stale token as "Signed in via browser", so there is nothing to fix there), **Open model settings** for others, and local-CLI providers keep pointing at their CLI. Sessions that fail to start over credentials get the same hint and action
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@cline/code",
"version": "0.0.27",
"version": "0.0.28",
"private": true,
"scripts": {
"build:ui": "bun -F @cline/ui build",
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Cline",
"version": "0.0.27",
"version": "0.0.28",
"identifier": "bot.cline.app",
"build": {
"beforeDevCommand": "bun run build:sidecar:bin && bun run dev:web",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "claude-dev",
"displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
"version": "4.1.17",
"version": "4.1.18",
"icon": "assets/icons/icon.png",
"engines": {
"vscode": "^1.101.0"
+2 -2
View File
@@ -165,7 +165,7 @@
},
"apps/examples/desktop-app": {
"name": "@cline/code",
"version": "0.0.25",
"version": "0.0.28",
"dependencies": {
"@base-ui/react": "^1.2.0",
"@cline/core": "workspace:*",
@@ -359,7 +359,7 @@
},
"apps/vscode": {
"name": "claude-dev",
"version": "4.1.17",
"version": "4.1.18",
"dependencies": {
"@anthropic-ai/sdk": "^0.37.0",
"@bufbuild/protobuf": "^2.2.5",