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.
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.gitignore +1 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/CHANGELOG.md +87 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/PKG-INFO +336 -50
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/README.md +334 -49
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/SPEC_V0_1.md +299 -50
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/docs/RELEASE.md +82 -15
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/pyproject.toml +1 -0
- patchshuttle-0.1.0a5/src/patchshuttle/_ai_log.py +437 -0
- patchshuttle-0.1.0a5/src/patchshuttle/_architecture.py +334 -0
- patchshuttle-0.1.0a5/src/patchshuttle/_python_discovery.py +236 -0
- patchshuttle-0.1.0a5/src/patchshuttle/_python_structure.py +220 -0
- patchshuttle-0.1.0a5/src/patchshuttle/_python_symbols.py +58 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_version.py +1 -1
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/__init__.py +8 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/constructors.py +75 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/audit.py +190 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/checks/__init__.py +2 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/checks/constructors.py +5 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/checks/runner.py +90 -16
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/cli.py +117 -12
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/config.py +103 -7
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/context.py +22 -8
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/errors.py +9 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/execution.py +62 -39
- patchshuttle-0.1.0a5/src/patchshuttle/history/__init__.py +35 -0
- patchshuttle-0.1.0a5/src/patchshuttle/history/cli.py +123 -0
- patchshuttle-0.1.0a5/src/patchshuttle/history/models.py +194 -0
- patchshuttle-0.1.0a5/src/patchshuttle/history/records.py +382 -0
- patchshuttle-0.1.0a5/src/patchshuttle/history/storage.py +370 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/logging.py +232 -14
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/models.py +82 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/operations.py +41 -34
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/planner.py +214 -8
- patchshuttle-0.1.0a5/src/patchshuttle/project_python.py +79 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/AI_GUIDE.md +126 -4
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/PATCHSHUTTLE_PROTOCOL.md +138 -5
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/runner.py +1 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/selfdoc.py +48 -1
- patchshuttle-0.1.0a5/src/patchshuttle/warning_baseline.py +271 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/workspace.py +12 -0
- patchshuttle-0.1.0a5/tests/unit/history/test_history.py +373 -0
- patchshuttle-0.1.0a5/tests/unit/history/test_history_integration.py +225 -0
- patchshuttle-0.1.0a5/tests/unit/history/test_history_storage_edges.py +292 -0
- patchshuttle-0.1.0a5/tests/unit/test_a4_coverage_regressions.py +568 -0
- patchshuttle-0.1.0a5/tests/unit/test_ai_log.py +273 -0
- patchshuttle-0.1.0a5/tests/unit/test_architecture.py +350 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_attempt_logging.py +6 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_audit_verify_execution.py +64 -1
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_checks.py +80 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_cli.py +111 -3
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_config.py +5 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_context_and_constructors.py +92 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_logging.py +83 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_models.py +65 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_operational_execution.py +39 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_package.py +1 -1
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_planner.py +200 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_policy.py +30 -0
- patchshuttle-0.1.0a5/tests/unit/test_project_python.py +219 -0
- patchshuttle-0.1.0a5/tests/unit/test_python_discovery.py +307 -0
- patchshuttle-0.1.0a5/tests/unit/test_python_structure.py +194 -0
- patchshuttle-0.1.0a5/tests/unit/test_python_structure_action.py +151 -0
- patchshuttle-0.1.0a5/tests/unit/test_python_symbols.py +72 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_release_configuration.py +1 -1
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_selfdoc.py +11 -1
- patchshuttle-0.1.0a5/tests/unit/test_warning_baseline.py +126 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_workspace.py +2 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/release_checks.py +5 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/wheel_smoke.py +6 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.github/workflows/ci.yml +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.github/workflows/release.yml +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/.github/workflows/testpypi.yml +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/LICENSE +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/SECURITY.md +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/__init__.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_diff.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_html_lint.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_line_ranges.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/_process.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/create.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/actions/modify.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/backup.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/formatter_policy.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/formatters/__init__.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/formatters/runner.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/identifiers.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/inventory.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/linters/__init__.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/linters/runner.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/parser.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/policy.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/preflight.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/py.typed +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/registry.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/AUDIT-EXAMPLE.psh.yaml +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/PATCH-EXAMPLE.psh.yaml +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/resources/__init__.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/rollback.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/runtime_cache.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/src/patchshuttle/verification.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/e2e/test_cli_project_cycle.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/integration/test_workflows.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_actions.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_backup.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_change_transaction.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_check_transaction.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_diff.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_execution.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_formatters.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_formatting_transaction.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_guarded_ranges.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_html_lint.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_identifiers.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_inventory.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_inventory_transaction.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_manual_rollback.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_modify_actions.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_parser.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_phase19_cli.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_planner_ux.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_preflight.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_registry.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_rollback.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_runner.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tests/unit/test_runtime_cache.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/check_tag.py +0 -0
- {patchshuttle-0.1.0a3 → patchshuttle-0.1.0a5}/tools/index_smoke.py +0 -0
|
@@ -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.
|
|
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.
|
|
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`
|
|
57
|
-
>
|
|
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.
|
|
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.
|
|
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
|
|
303
|
-
internally generated import code. The
|
|
304
|
-
|
|
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,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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`,
|
|
454
|
-
|
|
455
|
-
Traversal, file reads, match
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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`,
|
|
468
|
-
`delete_range`, `insert_at_line`, or `apply_diff` actions and
|
|
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
|
|
536
|
-
checks and tests, plain validated
|
|
537
|
-
imports
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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,
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
|
|
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
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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.
|
|
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
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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
|
|