jj: expand the skill and add a paren-free trunk alias
Assisted-by: Claude Opus 5 via Claude Code
change uusxnkrurqykxosnvwvtrypxvomzvmts
commit 87ddf83115e346e4c7752a2d40ce1c995dbb6579
author Alpha Chen <alpha@kejadlen.dev>
date
parent kpqumymv
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