Skip to content

pkrusche/jj-parallel-agents-skill

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jj-workspace-isolation

A Claude Code skill that isolates parallel subagent work in Jujutsu (jj) repositories using jj workspaces — the jj-native alternative to git worktree.

⚠️ AI Use: Created with the help of Claude

Why

git worktree doesn't work inside jj-managed repos (secondary worktrees have no .jj/, so jj commands fail). jj workspace is the correct primitive, but the ergonomics around multi-agent workflows (safe placement, base revision checks, sealed-commit conventions, safe teardown) require enough boilerplate that a skill + helper scripts make it worth packaging.

Requirements

  • jj ≥ 0.23 installed and on PATH
  • A jj-managed repository (.jj/ directory present, or jj root succeeds)

Installation

Install directly from GitHub (recommended):

# Project-local (skill available only in this repo)
git clone --depth=1 https://github.com/pkrusche/jj-parallel-agents-skill .claude/skills/jj-workspace-isolation

# Global (skill available in every repo)
git clone --depth=1 https://github.com/pkrusche/jj-parallel-agents-skill ~/.claude/skills/jj-workspace-isolation

Or copy from a local clone:

# Project-local
install -D SKILL.md .claude/skills/jj-workspace-isolation/SKILL.md && install -Dt .claude/skills/jj-workspace-isolation/scripts/ scripts/*.sh

# Global
install -D SKILL.md ~/.claude/skills/jj-workspace-isolation/SKILL.md && install -Dt ~/.claude/skills/jj-workspace-isolation/scripts/ scripts/*.sh

Claude Code loads SKILL.md automatically; no further configuration is needed.

.agents symlink (optional)

Some tooling uses .agents/ as the config directory instead of .claude/. Create a symlink so both names resolve to the same place:

ln -s .claude .agents

Add .agents to your .gitignore (the symlink itself should not be committed — it's a local convenience):

echo '.agents' >> .gitignore

Installing into a jj-managed repo

When you git clone into a directory that is already tracked by jj, jj will not snapshot .claude/ (or .agents/) provided those paths are listed in your .gitignore. Add them before cloning so jj never picks them up:

echo '/.claude/' >> .gitignore
echo '/.agents/' >> .gitignore   # if using the symlink
jj describe -m "ignore agent config dirs" && jj new
git clone --depth=1 https://github.com/pkrusche/jj-parallel-agents-skill .claude/skills/jj-workspace-isolation

If you cloned first and .claude/ is already tracked, untrack it:

jj file untrack .claude/
echo '/.claude/' >> .gitignore

What the skill does

When invoked — either explicitly or when Claude detects a jj repo and a parallel-agent task — it:

  1. Creates one isolated jj workspace per subagent via scripts/spawn-workspace.sh.
  2. Instructs each subagent to work only inside its workspace and to seal its result with jj describe -m "..." && jj new.
  3. Integrates finished work back onto the main branch using jj new/jj rebase.
  4. Cleans up workspaces via scripts/cleanup-workspaces.sh, refusing to discard unsealed work without -f.

Scripts

Both scripts must be run from the main (default) workspace.

scripts/spawn-workspace.sh

Usage: spawn-workspace.sh [-r REVSET] [-d WS_PARENT_DIR] NAME...

Creates one jj workspace per NAME under <repo-root>/../<repo-name>-ws/ (sibling of the repo, so workspaces are never snapshotted). Prints a machine-readable WORKSPACE name=... path=... base=... line per workspace on stdout.

# Create two workspaces based on main
scripts/spawn-workspace.sh -r main agent-auth agent-search

scripts/cleanup-workspaces.sh

Usage: cleanup-workspaces.sh [-f] [-d WS_PARENT_DIR] NAME...
       cleanup-workspaces.sh [-f] [-d WS_PARENT_DIR] -p PREFIX

Forgets each workspace and removes its directory. Refuses to remove workspaces with unsealed work unless -f is passed. Never removes the default workspace.

scripts/cleanup-workspaces.sh agent-auth agent-search
scripts/cleanup-workspaces.sh -p agent-    # all workspaces with this prefix

Key conventions

  • Sibling placement — workspaces live at <repo-root>/../<repo-name>-ws/<name> by default, keeping them outside the working copy and away from jj's snapshot.
  • Explicit base revision — always pass -r <revset> in local-only repos; without a remote main/master/trunk bookmark, trunk() falls back to root() and the scripts reject this.
  • Sealed commits — agents must end with jj describe -m "..." && jj new so their result is a stable, addressable commit (<name>@-).
  • No git in secondary workspaces — secondary workspaces have only .jj/, no .git/; git-dependent tooling won't work inside them.

End-to-end tests

The evals/ directory contains a harness that runs the skill against a live Claude agent and checks the result deterministically — no LLM judge.

What is tested

Case Trigger Gated
parallel-todo-merge Implicit — skill must auto-invoke from context No (monitoring)
parallel-todo-merge-explicit Explicit /jj-workspace-isolation in prompt Yes (CI gate)

Each case seeds a jj repo with a TODO.md listing three parallel file-creation tasks, then runs the agent and verifies:

  • Skill was discovered at session start (system/init slash_commands)
  • spawn-workspace.sh or jj workspace add was called (from a hook-based command log)
  • git worktree was never called (hard-denied at the hook level, exits 2)
  • All three result files exist in the working copy
  • Agent workspaces were cleaned up (only default remains in jj workspace list)
  • Working copy is clean (@ & empty())

Running

# Requires an authenticated claude CLI (ANTHROPIC_API_KEY or OAuth session)
bash evals/run_e2e.sh --trials 1 --case parallel-todo-merge-explicit

# Full 3-trial run of both cases
bash evals/run_e2e.sh

# Options
bash evals/run_e2e.sh --agent claude --trials 3 --case CASE_ID --report out.json

Exits 0 if all gated cases have at least one passing trial; exits 1 otherwise. Run artifacts (transcripts, grading results, cost per trial) are written to evals/.work/<timestamp>/ and gitignored.

Layout

evals/
  cases.json              — test case definitions (prompts, assertions, gate flag)
  fixtures/todo-repo/     — seed content copied into a fresh jj repo per trial
  hooks/settings.json     — PreToolUse hook: bash command logger + git-worktree deny
  runners/
    common.sh             — shared setup (jj repo init, skill install)
    run_claude.sh         — drives claude -p with stream-json transcript capture
  verify.py               — deterministic grader (no external dependencies)
  run_e2e.sh              — top-level runner: case × trial loop, pass@k/pass^k report

License

MIT

About

Jujutsu VCS Workspace skill for subagents.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Contributors