Implementation Plan: Migrate to Rust-based api-simulator (PyO3 Rewrite)#651
Conversation
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit PR Review — Verdict: changes_requested. 2 blocking issues found (1 critical, 1 warning). See inline comments.
| returncode=proc.returncode, | ||
| stdout=proc.stdout, | ||
| stderr=proc.stderr, | ||
| stderr="", |
There was a problem hiding this comment.
[critical] tests: Indentation error: stderr="" at L314 is indented with only 8 spaces inside the SubprocessResult(...) call, but other keyword arguments use 12 spaces. This will cause a SyntaxError/IndentationError at parse time — the test file will fail to import.
There was a problem hiding this comment.
Investigated — this is intentional. Direct byte inspection of L314 shows 12 spaces of indentation (matching L312 returncode=, L313 stdout=, L315 termination=, L316 pid=0 — all 12 spaces). 'python3 -m py_compile' exits cleanly with no output, confirming zero syntax/indentation errors. The reviewer misread the diff hunk's relative indentation (8 spaces in unified diff context = 12 absolute spaces in the file). No code change needed.
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit review found 2 blocking issues. See inline comments for details.
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit review found 2 blocking issues (verdict: changes_requested). See inline comments on the diff for details.
…rkflows dtolnay/rust-toolchain@stable is required before uv sync/uv lock so maturin can build the Rust-based api-simulator wheel from source.
- pyproject.toml: add subdirectory = 'crates/api-simulator-py' to resolve maturin-built wheel from Cargo workspace - uv.lock: regenerated; pinned to post-Rust commit e12c6140 - test_quota_http.py: migrate register/register_sequence calls to PyResponseSpec; update header access to dict-style requests[0]['headers'] - test_session_classification_e2e.py: update import to FakeClaudeCLI, change fake_claude.run() to run([], None), drop proc.stderr -> ''
…tead of private _api_simulator_py import Addresses reviewer warning: direct import from api_simulator._api_simulator_py accesses a private Rust extension module. The public surface re-exports PyResponseSpec as MockResponseSpec via api_simulator.http, so tests depend on the stable interface.
c4e22cc to
6063f77
Compare
Summary
The
api-simulatordependency has been rewritten in Rust with PyO3 bindings (TalonT-Org/api-simulator#74). The Python package now lives atcrates/api-simulator-py/within a Cargo workspace, is built via maturin, and exposes a new API surface (PyResponseSpec,PyFakeClaudeCLI,PySessionResult). This plan migrates AutoSkillit's CI infrastructure, dependency declaration, and test code to the new API without changing any production source code.Requirements
CI Infrastructure (CI)
api-simulatordev dependency (tests.yml,patch-bump-integration.yml,version-bump.yml) must include a Rust stable toolchain setup step before dependency installation.uv sync --locked --extra devanduv lockcommands in each workflow.Dependency Configuration (DEP)
pyproject.tomlmust reference the Rust-based api-simulator package such thatuv sync --extra devinstalls the maturin-built PyO3 wheel.uv.lockmust be regenerated and pinned to a post-fix(quota-guard): fail-closed on unknown reset time, raise threshold to 90% #75 commit of api-simulator.Test Migration (TEST)
api_simulatormust resolve against the Rust-based package.test_quota_http.pymust be rewritten to usePyResponseSpecfor route registration,url()method calls, dict-based request inspection, anddelay_msinteger milliseconds.test_session_classification_e2e.pymust be rewritten to userun(args, env)returningPySessionResult,session_log_dir()method, anddelay_message(n, millis).test_session_classification_e2e.pymust pass against the Rust api-simulator.test_quota_http.pymust pass against the Rust api-simulator.Architecture Impact
Development Diagram
%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; subgraph Deps ["DEPENDENCY LAYER"] PYPROJ["● pyproject.toml<br/>━━━━━━━━━━<br/>hatchling build<br/>api-simulator: git+subdir<br/>crates/api-simulator-py"] UVLOCK["● uv.lock<br/>━━━━━━━━━━<br/>Regenerated; pinned to<br/>post-#75 Rust commit"] end subgraph CIPreflight ["CI: preflight job (● tests.yml)"] RUST1["● dtolnay/rust-toolchain@stable<br/>━━━━━━━━━━<br/>Rust toolchain (new)<br/>Required for maturin build"] UVLOCK_CHK["uv lock --check<br/>━━━━━━━━━━<br/>Lockfile gate<br/>(fails fast on drift)"] end subgraph CITest ["CI: test job (● tests.yml)"] RUST2["● dtolnay/rust-toolchain@stable<br/>━━━━━━━━━━<br/>Rust toolchain (new)<br/>Compiles maturin wheel"] UV_SYNC["uv sync --locked --extra dev<br/>━━━━━━━━━━<br/>Installs all dev deps<br/>Builds api-simulator wheel"] TASK_ALL["task test-all<br/>━━━━━━━━━━<br/>lint-imports → pytest -n 4"] end subgraph CIPatchBump ["CI: patch-bump (● patch-bump-integration.yml)"] RUST_PB["● dtolnay/rust-toolchain@stable<br/>━━━━━━━━━━<br/>Required before uv lock"] UV_LOCK_PB["uv lock<br/>━━━━━━━━━━<br/>Regenerates lockfile<br/>with Rust dep resolution"] end subgraph CIVersionBump ["CI: version-bump (● version-bump.yml)"] RUST_VB["● dtolnay/rust-toolchain@stable<br/>━━━━━━━━━━<br/>Required before uv lock<br/>(main + integration)"] UV_LOCK_VB["uv lock × 2<br/>━━━━━━━━━━<br/>main branch + integration<br/>branch lockfile regeneration"] end subgraph Quality ["CODE QUALITY GATES"] RUFF["ruff format + check<br/>━━━━━━━━━━<br/>auto-fix on commit"] IMP_LINT["import-linter<br/>━━━━━━━━━━<br/>L0/L1/L2/L3 layering<br/>7 forbidden-import contracts"] MYPY["mypy<br/>━━━━━━━━━━<br/>type checking (py311)"] end subgraph TestFW ["TEST FRAMEWORK"] PYTEST["pytest -n 4 --dist worksteal<br/>━━━━━━━━━━<br/>pytest-asyncio · pytest-xdist<br/>timeout=60s (signal)"] MOCK_HTTP["● mock_http_server fixture<br/>━━━━━━━━━━<br/>PyResponseSpec API<br/>dict headers · url() method"] FAKE_CLD["● fake_claude fixture<br/>━━━━━━━━━━<br/>PyFakeClaudeCLI<br/>run([], None) · exit_code()"] end PYPROJ --> UVLOCK UVLOCK --> RUST1 RUST1 --> UVLOCK_CHK UVLOCK --> RUST2 RUST2 --> UV_SYNC UV_SYNC --> TASK_ALL TASK_ALL --> IMP_LINT IMP_LINT --> PYTEST PYTEST --> MOCK_HTTP PYTEST --> FAKE_CLD TASK_ALL --> RUFF RUFF --> MYPY PYPROJ --> RUST_PB RUST_PB --> UV_LOCK_PB PYPROJ --> RUST_VB RUST_VB --> UV_LOCK_VB class PYPROJ,UVLOCK stateNode; class RUST1,RUST2,RUST_PB,RUST_VB newComponent; class UV_SYNC,UV_LOCK_PB,UV_LOCK_VB phase; class UVLOCK_CHK,RUFF,IMP_LINT,MYPY detector; class TASK_ALL handler; class PYTEST handler; class MOCK_HTTP,FAKE_CLD output;Module Dependency Diagram
%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 70, 'curve': 'basis'}}}%% graph TB classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; subgraph Config ["DEPENDENCY CONFIGURATION"] PYPROJ["● pyproject.toml<br/>━━━━━━━━━━<br/>api-simulator: git + subdirectory<br/>crates/api-simulator-py (Rust)"] UVLOCK["● uv.lock<br/>━━━━━━━━━━<br/>Pinned to post-#75 commit<br/>maturin wheel resolution"] end subgraph External ["EXTERNAL PACKAGE (Rust/PyO3)"] API_SIM["api-simulator<br/>━━━━━━━━━━<br/>TalonT-Org/api-simulator<br/>crates/api-simulator-py"] PY_RESP["api_simulator._api_simulator_py<br/>━━━━━━━━━━<br/>PyResponseSpec<br/>mock_http_server fixture"] FAKE_CLI["api_simulator.claude<br/>━━━━━━━━━━<br/>FakeClaudeCLI<br/>fake_claude fixture"] end subgraph TestL1 ["L1 TEST CONSUMERS (tests/execution/)"] QUOTA_HTTP["● test_quota_http.py<br/>━━━━━━━━━━<br/>imports PyResponseSpec<br/>7 test functions"] SESSION_E2E["● test_session_classification_e2e.py<br/>━━━━━━━━━━<br/>imports FakeClaudeCLI<br/>10 test methods"] end subgraph ProdL1 ["L1 PRODUCTION MODULE"] EXEC["execution/<br/>━━━━━━━━━━<br/>quota.py · session.py<br/>No api-simulator imports"] end PYPROJ --> UVLOCK UVLOCK --> API_SIM API_SIM --> PY_RESP API_SIM --> FAKE_CLI PY_RESP -->|"imports PyResponseSpec"| QUOTA_HTTP FAKE_CLI -->|"imports FakeClaudeCLI"| SESSION_E2E EXEC -.->|"tested by"| QUOTA_HTTP EXEC -.->|"tested by"| SESSION_E2E class PYPROJ,UVLOCK stateNode; class API_SIM integration; class PY_RESP,FAKE_CLI handler; class QUOTA_HTTP,SESSION_E2E output; class EXEC phase;Closes #643
Implementation Plan
Plan file:
/home/talon/projects/autoskillit-runs/impl-20260406-193330-615247/.autoskillit/temp/make-plan/migrate_rust_api_simulator_plan_2026-04-06_000000.md🤖 Generated with Claude Code via AutoSkillit
Token Usage Summary