Skip to content

Repository files navigation

ChangePilot

ChangePilot is a version-aware migration workbench for Python projects. It finds API usage that crosses a dependency upgrade boundary, traces the affected local call graph, stages conservative edits, and rejects patches that change code outside the declared rewrite set.

The repository contains two working surfaces:

  • An interactive web workbench for exploring the migration flow
  • A Python AST engine and command line interface for repository analysis

ChangePilot is an early engineering prototype. It is useful for inspecting a focused upgrade and testing migration contracts. It is not a replacement for a project test suite or a general Python refactoring platform.

The default interface now includes ChangePilot: Contract-Aware Enterprise API Migration, an end-to-end demonstration for the fictional company Aster Manufacturing GmbH. It compares two original local Supplier Service contracts, resolves ten typed contract changes, traces Python usage, rejects a deliberately over-broad patch, verifies a corrected minimal patch, generates a polling test, and exports deterministic JSON and Markdown evidence.

Independent research demonstration using fictional enterprise data. Not affiliated with or endorsed by SAP.

What it does

Given a source tree and a set of current and target dependency versions, ChangePilot performs four steps:

  1. It selects API changes whose version boundary is crossed by the upgrade
  2. It locates matching call sites with Python AST analysis
  3. It follows reverse local calls to estimate the review surface
  4. It applies only deterministic edits and verifies a closed patch contract

Rules that need context-sensitive restructuring stay in the review queue. For example, a method rename can be safe enough for an automatic edit while a call shape rewrite remains manual.

Enterprise demonstration

The Aster Manufacturing fixture models a Supplier Service upgrade from 1.4.0 to 2.0.0. It contains ten original contract changes:

  1. risk_score becomes risk_rating
  2. supplier_status becomes an enum
  3. page and size pagination becomes cursor pagination
  4. create_order becomes asynchronous operation creation
  5. preferred_supplier becomes is_preferred
  6. a deprecated supplier search endpoint is replaced
  7. the fictional tenant.scope OAuth scope becomes required
  8. contact_email becomes nullable
  9. REJECTED is replaced by DECLINED
  10. errors gain typed codes

The primary path is calculated by the engine and patch verifier:

  1. Load the local v1-to-v2 fixture
  2. Select the asynchronous order-creation rule
  3. Inspect direct, aliased, wrapped, unaffected, and unsupported dynamic sites
  4. Generate the over-broad candidate
  5. Observe rejection for changing procurement/unaffected.py
  6. Load the corrected patch
  7. Inspect the generated polling test
  8. Run all seven verification stages
  9. Export JSON or Markdown
  10. Reopen the JSON report and validate its content hash

See Enterprise demo, contract subset, and fixture data card.

The repository also includes a captioned 80-second demonstration video recorded from the working local application. Its narration-ready sequence is documented in the video script.

Try the workbench

npm ci
npm run dev

Open the local URL printed by the development server. The sample workspace includes Pydantic and NumPy migration findings, a line-level patch preview, and contract verification.

The enterprise demonstration is the default mode. Use the mode switch at the top of the page to reopen the original dependency workbench.

The browser workbench uses a lightweight source detector so it can run without a backend. The Python package is the reference implementation for AST-based analysis.

Use the Python engine

ChangePilot requires Python 3.11 or newer and has no runtime dependencies.

python -m venv .venv

# Linux and macOS
source .venv/bin/activate

# Windows PowerShell
.venv\Scripts\Activate.ps1

python -m pip install -e .

Scan the included example:

changepilot scan examples/release_service.py \
  --from pydantic=1.10.14 \
  --from numpy=1.26.4 \
  --to pydantic=2.7.4 \
  --to numpy=2.0.0

Expected output:

Scanned 1 files and 28 lines. Found 3 migration call sites.
examples/release_service.py:12:12 LOW BaseModel.parse_obj -> BaseModel.model_validate
examples/release_service.py:16:12 LOW BaseModel.dict -> BaseModel.model_dump
examples/release_service.py:20:37 LOW numpy.float_ -> numpy.float64

Preview the safe patch:

changepilot migrate examples/release_service.py \
  --from pydantic=1.10.14 \
  --from numpy=1.26.4 \
  --to pydantic=2.7.4 \
  --to numpy=2.0.0

Add --write only after reviewing the diff.

Run the enterprise rejection fixture:

changepilot enterprise-demo \
  --candidate overbroad \
  --output-dir results/enterprise-overbroad

This command intentionally exits with status 4 after the real verifier rejects the candidate.

Run, export, and reopen the corrected fixture:

changepilot enterprise-demo \
  --candidate minimal \
  --output-dir results/enterprise \
  --reopen

The command writes:

  • results/enterprise/enterprise-report.json
  • results/enterprise/enterprise-report.md

The fixture is immutable. Reset the browser demonstration with Reset fixture. For the CLI, rerun either command to regenerate evidence from the original checked-in files.

Architecture

flowchart LR
    A["Current and target versions"] --> B["Version path resolver"]
    C["Python source tree"] --> D["AST index"]
    B --> E["Applicable change rules"]
    D --> F["Call-site matcher"]
    E --> F
    F --> G["Reverse call impact graph"]
    G --> H["Safe patch set"]
    G --> I["Manual review queue"]
    H --> J["Closed patch contract"]
    J --> K["Verified diff"]
Loading

The core modules have narrow responsibilities:

Module Responsibility
catalog.py Stores API change records and selects rules for a version path
analyzer.py Builds AST indexes, matches call sites, and traces reverse callers
migrate.py Applies the subset of rules marked as deterministic
evaluation.py Verifies patch boundaries and entity state changes
benchmark.py Runs labeled positive and negative regression fixtures
cli.py Exposes scan, migrate, and benchmark commands
enterprise.py Loads the local contract subset, traces enterprise sites, verifies multi-file scope, and exports reports
src/engine/enterprise.ts Runs the same deterministic enterprise fixture and browser-safe contract checks in the workbench

See Architecture for the data contracts and failure boundaries.

Rule catalog

The starter catalog contains seven rules across Pydantic, NumPy, pandas, and Flask.

Library Change Action
Pydantic BaseModel.dict to BaseModel.model_dump Automatic
Pydantic BaseModel.json to BaseModel.model_dump_json Automatic
Pydantic BaseModel.parse_obj to BaseModel.model_validate Automatic
NumPy numpy.float_ to numpy.float64 Automatic
NumPy numpy.complex_ to numpy.complex128 Automatic
pandas DataFrame.append to pandas.concat Manual review
Flask Remove the JSON helper app keyword Manual review

Each rule records a version boundary, match target, replacement, risk level, confidence, edit policy, and upstream documentation URL. A rule is active only when the requested upgrade crosses its boundary.

The catalog is intentionally small. Adding hundreds of weak rules would make the interface look complete while reducing trust in the patch set. See Rule authoring for the acceptance checklist.

Patch contract

Automatic edits are evaluated under a closed patch boundary.

A patch passes when:

  • The migrated source parses as Python
  • All automatically fixable findings are cleared
  • Every changed line maps to an automatic rule
  • Lines with manual findings remain unchanged

The state evaluator also supports added, deleted, and updated entity assertions. Any state change that is not explained by an assertion causes verification to fail.

This outcome-based check is useful for testing migration runners and agent workflows. It does not execute untrusted source code.

The enterprise verifier adds allowed-file and allowed-range checks across a project candidate. It also verifies expected operation additions, deletions, and updates from the two contract snapshots. A changed file or line outside the declared scope rejects the entire candidate, even when every changed Python file parses.

Evaluation

Run the regression benchmark:

changepilot benchmark --output results/baseline.json

The checked-in baseline covers 11 labeled fixtures:

  • Five exact automatic patch cases
  • Two manual review cases
  • Four negative or boundary cases

The current fixture result is:

Metric Result
Detection precision 1.0000
Detection recall 1.0000
Detection F1 1.0000
Exact patch rate 1.0000
Contract pass rate 1.0000

These values describe only the small repository fixture set. They are regression signals, not evidence of broad performance on arbitrary projects.

Test suite

# Python unit and contract tests
python -m unittest discover -s python/tests -v

# Python lint and formatting checks
python -m ruff check python/src python/tests examples
python -m ruff format --check python/src python/tests examples

# Web build and rendered output tests
npm test

# TypeScript and React lint
npm run lint

The Python suite currently contains 39 tests covering:

  • Version boundary selection
  • Import alias resolution
  • Typed Pydantic receiver detection
  • NumPy namespace changes
  • pandas review routing
  • Reverse caller tracing
  • Syntax error handling
  • Safe rewrite idempotence
  • Closed patch rejection
  • Entity state diff assertions
  • Benchmark metric generation
  • Strict local contract parsing
  • Contract version boundaries
  • Aliased imports, wrappers, and multiline calls
  • Unsupported reflection and syntax-error reporting
  • Multi-file and line-range patch rejection
  • Added, deleted, and updated operation assertions
  • Deterministic report export and reopening
  • Malformed contracts, duplicate rules, conflicting rules, and rename collisions

GitHub Actions runs the Python and web suites independently.

Repository layout

app/                         Interactive workbench
src/engine/                  Browser analysis model
python/src/changepilot/      Python AST engine and CLI
python/tests/                Unit and contract tests
examples/                    Small original migration example
fixtures/enterprise/         Original Aster contracts, rules, source, and patch candidates
results/baseline.json        Reproducible fixture benchmark output
results/enterprise/          Reopened deterministic enterprise evidence
docs/                        Architecture and rule authoring notes
.github/workflows/           Continuous integration

Design decisions

Deterministic before generative

The first release does not call an LLM. Every automatic edit must be explainable by a catalog rule and testable with an exact contract. A future model-backed planner can propose candidates, but it should not weaken the verification boundary.

Version paths are explicit

A deprecated symbol is not automatically relevant. The current version must be below the recorded change boundary and the target version must reach or cross it.

Confidence is not permission

Confidence helps prioritize findings. The auto_fix field controls mutation authority. A high-confidence match can still require manual review when the replacement changes call structure.

Analysis does not execute the target project

The scanner parses source and inspects syntax trees. It does not import or run the analyzed repository. This keeps scans deterministic and avoids triggering project side effects.

Known limitations

  • The catalog is small and manually reviewed
  • Cross-file type and data flow inference are not implemented
  • The reverse call graph covers direct local function names
  • Method receiver inference is deliberately conservative
  • The browser detector is less precise than the Python AST engine
  • Safe rewrites operate at line granularity
  • Python syntax validation does not prove runtime compatibility
  • The enterprise parser supports a strict documented OpenAPI-like subset, not arbitrary OpenAPI documents
  • Enterprise impact tracing is file-local and bounded, not a complete interprocedural call graph
  • The browser uses a lighter structural detector while the Python module remains the AST reference implementation

Provenance and claims

This repository contains an independent implementation and original fixtures.

It does not include third-party papers, research datasets, benchmark traces, source archives, or copied project assets. The checked-in metrics are produced only from fixtures in this repository. The project does not claim reproduction or authorship of external results.

API migration facts are linked to the official upstream documentation embedded in each catalog rule. The rule catalog is engineering metadata, not a claim of ownership over the underlying libraries or their release notes.

The Aster Manufacturing company, Supplier Service, contracts, identifiers, source files, migration notes, and results are fictional and original to this repository. No SAP logos, APIs, UI assets, or proprietary data are included.

Contributing

Read CONTRIBUTING.md before proposing a new rule. Every automatic rule needs a version boundary, a positive fixture, a negative fixture, an idempotence test, and an upstream documentation link.

Security

ChangePilot does not execute scanned source code. Report security issues through the process in SECURITY.md.

License

ChangePilot is available under the MIT License.

About

Version-aware Python API migration analysis, conservative codemods, and closed-world verification

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages