diff --git a/modules/home/ai-agent/skills/using-jjc/SKILL.md b/modules/home/ai-agent/skills/using-jjc/SKILL.md index 4b749e2..d82a383 100644 --- a/modules/home/ai-agent/skills/using-jjc/SKILL.md +++ b/modules/home/ai-agent/skills/using-jjc/SKILL.md @@ -7,6 +7,8 @@ description: Selects individual text hunks or change atoms in Jujutsu with jjc. Use `jjc` for selecting changes within a file. Use native JJ commands when whole files or commits already match the intended scope. Follow the `using-jujutsu` work mode and preserve unrelated changes. Hunk selection does not authorize discarding work or rewriting another session's changes. +Before mutations, read [history editing](../using-jujutsu/references/history-editing.md). For large listings, multiple source revisions, or part of one atom, read [advanced selection](references/advanced-selection.md). + ## Select and apply changes 1. Identify the source revision and, for folding, the destination. Check their patches and descendants with JJ before rewriting them. Keep the relevant change IDs and final content for verification. @@ -33,7 +35,7 @@ The listing displays a short unique ID prefix followed by the remaining characte | `@15` | Whole atoms touching target-file line 15 | | `@15-20` | Whole atoms touching the target-file line range | -An atom is a contiguous block of changed lines. Line selectors choose whole atoms, so they can include more lines than the specified range. Use target-file line numbers, not numbered rows in a diff display. If one atom mixes unrelated changes, use a carefully verified non-interactive diff editor instead of pretending the line selector can split it. +An atom is a contiguous block of changed lines. Line selectors choose whole atoms, so they can include more lines than the specified range. Use target-file line numbers, not numbered rows in a diff display. If one atom mixes unrelated changes, use the advanced-selection procedure; a line selector cannot split the atom. `jjc` supports hunks in modified text files. Use native JJ operations for file additions, deletions, renames, copies, and binary files. `jjc hunks` can snapshot the working copy; treat it as a repository operation, not a passive file reader. diff --git a/modules/home/ai-agent/skills/using-jjc/references/advanced-selection.md b/modules/home/ai-agent/skills/using-jjc/references/advanced-selection.md new file mode 100644 index 0000000..d367e5c --- /dev/null +++ b/modules/home/ai-agent/skills/using-jjc/references/advanced-selection.md @@ -0,0 +1,43 @@ +# Advanced hunk selection + +Apply the history-editing safeguards before rewriting changes. Discover selectors from the relevant source revision and verify both the selected patch and the content that must remain. + +## Filter a large listing + +`jjc hunks` has no path filter. To limit output to a known path while retaining its previews, filter hunk blocks rather than individual matching lines: + +```sh +jjc hunks -r '' | awk -v path='src/main.rs' ' + /^[[:xdigit:]]+(\[[[:xdigit:]]+\])? / { show = index($0, " " path " (") > 0 } + show +' +``` + +Replace the source and path with the task's values. Inspect the candidate with `jjc hunks -r --full`. If the pipeline reports no matches, check the source command's exit status and the path before concluding that there are no hunks. + +## Fold from multiple revisions + +Repeat `--from` for the sources and qualify each selector with its source revision: + +```sh +jjc fold --from '' --from '' \ + --into '' \ + --select ':' \ + --select ':' +``` + +Use recorded change IDs so that the references remain meaningful through rewrites. Inspect each source against its parent before folding. Afterward, verify the destination's combined patch, each source's remaining changes, and the affected descendants. If the correction broadens the destination's purpose, update its description. + +## Isolate an append within one atom + +A multi-line insertion can be one atom. Selecting a few of its lines still selects the whole insertion. For an append-only subset in an existing tracked file, you can temporarily isolate the requested append, pick it, and restore the remaining content. + +Use this only when you can exclude concurrent writers to that file and repository mutations during the operation. If you cannot establish that boundary, do not temporarily replace the working file. This procedure does not apply to replacements, deletions, mixed-author new files, or conflicted files. + +1. Snapshot the full working copy and record its commit ID and change ID. Save the original file bytes outside the repository. Read the parent file with `jj file show -r @- -- ` and identify the exact requested append. Confirm that it is absent from the parent. +2. Write the parent bytes followed by only the requested append. Keep the expected isolated bytes for comparison. Run `jj --no-pager diff -- ` to snapshot and inspect the patch. The path limits displayed output; JJ still snapshots the working copy. +3. Rediscover the hunk with `jjc hunks` and verify that its additions contain exactly the requested append. Run `jjc pick '' -m ''` and inspect the new commit's patch. +4. Before restoring the backup, verify that the file still matches the expected isolated bytes and the repository has the expected post-pick state. If either changed unexpectedly, retain the backup and inspect the concurrent work instead of overwriting it or rolling back the repository. +5. Restore the original bytes and snapshot them. Verify that the picked commit contains only the requested addition and that `jj diff --from --to @` is empty. Check that the unselected addition remains in the working-copy patch. + +If the pick fails, restore the backup only after the same file and repository checks. Keep the backup until final-content verification succeeds. For a different kind of partial atom, use a task-specific non-interactive JJ diff editor with explicit input/output checks, then verify the selected patch and final tree; do not generalize this append procedure to it. diff --git a/modules/home/ai-agent/skills/using-jujutsu/SKILL.md b/modules/home/ai-agent/skills/using-jujutsu/SKILL.md index 370c88d..e78eb9c 100644 --- a/modules/home/ai-agent/skills/using-jujutsu/SKILL.md +++ b/modules/home/ai-agent/skills/using-jujutsu/SKILL.md @@ -27,6 +27,8 @@ Run the check from the command's working directory. Repeat it whenever you chang If the result is `jujutsu`, use `jj` for version control and `jjc` for hunk selection. **Never run `git` commands in a Jujutsu repository**, including `git status`, `git log`, and `git diff`. A `.git/` directory or Git status supplied by the environment does not prove that Git commands are appropriate because Jujutsu uses Git as a backend. +Read the target repository's instructions, including `.agents/AGENTS-JJ.md` at its root when present. Apply its repository-specific JJ rules alongside the task's established scope. + ## Choose the work mode **Development** is the default for implementation, experiments, and fixes. Keep useful, validated checkpoints and make local corrections as needed. Do not repeatedly reorganize the whole stack or squash all checkpoints at the end of each turn. @@ -35,6 +37,15 @@ If the result is `jujutsu`, use `jj` for version control and `jjc` for hunk sele Stay in submission preparation while fixing findings and validating the resulting series. Return to development for new implementation work after handing back or submitting the prepared series. A review-only request reports findings without changing commits. Preparation alone does not authorize publication. +## Load the relevant procedure + +- Before editing earlier commits, splitting, squashing, rebasing, restoring, abandoning, or repairing existing changes in either mode, read [history editing](references/history-editing.md). These safeguards also apply to `jjc` mutations. +- For conflicts, also read [stack conflict repair](references/conflict-repair.md). +- When the intended submission needs a linear series from branched development, also read [merge flattening](references/merge-flattening.md). +- For changes within one file, read [using jjc](../using-jjc/SKILL.md). + +Load only the procedures the operation needs. Use `jj help -k revsets`, `jj help -k templates`, or command help to check unfamiliar syntax. Run automated JJ commands with `--no-pager`. + ## Core mental model **The working copy is a commit.** There is no staging area. Every file change is automatically part of the working-copy commit — no `add` step needed. The next `jj` command you run will snapshot the working copy. @@ -101,24 +112,15 @@ jj git fetch For individual hunks within a file, read [using jjc](../using-jjc/SKILL.md). ```bash -jj squash -m "description" # squash working copy into parent -jj rebase -r @ -d main # rebase current commit onto main +jj squash -u # fold into parent, keeping its message +jj rebase -r @ -o main # move only the working-copy commit ``` -Always pass `-m` to `jj squash` — without it, jj opens an interactive editor when both commits have descriptions. +Pass `-u` to retain the destination message, or `-m "description"` when the combined change needs a new message. Without either option, `jj squash` can open an editor. For moving a whole stack, use the history-editing procedure to choose the correct rebase scope. ### Resolving conflicts -Conflicts are stored in commits — they don't block work. To resolve: - -```bash -jj new # create child commit to work in -# edit conflicted files directly to remove conflict markers -jj st # verify conflicts are resolved -jj squash -m "resolve conflict" # fold resolution into conflicted commit -``` - -Do not use `jj resolve` — it launches an interactive merge tool. Edit the conflicted files directly instead. +Conflicts are stored in commits. Use the stack conflict-repair procedure to select the earliest affected ancestor, preserve its message, and return to the intended tip. Edit conflict files directly; do not launch an interactive merge tool in automation. **Editing an existing commit:** use `jj edit ` to check it out, then make changes directly. The commit updates in place — no need to squash. diff --git a/modules/home/ai-agent/skills/using-jujutsu/references/conflict-repair.md b/modules/home/ai-agent/skills/using-jujutsu/references/conflict-repair.md new file mode 100644 index 0000000..0b0a367 --- /dev/null +++ b/modules/home/ai-agent/skills/using-jujutsu/references/conflict-repair.md @@ -0,0 +1,46 @@ +# Stack conflict repair + +Apply the history-editing safeguards before repairing a series. For merge flattening that must preserve descendant trees, use the merge-flattening procedure instead of ordinary patch replay. + +## Anchor the series + +Before moving `@`, record its change ID and commit ID. If it is empty, record its parent change ID as both the series tip and the intended return point. Otherwise, use the starting change as the tip. An unnamed empty working-copy commit can be abandoned when you leave it, so do not use that scratch change as the anchor. Query the recorded logical tip to find conflicts throughout the live series: + +```sh +jj --no-pager log -r 'conflicts() & ::' +jj --no-pager log -r 'roots(conflicts() & ::)' +``` + +The second query finds the earliest conflicted ancestors. Inspect them and choose one within the task's scope. Neither `@` nor `@-` necessarily identifies that commit. If multiple branch roots conflict, repair prerequisites before their dependents. + +## Repair and advance + +1. Create a scratch child of the selected conflicted change: + + ```sh + jj --no-pager new '' + ``` + +2. Resolve the files according to the commit's intended behavior. For a large rewrite, choose a coherent final file structure and deliberately carry over required details. Keeping both sides of each hunk can duplicate implementations or document outlines. +3. Inspect the full affected files and their diff. Check JJ's conflict state and search the affected paths for remaining markers: + + ```sh + jj --no-pager status + jj --no-pager diff + rg -n '^(<<<<<<<|%%%%%%%|\+\+\+\+\+\+\+|>>>>>>>)' -- + ``` + + Interpret any matches in context. No marker matches does not prove semantic correctness. Run the narrow checks relevant to the repaired commit. +4. Fold the scratch correction into the original change while retaining its message: + + ```sh + jj --no-pager squash --from @ --into '' -u + ``` + +5. Query the recorded series again. If conflicts remain, select the earliest remaining conflicted ancestor from that same anchored series and repeat. Do not switch to a range ending at the live `@`; it may be a scratch child or an older ancestor. + +## Verify and return + +Inspect each repaired commit against its parent and intended message. For documentation rewrites, check that the heading structure forms one coherent outline. Review descendant patches and run the series' required checks; removing conflict markers is only one part of repair. + +When the anchored series is clear, restore the working position. If you started on a nonempty change, use `jj edit `. If you started on an empty child, return to an empty child of the recorded parent; reuse the existing empty child when it is already in that position. Do not leave the checkout on an older repaired ancestor. Apply submission preparation's handoff rule when that mode is active. diff --git a/modules/home/ai-agent/skills/using-jujutsu/references/history-editing.md b/modules/home/ai-agent/skills/using-jujutsu/references/history-editing.md new file mode 100644 index 0000000..61b434f --- /dev/null +++ b/modules/home/ai-agent/skills/using-jujutsu/references/history-editing.md @@ -0,0 +1,48 @@ +# History editing + +Use this procedure before rewriting existing changes in development or submission preparation. Apply the task's established authorization; routine cleanup of task-owned, unpublished changes does not need another approval. + +## Check scope and ownership + +Inspect the target changes and their descendants. Rewriting an ancestor can also rewrite another workspace's working-copy commit. Check the affected set, replacing `` with the revisions you intend to rewrite: + +```sh +jj --no-pager log -r '():: & (working_copies() ~ @)' +``` + +If another session uses an affected change, preserve its work and establish the scope of any shared rewrite before proceeding. Keep immutable and unrelated changes outside the operation. Run history mutations sequentially. Do not create extra workspaces unless requested. + +Before a stack rewrite, snapshot and record the original working-copy change and commit IDs, the intended tip and base, and the starting operation ID. If `@` is empty, also record its parent and the fact that you started on an empty child. Save the affected heads and bookmarks for comparison. + +```sh +jj --no-pager log -r '@ | @-' --no-graph \ + -T 'change_id ++ " " ++ commit_id ++ "\n"' +jj --no-pager --at-op=@ --ignore-working-copy op log --limit 1 --no-graph -T 'id ++ "\n"' +``` + +Use change IDs to follow live changes through rewrites and saved commit IDs to compare old content. Keep inspections anchored to the recorded series rather than a moving `@`. If a split or squash replaces an endpoint, track the resulting tip explicitly. + +## Choose the operation + +- To move a whole branch relative to a destination, use `jj rebase -b -o `. It includes prerequisites outside the destination's ancestry and their descendants; inspect that set first. +- To move an exact subtree, use `jj rebase -s -o `. Starting at only the tip can leave its prerequisites behind. +- To move selected revisions while reconnecting their former descendants, use `jj rebase -r -o `. +- To fold a correction while retaining the destination message, use `jj squash --from --into -u`. If its purpose changes, supply the final message with `-m` instead. +- Abandon only explicit changes you can attribute to the task and have verified are disposable. A repository-wide query for empty or conflicted commits does not establish ownership. + +## Audit the result + +For a complex rewrite, inspect the relevant operation sequence and the evolution of affected changes. These commands avoid snapshotting the working copy or reconciling concurrent operations: + +```sh +jj --no-pager --at-op=@ --ignore-working-copy op log --limit 10 +jj --no-pager --at-op=@ --ignore-working-copy evolog -r '' -p --limit 10 +``` + +Use the starting operation ID to extend the inspection if ten entries do not cover the rewrite. Check for lost prerequisites, unexpected patch changes, and effects on other sessions. Compare the final tree with the saved tip commit; explain intentional content changes. Check affected ancestry, conflicts, divergence, heads, and bookmarks. A successful command and an unchanged final tree do not establish that the intermediate commits are correct. + +If you moved away from the original working position, return to the saved logical tip or an empty child of the saved parent, as appropriate. Submission preparation returns to an empty child of the prepared tip unless the task specifies another position. + +## Recover without erasing other work + +The operation log belongs to the shared repository. Both `jj undo` and `jj op restore` can affect other workspaces; an operation ID is not a workspace-local backup. Before using either command, inspect intervening operations and determine exactly what would change. Prefer a targeted correction when it preserves unrelated progress. Do not roll back shared or published work without authorization for that impact. diff --git a/modules/home/ai-agent/skills/using-jujutsu/references/merge-flattening.md b/modules/home/ai-agent/skills/using-jujutsu/references/merge-flattening.md new file mode 100644 index 0000000..c4a3ddd --- /dev/null +++ b/modules/home/ai-agent/skills/using-jujutsu/references/merge-flattening.md @@ -0,0 +1,70 @@ +# Merge flattening + +Use this procedure when the intended review or landing shape requires a linear series of task-owned, mutable changes. A merge alone does not justify changing published integration history or an intentional branch relationship. Apply the history-editing safeguards first. + +## Record the original graph + +Identify the merge, its original parent change IDs, the final tip, and the order in which the branches should appear. Prefer keeping the first-parent branch earlier unless the review's dependency order requires otherwise. + +Save the merge and tip commit IDs for content comparisons. Also save a root-to-tip manifest of change IDs and commit IDs for the merge and its descendants through the intended tip: + +```sh +jj --no-pager log -r 'parents()' --no-graph -T 'change_id ++ "\n"' +jj --no-pager log -r ':: & ::' --reversed --no-graph \ + -T 'change_id ++ " " ++ commit_id ++ "\n"' +``` + +Inspect all descendants that the rewrite would affect, including any outside that path. Preserve shared or unrelated branches before proceeding. The manifest supports targeted recovery; it does not authorize restoring unrelated work. + +## Linearize the existing branches + +For two parents, inspect the side branch and its descendants, then move that branch onto the earlier parent: + +```sh +jj --no-pager log -r '(..)::' +jj --no-pager rebase -b '' -o '' --simplify-parents +``` + +The branch selector includes the side branch's prerequisites. Using `-s ` is appropriate only when that parent is also the first change you need to move. Rebasing only a multi-commit branch's tip can strand earlier changes. Parent simplification removes redundant merge edges as the branches become linear. If there are more than two parents, establish their dependency order and handle one branch at a time. + +Prefer retaining the existing change identities. Creating a replacement on one parent can reproduce the final tree while leaving the other branch outside the result's ancestry. + +## Resolve without replaying every descendant + +If a moved side-branch commit conflicts, resolve its logical patch on top of its new parent. Do not restore that commit's old tree: it lacks the earlier branch's additions and can undo them. + +Create a scratch child, resolve its files, and review the result. Then apply its tree to the conflicted change while preserving descendant content: + +```sh +jj --no-pager new '' +# Resolve and validate the scratch child's files before continuing. +jj --no-pager restore --from @ --into '' --restore-descendants +``` + +Review the moved change with `jj evolog -r -p --stat`. Unexpected removals from the new base indicate a bad resolution. Directly editing or resolving the moved ancestor replays descendant patches; restoring from the scratch child avoids that replay when preserving descendant trees matters. + +Preserving descendant trees can also preserve conflicts introduced by the initial rebase. If the merge or its descendants no longer have their intended content, use the saved manifest to restore those trees in root-to-tip order: + +```sh +jj --no-pager restore --from '' --into '' --restore-descendants +``` + +Use this fallback only for the saved merge and descendants whose content must remain unchanged. Do not apply it to the moved side branch's old trees. If the scratch child remains as an empty task-owned head, move it to the saved final tip with `jj rebase -r @ -o `. Inspect any nonempty scratch content before deciding what to retain. + +## Verify content and ancestry + +Check that the flattened merge has one parent and exactly the old merge's tree. Compare the final tip with its saved commit as well: + +```sh +jj --no-pager log -r 'parents()' +jj --no-pager diff --from '' --to '' --stat +jj --no-pager diff --from '' --to '' --stat +``` + +For each original parent change ID, verify that it is still an ancestor of the flattened merge: + +```sh +jj --no-pager log -r ' & ::' +``` + +Every parent must appear. Check the affected series for conflicts and divergence, compare heads and bookmarks with the starting state, and inspect intermediate patches for coherent purposes and prerequisites. Use the operation and evolution logs to investigate discrepancies. Return to the intended tip and complete the submission review and validation loop. diff --git a/modules/home/ai-agent/skills/using-jujutsu/references/submission-preparation.md b/modules/home/ai-agent/skills/using-jujutsu/references/submission-preparation.md index c434201..3c44db2 100644 --- a/modules/home/ai-agent/skills/using-jujutsu/references/submission-preparation.md +++ b/modules/home/ai-agent/skills/using-jujutsu/references/submission-preparation.md @@ -22,7 +22,7 @@ Describe the intended review units briefly, then reshape task-owned, unpublished - Preserve independent changes as separate commits. Do not squash an entire series merely to make it shorter. - Rewrite messages to describe the final result using the `writing-commit-messages` skill, including its rules for when a body is useful. -Use native, non-interactive JJ operations and explicit revisions. Run history mutations sequentially. For example: +Apply the history-editing procedure before reshaping the series. Load conflict repair or merge flattening only when needed. Use native, non-interactive JJ operations and explicit revisions. Run history mutations sequentially. For example: ```sh jj split -r -m "extract retry configuration" -- @@ -30,14 +30,14 @@ jj squash --from --into -m "handle reconnect retr jj describe -r -m "test reconnect after reset" ``` -Path-based splitting is suitable only when the selected files match the intended outcome. If changes within one file need different commits, use the `using-jjc` skill and verify the exact selection. For selections that span only part of a change atom, use a non-interactive diff editor. Preserve the full intended final content. Moving unrelated changes out of a commit must not discard them from the repository. +Path-based splitting is suitable only when the selected files match the intended outcome. If changes within one file need different commits, use the `using-jjc` skill and verify the exact selection. For part of one atom, follow its advanced-selection procedure. Preserve the full intended final content. Moving unrelated changes out of a commit must not discard them from the repository. ## Review, fix, and validate 1. Inspect every resulting commit against its parent. Read the changed files at that revision, check the message, and confirm that prerequisites are present. Give the `reviewing-changes` skill the recorded series explicitly; its default `@` may be an empty commit or an ancestor being edited. 2. Fix actionable findings in the responsible commit and resolve any conflicts propagated to descendants. The review skill reports findings; submission preparation owns the corrections. Revisit affected commits after a split, squash, reorder, or behavior change. 3. Run the checks that establish each review unit works with its prerequisites, plus the required checks for the final result. A passing tip does not establish that intermediate commits build or pass their relevant tests. If a check cannot run, report the specific gap. -4. Compare the final tree with the recorded starting tip. History-only reshaping should preserve it exactly. If review fixes intentionally change behavior or content, account for those differences. Check the scoped series for conflicts, divergence, lost changes, and unintended heads or bookmark moves. +4. Compare the final tree with the recorded starting tip. History-only reshaping should preserve it exactly. If review fixes intentionally change behavior or content, account for those differences. Use operation and evolution logs to audit complex rewrites. Check the scoped series for conflicts, divergence, lost changes, and unintended heads or bookmark moves; after flattening, verify that both original branches remain in the result's ancestry. 5. Repeat review and validation where changes or findings justify it. Finish when the intended series has no unresolved actionable findings and its required checks pass. Report external blockers or explicitly accepted limitations; do not invent extra changes or repeat unchanged checks merely to keep iterating. If you moved away from the series tip, return to an empty child of the prepared tip before handing back. Keep independent series separate and preserve the requested working position when one was specified. Do not restore the repository-wide operation log to undo a local mistake without accounting for intervening work.