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
- 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.
- 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.
- 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.
Summary
Script.channelfalls back togit 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
How it presents (this cost an afternoon)
Built
integration-v2from a jj-colocated checkout and installed it. The service then failed to boot: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:
""→ the binary resolved toopencode-.db, a stale 55-migration profile last written 2026-08-12, instead ofopencode-integration-v2.db(69 migrations, healthy).session_pending_session_delivery_seq_idxbut not theloose_psylockeledger row, so the migration re-ran and collided.OPENCODE_DBset: HTTP 200, 547 models, one migration applied (20260824080928_session_dynamic_tools, 4 ms), clean log.Same root cause for the service file: channel
""writes/readsservice-.jsonrather thanservice-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
git branch --show-currentis empty, fall back before giving up — e.g.jj log -r @ -T 'bookmarks' --no-graph(orgit config jj.*/jj bookmark list -r @), thengit rev-parse --abbrev-ref HEAD."". An empty channel is never a deliberate choice, and it silently forks a user's data directory.20260804233008_loose_psylockecreates an index unconditionally.CREATE INDEX IF NOT EXISTSwould make a mixed-history database survivable rather than fatal.Environment: Arch Linux,
integration-v2@4668dfcd, jj-colocated checkout, bun build--single.