gitgrip 2.0.0a11__tar.gz → 2.0.0a12__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 (517) hide show
  1. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/PKG-INFO +1 -1
  2. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gitgrip.egg-info/PKG-INFO +1 -1
  3. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gitgrip.egg-info/SOURCES.txt +5 -0
  4. gitgrip-2.0.0a12/gr2/python_cli/_build.json +1 -0
  5. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/app.py +9 -9
  6. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/approvals.py +6 -4
  7. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/clone_exec.py +2 -2
  8. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/gitops.py +3 -1
  9. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/grip_cli.py +4 -4
  10. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/hooks.py +2 -1
  11. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/merge_gate.py +8 -3
  12. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/migration.py +97 -11
  13. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/workspace_guidance.py +2 -1
  14. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/pyproject.toml +1 -1
  15. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_branch_in_lane.py +51 -0
  16. gitgrip-2.0.0a12/tests/test_migrate_gr1_member_origin.py +215 -0
  17. gitgrip-2.0.0a12/tests/test_pin_rewind_shallow_path_quoted.py +37 -0
  18. gitgrip-2.0.0a12/tests/test_printed_command_values_quoted.py +140 -0
  19. gitgrip-2.0.0a12/tests/test_review_merge_tracking_command_quoted.py +269 -0
  20. gitgrip-2.0.0a12/tests/test_spec_sourced_git_lines_quoted.py +153 -0
  21. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_submodule_init.py +19 -1
  22. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_version_static.py +2 -2
  23. gitgrip-2.0.0a11/gr2/python_cli/_build.json +0 -1
  24. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/README.md +0 -0
  25. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gitgrip.egg-info/dependency_links.txt +0 -0
  26. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gitgrip.egg-info/entry_points.txt +0 -0
  27. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gitgrip.egg-info/requires.txt +0 -0
  28. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gitgrip.egg-info/top_level.txt +0 -0
  29. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/PLATFORM_ADAPTER_CHANGELOG.md +0 -0
  30. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/__init__.py +0 -0
  31. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/api_core.py +0 -0
  32. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/__init__.py +0 -0
  33. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/activate.py +0 -0
  34. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/agent_manifest.py +0 -0
  35. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/cli.py +0 -0
  36. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/cross_repo.py +0 -0
  37. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/drivers.py +0 -0
  38. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/introspection.py +0 -0
  39. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/language_drivers.py +0 -0
  40. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/objects.py +0 -0
  41. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/perf.py +0 -0
  42. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/refs.py +0 -0
  43. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/trust.py +0 -0
  44. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/types.py +0 -0
  45. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/units.py +0 -0
  46. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/overlay/workspace_spec.py +0 -0
  47. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/__init__.py +0 -0
  48. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/cache_materialization_probe.py +0 -0
  49. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/concurrent_event_stress.py +0 -0
  50. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/concurrent_lease_stress.py +0 -0
  51. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/concurrent_workspace_cap_stress.py +0 -0
  52. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/contribution_protocol.py +0 -0
  53. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/cross_mode_lane_stress.py +0 -0
  54. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/jsonl_store.py +0 -0
  55. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/lane_workspace_prototype.py +0 -0
  56. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/layout_model_probe.py +0 -0
  57. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/propagation_daemon.py +0 -0
  58. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/propagation_state_machine.py +0 -0
  59. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/python_exec_playground.py +0 -0
  60. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/python_hook_runtime_playground.py +0 -0
  61. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/python_migration_playground.py +0 -0
  62. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/python_review_checkout_playground.py +0 -0
  63. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/python_spec_apply_playground.py +0 -0
  64. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/real_git_lane_materialization.py +0 -0
  65. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/real_git_playground.py +0 -0
  66. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/recall_lane_history.py +0 -0
  67. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/repo_maintenance_prototype.py +0 -0
  68. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/prototypes/repo_transport_probe.py +0 -0
  69. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/__init__.py +0 -0
  70. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/__main__.py +0 -0
  71. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/add.py +0 -0
  72. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/agent_record.py +0 -0
  73. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/approval_schema.py +0 -0
  74. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/arbon.py +0 -0
  75. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/branch.py +0 -0
  76. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/branch_update.py +0 -0
  77. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/channel_bridge.py +0 -0
  78. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/check_records.py +0 -0
  79. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/commit.py +0 -0
  80. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/config.py +0 -0
  81. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/consent.py +0 -0
  82. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/context.py +0 -0
  83. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/declared_sets.py +0 -0
  84. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/defaults.py +0 -0
  85. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/delete.py +0 -0
  86. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/ecosystems_python.py +0 -0
  87. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/env_exec.py +0 -0
  88. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/events.py +0 -0
  89. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/execops.py +0 -0
  90. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/exit_codes.py +0 -0
  91. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/failures.py +0 -0
  92. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/file_exec.py +0 -0
  93. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/git_review.py +0 -0
  94. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/gitinclude.py +0 -0
  95. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/gr_resolver.py +0 -0
  96. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/gr_shadow.py +0 -0
  97. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/grip.py +0 -0
  98. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/lane_defaults.py +0 -0
  99. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/lane_downstream.py +0 -0
  100. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/lane_graph.py +0 -0
  101. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/lane_plugins.py +0 -0
  102. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/launch_exec.py +0 -0
  103. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/layout.py +0 -0
  104. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/leased_push.py +0 -0
  105. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/member_field_tree.py +0 -0
  106. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/merge_receipts.py +0 -0
  107. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/merge_verification.py +0 -0
  108. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/names.py +0 -0
  109. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/next_steps.py +0 -0
  110. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/open_gr_review.py +0 -0
  111. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/platform.py +0 -0
  112. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/pr.py +0 -0
  113. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/project_review.py +0 -0
  114. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/proto_json.py +0 -0
  115. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/prune.py +0 -0
  116. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/push.py +0 -0
  117. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/read_review.py +0 -0
  118. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review.py +0 -0
  119. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_allocation.py +0 -0
  120. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_diff.py +0 -0
  121. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_dispatch.py +0 -0
  122. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_ephemeral.py +0 -0
  123. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_field_tree.py +0 -0
  124. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_members.py +0 -0
  125. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_rebind.py +0 -0
  126. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_records.py +0 -0
  127. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_run.py +0 -0
  128. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_runners.py +0 -0
  129. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/review_update.py +0 -0
  130. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/root_option.py +0 -0
  131. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/set_check.py +0 -0
  132. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/set_hints.py +0 -0
  133. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/shadow.py +0 -0
  134. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/spec_apply.py +0 -0
  135. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/staging_cleanup.py +0 -0
  136. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/store_hooks.py +0 -0
  137. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/syncops.py +0 -0
  138. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/target.py +0 -0
  139. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/tracking.py +0 -0
  140. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/version.py +0 -0
  141. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/workspace_mode.py +0 -0
  142. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/python_cli/workspace_snapshot.py +0 -0
  143. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.approval.v1alpha1/approval.proto +0 -0
  144. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.approval.v1alpha1/descriptor_set.pb +0 -0
  145. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.check.v1alpha1/check.proto +0 -0
  146. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.check.v1alpha1/descriptor_set.pb +0 -0
  147. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.merge.v1alpha1/descriptor_set.pb +0 -0
  148. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.merge.v1alpha1/merge.proto +0 -0
  149. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.review.v1alpha1/descriptor_set.pb +0 -0
  150. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/dev.synapt.grip.review.v1alpha1/review.proto +0 -0
  151. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/gr2-materialization-plan-v1.schema.json +0 -0
  152. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2/schemas/gr2-workspace-spec-v1.schema.json +0 -0
  153. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/gr2_overlay/__init__.py +0 -0
  154. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/setup.cfg +0 -0
  155. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/setup.py +0 -0
  156. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_add.py +0 -0
  157. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_add_lane_from_a_subdirectory.py +0 -0
  158. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_agent_manifest_overlay_source.py +0 -0
  159. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_agent_record_append.py +0 -0
  160. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_agent_record_fold.py +0 -0
  161. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_agent_record_stamps.py +0 -0
  162. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_api_core_seam.py +0 -0
  163. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_api_dump.py +0 -0
  164. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_app_event_outcomes.py +0 -0
  165. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_apply_convergence.py +0 -0
  166. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_apply_gr1_sibling_adoption.py +0 -0
  167. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_approval_schema.py +0 -0
  168. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_arbon.py +0 -0
  169. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_bind_merge_head_refusals.py +0 -0
  170. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_branch.py +0 -0
  171. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_break_17_unit_path_containment.py +0 -0
  172. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_break_18_gr1_sibling_units.py +0 -0
  173. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_channel_bridge.py +0 -0
  174. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_cli_adapter_error_boundary.py +0 -0
  175. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_cli_shape_witnesses.py +0 -0
  176. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_clone_materialization.py +0 -0
  177. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_commit.py +0 -0
  178. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_config_base_path_guard.py +0 -0
  179. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_config_overlay.py +0 -0
  180. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_conftest_git_isolation.py +0 -0
  181. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_consent_key_paths.py +0 -0
  182. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_context_resolver.py +0 -0
  183. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_contribution_protocol.py +0 -0
  184. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_declared_set_pins.py +0 -0
  185. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_declared_sets.py +0 -0
  186. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_defaults_from_repo.py +0 -0
  187. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_delete.py +0 -0
  188. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_deprecation_registry.py +0 -0
  189. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_ecosystems_python.py +0 -0
  190. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_ensure_lane_checkout_reference_clone.py +0 -0
  191. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_env_materialization.py +0 -0
  192. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_event_log_integrity.py +0 -0
  193. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_event_outcome_policy.py +0 -0
  194. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_events.py +0 -0
  195. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_events_torn_line.py +0 -0
  196. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_exec_apostrophe_and_oserror.py +0 -0
  197. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_exec_run_names_what_did_not_run.py +0 -0
  198. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_exec_single_string_and_materialize_plan.py +0 -0
  199. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_execops.py +0 -0
  200. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_failure_event_outcome.py +0 -0
  201. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_fetch_refmap.py +0 -0
  202. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_fetch_refmap_pr_base.py +0 -0
  203. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_fetch_refmap_verbs.py +0 -0
  204. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_field_tree_name.py +0 -0
  205. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_field_tree_schema_reader.py +0 -0
  206. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_files_compose.py +0 -0
  207. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_first_five_minutes.py +0 -0
  208. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_foreground_launch_runtime.py +0 -0
  209. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_git_positional_refusal.py +0 -0
  210. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_git_positional_sites.py +0 -0
  211. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_git_review.py +0 -0
  212. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_git_transport_allow_list.py +0 -0
  213. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gitinclude_compiler.py +0 -0
  214. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gitops_locating_env.py +0 -0
  215. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gitops_shared.py +0 -0
  216. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr1_first_run_guidance.py +0 -0
  217. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr2_packaging.py +0 -0
  218. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr2_review_receipt.py +0 -0
  219. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr_installed_wheel.py +0 -0
  220. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr_resolved_cleared.py +0 -0
  221. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr_resolver_conformance.py +0 -0
  222. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_gr_shadow.py +0 -0
  223. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_grip_cli.py +0 -0
  224. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_grip_hardening.py +0 -0
  225. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_grip_snapshot.py +0 -0
  226. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_grip_toml_literals.py +0 -0
  227. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_grip_toml_schema_v1.py +0 -0
  228. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_hook_events.py +0 -0
  229. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_hook_hardening.py +0 -0
  230. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_hooks_consent.py +0 -0
  231. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_hooks_group_names.py +0 -0
  232. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_hostile_names.py +0 -0
  233. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_hostile_names_helper.py +0 -0
  234. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_init_advice_never_nests.py +0 -0
  235. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_jsonl_store.py +0 -0
  236. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_cache_keyed_by_member_name.py +0 -0
  237. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_checkout_root.py +0 -0
  238. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_create_defaults.py +0 -0
  239. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_create_fork_base_cli.py +0 -0
  240. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_create_refusal_leaves_nothing.py +0 -0
  241. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_create_refuses_own_branch.py +0 -0
  242. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_downstream.py +0 -0
  243. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_downstream_run.py +0 -0
  244. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_enter_hook_warnings.py +0 -0
  245. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_exit_keeps_dirty_work.py +0 -0
  246. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_graph.py +0 -0
  247. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_lease_show_json.py +0 -0
  248. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_lifecycle_safety.py +0 -0
  249. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_plugins.py +0 -0
  250. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_probe.py +0 -0
  251. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_probe_batch.py +0 -0
  252. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_push.py +0 -0
  253. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_remove.py +0 -0
  254. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_repo_paths_and_all_skipped.py +0 -0
  255. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_roles.py +0 -0
  256. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_show_name.py +0 -0
  257. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_store_path_pins.py +0 -0
  258. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_lane_toml_serialization.py +0 -0
  259. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_launch_primitive.py +0 -0
  260. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_layout_census.py +0 -0
  261. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_leased_push.py +0 -0
  262. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_linked_review_receipts.py +0 -0
  263. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_m2_overlay_hindsight.py +0 -0
  264. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_m2_overlay_mem0.py +0 -0
  265. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_m2_overlay_zep.py +0 -0
  266. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_malformed_remote_refusal.py +0 -0
  267. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_materialization_plan_contract.py +0 -0
  268. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_materialize_pin.py +0 -0
  269. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_member_working_root_checkout_materialize.py +0 -0
  270. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_merge_credential_remotes.py +0 -0
  271. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_merge_evidence.py +0 -0
  272. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_merge_parent_verification.py +0 -0
  273. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_migrate_gr1_one_root.py +0 -0
  274. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_migration.py +0 -0
  275. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_migration_overlay_compatibility.py +0 -0
  276. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_names.py +0 -0
  277. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_native_store_gitinclude.py +0 -0
  278. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_native_store_review_record_probes.py +0 -0
  279. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_no_bare_python_cli_imports.py +0 -0
  280. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_np_bench_contract_scenario.py +0 -0
  281. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_null_current_lane_refusal.py +0 -0
  282. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_one_pin_record.py +0 -0
  283. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_open_gr_enter.py +0 -0
  284. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_open_project_refusal_reason_cli.py +0 -0
  285. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_out_of_set_hint.py +0 -0
  286. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_outbox_ack_after_effect.py +0 -0
  287. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_outside_names_as_git_options.py +0 -0
  288. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_acceptance.py +0 -0
  289. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_activate.py +0 -0
  290. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_cli.py +0 -0
  291. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_cli_wiring.py +0 -0
  292. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_composition_merge.py +0 -0
  293. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_cross_repo_atomic_apply.py +0 -0
  294. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_cross_repo_perf_gate.py +0 -0
  295. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_cross_repo_rollback.py +0 -0
  296. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_driver_registry.py +0 -0
  297. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_introspection.py +0 -0
  298. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_multi_stack.py +0 -0
  299. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_object_encoding.py +0 -0
  300. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_perf_gates.py +0 -0
  301. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_python_driver.py +0 -0
  302. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_tier_b_encoding.py +0 -0
  303. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_tier_b_perf_gates.py +0 -0
  304. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_tier_b_repo_state.py +0 -0
  305. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_tier_b_staging_discipline.py +0 -0
  306. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_trust_model.py +0 -0
  307. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_overlay_workspace_spec.py +0 -0
  308. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_platform_adapter_changelog.py +0 -0
  309. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_platform_adapter_plugins.py +0 -0
  310. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_adapter_v2_target.py +0 -0
  311. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_create_pins_itself.py +0 -0
  312. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_events.py +0 -0
  313. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_group_legibility.py +0 -0
  314. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_merge_head_pin.py +0 -0
  315. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_merge_method.py +0 -0
  316. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_merge_missing_target.py +0 -0
  317. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_merge_partial_failure.py +0 -0
  318. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_review_subject.py +0 -0
  319. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_pr_view.py +0 -0
  320. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_project_file_materialization.py +0 -0
  321. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_project_review_range_reconstruction.py +0 -0
  322. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_project_review_three_repo.py +0 -0
  323. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_projection_tracked_dest.py +0 -0
  324. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_propagation_contribution.py +0 -0
  325. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_propagation_daemon.py +0 -0
  326. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_propagation_state_machine.py +0 -0
  327. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_proto_json.py +0 -0
  328. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_prune.py +0 -0
  329. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_push.py +0 -0
  330. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_python_group_install.py +0 -0
  331. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_readme_verbs_exist.py +0 -0
  332. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_readme_walk.py +0 -0
  333. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_record_schema_copies.py +0 -0
  334. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_refusal_names_the_cause.py +0 -0
  335. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_remote_check_records.py +0 -0
  336. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_repo_maintenance_gitops.py +0 -0
  337. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_repo_path_read_through.py +0 -0
  338. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_repo_status_linked_worktree.py +0 -0
  339. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_repo_status_schema_version.py +0 -0
  340. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_repo_status_symlinked_git_dir.py +0 -0
  341. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_retire_gr1.py +0 -0
  342. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_a4.py +0 -0
  343. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_a4_v2.py +0 -0
  344. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_a4_v3.py +0 -0
  345. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_allocation_recovery.py +0 -0
  346. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_ansi_parse.py +0 -0
  347. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_approvals.py +0 -0
  348. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_b3_reader_alignment.py +0 -0
  349. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_author.py +0 -0
  350. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_fork_base.py +0 -0
  351. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_from_range.py +0 -0
  352. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_migration.py +0 -0
  353. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_native_store.py +0 -0
  354. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_root_refs.py +0 -0
  355. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_bind_verify.py +0 -0
  356. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_check_name.py +0 -0
  357. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_check_unknown.py +0 -0
  358. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_cli.py +0 -0
  359. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_close_gr.py +0 -0
  360. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_close_keeps_local_work.py +0 -0
  361. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_context_defaults.py +0 -0
  362. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_context_witnesses.py +0 -0
  363. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_create_project_cli.py +0 -0
  364. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_dispatch.py +0 -0
  365. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_ephemeral.py +0 -0
  366. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_events.py +0 -0
  367. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_field_tree.py +0 -0
  368. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_field_tree_reader_edges.py +0 -0
  369. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_field_tree_readers.py +0 -0
  370. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_install_hint.py +0 -0
  371. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_json_refusals.py +0 -0
  372. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_gate.py +0 -0
  373. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_gate_lost_ack.py +0 -0
  374. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_platform.py +0 -0
  375. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_receipt.py +0 -0
  376. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_set_alone.py +0 -0
  377. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_set_required.py +0 -0
  378. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_merge_updates_tracking_ref.py +0 -0
  379. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_named_refusals.py +0 -0
  380. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_open_lane.py +0 -0
  381. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_open_reader_lane.py +0 -0
  382. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_pin_after_push.py +0 -0
  383. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_pin_ratified.py +0 -0
  384. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_pin_rows_json_help.py +0 -0
  385. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_pin_stamp_names.py +0 -0
  386. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_pinned_target.py +0 -0
  387. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_pr_marker_context.py +0 -0
  388. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_publish_all.py +0 -0
  389. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_read_pr.py +0 -0
  390. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_reader_defaults.py +0 -0
  391. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_reads.py +0 -0
  392. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_rebind.py +0 -0
  393. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_receipt_cleanup.py +0 -0
  394. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_receive_no_migrate.py +0 -0
  395. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_ref_v1_migration.py +0 -0
  396. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_ref_v1_reader.py +0 -0
  397. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_ref_v1_writer.py +0 -0
  398. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_remote_safety.py +0 -0
  399. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run.py +0 -0
  400. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_context.py +0 -0
  401. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_deleted_cwd.py +0 -0
  402. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_dependency_order.py +0 -0
  403. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_import_isolation.py +0 -0
  404. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_integrity_recheck.py +0 -0
  405. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_multi_repo.py +0 -0
  406. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_package_name.py +0 -0
  407. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_plugin_order.py +0 -0
  408. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_rerun_and_refusal_receipt.py +0 -0
  409. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_run_venv_and_refusal_identity.py +0 -0
  410. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_runners.py +0 -0
  411. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_show_diff.py +0 -0
  412. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_subject.py +0 -0
  413. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_transport.py +0 -0
  414. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_update.py +0 -0
  415. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_v1_format_guard.py +0 -0
  416. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_verify_inside.py +0 -0
  417. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_verify_json_refusals.py +0 -0
  418. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_review_vocabulary.py +0 -0
  419. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_rmtree_or_refuse.py +0 -0
  420. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_root_option_verbs.py +0 -0
  421. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_root_optional_help.py +0 -0
  422. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_selected_workspace_commit.py +0 -0
  423. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_set_check.py +0 -0
  424. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_set_check_credential_remote.py +0 -0
  425. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_set_check_merge_head.py +0 -0
  426. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_set_check_remote_heads.py +0 -0
  427. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_shadow.py +0 -0
  428. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_shadow_lease.py +0 -0
  429. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_single_repo_cwd_refusal.py +0 -0
  430. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_skill_dump.py +0 -0
  431. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_snapshot_id_prefix_resolution.py +0 -0
  432. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_spec_apply_event_outcomes.py +0 -0
  433. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_spec_declaration_ref_pin.py +0 -0
  434. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_spec_show_native_root.py +0 -0
  435. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sprint21_sync_platform.py +0 -0
  436. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_staging_cleanup.py +0 -0
  437. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_break_attempts.py +0 -0
  438. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_check_undeclared_gitlink.py +0 -0
  439. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_checkout_atomicity.py +0 -0
  440. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_commit_compat_next_step.py +0 -0
  441. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_commit_follows_branch.py +0 -0
  442. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_commit_keeps_record.py +0 -0
  443. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_commit_noop.py +0 -0
  444. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_commit_refuses_rewind.py +0 -0
  445. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_declared_members.py +0 -0
  446. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_fence_no_pin.py +0 -0
  447. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_full_mode_carrier.py +0 -0
  448. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_full_mode_coverage.py +0 -0
  449. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_full_mode_layers.py +0 -0
  450. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_full_mode_records.py +0 -0
  451. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_git_hooks.py +0 -0
  452. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_git_native_smoke.py +0 -0
  453. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_gitmodules.py +0 -0
  454. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_gitmodules_branch.py +0 -0
  455. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_hook_takes_verb_coverage.py +0 -0
  456. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_init_branch_inference.py +0 -0
  457. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_init_clone.py +0 -0
  458. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_init_derived_spec.py +0 -0
  459. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_init_slot_pointer.py +0 -0
  460. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_init_slot_write_error.py +0 -0
  461. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_log_adoption_boundary.py +0 -0
  462. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_materialize_branch.py +0 -0
  463. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_materialize_fetches_new_pin.py +0 -0
  464. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_materialize_installs_hooks.py +0 -0
  465. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_member_path_hygiene.py +0 -0
  466. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_member_state.py +0 -0
  467. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_migrate_branch_inference.py +0 -0
  468. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_moved_workspace_guard.py +0 -0
  469. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_push_landed_by_another.py +0 -0
  470. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_push_member_refs.py +0 -0
  471. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_push_s2.py +0 -0
  472. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_push_s3a.py +0 -0
  473. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_push_s3b.py +0 -0
  474. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_refusal_names_both_exits.py +0 -0
  475. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_shadow_fence.py +0 -0
  476. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_shadow_s1.py +0 -0
  477. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_status_inspection.py +0 -0
  478. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_status_push_undeclared_gitlink.py +0 -0
  479. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_status_values.py +0 -0
  480. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_submodule_update_merge.py +0 -0
  481. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_untracked_member.py +0 -0
  482. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_verb_rename.py +0 -0
  483. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_verb_witnesses.py +0 -0
  484. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_store_verbs_name_their_root.py +0 -0
  485. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_subdir_cwd_review_verbs.py +0 -0
  486. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_subdir_cwd_verbs.py +0 -0
  487. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_submodule_member_predicate.py +0 -0
  488. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_superproject_import.py +0 -0
  489. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sync_event_outcomes.py +0 -0
  490. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sync_fast_forward.py +0 -0
  491. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sync_fetch.py +0 -0
  492. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sync_seed_local.py +0 -0
  493. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sync_ssh_rewrite.py +0 -0
  494. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_sync_status_probe.py +0 -0
  495. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_target.py +0 -0
  496. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_tmux_launch_runtime.py +0 -0
  497. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_tracking.py +0 -0
  498. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_unit_abort.py +0 -0
  499. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_unit_apply.py +0 -0
  500. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_unit_apply_dry_run.py +0 -0
  501. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_unit_manifest_schema.py +0 -0
  502. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_unit_propose.py +0 -0
  503. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_unit_topological_apply.py +0 -0
  504. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_update.py +0 -0
  505. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_validate_repo_path_conflict.py +0 -0
  506. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_version_build_stamp.py +0 -0
  507. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_version_identity.py +0 -0
  508. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_vocabulary.py +0 -0
  509. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_convert_clone.py +0 -0
  510. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_edit_lease_cap.py +0 -0
  511. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_identity_boundary.py +0 -0
  512. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_init_from_topology.py +0 -0
  513. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_init_local_repos.py +0 -0
  514. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_root_defaults.py +0 -0
  515. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_root_nearest_marker.py +0 -0
  516. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_snapshot.py +0 -0
  517. {gitgrip-2.0.0a11 → gitgrip-2.0.0a12}/tests/test_workspace_spec_missing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitgrip
3
- Version: 2.0.0a11
3
+ Version: 2.0.0a12
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitgrip
3
- Version: 2.0.0a11
3
+ Version: 2.0.0a12
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
@@ -259,6 +259,7 @@ tests/test_member_working_root_checkout_materialize.py
259
259
  tests/test_merge_credential_remotes.py
260
260
  tests/test_merge_evidence.py
261
261
  tests/test_merge_parent_verification.py
262
+ tests/test_migrate_gr1_member_origin.py
262
263
  tests/test_migrate_gr1_one_root.py
263
264
  tests/test_migration.py
264
265
  tests/test_migration_overlay_compatibility.py
@@ -294,6 +295,7 @@ tests/test_overlay_tier_b_repo_state.py
294
295
  tests/test_overlay_tier_b_staging_discipline.py
295
296
  tests/test_overlay_trust_model.py
296
297
  tests/test_overlay_workspace_spec.py
298
+ tests/test_pin_rewind_shallow_path_quoted.py
297
299
  tests/test_platform_adapter_changelog.py
298
300
  tests/test_platform_adapter_plugins.py
299
301
  tests/test_pr_adapter_v2_target.py
@@ -306,6 +308,7 @@ tests/test_pr_merge_missing_target.py
306
308
  tests/test_pr_merge_partial_failure.py
307
309
  tests/test_pr_review_subject.py
308
310
  tests/test_pr_view.py
311
+ tests/test_printed_command_values_quoted.py
309
312
  tests/test_project_file_materialization.py
310
313
  tests/test_project_review_range_reconstruction.py
311
314
  tests/test_project_review_three_repo.py
@@ -364,6 +367,7 @@ tests/test_review_merge_platform.py
364
367
  tests/test_review_merge_receipt.py
365
368
  tests/test_review_merge_set_alone.py
366
369
  tests/test_review_merge_set_required.py
370
+ tests/test_review_merge_tracking_command_quoted.py
367
371
  tests/test_review_merge_updates_tracking_ref.py
368
372
  tests/test_review_named_refusals.py
369
373
  tests/test_review_open_lane.py
@@ -421,6 +425,7 @@ tests/test_snapshot_id_prefix_resolution.py
421
425
  tests/test_spec_apply_event_outcomes.py
422
426
  tests/test_spec_declaration_ref_pin.py
423
427
  tests/test_spec_show_native_root.py
428
+ tests/test_spec_sourced_git_lines_quoted.py
424
429
  tests/test_sprint21_sync_platform.py
425
430
  tests/test_staging_cleanup.py
426
431
  tests/test_store_break_attempts.py
@@ -0,0 +1 @@
1
+ {"commit": "8c58df6", "release": true}
@@ -1678,7 +1678,7 @@ def workspace_init(
1678
1678
  lines.append("no origin: materialize refuses these repos until a url is set:")
1679
1679
  for repo in without_url:
1680
1680
  lines.append(
1681
- f"- {repo['name']}: git -C {workspace_root / repo['path']} "
1681
+ f"- {repo['name']}: git -C {next_steps._shown(workspace_root / repo['path'])} "
1682
1682
  "remote add origin <url>"
1683
1683
  )
1684
1684
  typer.echo("\n".join(lines))
@@ -2087,7 +2087,7 @@ def _lane_member_branch_in(workspace_root: Path, repo_top: Path) -> tuple[str, s
2087
2087
  if key is not None:
2088
2088
  # A native workspace lane materializes every member of the selected workspace commit, which can
2089
2089
  # differ from this lane's repos once the store grows: name no --repos there, so lane create takes them all.
2090
- repos = "" if grip._is_native_workspace(workspace_root) else f" --repos {key}"
2090
+ repos = "" if grip._is_native_workspace(workspace_root) else f" --repos {next_steps._shown(key)}"
2091
2091
  return unit, lane_name, key, str(branch_map.get(key) or lane_name), repos
2092
2092
  return None
2093
2093
 
@@ -2128,9 +2128,9 @@ def _refuse_branching_a_lane_member(target: Path, name: str) -> None:
2128
2128
  if name == lane_branch:
2129
2129
  return
2130
2130
  typer.echo(
2131
- f"Error: {key} is a repo of lane {unit}/{lane_name}, whose branch for it is '{lane_branch}'; "
2131
+ f"Error: {key} is a repo of lane {next_steps._shown(f'{unit}/{lane_name}')}, whose branch for it is '{lane_branch}'; "
2132
2132
  f"'{name}' was not created or switched to. The lane records its branch and fork base, so work on "
2133
- f"another branch belongs in a new lane: gr2 lane create <workspace> {unit} <lane>{repos} "
2133
+ f"another branch belongs in a new lane: gr2 lane create <workspace> {next_steps._shown(unit)} <lane>{repos} "
2134
2134
  f"--branch {name}",
2135
2135
  err=True,
2136
2136
  )
@@ -2788,12 +2788,12 @@ def repo_status(
2788
2788
  if state == "changed":
2789
2789
  hook_lines.append(
2790
2790
  f"hooks changed since grant {getattr(target, 'name', target.path.name)} "
2791
- f"(sha {hooks_sha(target.path)[:12]}) — re-bind: gr2 hooks trust {getattr(target, 'name', target.path.name)}"
2791
+ f"(sha {hooks_sha(target.path)[:12]}) — re-bind: gr2 hooks trust {next_steps._shown(getattr(target, 'name', target.path.name))}"
2792
2792
  )
2793
2793
  else:
2794
2794
  hook_lines.append(
2795
2795
  f"hooks unbound {getattr(target, 'name', target.path.name)} "
2796
- f"(sha {hooks_sha(target.path)[:12]}) — bind: gr2 hooks trust {getattr(target, 'name', target.path.name)}"
2796
+ f"(sha {hooks_sha(target.path)[:12]}) — bind: gr2 hooks trust {next_steps._shown(getattr(target, 'name', target.path.name))}"
2797
2797
  )
2798
2798
 
2799
2799
  if json_output:
@@ -2967,9 +2967,9 @@ def hooks_status(
2967
2967
  if row["state"] == "bound":
2968
2968
  typer.echo(f"hooks bound {row['member']} (sha {row['hooks_sha']}, granted {row['granted_at']})")
2969
2969
  elif row["state"] == "changed":
2970
- typer.echo(f"hooks changed since grant {row['member']} (now sha {row['hooks_sha']}) — re-bind: gr2 hooks trust {row['member']}")
2970
+ typer.echo(f"hooks changed since grant {row['member']} (now sha {row['hooks_sha']}) — re-bind: gr2 hooks trust {next_steps._shown(row['member'])}")
2971
2971
  else:
2972
- typer.echo(f"hooks unbound {row['member']} (sha {row['hooks_sha']}) — bind: gr2 hooks trust {row['member']}")
2972
+ typer.echo(f"hooks unbound {row['member']} (sha {row['hooks_sha']}) — bind: gr2 hooks trust {next_steps._shown(row['member'])}")
2973
2973
 
2974
2974
 
2975
2975
  @hooks_app.command("show")
@@ -4219,7 +4219,7 @@ def review_read_pr(
4219
4219
  typer.echo(f"base {opened.base}")
4220
4220
  if opened.stat:
4221
4221
  typer.echo(opened.stat)
4222
- typer.echo(f"Read the full diff: git -C {opened.path} diff {opened.base}...{opened.head}")
4222
+ typer.echo(f"Read the full diff: git -C {next_steps._shown(opened.path)} diff {next_steps._shown(f'{opened.base}...{opened.head}')}")
4223
4223
 
4224
4224
 
4225
4225
  class _ReviewSelectRefused(typer.BadParameter):
@@ -16,6 +16,7 @@ from datetime import datetime, timezone
16
16
  from pathlib import Path
17
17
 
18
18
  from . import approval_schema, gitops, grip
19
+ from .next_steps import _shown
19
20
 
20
21
  PREFIX = "refs/dev.synapt.grip/__approvals__/v1/"
21
22
  SCHEMA = approval_schema.PACKAGE
@@ -151,14 +152,14 @@ def review_open_lane(lane: Path) -> tuple[Path, str, dict[str, Path]] | None:
151
152
  raise ApprovalRefused(f"approval_lane_names_no_workspace: {lane} was opened by an older gr2 and does not name "
152
153
  "the workspace it was opened from",
153
154
  next_step=f"next: open the review again, then stamp the new lane: "
154
- f"gr2 review open <your-workspace> gr:{marker['gr_commit']} --lane-dir <new-lane>, "
155
+ f"gr2 review open <your-workspace> {_shown('gr:' + str(marker['gr_commit']))} --lane-dir <new-lane>, "
155
156
  "then gr2 review stamp <new-lane>")
156
157
  if not Path(marker["workspace_root"]).is_absolute():
157
158
  raise ApprovalRefused(f"approval_lane_workspace_not_absolute: {lane} names the workspace "
158
159
  f"{marker['workspace_root']!r}, which is not absolute and would resolve against the "
159
160
  "current directory; review open writes it absolute, so this marker was edited by hand or written elsewhere",
160
161
  next_step=f"next: open the review again, then stamp the new lane: "
161
- f"gr2 review open <your-workspace> gr:{marker['gr_commit']} --lane-dir <new-lane>, "
162
+ f"gr2 review open <your-workspace> {_shown('gr:' + str(marker['gr_commit']))} --lane-dir <new-lane>, "
162
163
  "then gr2 review stamp <new-lane>")
163
164
  try:
164
165
  members = ({rows[0]["key"]: lane} if len(rows) == 1
@@ -190,11 +191,12 @@ def _context(workspace: Path, review_id: str, member_dirs: dict[str, Path] | Non
190
191
  repo = member_dirs.get(m["key"])
191
192
  if repo is None:
192
193
  keys = [r["key"] for r in view["members"]]
194
+ from . import next_steps
193
195
  raise ApprovalRefused(
194
196
  f"approval_lane_partial: this lane holds {', '.join(sorted(member_dirs))}; the review has "
195
197
  f"{', '.join(keys)}",
196
- next_step=f"next: open the whole review and stamp that lane: gr2 review open {workspace} "
197
- f"gr:{rid} --lane-dir <new-lane>, then gr2 review stamp <new-lane>")
198
+ next_step=f"next: open the whole review and stamp that lane: gr2 review open {next_steps._shown(workspace)} "
199
+ f"{next_steps._shown('gr:' + rid)} --lane-dir <new-lane>, then gr2 review stamp <new-lane>")
198
200
  if merge_gate._toplevel(repo) != repo.resolve():
199
201
  raise ApprovalRefused(f"approval_member_repo_mismatch: {m['key']}")
200
202
  repo = repo.resolve()
@@ -48,7 +48,7 @@ import time
48
48
  import urllib.parse
49
49
  from pathlib import Path
50
50
 
51
- from . import gitops
51
+ from . import gitops, next_steps
52
52
  from .spec_apply import (
53
53
  MaterializationPlanError,
54
54
  ValidatedPlan,
@@ -972,7 +972,7 @@ def _reuse_existing_lane(
972
972
  raise CloneExecutionError(
973
973
  f"existing lane at {dest} is on branch {actual_branch!r}, not the expected "
974
974
  f"{expected_branch!r}. Materialization never switches a lane's branch; move "
975
- f"it back with `git -C {dest} checkout {expected_branch}` or remove the lane "
975
+ f"it back with `git -C {next_steps._shown(dest)} checkout {next_steps._shown(expected_branch)}` or remove the lane "
976
976
  "to re-materialize (grip#807 step 6)"
977
977
  )
978
978
  if expected_seed is not None and actual_head != expected_seed:
@@ -4,6 +4,8 @@ import os
4
4
  import subprocess
5
5
  from pathlib import Path
6
6
 
7
+ from . import next_steps
8
+
7
9
 
8
10
  _SSH_GITHUB_PREFIX = "git@github.com:"
9
11
  _HTTPS_GITHUB_PREFIX = "https://github.com/"
@@ -588,7 +590,7 @@ def checkout_declared_pin(repo_root: Path, pin: str, *, member: str) -> None:
588
590
  raise SystemExit(
589
591
  f"member '{member}' declares pin {pin}, the clone cannot reach that commit, and "
590
592
  f"fetching it by sha from origin did not provide it. Try "
591
- f"`git -C {repo_root} fetch --depth 1 origin -- {pin}` by hand to see why. This verb "
593
+ f"`git -C {next_steps._shown(repo_root)} fetch --depth 1 origin -- {next_steps._shown(pin)}` by hand to see why. This verb "
592
594
  "will not leave the member on its default branch instead: that is a commit the "
593
595
  "root never declared."
594
596
  )
@@ -591,13 +591,13 @@ def _register_submodules(root: Path, paths: list[str]) -> None:
591
591
  done = _store_git(root, "submodule", "init", "--", path, check=False)
592
592
  if done.returncode:
593
593
  typer.echo(f"{path}: not registered as a submodule ({_git_detail(done)}); plain `git submodule` verbs "
594
- f"need `git submodule init {path}` first", err=True)
594
+ f"need `git submodule init -- {next_steps._shown(path)}` first", err=True)
595
595
  continue
596
596
  now = _registered_submodule_urls(root)
597
597
  new, registered = now - registered, now
598
598
  if new:
599
599
  typer.echo(f"{path}: registered as a submodule, so plain `git submodule` verbs act on it "
600
- f"(`git submodule deinit {path}` lasts until the next store commit)", err=True)
600
+ f"(`git submodule deinit -- {next_steps._shown(path)}` lasts until the next store commit)", err=True)
601
601
 
602
602
 
603
603
  def _existing_sets(root: Path) -> list[dict]:
@@ -2013,7 +2013,7 @@ def _refuse_pin_rewind(
2013
2013
  # says nothing about direction there; materialize would be the wrong advice for a checkout ahead.
2014
2014
  raise PinRewindRefusal(
2015
2015
  f"{name}: cannot tell whether HEAD {head[:12]} is behind the recorded pin {recorded[:12]}: this "
2016
- f"checkout is shallow and does not reach it; run `git fetch --unshallow` in {member['path']} and "
2016
+ f"checkout is shallow and does not reach it; run `git fetch --unshallow` in {next_steps._shown(member['path'])} and "
2017
2017
  f"commit again, or {allow} to record HEAD anyway",
2018
2018
  3,
2019
2019
  )
@@ -3831,7 +3831,7 @@ def grip_materialize_cmd(
3831
3831
  if member["branch"] is None:
3832
3832
  typer.echo(
3833
3833
  f"{member['name']}: left detached at {member['pin'][:12]}: {member['detached']}; "
3834
- f"to work on a branch: git -C {member['path']} switch -c <branch>",
3834
+ f"to work on a branch: git -C {next_steps._shown(member['path'])} switch -c <branch>",
3835
3835
  err=True,
3836
3836
  )
3837
3837
  # stderr in both modes, so neither stdout contract moves
@@ -9,6 +9,7 @@ import tomllib
9
9
  from pathlib import Path
10
10
 
11
11
  from .events import EventType, emit, emit_after_outcome
12
+ from .next_steps import _shown
12
13
  from . import gitops
13
14
 
14
15
 
@@ -844,7 +845,7 @@ def _report_unbound(
844
845
  + ", ".join(ignored)
845
846
  + " — consent is a local record on the host, never inside the hooks table"
846
847
  )
847
- print(f"bind: gr2 hooks trust {ctx.repo_name}")
848
+ print(f"bind: gr2 hooks trust {_shown(ctx.repo_name)}")
848
849
 
849
850
 
850
851
  def run_lifecycle_stage(
@@ -111,11 +111,15 @@ def _refresh_tracking(repo: Path, remote: str, into: str) -> dict | None:
111
111
  name = next((n for n in names if urls[n] and _url_key(urls[n], repo) == want), None)
112
112
  if name is None:
113
113
  target = "origin" if "origin" in names else names[0]
114
- return {"command": f"git -C {repo} fetch --no-tags --refmap= {remote} +refs/heads/{into}:refs/remotes/{target}/{into}",
114
+ shown = next_steps._shown # every value quoted whole: a refname may hold ';', so the line runs as printed
115
+ return {"command": f"git -C {shown(repo)} fetch --no-tags --refmap= -- {shown(remote)} "
116
+ f"{shown(f'+refs/heads/{into}:refs/remotes/{target}/{into}')}",
115
117
  "reason": f"no remote of this clone names {remote}"}
116
118
  spec = f"+refs/heads/{into}:refs/remotes/{name}/{into}"
117
119
  proc = gitops.run(repo, "fetch", "--no-tags", "-q", "--refmap=", name, spec, timeout=120)
118
- return None if proc.returncode == 0 else {"command": f"git -C {repo} fetch --no-tags --refmap= {name} {spec}",
120
+ shown = next_steps._shown
121
+ return None if proc.returncode == 0 else {"command": f"git -C {shown(repo)} fetch --no-tags --refmap= -- {shown(name)} "
122
+ f"{shown(spec)}",
119
123
  "reason": "the fetch failed"}
120
124
 
121
125
 
@@ -652,7 +656,8 @@ def review_merge(workspace: Path, review_id: str, *, into: str = "main", feature
652
656
  try:
653
657
  failed = _refresh_tracking(repo, m["remote"], into)
654
658
  except (RuntimeError, subprocess.SubprocessError, OSError) as exc:
655
- failed = {"command": f"git -C {repo} fetch --no-tags <remote> +refs/heads/{into}:refs/remotes/<remote>/{into}",
659
+ spec = next_steps._shown(f"+refs/heads/{into}:refs/remotes/<remote>/{into}")
660
+ failed = {"command": f"git -C {next_steps._shown(repo)} fetch --no-tags <remote> {spec}",
656
661
  "reason": f"the clone could not be read: {exc}"}
657
662
  if failed:
658
663
  tracking_stale.append({"member": m["key"], **failed})
@@ -11,11 +11,12 @@ import tempfile
11
11
  import tomllib
12
12
  import contextlib
13
13
  from pathlib import Path, PurePosixPath, PureWindowsPath
14
+ from urllib.parse import urlsplit, urlunsplit
14
15
 
15
16
  import yaml
16
17
 
17
18
  from . import grip
18
- from .gitops import git
19
+ from .gitops import git, remote_origin_url
19
20
  from .workspace_guidance import GR1_ONLY_NEXT_STEP, init_unless_inside_workspace, workspace_init_command
20
21
  from gr2.prototypes import lane_workspace_prototype as lane_proto
21
22
  from gr2.prototypes import repo_maintenance_prototype as repo_proto
@@ -117,6 +118,7 @@ def migrate_gr1_workspace(
117
118
  "unit_count": len(compiled["units"]),
118
119
  "snapshots": snapshots,
119
120
  "not_migrated": list(compiled.get("not_migrated") or []),
121
+ "member_origins": list(compiled.get("member_origins") or []),
120
122
  }
121
123
  summary_path.write_text(json.dumps(summary, indent=2) + "\n")
122
124
 
@@ -794,6 +796,33 @@ def _rollback_bootstrap(
794
796
  shutil.rmtree(grip_dir)
795
797
 
796
798
 
799
+ def _member_checkout_origin(workspace_root: Path, member_path: str) -> str | None:
800
+ """The origin url of the checkout at a member's path, when that path is a repository of its own.
801
+
802
+ A path with no `.git` of its own is not the member's checkout: inside a root that is itself a
803
+ repository, asking git there would answer with the ROOT's origin.
804
+ """
805
+ checkout = workspace_root / member_path
806
+ if not (checkout / ".git").exists():
807
+ return None
808
+ return remote_origin_url(checkout)
809
+
810
+
811
+ _SSH_SCHEMES = frozenset({"ssh", "git+ssh", "ssh+git"})
812
+
813
+
814
+ def _strip_url_userinfo(url: str) -> str:
815
+ """The url without its userinfo, except an SSH login (`ssh://git@host/...`), which names an account and
816
+ holds no secret. An scp-like `git@host:path` has no scheme and is returned as written."""
817
+ try:
818
+ parts = urlsplit(url)
819
+ except ValueError:
820
+ return url
821
+ if "@" not in parts.netloc or parts.scheme.lower() in _SSH_SCHEMES:
822
+ return url
823
+ return urlunsplit(parts._replace(netloc=parts.netloc.rpartition("@")[2]))
824
+
825
+
797
826
  def compile_gr1_to_workspace_spec(
798
827
  workspace_root: Path,
799
828
  manifest: dict[str, object],
@@ -806,6 +835,7 @@ def compile_gr1_to_workspace_spec(
806
835
  raise ValueError("canonical gripspace manifest repos must be a mapping")
807
836
  repos: list[dict[str, object]] = []
808
837
  writable_repo_names: list[str] = []
838
+ member_origins: list[dict[str, str]] = []
809
839
 
810
840
  for repo_name, repo_doc in repos_doc.items():
811
841
  safe_repo_name = _safe_workspace_component(repo_name, "repo name")
@@ -813,10 +843,30 @@ def compile_gr1_to_workspace_spec(
813
843
  raise ValueError(f"repository {safe_repo_name!r} must be a mapping")
814
844
  path = repo_doc.get("path", "")
815
845
  normalized_path = _safe_workspace_relative_path(path, f"repository {safe_repo_name!r} path")
846
+ manifest_url = str(repo_doc.get("url", "")).strip()
847
+ # The checkout at the member's path is what this desk actually fetches from; the manifest's url
848
+ # is what it said when the repo was added, and the two drift (a renamed repo, an SSH url, a fork).
849
+ checkout_url = _member_checkout_origin(workspace_root, normalized_path)
850
+ url = manifest_url
851
+ if checkout_url is not None:
852
+ # A credential never leaves the checkout's own git config: userinfo is stripped, and an origin that
853
+ # still carries one (a token in the query, a fragment) is not used and not printed.
854
+ checkout_url = _strip_url_userinfo(checkout_url)
855
+ if grip.url_has_credentials(checkout_url):
856
+ member_origins.append(
857
+ {"repo": safe_repo_name, "manifest_url": manifest_url, "checkout_url": None,
858
+ "refused": "credentials"}
859
+ )
860
+ else:
861
+ url = checkout_url
862
+ if checkout_url != manifest_url:
863
+ member_origins.append(
864
+ {"repo": safe_repo_name, "manifest_url": manifest_url, "checkout_url": checkout_url}
865
+ )
816
866
  repo_item = {
817
867
  "name": safe_repo_name,
818
868
  "path": normalized_path,
819
- "url": str(repo_doc.get("url", "")).strip(),
869
+ "url": url,
820
870
  }
821
871
  if "revision" in repo_doc:
822
872
  repo_item["default_branch"] = str(repo_doc.get("revision") or "").strip()
@@ -859,6 +909,7 @@ def compile_gr1_to_workspace_spec(
859
909
  "repos": repos,
860
910
  "units": units,
861
911
  "not_migrated": not_migrated,
912
+ "member_origins": member_origins,
862
913
  "workspace_constraints": {
863
914
  "migration_source": "gr1",
864
915
  },
@@ -1067,30 +1118,46 @@ def preserve_gr1_state(workspace_root: Path, migration_dir: Path) -> dict[str, s
1067
1118
  return snapshots
1068
1119
 
1069
1120
 
1121
+ def _toml_str(value: object) -> str:
1122
+ """A TOML basic string holding exactly `value`: a quote, backslash, newline or other control character in a url
1123
+ or name stays data. Only what TOML requires is escaped (the quote, the backslash, U+0000..U+001F and U+007F);
1124
+ everything else is written literally, since TOML is UTF-8 and an escaped surrogate pair (what an ASCII-only
1125
+ JSON string makes of a character above U+FFFF) is not a value TOML will load."""
1126
+ out = []
1127
+ for ch in str(value):
1128
+ if ch in ('"', "\\"):
1129
+ out.append("\\" + ch)
1130
+ elif ord(ch) < 0x20 or ord(ch) == 0x7F:
1131
+ out.append(f"\\u{ord(ch):04X}")
1132
+ else:
1133
+ out.append(ch)
1134
+ return '"' + "".join(out) + '"'
1135
+
1136
+
1070
1137
  def render_workspace_spec(compiled: dict[str, object]) -> str:
1071
1138
  lines = [
1072
- f'workspace_name = "{compiled["workspace_name"]}"',
1139
+ f'workspace_name = {_toml_str(compiled["workspace_name"])}',
1073
1140
  "",
1074
1141
  ]
1075
1142
  constraints = compiled.get("workspace_constraints") or {}
1076
1143
  if constraints:
1077
1144
  lines.append("[workspace_constraints]")
1078
1145
  for key, value in constraints.items():
1079
- lines.append(f'{key} = "{value}"')
1146
+ lines.append(f'{key} = {_toml_str(value)}')
1080
1147
  lines.append("")
1081
1148
 
1082
1149
  for repo in compiled["repos"]:
1083
1150
  lines.extend(
1084
1151
  [
1085
1152
  "[[repos]]",
1086
- f'name = "{repo["name"]}"',
1087
- f'path = "{repo["path"]}"',
1088
- f'url = "{repo["url"]}"',
1153
+ f'name = {_toml_str(repo["name"])}',
1154
+ f'path = {_toml_str(repo["path"])}',
1155
+ f'url = {_toml_str(repo["url"])}',
1089
1156
  ]
1090
1157
  )
1091
1158
  default_branch = str(repo.get("default_branch", "")).strip()
1092
1159
  if default_branch:
1093
- lines.append(f'default_branch = "{default_branch}"')
1160
+ lines.append(f'default_branch = {_toml_str(default_branch)}')
1094
1161
  if repo.get("reference", False):
1095
1162
  lines.append("reference = true")
1096
1163
  lines.append("")
@@ -1099,9 +1166,9 @@ def render_workspace_spec(compiled: dict[str, object]) -> str:
1099
1166
  lines.extend(
1100
1167
  [
1101
1168
  "[[units]]",
1102
- f'name = "{unit["name"]}"',
1103
- f'path = "{unit["path"]}"',
1104
- "repos = [" + ", ".join(f'"{repo}"' for repo in unit["repos"]) + "]",
1169
+ f'name = {_toml_str(unit["name"])}',
1170
+ f'path = {_toml_str(unit["path"])}',
1171
+ "repos = [" + ", ".join(_toml_str(repo) for repo in unit["repos"]) + "]",
1105
1172
  ]
1106
1173
  )
1107
1174
  lines.append("")
@@ -1147,6 +1214,25 @@ def render_migration(payload: dict[str, object]) -> str:
1147
1214
  f"{len(not_migrated)} unit(s) in the gr1 agents manifest are not in this workspace's spec,"
1148
1215
  " and nothing under them was read. A desk that is its own root migrates from that root."
1149
1216
  )
1217
+ member_origins = payload.get("member_origins") or []
1218
+ if member_origins:
1219
+ lines.append("MEMBER ORIGIN")
1220
+ for row in member_origins:
1221
+ if row.get("refused") == "credentials":
1222
+ lines.append(
1223
+ f"- {_printable(row['repo'])}: the checkout's origin carries credentials or cannot be parsed and"
1224
+ " was not used;"
1225
+ f" the spec keeps {_printable(row['manifest_url'])}. Remove the credential from the url and use a"
1226
+ " credential helper."
1227
+ )
1228
+ continue
1229
+ lines.append(
1230
+ f"- {_printable(row['repo'])}: {_printable(row['manifest_url'])} -> {_printable(row['checkout_url'])}"
1231
+ )
1232
+ lines.append(
1233
+ f"{len(member_origins)} member(s) differ from the gr1 manifest; the spec records each checkout's own"
1234
+ " origin (userinfo removed), which is what this desk fetches from."
1235
+ )
1150
1236
  lines.append("SNAPSHOTS")
1151
1237
  lines.extend(f"- {name}\t{path}" for name, path in payload["snapshots"].items())
1152
1238
  return "\n".join(lines)
@@ -3,6 +3,7 @@
3
3
  from pathlib import Path
4
4
 
5
5
  from .layout import grip_dir
6
+ from .next_steps import _shown
6
7
 
7
8
  GR1_ONLY_NEXT_STEP = (
8
9
  "This workspace is still in gr1 format. Run `gr2 workspace migrate-gr1 .` "
@@ -51,7 +52,7 @@ def missing_workspace_spec_message(workspace_root: Path) -> str:
51
52
  def workspace_init_command(target: object = "") -> str:
52
53
  """The one place a verb's advice spells the init command (a structural test holds every other text to it), so
53
54
  whether that advice suits the path is decided once, in `init_unless_inside_workspace`."""
54
- return f"`gr2 workspace init {target}`" if str(target) else "`gr2 workspace init`"
55
+ return f"`gr2 workspace init {_shown(target)}`" if str(target) else "`gr2 workspace init`"
55
56
 
56
57
 
57
58
  def inside_workspace_sentence(path: Path, enclosing: Path) -> str:
@@ -11,7 +11,7 @@ name = "gitgrip"
11
11
  # number and it is not derived from Cargo.toml. gr1 releases as crates.io/GitHub
12
12
  # `v1.x.y`; gr2 releases as PyPI `gr2/v2.x.y`. Runtime reads it via
13
13
  # importlib.metadata.version("gitgrip"), never a checked-in file.
14
- version = "2.0.0a11"
14
+ version = "2.0.0a12"
15
15
  description = "Workspace layer for multi-repo work: workspaces, isolated lanes, and one grouped review per slice"
16
16
  readme = "README.md"
17
17
  requires-python = ">=3.11"
@@ -388,3 +388,54 @@ def test_an_unloadable_bound_lane_without_a_usable_worktree_does_not_hold_its_la
388
388
  made = _branch(repo, "other")
389
389
  assert made.exit_code == 0, made.output
390
390
  assert _git(repo, "branch", "--show-current") == "other"
391
+
392
+
393
+ def test_the_lane_named_in_the_refusal_is_quoted_when_its_directories_hold_shell_text(tmp_path: Path) -> None:
394
+ # Rule: the unit and lane come from the lane doc's directory names, not from the person typing `gr2 branch`,
395
+ # so the printed lane create quotes them: shlex.split gives the unit as one argument, and the line run by a
396
+ # shell (gr2 stubbed) starts nothing the directory names hold.
397
+ from gr2.prototypes import lane_workspace_prototype as lanes
398
+ import subprocess
399
+
400
+ ws, _repos = _lane(tmp_path)
401
+ repo = _plain(ws / "solo-repo")
402
+ unit, lane = "u; touch MARKU", "l; touch MARKL"
403
+ path = lanes.lane_file(ws, unit, lane)
404
+ path.parent.mkdir(parents=True)
405
+ path.write_text(f'lane_kind = "bound"\nrepos = ["solo"]\nbound_worktree = "{repo}"\n')
406
+ moved = _branch(repo, "other")
407
+ assert moved.exit_code == 2, moved.output
408
+ assert f"lane {shlex.quote(f'{unit}/{lane}')}," in moved.output, moved.output
409
+ printed = moved.output.split("new lane: ", 1)[1].strip().replace("<workspace>", str(ws)).replace("<lane>", "next")
410
+ argv = shlex.split(printed)
411
+ assert argv[:6] == ["gr2", "lane", "create", str(ws), unit, "next"], printed
412
+ assert argv[-2:] == ["--branch", "other"], printed
413
+ ran = subprocess.run(["bash", "-c", "gr2() { :; }\n" + printed], cwd=tmp_path, capture_output=True, text=True)
414
+ assert ran.returncode == 0, ran.stderr
415
+ assert not (tmp_path / "MARKU").exists() and not (tmp_path / "MARKL").exists(), printed
416
+ assert list(tmp_path.glob("MARK*")) == [], printed
417
+
418
+
419
+ def test_the_member_key_in_the_refusal_is_quoted_when_the_lane_record_holds_shell_text(tmp_path: Path) -> None:
420
+ # Rule: the `--repos` key comes from the lane record, not from the person typing `gr2 branch`, and a plain
421
+ # member key may hold ';' and a space (only separators, NUL and newline are refused), so the printed lane
422
+ # create quotes it: shlex.split gives the key as one argument, and the line run by a shell starts nothing.
423
+ from gr2.prototypes import lane_workspace_prototype as lanes
424
+ import subprocess
425
+
426
+ ws = tmp_path / "ws" # a spec workspace (no native root), where the refusal names --repos
427
+ (ws / ".grip").mkdir(parents=True)
428
+ (ws / ".grip" / "workspace_spec.toml").write_text('schema_version = 1\nworkspace_name = "m"\n')
429
+ repo = _plain(ws / "solo-repo")
430
+ key = "s; touch MARKK #"
431
+ path = lanes.lane_file(ws, "u", "l")
432
+ path.parent.mkdir(parents=True)
433
+ path.write_text(f'lane_kind = "bound"\nrepos = ["{key}"]\nbound_worktree = "{repo}"\n')
434
+ moved = _branch(repo, "other")
435
+ assert moved.exit_code == 2, moved.output
436
+ printed = moved.output.split("new lane: ", 1)[1].strip().replace("<workspace>", str(ws)).replace("<lane>", "next")
437
+ argv = shlex.split(printed)
438
+ assert argv[6:] == ["--repos", key, "--branch", "other"], printed
439
+ ran = subprocess.run(["bash", "-c", "gr2() { :; }\n" + printed], cwd=tmp_path, capture_output=True, text=True)
440
+ assert ran.returncode == 0, ran.stderr
441
+ assert list(tmp_path.glob("MARK*")) == [], printed