patchshuttle 0.1.0a5__tar.gz → 0.1.0a6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/.github/workflows/ci.yml +37 -5
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/CHANGELOG.md +24 -1
- patchshuttle-0.1.0a5/README.md → patchshuttle-0.1.0a6/PKG-INFO +113 -4
- patchshuttle-0.1.0a5/PKG-INFO → patchshuttle-0.1.0a6/README.md +69 -41
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/SPEC_V0_1.md +85 -1
- patchshuttle-0.1.0a6/docs/CHATGPT_INTEGRATION.md +54 -0
- patchshuttle-0.1.0a6/docs/DJANGO_MIGRATIONS.md +83 -0
- patchshuttle-0.1.0a6/docs/NEXT_RELEASE.md +170 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/docs/RELEASE.md +14 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/pyproject.toml +5 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_architecture.py +3 -1
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_python_symbols.py +13 -1
- patchshuttle-0.1.0a6/src/patchshuttle/_refactor.py +147 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_version.py +1 -1
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/actions/__init__.py +14 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/actions/constructors.py +108 -2
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/actions/create.py +6 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/actions/modify.py +12 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/audit.py +34 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/backup.py +28 -2
- patchshuttle-0.1.0a6/src/patchshuttle/django_app/__init__.py +1 -0
- patchshuttle-0.1.0a6/src/patchshuttle/django_app/management/__init__.py +1 -0
- patchshuttle-0.1.0a6/src/patchshuttle/django_app/management/commands/__init__.py +1 -0
- patchshuttle-0.1.0a6/src/patchshuttle/django_app/management/commands/patchshuttle_migrate.py +50 -0
- patchshuttle-0.1.0a6/src/patchshuttle/django_app/workflow.py +306 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/execution.py +2 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/formatters/runner.py +5 -1
- patchshuttle-0.1.0a6/src/patchshuttle/integrations/__init__.py +1 -0
- patchshuttle-0.1.0a6/src/patchshuttle/integrations/mcp_server.py +182 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/linters/runner.py +1 -1
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/logging.py +1 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/models.py +114 -4
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/planner.py +248 -187
- patchshuttle-0.1.0a6/src/patchshuttle/planning/__init__.py +1 -0
- patchshuttle-0.1.0a6/src/patchshuttle/planning/imports.py +184 -0
- patchshuttle-0.1.0a6/src/patchshuttle/planning/types.py +194 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/preflight.py +2 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/resources/AI_GUIDE.md +83 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/resources/PATCHSHUTTLE_PROTOCOL.md +79 -2
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/rollback.py +6 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/runner.py +18 -18
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/selfdoc.py +33 -3
- patchshuttle-0.1.0a6/tests/integrations/test_django_migrations.py +356 -0
- patchshuttle-0.1.0a6/tests/integrations/test_imports.py +186 -0
- patchshuttle-0.1.0a6/tests/integrations/test_mcp_server.py +242 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_cli.py +2 -2
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_formatters.py +4 -0
- patchshuttle-0.1.0a6/tests/unit/test_move_file.py +217 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_package.py +1 -1
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_preflight.py +2 -0
- patchshuttle-0.1.0a6/tests/unit/test_refactor.py +330 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_release_configuration.py +23 -3
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_selfdoc.py +1 -1
- patchshuttle-0.1.0a6/tests/unit/test_structural_guards.py +251 -0
- patchshuttle-0.1.0a6/tools/django_postgres_smoke.py +88 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/.github/workflows/release.yml +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/.github/workflows/testpypi.yml +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/.gitignore +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/LICENSE +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/SECURITY.md +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/__init__.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_ai_log.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_diff.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_html_lint.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_line_ranges.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_process.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_python_discovery.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/_python_structure.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/checks/__init__.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/checks/constructors.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/checks/runner.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/cli.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/config.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/context.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/errors.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/formatter_policy.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/formatters/__init__.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/history/__init__.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/history/cli.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/history/models.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/history/records.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/history/storage.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/identifiers.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/inventory.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/linters/__init__.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/operations.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/parser.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/policy.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/project_python.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/py.typed +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/registry.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/resources/AUDIT-EXAMPLE.psh.yaml +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/resources/PATCH-EXAMPLE.psh.yaml +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/resources/__init__.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/runtime_cache.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/verification.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/warning_baseline.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/src/patchshuttle/workspace.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/e2e/test_cli_project_cycle.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/integration/test_workflows.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/history/test_history.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/history/test_history_integration.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/history/test_history_storage_edges.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_a4_coverage_regressions.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_actions.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_ai_log.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_architecture.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_attempt_logging.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_audit_verify_execution.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_backup.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_change_transaction.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_check_transaction.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_checks.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_config.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_context_and_constructors.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_diff.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_execution.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_formatting_transaction.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_guarded_ranges.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_html_lint.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_identifiers.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_inventory.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_inventory_transaction.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_logging.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_manual_rollback.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_models.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_modify_actions.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_operational_execution.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_parser.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_phase19_cli.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_planner.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_planner_ux.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_policy.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_project_python.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_python_discovery.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_python_structure.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_python_structure_action.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_python_symbols.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_registry.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_rollback.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_runner.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_runtime_cache.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_warning_baseline.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tests/unit/test_workspace.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tools/check_tag.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tools/index_smoke.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tools/release_checks.py +0 -0
- {patchshuttle-0.1.0a5 → patchshuttle-0.1.0a6}/tools/wheel_smoke.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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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,30 @@
|
|
|
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
|
-
##
|
|
6
|
+
## 0.1.0a6 - 2026-10-21
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Guarded `read_imports`, `ensure_import` and `remove_import` with alias-aware AST matching, comment preservation and binding checks.
|
|
11
|
+
|
|
12
|
+
- Optional authenticated MCP history server for ChatGPT, scoped to one workspace and OAuth subject.
|
|
13
|
+
- Optional Django migration command with hashed plans, database locks, pre/post checks, explicit reversals and separate recovery journals.
|
|
14
|
+
- Guarded `move_range` and `move_symbol` source transfers between files,
|
|
15
|
+
including destination fingerprints, structural validation, Python constructors,
|
|
16
|
+
and shared transaction rollback.
|
|
17
|
+
- Guarded `move_file` with destination absence checks, byte/mode preservation,
|
|
18
|
+
explicit deleted-file reporting, and automatic and manual rollback.
|
|
19
|
+
|
|
20
|
+
- Optional AST guards for `replace_symbol`, with hashes and Python version
|
|
21
|
+
reported by `read_symbol`, replacement declaration validation and compile
|
|
22
|
+
validation. Existing text-only job serialization remains compatible.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Formatter child interpreters and Black preflight explicitly enable Python
|
|
27
|
+
UTF-8 mode, including under isolated execution.
|
|
28
|
+
|
|
29
|
+
See `docs/NEXT_RELEASE.md` for implemented scope and remaining release work.
|
|
7
30
|
|
|
8
31
|
## 0.1.0a5 - 2026-08-28
|
|
9
32
|
|
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: patchshuttle
|
|
3
|
+
Version: 0.1.0a6
|
|
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,7 @@ 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.
|
|
53
|
+
> PatchShuttle is alpha software. Version `0.1.0a6` implements the complete
|
|
10
54
|
> local v0.1 workflow. It executes bounded read-only
|
|
11
55
|
> audits, approved patch transactions, and approved one-pass verification jobs
|
|
12
56
|
> under a project lock. Patch jobs retain backups, run controlled checks, apply
|
|
@@ -63,7 +107,7 @@ After publication, install the exact alpha release from PyPI:
|
|
|
63
107
|
```bash
|
|
64
108
|
python -m venv .venv
|
|
65
109
|
python -m pip install --upgrade pip
|
|
66
|
-
python -m pip install "patchshuttle==0.1.
|
|
110
|
+
python -m pip install "patchshuttle==0.1.0a6"
|
|
67
111
|
```
|
|
68
112
|
|
|
69
113
|
For development from a local checkout:
|
|
@@ -76,7 +120,7 @@ To enable opt-in HTML template linting in an installed release, install the
|
|
|
76
120
|
`html` extra:
|
|
77
121
|
|
|
78
122
|
```bash
|
|
79
|
-
python -m pip install "patchshuttle[html]==0.1.
|
|
123
|
+
python -m pip install "patchshuttle[html]==0.1.0a6"
|
|
80
124
|
```
|
|
81
125
|
|
|
82
126
|
For a local development checkout, install both development and HTML extras:
|
|
@@ -478,6 +522,13 @@ rejects missing, duplicate, syntactically invalid, or stale targets, and does
|
|
|
478
522
|
not use fuzzy relocation or automatic line shifting. Exact desired symbol
|
|
479
523
|
content is an idempotent `NO_CHANGE`.
|
|
480
524
|
|
|
525
|
+
The optional `expected_ast_sha256` guard uses the structural hash from
|
|
526
|
+
`read_symbol` to tolerate formatting changes. It ignores ordinary comments,
|
|
527
|
+
includes docstrings and decorators, and is scoped to the reported Python minor
|
|
528
|
+
version. Supply the text hash too when comments must be guarded. AST-guarded
|
|
529
|
+
replacements retain the name and kind and undergo compile and resolution
|
|
530
|
+
validation. See [next-release work](docs/NEXT_RELEASE.md) for current scope.
|
|
531
|
+
|
|
481
532
|
For a supported plan, the implemented sequence is:
|
|
482
533
|
|
|
483
534
|
1. validate the workspace lock file and acquire a non-blocking cross-platform
|
|
@@ -840,7 +891,7 @@ python -m coverage report --fail-under=100
|
|
|
840
891
|
python -m build
|
|
841
892
|
python -m twine check dist/*
|
|
842
893
|
python tools/release_checks.py dist
|
|
843
|
-
python tools/wheel_smoke.py dist/patchshuttle-0.1.
|
|
894
|
+
python tools/wheel_smoke.py dist/patchshuttle-0.1.0a6-py3-none-any.whl --version 0.1.0a6
|
|
844
895
|
```
|
|
845
896
|
|
|
846
897
|
The `0.1.0a2`, `0.1.0a3`, and `0.1.0a4` alpha releases each passed the
|
|
@@ -971,3 +1022,61 @@ model, or project.
|
|
|
971
1022
|
## License
|
|
972
1023
|
|
|
973
1024
|
PatchShuttle is licensed under the MIT License.
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
## Guarded source transfers
|
|
1028
|
+
|
|
1029
|
+
- `move_range`: `path`, `start_line`, `end_line`, source `expected_content`
|
|
1030
|
+
and/or `expected_sha256`, `destination`, `destination_line` and
|
|
1031
|
+
`expected_destination_sha256`.
|
|
1032
|
+
- `move_symbol`: `path`, `symbol`, `expected_ast_sha256`, `destination`,
|
|
1033
|
+
`expected_destination_sha256` and optional `destination_class`.
|
|
1034
|
+
|
|
1035
|
+
Destination hashes cover simulated raw bytes; source range hashes cover
|
|
1036
|
+
canonical LF/UTF-8 text. Destinations must exist or be created earlier in the
|
|
1037
|
+
same job. Range insertion is before the 1-based destination line; EOF+1 appends.
|
|
1038
|
+
Symbol insertion appends at module scope or to the named class. Both files are
|
|
1039
|
+
validated and participate in the same backup/check/rollback transaction.
|
|
1040
|
+
|
|
1041
|
+
Same-file transfers, case-only path differences, duplicate destination names,
|
|
1042
|
+
closure extraction, class-bound super/private-name moves and multiline-string
|
|
1043
|
+
reindentation are rejected. Imports and callers are not automatically rewritten;
|
|
1044
|
+
include explicit edits and focused project checks. These actions move source
|
|
1045
|
+
content and leave the source file present. Whole-file moves are separate work.
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
## Whole-file transfers
|
|
1049
|
+
|
|
1050
|
+
`move_file` moves an existing supported text file to an absent destination.
|
|
1051
|
+
Supply `path`, `destination`, and `expected_sha256` of the original raw bytes.
|
|
1052
|
+
The transaction preserves original bytes and permissions, creates missing
|
|
1053
|
+
parent directories, and reports the source in `deleted_files`. Formatters and
|
|
1054
|
+
checks target the surviving destination. Add explicit import and caller edits
|
|
1055
|
+
when moving Python modules; imports are not automatically rewritten.
|
|
1056
|
+
|
|
1057
|
+
The source and destination must not have been touched earlier in the job.
|
|
1058
|
+
Subsequent edits to the destination are supported. Case-only moves, chains,
|
|
1059
|
+
swaps, binary files and reuse of the vacated path are currently rejected.
|
|
1060
|
+
A failed check restores the source and removes the destination. Manual rollback
|
|
1061
|
+
also verifies the applied state and refuses to overwrite a recreated source.
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
## Optional integrations
|
|
1065
|
+
|
|
1066
|
+
- `patchshuttle[mcp]`: authenticated, read-only history retrieval for ChatGPT. See [setup and deployment limits](docs/CHATGPT_INTEGRATION.md).
|
|
1067
|
+
- `patchshuttle[django]`: reviewed migration plans, explicit execution/reversal, and a separate recovery journal. See [Django workflow](docs/DJANGO_MIGRATIONS.md).
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
## Structural editing and optional adapters in 0.1.0a6
|
|
1071
|
+
|
|
1072
|
+
Use `read_symbol` AST hashes for guarded class/method replacements; use
|
|
1073
|
+
`read_imports`, `ensure_import`, and `remove_import` for structural imports.
|
|
1074
|
+
`move_file`, `move_range`, and `move_symbol` support explicit refactoring plans
|
|
1075
|
+
with shared checks and rollback. Imports and callers are not inferred.
|
|
1076
|
+
See [scope and examples](docs/NEXT_RELEASE.md).
|
|
1077
|
+
|
|
1078
|
+
Optional [ChatGPT history retrieval](docs/CHATGPT_INTEGRATION.md) uses an
|
|
1079
|
+
owner-configured authenticated MCP server (`patchshuttle[mcp]`).
|
|
1080
|
+
Optional [Django migrations](docs/DJANGO_MIGRATIONS.md) provide reviewed plans,
|
|
1081
|
+
locks and recovery journals (`patchshuttle[django]`). A live ChatGPT deployment
|
|
1082
|
+
requires the owner's HTTPS endpoint and OAuth configuration.
|
|
@@ -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,7 @@ 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.
|
|
9
|
+
> PatchShuttle is alpha software. Version `0.1.0a6` implements the complete
|
|
47
10
|
> local v0.1 workflow. It executes bounded read-only
|
|
48
11
|
> audits, approved patch transactions, and approved one-pass verification jobs
|
|
49
12
|
> under a project lock. Patch jobs retain backups, run controlled checks, apply
|
|
@@ -100,7 +63,7 @@ After publication, install the exact alpha release from PyPI:
|
|
|
100
63
|
```bash
|
|
101
64
|
python -m venv .venv
|
|
102
65
|
python -m pip install --upgrade pip
|
|
103
|
-
python -m pip install "patchshuttle==0.1.
|
|
66
|
+
python -m pip install "patchshuttle==0.1.0a6"
|
|
104
67
|
```
|
|
105
68
|
|
|
106
69
|
For development from a local checkout:
|
|
@@ -113,7 +76,7 @@ To enable opt-in HTML template linting in an installed release, install the
|
|
|
113
76
|
`html` extra:
|
|
114
77
|
|
|
115
78
|
```bash
|
|
116
|
-
python -m pip install "patchshuttle[html]==0.1.
|
|
79
|
+
python -m pip install "patchshuttle[html]==0.1.0a6"
|
|
117
80
|
```
|
|
118
81
|
|
|
119
82
|
For a local development checkout, install both development and HTML extras:
|
|
@@ -515,6 +478,13 @@ rejects missing, duplicate, syntactically invalid, or stale targets, and does
|
|
|
515
478
|
not use fuzzy relocation or automatic line shifting. Exact desired symbol
|
|
516
479
|
content is an idempotent `NO_CHANGE`.
|
|
517
480
|
|
|
481
|
+
The optional `expected_ast_sha256` guard uses the structural hash from
|
|
482
|
+
`read_symbol` to tolerate formatting changes. It ignores ordinary comments,
|
|
483
|
+
includes docstrings and decorators, and is scoped to the reported Python minor
|
|
484
|
+
version. Supply the text hash too when comments must be guarded. AST-guarded
|
|
485
|
+
replacements retain the name and kind and undergo compile and resolution
|
|
486
|
+
validation. See [next-release work](docs/NEXT_RELEASE.md) for current scope.
|
|
487
|
+
|
|
518
488
|
For a supported plan, the implemented sequence is:
|
|
519
489
|
|
|
520
490
|
1. validate the workspace lock file and acquire a non-blocking cross-platform
|
|
@@ -877,7 +847,7 @@ python -m coverage report --fail-under=100
|
|
|
877
847
|
python -m build
|
|
878
848
|
python -m twine check dist/*
|
|
879
849
|
python tools/release_checks.py dist
|
|
880
|
-
python tools/wheel_smoke.py dist/patchshuttle-0.1.
|
|
850
|
+
python tools/wheel_smoke.py dist/patchshuttle-0.1.0a6-py3-none-any.whl --version 0.1.0a6
|
|
881
851
|
```
|
|
882
852
|
|
|
883
853
|
The `0.1.0a2`, `0.1.0a3`, and `0.1.0a4` alpha releases each passed the
|
|
@@ -1008,3 +978,61 @@ model, or project.
|
|
|
1008
978
|
## License
|
|
1009
979
|
|
|
1010
980
|
PatchShuttle is licensed under the MIT License.
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
## Guarded source transfers
|
|
984
|
+
|
|
985
|
+
- `move_range`: `path`, `start_line`, `end_line`, source `expected_content`
|
|
986
|
+
and/or `expected_sha256`, `destination`, `destination_line` and
|
|
987
|
+
`expected_destination_sha256`.
|
|
988
|
+
- `move_symbol`: `path`, `symbol`, `expected_ast_sha256`, `destination`,
|
|
989
|
+
`expected_destination_sha256` and optional `destination_class`.
|
|
990
|
+
|
|
991
|
+
Destination hashes cover simulated raw bytes; source range hashes cover
|
|
992
|
+
canonical LF/UTF-8 text. Destinations must exist or be created earlier in the
|
|
993
|
+
same job. Range insertion is before the 1-based destination line; EOF+1 appends.
|
|
994
|
+
Symbol insertion appends at module scope or to the named class. Both files are
|
|
995
|
+
validated and participate in the same backup/check/rollback transaction.
|
|
996
|
+
|
|
997
|
+
Same-file transfers, case-only path differences, duplicate destination names,
|
|
998
|
+
closure extraction, class-bound super/private-name moves and multiline-string
|
|
999
|
+
reindentation are rejected. Imports and callers are not automatically rewritten;
|
|
1000
|
+
include explicit edits and focused project checks. These actions move source
|
|
1001
|
+
content and leave the source file present. Whole-file moves are separate work.
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
## Whole-file transfers
|
|
1005
|
+
|
|
1006
|
+
`move_file` moves an existing supported text file to an absent destination.
|
|
1007
|
+
Supply `path`, `destination`, and `expected_sha256` of the original raw bytes.
|
|
1008
|
+
The transaction preserves original bytes and permissions, creates missing
|
|
1009
|
+
parent directories, and reports the source in `deleted_files`. Formatters and
|
|
1010
|
+
checks target the surviving destination. Add explicit import and caller edits
|
|
1011
|
+
when moving Python modules; imports are not automatically rewritten.
|
|
1012
|
+
|
|
1013
|
+
The source and destination must not have been touched earlier in the job.
|
|
1014
|
+
Subsequent edits to the destination are supported. Case-only moves, chains,
|
|
1015
|
+
swaps, binary files and reuse of the vacated path are currently rejected.
|
|
1016
|
+
A failed check restores the source and removes the destination. Manual rollback
|
|
1017
|
+
also verifies the applied state and refuses to overwrite a recreated source.
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
## Optional integrations
|
|
1021
|
+
|
|
1022
|
+
- `patchshuttle[mcp]`: authenticated, read-only history retrieval for ChatGPT. See [setup and deployment limits](docs/CHATGPT_INTEGRATION.md).
|
|
1023
|
+
- `patchshuttle[django]`: reviewed migration plans, explicit execution/reversal, and a separate recovery journal. See [Django workflow](docs/DJANGO_MIGRATIONS.md).
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
## Structural editing and optional adapters in 0.1.0a6
|
|
1027
|
+
|
|
1028
|
+
Use `read_symbol` AST hashes for guarded class/method replacements; use
|
|
1029
|
+
`read_imports`, `ensure_import`, and `remove_import` for structural imports.
|
|
1030
|
+
`move_file`, `move_range`, and `move_symbol` support explicit refactoring plans
|
|
1031
|
+
with shared checks and rollback. Imports and callers are not inferred.
|
|
1032
|
+
See [scope and examples](docs/NEXT_RELEASE.md).
|
|
1033
|
+
|
|
1034
|
+
Optional [ChatGPT history retrieval](docs/CHATGPT_INTEGRATION.md) uses an
|
|
1035
|
+
owner-configured authenticated MCP server (`patchshuttle[mcp]`).
|
|
1036
|
+
Optional [Django migrations](docs/DJANGO_MIGRATIONS.md) provide reviewed plans,
|
|
1037
|
+
locks and recovery journals (`patchshuttle[django]`). A live ChatGPT deployment
|
|
1038
|
+
requires the owner's HTTPS endpoint and OAuth configuration.
|
|
@@ -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`:
|
|
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.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Read PatchShuttle results from ChatGPT
|
|
2
|
+
|
|
3
|
+
The optional MCP server exposes `history_list`, `history_latest` and
|
|
4
|
+
`history_show` for one explicitly selected workspace. It does not expose job
|
|
5
|
+
execution, database execution, raw log downloads or arbitrary filesystem reads.
|
|
6
|
+
Returned project text is untrusted data, not instructions.
|
|
7
|
+
|
|
8
|
+
Install `patchshuttle[mcp]`. Create an owner-controlled JSON configuration
|
|
9
|
+
outside job content, replacing every example value:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"issuer": "https://identity.example/",
|
|
14
|
+
"jwks_url": "https://identity.example/.well-known/jwks.json",
|
|
15
|
+
"resource_url": "https://history.example/mcp",
|
|
16
|
+
"subject": "your-exact-OAuth-subject",
|
|
17
|
+
"port": 8765
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Start with `patchshuttle-mcp --workspace /absolute/project --config /absolute/mcp.json`.
|
|
22
|
+
The process binds only `127.0.0.1`. Put it behind your HTTPS reverse proxy and
|
|
23
|
+
forward `/mcp` and `/.well-known/oauth-protected-resource/mcp` to port 8765.
|
|
24
|
+
Keep Authorization and Host headers intact. Do not expose an unauthenticated
|
|
25
|
+
alternate listener. The resource URL must end in the exact `/mcp` path.
|
|
26
|
+
|
|
27
|
+
Use an established OAuth authorization server with discovery, authorization-code
|
|
28
|
+
and PKCE support, and ChatGPT-compatible client registration (CIMD or DCR, or
|
|
29
|
+
configured static client credentials). Configure audience to the exact resource
|
|
30
|
+
URL and issue the `patchshuttle:history:read` scope to the configured subject.
|
|
31
|
+
The resource server accepts RS256/ES256 JWT access tokens only and validates
|
|
32
|
+
signature, expiry, issuer, audience, scope and the exact subject. It is not an
|
|
33
|
+
OAuth authorization server; opaque tokens/introspection are not implemented.
|
|
34
|
+
No access tokens or signing secrets belong in patch jobs or this configuration.
|
|
35
|
+
|
|
36
|
+
In ChatGPT's custom MCP connection settings, supply the HTTPS MCP URL and
|
|
37
|
+
configure OAuth. Install/select the resulting plugin and ask for the latest
|
|
38
|
+
PatchShuttle result. Availability and the actual linking flow depend on the
|
|
39
|
+
account/workspace. A live connection to the user's deployment is not established
|
|
40
|
+
by installing this Python package.
|
|
41
|
+
|
|
42
|
+
Local validation covers signed tokens, rejected claims/signatures, protected
|
|
43
|
+
resource discovery, HTTP authorization and history reads. It does not constitute
|
|
44
|
+
an end-to-end ChatGPT/OAuth-provider deployment test. The existing history
|
|
45
|
+
redaction remains best effort; authenticated access can reveal project details.
|
|
46
|
+
|
|
47
|
+
Official setup and authorization references:
|
|
48
|
+
|
|
49
|
+
- https://developers.openai.com/api/docs/guides/custom-mcp-server
|
|
50
|
+
- https://developers.openai.com/plugins/build/auth
|
|
51
|
+
- https://developers.openai.com/api/docs/guides/secure-mcp-tunnels
|
|
52
|
+
|
|
53
|
+
A Secure MCP Tunnel is another documented transport option, but this package's
|
|
54
|
+
initial configuration and local tests target an HTTPS reverse-proxy deployment.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Controlled Django migrations
|
|
2
|
+
|
|
3
|
+
Install `patchshuttle[django]` in the project's interpreter and add
|
|
4
|
+
`"patchshuttle.django_app"` to the owner's `INSTALLED_APPS`. The adapter has no
|
|
5
|
+
models and adds no migration of its own. Initialize a PatchShuttle workspace
|
|
6
|
+
and run the management command from the Django project as usual.
|
|
7
|
+
|
|
8
|
+
Preview an exact target (prefix matching is deliberately unsupported):
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python manage.py patchshuttle_migrate orders 0007_add_status --workspace . --database default
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The JSON plan contains ordered operations, direction, atomicity, declared
|
|
15
|
+
reversibility, migration-file checksums, current applied migrations and a database
|
|
16
|
+
fingerprint. Credentials come from Django settings and are not written into the
|
|
17
|
+
plan. Planning loads trusted Django project/migration code and reads database
|
|
18
|
+
state; the adapter does not apply schema operations during preview. Application
|
|
19
|
+
startup hooks remain project code and may have their own effects.
|
|
20
|
+
|
|
21
|
+
Review the plan and prepare a tested database backup/recovery procedure. Apply
|
|
22
|
+
using the exact returned SHA-256 and a non-secret reference to that procedure:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python manage.py patchshuttle_migrate orders 0007_add_status --workspace . --database default --apply --expected-plan <sha256> --recovery-reference <backup-or-recovery-record>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The reference records the owner's recovery preparation; PatchShuttle does not
|
|
29
|
+
create or verify the backup. A workspace lock and database lock are held while
|
|
30
|
+
rechecking the plan, running Django checks, applying migrations and verifying
|
|
31
|
+
both the exact applied set and unchanged migration sources. System checks run
|
|
32
|
+
before and after migration. Add domain-specific checks to Django's check
|
|
33
|
+
framework; model checks alone do not validate business data.
|
|
34
|
+
|
|
35
|
+
PostgreSQL uses a session-level advisory lock shared by cooperating adapter
|
|
36
|
+
processes in that database. Persistent SQLite uses a companion file lock.
|
|
37
|
+
Ordinary `manage.py migrate`, other applications and manual SQL do not observe
|
|
38
|
+
these locks. Use one canonical workspace/journal for each database's migration
|
|
39
|
+
operations. PostgreSQL connection poolers must preserve the same server session
|
|
40
|
+
for the operation; transaction-pooling mode is not supported.
|
|
41
|
+
|
|
42
|
+
Initial limits: PostgreSQL and persistent SQLite, autocommit connections,
|
|
43
|
+
transactional DDL and atomic migrations. In-memory/URI SQLite, other backends,
|
|
44
|
+
non-atomic migrations, unresolved graph conflicts and squashed replacements
|
|
45
|
+
are rejected. Migration checksums cover migration source files, not imported
|
|
46
|
+
helper implementations or arbitrary application dependencies. Keep the deployed
|
|
47
|
+
code immutable during planning/execution.
|
|
48
|
+
|
|
49
|
+
## Failure and recovery
|
|
50
|
+
|
|
51
|
+
Records are separate from filesystem patch backups, under
|
|
52
|
+
`patches/state/django-migrations`. An exclusive, flushed `*.start.json` precedes
|
|
53
|
+
migration execution; a separate `*.result.json` records success or failure.
|
|
54
|
+
An unfinished start record or `FAILED_REVIEW_REQUIRED` blocks another execution.
|
|
55
|
+
The adapter does not automatically retry an interrupted operation or reverse
|
|
56
|
+
already applied migrations. A multi-migration plan can partially succeed before
|
|
57
|
+
failure even if each individual migration is atomic. Python migration code may
|
|
58
|
+
also have external effects that database transactions cannot undo.
|
|
59
|
+
|
|
60
|
+
After independently inspecting/recovering the database, generate a fresh plan
|
|
61
|
+
and explicitly record owner reconciliation:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python manage.py patchshuttle_migrate orders 0007_add_status --workspace . --database default --reconcile <attempt-id> --expected-plan <fresh-sha256> --recovery-reference <verified-recovery-record>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This appends an acknowledgement. It does not repair, replay or prove the
|
|
68
|
+
correctness of database contents. Corrupt journals must be investigated;
|
|
69
|
+
do not delete them just to bypass an unresolved attempt.
|
|
70
|
+
|
|
71
|
+
## Explicit database reversal
|
|
72
|
+
|
|
73
|
+
Choose the earlier target (or `zero`) and preview with `--reverse`. Apply that
|
|
74
|
+
reviewed reverse plan with the same `--reverse`, hash and recovery reference.
|
|
75
|
+
An irreversible operation is rejected. Django's reversible flag does not imply
|
|
76
|
+
that data lost by forward operations can be restored. Reversal can remove data
|
|
77
|
+
and requires separate review and backup planning.
|
|
78
|
+
|
|
79
|
+
Filesystem `rollback` never reverses the database. The MCP history adapter
|
|
80
|
+
currently retrieves filesystem-job history; this separate migration journal
|
|
81
|
+
is not yet exposed through MCP.
|
|
82
|
+
|
|
83
|
+
Django's migration behavior: https://docs.djangoproject.com/en/6.0/topics/migrations/
|