Skip to content

Build channel detection returns empty under jj (detached HEAD), silently selecting the wrong profile database #361

Description

@shuv1337

Summary

Script.channel falls back to git branch --show-current. In a jj-colocated checkout the working copy is a detached HEAD, so git prints nothing, the channel is baked in as the empty string, and the resulting binary silently uses a different profile database and service file than the one the branch's builds normally use.

https://github.com/Latitudes-Dev/shuvcode/blob/integration-v2/packages/script/src/index.ts#L27-L32

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.startsWith("0.0.0-")) return "latest"
  return await $`git branch --show-current`.text().then((x) => x.trim())   // <-- empty under jj
})()

How it presents (this cost an afternoon)

Built integration-v2 from a jj-colocated checkout and installed it. The service then failed to boot:

database migration started  migration=20260804233008_loose_psylocke
background service boot failed  cause="Cause([Die(SQLiteError: index session_pending_session_delivery_seq_idx already exists)])"

The obvious reading — "my profile database is corrupt, the migration ledger and schema disagree" — is wrong, and it sends you straight into ledger forensics on a 1 GB database. What actually happened:

  • channel "" → the binary resolved to opencode-.db, a stale 55-migration profile last written 2026-08-12, instead of opencode-integration-v2.db (69 migrations, healthy).
  • That stale database happens to have session_pending_session_delivery_seq_idx but not the loose_psylocke ledger row, so the migration re-ran and collided.
  • The intended database was fine the whole time. Booting the same binary against it with OPENCODE_DB set: HTTP 200, 547 models, one migration applied (20260824080928_session_dynamic_tools, 4 ms), clean log.

Same root cause for the service file: channel "" writes/reads service-.json rather than service-integration-v2.json, so port and password discovery also diverge from the running deployment.

Workaround: OPENCODE_CHANNEL=integration-v2 bun run --cwd packages/cli build --single.

Why it is worth fixing rather than documenting

The failure is silent and misdirecting. Nothing warns that the channel is empty; you get a database-corruption error message for what is actually a build-metadata problem, against a database that is not the one you were looking at.

Suggested fix

  1. When git branch --show-current is empty, fall back before giving up — e.g. jj log -r @ -T 'bookmarks' --no-graph (or git config jj.* / jj bookmark list -r @), then git rev-parse --abbrev-ref HEAD.
  2. If the channel still resolves to empty, fail the build loudly (or warn hard) instead of embedding "". An empty channel is never a deliberate choice, and it silently forks a user's data directory.
  3. Independently: 20260804233008_loose_psylocke creates an index unconditionally. CREATE INDEX IF NOT EXISTS would make a mixed-history database survivable rather than fatal.

Environment: Arch Linux, integration-v2 @ 4668dfcd, jj-colocated checkout, bun build --single.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions