jj: expand the skill and add a paren-free trunk alias
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/.config/jj/config.toml b/.config/jj/config.toml
index cf4293aa..34273671 100644
--- a/.config/jj/config.toml
+++ b/.config/jj/config.toml
@@ -112,6 +112,10 @@ edit = true
'user(x)' = 'author(x) | committer(x)'
+# Paren-free spelling of trunk(): Claude Code's shell parser reads () as a
+# subshell even when quoted. Respects per-repo overrides (not all are main@origin).
+'trunk' = 'trunk()'
+
'wip()' = 'description("wip:*")'
'private()' = 'description("private:*")'
diff --git a/ai/skills/jj-workspaces/SKILL.md b/ai/skills/jj-workspaces/SKILL.md
index d20ae290..277ea7be 100644
--- a/ai/skills/jj-workspaces/SKILL.md
+++ b/ai/skills/jj-workspaces/SKILL.md
@@ -40,16 +40,21 @@ jj workspace add --name=<name> work/<name>
jj workspace add --name=<name> -r <rev> work/<name>
# 2. Place in DAG - if megamerge (mm) exists, insert between trunk and mm
-jj rebase -r <name>@ -A 'trunk()' -B mm
+jj rebase -r <name>@ -A trunk -B mm
# If no megamerge, just rebase onto trunk:
-# jj rebase -r <name>@ -d 'trunk()'
+# jj rebase -r <name>@ -d trunk
# 3. Sync main workspace
jj workspace update-stale
```
-**Result (with mm):** `trunk() → workspace change → mm`
+Use the paren-free `trunk` alias, not `trunk()` — Claude Code's shell
+parser treats the parentheses as a subshell even when quoted and triggers
+a permission prompt. Don't hardcode a bookmark instead; `trunk()` resolves
+differently per repo. See the `jj` skill's Common Pitfalls section.
+
+**Result (with mm):** `trunk → workspace change → mm`
`<name>@` names that workspace's working-copy commit and resolves from any
workspace; `jj workspace list` shows them all.
diff --git a/ai/skills/jj/SKILL.md b/ai/skills/jj/SKILL.md
index dc04f00f..76f5e195 100644
--- a/ai/skills/jj/SKILL.md
+++ b/ai/skills/jj/SKILL.md
@@ -50,6 +50,39 @@ Splitting an ancestor rebases every descendant, `@` included — content is
preserved but commit IDs change, so it isn't an option when a commit ID
has to stay put.
+## Landing a Follow-up Change
+
+**Never rewrite an existing commit with `jj edit`.** `jj edit` moves `@`
+onto that commit and streams later edits straight into it, which is easy
+to forget and rewrites history in place. Make the fix in a new commit,
+then route it to where it belongs:
+
+- `jj absorb` splits the new change hunk by hunk and moves each to the
+ closest mutable ancestor that last touched those lines. It abandons the
+ source commit when every hunk lands and the source has no description;
+ hunks it can't place unambiguously stay put, so re-check `jj st` after.
+- `jj squash --into <rev> <fileset>` folds specific paths into one target
+ commit (see the squash pitfalls below for the `-u`/`-m` requirement).
+- `jj rebase` inserts the new commit as its own commit elsewhere.
+- Leaving it alone is fine when the change stands on its own.
+
+```bash
+jj absorb # distribute every hunk to its ancestor
+jj absorb <fileset> # only these paths
+jj absorb --into <rev> # limit destinations to <rev> and ancestors
+```
+
+## Megamerges
+
+A megamerge is a single working commit that merges several parallel
+branches at once (often bookmarked `mm`), giving one working copy that
+integrates all of them. Moving `@` off it — `jj new` or `jj edit` onto
+another commit — discards that combined view, and rebuilding it means
+re-running the merge by hand. Stay on the megamerge unless explicitly
+told to move; land new work with `jj absorb` or `jj squash --into` so `@`
+stays put. The `jj-workspaces` skill uses `mm` when placing workspaces in
+the DAG.
+
## Key Differences from Git
No staging area: working copy changes map directly to commits. Operations
@@ -99,6 +132,11 @@ jj git push --named <name>=<rev> # create named bookmark and push
bookmark"). Use `--named` to create, push, and auto-track in one step.
`-c` generates a bookmark name from the change ID (`push-<short-change-id>`).
+`--named` is the whole step, not a follow-up to `jj bookmark create` —
+it errors with "Bookmark already exists" when the name is taken locally.
+Running `create` first therefore fails twice: `--named` rejects the
+existing name, and `-b` rejects it for being untracked.
+
## Restoring Files
`jj restore` uses `--from` and `--into`/`--to`, not `-r`:
@@ -144,13 +182,15 @@ command parser treats `()` as subshell syntax even when quoted, which
triggers permission prompts. Use the underlying value directly:
```bash
-jj log -r main@origin # correct — no parens
+jj log -r trunk # correct — paren-free alias
jj log -r 'trunk()' # WRONG — triggers permission prompt
```
-`trunk()` usually resolves to `main@origin` but depends on the repo's
-config. Check `jj config get revset-aliases."trunk()"` if unsure.
-Prefer the resolved value (e.g., `main@origin`) everywhere.
+`trunk` is a config alias for `trunk()`, so it stays correct per repo.
+Don't substitute a hardcoded bookmark: `trunk()` is itself an alias and
+resolves differently across repos — of the 20 that override it here, four
+point at something other than `main@origin` (`mm@origin`, `master@origin`).
+`jj config get revset-aliases."trunk()"` shows the current repo's value.
**`jj show` does not accept path arguments.** It takes an optional revision
but cannot be scoped to a path. Use `jj diff` instead:
@@ -212,6 +252,15 @@ and re-check `jj st` immediately before any rewrite:
jj squash --from <rev> --into <rev> -u FILE # explicit source
```
+**`--from` is repeatable, so collapsing several commits is one rewrite.**
+Each `--from` takes a revset and the sources need not be adjacent —
+commits in between are rebased, not folded in. Pair it with `-m`, since
+abandoning several described sources otherwise opens the editor:
+
+```bash
+jj squash --from <rev> --from <rev> --into <rev> -m 'combined message'
+```
+
**`jj squash` opens `$EDITOR` to merge descriptions and hangs in
non-interactive shells.** When source and destination both have
descriptions, jj launches the editor to combine them — Claude Code's
diff --git a/ai/skills/pr/SKILL.md b/ai/skills/pr/SKILL.md
index 2d7f4bec..ccd72f1a 100644
--- a/ai/skills/pr/SKILL.md
+++ b/ai/skills/pr/SKILL.md
@@ -23,8 +23,8 @@ Revision must be provided (bookmark, change ID, revset, or commit hash). The rev
1. Gather context for the PR:
- Run `jj log -r '<revision>' --no-graph` to check for existing bookmarks
- - Run `jj log -r 'main@origin..<revision>'` to list commits between trunk and the revision
- - Run `jj diff --from main@origin --to <revision> --stat` to review all changes
+ - Run `jj log -r trunk..<revision>` to list commits between trunk and the revision
+ - Run `jj diff --from trunk --to <revision> --stat` to review all changes
2. Push revision and get bookmark name:
- If revision has a remote-tracked bookmark: `jj git push --bookmark <name>`
- If revision has a local-only bookmark (no `@origin`): push with
@@ -75,7 +75,7 @@ Revision must be provided (bookmark, change ID, revset, or commit hash). The rev
the diff, restates the title, or exists to look thorough.
9. **Code review**: Dispatch the `superpowers:code-reviewer` subagent to review
the changes. Tell the reviewer to run
- `jj diff --git --from main@origin --to <revision>` to get the actual commit
+ `jj diff --git --from trunk --to <revision>` to get the actual commit
diff—do not let it grep or read the working directory, which may be on a
different revision. Pass `--git` so the reviewer gets a standard unified diff
with `@@` hunk headers; the default color-words format prints two