|
| 1 | +--- |
| 2 | +description: Cut a BETA (prerelease) of altimate-code to the npm `beta` channel. Existing `latest` users are NOT auto-upgraded — only opt-in beta testers get it. Use to soak a risky change (e.g. a big upstream merge) before promoting to `latest` with /release. |
| 3 | +--- |
| 4 | + |
| 5 | +# Release altimate-code BETA |
| 6 | + |
| 7 | +Cut a prerelease to the **`beta`** npm dist-tag. The whole point: **existing |
| 8 | +stable (`latest`) users are never touched.** Only people who opted into the |
| 9 | +beta channel (`npm i -g @altimateai/altimate-code@beta`, or a beta install) |
| 10 | +receive it. Use this to dogfood a risky release before promoting it to |
| 11 | +`latest` with `/release`. |
| 12 | + |
| 13 | +## Why this is a separate skill (read once) |
| 14 | + |
| 15 | +- `/release` tags a plain `vX.Y.Z` → the workflow publishes to **`latest`** → |
| 16 | + **every existing user auto-upgrades on next launch.** For a big/risky change |
| 17 | + that is exactly the "brick everyone if there's a bug" risk. |
| 18 | +- A beta tag has a `-` suffix (`vX.Y.Z-beta.N`). `.github/workflows/release.yml` |
| 19 | + derives `OPENCODE_CHANNEL` from the tag: a `-` → the **`beta`** channel → |
| 20 | + `npm publish --tag beta`. The `latest` dist-tag is left pointing at the old |
| 21 | + stable, so `latest` users do not move. |
| 22 | +- The upgrade check (`cli/upgrade.ts`) is fail-safe and orders prerelease |
| 23 | + identifiers, so a beta tester on `beta.1` auto-upgrades to `beta.2`, and any |
| 24 | + beta upgrades to the eventual stable. This is verified by the round-trip below. |
| 25 | + |
| 26 | +## Input |
| 27 | + |
| 28 | +`$ARGUMENTS` = the target STABLE version this beta leads to (`patch` | `minor` | |
| 29 | +`major`, or an explicit `X.Y.Z`). Default `minor` — a big upstream merge is not |
| 30 | +a patch. The beta tag becomes `vX.Y.Z-beta.N`. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Step 1 — Determine the beta version |
| 35 | + |
| 36 | +```bash |
| 37 | +# current published STABLE (the latest dist-tag) |
| 38 | +npm view @altimateai/altimate-code dist-tags --json |
| 39 | +``` |
| 40 | + |
| 41 | +- Base stable `X.Y.Z` from `$ARGUMENTS`: |
| 42 | + - `patch`/empty → not typical for a beta; prefer `minor`. |
| 43 | + - `minor` → bump minor of the current stable (0.8.10 → **0.9.0**). |
| 44 | + - `major` → 0.8.10 → **1.0.0**. |
| 45 | + - explicit `X.Y.Z` → use it. |
| 46 | +- Find the next `-beta.N`: look at the current `beta` dist-tag. If it is already |
| 47 | + `X.Y.Z-beta.M`, next is `beta.(M+1)`; otherwise start at `beta.1`. |
| 48 | + |
| 49 | +```bash |
| 50 | +# does a beta for this base already exist? |
| 51 | +npm view @altimateai/altimate-code@beta version 2>/dev/null || echo "no beta yet" |
| 52 | +``` |
| 53 | + |
| 54 | +Confirm with the user: **"Cutting beta `vX.Y.Z-beta.N`. This publishes to the |
| 55 | +`beta` channel only — `latest` stays at `<current stable>`, so existing users |
| 56 | +are NOT auto-upgraded. Proceed?"** Wait for an explicit yes. |
| 57 | + |
| 58 | +## Step 2 — Ensure clean, correct base |
| 59 | + |
| 60 | +```bash |
| 61 | +git branch --show-current # expect main (or the branch you're betaing) |
| 62 | +git status --short # must be clean |
| 63 | +git fetch origin |
| 64 | +git log HEAD..origin/$(git branch --show-current) --oneline # must be up to date |
| 65 | +``` |
| 66 | +Stop if dirty, behind, or on an unexpected branch. |
| 67 | + |
| 68 | +## Step 3 — Pre-tag gates (run the SAME local gates as /release — non-negotiable) |
| 69 | + |
| 70 | +The npm publish + Verdaccio + size checks only run in the release workflow AFTER |
| 71 | +the tag, and the tag→publish is irreversible. So you MUST reproduce those gates |
| 72 | +LOCALLY before tagging. A green PR check is NOT sufficient — `ci.yml` uses |
| 73 | +`dorny/paths-filter` and only typechecks CHANGED packages, so it misses whole-repo |
| 74 | +issues the release workflow (full `bun turbo typecheck` + Verdaccio + npm publish) |
| 75 | +will hit. Do NOT skip any of these: |
| 76 | + |
| 77 | +```bash |
| 78 | +# 1. FULL monorepo typecheck (what the release workflow runs — clean install to match CI) |
| 79 | +rm -rf node_modules && bun install --frozen-lockfile |
| 80 | +bun turbo typecheck --force # all packages, not just changed ones |
| 81 | + |
| 82 | +# 2. Mandatory pre-release sanity (restored gate; builds a binary that starts) |
| 83 | +(cd packages/opencode && bun run pre-release) |
| 84 | + |
| 85 | +# 3. Package SIZE check — npm rejects platform tarballs over ~200MB compressed (E413). |
| 86 | +# Build one platform, measure the packed size BEFORE tagging. |
| 87 | +(cd packages/opencode && bun run build:local) |
| 88 | +DIST=$(find packages/opencode/dist -type d -name '*'"$(uname -m | sed s/arm64/arm64/)"'*' | head -1) |
| 89 | +(cd "$DIST" && npm pack --dry-run --json | python3 -c "import sys,json;d=json.load(sys.stdin)[0];mb=d['size']/1048576;print(f'compressed {mb:.0f}MB', 'OK' if mb<190 else 'TOO BIG — will 413')") |
| 90 | + |
| 91 | +# 4. marker guard |
| 92 | +bun run script/upstream/analyze.ts --markers --base main --strict |
| 93 | + |
| 94 | +# 5. Local Verdaccio sanity IF docker + a native-platform build are available |
| 95 | +# (the docker image is linux; on a mac you cannot cross-build the linux NAPI dist, |
| 96 | +# so this validates the current platform only — CI covers the rest): |
| 97 | +# (cd packages/dbt-tools && bun run build) && docker compose \ |
| 98 | +# -f test/sanity/docker-compose.verdaccio.yml up --build --abort-on-container-exit --exit-code-from sanity |
| 99 | +``` |
| 100 | + |
| 101 | +If ANY gate is red, stop and fix BEFORE tagging. The whole point of these local |
| 102 | +gates is that a release-workflow failure after the tag is irreversible-adjacent |
| 103 | +(partial npm publishes, orphan sub-packages that block a same-version retry). |
| 104 | + |
| 105 | +## Step 4 — Tag and push the beta |
| 106 | + |
| 107 | +The `-beta.N` suffix is what routes to the beta channel. Do NOT omit it. |
| 108 | + |
| 109 | +```bash |
| 110 | +BETA_TAG="vX.Y.Z-beta.N" |
| 111 | +git tag "$BETA_TAG" |
| 112 | +git push origin "$BETA_TAG" # push the TAG (not necessarily main) |
| 113 | +``` |
| 114 | + |
| 115 | +Note: unlike `/release`, do not `git push origin main` unless main already |
| 116 | +contains this commit. A beta can be tagged on a branch or on main; the tag is |
| 117 | +what triggers the workflow. |
| 118 | + |
| 119 | +## Step 5 — Monitor the release workflow |
| 120 | + |
| 121 | +```bash |
| 122 | +gh run list --workflow=release.yml --repo AltimateAI/altimate-code --limit 1 |
| 123 | +gh run watch --repo AltimateAI/altimate-code |
| 124 | +``` |
| 125 | +The workflow's native linux-x64 smoke + pre-publish smoke are the last gates. |
| 126 | +If it fails, do NOT delete the tag — investigate (`gh run view --log-failed`). |
| 127 | + |
| 128 | +## Step 6 — CRITICAL post-publish guard: confirm `latest` did NOT move |
| 129 | + |
| 130 | +This is the safety assertion. After publish: |
| 131 | + |
| 132 | +```bash |
| 133 | +npm view @altimateai/altimate-code dist-tags --json |
| 134 | +``` |
| 135 | + |
| 136 | +- `beta` MUST now be `X.Y.Z-beta.N`. |
| 137 | +- `latest` MUST be UNCHANGED (still the previous stable, e.g. 0.8.10). |
| 138 | + |
| 139 | +If `latest` moved to the beta version, **the beta leaked to all users** — treat |
| 140 | +as an incident: publish a corrected `latest` dist-tag back to the last-good |
| 141 | +stable immediately (`npm dist-tag add @altimateai/altimate-code@<good> latest`) |
| 142 | +and investigate the channel derivation in release.yml. |
| 143 | + |
| 144 | +## Step 7 — Verify the beta works AND can upgrade out (round-trip) |
| 145 | + |
| 146 | +The whole reason for a beta is to prove the new codebase is safe — including |
| 147 | +its own auto-upgrade — before stable users touch it. |
| 148 | + |
| 149 | +```bash |
| 150 | +# install the beta hermetically (does not touch your normal install if you use a temp prefix) |
| 151 | +npm i -g @altimateai/altimate-code@beta # or an isolated prefix |
| 152 | +altimate --version # reports X.Y.Z-beta.N |
| 153 | +altimate agent list; altimate skill list # fork surfaces load |
| 154 | +``` |
| 155 | + |
| 156 | +Dogfood real workflows on the beta. Then prove the **round-trip**: when you cut |
| 157 | +`vX.Y.Z-beta.(N+1)`, a machine already on `beta.N` must auto-upgrade to it on |
| 158 | +next launch (compareVersions orders betas). If it does, the updater on the new |
| 159 | +codebase is proven — stable users can safely be promoted onto it. |
| 160 | + |
| 161 | +## Step 8 — Promote to `latest` (separate, deliberate step) |
| 162 | + |
| 163 | +Only after the beta has soaked and the round-trip is proven: |
| 164 | + |
| 165 | +- Run **`/release {same bump}`** to cut the stable `vX.Y.Z` → `latest`. That is |
| 166 | + the step that auto-upgrades existing users — and by then you've proven the |
| 167 | + exact code and its updater on the beta channel. |
| 168 | +- Do NOT move `latest` to a `-beta` version by hand; ship a clean stable tag. |
| 169 | + |
| 170 | +--- |
| 171 | + |
| 172 | +## Hard rules |
| 173 | + |
| 174 | +- The tag MUST contain `-beta.N`. A plain `vX.Y.Z` from this skill would hit |
| 175 | + `latest` — never do that here. |
| 176 | +- Never skip Step 6 (the `latest`-didn't-move assertion). It is the one check |
| 177 | + that catches a channel-routing regression before it bricks everyone. |
| 178 | +- npm publishes are effectively irreversible — get the explicit user yes at |
| 179 | + Step 1 before tagging, and never publish credentials or move `latest` without |
| 180 | + intent. |
0 commit comments