jj-stack 0.1.1__tar.gz → 0.1.2__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 (229) hide show
  1. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/workflows/ci.yml +3 -3
  2. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/workflows/codeql.yml +2 -2
  3. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/workflows/release.yml +30 -4
  4. {jj_stack-0.1.1 → jj_stack-0.1.2}/AGENTS.md +13 -17
  5. {jj_stack-0.1.1 → jj_stack-0.1.2}/PKG-INFO +25 -8
  6. {jj_stack-0.1.1 → jj_stack-0.1.2}/README.md +22 -7
  7. {jj_stack-0.1.1 → jj_stack-0.1.2}/check.py +10 -2
  8. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/README.md +2 -1
  9. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/submit-and-update.md +7 -0
  10. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/working-on-github.md +19 -16
  11. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/code-reviews.md +2 -2
  12. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/design.md +30 -10
  13. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/implementation-strategy.md +9 -0
  14. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/property-testing.md +2 -2
  15. jj_stack-0.1.2/docs/internals/releasing.md +71 -0
  16. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/json-output.schema.json +8 -0
  17. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/quick-start.md +19 -0
  18. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/reference/automation.md +3 -3
  19. jj_stack-0.1.2/docs/reference/bookmarks-and-selection.md +149 -0
  20. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/reference/commands.md +3 -0
  21. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/reference/configuration.md +10 -1
  22. jj_stack-0.1.2/docs/reference/descriptions.md +84 -0
  23. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/reference/json-output.md +14 -7
  24. jj_stack-0.1.2/docs/tool-comparison.md +215 -0
  25. jj_stack-0.1.2/justfile +60 -0
  26. {jj_stack-0.1.1 → jj_stack-0.1.2}/pyproject.toml +5 -3
  27. jj_stack-0.1.2/release-notes/v0.1.2.md +34 -0
  28. {jj_stack-0.1.1 → jj_stack-0.1.2}/skills/jj-stack/SKILL.md +15 -4
  29. {jj_stack-0.1.1 → jj_stack-0.1.2}/skills/jj-stack/references/recovery.md +13 -9
  30. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/cli.py +8 -4
  31. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/_cleanup_actions.py +18 -21
  32. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/_json_status.py +1 -0
  33. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/checkout.py +25 -23
  34. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/cleanup/command.py +21 -15
  35. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/list_.py +59 -19
  36. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/command.py +10 -3
  37. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/github_stack.py +26 -7
  38. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/models.py +2 -0
  39. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/preconditions.py +17 -22
  40. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/relink.py +47 -26
  41. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/auto_close.py +3 -1
  42. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/command.py +77 -23
  43. jj_stack-0.1.2/src/jj_stack/commands/submit/comments.py +62 -0
  44. jj_stack-0.1.2/src/jj_stack/commands/submit/default_pr_text.py +54 -0
  45. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/descriptions.py +118 -25
  46. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/github_stack.py +47 -4
  47. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/inputs.py +49 -0
  48. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/models.py +5 -2
  49. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/overview_comments.py +72 -39
  50. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/prs.py +27 -39
  51. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/render.py +12 -6
  52. jj_stack-0.1.2/src/jj_stack/commands/submit/revision_comments.py +166 -0
  53. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/sync.py +13 -10
  54. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/sync_apply.py +16 -6
  55. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/unstack.py +6 -6
  56. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/view.py +72 -45
  57. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/completion.py +1 -1
  58. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/console.py +15 -2
  59. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/formatting.py +47 -8
  60. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/client.py +337 -133
  61. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/resolution.py +0 -6
  62. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/jj/client.py +23 -13
  63. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/models/github.py +37 -0
  64. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/models/tracking.py +4 -19
  65. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/change_status.py +7 -0
  66. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/convergence.py +16 -25
  67. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/convergence_observation.py +2 -10
  68. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/github_stack_safety.py +9 -3
  69. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/global_convergence.py +13 -18
  70. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/pr_branches.py +1 -4
  71. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/pr_facts.py +3 -6
  72. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/repo.py +4 -4
  73. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/selected.py +13 -3
  74. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/selection.py +27 -12
  75. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/status.py +14 -4
  76. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/trunk_evidence.py +11 -15
  77. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/state/migrations.py +15 -1
  78. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/ui.py +23 -1
  79. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_jj_stack.py +33 -3
  80. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_list_command.py +12 -2
  81. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_relink_command.py +19 -11
  82. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_submit_command.py +228 -10
  83. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_view_command.py +6 -3
  84. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/fake_github.py +64 -11
  85. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/integration_helpers.py +25 -9
  86. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/tracking.py +0 -6
  87. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_check_script.py +13 -0
  88. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_cleanup.py +8 -5
  89. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_completion.py +3 -0
  90. jj_stack-0.1.2/tests/unit/test_default_pr_text.py +56 -0
  91. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_github_client.py +110 -37
  92. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_github_models.py +27 -5
  93. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_github_stack_planning.py +35 -3
  94. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_in_use_command.py +0 -3
  95. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_jj_client.py +9 -3
  96. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_pr_branches.py +0 -11
  97. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_relink.py +0 -1
  98. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_store.py +22 -16
  99. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_submit.py +35 -28
  100. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_submit_descriptions.py +19 -2
  101. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_trunk_evidence.py +4 -19
  102. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_ui.py +24 -0
  103. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_view.py +54 -48
  104. {jj_stack-0.1.1 → jj_stack-0.1.2}/uv.lock +23 -21
  105. jj_stack-0.1.1/docs/gh-stack.md +0 -79
  106. jj_stack-0.1.1/docs/internals/releasing.md +0 -47
  107. jj_stack-0.1.1/docs/reference/descriptions.md +0 -59
  108. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/SECURITY.md +0 -0
  109. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/dependabot.yml +0 -0
  110. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/workflows/block-pr-base-merges.yml +0 -0
  111. {jj_stack-0.1.1 → jj_stack-0.1.2}/.github/workflows/check-jj-release-updates.yml +0 -0
  112. {jj_stack-0.1.1 → jj_stack-0.1.2}/.gitignore +0 -0
  113. {jj_stack-0.1.1 → jj_stack-0.1.2}/CLAUDE.md +0 -0
  114. {jj_stack-0.1.1 → jj_stack-0.1.2}/LICENSE +0 -0
  115. {jj_stack-0.1.1 → jj_stack-0.1.2}/NOTICE +0 -0
  116. {jj_stack-0.1.1 → jj_stack-0.1.2}/complexity-budget.toml +0 -0
  117. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/AGENTS.md +0 -0
  118. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/close-or-separate.md +0 -0
  119. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/continue-a-stack.md +0 -0
  120. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/merge-and-sync.md +0 -0
  121. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/multiple-stacks.md +0 -0
  122. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/review-a-stack.md +0 -0
  123. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/guides/revise.md +0 -0
  124. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/AGENTS.md +0 -0
  125. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/README.md +0 -0
  126. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/internals/testing-philosophy.md +0 -0
  127. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/mental-model.md +0 -0
  128. {jj_stack-0.1.1 → jj_stack-0.1.2}/docs/troubleshooting.md +0 -0
  129. {jj_stack-0.1.1 → jj_stack-0.1.2}/evals/jj-stack-skill.md +0 -0
  130. {jj_stack-0.1.1 → jj_stack-0.1.2}/scripts/README.md +0 -0
  131. {jj_stack-0.1.1 → jj_stack-0.1.2}/scripts/describe_with_claude.py +0 -0
  132. {jj_stack-0.1.1 → jj_stack-0.1.2}/scripts/describe_with_codex.py +0 -0
  133. {jj_stack-0.1.1 → jj_stack-0.1.2}/scripts/describe_with_editor.py +0 -0
  134. {jj_stack-0.1.1 → jj_stack-0.1.2}/scripts/describe_with_prompt.py +0 -0
  135. {jj_stack-0.1.1 → jj_stack-0.1.2}/skills/jj-stack/agents/openai.yaml +0 -0
  136. {jj_stack-0.1.1 → jj_stack-0.1.2}/skills/jj-stack/references/multi-stack.md +0 -0
  137. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/__init__.py +0 -0
  138. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/__main__.py +0 -0
  139. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/bootstrap.py +0 -0
  140. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/cli_help.py +0 -0
  141. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/__init__.py +0 -0
  142. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/cleanup/shared.py +0 -0
  143. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/cleanup/stale.py +0 -0
  144. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/doctor.py +0 -0
  145. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/in_use.py +0 -0
  146. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/__init__.py +0 -0
  147. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/plan.py +0 -0
  148. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/merge/render.py +0 -0
  149. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/__init__.py +0 -0
  150. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/commands/submit/changes.py +0 -0
  151. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/concurrency.py +0 -0
  152. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/config.py +0 -0
  153. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/errors.py +0 -0
  154. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/__init__.py +0 -0
  155. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/auth.py +0 -0
  156. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/error_messages.py +0 -0
  157. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/overview_comments.py +0 -0
  158. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/pr_refs.py +0 -0
  159. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/github/stack_availability.py +0 -0
  160. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/identifiers.py +0 -0
  161. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/jj/__init__.py +0 -0
  162. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/jj/cli_args.py +0 -0
  163. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/jj/colors.py +0 -0
  164. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/models/__init__.py +0 -0
  165. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/models/git.py +0 -0
  166. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/models/stack.py +0 -0
  167. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/pr_branch_namespace.py +0 -0
  168. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/__init__.py +0 -0
  169. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/convergence_models.py +0 -0
  170. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/stack/path.py +0 -0
  171. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/state/__init__.py +0 -0
  172. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/state/operation_lock.py +0 -0
  173. {jj_stack-0.1.1 → jj_stack-0.1.2}/src/jj_stack/state/store.py +0 -0
  174. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/__init__.py +0 -0
  175. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/conftest.py +0 -0
  176. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/__init__.py +0 -0
  177. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/conftest.py +0 -0
  178. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/submit_command_helpers.py +0 -0
  179. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_boundary_conditions.py +0 -0
  180. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_checkout_command.py +0 -0
  181. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_cleanup_command.py +0 -0
  182. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_doctor_command.py +0 -0
  183. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_merge_command.py +0 -0
  184. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_sync_command.py +0 -0
  185. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/integration/test_unstack_command.py +0 -0
  186. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/property/test_submit_property_scenarios.py +0 -0
  187. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/run_live_github.py +0 -0
  188. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/run_submit_property_scenarios.py +0 -0
  189. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/__init__.py +0 -0
  190. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/change_helpers.py +0 -0
  191. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/contexts.py +0 -0
  192. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/json_schema.py +0 -0
  193. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/output_assertions.py +0 -0
  194. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/pytest_concurrency.py +0 -0
  195. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/stack_edit_scenarios.py +0 -0
  196. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/submit_property_harness.py +0 -0
  197. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/support/submit_property_scenarios.py +0 -0
  198. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/__init__.py +0 -0
  199. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/conftest.py +0 -0
  200. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_bootstrap.py +0 -0
  201. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_change_status.py +0 -0
  202. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_change_status_topology.py +0 -0
  203. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_checkout.py +0 -0
  204. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_cli.py +0 -0
  205. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_cli_dispatch.py +0 -0
  206. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_concurrency.py +0 -0
  207. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_config.py +0 -0
  208. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_describe_with_editor_script.py +0 -0
  209. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_describe_with_prompt_script.py +0 -0
  210. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_errors.py +0 -0
  211. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_github_auth.py +0 -0
  212. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_github_resolution.py +0 -0
  213. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_list.py +0 -0
  214. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_live_github_runner.py +0 -0
  215. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_merge.py +0 -0
  216. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_operation_lock.py +0 -0
  217. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_pr_refs.py +0 -0
  218. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_property_runner.py +0 -0
  219. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_property_scenarios.py +0 -0
  220. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_selection.py +0 -0
  221. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_stack_path.py +0 -0
  222. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_stack_status.py +0 -0
  223. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_sync.py +0 -0
  224. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_unstack.py +0 -0
  225. {jj_stack-0.1.1 → jj_stack-0.1.2}/tests/unit/test_view_entrypoint.py +0 -0
  226. {jj_stack-0.1.1 → jj_stack-0.1.2}/tools/check_complexity.py +0 -0
  227. {jj_stack-0.1.1 → jj_stack-0.1.2}/tools/check_jj_release_updates.py +0 -0
  228. {jj_stack-0.1.1 → jj_stack-0.1.2}/tools/check_release_artifacts.py +0 -0
  229. {jj_stack-0.1.1 → jj_stack-0.1.2}/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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
21
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
22
22
  with:
23
23
  python-version: "3.14"
24
24
 
@@ -51,7 +51,7 @@ jobs:
51
51
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
52
52
 
53
53
  - name: Set up uv
54
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
54
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
55
55
  with:
56
56
  python-version: ${{ matrix.python-version }}
57
57
 
@@ -78,7 +78,7 @@ jobs:
78
78
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
79
79
 
80
80
  - name: Set up uv
81
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
81
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
82
82
  with:
83
83
  python-version: "3.14"
84
84
 
@@ -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.3
26
+ uses: github/codeql-action/init@v4.37.7
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.3
32
+ uses: github/codeql-action/analyze@v4.37.7
@@ -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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
37
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
38
38
  with:
39
39
  python-version: "3.14"
40
40
 
@@ -50,6 +50,17 @@ jobs:
50
50
  if: github.event_name == 'push' || inputs.release_tag != ''
51
51
  run: git merge-base --is-ancestor HEAD refs/remotes/origin/main
52
52
 
53
+ - name: Verify release notes
54
+ if: github.event_name == 'push' || inputs.release_tag != ''
55
+ env:
56
+ RELEASE_TAG: ${{ inputs.release_tag || github.ref_name }}
57
+ run: |
58
+ release_notes="release-notes/$RELEASE_TAG.md"
59
+ if ! test -s "$release_notes"; then
60
+ echo "::error file=$release_notes::Add human-written release notes for $RELEASE_TAG"
61
+ exit 1
62
+ fi
63
+
53
64
  - name: Install jj
54
65
  run: echo "$(tools/install-jj-release.sh "$JJ_VERSION")" >> "$GITHUB_PATH"
55
66
 
@@ -83,7 +94,7 @@ jobs:
83
94
 
84
95
  steps:
85
96
  - name: Set up uv
86
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
97
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
87
98
  with:
88
99
  python-version: "3.14"
89
100
 
@@ -116,7 +127,7 @@ jobs:
116
127
 
117
128
  steps:
118
129
  - name: Set up uv
119
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
130
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
120
131
  with:
121
132
  python-version: "3.14"
122
133
 
@@ -141,6 +152,11 @@ jobs:
141
152
  contents: write
142
153
 
143
154
  steps:
155
+ - name: Check out release
156
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
157
+ with:
158
+ ref: ${{ inputs.release_tag || github.ref }}
159
+
144
160
  - name: Download distributions
145
161
  uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
146
162
  with:
@@ -152,12 +168,22 @@ jobs:
152
168
  GH_TOKEN: ${{ github.token }}
153
169
  RELEASE_TAG: ${{ inputs.release_tag || github.ref_name }}
154
170
  run: |
171
+ release_notes="release-notes/$RELEASE_TAG.md"
155
172
  if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
173
+ expected_notes="$(cat "$release_notes")"
174
+ published_notes="$(
175
+ gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
176
+ --json body --jq .body
177
+ )"
178
+ if test "$published_notes" != "$expected_notes"; then
179
+ echo "::error file=$release_notes::Published notes do not match $release_notes"
180
+ exit 1
181
+ fi
156
182
  for artifact in dist/*; do
157
183
  gh release verify-asset "$RELEASE_TAG" "$artifact" \
158
184
  --repo "$GITHUB_REPOSITORY"
159
185
  done
160
186
  else
161
187
  gh release create "$RELEASE_TAG" dist/* --repo "$GITHUB_REPOSITORY" \
162
- --verify-tag --generate-notes --title "$RELEASE_TAG"
188
+ --verify-tag --notes-file "$release_notes" --title "$RELEASE_TAG"
163
189
  fi
@@ -8,10 +8,9 @@
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. CI runs
12
- `uv run tools/check_complexity.py`; run it locally when the pinned `tokei` is installed. A
13
- budget increase is a design stop that requires explicit review, not routine maintenance of the
14
- budget file.
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
13
+ design stop that requires explicit review, not routine maintenance of the budget file.
15
14
  - If the same subsystem needs a third consecutive hardening change, stop patching it and
16
15
  re-derive the design from the core invariants.
17
16
 
@@ -20,8 +19,8 @@
20
19
  - This is a `jj` repo. Do not use `git` to work on the repo itself.
21
20
  - Do not use git worktree-based agent isolation in this repo. For isolated parallel work, use
22
21
  `jj workspace` instead.
23
- - Run the CLI locally with `uv run jj-stack ...` instead of invoking the module or virtualenv
24
- path directly.
22
+ - Run the CLI locally with `just run ...` instead of invoking the module or virtualenv path
23
+ directly.
25
24
  - Hard-wrap code and markdown files at 98 columns unless a file uses a different convention.
26
25
 
27
26
  # Commit messages
@@ -33,7 +32,7 @@
33
32
  - Hard-wrap commit message bodies at 72 columns.
34
33
  - The body should explain the motivation for the change, the intended behavior or design outcome,
35
34
  and any important scope or design constraints.
36
- - Do not use the body to narrate the code or to record routine validation such as `./check.py`.
35
+ - Do not use the body to narrate the code or to record routine validation such as `just check`.
37
36
  - Prefer explaining why the commit exists and what rule or user-visible behavior it is enforcing.
38
37
 
39
38
  # Documentation
@@ -42,9 +41,10 @@
42
41
  rules and the public/internal split. Built-in `--help` text is held to the same standard as
43
42
  the user docs: assume jj/git familiarity, avoid `jj-stack` internal design jargon.
44
43
  - The web version of the user docs normally lives in the sibling jj repository at
45
- `$(jj root)/../website`. When user-facing docs change here, inspect and update the corresponding
46
- web docs there too when needed. If the change here is committed, commit the corresponding
47
- website update in that repository as well; preserve unrelated work in either working copy.
44
+ `$(jj root)/../website`. When user-facing docs change here, run `just website`, inspect the
45
+ corresponding website changes, and update them as needed. If the change here is committed,
46
+ commit the corresponding website update in that repository as well; preserve unrelated work in
47
+ either working copy.
48
48
  - Active internal docs use ordinary technical language too. Introduce a project-specific term
49
49
  only when it names a real type, field, or enduring rule, define it at first use, and prefer
50
50
  describing concrete inputs and effects.
@@ -71,13 +71,9 @@
71
71
 
72
72
  # Testing
73
73
 
74
- - Run `./check.py` before finishing a code change. Docs-only edits under `docs/` do not require
75
- a test run.
76
- - Run `./check.py` for the default local Ruff, type-check, and test pass before finishing a
77
- code change.
78
- - For focused test runs, do not use plain `uv run pytest ...`; it can miss the repo's package
79
- path in this project layout. First run `uv sync --locked`, then invoke pytest through the repo
80
- virtualenv, for example `.venv/bin/python -m pytest tests/unit/test_jj_client.py`.
74
+ - Run `just check` for the default local Ruff, type-check, and test pass before finishing a code
75
+ change. Docs-only edits under `docs/` do not require a test run.
76
+ - Run focused tests with `just test`, for example `just test tests/unit/test_jj_client.py`.
81
77
  - Before adding, modifying, removing, or reviewing tests, fixtures, helpers, or property
82
78
  scenarios, read and follow
83
79
  [docs/internals/testing-philosophy.md](docs/internals/testing-philosophy.md). Add or retain
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: jj-stack
3
- Version: 0.1.1
3
+ Version: 0.1.2
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/
7
+ Project-URL: Changelog, https://github.com/bos/jj-stack/releases
7
8
  Project-URL: Repository, https://github.com/bos/jj-stack
8
9
  Project-URL: Issues, https://github.com/bos/jj-stack/issues
9
10
  License-Expression: Apache-2.0
@@ -20,6 +21,7 @@ Classifier: Topic :: Software Development :: Version Control
20
21
  Classifier: Topic :: Software Development :: Version Control :: Git
21
22
  Requires-Python: >=3.14
22
23
  Requires-Dist: httpxyz<0.32,>=0.31.0
24
+ Requires-Dist: markdown-it-py<5,>=4.0.0
23
25
  Requires-Dist: pydantic<3,>=2.13.3
24
26
  Requires-Dist: rich<16,>=15.0.0
25
27
  Description-Content-Type: text/markdown
@@ -61,16 +63,25 @@ python -m pip install jj-stack
61
63
  To upgrade an installation made with `uv`, rerun its command with `--force`. If the command is
62
64
  not on your shell `PATH`, run `uv tool update-shell`.
63
65
 
64
- For tab completion, add the output of `jj-stack completion` to your shell startup file:
66
+ ### Invoke it as `jj stack`
65
67
 
66
- ```bash
67
- eval "$(jj-stack completion zsh)"
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"]
68
73
  ```
69
74
 
70
- `bash` and `fish` work the same way.
75
+ For tab completion of both `jj-stack` and `jj stack`, add the output of `jj-stack completion` to
76
+ your shell startup file:
71
77
 
72
- To invoke it as `jj stack` and complete that alias too, see
73
- [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/).
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.
74
85
 
75
86
  ### Submit your first stack
76
87
 
@@ -137,7 +148,7 @@ merge queues, cleanup, and working with multiple stacks.
137
148
  - [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/)
138
149
  - [Writing PR descriptions](https://www.serpentine.com/software/jj-stack/reference/descriptions/)
139
150
  - [Troubleshooting](https://www.serpentine.com/software/jj-stack/troubleshooting/)
140
- - [`jj-stack` and `gh stack`](https://www.serpentine.com/software/jj-stack/gh-stack/)
151
+ - [Tool comparison](https://www.serpentine.com/software/jj-stack/tool-comparison/)
141
152
  - [JSON output](https://www.serpentine.com/software/jj-stack/reference/json-output/)
142
153
  - [Automation and exit codes](https://www.serpentine.com/software/jj-stack/reference/automation/)
143
154
 
@@ -149,6 +160,12 @@ jj-stack <command> --help
149
160
  jj-stack help --all
150
161
  ```
151
162
 
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
+
152
169
  ## Coding agent integration
153
170
 
154
171
  Install the bundled skill to teach coding agents to work with local `jj` stacks and refresh their
@@ -35,16 +35,25 @@ python -m pip install jj-stack
35
35
  To upgrade an installation made with `uv`, rerun its command with `--force`. If the command is
36
36
  not on your shell `PATH`, run `uv tool update-shell`.
37
37
 
38
- For tab completion, add the output of `jj-stack completion` to your shell startup file:
38
+ ### Invoke it as `jj stack`
39
39
 
40
- ```bash
41
- eval "$(jj-stack completion zsh)"
40
+ Add a command alias to your user configuration with `jj config edit --user`:
41
+
42
+ ```toml
43
+ [aliases]
44
+ stack = ["util", "exec", "--", "jj-stack"]
42
45
  ```
43
46
 
44
- `bash` and `fish` work the same way.
47
+ For tab completion of both `jj-stack` and `jj stack`, add the output of `jj-stack completion` to
48
+ your shell startup file:
45
49
 
46
- To invoke it as `jj stack` and complete that alias too, see
47
- [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/).
50
+ ```bash
51
+ eval "$(jj-stack completion zsh --jj-alias stack)"
52
+ ```
53
+
54
+ `bash` and `fish` work the same way. See
55
+ [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/) for more
56
+ setup options.
48
57
 
49
58
  ### Submit your first stack
50
59
 
@@ -111,7 +120,7 @@ merge queues, cleanup, and working with multiple stacks.
111
120
  - [Configuration](https://www.serpentine.com/software/jj-stack/reference/configuration/)
112
121
  - [Writing PR descriptions](https://www.serpentine.com/software/jj-stack/reference/descriptions/)
113
122
  - [Troubleshooting](https://www.serpentine.com/software/jj-stack/troubleshooting/)
114
- - [`jj-stack` and `gh stack`](https://www.serpentine.com/software/jj-stack/gh-stack/)
123
+ - [Tool comparison](https://www.serpentine.com/software/jj-stack/tool-comparison/)
115
124
  - [JSON output](https://www.serpentine.com/software/jj-stack/reference/json-output/)
116
125
  - [Automation and exit codes](https://www.serpentine.com/software/jj-stack/reference/automation/)
117
126
 
@@ -123,6 +132,12 @@ jj-stack <command> --help
123
132
  jj-stack help --all
124
133
  ```
125
134
 
135
+ ## Development
136
+
137
+ Contributor workflows live in the [`justfile`](justfile). With `uv`, `jj`, and `just` installed,
138
+ run `just` to list the setup, formatting, focused test, verification, documentation, and release
139
+ recipes.
140
+
126
141
  ## Coding agent integration
127
142
 
128
143
  Install the bundled skill to teach coding agents to work with local `jj` stacks and refresh their
@@ -18,6 +18,7 @@ VENV_PYTHON = (
18
18
  REPO_ROOT / ".venv" / (Path("Scripts/python.exe") if os.name == "nt" else Path("bin/python"))
19
19
  )
20
20
  PytestJobs = int | Literal["auto"]
21
+ _PYREFLY_TARGETS = ("src", "tests", "tools", "check.py")
21
22
  _FRAGILE_TEST_OUTPUT_PATTERNS: tuple[tuple[str, re.Pattern[str]], ...] = (
22
23
  (
23
24
  "use output assertion helpers instead of exact captured output equality",
@@ -77,10 +78,17 @@ def _build_checks(
77
78
  return (
78
79
  ("ruff", ("-m", "ruff", "check")),
79
80
  ("ruff-format", ("-m", "ruff", "format", "--check")),
80
- ("pyrefly", ("-m", "pyrefly", "check")),
81
+ ("pyrefly", ("-m", "pyrefly", "check", *_PYREFLY_TARGETS)),
81
82
  (
82
83
  "pyrefly-windows",
83
- ("-m", "pyrefly", "check", "--python-platform", "win32"),
84
+ (
85
+ "-m",
86
+ "pyrefly",
87
+ "check",
88
+ "--python-platform",
89
+ "win32",
90
+ *_PYREFLY_TARGETS,
91
+ ),
84
92
  ),
85
93
  ("pytest", pytest_command),
86
94
  )
@@ -23,12 +23,13 @@ web navigation and presentation.
23
23
  ## Reference and troubleshooting
24
24
 
25
25
  - [Command reference](reference/commands.md)
26
+ - [Bookmarks and stack selection](reference/bookmarks-and-selection.md)
26
27
  - [Configuration](reference/configuration.md)
27
28
  - [Pull request descriptions](reference/descriptions.md)
28
29
  - [Automation and agents](reference/automation.md)
29
30
  - [JSON output](reference/json-output.md)
30
31
  - [Troubleshooting](troubleshooting.md)
31
- - [`jj-stack` and `gh stack`](gh-stack.md)
32
+ - [Compare `jj-stack` with other tools](tool-comparison.md)
32
33
 
33
34
  The built-in help is the exact flag and alias reference:
34
35
 
@@ -78,6 +78,12 @@ previously approved or asked for changes (i.e. revoking the approved state of yo
78
78
  jj-stack submit --re-request
79
79
  ```
80
80
 
81
+ ## PR history
82
+
83
+ To make it easier for a reader to understand how a pull request has changed over time, when you
84
+ run `submit` to update a PR that you have submitted before, `jj-stack` will keep a comment up to
85
+ date on the PR that shows its most recent versions, with a link to the diff for each update.
86
+
81
87
  ## What the `submit` command changes
82
88
 
83
89
  | Surface | Effect |
@@ -86,6 +92,7 @@ jj-stack submit --re-request
86
92
  | PR branches | Created or updated |
87
93
  | Pull requests | Created or updated |
88
94
  | Pull request order | Updated to match the local change order |
95
+ | Pull request comments | Revision history maintained |
89
96
  | Other local stacks | Not changed |
90
97
 
91
98
  With `jj`, you can easily move your changes between existing stacks. However, GitHub allows each
@@ -25,28 +25,31 @@ edits and GitHub's rewritten contents disagree, `sync` stops instead of choosing
25
25
  ## What the next submit replaces
26
26
 
27
27
  `jj-stack submit` makes GitHub's stack match your local stack. It pushes each change in your stack
28
- to its PR branch, sets each pull request's base from the local parent order, refreshes titles and
29
- bodies from `jj` descriptions or supplied description files, and updates native stack membership.
28
+ to its PR branch, sets each pull request's base from the local parent order, and updates native
29
+ stack membership.
30
+
31
+ By default, `submit` will generate a pull request description from the `jj` change. On a
32
+ subsequent submit, if the PR description still matches the last automated PR description,
33
+ `submit` will refresh it from the current change description. Otherwise, `submit` will leave it
34
+ untouched.
35
+
36
+ Use `--describe` to replace one body deliberately (or `--describe-with` or `--edit` for titles and
37
+ bodies).
38
+
30
39
  `--draft` affects new pull requests. `--draft=all`, `--open`, and the choices made through
31
40
  `--edit` can change existing draft states. Labels and reviewer requests that submit applies are
32
- additive;
33
- unrelated existing labels and reviewers are not removed.
34
-
35
- Edits made directly to a pull request title or body are therefore temporary unless you copy them
36
- back into the change description or pass them again on the next submit.
41
+ additive; unrelated existing labels and reviewers are not removed.
37
42
 
38
43
  ## Changes to avoid on GitHub
39
44
 
40
- Do not force-push, rename, or delete `jj-stack/` PR branches. Do not manually retarget pull
41
- request bases, reorder members, or add pull requests to the native stack when you intend the
42
- local stack order to remain authoritative. A later submit may replace grouping and bases; an
43
- unexpected branch move instead causes jj-stack to stop so it does not overwrite someone else's
44
- work.
45
+ Do not force-push, rename, or delete `jj-stack/` PR branches. If a branch moves unexpectedly,
46
+ jj-stack stops instead of overwriting it. See [troubleshooting](../troubleshooting.md) for the
47
+ recovery steps.
45
48
 
46
- If an external edit was intentional, inspect the result before deciding whether to restore the
47
- GitHub state, reproduce the change with `jj` and submit it, or remove the GitHub grouping with
48
- `jj-stack unstack --stack <number>`. See [troubleshooting](../troubleshooting.md) for specific
49
- recovery paths.
49
+ Do not change pull request bases or GitHub stack membership by hand. `jj-stack submit` derives
50
+ both from the local `jj` history. To keep a different base or order, make that change locally and
51
+ submit again. To leave the pull requests open but remove their GitHub stack grouping, run
52
+ `jj-stack unstack --stack <number>`.
50
53
 
51
54
  For reviewer and repo configuration guidance, see
52
55
  [review and operate a stack](review-a-stack.md).
@@ -54,8 +54,8 @@ wrong PR or ref, guessed linkage, then inconsistencies in reconstructible metada
54
54
  an elaborate recovery system to protect data that can be observed again.
55
55
 
56
56
  The limits in `complexity-budget.toml` are design constraints. Review changes to the limits,
57
- governed paths, and test markers as carefully as production code. CI runs
58
- `uv run tools/check_complexity.py`; run it locally when the pinned `tokei` version is installed.
57
+ governed paths, and test markers as carefully as production code. Run `just complexity` locally
58
+ when the pinned `tokei` version is installed; CI invokes the underlying checker.
59
59
  Moving the same logic into a helper or neighboring module is not a reduction in complexity.
60
60
 
61
61
  ## Review the user experience
@@ -262,8 +262,9 @@ Three sources answer questions about a tracked PR, each for a different domain:
262
262
 
263
263
  1. The **`jj` DAG** determines which local changes exist, how they are related, and what they
264
264
  contain.
265
- 2. **GitHub** reports PR existence, lifecycle, reviews, GitHub stack membership, and merge
266
- results. Whether work actually reached trunk is proven separately by ancestry from it.
265
+ 2. **GitHub** reports PR existence, lifecycle, reviews, check rollups, GitHub stack membership,
266
+ and merge results. Whether work actually reached trunk is proven separately by ancestry from
267
+ it.
267
268
  3. **Local tracking** records only the PR identity and submitted baseline of each change. It
268
269
  prevents mutation of the wrong PR but cannot make a mutation safe on its own.
269
270
 
@@ -305,15 +306,16 @@ These facts are re-derived and never need tool-owned durable state:
305
306
 
306
307
  Tracking stores one pair, keyed by full `change_id`:
307
308
 
308
- - `PRIdentity`: GitHub repo owner/name, PR number, and one canonical head owner/ref
309
+ - `PRIdentity`: the PR number and its one canonical head ref
309
310
  - `SubmittedBaseline`: the exact `commit_id` last successfully submitted for that identity
310
311
 
311
312
  Both records are created, replaced, and removed together. Partial pairs are invalid.
313
+ The configured GitHub repo is command context, not per-change tracking; `jj-stack` does not carry
314
+ tracking across repos.
312
315
 
313
316
  Two named checks recur throughout the policies:
314
317
 
315
- - **identity match**: the live PR's repo, number, and head owner/ref equal the saved
316
- `PRIdentity`
318
+ - **identity match**: the live PR's number and head ref equal the saved `PRIdentity`
317
319
  - **snapshot match**: an identity match whose live PR head SHA also equals
318
320
  `SubmittedBaseline.commit_id`
319
321
 
@@ -677,6 +679,10 @@ deletes a branch needed by an active GitHub stack member.
677
679
  GitHub stacks whose active members are all selected. It may also dissolve one partially selected
678
680
  GitHub stack when the selection is a maximal local path and touches no other GitHub stack. A
679
681
  non-maximal selection could silently truncate a still-valid stack, so it stops before mutation.
682
+ An unselected active member does not trigger that guard when its observed PR number, branch, and
683
+ head still match the saved PR number, branch, and submitted baseline, and its tracked change has
684
+ no visible off-trunk copy. That change cannot be on an extension of the selected path. Rebuilding
685
+ the grouping leaves the orphaned pull request open and retains its tracking.
680
686
  Likewise, a selection that partly overlaps one GitHub stack while including any previously
681
687
  submitted PR outside that resource stops; the user submits the source path first, then the
682
688
  destination path.
@@ -696,15 +702,26 @@ they do not block the mutation because they are no longer active.
696
702
 
697
703
  ### Derived artifacts
698
704
 
699
- PR titles, bodies, and the stack overview comment are derived on every submit and never determine
700
- topology; see [pull request descriptions](../reference/descriptions.md).
705
+ A subsequent submit refreshes a PR description only if it still matches the last automated PR
706
+ description. Otherwise it is preserved; explicitly supplied text still takes effect. PR text is
707
+ not stored locally and never determines topology; see
708
+ [pull request descriptions](../reference/descriptions.md).
709
+
710
+ When a submit supplies no stack overview, the existing overview text is preserved and moves to
711
+ the current head PR if the stack grows. An explicitly supplied overview replaces it. A lone PR has
712
+ no overview comment. New PRs are created in the requested draft state.
713
+
714
+ Submit maintains a per-PR revision-history comment with the most recent versions available from
715
+ GitHub's force-push timeline. The comment is presentation-only and remains after merge and
716
+ cleanup. This makes it easier for a reader to understand how a PR has evolved.
701
717
 
702
- The managed overview comment is the first comment whose body contains its marker; its ID is never
703
- stored. A lone PR has no overview comment. New PRs are created in the requested draft state.
704
718
  Existing PRs become draft only with `--draft=all` and become ready only with `--open`; plain
705
719
  `submit --draft` never unpublishes an existing PR. With `--edit`, GitHub's current state and those
706
720
  command-wide defaults populate one editable draft choice per change. The validated document then
707
- determines each selected PR's draft state without adding local state.
721
+ determines each selected PR's draft state without adding local state. A newly generated editor
722
+ file remains until the entire submit succeeds. If the command stops, the user can pass that file
723
+ back to `--edit`; the retry re-observes the local stack and GitHub, and accepts the file only when
724
+ it names exactly the currently selected changes. The file carries no submit plan or phase.
708
725
 
709
726
  `--reviewers` and `--team-reviewers` request the named reviewers even when a PR is otherwise
710
727
  unchanged and never remove omitted reviewers. `--re-request` acts on an otherwise unchanged PR,
@@ -713,6 +730,9 @@ adds requests and never cancels a pending one. An explicit `--label` request app
713
730
  PR is otherwise unchanged; configured labels alone do not turn a no-op submit into a metadata
714
731
  update. Labels are also additive; omitted labels are never removed.
715
732
 
733
+ Default PR bodies derived from change descriptions unfold Markdown soft line breaks into spaces.
734
+ Markdown block boundaries, code, tables, and explicit hard line breaks remain unchanged.
735
+
716
736
  ### Unstack and cleanup
717
737
 
718
738
  `unstack` removes one exact GitHub stack grouping and leaves every pull request, PR branch,
@@ -89,6 +89,15 @@ it does not add site navigation or styling. The website invokes that renderer fr
89
89
  jj-stack checkout whose canonical user guides it snapshots, then owns the generated page's front
90
90
  matter, CSS, and publication.
91
91
 
92
+ ## Development workflows
93
+
94
+ The root `justfile` is the contributor entry point for setup, local CLI execution, formatting,
95
+ focused tests, standard checks, complexity checks, generated scenarios, live GitHub qualification,
96
+ website documentation synchronization, and release artifacts. Recipes delegate substantive
97
+ behavior to existing scripts, including the website-owned documentation sync and the check scripts
98
+ shared with CI; they do not reimplement them. The README points contributors to `just` instead of
99
+ duplicating the underlying commands.
100
+
92
101
  ## Test boundaries
93
102
 
94
103
  The local integration environment uses real `jj` and Git repos with a purpose-built
@@ -39,10 +39,10 @@ hash-order dependence. Every pytest worker must collect the same cases. If gener
39
39
  the requested number of unique cases within its attempt limit, it returns the cases found instead
40
40
  of looping indefinitely.
41
41
 
42
- `./check.py` runs a small fixed set. Larger deterministic pools are opt-in:
42
+ `just check` runs a small fixed set. Larger deterministic pools are opt-in:
43
43
 
44
44
  ```console
45
- $ tests/run_submit_property_scenarios.py 500
45
+ $ just property 500
46
46
  ```
47
47
 
48
48
  The runner prints a complete reproduction command with the resolved seed, family counts, worker
@@ -0,0 +1,71 @@
1
+ # Release process
2
+
3
+ A release is built from a `v<version>`-tagged commit on `main`. The tag workflow builds and tests
4
+ both distributions, publishes them to PyPI, and creates the matching GitHub Release. A manual
5
+ workflow run publishes to TestPyPI unless it names an existing release tag to retry.
6
+
7
+ ## Write the release notes
8
+
9
+ Create `release-notes/v<version>.md`, using the same version as `pyproject.toml`. This file is the
10
+ source for the GitHub Release body and must be part of the tagged commit. The production workflow
11
+ fails before publishing if the file is absent or empty. On a retry, it also fails if an existing
12
+ GitHub Release does not contain exactly the notes from the tagged commit.
13
+
14
+ Write for someone deciding whether to upgrade, not for someone reconstructing the commit history:
15
+
16
+ - Open with a short summary of the release's user-visible value.
17
+ - Group changes by user impact. Use headings such as `Breaking changes`, `Highlights`, `Fixes`,
18
+ and `Documentation`, but omit empty sections.
19
+ - Put breaking changes first. State who is affected, what happens after upgrading, and the exact
20
+ migration or workaround.
21
+ - Make each bullet describe one observable outcome in plain language. Include commands, version
22
+ requirements, and links to the relevant guide when they help the reader act.
23
+ - Explain important fixes in terms of the symptom that is gone. Do not paste commit subjects,
24
+ internal type names, or an automatically generated pull request list.
25
+ - End with a comparison link from the previous tag to the new tag, labeled `Full changelog`.
26
+
27
+ Keep the notes self-contained even when they link to a pull request or issue. Proofread the
28
+ rendered Markdown and check every command and link before tagging. A release with no breaking
29
+ changes does not need a `Breaking changes` heading.
30
+
31
+ ## Qualify the candidate
32
+
33
+ Set the intended version in `pyproject.toml`, finish the release changes and release notes, and
34
+ run the release gates:
35
+
36
+ ```console
37
+ just release-check
38
+ ```
39
+
40
+ The live test requires a `gh` login that can create and delete a private repo, push to it, and
41
+ manage its pull requests.
42
+
43
+ Check the website snapshot and production build:
44
+
45
+ ```console
46
+ just website-check
47
+ cd ../website
48
+ just check
49
+ ```
50
+
51
+ If the snapshot is out of date, run `just website` from the jj-stack checkout, review and commit
52
+ the website change, then rerun the checks. Push the release changes to `main` before creating the
53
+ tag. A manual run of the release workflow is the optional TestPyPI smoke test.
54
+
55
+ ## Publish the tag
56
+
57
+ Create the version tag and push it explicitly:
58
+
59
+ ```console
60
+ jj tag set v0.1.0 -r main
61
+ jj git push --tag v0.1.0
62
+ ```
63
+
64
+ Use the version from `pyproject.toml` in place of `0.1.0`. After the workflow succeeds, verify the
65
+ GitHub Release contains the authored notes and downloadable distributions. PyPI has no separate
66
+ per-version release-notes field; its project page exposes the GitHub Releases page through the
67
+ standard `Changelog` project link. Verify that link along with the package page, publish the
68
+ already-checked website with `just publish`, and verify the live quick start and install command.
69
+
70
+ Never move or reuse a published version tag. A failed workflow can be rerun against the same tag;
71
+ a source change requires a new version and tag.
@@ -132,6 +132,14 @@
132
132
  }
133
133
  ],
134
134
  "properties": {
135
+ "checks": {
136
+ "type": "string",
137
+ "enum": [
138
+ "passed",
139
+ "failed",
140
+ "pending"
141
+ ]
142
+ },
135
143
  "number": {
136
144
  "type": "integer",
137
145
  "minimum": 1