jj-stack 0.1.3__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/workflows/ci.yml +7 -12
  2. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/workflows/codeql.yml +2 -2
  3. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/workflows/release.yml +3 -3
  4. {jj_stack-0.1.3 → jj_stack-0.1.5}/.gitignore +2 -0
  5. {jj_stack-0.1.3 → jj_stack-0.1.5}/AGENTS.md +21 -19
  6. {jj_stack-0.1.3 → jj_stack-0.1.5}/CONTRIBUTING.md +26 -6
  7. {jj_stack-0.1.3 → jj_stack-0.1.5}/PKG-INFO +83 -97
  8. jj_stack-0.1.5/README.md +159 -0
  9. {jj_stack-0.1.3 → jj_stack-0.1.5}/check.py +55 -18
  10. {jj_stack-0.1.3 → jj_stack-0.1.5}/complexity-budget.toml +2 -1
  11. jj_stack-0.1.5/docs/AGENTS.md +57 -0
  12. {jj_stack-0.1.3 → jj_stack-0.1.5}/docs/README.md +5 -6
  13. jj_stack-0.1.5/docs/guides/close-or-separate.md +90 -0
  14. jj_stack-0.1.5/docs/guides/continue-a-stack.md +89 -0
  15. jj_stack-0.1.5/docs/guides/merge-and-sync.md +207 -0
  16. jj_stack-0.1.5/docs/guides/multiple-stacks.md +167 -0
  17. jj_stack-0.1.5/docs/guides/review-a-stack.md +71 -0
  18. jj_stack-0.1.5/docs/guides/revise.md +87 -0
  19. jj_stack-0.1.5/docs/guides/submit-and-update.md +98 -0
  20. jj_stack-0.1.5/docs/guides/working-on-github.md +60 -0
  21. jj_stack-0.1.5/docs/internals/AGENTS.md +20 -0
  22. jj_stack-0.1.5/docs/internals/README.md +45 -0
  23. jj_stack-0.1.5/docs/internals/code-reviews.md +78 -0
  24. jj_stack-0.1.5/docs/internals/design.md +770 -0
  25. jj_stack-0.1.5/docs/internals/property-testing.md +14 -0
  26. jj_stack-0.1.5/docs/internals/releasing.md +118 -0
  27. {jj_stack-0.1.3 → jj_stack-0.1.5}/docs/internals/testing-philosophy.md +29 -12
  28. {jj_stack-0.1.3 → jj_stack-0.1.5}/docs/json-output.schema.json +111 -1
  29. jj_stack-0.1.5/docs/mental-model.md +126 -0
  30. jj_stack-0.1.5/docs/quick-start.md +194 -0
  31. jj_stack-0.1.5/docs/reference/automation.md +129 -0
  32. jj_stack-0.1.5/docs/reference/bookmarks-and-selection.md +127 -0
  33. {jj_stack-0.1.3 → jj_stack-0.1.5}/docs/reference/commands.md +15 -19
  34. jj_stack-0.1.5/docs/reference/configuration.md +128 -0
  35. jj_stack-0.1.5/docs/reference/descriptions.md +94 -0
  36. jj_stack-0.1.5/docs/reference/json-output.md +217 -0
  37. jj_stack-0.1.5/docs/tool-comparison.md +128 -0
  38. jj_stack-0.1.5/docs/troubleshooting.md +288 -0
  39. {jj_stack-0.1.3 → jj_stack-0.1.5}/evals/jj-stack-skill.md +5 -5
  40. {jj_stack-0.1.3 → jj_stack-0.1.5}/justfile +9 -6
  41. {jj_stack-0.1.3 → jj_stack-0.1.5}/pyproject.toml +8 -3
  42. {jj_stack-0.1.3 → jj_stack-0.1.5}/release-notes/v0.1.2.md +1 -1
  43. jj_stack-0.1.5/release-notes/v0.1.4.md +23 -0
  44. jj_stack-0.1.5/release-notes/v0.1.5.md +28 -0
  45. {jj_stack-0.1.3 → jj_stack-0.1.5}/scripts/describe_with_claude.py +0 -2
  46. {jj_stack-0.1.3 → jj_stack-0.1.5}/scripts/describe_with_codex.py +0 -2
  47. {jj_stack-0.1.3 → jj_stack-0.1.5}/scripts/describe_with_editor.py +0 -8
  48. {jj_stack-0.1.3 → jj_stack-0.1.5}/scripts/describe_with_prompt.py +0 -2
  49. {jj_stack-0.1.3 → jj_stack-0.1.5}/skills/jj-stack/SKILL.md +58 -42
  50. {jj_stack-0.1.3 → jj_stack-0.1.5}/skills/jj-stack/references/multi-stack.md +33 -29
  51. jj_stack-0.1.5/skills/jj-stack/references/recovery.md +117 -0
  52. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/bootstrap.py +13 -18
  53. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/cli.py +194 -391
  54. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/cli_help.py +36 -31
  55. jj_stack-0.1.5/src/jj_stack/commands/_json_status.py +64 -0
  56. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/checkout.py +185 -235
  57. jj_stack-0.1.5/src/jj_stack/commands/cleanup/actions.py +245 -0
  58. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/cleanup/command.py +226 -293
  59. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/doctor.py +103 -93
  60. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/in_use.py +4 -4
  61. jj_stack-0.1.5/src/jj_stack/commands/list_.py +518 -0
  62. jj_stack-0.1.5/src/jj_stack/commands/merge/command.py +414 -0
  63. jj_stack-0.1.5/src/jj_stack/commands/merge/github_stack.py +292 -0
  64. jj_stack-0.1.5/src/jj_stack/commands/merge/plan.py +247 -0
  65. jj_stack-0.1.5/src/jj_stack/commands/merge/wait.py +202 -0
  66. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/relink.py +52 -63
  67. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/auto_close.py +16 -38
  68. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/changes.py +20 -67
  69. jj_stack-0.1.5/src/jj_stack/commands/submit/command.py +681 -0
  70. jj_stack-0.1.5/src/jj_stack/commands/submit/descriptions.py +339 -0
  71. jj_stack-0.1.5/src/jj_stack/commands/submit/editor.py +274 -0
  72. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/github_stack.py +19 -58
  73. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/inputs.py +57 -36
  74. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/managed_comments.py +8 -7
  75. jj_stack-0.1.5/src/jj_stack/commands/submit/models.py +151 -0
  76. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/overview_comments.py +22 -52
  77. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/prs.py +47 -86
  78. jj_stack-0.1.5/src/jj_stack/commands/submit/publication.py +278 -0
  79. jj_stack-0.1.5/src/jj_stack/commands/submit/render.py +139 -0
  80. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/revision_comments.py +9 -13
  81. jj_stack-0.1.5/src/jj_stack/commands/sync.py +459 -0
  82. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/sync_apply.py +128 -185
  83. jj_stack-0.1.5/src/jj_stack/commands/sync_prs.py +118 -0
  84. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/unstack.py +48 -67
  85. jj_stack-0.1.5/src/jj_stack/commands/view.py +778 -0
  86. jj_stack-0.1.5/src/jj_stack/commands/view_details.py +142 -0
  87. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/completion.py +20 -46
  88. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/concurrency.py +21 -13
  89. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/config.py +39 -75
  90. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/console.py +123 -155
  91. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/errors.py +46 -6
  92. jj_stack-0.1.5/src/jj_stack/formatting.py +85 -0
  93. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/client.py +379 -298
  94. jj_stack-0.1.5/src/jj_stack/github/error_messages.py +89 -0
  95. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/pr_refs.py +5 -10
  96. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/resolution.py +8 -6
  97. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/identifiers.py +7 -0
  98. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/jj/cli_args.py +0 -3
  99. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/jj/client.py +256 -408
  100. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/jj/colors.py +26 -91
  101. jj_stack-0.1.5/src/jj_stack/jj/settings.py +65 -0
  102. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/models/github.py +54 -31
  103. jj_stack-0.1.5/src/jj_stack/models/github_details.py +88 -0
  104. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/models/stack.py +11 -15
  105. jj_stack-0.1.5/src/jj_stack/models/tracking.py +44 -0
  106. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/pr_branch_namespace.py +2 -2
  107. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/change_state.py +182 -204
  108. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/convergence.py +210 -167
  109. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/convergence_models.py +16 -24
  110. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/convergence_observation.py +36 -36
  111. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/github_stack_safety.py +4 -4
  112. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/global_convergence.py +60 -62
  113. jj_stack-0.1.5/src/jj_stack/stack/observation.py +174 -0
  114. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/path.py +37 -38
  115. jj_stack-0.1.5/src/jj_stack/stack/pr_branches.py +149 -0
  116. jj_stack-0.1.5/src/jj_stack/stack/pr_facts.py +183 -0
  117. jj_stack-0.1.5/src/jj_stack/stack/preparation.py +84 -0
  118. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/repo.py +18 -19
  119. jj_stack-0.1.5/src/jj_stack/stack/reporting.py +203 -0
  120. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/selected.py +39 -30
  121. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/selection.py +18 -32
  122. jj_stack-0.1.5/src/jj_stack/stack/status.py +251 -0
  123. jj_stack-0.1.5/src/jj_stack/stack/trunk.py +48 -0
  124. jj_stack-0.1.5/src/jj_stack/stack/trunk_evidence.py +51 -0
  125. jj_stack-0.1.5/src/jj_stack/state/migrations.py +58 -0
  126. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/state/operation_lock.py +19 -24
  127. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/state/store.py +45 -84
  128. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/ui.py +1 -22
  129. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/conftest.py +24 -3
  130. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/conftest.py +0 -18
  131. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/submit_command_helpers.py +0 -10
  132. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_boundary_conditions.py +1 -1
  133. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_checkout_command.py +40 -62
  134. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_cleanup_command.py +35 -60
  135. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_doctor_command.py +36 -47
  136. jj_stack-0.1.5/tests/integration/test_fake_github_contracts.py +114 -0
  137. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_jj_stack.py +69 -26
  138. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_list_command.py +56 -56
  139. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_merge_command.py +254 -131
  140. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_relink_command.py +7 -5
  141. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_submit_command.py +338 -214
  142. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_sync_command.py +179 -143
  143. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_unstack_command.py +18 -35
  144. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/test_view_command.py +122 -60
  145. jj_stack-0.1.5/tests/property/test_submit_property_scenarios.py +105 -0
  146. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/run_live_github.py +1 -1
  147. jj_stack-0.1.5/tests/run_submit_property_scenarios.py +123 -0
  148. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/change_helpers.py +1 -0
  149. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/contexts.py +1 -3
  150. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/fake_github.py +338 -172
  151. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/integration_helpers.py +39 -67
  152. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/json_schema.py +5 -6
  153. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/output_assertions.py +0 -10
  154. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/pytest_concurrency.py +1 -14
  155. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/stack_edit_scenarios.py +0 -2
  156. jj_stack-0.1.5/tests/support/stack_machine.py +1354 -0
  157. jj_stack-0.1.5/tests/support/submit_faults.py +91 -0
  158. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/tracking.py +1 -1
  159. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_bootstrap.py +1 -35
  160. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_change_state.py +19 -45
  161. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_check_script.py +2 -26
  162. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_checkout.py +1 -1
  163. jj_stack-0.1.5/tests/unit/test_cli.py +151 -0
  164. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_cli_dispatch.py +5 -25
  165. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_completion.py +4 -20
  166. jj_stack-0.1.5/tests/unit/test_config.py +81 -0
  167. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_describe_with_editor_script.py +4 -20
  168. jj_stack-0.1.5/tests/unit/test_describe_with_prompt_script.py +22 -0
  169. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_errors.py +5 -22
  170. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_github_client.py +159 -67
  171. jj_stack-0.1.5/tests/unit/test_github_error_messages.py +52 -0
  172. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_github_models.py +25 -19
  173. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_github_resolution.py +8 -19
  174. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_github_stack_planning.py +6 -27
  175. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_in_use_command.py +4 -3
  176. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_jj_client.py +96 -243
  177. jj_stack-0.1.5/tests/unit/test_jj_settings.py +86 -0
  178. jj_stack-0.1.5/tests/unit/test_merge.py +174 -0
  179. jj_stack-0.1.5/tests/unit/test_merge_wait.py +101 -0
  180. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_operation_lock.py +0 -21
  181. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_pr_branches.py +29 -49
  182. jj_stack-0.1.5/tests/unit/test_pr_facts.py +112 -0
  183. jj_stack-0.1.5/tests/unit/test_pr_refs.py +34 -0
  184. jj_stack-0.1.5/tests/unit/test_property_runner.py +53 -0
  185. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_relink.py +21 -31
  186. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_selected.py +2 -2
  187. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_stack_path.py +12 -55
  188. jj_stack-0.1.5/tests/unit/test_stack_status.py +152 -0
  189. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_store.py +37 -38
  190. jj_stack-0.1.5/tests/unit/test_submit.py +172 -0
  191. jj_stack-0.1.5/tests/unit/test_submit_descriptions.py +121 -0
  192. jj_stack-0.1.5/tests/unit/test_submit_editor.py +164 -0
  193. jj_stack-0.1.5/tests/unit/test_trunk_evidence.py +95 -0
  194. jj_stack-0.1.5/tests/unit/test_ui.py +151 -0
  195. jj_stack-0.1.5/tests/unit/test_view.py +321 -0
  196. jj_stack-0.1.5/tests/unit/test_view_entrypoint.py +212 -0
  197. {jj_stack-0.1.3 → jj_stack-0.1.5}/tools/check_complexity.py +6 -7
  198. {jj_stack-0.1.3 → jj_stack-0.1.5}/tools/check_jj_release_updates.py +6 -10
  199. jj_stack-0.1.5/uv.lock +740 -0
  200. jj_stack-0.1.3/README.md +0 -173
  201. jj_stack-0.1.3/docs/AGENTS.md +0 -85
  202. jj_stack-0.1.3/docs/guides/close-or-separate.md +0 -73
  203. jj_stack-0.1.3/docs/guides/continue-a-stack.md +0 -66
  204. jj_stack-0.1.3/docs/guides/merge-and-sync.md +0 -149
  205. jj_stack-0.1.3/docs/guides/multiple-stacks.md +0 -113
  206. jj_stack-0.1.3/docs/guides/review-a-stack.md +0 -60
  207. jj_stack-0.1.3/docs/guides/revise.md +0 -54
  208. jj_stack-0.1.3/docs/guides/submit-and-update.md +0 -106
  209. jj_stack-0.1.3/docs/guides/working-on-github.md +0 -55
  210. jj_stack-0.1.3/docs/internals/AGENTS.md +0 -30
  211. jj_stack-0.1.3/docs/internals/README.md +0 -22
  212. jj_stack-0.1.3/docs/internals/code-reviews.md +0 -113
  213. jj_stack-0.1.3/docs/internals/design.md +0 -980
  214. jj_stack-0.1.3/docs/internals/implementation-strategy.md +0 -126
  215. jj_stack-0.1.3/docs/internals/property-testing.md +0 -54
  216. jj_stack-0.1.3/docs/internals/releasing.md +0 -119
  217. jj_stack-0.1.3/docs/mental-model.md +0 -66
  218. jj_stack-0.1.3/docs/quick-start.md +0 -131
  219. jj_stack-0.1.3/docs/reference/automation.md +0 -133
  220. jj_stack-0.1.3/docs/reference/bookmarks-and-selection.md +0 -149
  221. jj_stack-0.1.3/docs/reference/configuration.md +0 -75
  222. jj_stack-0.1.3/docs/reference/descriptions.md +0 -84
  223. jj_stack-0.1.3/docs/reference/json-output.md +0 -167
  224. jj_stack-0.1.3/docs/tool-comparison.md +0 -215
  225. jj_stack-0.1.3/docs/troubleshooting.md +0 -211
  226. jj_stack-0.1.3/skills/jj-stack/references/recovery.md +0 -101
  227. jj_stack-0.1.3/src/jj_stack/commands/_cleanup_actions.py +0 -352
  228. jj_stack-0.1.3/src/jj_stack/commands/_json_status.py +0 -115
  229. jj_stack-0.1.3/src/jj_stack/commands/cleanup/shared.py +0 -61
  230. jj_stack-0.1.3/src/jj_stack/commands/cleanup/stale.py +0 -79
  231. jj_stack-0.1.3/src/jj_stack/commands/list_.py +0 -692
  232. jj_stack-0.1.3/src/jj_stack/commands/merge/command.py +0 -370
  233. jj_stack-0.1.3/src/jj_stack/commands/merge/github_stack.py +0 -331
  234. jj_stack-0.1.3/src/jj_stack/commands/merge/models.py +0 -106
  235. jj_stack-0.1.3/src/jj_stack/commands/merge/plan.py +0 -108
  236. jj_stack-0.1.3/src/jj_stack/commands/merge/preconditions.py +0 -169
  237. jj_stack-0.1.3/src/jj_stack/commands/merge/render.py +0 -54
  238. jj_stack-0.1.3/src/jj_stack/commands/submit/command.py +0 -850
  239. jj_stack-0.1.3/src/jj_stack/commands/submit/comments.py +0 -64
  240. jj_stack-0.1.3/src/jj_stack/commands/submit/descriptions.py +0 -637
  241. jj_stack-0.1.3/src/jj_stack/commands/submit/models.py +0 -207
  242. jj_stack-0.1.3/src/jj_stack/commands/submit/render.py +0 -124
  243. jj_stack-0.1.3/src/jj_stack/commands/sync.py +0 -507
  244. jj_stack-0.1.3/src/jj_stack/commands/view.py +0 -1041
  245. jj_stack-0.1.3/src/jj_stack/formatting.py +0 -133
  246. jj_stack-0.1.3/src/jj_stack/github/error_messages.py +0 -79
  247. jj_stack-0.1.3/src/jj_stack/models/tracking.py +0 -93
  248. jj_stack-0.1.3/src/jj_stack/stack/pr_branches.py +0 -123
  249. jj_stack-0.1.3/src/jj_stack/stack/pr_facts.py +0 -203
  250. jj_stack-0.1.3/src/jj_stack/stack/status.py +0 -544
  251. jj_stack-0.1.3/src/jj_stack/stack/trunk.py +0 -25
  252. jj_stack-0.1.3/src/jj_stack/stack/trunk_evidence.py +0 -149
  253. jj_stack-0.1.3/src/jj_stack/state/migrations.py +0 -63
  254. jj_stack-0.1.3/tests/property/test_submit_property_scenarios.py +0 -382
  255. jj_stack-0.1.3/tests/run_submit_property_scenarios.py +0 -246
  256. jj_stack-0.1.3/tests/support/submit_property_harness.py +0 -1117
  257. jj_stack-0.1.3/tests/support/submit_property_scenarios.py +0 -1075
  258. jj_stack-0.1.3/tests/unit/conftest.py +0 -14
  259. jj_stack-0.1.3/tests/unit/test_cleanup.py +0 -72
  260. jj_stack-0.1.3/tests/unit/test_cli.py +0 -297
  261. jj_stack-0.1.3/tests/unit/test_config.py +0 -193
  262. jj_stack-0.1.3/tests/unit/test_describe_with_prompt_script.py +0 -67
  263. jj_stack-0.1.3/tests/unit/test_divergence.py +0 -15
  264. jj_stack-0.1.3/tests/unit/test_list.py +0 -25
  265. jj_stack-0.1.3/tests/unit/test_merge.py +0 -244
  266. jj_stack-0.1.3/tests/unit/test_pr_refs.py +0 -51
  267. jj_stack-0.1.3/tests/unit/test_property_runner.py +0 -50
  268. jj_stack-0.1.3/tests/unit/test_selection.py +0 -9
  269. jj_stack-0.1.3/tests/unit/test_stack_status.py +0 -260
  270. jj_stack-0.1.3/tests/unit/test_submit.py +0 -253
  271. jj_stack-0.1.3/tests/unit/test_submit_descriptions.py +0 -354
  272. jj_stack-0.1.3/tests/unit/test_trunk_evidence.py +0 -169
  273. jj_stack-0.1.3/tests/unit/test_ui.py +0 -270
  274. jj_stack-0.1.3/tests/unit/test_view.py +0 -368
  275. jj_stack-0.1.3/tests/unit/test_view_entrypoint.py +0 -252
  276. jj_stack-0.1.3/uv.lock +0 -595
  277. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  278. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  279. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  280. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/SECURITY.md +0 -0
  281. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/dependabot.yml +0 -0
  282. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/pull_request_template.md +0 -0
  283. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/workflows/block-pr-base-merges.yml +0 -0
  284. {jj_stack-0.1.3 → jj_stack-0.1.5}/.github/workflows/check-jj-release-updates.yml +0 -0
  285. {jj_stack-0.1.3 → jj_stack-0.1.5}/CLAUDE.md +0 -0
  286. {jj_stack-0.1.3 → jj_stack-0.1.5}/LICENSE +0 -0
  287. {jj_stack-0.1.3 → jj_stack-0.1.5}/NOTICE +0 -0
  288. {jj_stack-0.1.3 → jj_stack-0.1.5}/release-notes/v0.1.3.md +0 -0
  289. {jj_stack-0.1.3 → jj_stack-0.1.5}/scripts/README.md +0 -0
  290. {jj_stack-0.1.3 → jj_stack-0.1.5}/skills/jj-stack/agents/openai.yaml +0 -0
  291. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/__init__.py +0 -0
  292. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/__main__.py +0 -0
  293. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/__init__.py +0 -0
  294. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/merge/__init__.py +0 -0
  295. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/__init__.py +0 -0
  296. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/commands/submit/default_pr_text.py +0 -0
  297. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/__init__.py +0 -0
  298. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/auth.py +0 -0
  299. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/overview_comments.py +0 -0
  300. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/github/stack_availability.py +0 -0
  301. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/jj/__init__.py +0 -0
  302. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/models/__init__.py +0 -0
  303. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/models/git.py +0 -0
  304. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/__init__.py +0 -0
  305. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/stack/divergence.py +0 -0
  306. {jj_stack-0.1.3 → jj_stack-0.1.5}/src/jj_stack/state/__init__.py +0 -0
  307. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/__init__.py +0 -0
  308. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/integration/__init__.py +0 -0
  309. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/support/__init__.py +0 -0
  310. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/__init__.py +0 -0
  311. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_concurrency.py +0 -0
  312. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_default_pr_text.py +0 -0
  313. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_github_auth.py +0 -0
  314. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_live_github_runner.py +0 -0
  315. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_property_scenarios.py +0 -0
  316. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_sync.py +0 -0
  317. {jj_stack-0.1.3 → jj_stack-0.1.5}/tests/unit/test_unstack.py +0 -0
  318. {jj_stack-0.1.3 → jj_stack-0.1.5}/tools/check_release_artifacts.py +0 -0
  319. {jj_stack-0.1.3 → jj_stack-0.1.5}/tools/install-jj-release.sh +0 -0
@@ -18,7 +18,7 @@ jobs:
18
18
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
19
19
 
20
20
  - name: Set up uv
21
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
21
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
22
22
  with:
23
23
  python-version: "3.14"
24
24
 
@@ -26,9 +26,9 @@ jobs:
26
26
  run: |
27
27
  TOKEI_VERSION="$(
28
28
  uv run --no-project --python 3.14 python -c \
29
- 'from tools.check_complexity import TOKEI_VERSION; print(TOKEI_VERSION)'
29
+ 'from tools.check_complexity import TOKEI_VERSIONS; print(TOKEI_VERSIONS[-1])'
30
30
  )"
31
- cargo install tokei --locked --version "$TOKEI_VERSION"
31
+ cargo install tokei --locked --version "^$TOKEI_VERSION"
32
32
 
33
33
  - name: Install project environment
34
34
  run: uv sync --locked
@@ -52,7 +52,7 @@ jobs:
52
52
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
53
53
 
54
54
  - name: Set up uv
55
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
55
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
56
56
  with:
57
57
  python-version: ${{ matrix.python-version }}
58
58
 
@@ -79,18 +79,13 @@ jobs:
79
79
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
80
80
 
81
81
  - name: Set up uv
82
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
82
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
83
83
  with:
84
84
  python-version: "3.14"
85
85
 
86
86
  - name: Install jj
87
87
  run: echo "$(tools/install-jj-release.sh "$JJ_VERSION")" >> "$GITHUB_PATH"
88
88
 
89
- - name: Run submit property smoke scenarios
89
+ - name: Run generated client and server sequences
90
90
  run: >-
91
- tests/run_submit_property_scenarios.py 10 --random-seed -n auto
92
- --stack-join-scenarios 5
93
- --lifecycle-scenarios 10
94
- --stack-move-scenarios 5
95
- --retry-scenarios 5
96
- --drift-scenarios 21
91
+ tests/run_submit_property_scenarios.py 5 --steps 20 --shards 8 --random-seed -n auto
@@ -23,10 +23,10 @@ jobs:
23
23
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
24
 
25
25
  - name: Initialize CodeQL
26
- uses: github/codeql-action/init@v4.37.8
26
+ uses: github/codeql-action/init@v4.37.9
27
27
  with:
28
28
  languages: python
29
29
  build-mode: none
30
30
 
31
31
  - name: Analyze
32
- uses: github/codeql-action/analyze@v4.37.8
32
+ uses: github/codeql-action/analyze@v4.37.9
@@ -34,7 +34,7 @@ jobs:
34
34
  ref: ${{ inputs.release_tag || github.ref }}
35
35
 
36
36
  - name: Set up uv
37
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
37
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
38
38
  with:
39
39
  python-version: "3.14"
40
40
 
@@ -94,7 +94,7 @@ jobs:
94
94
 
95
95
  steps:
96
96
  - name: Set up uv
97
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
97
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
98
98
  with:
99
99
  python-version: "3.14"
100
100
 
@@ -127,7 +127,7 @@ jobs:
127
127
 
128
128
  steps:
129
129
  - name: Set up uv
130
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
130
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
131
131
  with:
132
132
  python-version: "3.14"
133
133
 
@@ -10,3 +10,5 @@ htmlcov/
10
10
  .uv-cache/
11
11
  .venv
12
12
  .venv/
13
+
14
+ .hypothesis/
@@ -1,15 +1,15 @@
1
- ## Complexity control
1
+ # Complexity control
2
2
 
3
3
  - A replacement is incomplete until it deletes the mechanism it supersedes in the same change.
4
- Do not add a temporary parallel model with a promise to remove it in a later cleanup slice.
5
- - Define each jj-stack-owned durable policy fact once and store it one way. Shared observation or
6
- storage code must not create a second path for deciding or changing it.
4
+ Do not keep a parallel model for a later cleanup.
5
+ - Store each persistent fact in one form, with one owner. Shared observation and storage code
6
+ must not introduce competing policy decisions.
7
7
  - Batch independent read-only facts, but keep dependent mutations in order. Bind an irreversible
8
8
  external mutation to the identity and version observed while planning when the platform
9
9
  supports a conditional write or lease. Re-observe only when an earlier mutation invalidates a
10
10
  precondition or when an observed trigger or platform contract requires it.
11
- - Apply the cumulative complexity budgets after every code slice. Run `just complexity` locally
12
- when the pinned `tokei` is installed; CI invokes the underlying checker. A budget increase is a
11
+ - Check the cumulative complexity budgets after each code change. Run `just complexity` locally
12
+ when a supported `tokei` is installed; CI invokes the underlying checker. A budget increase is a
13
13
  design stop that requires explicit review, not routine maintenance of the budget file.
14
14
  - If the same subsystem needs a third consecutive hardening change, stop patching it and
15
15
  re-derive the design from the core invariants.
@@ -38,25 +38,26 @@
38
38
 
39
39
  # Documentation
40
40
 
41
- - User-facing docs live in `docs/`. See [docs/AGENTS.md](docs/AGENTS.md) for the vocabulary
42
- rules and the public/internal split. Built-in `--help` text is held to the same standard as
43
- the user docs: assume jj/git familiarity, avoid `jj-stack` internal design jargon.
41
+ - User-facing docs live in `docs/`, except for contributor notes in `docs/internals/`. See
42
+ [docs/AGENTS.md](docs/AGENTS.md) for the vocabulary rules and the public/internal split.
43
+ Built-in `--help` text is held to the same standard as the user docs: assume jj/git familiarity,
44
+ avoid `jj-stack` internal design jargon.
44
45
  - The web version of the user docs normally lives in the sibling jj repository at
45
46
  `$(jj root)/../website`. When user-facing docs change here, run `just website`, inspect the
46
47
  corresponding website changes, and update them as needed. If the change here is committed,
47
48
  commit the corresponding website update in that repository as well; preserve unrelated work in
48
49
  either working copy.
49
50
  - Active internal docs use ordinary technical language too. Introduce a project-specific term
50
- only when it names a real type, field, or enduring rule, define it at first use, and prefer
51
+ only when it names an existing type, field, or enduring rule, define it at first use, and prefer
51
52
  describing concrete inputs and effects.
52
- - `design.md` and `implementation-strategy.md` describe the current product and architecture, not
53
- completed slices or abandoned mechanisms. Keep implementation history in `jj` commits.
54
- - Default to code and tests only. Update documentation only when a change intentionally adds or
55
- changes a supported product rule or user workflow, or makes a specific existing statement
56
- materially inaccurate. A bug and its fix are not by themselves a documentation trigger.
53
+ - Internal design and strategy documents describe the current product and architecture. Keep
54
+ implementation history in `jj` commits.
55
+ - For implementation work, update documentation when a change adds or alters a supported rule or
56
+ workflow, or makes an existing statement inaccurate. A bug fix alone does not require a docs
57
+ change. Documentation reviews and corrections can also address clarity, duplication, and gaps.
57
58
  - When documentation is required, update only the affected source: `design.md` for an enduring
58
- product rule, user docs or `--help` for user guidance, and `implementation-strategy.md` for an
59
- architecture, tooling, or test-layer strategy change.
59
+ product rule, user docs or `--help` for user guidance, and the relevant testing document for
60
+ test strategy.
60
61
 
61
62
  # Behaviour changes
62
63
 
@@ -67,8 +68,9 @@
67
68
  the other way around: evaluate a documented behavior on its merits before extending it, and
68
69
  prefer deleting case-specific rules that follow from the principles over adding new ones. Never
69
70
  add durable transaction or replay state; recovery is observational (see design.md).
70
- - Preserve the core invariants: the `jj` DAG determines stack topology, local cache is sparse,
71
- GitHub pull requests are derived from the local `jj` stack, and ambiguous linkage fails closed.
71
+ - Preserve the core invariants: the `jj` DAG determines stack topology, tracking stores only PR
72
+ links and submitted commits, GitHub PRs follow the local stack, and ambiguous linkage stops
73
+ mutation.
72
74
 
73
75
  # Testing
74
76
 
@@ -13,7 +13,8 @@ discussion.
13
13
 
14
14
  ## Development setup
15
15
 
16
- You need `uv`, `jj` 0.45.1 or newer, and `just`. Then:
16
+ You need `uv`, Git, `jj` 0.45.1 or newer, and `just`. The project requires Python 3.14 or newer;
17
+ `uv` can provide the interpreter when it creates the locked development environment:
17
18
 
18
19
  ```console
19
20
  just setup
@@ -30,12 +31,31 @@ virtualenv path directly:
30
31
  just run view
31
32
  ```
32
33
 
33
- Run the standard Ruff, type-check, and test pass before you finish:
34
+ Before finishing a code change, run the standard Ruff, type-check, and test pass. Docs-only edits
35
+ under `docs/` do not require it:
34
36
 
35
37
  ```console
36
38
  just check
37
39
  ```
38
40
 
41
+ Pyrefly is the default type checker, including a second pass targeting Windows. To select ty
42
+ for the current platform instead, use `--type-checker` or its short form, `-t`:
43
+
44
+ ```console
45
+ just check -t ty
46
+ ```
47
+
48
+ Repeat the option to run several in the given order, for example `just check -t pyrefly -t ty`.
49
+ The runner installs the selected checkers from the lockfile, checks `src`, `tests`, `tools`, and
50
+ `check.py`, and runs the usual Ruff and pytest checks. It stops at the first failed check. ty is
51
+ optional during development and can report issues that Pyrefly does not; its failures are
52
+ reported normally. Plain `just check` and CI continue to use Pyrefly. Before a release, follow the
53
+ [requirement for both type checkers to pass](docs/internals/releasing.md#qualify-the-candidate).
54
+
55
+ mypy is deliberately not part of the toolchain. It fixes a variable's type at its first
56
+ assignment and rejects a later assignment of another type, which Pyrefly and ty accept, so
57
+ keeping it green required annotations whose only purpose was to satisfy mypy.
58
+
39
59
  Run focused tests while you iterate:
40
60
 
41
61
  ```console
@@ -43,10 +63,10 @@ just test tests/unit/test_jj_client.py
43
63
  ```
44
64
 
45
65
  CI also enforces the cumulative complexity budgets. Check them locally with `just complexity`
46
- when the pinned `tokei` is installed. Raising a budget is a design decision, not routine
66
+ when a supported `tokei` is installed. Raising a budget is a design decision, not routine
47
67
  maintenance — say why in the pull request.
48
68
 
49
- If your change touches user-facing documentation, refresh the website snapshot with
69
+ If your change touches user-facing guides or references, refresh the website snapshot with
50
70
  `just website` and include the resulting change in the sibling `website` repository.
51
71
 
52
72
  ## Conventions
@@ -62,8 +82,8 @@ The short version:
62
82
  - Explain *why* the change exists in the body, wrapped at 72 columns.
63
83
  - Add tests at the narrowest layer that covers a distinct risk; consolidate overlapping coverage
64
84
  rather than adding parallel cases.
65
- - Update documentation only when a change adds or alters a supported rule or workflow, or makes
66
- an existing statement inaccurate.
85
+ - For code changes, update docs when supported rules, workflows, or existing statements change.
86
+ Documentation corrections and editorial improvements are welcome independently.
67
87
 
68
88
  ## Submitting
69
89
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: jj-stack
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Stacked GitHub pull requests for Jujutsu
5
5
  Project-URL: Homepage, https://www.serpentine.com/software/jj-stack/
6
6
  Project-URL: Documentation, https://www.serpentine.com/software/jj-stack/
@@ -29,64 +29,26 @@ Description-Content-Type: text/markdown
29
29
  # jj-stack: manage stacked GitHub PRs with jj
30
30
 
31
31
  `jj-stack` turns a linear series of local `jj` changes into a stack of GitHub pull requests.
32
- Rewrite, split, squash, or reorder the changes with `jj`, then let `jj-stack` update the
33
- matching PRs.
32
+ Rewrite, split, squash, or reorder the changes with `jj`, then run `jj-stack submit` to update
33
+ GitHub. Existing PRs follow their change IDs, keeping comments and review history together.
34
34
 
35
35
  ## Quick start
36
36
 
37
- ### Requirements
37
+ You need Python 3.14 or newer, `jj` 0.45.1 or newer, and a repo on github.com where you can push
38
+ branches and open pull requests. `jj-stack` uses `GITHUB_TOKEN`, then `GH_TOKEN`, then your GitHub
39
+ CLI login for authentication.
38
40
 
39
- - Python 3.14 or newer
40
- - `jj` 0.45.1 or newer
41
- - GitHub authentication
42
-
43
- ### Install
44
-
45
- Install `jj-stack` from PyPI with `uv` in an isolated tool environment (recommended):
41
+ Install with `uv`:
46
42
 
47
43
  ```bash
48
44
  uv tool install jj-stack
49
45
  ```
50
46
 
51
- `pipx` provides another isolated installation:
52
-
53
- ```bash
54
- pipx install jj-stack
55
- ```
56
-
57
- You can also use `pip` inside an activated virtual environment:
58
-
59
- ```bash
60
- python -m pip install jj-stack
61
- ```
62
-
63
- To upgrade an installation made with `uv`, rerun its command with `--force`. If the command is
64
- not on your shell `PATH`, run `uv tool update-shell`.
65
-
66
- ### Invoke it as `jj stack`
67
-
68
- Add a command alias to your user configuration with `jj config edit --user`:
69
-
70
- ```toml
71
- [aliases]
72
- stack = ["util", "exec", "--", "jj-stack"]
73
- ```
74
-
75
- For tab completion of both `jj-stack` and `jj stack`, add the output of `jj-stack completion` to
76
- your shell startup file:
77
-
78
- ```bash
79
- eval "$(jj-stack completion zsh --jj-alias stack)"
80
- ```
81
-
82
- `bash` and `fish` work the same way. See
83
- [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/) for more
84
- setup options.
85
-
86
47
  ### Submit your first stack
87
48
 
88
- Start with a linear series of local `jj` changes on top of `trunk()`. In a new repo, check
89
- the setup and apply the safe local fixes:
49
+ Start with a linear series of local `jj` changes on top of `trunk()`. Authenticate with
50
+ `gh auth login`, or supply a token in `GITHUB_TOKEN` or `GH_TOKEN`. Check the repo setup and
51
+ apply the safe local fixes:
90
52
 
91
53
  ```bash
92
54
  jj-stack doctor --fix
@@ -110,19 +72,26 @@ refresh. Use `jj-stack list` to see every tracked stack in the repo.
110
72
  ## Mental model
111
73
 
112
74
  Your local `jj` history determines which changes form a stack and their order. On GitHub, each
113
- change gets a stable PR branch and a PR; every PR targets the PR branch below it, except the
114
- bottom PR, which targets trunk by default:
75
+ change gets a stable PR branch and a PR. The bottom PR targets trunk by default, and each PR
76
+ above it targets the PR branch below:
115
77
 
116
78
  ```text
117
- jj-stack/add-ui-... -> PR #3 (base: jj-stack/add-api-...)
118
- jj-stack/add-api-... -> PR #2 (base: jj-stack/refactor-model-...)
119
- jj-stack/refactor-model-... -> PR #1 (base: main)
120
- main -> trunk
79
+ Local changes: trunk() <- A <- B <- C
80
+ GitHub PRs: main <- PR #1 <- PR #2 <- PR #3
121
81
  ```
122
82
 
123
- The PR branches normally stay out of your local bookmark view. When you rewrite a change,
124
- `jj-stack` updates that change's existing PR branch and PR, along with the PR branches and PRs for
125
- dependent changes.
83
+ Each PR's diff shows only the changes it adds on top of its base, so reviewers can consider
84
+ one change at a time. `jj-stack` manages the PR branches for you, and they normally stay out
85
+ of your local bookmark view.
86
+
87
+ When you rewrite a change, its change ID still connects it to the same PR. Submitting again
88
+ updates that PR and the PRs for any dependent changes, preserving their discussions.
89
+
90
+ To select a stack, pass the change ID of its head (the top change). `jj-stack` follows the
91
+ head's parents back to trunk to find the rest. If you don't name a head, it uses your working
92
+ copy when it has both a description and changes, or its parent otherwise. After editing a
93
+ lower change, pass the head's ID to `jj-stack submit` so the update includes the changes above
94
+ your edit.
126
95
 
127
96
  ## Everyday workflow
128
97
 
@@ -130,9 +99,11 @@ dependent changes.
130
99
  2. Run `jj-stack submit`.
131
100
  3. Revise, add, remove, or reorder the changes locally as reviews come in.
132
101
  4. Run `jj-stack submit` again to refresh GitHub.
133
- 5. Run `jj-stack merge` when the changes at the bottom are ready.
134
- 6. After a queued or externally initiated merge finishes, run
135
- `jj-stack sync <head-change-id>`.
102
+ 5. Run `jj-stack merge` when the PRs at the bottom are ready. Pass `--pull-request <pr>` to
103
+ stop at an earlier PR, and `--method` to choose a merge method your repo allows. The command
104
+ waits for GitHub, including its merge queue, then updates your local stack and remaining PRs.
105
+ 6. If you used `--no-wait`, interrupted the wait, or merged through GitHub, run
106
+ `jj-stack sync <head-change-id>` after GitHub finishes.
136
107
 
137
108
  `view`, `submit`, `merge`, and `sync` accept a change ID when you need to select a stack other
138
109
  than the one ending at the working copy.
@@ -140,19 +111,58 @@ than the one ending at the working copy.
140
111
  See the [user guide](https://www.serpentine.com/software/jj-stack/) for drafts, descriptions,
141
112
  merge queues, cleanup, and working with multiple stacks.
142
113
 
114
+ ## Optional setup
115
+
116
+ ### Invoke it as `jj stack`
117
+
118
+ Add a command alias to your user configuration with `jj config edit --user`:
119
+
120
+ ```toml
121
+ [aliases]
122
+ stack = ["util", "exec", "--", "jj-stack"]
123
+ ```
124
+
125
+ For tab completion of both `jj-stack` and `jj stack`, add the output of `jj-stack completion` to
126
+ your shell startup file:
127
+
128
+ ```bash
129
+ eval "$(jj-stack completion zsh --jj-alias stack)"
130
+ ```
131
+
132
+ `bash` and `fish` work the same way. See
133
+ [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/) for more
134
+ setup options.
135
+
136
+ ### Other installation options
137
+
138
+ `pipx` provides another isolated installation:
139
+
140
+ ```bash
141
+ pipx install jj-stack
142
+ ```
143
+
144
+ You can also use `pip` inside an activated virtual environment:
145
+
146
+ ```bash
147
+ python -m pip install jj-stack
148
+ ```
149
+
150
+ To upgrade an installation made with `uv`, run `uv tool upgrade jj-stack`. If the command is
151
+ not on your shell `PATH`, run `uv tool update-shell`.
152
+
143
153
  ## Learn more
144
154
 
145
- - [Mental model](https://www.serpentine.com/software/jj-stack/mental-model/)
146
- - [Quick start](https://www.serpentine.com/software/jj-stack/quick-start/)
147
- - [Everyday workflows](https://www.serpentine.com/software/jj-stack/guides/submit-and-update/)
155
+ - [How jj-stack works](https://www.serpentine.com/software/jj-stack/mental-model/)
156
+ - [Submit and update](https://www.serpentine.com/software/jj-stack/guides/submit-and-update/)
157
+ - [Merge and sync](https://www.serpentine.com/software/jj-stack/guides/merge-and-sync/)
158
+ - [Multiple stacks](https://www.serpentine.com/software/jj-stack/guides/multiple-stacks/)
148
159
  - [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/)
149
160
  - [Writing PR descriptions](https://www.serpentine.com/software/jj-stack/reference/descriptions/)
150
161
  - [Troubleshooting](https://www.serpentine.com/software/jj-stack/troubleshooting/)
151
- - [Tool comparison](https://www.serpentine.com/software/jj-stack/tool-comparison/)
152
- - [JSON output](https://www.serpentine.com/software/jj-stack/reference/json-output/)
153
- - [Automation and exit codes](https://www.serpentine.com/software/jj-stack/reference/automation/)
162
+ - [Compare tools](https://www.serpentine.com/software/jj-stack/tool-comparison/)
163
+ - [Automation](https://www.serpentine.com/software/jj-stack/reference/automation/)
154
164
 
155
- The built-in help is the canonical flag reference:
165
+ For all flags and aliases, use the built-in help:
156
166
 
157
167
  ```bash
158
168
  jj-stack --help
@@ -160,42 +170,18 @@ jj-stack <command> --help
160
170
  jj-stack help --all
161
171
  ```
162
172
 
163
- ## Development
164
-
165
- Contributor workflows live in the [`justfile`](justfile). With `uv`, `jj`, and `just` installed,
166
- run `just` to list the setup, formatting, focused test, verification, documentation, and release
167
- recipes.
168
-
169
173
  ## Coding agent integration
170
174
 
171
- Install the bundled skill to teach coding agents to work with local `jj` stacks and refresh their
172
- GitHub PRs safely:
175
+ Install the bundled skill to give coding agents instructions for working with `jj-stack`:
173
176
 
174
177
  ```bash
175
178
  gh skill install bos/jj-stack jj-stack
176
179
  ```
177
180
 
178
- See the [skill source](https://github.com/bos/jj-stack/blob/main/skills/jj-stack/SKILL.md). In
179
- my evaluations with Codex and Claude Code, agents with the `jj-stack` skill succeeded in 11/12
180
- scenarios versus 6/12 without it, with one critical error versus four, using 60% fewer failed
181
- command attempts and 18% fewer tool calls. Treat that as a small pilot rather than a published
182
- benchmark:
183
- [`evals/jj-stack-skill.md`](https://github.com/bos/jj-stack/blob/main/evals/jj-stack-skill.md)
184
- gives the evaluation design, but this repo does not include the traces behind those numbers.
185
- (The critical error was due to Claude Haiku understanding a rule and ignoring it. I haven't
186
- figured out how to get smaller Claude models to behave better, and I don't personally use them.)
187
-
188
- ## Performance
189
-
190
- Although `jj-stack` is written in Python, this does not significantly affect its speed.
191
- The real determinants of its performance are the GitHub API and the `jj` command.
181
+ The [skill source](skills/jj-stack/SKILL.md) and [evaluation notes](evals/jj-stack-skill.md) are
182
+ included in this repo.
192
183
 
193
- The GitHub API is *slow*; a single roundtrip takes many hundreds of milliseconds. `jj-stack`
194
- reduces its impact with:
195
-
196
- - GraphQL batch requests where possible
197
- - concurrent use of the GitHub REST API
198
- - periodic audits that its queries are minimal in extent
184
+ ## Development
199
185
 
200
- In pursuit of good performance, `jj-stack` also batches calls to `jj` and minimizes the amount
201
- of work those calls must do.
186
+ With `uv`, `jj`, and `just` installed, run `just` to list the development workflows. See
187
+ [CONTRIBUTING.md](CONTRIBUTING.md) for setup and validation instructions.
@@ -0,0 +1,159 @@
1
+ # jj-stack: manage stacked GitHub PRs with jj
2
+
3
+ `jj-stack` turns a linear series of local `jj` changes into a stack of GitHub pull requests.
4
+ Rewrite, split, squash, or reorder the changes with `jj`, then run `jj-stack submit` to update
5
+ GitHub. Existing PRs follow their change IDs, keeping comments and review history together.
6
+
7
+ ## Quick start
8
+
9
+ You need Python 3.14 or newer, `jj` 0.45.1 or newer, and a repo on github.com where you can push
10
+ branches and open pull requests. `jj-stack` uses `GITHUB_TOKEN`, then `GH_TOKEN`, then your GitHub
11
+ CLI login for authentication.
12
+
13
+ Install with `uv`:
14
+
15
+ ```bash
16
+ uv tool install jj-stack
17
+ ```
18
+
19
+ ### Submit your first stack
20
+
21
+ Start with a linear series of local `jj` changes on top of `trunk()`. Authenticate with
22
+ `gh auth login`, or supply a token in `GITHUB_TOKEN` or `GH_TOKEN`. Check the repo setup and
23
+ apply the safe local fixes:
24
+
25
+ ```bash
26
+ jj-stack doctor --fix
27
+ ```
28
+
29
+ Inspect the stack that ends at your working copy:
30
+
31
+ ```bash
32
+ jj-stack
33
+ ```
34
+
35
+ Create one GitHub PR per local change:
36
+
37
+ ```bash
38
+ jj-stack submit
39
+ ```
40
+
41
+ Revise the changes locally with `jj` and rerun `jj-stack submit` whenever the stack is ready to
42
+ refresh. Use `jj-stack list` to see every tracked stack in the repo.
43
+
44
+ ## Mental model
45
+
46
+ Your local `jj` history determines which changes form a stack and their order. On GitHub, each
47
+ change gets a stable PR branch and a PR. The bottom PR targets trunk by default, and each PR
48
+ above it targets the PR branch below:
49
+
50
+ ```text
51
+ Local changes: trunk() <- A <- B <- C
52
+ GitHub PRs: main <- PR #1 <- PR #2 <- PR #3
53
+ ```
54
+
55
+ Each PR's diff shows only the changes it adds on top of its base, so reviewers can consider
56
+ one change at a time. `jj-stack` manages the PR branches for you, and they normally stay out
57
+ of your local bookmark view.
58
+
59
+ When you rewrite a change, its change ID still connects it to the same PR. Submitting again
60
+ updates that PR and the PRs for any dependent changes, preserving their discussions.
61
+
62
+ To select a stack, pass the change ID of its head (the top change). `jj-stack` follows the
63
+ head's parents back to trunk to find the rest. If you don't name a head, it uses your working
64
+ copy when it has both a description and changes, or its parent otherwise. After editing a
65
+ lower change, pass the head's ID to `jj-stack submit` so the update includes the changes above
66
+ your edit.
67
+
68
+ ## Everyday workflow
69
+
70
+ 1. Write code as a series of local `jj` changes.
71
+ 2. Run `jj-stack submit`.
72
+ 3. Revise, add, remove, or reorder the changes locally as reviews come in.
73
+ 4. Run `jj-stack submit` again to refresh GitHub.
74
+ 5. Run `jj-stack merge` when the PRs at the bottom are ready. Pass `--pull-request <pr>` to
75
+ stop at an earlier PR, and `--method` to choose a merge method your repo allows. The command
76
+ waits for GitHub, including its merge queue, then updates your local stack and remaining PRs.
77
+ 6. If you used `--no-wait`, interrupted the wait, or merged through GitHub, run
78
+ `jj-stack sync <head-change-id>` after GitHub finishes.
79
+
80
+ `view`, `submit`, `merge`, and `sync` accept a change ID when you need to select a stack other
81
+ than the one ending at the working copy.
82
+
83
+ See the [user guide](https://www.serpentine.com/software/jj-stack/) for drafts, descriptions,
84
+ merge queues, cleanup, and working with multiple stacks.
85
+
86
+ ## Optional setup
87
+
88
+ ### Invoke it as `jj stack`
89
+
90
+ Add a command alias to your user configuration with `jj config edit --user`:
91
+
92
+ ```toml
93
+ [aliases]
94
+ stack = ["util", "exec", "--", "jj-stack"]
95
+ ```
96
+
97
+ For tab completion of both `jj-stack` and `jj stack`, add the output of `jj-stack completion` to
98
+ your shell startup file:
99
+
100
+ ```bash
101
+ eval "$(jj-stack completion zsh --jj-alias stack)"
102
+ ```
103
+
104
+ `bash` and `fish` work the same way. See
105
+ [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/) for more
106
+ setup options.
107
+
108
+ ### Other installation options
109
+
110
+ `pipx` provides another isolated installation:
111
+
112
+ ```bash
113
+ pipx install jj-stack
114
+ ```
115
+
116
+ You can also use `pip` inside an activated virtual environment:
117
+
118
+ ```bash
119
+ python -m pip install jj-stack
120
+ ```
121
+
122
+ To upgrade an installation made with `uv`, run `uv tool upgrade jj-stack`. If the command is
123
+ not on your shell `PATH`, run `uv tool update-shell`.
124
+
125
+ ## Learn more
126
+
127
+ - [How jj-stack works](https://www.serpentine.com/software/jj-stack/mental-model/)
128
+ - [Submit and update](https://www.serpentine.com/software/jj-stack/guides/submit-and-update/)
129
+ - [Merge and sync](https://www.serpentine.com/software/jj-stack/guides/merge-and-sync/)
130
+ - [Multiple stacks](https://www.serpentine.com/software/jj-stack/guides/multiple-stacks/)
131
+ - [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/)
132
+ - [Writing PR descriptions](https://www.serpentine.com/software/jj-stack/reference/descriptions/)
133
+ - [Troubleshooting](https://www.serpentine.com/software/jj-stack/troubleshooting/)
134
+ - [Compare tools](https://www.serpentine.com/software/jj-stack/tool-comparison/)
135
+ - [Automation](https://www.serpentine.com/software/jj-stack/reference/automation/)
136
+
137
+ For all flags and aliases, use the built-in help:
138
+
139
+ ```bash
140
+ jj-stack --help
141
+ jj-stack <command> --help
142
+ jj-stack help --all
143
+ ```
144
+
145
+ ## Coding agent integration
146
+
147
+ Install the bundled skill to give coding agents instructions for working with `jj-stack`:
148
+
149
+ ```bash
150
+ gh skill install bos/jj-stack jj-stack
151
+ ```
152
+
153
+ The [skill source](skills/jj-stack/SKILL.md) and [evaluation notes](evals/jj-stack-skill.md) are
154
+ included in this repo.
155
+
156
+ ## Development
157
+
158
+ With `uv`, `jj`, and `just` installed, run `just` to list the development workflows. See
159
+ [CONTRIBUTING.md](CONTRIBUTING.md) for setup and validation instructions.