gitgrip 2.0.0a4__tar.gz → 2.0.0a6__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 (284) hide show
  1. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/PKG-INFO +14 -10
  2. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/README.md +12 -9
  3. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gitgrip.egg-info/PKG-INFO +14 -10
  4. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gitgrip.egg-info/SOURCES.txt +46 -1
  5. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gitgrip.egg-info/requires.txt +1 -0
  6. gitgrip-2.0.0a6/gr2/api_core.py +306 -0
  7. gitgrip-2.0.0a6/gr2/schemas/gr2-workspace-spec-v1.schema.json +10 -0
  8. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2_overlay/__init__.py +8 -3
  9. gitgrip-2.0.0a6/prototypes/concurrent_event_stress.py +364 -0
  10. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/lane_workspace_prototype.py +3 -3
  11. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/python_hook_runtime_playground.py +6 -6
  12. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/real_git_playground.py +2 -2
  13. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/repo_maintenance_prototype.py +1 -1
  14. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/pyproject.toml +14 -3
  15. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/app.py +769 -92
  16. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/channel_bridge.py +28 -3
  17. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/clone_exec.py +57 -6
  18. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/commit.py +42 -11
  19. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/consent.py +33 -6
  20. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/events.py +45 -14
  21. gitgrip-2.0.0a6/python_cli/exit_codes.py +38 -0
  22. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/file_exec.py +5 -5
  23. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/git_review.py +44 -1
  24. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/gitinclude.py +15 -3
  25. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/gitops.py +150 -4
  26. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/grip.py +127 -4
  27. gitgrip-2.0.0a6/python_cli/grip_cli.py +1975 -0
  28. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/hooks.py +94 -6
  29. gitgrip-2.0.0a6/python_cli/layout.py +102 -0
  30. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/migration.py +123 -12
  31. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/open_gr_review.py +28 -11
  32. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/platform.py +218 -13
  33. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/pr.py +293 -8
  34. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/project_review.py +7 -1
  35. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/push.py +8 -6
  36. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/review.py +58 -21
  37. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/review_dispatch.py +4 -5
  38. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/review_rebind.py +2 -2
  39. gitgrip-2.0.0a6/python_cli/review_records.py +143 -0
  40. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/review_run.py +343 -32
  41. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/review_runners.py +35 -8
  42. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/spec_apply.py +479 -42
  43. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/syncops.py +51 -10
  44. gitgrip-2.0.0a6/python_cli/version.py +373 -0
  45. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/workspace_snapshot.py +1 -1
  46. gitgrip-2.0.0a6/tests/test_api_core_seam.py +360 -0
  47. gitgrip-2.0.0a6/tests/test_api_dump.py +775 -0
  48. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_apply_convergence.py +33 -15
  49. gitgrip-2.0.0a6/tests/test_apply_gr1_sibling_adoption.py +358 -0
  50. gitgrip-2.0.0a6/tests/test_break_17_unit_path_containment.py +188 -0
  51. gitgrip-2.0.0a6/tests/test_break_18_gr1_sibling_units.py +482 -0
  52. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_cli_shape_witnesses.py +1 -1
  53. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_clone_materialization.py +23 -19
  54. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_conftest_git_isolation.py +1 -0
  55. gitgrip-2.0.0a6/tests/test_deprecation_registry.py +277 -0
  56. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_ensure_lane_checkout_reference_clone.py +1 -1
  57. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_event_log_integrity.py +152 -0
  58. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_events.py +14 -10
  59. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_events_torn_line.py +5 -2
  60. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_exec_apostrophe_and_oserror.py +2 -2
  61. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_exec_single_string_and_materialize_plan.py +3 -3
  62. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_git_review.py +201 -0
  63. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_gitinclude_compiler.py +50 -3
  64. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_gr1_first_run_guidance.py +19 -5
  65. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_gr2_review_receipt.py +2 -2
  66. gitgrip-2.0.0a6/tests/test_grip_cli.py +450 -0
  67. gitgrip-2.0.0a6/tests/test_grip_toml_schema_v1.py +33 -0
  68. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_hooks_consent.py +276 -0
  69. gitgrip-2.0.0a6/tests/test_hooks_group_names.py +63 -0
  70. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_lane_create_fork_base_cli.py +36 -3
  71. gitgrip-2.0.0a6/tests/test_lane_create_refusal_leaves_nothing.py +354 -0
  72. gitgrip-2.0.0a6/tests/test_lane_enter_hook_warnings.py +205 -0
  73. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_lane_lifecycle_safety.py +4 -0
  74. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_lane_repo_paths_and_all_skipped.py +4 -4
  75. gitgrip-2.0.0a6/tests/test_lane_show_name.py +46 -0
  76. gitgrip-2.0.0a6/tests/test_layout_census.py +180 -0
  77. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_materialization_plan_contract.py +16 -16
  78. gitgrip-2.0.0a6/tests/test_materialize_pin.py +413 -0
  79. gitgrip-2.0.0a6/tests/test_member_working_root_checkout_materialize.py +329 -0
  80. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_migration.py +247 -2
  81. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_migration_unadopted_notice.py +10 -8
  82. gitgrip-2.0.0a6/tests/test_native_store_review_record_probes.py +266 -0
  83. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_open_gr_enter.py +22 -4
  84. gitgrip-2.0.0a6/tests/test_outbox_ack_after_effect.py +262 -0
  85. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_pr_events.py +8 -0
  86. gitgrip-2.0.0a6/tests/test_pr_group_legibility.py +236 -0
  87. gitgrip-2.0.0a6/tests/test_pr_merge_head_pin.py +393 -0
  88. gitgrip-2.0.0a6/tests/test_pr_merge_missing_target.py +694 -0
  89. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_pr_merge_partial_failure.py +9 -1
  90. gitgrip-2.0.0a6/tests/test_pr_view.py +502 -0
  91. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_project_file_materialization.py +4 -0
  92. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_project_review_three_repo.py +3 -3
  93. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_propagation_state_machine.py +10 -22
  94. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_readme_verbs_exist.py +67 -2
  95. gitgrip-2.0.0a6/tests/test_repo_path_read_through.py +382 -0
  96. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_bind_from_range.py +85 -1
  97. gitgrip-2.0.0a6/tests/test_review_bind_native_store.py +293 -0
  98. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_cli.py +4 -4
  99. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_close_gr.py +49 -4
  100. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_ephemeral.py +1 -1
  101. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_open_lane.py +100 -9
  102. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_rebind.py +22 -1
  103. gitgrip-2.0.0a6/tests/test_review_receipt_cleanup.py +92 -0
  104. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_run.py +7 -7
  105. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_run_import_isolation.py +8 -1
  106. gitgrip-2.0.0a6/tests/test_review_run_integrity_recheck.py +361 -0
  107. gitgrip-2.0.0a6/tests/test_review_run_multi_repo.py +344 -0
  108. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_run_rerun_and_refusal_receipt.py +4 -4
  109. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_runners.py +57 -0
  110. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_verify_inside.py +1 -1
  111. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_rmtree_or_refuse.py +14 -3
  112. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_single_repo_cwd_refusal.py +2 -2
  113. gitgrip-2.0.0a6/tests/test_snapshot_id_prefix_resolution.py +221 -0
  114. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_spec_apply_event_outcomes.py +5 -2
  115. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sprint21_sync_platform.py +228 -0
  116. gitgrip-2.0.0a6/tests/test_store_break_attempts.py +1958 -0
  117. gitgrip-2.0.0a6/tests/test_store_checkout_atomicity.py +405 -0
  118. gitgrip-2.0.0a6/tests/test_store_commit_noop.py +137 -0
  119. gitgrip-2.0.0a6/tests/test_store_declared_members.py +293 -0
  120. gitgrip-2.0.0a6/tests/test_store_git_native_smoke.py +220 -0
  121. gitgrip-2.0.0a6/tests/test_store_init_branch_inference.py +260 -0
  122. gitgrip-2.0.0a6/tests/test_store_log_adoption_boundary.py +205 -0
  123. gitgrip-2.0.0a6/tests/test_store_member_path_hygiene.py +128 -0
  124. gitgrip-2.0.0a6/tests/test_store_member_state.py +213 -0
  125. gitgrip-2.0.0a6/tests/test_store_migrate_branch_inference.py +106 -0
  126. gitgrip-2.0.0a6/tests/test_store_status_values.py +184 -0
  127. gitgrip-2.0.0a6/tests/test_store_verb_rename.py +140 -0
  128. gitgrip-2.0.0a6/tests/test_store_verb_witnesses.py +770 -0
  129. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_submodule_member_predicate.py +1 -1
  130. gitgrip-2.0.0a6/tests/test_superproject_import.py +384 -0
  131. gitgrip-2.0.0a6/tests/test_validate_repo_path_conflict.py +169 -0
  132. gitgrip-2.0.0a6/tests/test_version_identity.py +937 -0
  133. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_version_static.py +31 -8
  134. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_convert_clone.py +1 -1
  135. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_identity_boundary.py +11 -1
  136. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_init_local_repos.py +3 -3
  137. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_snapshot.py +1 -1
  138. gitgrip-2.0.0a4/prototypes/concurrent_event_stress.py +0 -199
  139. gitgrip-2.0.0a4/python_cli/grip_cli.py +0 -497
  140. gitgrip-2.0.0a4/tests/test_grip_cli.py +0 -536
  141. gitgrip-2.0.0a4/tests/test_grip_object_model.py +0 -357
  142. gitgrip-2.0.0a4/tests/test_snapshot_id_prefix_resolution.py +0 -132
  143. gitgrip-2.0.0a4/tests/test_store_verb_rename.py +0 -46
  144. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gitgrip.egg-info/dependency_links.txt +0 -0
  145. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gitgrip.egg-info/entry_points.txt +0 -0
  146. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gitgrip.egg-info/top_level.txt +0 -0
  147. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/__init__.py +0 -0
  148. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/__init__.py +0 -0
  149. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/activate.py +0 -0
  150. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/agent_manifest.py +0 -0
  151. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/cli.py +0 -0
  152. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/cross_repo.py +0 -0
  153. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/drivers.py +0 -0
  154. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/introspection.py +0 -0
  155. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/language_drivers.py +0 -0
  156. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/objects.py +0 -0
  157. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/perf.py +0 -0
  158. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/refs.py +0 -0
  159. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/trust.py +0 -0
  160. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/types.py +0 -0
  161. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/units.py +0 -0
  162. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/overlay/workspace_spec.py +0 -0
  163. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/gr2/schemas/gr2-materialization-plan-v1.schema.json +0 -0
  164. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/__init__.py +0 -0
  165. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/cache_materialization_probe.py +0 -0
  166. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/concurrent_lease_stress.py +0 -0
  167. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/concurrent_workspace_cap_stress.py +0 -0
  168. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/contribution_protocol.py +0 -0
  169. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/cross_mode_lane_stress.py +0 -0
  170. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/jsonl_store.py +0 -0
  171. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/layout_model_probe.py +0 -0
  172. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/propagation_daemon.py +0 -0
  173. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/propagation_state_machine.py +0 -0
  174. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/python_exec_playground.py +0 -0
  175. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/python_migration_playground.py +0 -0
  176. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/python_review_checkout_playground.py +0 -0
  177. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/python_spec_apply_playground.py +0 -0
  178. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/real_git_lane_materialization.py +0 -0
  179. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/recall_lane_history.py +0 -0
  180. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/prototypes/repo_transport_probe.py +0 -0
  181. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/__init__.py +0 -0
  182. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/__main__.py +0 -0
  183. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/add.py +0 -0
  184. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/branch.py +0 -0
  185. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/config.py +0 -0
  186. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/env_exec.py +0 -0
  187. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/execops.py +0 -0
  188. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/failures.py +0 -0
  189. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/launch_exec.py +0 -0
  190. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/merge_verification.py +0 -0
  191. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/prune.py +0 -0
  192. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/review_ephemeral.py +0 -0
  193. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/staging_cleanup.py +0 -0
  194. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/target.py +0 -0
  195. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/python_cli/workspace_guidance.py +0 -0
  196. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/setup.cfg +0 -0
  197. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_add.py +0 -0
  198. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_agent_manifest_overlay_source.py +0 -0
  199. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_app_event_outcomes.py +0 -0
  200. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_branch.py +0 -0
  201. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_channel_bridge.py +0 -0
  202. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_cli_adapter_error_boundary.py +0 -0
  203. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_commit.py +0 -0
  204. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_config_base_path_guard.py +0 -0
  205. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_config_overlay.py +0 -0
  206. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_contribution_protocol.py +0 -0
  207. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_env_materialization.py +0 -0
  208. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_event_outcome_policy.py +0 -0
  209. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_execops.py +0 -0
  210. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_failure_event_outcome.py +0 -0
  211. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_first_five_minutes.py +0 -0
  212. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_foreground_launch_runtime.py +0 -0
  213. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_gr2_packaging.py +0 -0
  214. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_grip_hardening.py +0 -0
  215. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_grip_snapshot.py +0 -0
  216. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_hook_events.py +0 -0
  217. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_hook_hardening.py +0 -0
  218. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_jsonl_store.py +0 -0
  219. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_lane_create_refuses_own_branch.py +0 -0
  220. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_lane_lease_show_json.py +0 -0
  221. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_lane_toml_serialization.py +0 -0
  222. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_launch_primitive.py +0 -0
  223. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_m2_overlay_hindsight.py +0 -0
  224. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_m2_overlay_mem0.py +0 -0
  225. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_m2_overlay_zep.py +0 -0
  226. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_merge_evidence.py +0 -0
  227. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_merge_parent_verification.py +0 -0
  228. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_migration_overlay_compatibility.py +0 -0
  229. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_no_bare_python_cli_imports.py +0 -0
  230. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_null_current_lane_refusal.py +0 -0
  231. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_open_project_refusal_reason_cli.py +0 -0
  232. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_acceptance.py +0 -0
  233. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_activate.py +0 -0
  234. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_cli.py +0 -0
  235. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_cli_wiring.py +0 -0
  236. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_composition_merge.py +0 -0
  237. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_cross_repo_atomic_apply.py +0 -0
  238. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_cross_repo_perf_gate.py +0 -0
  239. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_cross_repo_rollback.py +0 -0
  240. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_driver_registry.py +0 -0
  241. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_introspection.py +0 -0
  242. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_multi_stack.py +0 -0
  243. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_object_encoding.py +0 -0
  244. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_perf_gates.py +0 -0
  245. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_python_driver.py +0 -0
  246. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_encoding.py +0 -0
  247. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_perf_gates.py +0 -0
  248. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_repo_state.py +0 -0
  249. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_staging_discipline.py +0 -0
  250. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_trust_model.py +0 -0
  251. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_overlay_workspace_spec.py +0 -0
  252. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_pr_merge_method.py +0 -0
  253. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_project_review_range_reconstruction.py +0 -0
  254. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_projection_tracked_dest.py +0 -0
  255. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_propagation_contribution.py +0 -0
  256. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_propagation_daemon.py +0 -0
  257. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_prune.py +0 -0
  258. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_push.py +0 -0
  259. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_repo_status_linked_worktree.py +0 -0
  260. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_repo_status_schema_version.py +0 -0
  261. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_repo_status_symlinked_git_dir.py +0 -0
  262. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_bind_fork_base.py +0 -0
  263. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_bind_verify.py +0 -0
  264. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_create_project_cli.py +0 -0
  265. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_review_dispatch.py +0 -0
  266. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_spec_declaration_ref_pin.py +0 -0
  267. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_staging_cleanup.py +0 -0
  268. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sync_event_outcomes.py +0 -0
  269. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sync_fast_forward.py +0 -0
  270. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sync_fetch.py +0 -0
  271. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sync_seed_local.py +0 -0
  272. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sync_ssh_rewrite.py +0 -0
  273. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_sync_status_probe.py +0 -0
  274. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_target.py +0 -0
  275. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_tmux_launch_runtime.py +0 -0
  276. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_unit_abort.py +0 -0
  277. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_unit_apply.py +0 -0
  278. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_unit_apply_dry_run.py +0 -0
  279. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_unit_manifest_schema.py +0 -0
  280. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_unit_propose.py +0 -0
  281. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_unit_topological_apply.py +0 -0
  282. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_edit_lease_cap.py +0 -0
  283. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_init_from_topology.py +0 -0
  284. {gitgrip-2.0.0a4 → gitgrip-2.0.0a6}/tests/test_workspace_root_defaults.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitgrip
3
- Version: 2.0.0a4
3
+ Version: 2.0.0a6
4
4
  Summary: Workspace layer for multi-repo work: workspaces, isolated lanes, and one grouped review per slice
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -12,6 +12,7 @@ Requires-Dist: jsonschema>=4.18
12
12
  Provides-Extra: dev
13
13
  Requires-Dist: pytest>=8.0; extra == "dev"
14
14
  Requires-Dist: ruff>=0.4; extra == "dev"
15
+ Requires-Dist: packaging>=22; extra == "dev"
15
16
 
16
17
  # gr2
17
18
 
@@ -76,7 +77,7 @@ gr2 commit -m "my change" --workspace-root ~/ws --owner-unit default
76
77
  ```bash
77
78
  gr2 exec run ~/ws default --actor human:you -- pytest -q # one command, every lane repo
78
79
  gr2 review create-project ~/ws default feat-x # prints gr:<sha>: one object to review
79
- gr2 lane current ~/ws default # where am I?
80
+ gr2 lane show ~/ws default # where am I?
80
81
  gr2 lane exit ~/ws default --actor human:you
81
82
  ```
82
83
 
@@ -86,7 +87,7 @@ gr2 lane exit ~/ws default --actor human:you
86
87
 
87
88
  Alpha 2 is a working local workspace layer. Verified by running the walk above:
88
89
 
89
- - `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, snapshot, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
90
+ - `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, commit, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
90
91
  - Lanes are independent clones: two lanes, or two agents, never share refs, an index, or a working tree.
91
92
  - A lane commit that commits nothing anywhere refuses loudly instead of reporting success.
92
93
 
@@ -100,17 +101,18 @@ Not there yet:
100
101
 
101
102
  | Group | Verbs |
102
103
  |---|---|
103
- | `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1 |
104
+ | `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1, gitinclude |
104
105
  | `spec` | show, validate |
105
- | `lane` | create, enter, exit, current, resolve, bind, lease |
106
- | (top level) | branch, add, commit, push, prune |
106
+ | `lane` | create, enter, exit, show, resolve, bind, lease |
107
+ | (top level) | branch, add, commit, push, prune, status, plan, apply |
107
108
  | `sync` | status, run |
108
- | `pr` | create, status, checks, merge |
109
+ | `pr` | create, status, checks, merge, view |
109
110
  | `review` | open, close, checkout-pr, run, requirements, bind, verify, rebind, create-project |
110
111
  | `exec` | status, run |
111
- | `repo` | status, hooks, hook-run, projection-run |
112
- | `store` | init, snapshot, log, diff, checkout |
113
- | `target`, `config`, `plan`, `apply` | stored PR target; config overlays; show/apply the materialization plan |
112
+ | `repo` | status, projection-run |
113
+ | `hooks` | trust, revoke, status, show, run |
114
+ | `store` | init, commit, check, push, status, log, diff, checkout, materialize, migrate |
115
+ | `target`, `config` | stored PR target; config overlays |
114
116
 
115
117
  ## Overlay substrate
116
118
 
@@ -123,3 +125,5 @@ pip install -e "gr2[dev]" # from the repository root
123
125
  cd gr2 && pytest # the gr2 suite (tests + overlay tests)
124
126
  cd gr2 && ruff check . # lint, same config as CI
125
127
  ```
128
+
129
+ The Rust development binary is **`gr2-dev`** — built by `cargo build -p gr2-cli --release`, `publish = false`, and not shipped with `cargo install gitgrip`. It answered to the name `gr2` until 2026-09-30, so a branch written before that rename goes red on `cargo test -p gr2-cli` with `CARGO_BIN_EXE_gr2 is unset`; the repair is `cargo_bin("gr2-dev")`, from which Cargo derives `CARGO_BIN_EXE_gr2-dev`.
@@ -61,7 +61,7 @@ gr2 commit -m "my change" --workspace-root ~/ws --owner-unit default
61
61
  ```bash
62
62
  gr2 exec run ~/ws default --actor human:you -- pytest -q # one command, every lane repo
63
63
  gr2 review create-project ~/ws default feat-x # prints gr:<sha>: one object to review
64
- gr2 lane current ~/ws default # where am I?
64
+ gr2 lane show ~/ws default # where am I?
65
65
  gr2 lane exit ~/ws default --actor human:you
66
66
  ```
67
67
 
@@ -71,7 +71,7 @@ gr2 lane exit ~/ws default --actor human:you
71
71
 
72
72
  Alpha 2 is a working local workspace layer. Verified by running the walk above:
73
73
 
74
- - `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, snapshot, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
74
+ - `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, commit, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
75
75
  - Lanes are independent clones: two lanes, or two agents, never share refs, an index, or a working tree.
76
76
  - A lane commit that commits nothing anywhere refuses loudly instead of reporting success.
77
77
 
@@ -85,17 +85,18 @@ Not there yet:
85
85
 
86
86
  | Group | Verbs |
87
87
  |---|---|
88
- | `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1 |
88
+ | `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1, gitinclude |
89
89
  | `spec` | show, validate |
90
- | `lane` | create, enter, exit, current, resolve, bind, lease |
91
- | (top level) | branch, add, commit, push, prune |
90
+ | `lane` | create, enter, exit, show, resolve, bind, lease |
91
+ | (top level) | branch, add, commit, push, prune, status, plan, apply |
92
92
  | `sync` | status, run |
93
- | `pr` | create, status, checks, merge |
93
+ | `pr` | create, status, checks, merge, view |
94
94
  | `review` | open, close, checkout-pr, run, requirements, bind, verify, rebind, create-project |
95
95
  | `exec` | status, run |
96
- | `repo` | status, hooks, hook-run, projection-run |
97
- | `store` | init, snapshot, log, diff, checkout |
98
- | `target`, `config`, `plan`, `apply` | stored PR target; config overlays; show/apply the materialization plan |
96
+ | `repo` | status, projection-run |
97
+ | `hooks` | trust, revoke, status, show, run |
98
+ | `store` | init, commit, check, push, status, log, diff, checkout, materialize, migrate |
99
+ | `target`, `config` | stored PR target; config overlays |
99
100
 
100
101
  ## Overlay substrate
101
102
 
@@ -108,3 +109,5 @@ pip install -e "gr2[dev]" # from the repository root
108
109
  cd gr2 && pytest # the gr2 suite (tests + overlay tests)
109
110
  cd gr2 && ruff check . # lint, same config as CI
110
111
  ```
112
+
113
+ The Rust development binary is **`gr2-dev`** — built by `cargo build -p gr2-cli --release`, `publish = false`, and not shipped with `cargo install gitgrip`. It answered to the name `gr2` until 2026-09-30, so a branch written before that rename goes red on `cargo test -p gr2-cli` with `CARGO_BIN_EXE_gr2 is unset`; the repair is `cargo_bin("gr2-dev")`, from which Cargo derives `CARGO_BIN_EXE_gr2-dev`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitgrip
3
- Version: 2.0.0a4
3
+ Version: 2.0.0a6
4
4
  Summary: Workspace layer for multi-repo work: workspaces, isolated lanes, and one grouped review per slice
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -12,6 +12,7 @@ Requires-Dist: jsonschema>=4.18
12
12
  Provides-Extra: dev
13
13
  Requires-Dist: pytest>=8.0; extra == "dev"
14
14
  Requires-Dist: ruff>=0.4; extra == "dev"
15
+ Requires-Dist: packaging>=22; extra == "dev"
15
16
 
16
17
  # gr2
17
18
 
@@ -76,7 +77,7 @@ gr2 commit -m "my change" --workspace-root ~/ws --owner-unit default
76
77
  ```bash
77
78
  gr2 exec run ~/ws default --actor human:you -- pytest -q # one command, every lane repo
78
79
  gr2 review create-project ~/ws default feat-x # prints gr:<sha>: one object to review
79
- gr2 lane current ~/ws default # where am I?
80
+ gr2 lane show ~/ws default # where am I?
80
81
  gr2 lane exit ~/ws default --actor human:you
81
82
  ```
82
83
 
@@ -86,7 +87,7 @@ gr2 lane exit ~/ws default --actor human:you
86
87
 
87
88
  Alpha 2 is a working local workspace layer. Verified by running the walk above:
88
89
 
89
- - `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, snapshot, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
90
+ - `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, commit, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
90
91
  - Lanes are independent clones: two lanes, or two agents, never share refs, an index, or a working tree.
91
92
  - A lane commit that commits nothing anywhere refuses loudly instead of reporting success.
92
93
 
@@ -100,17 +101,18 @@ Not there yet:
100
101
 
101
102
  | Group | Verbs |
102
103
  |---|---|
103
- | `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1 |
104
+ | `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1, gitinclude |
104
105
  | `spec` | show, validate |
105
- | `lane` | create, enter, exit, current, resolve, bind, lease |
106
- | (top level) | branch, add, commit, push, prune |
106
+ | `lane` | create, enter, exit, show, resolve, bind, lease |
107
+ | (top level) | branch, add, commit, push, prune, status, plan, apply |
107
108
  | `sync` | status, run |
108
- | `pr` | create, status, checks, merge |
109
+ | `pr` | create, status, checks, merge, view |
109
110
  | `review` | open, close, checkout-pr, run, requirements, bind, verify, rebind, create-project |
110
111
  | `exec` | status, run |
111
- | `repo` | status, hooks, hook-run, projection-run |
112
- | `store` | init, snapshot, log, diff, checkout |
113
- | `target`, `config`, `plan`, `apply` | stored PR target; config overlays; show/apply the materialization plan |
112
+ | `repo` | status, projection-run |
113
+ | `hooks` | trust, revoke, status, show, run |
114
+ | `store` | init, commit, check, push, status, log, diff, checkout, materialize, migrate |
115
+ | `target`, `config` | stored PR target; config overlays |
114
116
 
115
117
  ## Overlay substrate
116
118
 
@@ -123,3 +125,5 @@ pip install -e "gr2[dev]" # from the repository root
123
125
  cd gr2 && pytest # the gr2 suite (tests + overlay tests)
124
126
  cd gr2 && ruff check . # lint, same config as CI
125
127
  ```
128
+
129
+ The Rust development binary is **`gr2-dev`** — built by `cargo build -p gr2-cli --release`, `publish = false`, and not shipped with `cargo install gitgrip`. It answered to the name `gr2` until 2026-09-30, so a branch written before that rename goes red on `cargo test -p gr2-cli` with `CARGO_BIN_EXE_gr2 is unset`; the repair is `cargo_bin("gr2-dev")`, from which Cargo derives `CARGO_BIN_EXE_gr2-dev`.
@@ -7,6 +7,7 @@ gitgrip.egg-info/entry_points.txt
7
7
  gitgrip.egg-info/requires.txt
8
8
  gitgrip.egg-info/top_level.txt
9
9
  gr2/__init__.py
10
+ gr2/api_core.py
10
11
  gr2/overlay/__init__.py
11
12
  gr2/overlay/activate.py
12
13
  gr2/overlay/agent_manifest.py
@@ -23,6 +24,7 @@ gr2/overlay/types.py
23
24
  gr2/overlay/units.py
24
25
  gr2/overlay/workspace_spec.py
25
26
  gr2/schemas/gr2-materialization-plan-v1.schema.json
27
+ gr2/schemas/gr2-workspace-spec-v1.schema.json
26
28
  gr2_overlay/__init__.py
27
29
  prototypes/__init__.py
28
30
  prototypes/cache_materialization_probe.py
@@ -59,6 +61,7 @@ python_cli/consent.py
59
61
  python_cli/env_exec.py
60
62
  python_cli/events.py
61
63
  python_cli/execops.py
64
+ python_cli/exit_codes.py
62
65
  python_cli/failures.py
63
66
  python_cli/file_exec.py
64
67
  python_cli/git_review.py
@@ -68,6 +71,7 @@ python_cli/grip.py
68
71
  python_cli/grip_cli.py
69
72
  python_cli/hooks.py
70
73
  python_cli/launch_exec.py
74
+ python_cli/layout.py
71
75
  python_cli/merge_verification.py
72
76
  python_cli/migration.py
73
77
  python_cli/open_gr_review.py
@@ -80,19 +84,26 @@ python_cli/review.py
80
84
  python_cli/review_dispatch.py
81
85
  python_cli/review_ephemeral.py
82
86
  python_cli/review_rebind.py
87
+ python_cli/review_records.py
83
88
  python_cli/review_run.py
84
89
  python_cli/review_runners.py
85
90
  python_cli/spec_apply.py
86
91
  python_cli/staging_cleanup.py
87
92
  python_cli/syncops.py
88
93
  python_cli/target.py
94
+ python_cli/version.py
89
95
  python_cli/workspace_guidance.py
90
96
  python_cli/workspace_snapshot.py
91
97
  tests/test_add.py
92
98
  tests/test_agent_manifest_overlay_source.py
99
+ tests/test_api_core_seam.py
100
+ tests/test_api_dump.py
93
101
  tests/test_app_event_outcomes.py
94
102
  tests/test_apply_convergence.py
103
+ tests/test_apply_gr1_sibling_adoption.py
95
104
  tests/test_branch.py
105
+ tests/test_break_17_unit_path_containment.py
106
+ tests/test_break_18_gr1_sibling_units.py
96
107
  tests/test_channel_bridge.py
97
108
  tests/test_cli_adapter_error_boundary.py
98
109
  tests/test_cli_shape_witnesses.py
@@ -102,6 +113,7 @@ tests/test_config_base_path_guard.py
102
113
  tests/test_config_overlay.py
103
114
  tests/test_conftest_git_isolation.py
104
115
  tests/test_contribution_protocol.py
116
+ tests/test_deprecation_registry.py
105
117
  tests/test_ensure_lane_checkout_reference_clone.py
106
118
  tests/test_env_materialization.py
107
119
  tests/test_event_log_integrity.py
@@ -121,32 +133,41 @@ tests/test_gr2_packaging.py
121
133
  tests/test_gr2_review_receipt.py
122
134
  tests/test_grip_cli.py
123
135
  tests/test_grip_hardening.py
124
- tests/test_grip_object_model.py
125
136
  tests/test_grip_snapshot.py
137
+ tests/test_grip_toml_schema_v1.py
126
138
  tests/test_hook_events.py
127
139
  tests/test_hook_hardening.py
128
140
  tests/test_hooks_consent.py
141
+ tests/test_hooks_group_names.py
129
142
  tests/test_jsonl_store.py
130
143
  tests/test_lane_create_fork_base_cli.py
144
+ tests/test_lane_create_refusal_leaves_nothing.py
131
145
  tests/test_lane_create_refuses_own_branch.py
146
+ tests/test_lane_enter_hook_warnings.py
132
147
  tests/test_lane_lease_show_json.py
133
148
  tests/test_lane_lifecycle_safety.py
134
149
  tests/test_lane_repo_paths_and_all_skipped.py
150
+ tests/test_lane_show_name.py
135
151
  tests/test_lane_toml_serialization.py
136
152
  tests/test_launch_primitive.py
153
+ tests/test_layout_census.py
137
154
  tests/test_m2_overlay_hindsight.py
138
155
  tests/test_m2_overlay_mem0.py
139
156
  tests/test_m2_overlay_zep.py
140
157
  tests/test_materialization_plan_contract.py
158
+ tests/test_materialize_pin.py
159
+ tests/test_member_working_root_checkout_materialize.py
141
160
  tests/test_merge_evidence.py
142
161
  tests/test_merge_parent_verification.py
143
162
  tests/test_migration.py
144
163
  tests/test_migration_overlay_compatibility.py
145
164
  tests/test_migration_unadopted_notice.py
165
+ tests/test_native_store_review_record_probes.py
146
166
  tests/test_no_bare_python_cli_imports.py
147
167
  tests/test_null_current_lane_refusal.py
148
168
  tests/test_open_gr_enter.py
149
169
  tests/test_open_project_refusal_reason_cli.py
170
+ tests/test_outbox_ack_after_effect.py
150
171
  tests/test_overlay_acceptance.py
151
172
  tests/test_overlay_activate.py
152
173
  tests/test_overlay_cli.py
@@ -168,8 +189,12 @@ tests/test_overlay_tier_b_staging_discipline.py
168
189
  tests/test_overlay_trust_model.py
169
190
  tests/test_overlay_workspace_spec.py
170
191
  tests/test_pr_events.py
192
+ tests/test_pr_group_legibility.py
193
+ tests/test_pr_merge_head_pin.py
171
194
  tests/test_pr_merge_method.py
195
+ tests/test_pr_merge_missing_target.py
172
196
  tests/test_pr_merge_partial_failure.py
197
+ tests/test_pr_view.py
173
198
  tests/test_project_file_materialization.py
174
199
  tests/test_project_review_range_reconstruction.py
175
200
  tests/test_project_review_three_repo.py
@@ -180,11 +205,13 @@ tests/test_propagation_state_machine.py
180
205
  tests/test_prune.py
181
206
  tests/test_push.py
182
207
  tests/test_readme_verbs_exist.py
208
+ tests/test_repo_path_read_through.py
183
209
  tests/test_repo_status_linked_worktree.py
184
210
  tests/test_repo_status_schema_version.py
185
211
  tests/test_repo_status_symlinked_git_dir.py
186
212
  tests/test_review_bind_fork_base.py
187
213
  tests/test_review_bind_from_range.py
214
+ tests/test_review_bind_native_store.py
188
215
  tests/test_review_bind_verify.py
189
216
  tests/test_review_cli.py
190
217
  tests/test_review_close_gr.py
@@ -193,8 +220,11 @@ tests/test_review_dispatch.py
193
220
  tests/test_review_ephemeral.py
194
221
  tests/test_review_open_lane.py
195
222
  tests/test_review_rebind.py
223
+ tests/test_review_receipt_cleanup.py
196
224
  tests/test_review_run.py
197
225
  tests/test_review_run_import_isolation.py
226
+ tests/test_review_run_integrity_recheck.py
227
+ tests/test_review_run_multi_repo.py
198
228
  tests/test_review_run_rerun_and_refusal_receipt.py
199
229
  tests/test_review_runners.py
200
230
  tests/test_review_verify_inside.py
@@ -205,8 +235,21 @@ tests/test_spec_apply_event_outcomes.py
205
235
  tests/test_spec_declaration_ref_pin.py
206
236
  tests/test_sprint21_sync_platform.py
207
237
  tests/test_staging_cleanup.py
238
+ tests/test_store_break_attempts.py
239
+ tests/test_store_checkout_atomicity.py
240
+ tests/test_store_commit_noop.py
241
+ tests/test_store_declared_members.py
242
+ tests/test_store_git_native_smoke.py
243
+ tests/test_store_init_branch_inference.py
244
+ tests/test_store_log_adoption_boundary.py
245
+ tests/test_store_member_path_hygiene.py
246
+ tests/test_store_member_state.py
247
+ tests/test_store_migrate_branch_inference.py
248
+ tests/test_store_status_values.py
208
249
  tests/test_store_verb_rename.py
250
+ tests/test_store_verb_witnesses.py
209
251
  tests/test_submodule_member_predicate.py
252
+ tests/test_superproject_import.py
210
253
  tests/test_sync_event_outcomes.py
211
254
  tests/test_sync_fast_forward.py
212
255
  tests/test_sync_fetch.py
@@ -221,6 +264,8 @@ tests/test_unit_apply_dry_run.py
221
264
  tests/test_unit_manifest_schema.py
222
265
  tests/test_unit_propose.py
223
266
  tests/test_unit_topological_apply.py
267
+ tests/test_validate_repo_path_conflict.py
268
+ tests/test_version_identity.py
224
269
  tests/test_version_static.py
225
270
  tests/test_workspace_convert_clone.py
226
271
  tests/test_workspace_edit_lease_cap.py
@@ -7,3 +7,4 @@ jsonschema>=4.18
7
7
  [dev]
8
8
  pytest>=8.0
9
9
  ruff>=0.4
10
+ packaging>=22
@@ -0,0 +1,306 @@
1
+ """The generic core of the API dumper: walker, line format, marker lookup.
2
+
3
+ THE SEAM. This module knows four things -- how to walk a command tree, how an
4
+ ``.api`` line is shaped, how a stability table is keyed, and how items are
5
+ GROUPED into blocks -- and it knows nothing about gr2: not its app, not its
6
+ tables, not its exit codes. Everything that names gr2 lives in the consumer
7
+ (``gr2/scripts/dump_api.py``), which REGISTERS the kinds it emits, supplies the
8
+ marker rules for them, and says which COMMAND each item belongs to (``Row.group``).
9
+ Grouping is a core rule over a consumer-supplied fact, which is the same
10
+ division as the marker table: the core owns the shape, the consumer owns the
11
+ meaning.
12
+
13
+ The boundary is a test, not a convention: ``tests/test_api_core_seam.py`` walks
14
+ this module's imports at any depth and fails if any of them reaches gr2, with
15
+ the plugin beside it as the control that proves the walker can fail.
16
+
17
+ WHY IT IS WORTH THE SPLIT. The dumper does not move to its own repo yet -- but
18
+ the regroup of the dump by command and subcommand is the moment to cut the
19
+ seam, so that the later extraction is a MOVE rather than a rewrite. A core with
20
+ no gr2 import can be lifted out with its tests; one that reaches into
21
+ ``grip_cli`` for an exit prefix cannot.
22
+
23
+ WHAT IS NOT HERE YET, named rather than implied: the alias registry
24
+ (``grip_cli.STORE_MOUNTS`` is gr2's own mount table) and the three gates are
25
+ still on the plugin side, because both answer questions about gr2's surface
26
+ rather than about the format. They move when a second consumer exists to make
27
+ their generic shape decidable.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import tomllib
33
+ from collections.abc import Callable
34
+ from dataclasses import dataclass
35
+ from pathlib import Path
36
+ from typing import NamedTuple
37
+
38
+
39
+ class Row(NamedTuple):
40
+ """One item, before it is rendered.
41
+
42
+ ``group`` is the COMMAND PATH the item belongs to, and the CONSUMER supplies
43
+ it rather than the core deriving one. Only the consumer knows what a command
44
+ is in its own tree: the walk answers for a verb and the flags beneath it, but
45
+ an exit code belongs to a GROUP and a layout path belongs to no verb at all.
46
+ Deriving it here would mean the core guessing at the shape of a spelling.
47
+
48
+ An EMPTY group means "attached to no command" and renders under the
49
+ ``UNGROUPED`` heading. A heading is what separates one block from the next,
50
+ so the ungrouped block is given one rather than left as rows appended to
51
+ whichever command sorted before them.
52
+ """
53
+
54
+ kind: str
55
+ spelling: str
56
+ hidden: bool = False
57
+ hidden_by: str | None = None
58
+ group: str = ""
59
+
60
+ # (kind, spelling) -> "may-change" | "reserved". An item absent here is stable.
61
+ Markers = dict[tuple[str, str], str]
62
+
63
+ # The spelling column is padded to this width so the stability markers line up
64
+ # and a diff reads as a change to one item rather than to the whole block.
65
+ COLUMN = 64
66
+
67
+ # The heading an item renders under when its consumer gave it no command.
68
+ #
69
+ # NAMED RATHER THAN LEFT BLANK, because a heading is the only thing separating
70
+ # one block from the next: an ungrouped row printed with NO heading lands
71
+ # directly under the last command's items and reads as one of them. The blank
72
+ # version looks tidier in the one case it helps -- a consumer that groups nothing
73
+ # -- and misleads in the case that matters, which is a consumer that groups
74
+ # almost everything and forgets one.
75
+ UNGROUPED = "(ungrouped)"
76
+
77
+
78
+ def walk(cmd, prefix: tuple[str, ...] = (), hidden_by: str | None = None):
79
+ """Every leaf command, as (path, command, hidden_by).
80
+
81
+ Hidden is INHERITED: a command is hidden when its own flag says so OR any
82
+ ancestor group is hidden. Typer marks a sub-app hidden at the GROUP level
83
+ (``add_typer(app, name="grip", hidden=True)``), and the group's children
84
+ keep ``hidden=False`` of their own -- so reading only the leaf reports a
85
+ hidden group's whole subtree as part of the visible surface. Measured: the
86
+ ``grip`` alias group is hidden and 28 of its items were dumped as visible.
87
+
88
+ ``hidden_by`` NAMES the ancestor group that did the hiding, because the dump
89
+ carries no group row of its own: without the name, one group flag reads as
90
+ dozens of identical lines and the cause is invisible to a reader.
91
+ """
92
+ subs = getattr(cmd, "commands", None)
93
+ if subs:
94
+ if getattr(cmd, "hidden", False) and prefix:
95
+ hidden_by = prefix[-1]
96
+ out: list[tuple[tuple[str, ...], object, str | None]] = []
97
+ for name, sub in subs.items():
98
+ out.extend(walk(sub, prefix + (name,), hidden_by))
99
+ return out
100
+ return [(prefix, cmd, hidden_by)]
101
+
102
+
103
+ def label(spelling: str, hidden: bool, hidden_by: str | None) -> str:
104
+ """The label column for one item -- the text BEFORE any parsing strips it.
105
+
106
+ One home for the rule, because a gate that wants to ask a question about the
107
+ label TEXT (does it end in a space? how long is it?) cannot use the parsed
108
+ rows: a parser strips the padding, and the trailing space it would be asking
109
+ about is exactly what stripping removes. Measured -- a first version of
110
+ ``test_no_label_ends_in_a_space`` read labels through a parser and could not
111
+ have failed for any input, because every label it saw had already been
112
+ stripped.
113
+
114
+ An inherited hide names the group that did it; a command hidden in its own
115
+ right is just ``(hidden)``. The dump carries no group row, so the name is the
116
+ only place the cause can live.
117
+ """
118
+ if not hidden:
119
+ return spelling
120
+ return f"{spelling} (hidden:{hidden_by})" if hidden_by else f"{spelling} (hidden)"
121
+
122
+
123
+ def line(kind: str, label: str, marker: str) -> str:
124
+ """One ``.api`` line: two fixed columns, with the separator GUARANTEED.
125
+
126
+ A label at or past ``COLUMN`` is emitted at its own length, so padding it to
127
+ ``COLUMN`` adds nothing and the marker runs straight into the label. Every
128
+ label in this dump contains spaces (a flag reads ``verb --flag <str>``), so a
129
+ reader splitting the two columns then takes a space INSIDE the label as the
130
+ separator and recovers neither column.
131
+
132
+ Measured before this rule existed: the 65-char ``exit`` label rendered as
133
+ ``...store verb: "may-change``, whose last space is the one inside the quoted
134
+ prefix. The columns came back as the label truncated at ``verb:`` and a
135
+ marker of ``"may-change``, the marker lookup then missed, and every exit line
136
+ silently read as ``stable``.
137
+
138
+ Pulled out of ``render`` so the width rule has ONE home and a synthetic long
139
+ label can be tested against it directly, rather than only through whichever
140
+ labels happen to reach ``COLUMN`` today.
141
+
142
+ ONE SHAPE THIS CANNOT FIX, because the ambiguity is on the reader's side of
143
+ the line: a label whose LAST character is a space is indistinguishable from
144
+ the padding, so a reader stripping the padding returns it one character
145
+ short. Guaranteeing the separator does not help -- the space is inside the
146
+ label, before it. No label ends in a space today, and
147
+ ``test_no_label_ends_in_a_space`` keeps it that way.
148
+ """
149
+ width = max(COLUMN, len(label) + 1)
150
+ return f"{kind:<6}{label:<{width}}{marker}"
151
+
152
+
153
+ def load_markers(path: Path) -> Markers:
154
+ """(kind, spelling) -> 'may-change' | 'reserved', from a stability table.
155
+
156
+ An absent file is an empty table rather than an error: a checkout with no
157
+ stability table has made no promises, and every item reading ``stable``
158
+ there is the stated default. The entries are ``"<kind> <spelling>"``, split
159
+ on the FIRST space, because a spelling carries spaces of its own.
160
+ """
161
+ if not path.exists():
162
+ return {}
163
+ data = tomllib.loads(path.read_text())
164
+ out: Markers = {}
165
+ for marker in ("may-change", "reserved"):
166
+ for entry in data.get(marker, []):
167
+ kind, _, spelling = entry.partition(" ")
168
+ out[(kind, spelling)] = marker
169
+ return out
170
+
171
+
172
+ def plain_marker(kind: str) -> Callable[[str, Markers], str]:
173
+ """The default marker rule for a kind: plain lookup, stable unless named.
174
+
175
+ Defaulting to stable is deliberate -- a new item is stable unless somebody
176
+ says otherwise in review -- and it is the rule most kinds want, so a
177
+ consumer registering one writes ``plain_marker("exit")`` rather than a
178
+ four-line closure that has to be read to be trusted.
179
+ """
180
+
181
+ def resolve(spelling: str, markers: Markers) -> str:
182
+ return markers.get((kind, spelling), "stable")
183
+
184
+ return resolve
185
+
186
+
187
+ @dataclass(frozen=True)
188
+ class Kind:
189
+ """One item kind, as the consumer registers it.
190
+
191
+ ``items`` yields ``Row``s; ``marker`` answers for this kind alone, which is
192
+ the point of registering per kind -- the lookup rule for a ``json`` key path
193
+ (inherit the canonical verb's promise) is not the rule for an ``exit`` code
194
+ (plain lookup) and a single dispatching function had to know both.
195
+ """
196
+
197
+ name: str
198
+ items: Callable[[], list[Row]]
199
+ marker: Callable[[str, Markers], str]
200
+
201
+
202
+ class Registry:
203
+ """The kinds this dump carries, in the order the consumer registered them."""
204
+
205
+ def __init__(self) -> None:
206
+ self._kinds: list[Kind] = []
207
+
208
+ def register(
209
+ self,
210
+ name: str,
211
+ items: Callable[[], list[Row]],
212
+ marker: Callable[[str, Markers], str],
213
+ ) -> Kind:
214
+ if any(kind.name == name for kind in self._kinds):
215
+ raise ValueError(f"kind {name!r} is already registered")
216
+ kind = Kind(name=name, items=items, marker=marker)
217
+ self._kinds.append(kind)
218
+ return kind
219
+
220
+ @property
221
+ def names(self) -> tuple[str, ...]:
222
+ return tuple(kind.name for kind in self._kinds)
223
+
224
+ def rows(self) -> list[Row]:
225
+ out: list[Row] = []
226
+ for kind in self._kinds:
227
+ out.extend(kind.items())
228
+ return out
229
+
230
+ def marker(self, kind: str, spelling: str, markers: Markers) -> str:
231
+ """The marker for one item, from the kind that owns it.
232
+
233
+ An UNREGISTERED kind is refused rather than defaulted. Answering
234
+ ``stable`` for a kind nobody registered would publish a promise no one
235
+ made, and it would do it silently -- the failure would look exactly like
236
+ an item the reviewers simply never named.
237
+ """
238
+ for registered in self._kinds:
239
+ if registered.name == kind:
240
+ return registered.marker(spelling, markers)
241
+ raise KeyError(
242
+ f"no kind {kind!r} is registered; registered: {list(self.names)}"
243
+ )
244
+
245
+
246
+ def blocks(rows: list[Row]) -> list[tuple[str, list[Row]]]:
247
+ """Rows grouped by command path, the groups in COMMAND-TREE order.
248
+
249
+ The ORDER is by path, which is what makes the file read as a tree: ``store``
250
+ before ``store init``, and both before ``store-x``, so a command's
251
+ subcommands sit with it rather than scattered by an unrelated key.
252
+
253
+ THE KEY IS WRITTEN ON THE PATH'S TOKENS, AND ON THIS DUMP THAT CHANGES
254
+ NOTHING -- measured, not assumed: the separator is a space (0x20), every
255
+ character a path component can begin with sorts above it, and token order and
256
+ joined-string order agree on all 88 real headings. It is written on tokens
257
+ because the RULE is what a reader auditing the order should find stated, and
258
+ citing a separator's incidental collation order as the reason is how a rule
259
+ becomes an accident nobody can check.
260
+
261
+ The UNGROUPED block sorts LAST, and THAT is the part the key actually earns:
262
+ it keys on the ABSENCE of tokens rather than on the empty string, which would
263
+ sort it first and open the file with the items that have no home.
264
+ """
265
+ by_group: dict[str, list[Row]] = {}
266
+ for row in rows:
267
+ by_group.setdefault(row.group, []).append(row)
268
+ return sorted(
269
+ by_group.items(), key=lambda item: (not item[0].split(), item[0].split())
270
+ )
271
+
272
+
273
+ def render(registry: Registry, markers: Markers | None = None) -> str:
274
+ """The exact bytes of an ``.api`` file, terminated by one newline.
275
+
276
+ GROUPED BY COMMAND, not by kind. The first column is still the item kind and
277
+ each line still carries one item, but the ORDER is now the command tree: a
278
+ heading names a command and its items sit under it. The previous order was a
279
+ single flat lexicographic sort, so all 120 ``arg`` rows preceded all 215
280
+ ``flag`` rows and no command's surface could be read in one place -- "what
281
+ does ``store init`` take" was four searches, one per kind.
282
+
283
+ WITHIN a block the rows are sorted by their rendered line, which is the old
284
+ (kind, then label) order: a diff then reads one command at a time, and an
285
+ item that moves shows up moving between two blocks rather than teleporting
286
+ to a different corner of the file.
287
+
288
+ The heading is a COMMENT (``# <command>``) rather than a row, so a reader
289
+ that parses item lines has one unambiguous shape to parse and the heading
290
+ cannot be mistaken for a kind. EVERY block gets one, the ungrouped block
291
+ included (``# (ungrouped)``): a heading is what separates one block from the
292
+ next, so the block without one is the block that gets misread.
293
+ """
294
+ markers = {} if markers is None else markers
295
+ lines: list[str] = []
296
+ for group, rows in blocks(registry.rows()):
297
+ lines.append(f"# {group or UNGROUPED}")
298
+ lines += sorted(
299
+ line(
300
+ row.kind,
301
+ label(row.spelling, row.hidden, row.hidden_by),
302
+ registry.marker(row.kind, row.spelling, markers),
303
+ )
304
+ for row in rows
305
+ )
306
+ return "\n".join(lines) + "\n"