Channel fallback can publish a prerelease tag to the `latest` dist-tag
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
Research direction
Start in packages/script/src/index.ts:25-30 and inspect the OPENCODE_VERSION fallback, then compare it with .github/workflows/release.yml at lines 98, 338, and 344. Review publish.ts and the release post-publish assertion to understand the affected dist-tags. Done means prerelease versions use beta, 0.0.0 previews still fall through to the branch channel, and stable versions continue using latest.
Written by the indexing model from the issue text.
Description
Problem
If OPENCODE_CHANNEL is ever empty when publish.ts runs, a prerelease tag publishes to the latest dist-tag and auto-upgrades every existing stable user onto a beta.
The channel fallback in packages/script/src/index.ts:25-30 is not prerelease-aware:
const CHANNEL = await (async () => {
if (env.OPENCODE_CHANNEL) return env.OPENCODE_CHANNEL
if (env.OPENCODE_BUMP) return "latest"
if (env.OPENCODE_VERSION && !env.OPENCODE_VERSION.replace(/^v/, "").startsWith("0.0.0-")) return "latest"
return await $`git branch --show-current`.text().then((x) => x.trim())
})()
Failure case
.github/workflows/release.yml:338 publishes with OPENCODE_VERSION: ${{ github.ref_name }}. For a beta tag that is v0.10.0-beta.1. If OPENCODE_CHANNEL did not reach the step:
env.OPENCODE_CHANNELis empty → skippedOPENCODE_BUMPunset → skippedOPENCODE_VERSION=v0.10.0-beta.1→.replace(/^v/, "")→0.10.0-beta.1→ does not start with0.0.0-→ returnslatest
All three npm publish calls then run --tag latest, and the latest dist-tag moves to a prerelease. Every existing user auto-upgrades to the beta on next launch — the exact outcome the beta channel exists to prevent.
Why it has not happened
release.yml:98 and :344 set OPENCODE_CHANNEL to ${{ contains(github.ref_name, '-') && 'beta' || 'latest' }}, which always evaluates to one of those two strings and never to empty. Verified: there is exactly one publish step, and it carries the variable in its own env: block. All three npm publish calls in publish.ts pass --tag ${Script.channel}; none publishes bare.
So today the hazard is masked entirely by one workflow expression. Nothing in the fallback itself prevents it.
Why it is worth fixing anyway
The blast radius is the whole user base, and the documented recovery (npm dist-tag add @altimateai/altimate-code@<good> latest) requires npm publish credentials that most of the team does not hold — so detection without the ability to remediate is thin protection. The release process makes "confirm latest did not move" a mandatory post-publish assertion precisely because this class of mistake is unrecoverable for most people who would hit it.
Defence in depth: make the fallback refuse to route a prerelease version to latest, so the safety does not rest solely on an env var reaching a step.
Fix
In the OPENCODE_VERSION branch, treat a version carrying a semver prerelease component as beta rather than latest. The 0.0.0- preview path must keep falling through to the branch-name channel, so the guard is scoped to the branch that already excludes it.
- Dominant language
- TypeScript
- Stars
- 813
- Forks
- 134
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 62
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from AltimateAI/altimate-code
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
AltimateAI/altimate-code#1323 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
AltimateAI/altimate-code#1288 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
AltimateAI/altimate-code#1285 ·
-
privacy: Altimate Base consent dialog no longer discloses persistent per-installation identifier Open
Difficulty 1/5 Under an hour Newbie friendliness 88/100
AltimateAI/altimate-code#1284 ·
-
Difficulty 2/5 Under an hour Newbie friendliness 72/100
AltimateAI/altimate-code#1283 ·
All issues in AltimateAI/altimate-code
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug clawsweeper:linked-pr-open clawsweeper:needs-live-repro clawsweeper:no-new-fix-pr impact:message-loss issue-rating: 🐚 platinum hermit P2 regression
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
fullcalendar/fullcalendar#8106 ·