patchshuttle 0.1.0a5__tar.gz → 0.1.0a7__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 (169) hide show
  1. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/.github/workflows/ci.yml +37 -5
  2. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/CHANGELOG.md +54 -1
  3. patchshuttle-0.1.0a5/README.md → patchshuttle-0.1.0a7/PKG-INFO +143 -13
  4. patchshuttle-0.1.0a5/PKG-INFO → patchshuttle-0.1.0a7/README.md +99 -50
  5. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/SPEC_V0_1.md +85 -1
  6. patchshuttle-0.1.0a7/docs/CHATGPT_INTEGRATION.md +54 -0
  7. patchshuttle-0.1.0a7/docs/DJANGO_MIGRATIONS.md +83 -0
  8. patchshuttle-0.1.0a7/docs/NEXT_RELEASE.md +193 -0
  9. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/docs/RELEASE.md +43 -3
  10. patchshuttle-0.1.0a7/docs/ROADMAP.md +56 -0
  11. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/pyproject.toml +5 -0
  12. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_architecture.py +3 -1
  13. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_python_discovery.py +16 -2
  14. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_python_symbols.py +13 -1
  15. patchshuttle-0.1.0a7/src/patchshuttle/_refactor.py +147 -0
  16. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_version.py +1 -1
  17. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/actions/__init__.py +20 -0
  18. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/actions/constructors.py +170 -3
  19. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/actions/create.py +6 -0
  20. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/actions/modify.py +12 -0
  21. patchshuttle-0.1.0a7/src/patchshuttle/analysis/__init__.py +1 -0
  22. patchshuttle-0.1.0a7/src/patchshuttle/analysis/dependencies.py +160 -0
  23. patchshuttle-0.1.0a7/src/patchshuttle/analysis/references.py +136 -0
  24. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/audit.py +81 -0
  25. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/backup.py +28 -2
  26. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/cli.py +4 -141
  27. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/context.py +10 -1
  28. patchshuttle-0.1.0a7/src/patchshuttle/django_app/__init__.py +1 -0
  29. patchshuttle-0.1.0a7/src/patchshuttle/django_app/management/__init__.py +1 -0
  30. patchshuttle-0.1.0a7/src/patchshuttle/django_app/management/commands/__init__.py +1 -0
  31. patchshuttle-0.1.0a7/src/patchshuttle/django_app/management/commands/patchshuttle_migrate.py +50 -0
  32. patchshuttle-0.1.0a7/src/patchshuttle/django_app/workflow.py +306 -0
  33. patchshuttle-0.1.0a7/src/patchshuttle/documentation/__init__.py +1 -0
  34. patchshuttle-0.1.0a7/src/patchshuttle/documentation/cli.py +93 -0
  35. patchshuttle-0.1.0a7/src/patchshuttle/documentation/refresh.py +132 -0
  36. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/execution.py +2 -0
  37. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/formatters/runner.py +5 -1
  38. patchshuttle-0.1.0a7/src/patchshuttle/integrations/__init__.py +1 -0
  39. patchshuttle-0.1.0a7/src/patchshuttle/integrations/mcp_server.py +182 -0
  40. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/linters/runner.py +1 -1
  41. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/logging.py +10 -57
  42. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/models.py +173 -5
  43. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/planner.py +236 -191
  44. patchshuttle-0.1.0a7/src/patchshuttle/planning/__init__.py +1 -0
  45. patchshuttle-0.1.0a7/src/patchshuttle/planning/discovery_log.py +70 -0
  46. patchshuttle-0.1.0a7/src/patchshuttle/planning/display.py +114 -0
  47. patchshuttle-0.1.0a7/src/patchshuttle/planning/guidance.py +38 -0
  48. patchshuttle-0.1.0a7/src/patchshuttle/planning/imports.py +209 -0
  49. patchshuttle-0.1.0a7/src/patchshuttle/planning/symbols.py +53 -0
  50. patchshuttle-0.1.0a7/src/patchshuttle/planning/templates.py +39 -0
  51. patchshuttle-0.1.0a7/src/patchshuttle/planning/types.py +199 -0
  52. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/preflight.py +2 -0
  53. patchshuttle-0.1.0a7/src/patchshuttle/resources/AI_GUIDE.md +734 -0
  54. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/resources/PATCHSHUTTLE_PROTOCOL.md +229 -4
  55. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/rollback.py +6 -0
  56. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/runner.py +18 -18
  57. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/selfdoc.py +67 -6
  58. patchshuttle-0.1.0a7/tests/integrations/test_django_migrations.py +356 -0
  59. patchshuttle-0.1.0a7/tests/integrations/test_imports.py +186 -0
  60. patchshuttle-0.1.0a7/tests/integrations/test_mcp_server.py +242 -0
  61. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_cli.py +2 -2
  62. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_formatters.py +4 -0
  63. patchshuttle-0.1.0a7/tests/unit/test_move_file.py +217 -0
  64. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_package.py +1 -1
  65. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_preflight.py +2 -0
  66. patchshuttle-0.1.0a7/tests/unit/test_refactor.py +330 -0
  67. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_release_configuration.py +23 -3
  68. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_selfdoc.py +1 -1
  69. patchshuttle-0.1.0a7/tests/unit/test_structural_guards.py +251 -0
  70. patchshuttle-0.1.0a5/src/patchshuttle/resources/AI_GUIDE.md → patchshuttle-0.1.0a7/tests/unit/workflow/fixtures/a6-guide.md +83 -0
  71. patchshuttle-0.1.0a7/tests/unit/workflow/test_ai_doc_contract.py +108 -0
  72. patchshuttle-0.1.0a7/tests/unit/workflow/test_ai_guidance.py +122 -0
  73. patchshuttle-0.1.0a7/tests/unit/workflow/test_audit_templates.py +131 -0
  74. patchshuttle-0.1.0a7/tests/unit/workflow/test_dependencies.py +142 -0
  75. patchshuttle-0.1.0a7/tests/unit/workflow/test_guide_refresh.py +180 -0
  76. patchshuttle-0.1.0a7/tests/unit/workflow/test_import_batch.py +167 -0
  77. patchshuttle-0.1.0a7/tests/unit/workflow/test_references.py +118 -0
  78. patchshuttle-0.1.0a7/tests/unit/workflow/test_symbol_diagnostics.py +81 -0
  79. patchshuttle-0.1.0a7/tools/django_postgres_smoke.py +88 -0
  80. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tools/wheel_smoke.py +12 -0
  81. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/.github/workflows/release.yml +0 -0
  82. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/.github/workflows/testpypi.yml +0 -0
  83. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/.gitignore +0 -0
  84. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/LICENSE +0 -0
  85. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/SECURITY.md +0 -0
  86. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/__init__.py +0 -0
  87. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_ai_log.py +0 -0
  88. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_diff.py +0 -0
  89. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_html_lint.py +0 -0
  90. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_line_ranges.py +0 -0
  91. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_process.py +0 -0
  92. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/_python_structure.py +0 -0
  93. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/checks/__init__.py +0 -0
  94. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/checks/constructors.py +0 -0
  95. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/checks/runner.py +0 -0
  96. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/config.py +0 -0
  97. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/errors.py +0 -0
  98. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/formatter_policy.py +0 -0
  99. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/formatters/__init__.py +0 -0
  100. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/history/__init__.py +0 -0
  101. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/history/cli.py +0 -0
  102. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/history/models.py +0 -0
  103. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/history/records.py +0 -0
  104. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/history/storage.py +0 -0
  105. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/identifiers.py +0 -0
  106. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/inventory.py +0 -0
  107. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/linters/__init__.py +0 -0
  108. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/operations.py +0 -0
  109. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/parser.py +0 -0
  110. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/policy.py +0 -0
  111. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/project_python.py +0 -0
  112. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/py.typed +0 -0
  113. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/registry.py +0 -0
  114. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/resources/AUDIT-EXAMPLE.psh.yaml +0 -0
  115. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/resources/PATCH-EXAMPLE.psh.yaml +0 -0
  116. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/resources/__init__.py +0 -0
  117. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/runtime_cache.py +0 -0
  118. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/verification.py +0 -0
  119. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/warning_baseline.py +0 -0
  120. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/src/patchshuttle/workspace.py +0 -0
  121. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/e2e/test_cli_project_cycle.py +0 -0
  122. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/integration/test_workflows.py +0 -0
  123. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/history/test_history.py +0 -0
  124. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/history/test_history_integration.py +0 -0
  125. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/history/test_history_storage_edges.py +0 -0
  126. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_a4_coverage_regressions.py +0 -0
  127. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_actions.py +0 -0
  128. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_ai_log.py +0 -0
  129. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_architecture.py +0 -0
  130. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_attempt_logging.py +0 -0
  131. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_audit_verify_execution.py +0 -0
  132. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_backup.py +0 -0
  133. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_change_transaction.py +0 -0
  134. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_check_transaction.py +0 -0
  135. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_checks.py +0 -0
  136. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_config.py +0 -0
  137. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_context_and_constructors.py +0 -0
  138. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_diff.py +0 -0
  139. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_execution.py +0 -0
  140. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_formatting_transaction.py +0 -0
  141. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_guarded_ranges.py +0 -0
  142. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_html_lint.py +0 -0
  143. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_identifiers.py +0 -0
  144. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_inventory.py +0 -0
  145. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_inventory_transaction.py +0 -0
  146. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_logging.py +0 -0
  147. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_manual_rollback.py +0 -0
  148. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_models.py +0 -0
  149. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_modify_actions.py +0 -0
  150. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_operational_execution.py +0 -0
  151. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_parser.py +0 -0
  152. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_phase19_cli.py +0 -0
  153. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_planner.py +0 -0
  154. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_planner_ux.py +0 -0
  155. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_policy.py +0 -0
  156. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_project_python.py +0 -0
  157. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_python_discovery.py +0 -0
  158. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_python_structure.py +0 -0
  159. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_python_structure_action.py +0 -0
  160. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_python_symbols.py +0 -0
  161. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_registry.py +0 -0
  162. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_rollback.py +0 -0
  163. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_runner.py +0 -0
  164. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_runtime_cache.py +0 -0
  165. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_warning_baseline.py +0 -0
  166. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tests/unit/test_workspace.py +0 -0
  167. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tools/check_tag.py +0 -0
  168. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tools/index_smoke.py +0 -0
  169. {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a7}/tools/release_checks.py +0 -0
@@ -47,14 +47,46 @@ jobs:
47
47
  - name: Check formatting
48
48
  run: python -m black --check src tests tools
49
49
  - name: Run tests with branch coverage
50
- run: |
51
- python -m coverage erase
52
- python -m coverage run -m pytest -q
53
- python -m coverage report --fail-under=100
50
+ run: python -m coverage run -m pytest -q
51
+ - name: Require full branch coverage
52
+ run: python -m coverage report --fail-under=100
53
+
54
+ postgres:
55
+ name: Django / PostgreSQL 16
56
+ runs-on: ubuntu-24.04
57
+ timeout-minutes: 10
58
+ services:
59
+ postgres:
60
+ image: postgres:16
61
+ env:
62
+ POSTGRES_DB: patchshuttle_test
63
+ POSTGRES_USER: postgres
64
+ POSTGRES_PASSWORD: disposable-ci-password
65
+ ports:
66
+ - 5432:5432
67
+ options: >-
68
+ --health-cmd pg_isready
69
+ --health-interval 10s
70
+ --health-timeout 5s
71
+ --health-retries 5
72
+ steps:
73
+ - uses: actions/checkout@v7.0.1
74
+ - uses: actions/setup-python@v7.0.0
75
+ with:
76
+ python-version: "3.12"
77
+ - name: Install Django adapter and PostgreSQL driver
78
+ run: python -m pip install -e ".[django]" "psycopg[binary]>=3.2,<4"
79
+ - name: Check migration lifecycle and cross-connection lock
80
+ env:
81
+ PSH_PG_DATABASE: patchshuttle_test
82
+ PSH_PG_USER: postgres
83
+ PSH_PG_PASSWORD: disposable-ci-password
84
+ PSH_PG_PORT: "5432"
85
+ run: python tools/django_postgres_smoke.py
54
86
 
55
87
  package:
56
88
  name: Build and smoke-test distributions
57
- needs: test
89
+ needs: [test, postgres]
58
90
  runs-on: ubuntu-24.04
59
91
  timeout-minutes: 20
60
92
  steps:
@@ -3,7 +3,60 @@
3
3
  All notable changes to PatchShuttle are recorded in this file. The project
4
4
  uses semantic versioning, including Python-compatible pre-release versions.
5
5
 
6
- ## Unreleased
6
+ ## 0.1.0a7 - release candidate (not yet published)
7
+
8
+ - Audit AI instructions: complete action catalogs, correct method YAML indentation
9
+ and job-ID rules, clarify guard sources, MCP limits and database boundaries.
10
+
11
+ - Add `python_references` for bounded, explicitly unverified symbol-reference
12
+ candidates with import aliases and unresolved attributes.
13
+
14
+ - Add read-only `python_dependencies` for static local import candidates, cycle
15
+ groups and explicitly unresolved/dynamic cases before module refactoring.
16
+
17
+ - Add opt-in, bounded action drafts to symbol/import audits with observed guards;
18
+ fragments require review and never execute during audit.
19
+
20
+ - Add `edit_imports`: 1-100 ordered import edits guarded by one initial import
21
+ hash, with step diagnostics and the existing import safety checks.
22
+
23
+ - Explain structural replacement failures with bounded indentation context and
24
+ YAML guidance, without rewriting input or changing guard semantics.
25
+
26
+ - Add reviewed `docs refresh` for recognized AI guide templates, retaining
27
+ appended owner notes and exact backups; unknown inline edits require review.
28
+ - Add conditional Python symbol-edit guidance to CLI plans, full and compact
29
+ logs, including targeting failures; advisory does not alter guards or policy.
30
+ - Add operation-selection rules and an audit-to-patch example to AI guidance.
31
+ - Prefer configured MCP history retrieval, retaining manual logs as fallback.
32
+ - Count symbol/range transfers and report structural imports and file moves.
33
+ - Extract plan and discovery-log presentation into smaller modules without
34
+ relaxing the existing architecture ratchet.
35
+
36
+ ## 0.1.0a6 - 2026-10-21
37
+
38
+ ### Added
39
+
40
+ - Guarded `read_imports`, `ensure_import` and `remove_import` with alias-aware AST matching, comment preservation and binding checks.
41
+
42
+ - Optional authenticated MCP history server for ChatGPT, scoped to one workspace and OAuth subject.
43
+ - Optional Django migration command with hashed plans, database locks, pre/post checks, explicit reversals and separate recovery journals.
44
+ - Guarded `move_range` and `move_symbol` source transfers between files,
45
+ including destination fingerprints, structural validation, Python constructors,
46
+ and shared transaction rollback.
47
+ - Guarded `move_file` with destination absence checks, byte/mode preservation,
48
+ explicit deleted-file reporting, and automatic and manual rollback.
49
+
50
+ - Optional AST guards for `replace_symbol`, with hashes and Python version
51
+ reported by `read_symbol`, replacement declaration validation and compile
52
+ validation. Existing text-only job serialization remains compatible.
53
+
54
+ ### Changed
55
+
56
+ - Formatter child interpreters and Black preflight explicitly enable Python
57
+ UTF-8 mode, including under isolated execution.
58
+
59
+ See `docs/NEXT_RELEASE.md` for implemented scope and remaining release work.
7
60
 
8
61
  ## 0.1.0a5 - 2026-08-28
9
62
 
@@ -1,3 +1,47 @@
1
+ Metadata-Version: 2.4
2
+ Name: patchshuttle
3
+ Version: 0.1.0a7
4
+ Summary: Local, auditable patch workflows for AI-assisted software development
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: ai,automation,cli,development-tools,patch
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: black<27,>=24
21
+ Requires-Dist: click<9,>=8.1
22
+ Requires-Dist: filelock<4,>=3.30
23
+ Requires-Dist: isort<7,>=5.13
24
+ Requires-Dist: pydantic<3,>=2.6
25
+ Requires-Dist: pyyaml<7,>=6
26
+ Requires-Dist: ruff<1,>=0.16
27
+ Requires-Dist: tomli<3,>=2; python_version < '3.11'
28
+ Provides-Extra: dev
29
+ Requires-Dist: build<2,>=1.2; extra == 'dev'
30
+ Requires-Dist: django<7,>=5.2; extra == 'dev'
31
+ Requires-Dist: mcp<2,>=1.30; extra == 'dev'
32
+ Requires-Dist: pyjwt[crypto]<3,>=2.10; extra == 'dev'
33
+ Requires-Dist: pytest-cov<8,>=5; extra == 'dev'
34
+ Requires-Dist: pytest<10,>=8; extra == 'dev'
35
+ Requires-Dist: twine<7,>=6.1; extra == 'dev'
36
+ Provides-Extra: django
37
+ Requires-Dist: django<7,>=5.2; extra == 'django'
38
+ Provides-Extra: html
39
+ Requires-Dist: djlint<2,>=1.44; extra == 'html'
40
+ Provides-Extra: mcp
41
+ Requires-Dist: mcp<2,>=1.30; extra == 'mcp'
42
+ Requires-Dist: pyjwt[crypto]<3,>=2.10; extra == 'mcp'
43
+ Description-Content-Type: text/markdown
44
+
1
45
  # PatchShuttle
2
46
 
3
47
  PatchShuttle is intended to provide local, auditable patch workflows for
@@ -6,7 +50,8 @@ describes a small job, the user reviews and runs it locally, and PatchShuttle
6
50
  records the result for the next iteration.
7
51
 
8
52
  > [!IMPORTANT]
9
- > PatchShuttle is alpha software. Version `0.1.0a5` implements the complete
53
+ > PatchShuttle is alpha software. This branch prepares `0.1.0a7`; the latest
54
+ > recorded published version is `0.1.0a6`. It implements the complete
10
55
  > local v0.1 workflow. It executes bounded read-only
11
56
  > audits, approved patch transactions, and approved one-pass verification jobs
12
57
  > under a project lock. Patch jobs retain backups, run controlled checks, apply
@@ -63,7 +108,7 @@ After publication, install the exact alpha release from PyPI:
63
108
  ```bash
64
109
  python -m venv .venv
65
110
  python -m pip install --upgrade pip
66
- python -m pip install "patchshuttle==0.1.0a5"
111
+ python -m pip install "patchshuttle==0.1.0a6"
67
112
  ```
68
113
 
69
114
  For development from a local checkout:
@@ -76,7 +121,7 @@ To enable opt-in HTML template linting in an installed release, install the
76
121
  `html` extra:
77
122
 
78
123
  ```bash
79
- python -m pip install "patchshuttle[html]==0.1.0a5"
124
+ python -m pip install "patchshuttle[html]==0.1.0a6"
80
125
  ```
81
126
 
82
127
  For a local development checkout, install both development and HTML extras:
@@ -439,7 +484,8 @@ retained formatted-file states, final check results, audit observations, and a
439
484
  workspace comparison where applicable.
440
485
 
441
486
  An audit executes `tree`, `read`, literal `search`, bounded `search_context`,
442
- Python `read_symbol`, `find_files`, `file_info`, SHA-256 `hash`, canonical
487
+ Python `read_symbol`, `read_imports`, `python_structure`, `python_dependencies`,
488
+ `python_references`, `find_files`, `file_info`, SHA-256 `hash`, canonical
443
489
  `hash_range`, `git_status`, and `environment`. Traversal, file reads, match
444
490
  counts, and recorded output are bounded by local policy. Protected and ignored
445
491
  paths are skipped, source content appears only when explicitly requested by a
@@ -459,7 +505,8 @@ automatically undo those changes.
459
505
 
460
506
  A patch plan may contain `create_directory`, `create_file`, `replace_exact`,
461
507
  `replace_symbol`, `insert_before`, `insert_after`, `delete_exact`,
462
- `replace_range`, `delete_range`, `insert_at_line`, or `apply_diff` actions and
508
+ `replace_range`, `delete_range`, `insert_at_line`, `apply_diff`, `ensure_import`,
509
+ `remove_import`, `edit_imports`, `move_file`, `move_range`, or `move_symbol` actions and
463
510
  applies the final bytes already computed by the planner.
464
511
 
465
512
  The optional line-range actions are strict guarded operations for cases where
@@ -478,6 +525,13 @@ rejects missing, duplicate, syntactically invalid, or stale targets, and does
478
525
  not use fuzzy relocation or automatic line shifting. Exact desired symbol
479
526
  content is an idempotent `NO_CHANGE`.
480
527
 
528
+ The optional `expected_ast_sha256` guard uses the structural hash from
529
+ `read_symbol` to tolerate formatting changes. It ignores ordinary comments,
530
+ includes docstrings and decorators, and is scoped to the reported Python minor
531
+ version. Supply the text hash too when comments must be guarded. AST-guarded
532
+ replacements retain the name and kind and undergo compile and resolution
533
+ validation. See [next-release work](docs/NEXT_RELEASE.md) for current scope.
534
+
481
535
  For a supported plan, the implemented sequence is:
482
536
 
483
537
  1. validate the workspace lock file and acquire a non-blocking cross-platform
@@ -713,18 +767,19 @@ actions:
713
767
  symbol: Service.run
714
768
  ```
715
769
 
716
- If a change is needed, copy the canonical SHA-256 returned by `read_symbol`
717
- into a guarded patch action:
770
+ If a change is needed, use the audit-derived draft or copy `ast_sha256` from
771
+ `read_symbol` on the same Python minor version. Add its text `sha256` too when
772
+ comments must be guarded. The action below is illustrative:
718
773
 
719
774
  ```yaml
720
775
  actions:
721
776
  - replace_symbol:
722
777
  path: src/example/service.py
723
778
  symbol: Service.run
724
- expected_sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
725
- new_content: |
726
- def run(self, value):
727
- return value
779
+ expected_ast_sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
780
+ new_content: |2
781
+ def run(self, value):
782
+ return value
728
783
  ```
729
784
 
730
785
  The same audit can be constructed through the public Python adapter:
@@ -840,7 +895,7 @@ python -m coverage report --fail-under=100
840
895
  python -m build
841
896
  python -m twine check dist/*
842
897
  python tools/release_checks.py dist
843
- python tools/wheel_smoke.py dist/patchshuttle-0.1.0a5-py3-none-any.whl --version 0.1.0a5
898
+ python tools/wheel_smoke.py dist/patchshuttle-0.1.0a7-py3-none-any.whl --version 0.1.0a7
844
899
  ```
845
900
 
846
901
  The `0.1.0a2`, `0.1.0a3`, and `0.1.0a4` alpha releases each passed the
@@ -958,7 +1013,9 @@ The implemented local cycle is:
958
1013
  5. For a patch, run optional changed-HTML linting, controlled checks, the
959
1014
  locally resolved isort/Black scopes, and final checks.
960
1015
  6. Receive a timestamped log or generate a fresh handoff.
961
- 7. Review the log, upload it to the AI, and continue with the next job.
1016
+ 7. Retrieve configured MCP history or provide the log through an authorized
1017
+ channel. Current MCP history returns summaries/references, not full logs.
1018
+ Review the observed result before continuing with the next job.
962
1019
 
963
1020
  See [SPEC_V0_1.md](SPEC_V0_1.md) for the approved product contract and
964
1021
  [CHANGELOG.md](CHANGELOG.md) for release notes. PatchShuttle is designed for
@@ -971,3 +1028,76 @@ model, or project.
971
1028
  ## License
972
1029
 
973
1030
  PatchShuttle is licensed under the MIT License.
1031
+
1032
+
1033
+ ## Guarded source transfers
1034
+
1035
+ - `move_range`: `path`, `start_line`, `end_line`, source `expected_content`
1036
+ and/or `expected_sha256`, `destination`, `destination_line` and
1037
+ `expected_destination_sha256`.
1038
+ - `move_symbol`: `path`, `symbol`, `expected_ast_sha256`, `destination`,
1039
+ `expected_destination_sha256` and optional `destination_class`.
1040
+
1041
+ Destination hashes cover simulated raw bytes; source range hashes cover
1042
+ canonical LF/UTF-8 text. Destinations must exist or be created earlier in the
1043
+ same job. Range insertion is before the 1-based destination line; EOF+1 appends.
1044
+ Symbol insertion appends at module scope or to the named class. Both files are
1045
+ validated and participate in the same backup/check/rollback transaction.
1046
+
1047
+ Same-file transfers, case-only path differences, duplicate destination names,
1048
+ closure extraction, class-bound super/private-name moves and multiline-string
1049
+ reindentation are rejected. Imports and callers are not automatically rewritten;
1050
+ include explicit edits and focused project checks. These actions move source
1051
+ content and leave the source file present. Whole-file moves are separate work.
1052
+
1053
+
1054
+ ## Whole-file transfers
1055
+
1056
+ `move_file` moves an existing supported text file to an absent destination.
1057
+ Supply `path`, `destination`, and `expected_sha256` of the original raw bytes.
1058
+ The transaction preserves original bytes and permissions, creates missing
1059
+ parent directories, and reports the source in `deleted_files`. Formatters and
1060
+ checks target the surviving destination. Add explicit import and caller edits
1061
+ when moving Python modules; imports are not automatically rewritten.
1062
+
1063
+ The source and destination must not have been touched earlier in the job.
1064
+ Subsequent edits to the destination are supported. Case-only moves, chains,
1065
+ swaps, binary files and reuse of the vacated path are currently rejected.
1066
+ A failed check restores the source and removes the destination. Manual rollback
1067
+ also verifies the applied state and refuses to overwrite a recreated source.
1068
+
1069
+
1070
+ ## Optional integrations
1071
+
1072
+ - `patchshuttle[mcp]`: authenticated, read-only history retrieval for ChatGPT. See [setup and deployment limits](docs/CHATGPT_INTEGRATION.md).
1073
+ - `patchshuttle[django]`: reviewed migration plans, explicit execution/reversal, and a separate recovery journal. See [Django workflow](docs/DJANGO_MIGRATIONS.md).
1074
+
1075
+
1076
+ ## Structural editing and optional adapters in 0.1.0a6
1077
+
1078
+ Use `read_symbol` AST hashes for guarded class/method replacements; use
1079
+ `read_imports`, `ensure_import`, and `remove_import` for structural imports.
1080
+ `move_file`, `move_range`, and `move_symbol` support explicit refactoring plans
1081
+ with shared checks and rollback. Imports and callers are not inferred.
1082
+ See [scope and examples](docs/NEXT_RELEASE.md).
1083
+
1084
+ Optional [ChatGPT history retrieval](docs/CHATGPT_INTEGRATION.md) uses an
1085
+ owner-configured authenticated MCP server (`patchshuttle[mcp]`).
1086
+ Optional [Django migrations](docs/DJANGO_MIGRATIONS.md) provide reviewed plans,
1087
+ locks and recovery journals (`patchshuttle[django]`). A live ChatGPT deployment
1088
+ requires the owner's HTTPS endpoint and OAuth configuration.
1089
+
1090
+
1091
+ ## AI workflow in a7 development
1092
+
1093
+ Start with the installed capabilities/schema and the workspace handoff. The
1094
+ [AI guide](src/patchshuttle/resources/AI_GUIDE.md) contains the operation decision
1095
+ table, guard sources, refactoring checklist and recovery boundaries. Existing
1096
+ Python declaration edits should normally use `read_symbol` then `replace_symbol`;
1097
+ request `include_template: true` for a guarded draft. Use `edit_imports` for a
1098
+ sequence of import edits. Keep audits separate from patches and review actual
1099
+ execution outcomes. The explicit `|2` in the method example preserves Python
1100
+ indentation after YAML parsing. Documentation examples use illustrative bodies
1101
+ and hashes; replace these with observed source and guards before submitting.
1102
+
1103
+ This section describes the development branch, not a published a7 release.
@@ -1,40 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: patchshuttle
3
- Version: 0.1.0a5
4
- Summary: Local, auditable patch workflows for AI-assisted software development
5
- License-Expression: MIT
6
- License-File: LICENSE
7
- Keywords: ai,automation,cli,development-tools,patch
8
- Classifier: Development Status :: 3 - Alpha
9
- Classifier: Environment :: Console
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Operating System :: OS Independent
12
- Classifier: Programming Language :: Python :: 3 :: Only
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
- Classifier: Programming Language :: Python :: 3.14
18
- Classifier: Typing :: Typed
19
- Requires-Python: >=3.10
20
- Requires-Dist: black<27,>=24
21
- Requires-Dist: click<9,>=8.1
22
- Requires-Dist: filelock<4,>=3.30
23
- Requires-Dist: isort<7,>=5.13
24
- Requires-Dist: pydantic<3,>=2.6
25
- Requires-Dist: pyyaml<7,>=6
26
- Requires-Dist: ruff<1,>=0.16
27
- Requires-Dist: tomli<3,>=2; python_version < '3.11'
28
- Provides-Extra: dev
29
- Requires-Dist: build<2,>=1.2; extra == 'dev'
30
- Requires-Dist: django<7,>=5.2; extra == 'dev'
31
- Requires-Dist: pytest-cov<8,>=5; extra == 'dev'
32
- Requires-Dist: pytest<10,>=8; extra == 'dev'
33
- Requires-Dist: twine<7,>=6.1; extra == 'dev'
34
- Provides-Extra: html
35
- Requires-Dist: djlint<2,>=1.44; extra == 'html'
36
- Description-Content-Type: text/markdown
37
-
38
1
  # PatchShuttle
39
2
 
40
3
  PatchShuttle is intended to provide local, auditable patch workflows for
@@ -43,7 +6,8 @@ describes a small job, the user reviews and runs it locally, and PatchShuttle
43
6
  records the result for the next iteration.
44
7
 
45
8
  > [!IMPORTANT]
46
- > PatchShuttle is alpha software. Version `0.1.0a5` implements the complete
9
+ > PatchShuttle is alpha software. This branch prepares `0.1.0a7`; the latest
10
+ > recorded published version is `0.1.0a6`. It implements the complete
47
11
  > local v0.1 workflow. It executes bounded read-only
48
12
  > audits, approved patch transactions, and approved one-pass verification jobs
49
13
  > under a project lock. Patch jobs retain backups, run controlled checks, apply
@@ -100,7 +64,7 @@ After publication, install the exact alpha release from PyPI:
100
64
  ```bash
101
65
  python -m venv .venv
102
66
  python -m pip install --upgrade pip
103
- python -m pip install "patchshuttle==0.1.0a5"
67
+ python -m pip install "patchshuttle==0.1.0a6"
104
68
  ```
105
69
 
106
70
  For development from a local checkout:
@@ -113,7 +77,7 @@ To enable opt-in HTML template linting in an installed release, install the
113
77
  `html` extra:
114
78
 
115
79
  ```bash
116
- python -m pip install "patchshuttle[html]==0.1.0a5"
80
+ python -m pip install "patchshuttle[html]==0.1.0a6"
117
81
  ```
118
82
 
119
83
  For a local development checkout, install both development and HTML extras:
@@ -476,7 +440,8 @@ retained formatted-file states, final check results, audit observations, and a
476
440
  workspace comparison where applicable.
477
441
 
478
442
  An audit executes `tree`, `read`, literal `search`, bounded `search_context`,
479
- Python `read_symbol`, `find_files`, `file_info`, SHA-256 `hash`, canonical
443
+ Python `read_symbol`, `read_imports`, `python_structure`, `python_dependencies`,
444
+ `python_references`, `find_files`, `file_info`, SHA-256 `hash`, canonical
480
445
  `hash_range`, `git_status`, and `environment`. Traversal, file reads, match
481
446
  counts, and recorded output are bounded by local policy. Protected and ignored
482
447
  paths are skipped, source content appears only when explicitly requested by a
@@ -496,7 +461,8 @@ automatically undo those changes.
496
461
 
497
462
  A patch plan may contain `create_directory`, `create_file`, `replace_exact`,
498
463
  `replace_symbol`, `insert_before`, `insert_after`, `delete_exact`,
499
- `replace_range`, `delete_range`, `insert_at_line`, or `apply_diff` actions and
464
+ `replace_range`, `delete_range`, `insert_at_line`, `apply_diff`, `ensure_import`,
465
+ `remove_import`, `edit_imports`, `move_file`, `move_range`, or `move_symbol` actions and
500
466
  applies the final bytes already computed by the planner.
501
467
 
502
468
  The optional line-range actions are strict guarded operations for cases where
@@ -515,6 +481,13 @@ rejects missing, duplicate, syntactically invalid, or stale targets, and does
515
481
  not use fuzzy relocation or automatic line shifting. Exact desired symbol
516
482
  content is an idempotent `NO_CHANGE`.
517
483
 
484
+ The optional `expected_ast_sha256` guard uses the structural hash from
485
+ `read_symbol` to tolerate formatting changes. It ignores ordinary comments,
486
+ includes docstrings and decorators, and is scoped to the reported Python minor
487
+ version. Supply the text hash too when comments must be guarded. AST-guarded
488
+ replacements retain the name and kind and undergo compile and resolution
489
+ validation. See [next-release work](docs/NEXT_RELEASE.md) for current scope.
490
+
518
491
  For a supported plan, the implemented sequence is:
519
492
 
520
493
  1. validate the workspace lock file and acquire a non-blocking cross-platform
@@ -750,18 +723,19 @@ actions:
750
723
  symbol: Service.run
751
724
  ```
752
725
 
753
- If a change is needed, copy the canonical SHA-256 returned by `read_symbol`
754
- into a guarded patch action:
726
+ If a change is needed, use the audit-derived draft or copy `ast_sha256` from
727
+ `read_symbol` on the same Python minor version. Add its text `sha256` too when
728
+ comments must be guarded. The action below is illustrative:
755
729
 
756
730
  ```yaml
757
731
  actions:
758
732
  - replace_symbol:
759
733
  path: src/example/service.py
760
734
  symbol: Service.run
761
- expected_sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
762
- new_content: |
763
- def run(self, value):
764
- return value
735
+ expected_ast_sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
736
+ new_content: |2
737
+ def run(self, value):
738
+ return value
765
739
  ```
766
740
 
767
741
  The same audit can be constructed through the public Python adapter:
@@ -877,7 +851,7 @@ python -m coverage report --fail-under=100
877
851
  python -m build
878
852
  python -m twine check dist/*
879
853
  python tools/release_checks.py dist
880
- python tools/wheel_smoke.py dist/patchshuttle-0.1.0a5-py3-none-any.whl --version 0.1.0a5
854
+ python tools/wheel_smoke.py dist/patchshuttle-0.1.0a7-py3-none-any.whl --version 0.1.0a7
881
855
  ```
882
856
 
883
857
  The `0.1.0a2`, `0.1.0a3`, and `0.1.0a4` alpha releases each passed the
@@ -995,7 +969,9 @@ The implemented local cycle is:
995
969
  5. For a patch, run optional changed-HTML linting, controlled checks, the
996
970
  locally resolved isort/Black scopes, and final checks.
997
971
  6. Receive a timestamped log or generate a fresh handoff.
998
- 7. Review the log, upload it to the AI, and continue with the next job.
972
+ 7. Retrieve configured MCP history or provide the log through an authorized
973
+ channel. Current MCP history returns summaries/references, not full logs.
974
+ Review the observed result before continuing with the next job.
999
975
 
1000
976
  See [SPEC_V0_1.md](SPEC_V0_1.md) for the approved product contract and
1001
977
  [CHANGELOG.md](CHANGELOG.md) for release notes. PatchShuttle is designed for
@@ -1008,3 +984,76 @@ model, or project.
1008
984
  ## License
1009
985
 
1010
986
  PatchShuttle is licensed under the MIT License.
987
+
988
+
989
+ ## Guarded source transfers
990
+
991
+ - `move_range`: `path`, `start_line`, `end_line`, source `expected_content`
992
+ and/or `expected_sha256`, `destination`, `destination_line` and
993
+ `expected_destination_sha256`.
994
+ - `move_symbol`: `path`, `symbol`, `expected_ast_sha256`, `destination`,
995
+ `expected_destination_sha256` and optional `destination_class`.
996
+
997
+ Destination hashes cover simulated raw bytes; source range hashes cover
998
+ canonical LF/UTF-8 text. Destinations must exist or be created earlier in the
999
+ same job. Range insertion is before the 1-based destination line; EOF+1 appends.
1000
+ Symbol insertion appends at module scope or to the named class. Both files are
1001
+ validated and participate in the same backup/check/rollback transaction.
1002
+
1003
+ Same-file transfers, case-only path differences, duplicate destination names,
1004
+ closure extraction, class-bound super/private-name moves and multiline-string
1005
+ reindentation are rejected. Imports and callers are not automatically rewritten;
1006
+ include explicit edits and focused project checks. These actions move source
1007
+ content and leave the source file present. Whole-file moves are separate work.
1008
+
1009
+
1010
+ ## Whole-file transfers
1011
+
1012
+ `move_file` moves an existing supported text file to an absent destination.
1013
+ Supply `path`, `destination`, and `expected_sha256` of the original raw bytes.
1014
+ The transaction preserves original bytes and permissions, creates missing
1015
+ parent directories, and reports the source in `deleted_files`. Formatters and
1016
+ checks target the surviving destination. Add explicit import and caller edits
1017
+ when moving Python modules; imports are not automatically rewritten.
1018
+
1019
+ The source and destination must not have been touched earlier in the job.
1020
+ Subsequent edits to the destination are supported. Case-only moves, chains,
1021
+ swaps, binary files and reuse of the vacated path are currently rejected.
1022
+ A failed check restores the source and removes the destination. Manual rollback
1023
+ also verifies the applied state and refuses to overwrite a recreated source.
1024
+
1025
+
1026
+ ## Optional integrations
1027
+
1028
+ - `patchshuttle[mcp]`: authenticated, read-only history retrieval for ChatGPT. See [setup and deployment limits](docs/CHATGPT_INTEGRATION.md).
1029
+ - `patchshuttle[django]`: reviewed migration plans, explicit execution/reversal, and a separate recovery journal. See [Django workflow](docs/DJANGO_MIGRATIONS.md).
1030
+
1031
+
1032
+ ## Structural editing and optional adapters in 0.1.0a6
1033
+
1034
+ Use `read_symbol` AST hashes for guarded class/method replacements; use
1035
+ `read_imports`, `ensure_import`, and `remove_import` for structural imports.
1036
+ `move_file`, `move_range`, and `move_symbol` support explicit refactoring plans
1037
+ with shared checks and rollback. Imports and callers are not inferred.
1038
+ See [scope and examples](docs/NEXT_RELEASE.md).
1039
+
1040
+ Optional [ChatGPT history retrieval](docs/CHATGPT_INTEGRATION.md) uses an
1041
+ owner-configured authenticated MCP server (`patchshuttle[mcp]`).
1042
+ Optional [Django migrations](docs/DJANGO_MIGRATIONS.md) provide reviewed plans,
1043
+ locks and recovery journals (`patchshuttle[django]`). A live ChatGPT deployment
1044
+ requires the owner's HTTPS endpoint and OAuth configuration.
1045
+
1046
+
1047
+ ## AI workflow in a7 development
1048
+
1049
+ Start with the installed capabilities/schema and the workspace handoff. The
1050
+ [AI guide](src/patchshuttle/resources/AI_GUIDE.md) contains the operation decision
1051
+ table, guard sources, refactoring checklist and recovery boundaries. Existing
1052
+ Python declaration edits should normally use `read_symbol` then `replace_symbol`;
1053
+ request `include_template: true` for a guarded draft. Use `edit_imports` for a
1054
+ sequence of import edits. Keep audits separate from patches and review actual
1055
+ execution outcomes. The explicit `|2` in the method example preserves Python
1056
+ indentation after YAML parsing. Documentation examples use illustrative bodies
1057
+ and hashes; replace these with observed source and guards before submitting.
1058
+
1059
+ This section describes the development branch, not a published a7 release.
@@ -863,7 +863,9 @@ Fields:
863
863
 
864
864
  - `path`: required existing Python text file;
865
865
  - `symbol`: required dotted class, function, method, or nested-symbol name;
866
- - `expected_sha256`: required canonical hash returned by `read_symbol`;
866
+ - `expected_sha256`: optional canonical text hash returned by `read_symbol`;
867
+ - `expected_ast_sha256`: optional structural hash returned by `read_symbol`;
868
+ - at least one hash is required; when both are supplied both must match;
867
869
  - `new_content`: required complete replacement source.
868
870
 
869
871
  Planning parses the current sequential simulated file without importing it,
@@ -874,6 +876,15 @@ desired symbol content returns `NO_CHANGE`. Execution publishes the planner's
874
876
  final file bytes through the normal guarded transaction, backup, check,
875
877
  workspace-comparison, and rollback lifecycle.
876
878
 
879
+ With `expected_ast_sha256`, whitespace and ordinary comment differences do not
880
+ invalidate the guard. Hashes include AST fields but exclude source positions,
881
+ and are scoped to the `ast_python` major/minor version reported by the audit.
882
+ Docstrings, decorators and annotations remain part of the AST. The replacement
883
+ must be one declaration of the same name and kind, the final module must compile
884
+ without execution, and the qualified target must still resolve uniquely.
885
+ Use the text guard as well when comment changes must invalidate the job.
886
+ Old text-guarded jobs retain their normalized serialization and registry hash.
887
+
877
888
  ### 13.5 `insert_before`
878
889
 
879
890
  Fields:
@@ -2085,3 +2096,76 @@ unless this specification is explicitly revised:
2085
2096
  - logs are plain UTF-8 text with timestamped names and an AI handoff block;
2086
2097
  - implementation begins with tests for the contract before completing the
2087
2098
  execution engine.
2099
+
2100
+
2101
+ ## Guarded source transfers
2102
+
2103
+ - `move_range`: `path`, `start_line`, `end_line`, source `expected_content`
2104
+ and/or `expected_sha256`, `destination`, `destination_line` and
2105
+ `expected_destination_sha256`.
2106
+ - `move_symbol`: `path`, `symbol`, `expected_ast_sha256`, `destination`,
2107
+ `expected_destination_sha256` and optional `destination_class`.
2108
+
2109
+ Destination hashes cover simulated raw bytes; source range hashes cover
2110
+ canonical LF/UTF-8 text. Destinations must exist or be created earlier in the
2111
+ same job. Range insertion is before the 1-based destination line; EOF+1 appends.
2112
+ Symbol insertion appends at module scope or to the named class. Both files are
2113
+ validated and participate in the same backup/check/rollback transaction.
2114
+
2115
+ Same-file transfers, case-only path differences, duplicate destination names,
2116
+ closure extraction, class-bound super/private-name moves and multiline-string
2117
+ reindentation are rejected. Imports and callers are not automatically rewritten;
2118
+ include explicit edits and focused project checks. These actions move source
2119
+ content and leave the source file present. Whole-file moves are separate work.
2120
+
2121
+
2122
+ ## Whole-file transfers
2123
+
2124
+ `move_file` moves an existing supported text file to an absent destination.
2125
+ Supply `path`, `destination`, and `expected_sha256` of the original raw bytes.
2126
+ The transaction preserves original bytes and permissions, creates missing
2127
+ parent directories, and reports the source in `deleted_files`. Formatters and
2128
+ checks target the surviving destination. Add explicit import and caller edits
2129
+ when moving Python modules; imports are not automatically rewritten.
2130
+
2131
+ The source and destination must not have been touched earlier in the job.
2132
+ Subsequent edits to the destination are supported. Case-only moves, chains,
2133
+ swaps, binary files and reuse of the vacated path are currently rejected.
2134
+ A failed check restores the source and removes the destination. Manual rollback
2135
+ also verifies the applied state and refuses to overwrite a recreated source.
2136
+
2137
+
2138
+ ## Structural import edits
2139
+
2140
+ `read_imports` accepts `path` and optional `max_bytes`. It reports top-level
2141
+ imports, `imports_ast_sha256`, and `ast_python`. The hash ignores formatting
2142
+ and comments, includes alias names and order, and is Python-minor-specific.
2143
+ Nested/conditional imports are not selected. Use the same interpreter for
2144
+ inspection and planning.
2145
+
2146
+ `ensure_import` and `remove_import` require `path`, `statement` and
2147
+ `expected_imports_ast_sha256`. The statement must be exactly one Python
2148
+ `import` or `from ... import ...` with one alias, optionally renamed. Relative
2149
+ imports are supported. Star requests and ambiguous duplicate matches fail.
2150
+ The guard is always checked, including for already-present/absent imports.
2151
+
2152
+ ```yaml
2153
+ - ensure_import:
2154
+ path: services/offers.py
2155
+ statement: from .pricing import calculate_price
2156
+ expected_imports_ast_sha256: "<64 hex digits from read_imports>"
2157
+ ```
2158
+
2159
+ An existing alias in a multi-alias import satisfies `ensure_import` without
2160
+ rewriting that line. Removal from a multi-alias import requires a separately
2161
+ reviewed rewrite. Removal retains comments, including comments inside a
2162
+ parenthesized import. Shared semicolon lines are rejected when editing them.
2163
+ Insertion preserves headers, the module docstring and future-import ordering.
2164
+ Existing bindings are checked conservatively across scopes; a conflicting
2165
+ local name may therefore require a separately reviewed edit too. The resulting
2166
+ module is compiled without importing or executing it.
2167
+
2168
+ Unrelated source remains intact before the normal scoped formatter phase.
2169
+ Neither action rewrites callers, proves runtime compatibility, nor infers
2170
+ imports when moving files or symbols. A sequence of import edits needs the
2171
+ hash of each simulated intermediate import set, or separate audited jobs.