Skip to content

feat: opt-in jujutsu (jj) backend for workspace isolation #37

@frits-v

Description

@frits-v

Summary

Add opt-in jujutsu (jj) support as an alternative VCS backend for muzzle's session isolation. When a repo has .jj/, muzzle uses jj workspace commands instead of git worktree. Git remains the default; jj is activated by auto-detection or explicit opt-in.

Research

Full feasibility analysis: .agents/research/2026-03-23-jujutsu-feasibility.md

Motivation

jj's architecture eliminates several categories of complexity muzzle currently handles:

  • 5 of 8 git safety regex patterns become unnecessary — immutable_heads() enforces branch protection at the VCS level
  • Ephemeral branch management disappears — jj workspaces aren't branch-bound
  • Dirty worktree handling simplified — conflicts are first-class commits, not blocking states
  • Lost work on crash eliminated — jj auto-snapshots the working copy
  • Worktree prune/retry logic removed — jj workspace management has no stale metadata issues
  • Operation log provides a free audit trail with point-in-time rollback

Design

VcsBackend trait

Extract current git logic behind a VcsBackend trait, then implement a JjBackend:

trait VcsBackend {
    fn workspace_add(&self, repo: &Path, dest: &Path, rev: Option<&str>) -> Result<()>;
    fn workspace_remove(&self, repo: &Path, name: &str, force: bool) -> Result<()>;
    fn workspace_list(&self, repo: &Path) -> Result<Vec<WorkspaceInfo>>;
    fn is_clean(&self, path: &Path) -> Result<bool>;
    fn fetch(&self, repo: &Path) -> Result<()>;
    fn default_branch(&self, repo: &Path) -> Result<String>;
    fn check_safety(&self, command: &str, session: &Session) -> Result<Verdict>;
}

Auto-detection

.jj/ only        → JjBackend (non-colocated)
.jj/ + .git/     → JjBackend (colocated, set GIT_DIR for gh CLI, jj git export before gh ops)
.git/ only       → GitBackend (current behavior)

Colocated workaround

jj workspace add in colocated repos creates non-colocated children. Muzzle handles this by:

  • Setting GIT_DIR + GIT_WORK_TREE env vars pointing to parent's .git/
  • Running jj git export before gh CLI invocations

Phased implementation

  1. Phase 1: Extract VcsBackend trait from current git code (pure refactor)
  2. Phase 2: Implement JjBackend via Command::new("jj") CLI calls
  3. Phase 3: Auto-detection in session_start.rs and ensure_worktree.rs
  4. Phase 4: Colocated mode (GIT_DIR injection, jj git export hook)

Acceptance criteria

  • VcsBackend trait with GitBackend and JjBackend implementations
  • Auto-detect VCS type from .jj/ vs .git/ presence
  • jj workspace create/remove/list operations work for session isolation
  • jj-specific safety checks (leverage immutable_heads(), block jj git push to protected bookmarks)
  • Colocated repos: GIT_DIR injection + jj git export for gh CLI compatibility
  • Git remains default; jj is opt-in via detection
  • All existing git tests continue to pass
  • New tests for jj backend (unit + integration)
  • mise run ci passes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions