Submit an issue View all issues Source
MIR-1816

miren deploy silently ships no git provenance from a jj workspace

Done Bug public
phinze phinze Opened Sep 10, 2026 Updated Sep 13, 2026

A deploy run from a jj workspace records no commit, branch, author, message, or dirty state. The deploy succeeds and nothing warns, so the loss is invisible until you go looking for the commit later and it isn't there.

What happens

miren deploy collects git info client-side via git.GetInfo(dir) (cli/commands/deploy.go:365). That gates on isGitRepo, which shells out to git rev-parse --git-dir (pkg/git/info.go:117). A non-colocated jj workspace has .jj/ and no .git/, so the command fails, GetInfo returns "not a git repository", and the caller does:

if gitErr != nil {
    ctx.Log.Debug("Failed to get git info", "error", gitErr)
    // Don't fail deployment if git info is unavailable
}

Debug, so nothing surfaces at normal log level. The deployment record is created with an empty GitInfo and that is what reaches cloud.

Confirmed in prod

The three most recent metrics deploys on garden, read from cloud's projection:

operation=build  source_kind=dockerfile  deploy git=(none)  version git=(none)

deployed_by is populated on the same records, so this is not a stale runtime or a contract problem. Identity capture works; git collection is what fails.

Why it matters more now

MIR-1562 put a deployment detail view in the console, so the gap is now visible: the Commit section is absent, and the COMMIT column in deployment history is a dash. Cloud renders that correctly, since it genuinely has nothing, but the user has no way to tell "this deploy has no commit" from "the console can't show it".

Blast radius grows on its own. Rig workspaces are non-colocated jj workspaces by construction, so every deploy anyone makes from a rig loses provenance.

Two fixes, probably both

Make it loud. Debug to Warn, so a deploy shipping without provenance says so at the time, when the person can still do something about it. Small, and worth doing regardless of the second.

Teach pkg/git about jj. jj log -r @ yields a real git commit id and the bookmarks pointing at it, which covers sha and branch. Author, message and timestamp come from the same template. Dirty needs a decision: jj's @ already contains the working copy, so "uncommitted changes" does not map cleanly, and the honest answer may be to leave is_dirty unset rather than assert either value. Note that leaving it unset is already meaningful downstream, since cloud stores it three-valued precisely so that "we were not told" stays distinct from "the tree was clean".