Channel fallback can publish a prerelease tag to the `latest` dist-tag

Open Beginner friendly
#1,233 1 comment 0 reactions 0 assignees View on GitHub

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
Domain
ci-cd, release

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:

  1. env.OPENCODE_CHANNEL is empty → skipped
  2. OPENCODE_BUMP unset → skipped
  3. OPENCODE_VERSION = v0.10.0-beta.1.replace(/^v/, "")0.10.0-beta.1 → does not start with 0.0.0-returns latest

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from AltimateAI/altimate-code

All issues in AltimateAI/altimate-code

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.