patchshuttle 0.1.0a3__tar.gz → 0.1.0a5__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 (127) hide show
  1. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.gitignore +1 -0
  2. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/CHANGELOG.md +87 -0
  3. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/PKG-INFO +336 -50
  4. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/README.md +334 -49
  5. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/SPEC_V0_1.md +299 -50
  6. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/docs/RELEASE.md +82 -15
  7. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/pyproject.toml +1 -0
  8. patchshuttle-0.1.0a5/src/patchshuttle/_ai_log.py +437 -0
  9. patchshuttle-0.1.0a5/src/patchshuttle/_architecture.py +334 -0
  10. patchshuttle-0.1.0a5/src/patchshuttle/_python_discovery.py +236 -0
  11. patchshuttle-0.1.0a5/src/patchshuttle/_python_structure.py +220 -0
  12. patchshuttle-0.1.0a5/src/patchshuttle/_python_symbols.py +58 -0
  13. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_version.py +1 -1
  14. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/__init__.py +8 -0
  15. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/constructors.py +75 -0
  16. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/audit.py +190 -0
  17. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/checks/__init__.py +2 -0
  18. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/checks/constructors.py +5 -0
  19. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/checks/runner.py +90 -16
  20. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/cli.py +117 -12
  21. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/config.py +103 -7
  22. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/context.py +22 -8
  23. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/errors.py +9 -0
  24. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/execution.py +62 -39
  25. patchshuttle-0.1.0a5/src/patchshuttle/history/__init__.py +35 -0
  26. patchshuttle-0.1.0a5/src/patchshuttle/history/cli.py +123 -0
  27. patchshuttle-0.1.0a5/src/patchshuttle/history/models.py +194 -0
  28. patchshuttle-0.1.0a5/src/patchshuttle/history/records.py +382 -0
  29. patchshuttle-0.1.0a5/src/patchshuttle/history/storage.py +370 -0
  30. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/logging.py +232 -14
  31. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/models.py +82 -0
  32. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/operations.py +41 -34
  33. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/planner.py +214 -8
  34. patchshuttle-0.1.0a5/src/patchshuttle/project_python.py +79 -0
  35. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/AI_GUIDE.md +126 -4
  36. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/PATCHSHUTTLE_PROTOCOL.md +138 -5
  37. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/runner.py +1 -0
  38. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/selfdoc.py +48 -1
  39. patchshuttle-0.1.0a5/src/patchshuttle/warning_baseline.py +271 -0
  40. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/workspace.py +12 -0
  41. patchshuttle-0.1.0a5/tests/unit/history/test_history.py +373 -0
  42. patchshuttle-0.1.0a5/tests/unit/history/test_history_integration.py +225 -0
  43. patchshuttle-0.1.0a5/tests/unit/history/test_history_storage_edges.py +292 -0
  44. patchshuttle-0.1.0a5/tests/unit/test_a4_coverage_regressions.py +568 -0
  45. patchshuttle-0.1.0a5/tests/unit/test_ai_log.py +273 -0
  46. patchshuttle-0.1.0a5/tests/unit/test_architecture.py +350 -0
  47. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_attempt_logging.py +6 -0
  48. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_audit_verify_execution.py +64 -1
  49. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_checks.py +80 -0
  50. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_cli.py +111 -3
  51. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_config.py +5 -0
  52. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_context_and_constructors.py +92 -0
  53. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_logging.py +83 -0
  54. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_models.py +65 -0
  55. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_operational_execution.py +39 -0
  56. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_package.py +1 -1
  57. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_planner.py +200 -0
  58. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_policy.py +30 -0
  59. patchshuttle-0.1.0a5/tests/unit/test_project_python.py +219 -0
  60. patchshuttle-0.1.0a5/tests/unit/test_python_discovery.py +307 -0
  61. patchshuttle-0.1.0a5/tests/unit/test_python_structure.py +194 -0
  62. patchshuttle-0.1.0a5/tests/unit/test_python_structure_action.py +151 -0
  63. patchshuttle-0.1.0a5/tests/unit/test_python_symbols.py +72 -0
  64. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_release_configuration.py +1 -1
  65. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_selfdoc.py +11 -1
  66. patchshuttle-0.1.0a5/tests/unit/test_warning_baseline.py +126 -0
  67. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_workspace.py +2 -0
  68. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/release_checks.py +5 -0
  69. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/wheel_smoke.py +6 -0
  70. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.github/workflows/ci.yml +0 -0
  71. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.github/workflows/release.yml +0 -0
  72. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.github/workflows/testpypi.yml +0 -0
  73. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/LICENSE +0 -0
  74. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/SECURITY.md +0 -0
  75. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/__init__.py +0 -0
  76. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_diff.py +0 -0
  77. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_html_lint.py +0 -0
  78. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_line_ranges.py +0 -0
  79. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_process.py +0 -0
  80. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/create.py +0 -0
  81. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/modify.py +0 -0
  82. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/backup.py +0 -0
  83. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/formatter_policy.py +0 -0
  84. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/formatters/__init__.py +0 -0
  85. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/formatters/runner.py +0 -0
  86. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/identifiers.py +0 -0
  87. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/inventory.py +0 -0
  88. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/linters/__init__.py +0 -0
  89. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/linters/runner.py +0 -0
  90. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/parser.py +0 -0
  91. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/policy.py +0 -0
  92. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/preflight.py +0 -0
  93. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/py.typed +0 -0
  94. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/registry.py +0 -0
  95. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/AUDIT-EXAMPLE.psh.yaml +0 -0
  96. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/PATCH-EXAMPLE.psh.yaml +0 -0
  97. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/__init__.py +0 -0
  98. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/rollback.py +0 -0
  99. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/runtime_cache.py +0 -0
  100. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/verification.py +0 -0
  101. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/e2e/test_cli_project_cycle.py +0 -0
  102. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/integration/test_workflows.py +0 -0
  103. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_actions.py +0 -0
  104. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_backup.py +0 -0
  105. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_change_transaction.py +0 -0
  106. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_check_transaction.py +0 -0
  107. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_diff.py +0 -0
  108. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_execution.py +0 -0
  109. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_formatters.py +0 -0
  110. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_formatting_transaction.py +0 -0
  111. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_guarded_ranges.py +0 -0
  112. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_html_lint.py +0 -0
  113. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_identifiers.py +0 -0
  114. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_inventory.py +0 -0
  115. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_inventory_transaction.py +0 -0
  116. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_manual_rollback.py +0 -0
  117. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_modify_actions.py +0 -0
  118. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_parser.py +0 -0
  119. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_phase19_cli.py +0 -0
  120. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_planner_ux.py +0 -0
  121. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_preflight.py +0 -0
  122. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_registry.py +0 -0
  123. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_rollback.py +0 -0
  124. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_runner.py +0 -0
  125. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_runtime_cache.py +0 -0
  126. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/check_tag.py +0 -0
  127. {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/index_smoke.py +0 -0
@@ -26,5 +26,6 @@ Thumbs.db
26
26
  patches/applied/
27
27
  patches/backups/
28
28
  patches/failed/
29
+ patches/history/
29
30
  patches/logs/
30
31
  patches/state/
@@ -5,6 +5,93 @@ uses semantic versioning, including Python-compatible pre-release versions.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.1.0a5 - 2026-08-28
9
+
10
+ Fourth alpha release, focused on persistent structured execution history
11
+ for bounded external retrieval and long-term orchestration.
12
+
13
+ ### Added
14
+
15
+ - Schema-versioned, append-only structured job history records under
16
+ `patches/history/<job-id>/` separate declared intent from observed execution
17
+ facts, reference detailed logs, preserve successful, failed, declined, and
18
+ rollback attempts, and expose bounded read-only Python and CLI access. History
19
+ persistence is a best-effort secondary artifact and cannot change a job result
20
+ or trigger rollback.
21
+
22
+ ### Fixed
23
+
24
+ - Structured history models retain the canonical external `schema` JSON key
25
+ without shadowing Pydantic's legacy `BaseModel.schema` attribute or emitting a
26
+ warning during command startup.
27
+
28
+ ## 0.1.0a4 - 2026-08-27
29
+
30
+ Third alpha release, focused on bounded Python source discovery, compact
31
+ structure output, and owner-controlled architecture ratchets.
32
+
33
+ ### Added
34
+ - Owner-controlled Python architecture ratchets evaluate the planner's virtual
35
+ file state before execution. Defaults warn at 500 lines per module, 15 direct
36
+ Python files per package, five new Python files, or one new package; hard
37
+ limits are 1000, 25, 10, and three respectively. Stable bounded `ARCH` findings
38
+ appear in plans and logs, while unchanged or improving legacy violations remain
39
+ allowed.
40
+
41
+ - Read-only `python_structure` uses the standard-library AST to report a
42
+ bounded declaration map for one Python file or directory, including
43
+ imports, classes, functions, methods, parameters, decorators, bases,
44
+ source ranges, parse errors, and explicit file and symbol limit signals,
45
+ without importing or executing project code. Its opt-in `compact: true`
46
+ mode retains navigational symbol identities and ranges while omitting
47
+ detailed import and declaration metadata; full output remains the default.
48
+
49
+ - Read-only `search_context` returns bounded physical-line context around
50
+ literal matches, and `read_symbol` returns one exactly resolved
51
+ decorator-aware Python symbol with its physical range and canonical
52
+ LF/UTF-8 SHA-256.
53
+ - Guarded `replace_symbol` replaces one exactly resolved Python class,
54
+ function, method, or nested symbol using the hash returned by
55
+ `read_symbol` and the planner's sequential simulated file state.
56
+ - The built-in `ruff: {}` check runs fixed Ruff `F` rules without fixes
57
+ over only the Python files changed by the current patch.
58
+ - `logs --last --ai` and `logs --last --ai-json` render bounded
59
+ deterministic text and JSON views of the newest stored log without
60
+ changing it or executing project code.
61
+ - Run and early-failure logs include evidence-only
62
+ `PYTHON_DISCOVERY_EVALUATION` telemetry for the current job. It records
63
+ explicit Python paths, audit action/output volume, targeting styles, and
64
+ relevant failure signals without estimating tokens or deciding whether a
65
+ project symbol index should be implemented.
66
+ - Python discovery telemetry also aggregates reported file and search
67
+ matches, result-limit signals, and durations already present in bounded
68
+ audit results. Newly generated workspace policy covers conventional
69
+ `.venv`, `venv`, and `node_modules` trees at any depth; backup directories
70
+ remain owner-configured exclusions.
71
+ - Best-effort whole-log redaction recognizes common sensitive Python
72
+ assignment identifiers, including prefixed passwords, API and access keys,
73
+ secret and private keys, tokens, and one-line `SECRET_KEY_FALLBACKS`
74
+ collections. Safe annotations, same-name references, and loader calls remain
75
+ visible; the redaction guarantee remains `NONE`.
76
+ - Explicit project-local Django warning baselines classify known and new
77
+ W-class system-check warnings without changing the check exit status.
78
+ - Optional owner-controlled `[execution].python_executable` selects the
79
+ interpreter for Python-based project checks and `{python}` local
80
+ profiles. Jobs cannot set it, and omitted configuration retains the
81
+ existing interpreter behavior.
82
+
83
+ ### Changed
84
+
85
+ - AI handoffs use a deterministic capability hash, generated handoff trees
86
+ omit legacy backup and archive artifacts, and source audit output avoids
87
+ the confirmed token-name redaction false positive.
88
+
89
+ ### Fixed
90
+
91
+ - `replace_symbol` execution now enters the same guarded file-change
92
+ transaction as other planned text modifications, including revalidation,
93
+ backups, checks, rollback, logging, and workspace comparison.
94
+
8
95
  ## 0.1.0a3 - 2026-08-23
9
96
 
10
97
  Second alpha release, focused on AI-facing planning diagnostics, legacy-project
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: patchshuttle
3
- Version: 0.1.0a3
3
+ Version: 0.1.0a5
4
4
  Summary: Local, auditable patch workflows for AI-assisted software development
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -23,6 +23,7 @@ Requires-Dist: filelock<4,>=3.30
23
23
  Requires-Dist: isort<7,>=5.13
24
24
  Requires-Dist: pydantic<3,>=2.6
25
25
  Requires-Dist: pyyaml<7,>=6
26
+ Requires-Dist: ruff<1,>=0.16
26
27
  Requires-Dist: tomli<3,>=2; python_version < '3.11'
27
28
  Provides-Extra: dev
28
29
  Requires-Dist: build<2,>=1.2; extra == 'dev'
@@ -42,7 +43,7 @@ describes a small job, the user reviews and runs it locally, and PatchShuttle
42
43
  records the result for the next iteration.
43
44
 
44
45
  > [!IMPORTANT]
45
- > PatchShuttle is alpha software. Version `0.1.0a3` implements the complete
46
+ > PatchShuttle is alpha software. Version `0.1.0a5` implements the complete
46
47
  > local v0.1 workflow. It executes bounded read-only
47
48
  > audits, approved patch transactions, and approved one-pass verification jobs
48
49
  > under a project lock. Patch jobs retain backups, run controlled checks, apply
@@ -53,8 +54,9 @@ records the result for the next iteration.
53
54
  > Trusted Publishing workflows are implemented. Every versioned release must
54
55
  > pass its own local qualification, GitHub-hosted Windows/Ubuntu matrix,
55
56
  > TestPyPI installation, production publication, and post-release smoke gates.
56
- > The immutable `0.1.0a2` evidence and documented ChatGPT end-to-end workflow
57
- > are retained in [docs/RELEASE.md](docs/RELEASE.md).
57
+ > The immutable `0.1.0a2`, `0.1.0a3`, and `0.1.0a4` qualification
58
+ > evidence, plus the separately scoped `0.1.0a2` ChatGPT end-to-end
59
+ > workflow, are retained in [docs/RELEASE.md](docs/RELEASE.md).
58
60
 
59
61
  > [!NOTE]
60
62
  > Compared with `0.1.0a2`, version `0.1.0a3` adds AI-facing planner diagnostics,
@@ -64,6 +66,22 @@ records the result for the next iteration.
64
66
  > cleanup, and guarded physical-line range actions with a canonical read-only
65
67
  > range hash.
66
68
 
69
+ > [!NOTE]
70
+ > Version `0.1.0a4` adds `search_context`, `read_symbol`, guarded
71
+ > `replace_symbol`, the fixed Ruff F-only check, compact AI log views, Django
72
+ > warning baselines, owner-controlled project-check Python selection,
73
+ > evidence-only Python discovery telemetry, expanded redaction, bounded
74
+ > declaration-only `python_structure` discovery with compact output, and
75
+ > owner-controlled architecture ratchets. These features are included in the
76
+ > immutable `0.1.0a4` PyPI artifacts.
77
+
78
+ > [!NOTE]
79
+ > Version `0.1.0a5` adds schema-versioned append-only structured job
80
+ > history, facts-first records, best-effort persistence, and bounded
81
+ > read-only Python and CLI retrieval. Detailed execution logs remain the
82
+ > diagnostic source; external orchestrators remain responsible for project
83
+ > memory, semantic retrieval, requirements, and user decisions.
84
+
67
85
  ## Design goals
68
86
 
69
87
  - Keep every change local and explicitly initiated by the user.
@@ -82,7 +100,7 @@ After publication, install the exact alpha release from PyPI:
82
100
  ```bash
83
101
  python -m venv .venv
84
102
  python -m pip install --upgrade pip
85
- python -m pip install "patchshuttle==0.1.0a3"
103
+ python -m pip install "patchshuttle==0.1.0a5"
86
104
  ```
87
105
 
88
106
  For development from a local checkout:
@@ -95,7 +113,7 @@ To enable opt-in HTML template linting in an installed release, install the
95
113
  `html` extra:
96
114
 
97
115
  ```bash
98
- python -m pip install "patchshuttle[html]==0.1.0a3"
116
+ python -m pip install "patchshuttle[html]==0.1.0a5"
99
117
  ```
100
118
 
101
119
  For a local development checkout, install both development and HTML extras:
@@ -158,6 +176,7 @@ initializing a workspace or reading protected generated files:
158
176
  patchshuttle capabilities
159
177
  patchshuttle schema
160
178
  patchshuttle explain replace_exact
179
+ patchshuttle explain replace_symbol
161
180
  patchshuttle explain replace_range
162
181
  patchshuttle explain hash_range
163
182
  patchshuttle explain apply_diff
@@ -299,9 +318,14 @@ checks:
299
318
  modules: [email_client.views, email_client.urls]
300
319
  ```
301
320
 
302
- PatchShuttle runs the current interpreter with `manage.py shell -c` and
303
- internally generated import code. The YAML accepts only bounded dotted module
304
- identifiers, not an arbitrary expression or shell command.
321
+ PatchShuttle runs the owner-selected project interpreter with
322
+ `manage.py shell -c` and internally generated import code. The optional local
323
+ `[execution] python_executable` value in `patches/patchshuttle.toml` may be an
324
+ absolute path or a path relative to the workspace root. When it is omitted,
325
+ PatchShuttle preserves the current `sys.executable` behavior. Jobs cannot set
326
+ or override the interpreter, and PatchShuttle does not auto-detect a virtual
327
+ environment. The YAML accepts only bounded dotted module identifiers, not an
328
+ arbitrary expression or shell command.
305
329
 
306
330
  Load a job from disk with the same validation contract:
307
331
 
@@ -329,10 +353,11 @@ patchshuttle plan patches/examples/PATCH-EXAMPLE.psh.yaml
329
353
  patchshuttle plan patches/examples/PATCH-EXAMPLE.psh.yaml --diff
330
354
  ```
331
355
 
332
- The command displays the normalized job hash, sequential action dispositions,
333
- files and directories that would be created or modified, requested checks,
334
- Python formatting scope, a per-file isort/Black decision matrix, optional HTML
335
- lint scope, successful quality preflight records, protected-path result, backup
356
+ The command displays the normalized job hash, effective project Python
357
+ interpreter when one is used, sequential action dispositions, files and
358
+ directories that would be created or modified, requested checks, Python
359
+ formatting scope, a per-file isort/Black decision matrix, optional HTML lint
360
+ scope, successful quality preflight records, protected-path result, backup
336
361
  destination template, rollback policy, and whether later execution will
337
362
  require confirmation.
338
363
  `--diff` also prints a bounded unified preview of the final resolved bytes
@@ -450,12 +475,18 @@ locations, the archived job copy, initial check results, formatter results,
450
475
  retained formatted-file states, final check results, audit observations, and a
451
476
  workspace comparison where applicable.
452
477
 
453
- An audit executes `tree`, `read`, literal `search`, `find_files`, `file_info`,
454
- SHA-256 `hash`, canonical `hash_range`, `git_status`, and `environment`.
455
- Traversal, file reads, match counts, and recorded output are bounded by local
456
- policy. Protected and ignored paths are skipped, source content appears only
457
- when explicitly requested by a `read` or `search` action, and a before/after
458
- inventory verifies that the audit did not modify the workspace.
478
+ An audit executes `tree`, `read`, literal `search`, bounded `search_context`,
479
+ Python `read_symbol`, `find_files`, `file_info`, SHA-256 `hash`, canonical
480
+ `hash_range`, `git_status`, and `environment`. Traversal, file reads, match
481
+ counts, and recorded output are bounded by local policy. Protected and ignored
482
+ paths are skipped, source content appears only when explicitly requested by a
483
+ content-reading audit action, and a before/after inventory verifies that the
484
+ audit did not modify the workspace.
485
+
486
+ `search_context` reports bounded physical-line windows around literal matches.
487
+ `read_symbol` parses Python source without importing it, resolves exactly one
488
+ class, function, method, or nested symbol, includes its decorators, and reports
489
+ the canonical source range and SHA-256 used by `replace_symbol`.
459
490
 
460
491
  A verify job runs its controlled checks once, does not create a backup or run
461
492
  formatters, and compares the workspace before and after. A successful check
@@ -464,9 +495,9 @@ project checks can have external effects, PatchShuttle reports but does not
464
495
  automatically undo those changes.
465
496
 
466
497
  A patch plan may contain `create_directory`, `create_file`, `replace_exact`,
467
- `insert_before`, `insert_after`, `delete_exact`, `replace_range`,
468
- `delete_range`, `insert_at_line`, or `apply_diff` actions and applies the final
469
- bytes already computed by the planner.
498
+ `replace_symbol`, `insert_before`, `insert_after`, `delete_exact`,
499
+ `replace_range`, `delete_range`, `insert_at_line`, or `apply_diff` actions and
500
+ applies the final bytes already computed by the planner.
470
501
 
471
502
  The optional line-range actions are strict guarded operations for cases where
472
503
  a current audit already established an exact physical range. Lines are 1-based
@@ -477,6 +508,13 @@ and SHA-256 over UTF-8 bytes. PatchShuttle never fuzzes, relocates, partially
477
508
  applies, or automatically shifts a stale range. Use an audit `hash_range` when
478
509
  a digest is more compact than repeating a large old block.
479
510
 
511
+ A `replace_symbol` action names a dotted Python symbol, supplies the canonical
512
+ SHA-256 returned by `read_symbol`, and provides complete replacement source.
513
+ Planning resolves the symbol against the current sequential simulated file,
514
+ rejects missing, duplicate, syntactically invalid, or stale targets, and does
515
+ not use fuzzy relocation or automatic line shifting. Exact desired symbol
516
+ content is an idempotent `NO_CHANGE`.
517
+
480
518
  For a supported plan, the implemented sequence is:
481
519
 
482
520
  1. validate the workspace lock file and acquire a non-blocking cross-platform
@@ -532,18 +570,21 @@ overwritten. Rollback validates retained originals before restoring them,
532
570
  refuses to follow symbolic links or remove foreign non-empty directories, and
533
571
  does not claim success when a tracked path cannot be restored.
534
572
 
535
- The internal check runner supports `compileall`, `pytest`, `unittest`, Django
536
- checks and tests, plain validated module imports, Django-aware validated module
537
- imports through `manage.py shell -c`, and locally configured profiles. It
538
- inherits the current process environment because project checks execute project
539
- code and PatchShuttle is not an operating-system sandbox. PatchShuttle overrides
540
- `PYTHONPYCACHEPREFIX` with a fresh temporary directory for each check and
541
- removes that directory afterward, keeping ordinary generated Python bytecode
542
- outside the workspace. A defensive cache ledger also handles `.pyc` files
543
- created inside newly added packages despite that override. Formatter order is
544
- fixed to isort then Black for protocol 1; local exact-path exclusions are
545
- resolved separately for each tool. Non-Python jobs skip formatting and do not
546
- repeat checks. Optional djLint uses a locally selected template profile,
573
+ The internal check runner supports the fixed `ruff: {}` F-only check,
574
+ `compileall`, `pytest`, `unittest`, Django checks and tests, plain validated
575
+ module imports, Django-aware validated module imports through
576
+ `manage.py shell -c`, and locally configured profiles. Ruff receives only the
577
+ immutable ordered Python scope derived from current non-no-change patch actions;
578
+ a job cannot supply paths, rules, fixes, or arbitrary Ruff arguments.
579
+ Project checks inherit the current process environment because they execute
580
+ project code and PatchShuttle is not an operating-system sandbox. PatchShuttle
581
+ overrides `PYTHONPYCACHEPREFIX` with a fresh temporary directory for each check
582
+ and removes that directory afterward, keeping ordinary generated Python
583
+ bytecode outside the workspace. A defensive cache ledger also handles `.pyc`
584
+ files created inside newly added packages despite that override. Formatter
585
+ order is fixed to isort then Black for protocol 1; local exact-path exclusions
586
+ are resolved separately for each tool. Non-Python jobs skip formatting and do
587
+ not repeat checks. Optional djLint uses a locally selected template profile,
547
588
  targets only changed `.html` files, never reformats them, and triggers the same
548
589
  rollback path on failure. It reads content through stdin from an isolated
549
590
  temporary configuration root, so project djLint configuration cannot weaken
@@ -592,9 +633,12 @@ Every job run log contains all standard sections in a fixed order, including
592
633
  `LINT_HTML`, using `NOT_APPLICABLE` where a stage did not run, and ends with
593
634
  `PATCHSHUTTLE_AI_HANDOFF`. Check and formatter output is bounded by local
594
635
  policy, as is HTML linter output. Common password, token, API-key,
595
- authorization-header, and private-key shapes are masked when redaction is
596
- enabled. Redaction is best-effort and is not a guarantee that a log contains no
597
- secrets; review a log before uploading it to any AI service.
636
+ authorization-header, private-key, and sensitive Python assignment shapes are
637
+ masked when redaction is enabled. Python assignment matching includes prefixed
638
+ passwords, API and access keys, secret and private keys, tokens, and one-line
639
+ `SECRET_KEY_FALLBACKS` collections while preserving loader calls such as
640
+ `config("SECRET_KEY")`. Redaction is best-effort and is not a guarantee that a
641
+ log contains no secrets; review a log before uploading it to any AI service.
598
642
 
599
643
  Early `VALIDATION_FAILED` and `PLAN_FAILED` logs use a smaller fixed attempt
600
644
  format with `SUMMARY` and `PATCHSHUTTLE_AI_HANDOFF` sections. They record no
@@ -602,16 +646,162 @@ project changes, backup, job archive, or registry update. An explicitly
602
646
  declined reviewed plan continues to use the full job log and exact failed-job
603
647
  archive with result `USER_DECLINED`.
604
648
 
605
- Find the latest upload-friendly log:
649
+ Django system-check warnings can use an explicit project-local baseline:
650
+
651
+ ```bash
652
+ patchshuttle warnings
653
+ patchshuttle warnings --add urls.W005
654
+ patchshuttle warnings --remove urls.W005
655
+ ```
656
+
657
+ Only explicit W-class IDs are accepted. A `django_check` retains its normal
658
+ exit code and full log output while adding `known_warnings`, `new_warnings`,
659
+ and complete new-warning details. If captured output is truncated,
660
+ classification is reported as incomplete. Existing workspaces without the
661
+ protected baseline file start with an empty baseline; `patchshuttle init`
662
+ creates the missing scaffold file without overwriting existing state.
663
+ Find or compactly render the latest upload-friendly log:
606
664
 
607
665
  ```bash
608
666
  patchshuttle logs --last
667
+ patchshuttle logs --last --ai
668
+ patchshuttle logs --last --ai-json
669
+ ```
670
+
671
+ The default prints the path. `--ai` prints a bounded deterministic text view,
672
+ while `--ai-json` prints the same selected data as compact JSON. These modes
673
+ read the already stored log, preserve it unchanged, and never execute project
674
+ code. They are mutually exclusive and retain useful audit outputs and failed
675
+ check diagnostics within explicit bounds.
676
+
677
+ Run and early validation/planning failure logs also expose
678
+ `PYTHON_DISCOVERY_EVALUATION`. The section records only current-job evidence:
679
+ explicit Python paths, executed audit action counts and bounded output size
680
+ before whole-log redaction,
681
+ declared text/symbol/line targeting, and relevant failure signals.
682
+ `index_assessment: NOT_EVALUATED` is intentional: the report neither estimates
683
+ AI token savings nor decides whether a project symbol index is needed.
684
+
685
+ ### Discover Python structure before targeted reads
686
+
687
+ The read-only `python_structure` audit action collects a bounded declaration
688
+ map before targeted reads. Start with the narrowest useful directory or one
689
+ `.py` file because a broad package can produce a large audit result.
690
+
691
+ A complete YAML audit job looks like this:
692
+
693
+ ```yaml
694
+ protocol: 1
695
+ project_id: PSH-0123456789ABCDEF
696
+ id: AUDIT-PYTHON-STRUCTURE-001
697
+ kind: audit
698
+ actions:
699
+ - python_structure:
700
+ path: src/example
701
+ max_files: 50
702
+ max_symbols: 400
703
+ compact: true
704
+ checks: []
705
+ ```
706
+
707
+ `path` defaults to `.`, `max_files` defaults to `300`, `max_symbols`
708
+ defaults to `2000`, and `compact` defaults to `false`. A file target must end
709
+ in `.py`. Directory traversal respects the workspace's protected and ignored
710
+ paths. The standard-library AST is used without importing or executing project
711
+ code. Use `compact: true` for a navigational overview and omit it when detailed
712
+ imports, parameters, decorators, and bases are required.
713
+
714
+ Compact output begins with the same collection counts, parse errors, and limit
715
+ signals as full output, followed by per-file records containing only counts and
716
+ navigational symbol metadata:
717
+
718
+ ```text
719
+ schema: patchshuttle.python_structure_collection.compact.v1
720
+ path: src/example
721
+ glob: *.py
722
+ files_considered: 1
723
+ files_parsed: 1
724
+ parse_errors: 0
725
+ imports_reported: 3
726
+ symbols_available: 2
727
+ symbols_reported: 2
728
+ file_limit_reached: false
729
+ symbol_limit_reached: false
730
+
731
+ schema: patchshuttle.python_structure.compact.v1
732
+ file: "src/example/service.py"
733
+ imports: 3
734
+ symbols: 2
735
+ symbol: {"kind":"class","lines":[10,42],"qualified_name":"Service"}
736
+ symbol: {"kind":"method","lines":[14,31],"qualified_name":"Service.run"}
737
+ ```
738
+
739
+ With `compact: false`, the existing
740
+ `patchshuttle.python_structure_collection.v1` and
741
+ `patchshuttle.python_structure.v1` schemas remain unchanged and additionally
742
+ include top-level import records, parameters, decorators, and bases.
743
+
744
+ Use the reported qualified name for a smaller follow-up audit:
745
+
746
+ ```yaml
747
+ actions:
748
+ - read_symbol:
749
+ path: src/example/service.py
750
+ symbol: Service.run
751
+ ```
752
+
753
+ If a change is needed, copy the canonical SHA-256 returned by `read_symbol`
754
+ into a guarded patch action:
755
+
756
+ ```yaml
757
+ actions:
758
+ - replace_symbol:
759
+ path: src/example/service.py
760
+ symbol: Service.run
761
+ expected_sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
762
+ new_content: |
763
+ def run(self, value):
764
+ return value
609
765
  ```
610
766
 
611
- This selects the newest recorded run, snapshot, handoff, or failure-attempt
612
- log. Commands that intentionally produce no artifact, including successful
613
- `validate`, successful `plan`, `version`, `capabilities`, `schema`, and
614
- `explain`, do not replace it.
767
+ The same audit can be constructed through the public Python adapter:
768
+
769
+ ```python
770
+ from patchshuttle import Job
771
+ from patchshuttle.actions import python_structure
772
+
773
+ job = Job(
774
+ protocol=1,
775
+ project_id="PSH-0123456789ABCDEF",
776
+ id="AUDIT-PYTHON-STRUCTURE-001",
777
+ kind="audit",
778
+ actions=[
779
+ python_structure(
780
+ "src/example", max_files=50, max_symbols=400, compact=True
781
+ )
782
+ ],
783
+ )
784
+ ```
785
+
786
+ Full `python_structure` output reports declarations, top-level imports,
787
+ parameters, decorators, bases, source ranges, parse errors, and explicit limit
788
+ signals. Compact output retains file and symbol counts, symbol kinds, qualified
789
+ names, ranges, parse errors, and limit signals. Both modes exclude source values,
790
+ calls, references, inferred types, caching, persistence, and automatic patch
791
+ scope.
792
+
793
+ The same section reports aggregate Python file/search matches, whether file
794
+ discovery or searches reached their declared result bounds, and total duration
795
+ of executed Python-targeted audit actions. Newly initialized workspaces ignore
796
+ and protect conventional `.venv`, `venv`, and `node_modules` trees at any
797
+ depth. Existing `patches/patchshuttle.toml` files are never rewritten by
798
+ `init`; owners must add nested environment patterns and project-specific
799
+ backup paths to `project.ignored_paths` when needed.
800
+
801
+ The command selects the newest recorded run, snapshot, handoff, or
802
+ failure-attempt log. Commands that intentionally produce no artifact,
803
+ including successful `validate`, successful `plan`, `version`,
804
+ `capabilities`, `schema`, and `explain`, do not replace it.
615
805
 
616
806
  Inspect all registered jobs or one job ID:
617
807
 
@@ -687,16 +877,112 @@ python -m coverage report --fail-under=100
687
877
  python -m build
688
878
  python -m twine check dist/*
689
879
  python tools/release_checks.py dist
690
- python tools/wheel_smoke.py dist/patchshuttle-0.1.0a3-py3-none-any.whl --version 0.1.0a3
880
+ python tools/wheel_smoke.py dist/patchshuttle-0.1.0a5-py3-none-any.whl --version 0.1.0a5
881
+ ```
882
+
883
+ The `0.1.0a2`, `0.1.0a3`, and `0.1.0a4` alpha releases each passed the
884
+ required Ubuntu and Windows compatibility matrix, TestPyPI qualification,
885
+ production PyPI Trusted Publishing, and a clean post-release installation.
886
+ Their exact release and
887
+ workflow links and artifact hashes, together with the separately scoped
888
+ `0.1.0a2` ChatGPT end-to-end evidence, are recorded in
889
+ [docs/RELEASE.md](docs/RELEASE.md). Follow that guide in order for every future
890
+ release and append separate immutable evidence after each gate completes.
891
+ Rerun CI after every release-candidate change.
892
+
893
+ ## Python architecture policy
894
+
895
+ PatchShuttle can stop an AI-generated patch from making an already crowded Python
896
+ codebase worse. The policy is local owner configuration, not a job option, and is
897
+ enabled by default for the fixed `modular-monolith` / `package-by-feature`
898
+ profile in `ratchet` mode.
899
+
900
+ ```toml
901
+ [architecture]
902
+ enabled = true
903
+ profile = "modular-monolith"
904
+ organization = "package-by-feature"
905
+ mode = "ratchet"
906
+ exclude = ["**/migrations/**", "**/generated/**"]
907
+ max_report_items = 50
908
+
909
+ [architecture.module]
910
+ warning_lines = 500
911
+ max_lines = 1000
912
+
913
+ [architecture.package]
914
+ warning_python_files = 15
915
+ max_python_files = 25
916
+
917
+ [architecture.patch]
918
+ warning_new_python_files = 5
919
+ max_new_python_files = 10
920
+ warning_new_packages = 1
921
+ max_new_packages = 3
922
+ ```
923
+
924
+ The planner evaluates its in-memory result before touching the workspace. A file
925
+ already above a limit may stay the same size or shrink; making it larger is a new
926
+ regression and is blocked at the hard limit. The same rule applies to direct
927
+ Python-file counts in touched directories. Patch-wide file and package budgets
928
+ catch the kind of broad root-level file growth that is difficult to review.
929
+
930
+ Plans and stored logs expose the profile, status, measured counts, bounded
931
+ findings, and whether the report was truncated. `ARCH001`/`ARCH002` cover module
932
+ size, `ARCH010`/`ARCH011` package size, and `ARCH020`/`ARCH021` patch growth.
933
+ Warnings remain visible but do not block execution; errors fail planning before
934
+ confirmation, backup, or project checks.
935
+
936
+ This is deliberately a structural guard. It does not infer feature boundaries,
937
+ build dependency or call graphs, detect cycles, judge semantic responsibility,
938
+ reorganize files, or change the patch scope selected by the AI.
939
+
940
+ ## Structured project history
941
+
942
+ PatchShuttle writes one compact JSON record for each recorded execution attempt to
943
+ `patches/history/<job-id>/<record-id>.json`. These records are intended for
944
+ long-term external ingestion and AI retrieval. They describe PatchShuttle's own
945
+ operations only; requirements, conversations, product decisions, stages, browser
946
+ tests, and semantic project memory belong to an external orchestrator.
947
+
948
+ History has a different purpose from the other outputs:
949
+
950
+ - execution logs contain detailed diagnostic evidence and command output;
951
+ - AI logs are bounded deterministic views derived from execution logs;
952
+ - history records contain compact schema-versioned facts plus references to the
953
+ detailed log and its derived AI-log view.
954
+
955
+ `job.title` and bounded, redacted `job.description` are stored below `declared` as
956
+ operator or AI-supplied intent. They are never presented as observed facts.
957
+ `observed` contains only trusted execution state: status, files, checks, failures,
958
+ rollback, warnings, and reliably targeted symbols. PatchShuttle does not infer
959
+ workarounds, semantic meaning, relationships, or affected symbols it cannot prove.
960
+
961
+ Read records without parsing human-readable logs:
962
+
963
+ ```powershell
964
+ patchshuttle history list --limit 20
965
+ patchshuttle history latest
966
+ patchshuttle history latest PATCH-EXAMPLE-001
967
+ patchshuttle history show PATCH-EXAMPLE-001/2026_08_27_120000_000001_abcdef01
691
968
  ```
692
969
 
693
- The `0.1.0a2` alpha release passed the required Ubuntu and Windows
694
- compatibility matrix, TestPyPI qualification, production PyPI Trusted
695
- Publishing, and a clean post-release installation. The exact release, workflow
696
- links, artifact hashes, and ChatGPT end-to-end evidence are recorded in
697
- [docs/RELEASE.md](docs/RELEASE.md). Follow that guide in order for future
698
- releases, including `0.1.0a3`, and append separate immutable evidence after
699
- every gate completes. Rerun CI after every release-candidate change.
970
+ `latest` and `show` return the full canonical JSON record. `list` returns bounded
971
+ JSON summaries, one per line. Python integrations can call
972
+ `list_history_records`, `latest_history_record`, and `read_history_record` from
973
+ `patchshuttle.history`. External systems may also ingest the per-attempt JSON files
974
+ directly.
975
+
976
+ History writing occurs after the required detailed log and registry update. It is
977
+ best-effort: a history storage failure is exposed through the Python result's
978
+ `history_warning`, but does not turn a completed job into a failure or trigger
979
+ rollback. Existing protocol-1 jobs require no changes. Existing workspaces may add
980
+ `patches/history/**` to `project.ignored_paths`; new default configurations already
981
+ exclude it from workspace inventories.
982
+
983
+ The v1 layout is append-only and uses exclusive file creation. It deliberately has
984
+ no database, JSONL index, semantic search, vector store, relationship graph, or
985
+ Farm-specific behavior.
700
986
 
701
987
  ## Manual workflow
702
988