deployproof 0.2.1__tar.gz → 1.0.0__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 (40) hide show
  1. {deployproof-0.2.1/src/deployproof.egg-info → deployproof-1.0.0}/PKG-INFO +68 -13
  2. {deployproof-0.2.1 → deployproof-1.0.0}/README.md +67 -12
  3. {deployproof-0.2.1 → deployproof-1.0.0}/pyproject.toml +1 -1
  4. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/__init__.py +1 -1
  5. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/cli.py +24 -12
  6. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/control_flow.py +7 -2
  7. deployproof-1.0.0/src/deployproof/coverage_plugin.py +14 -0
  8. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/dependencies.py +73 -6
  9. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/diff.py +87 -2
  10. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/mocks.py +6 -4
  11. deployproof-1.0.0/src/deployproof/mutator.py +1836 -0
  12. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/reporter.py +7 -3
  13. {deployproof-0.2.1 → deployproof-1.0.0/src/deployproof.egg-info}/PKG-INFO +68 -13
  14. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof.egg-info/SOURCES.txt +2 -0
  15. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_cli.py +68 -3
  16. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_dependencies_extractor.py +39 -0
  17. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_diff.py +63 -0
  18. deployproof-1.0.0/tests/test_import_graph_discovery.py +62 -0
  19. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_mutator.py +328 -8
  20. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_wsl.py +3 -1
  21. deployproof-0.2.1/src/deployproof/mutator.py +0 -757
  22. {deployproof-0.2.1 → deployproof-1.0.0}/LICENSE +0 -0
  23. {deployproof-0.2.1 → deployproof-1.0.0}/setup.cfg +0 -0
  24. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/secrets.py +0 -0
  25. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/symlinks.py +0 -0
  26. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof/wsl.py +0 -0
  27. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof.egg-info/dependency_links.txt +0 -0
  28. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof.egg-info/entry_points.txt +0 -0
  29. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof.egg-info/requires.txt +0 -0
  30. {deployproof-0.2.1 → deployproof-1.0.0}/src/deployproof.egg-info/top_level.txt +0 -0
  31. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_baseline_collection_error.py +0 -0
  32. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_cli_dependency_scan.py +0 -0
  33. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_control_flow.py +0 -0
  34. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_dependencies_pypi.py +0 -0
  35. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_edge_cases_validation.py +0 -0
  36. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_mocks.py +0 -0
  37. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_mutation_suites.py +0 -0
  38. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_secrets.py +0 -0
  39. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_stress_regressions.py +0 -0
  40. {deployproof-0.2.1 → deployproof-1.0.0}/tests/test_symlinks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deployproof
3
- Version: 0.2.1
3
+ Version: 1.0.0
4
4
  Summary: A deterministic AI-code deployability checker that catches what coverage misses.
5
5
  Author-email: SVS Praveen <svspraveens@gmail.com>
6
6
  License-Expression: MIT
@@ -32,10 +32,10 @@ Dynamic: license-file
32
32
 
33
33
  > Deterministic pre-push verification for AI-assisted codebases: AST mutation testing, credential scanning, sandbox-escape detection, mock-usage alerts, swallowed-exception checks, and dependency hallucination defense.
34
34
 
35
- [![PyPI version](https://img.shields.io/badge/pypi-v0.2.1-007ec6.svg)](https://pypi.org/project/deployproof/)
35
+ [![PyPI version](https://img.shields.io/badge/pypi-v1.0.0-007ec6.svg)](https://pypi.org/project/deployproof/)
36
36
  [![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-3776ab.svg)](https://pypi.org/project/deployproof/)
37
37
  [![CI](https://github.com/SVSPraveen/DeployProof/actions/workflows/ci.yml/badge.svg)](https://github.com/SVSPraveen/DeployProof/actions/workflows/ci.yml)
38
- [![Tests](https://img.shields.io/badge/tests-94%20passed-2ea44f.svg)](https://github.com/SVSPraveen/DeployProof)
38
+ [![Tests](https://img.shields.io/badge/tests-111%20passed-2ea44f.svg)](https://github.com/SVSPraveen/DeployProof)
39
39
  [![Stress Tests](https://img.shields.io/badge/stress%20tests-11%2F11%20passed-2ea44f.svg)](stress_fixtures/)
40
40
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
41
41
 
@@ -45,6 +45,8 @@ Dynamic: license-file
45
45
 
46
46
  AI-assisted development introduces subtle failure modes that standard linters and coverage tools miss: test suites with high line coverage but near-zero mutation scores, hardcoded credentials generated in passing, symlinks that deceive approval prompts into escaping the repository sandbox, silently swallowed exceptions, and package names hallucinated by LLMs that don't exist on PyPI. DeployProof catches these at the pre-push stage, before they reach CI or production.
47
47
 
48
+ > **Privacy & Security Guarantee**: DeployProof runs 100% locally on your machine. It makes zero outbound network calls, except for querying the official PyPI registry (JSON API) to verify that newly introduced dependencies exist and are not hallucinated. DeployProof sends no source code, telemetry, test results, or secret findings to any external server.
49
+
48
50
  ## Install
49
51
 
50
52
  ```bash
@@ -53,20 +55,70 @@ pip install deployproof
53
55
 
54
56
  Requires Python 3.10+.
55
57
 
56
- ## Quickstart
58
+ ## How to Use DeployProof
59
+
60
+ DeployProof provides two distinct modes of operation. We believe in total transparency about execution costs:
57
61
 
58
- Initialize in your repository:
62
+ ### 1. Diff-Scoped Pre-Push Gate (`deployproof check`) — *Primary Fast Workflow*
63
+ Evaluates **only the files modified in your active session or git diff** (1–3 files typically). Because only newly edited AST nodes are mutated, it executes in **2 to 5 seconds** in local developer loops, pre-commit hooks, and pre-push gates.
59
64
 
60
65
  ```bash
61
- deployproof init
66
+ # Fast daily check: verifies modified files in your current working tree / git diff (2-5s)
67
+ deployproof check
68
+
69
+ # Output structured JSON for automation or IDE tooling
70
+ deployproof check --json
71
+
72
+ # Enforce strict gates on newly introduced mocks or swallowed errors
73
+ deployproof check --strict-mocks --strict-error-handling
62
74
  ```
63
75
 
64
- Run all verification checks against changes in the current session (git diff):
76
+ ### 2. Full Repository Audit Mode (`deployproof check --full-repo`) — *Thorough Codebase Audits*
77
+ Evaluates **every tracked Python file across the entire repository**. Because mutation testing generates hundreds or thousands of mutants and runs the test suite against each one in isolated multi-worker sandboxes, full repository scans take real, honest compute time:
65
78
 
66
79
  ```bash
67
- deployproof check
80
+ # Run a full-repository audit across all tracked files
81
+ deployproof check --full-repo
82
+
83
+ # Customize the parallel worker process count (defaults to auto-detected CPU count capped at 8)
84
+ deployproof check --full-repo --workers 8
85
+ ```
86
+
87
+ #### Real-World Timing Expectations:
88
+ | Scan Mode | Target Scope | Typical Duration | Intended Use Case |
89
+ | :--- | :--- | :--- | :--- |
90
+ | **`deployproof check`** | Git Diff (1–3 modified files) | **2 – 5 seconds** | Local pre-commit, active AI IDE coding loops, pre-push sanity checks. |
91
+ | **`deployproof check --workers 8`** | Large Multi-File Diff (100+ mutants) | **1 – 3 minutes** | Large feature branch reviews, refactors. |
92
+ | **`deployproof check --full-repo`** | Small Repo (< 100 mutants) | **30s – 2 minutes** | Initial repo onboarding, weekly audits. |
93
+ | **`deployproof check --full-repo`** | Medium Repo (200–500 mutants) | **5 – 15 minutes** | Release validation, scheduled CI jobs. |
94
+ | **`deployproof check --full-repo`** | Heavy / Network Lib (`requests`, 800 mutants) | **60 – 85 minutes** | Deep occasional audit sweeps. |
95
+
96
+ ### Parallel Multi-Worker Sandboxing (`--workers N`)
97
+
98
+ DeployProof includes a built-in multi-process execution engine (`ProcessPoolExecutor`) for parallel mutation testing. You can supply `--workers <N>` to both diff-scoped checks and full-repo audits:
99
+
100
+ ```bash
101
+ # Parallelize a large uncommitted diff across 8 worker processes
102
+ deployproof check --workers 8
103
+
104
+ # Full repository audit distributed across 8 worker processes
105
+ deployproof check --full-repo --workers 8
68
106
  ```
69
107
 
108
+ #### How Parallel Sandboxing Works:
109
+ 1. **Snapshot Creation**: DeployProof takes an initial atomic snapshot of your project into a clean temporary directory.
110
+ 2. **PID-Keyed Sandboxes**: Each worker process receives its own dedicated filesystem sandbox (`worker_<PID>`), with an independent `pytest` cache (`--override-ini=cache_dir=...`) and separate temporary directory (`--basetemp=...`).
111
+ 3. **Zero Mutation Leaks**: Mutants are generated and executed inside individual worker sandboxes in parallel. The working tree is untouched, and signal handlers (`SIGINT`, `SIGTERM`, `SIGBREAK`) ensure sandboxes are cleanly purged upon completion or interruption.
112
+
113
+ #### Advantages & When to Use:
114
+ * **Large Diff / Full Repo Speedup**: Near-linear execution scaling across CPU cores for batches with 50+ mutants, cutting 30-minute sweeps down to 5–8 minutes.
115
+ * **Process & Cache Isolation**: Eliminates cross-test state leakage, shared database locking, and `.pytest_cache` collisions between concurrent workers.
116
+
117
+ #### Trade-offs & When NOT to Use:
118
+ * **Small Daily Diffs (1–3 files)**: Do NOT use `--workers` for small 2-line edits. Spawning isolated sandboxes and copying file trees incurs ~1–2 seconds of snapshot overhead. Sequential in-place mutation executes in **2–5 seconds** with zero snapshot overhead.
119
+ * **Disk Space & I/O Overhead**: Running $N$ workers copies the repository snapshot $N$ times into temporary storage ($N \times \text{repo size}$ in `tempfile.gettempdir()`). On disk-constrained environments, use fewer workers (e.g. `--workers 2` or `--workers 4`).
120
+ * **Subprocess / Port Collisions**: If your test suite binds to fixed network ports (e.g. localhost:8080) without dynamic port selection, parallel workers running tests concurrently may trigger port conflicts. Use isolated ports or run sequentially in such environments.
121
+
70
122
  Output includes a section for each check — symlink scan, secrets scan, dependency scan, mock detection, control flow analysis, and mutation score — with a pass/fail line at the bottom.
71
123
 
72
124
  ### Exit Codes:
@@ -173,6 +225,8 @@ Pre-check clean: 100% of tested basic mutations caught.
173
225
  | `deployproof check --files <paths...>` | Explicitly evaluate specific files (bypasses git diff). |
174
226
  | `deployproof check --threshold <float>` | Minimum mutation score percentage required to pass (default: `80.0`). |
175
227
  | `deployproof check --base <ref>` | Base git ref (branch/commit/tag) to diff against. |
228
+ | `deployproof check --full-repo` | Audit all tracked files across the entire repository root (respecting `.gitignore`), using isolated parallel workers. |
229
+ | `deployproof check --workers <int>` | Set the number of isolated parallel worker processes for `--full-repo` scans (default: auto-detected CPU count capped at 8). |
176
230
  | `deployproof check --wsl` | Delegate mutation testing to `mutmut` inside WSL (Windows only). |
177
231
 
178
232
  > **Note:** `deployproof check --wsl` (Windows only) is newer and less battle-tested than the core checks — [file an issue](https://github.com/SVSPraveen/DeployProof/issues) if you hit something.
@@ -189,7 +243,7 @@ deployproof check --json
189
243
 
190
244
  ```json
191
245
  {
192
- "version": "0.2.1",
246
+ "version": "1.0.0",
193
247
  "status": "passed",
194
248
  "summary": {
195
249
  "target_files_count": 1,
@@ -270,9 +324,9 @@ deployproof check --json
270
324
  - **Control Flow and Error Handling** — AST-based detector for bare `except:` without re-raise, silently swallowed broad exceptions (`except Exception:` that only `pass` or log/print without re-raising or returning error indicators), and dead/unreachable code following unconditional `return`, `raise`, `break`, or `continue`, with an optional `--strict-error-handling` hard gate.
271
325
 
272
326
  ## What This Doesn't Do
273
-
327
+
274
328
  - **Test-only diffs are not yet caught.** If a diff modifies or weakens assertions in a test file without changing the corresponding source file, DeployProof currently sees zero modified source lines and passes with 0 mutants evaluated. This is a known gap — see [INVESTIGATION_blastradius.md](INVESTIGATION_blastradius.md) for the reverse-mapping approach being evaluated to close it. Until this lands, DeployProof does not protect against test suites being weakened directly.
275
- - **Not a full-repo audit.** Checks are scoped to files changed in the current session (git diff). Files you haven't touched are not re-evaluated.
329
+ - **Diff-scoping vs Full Audits:** Standard `deployproof check` is intentionally scoped strictly to files modified in the active git diff (for 2–5s speed). To audit every file in the entire repository, explicitly pass the `--full-repo` flag.
276
330
  - **Python only.** Mutation testing and import extraction currently support Python files only. Other languages are not scanned.
277
331
  - **No auto-fix.** DeployProof reports findings; it does not modify your code, rewrite imports, or suggest patches.
278
332
  - **No IDE plugin yet.** There is no VS Code extension or JetBrains plugin. The CLI is the interface. IDE integration is on the roadmap.
@@ -289,8 +343,8 @@ Fixtures cover weak test suites, zero-test orphan modules, planted OpenAI/AWS cr
289
343
 
290
344
  ## Status & Roadmap
291
345
 
292
- - **Current (v0.2.1):** Diff-scoped AST mutation testing (with recursive `test/`/`tests/` discovery, AST column-offset snippet reconstruction, and `SIGINT`/`SIGTERM`/`SIGBREAK` signal-safe disk restoration), baseline test-collection failure isolation with distinct exit code `2`, entropy-driven value-based secrets scanner (including unquoted .env values and prefix validation), GhostApproval symlink sandbox-escape detector, PyPI dependency hallucination / slopsquatting scanner (with import-to-distribution translation, recursive `-r` requirements scanning, and dynamic import detection via `importlib` / `__import__`), mock-introduction detector (`--strict-mocks`), control-flow / swallowed-exception scanner (`--strict-error-handling`), 11/11 launch-day stress test suite, 94 unit tests, machine-readable `--json` output, and Tier 2 CI verification via GitHub Actions (mutmut).
293
- - **Next:** Multi-language mutation support and expanded ecosystem rule packs.
346
+ - **Current (v1.0.0):** Diff-scoped AST mutation testing (with recursive discovery and `SIGINT`/`SIGTERM`/`SIGBREAK` signal-safe disk restoration), **Full Repository Audit Mode (`--full-repo`)** with isolated parallel multi-worker sandboxes and AST import-graph test discovery, baseline test-collection failure isolation with distinct exit code `2`, entropy-driven value-based secrets scanner, GhostApproval symlink sandbox-escape detector, PyPI dependency hallucination / slopsquatting scanner (with import-to-distribution translation, recursive `-r` requirements scanning, and dynamic import detection via `importlib` / `__import__`), mock-introduction detector (`--strict-mocks`), control-flow / swallowed-exception scanner (`--strict-error-handling`), 11/11 launch-day stress test suite, **111 unit tests**, live unbuffered progress streaming, and machine-readable `--json` output.
347
+ - **Next:** Reverse test-to-source dependency mapping (see `FUTURE_SCOPE.md`), SARIF 2.1.0 PR annotations, and multi-language mutation rule packs.
294
348
 
295
349
  ## Contributing
296
350
 
@@ -303,3 +357,4 @@ MIT. See [LICENSE](LICENSE).
303
357
  ---
304
358
 
305
359
  *Created by [SVS Praveen](https://github.com/SVSPraveen) · [Portfolio](https://svspraveen.vercel.app/) · [LinkedIn](https://www.linkedin.com/in/svs-praveen-s/)*
360
+
@@ -2,10 +2,10 @@
2
2
 
3
3
  > Deterministic pre-push verification for AI-assisted codebases: AST mutation testing, credential scanning, sandbox-escape detection, mock-usage alerts, swallowed-exception checks, and dependency hallucination defense.
4
4
 
5
- [![PyPI version](https://img.shields.io/badge/pypi-v0.2.1-007ec6.svg)](https://pypi.org/project/deployproof/)
5
+ [![PyPI version](https://img.shields.io/badge/pypi-v1.0.0-007ec6.svg)](https://pypi.org/project/deployproof/)
6
6
  [![Python versions](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-3776ab.svg)](https://pypi.org/project/deployproof/)
7
7
  [![CI](https://github.com/SVSPraveen/DeployProof/actions/workflows/ci.yml/badge.svg)](https://github.com/SVSPraveen/DeployProof/actions/workflows/ci.yml)
8
- [![Tests](https://img.shields.io/badge/tests-94%20passed-2ea44f.svg)](https://github.com/SVSPraveen/DeployProof)
8
+ [![Tests](https://img.shields.io/badge/tests-111%20passed-2ea44f.svg)](https://github.com/SVSPraveen/DeployProof)
9
9
  [![Stress Tests](https://img.shields.io/badge/stress%20tests-11%2F11%20passed-2ea44f.svg)](stress_fixtures/)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
11
11
 
@@ -15,6 +15,8 @@
15
15
 
16
16
  AI-assisted development introduces subtle failure modes that standard linters and coverage tools miss: test suites with high line coverage but near-zero mutation scores, hardcoded credentials generated in passing, symlinks that deceive approval prompts into escaping the repository sandbox, silently swallowed exceptions, and package names hallucinated by LLMs that don't exist on PyPI. DeployProof catches these at the pre-push stage, before they reach CI or production.
17
17
 
18
+ > **Privacy & Security Guarantee**: DeployProof runs 100% locally on your machine. It makes zero outbound network calls, except for querying the official PyPI registry (JSON API) to verify that newly introduced dependencies exist and are not hallucinated. DeployProof sends no source code, telemetry, test results, or secret findings to any external server.
19
+
18
20
  ## Install
19
21
 
20
22
  ```bash
@@ -23,20 +25,70 @@ pip install deployproof
23
25
 
24
26
  Requires Python 3.10+.
25
27
 
26
- ## Quickstart
28
+ ## How to Use DeployProof
29
+
30
+ DeployProof provides two distinct modes of operation. We believe in total transparency about execution costs:
27
31
 
28
- Initialize in your repository:
32
+ ### 1. Diff-Scoped Pre-Push Gate (`deployproof check`) — *Primary Fast Workflow*
33
+ Evaluates **only the files modified in your active session or git diff** (1–3 files typically). Because only newly edited AST nodes are mutated, it executes in **2 to 5 seconds** in local developer loops, pre-commit hooks, and pre-push gates.
29
34
 
30
35
  ```bash
31
- deployproof init
36
+ # Fast daily check: verifies modified files in your current working tree / git diff (2-5s)
37
+ deployproof check
38
+
39
+ # Output structured JSON for automation or IDE tooling
40
+ deployproof check --json
41
+
42
+ # Enforce strict gates on newly introduced mocks or swallowed errors
43
+ deployproof check --strict-mocks --strict-error-handling
32
44
  ```
33
45
 
34
- Run all verification checks against changes in the current session (git diff):
46
+ ### 2. Full Repository Audit Mode (`deployproof check --full-repo`) — *Thorough Codebase Audits*
47
+ Evaluates **every tracked Python file across the entire repository**. Because mutation testing generates hundreds or thousands of mutants and runs the test suite against each one in isolated multi-worker sandboxes, full repository scans take real, honest compute time:
35
48
 
36
49
  ```bash
37
- deployproof check
50
+ # Run a full-repository audit across all tracked files
51
+ deployproof check --full-repo
52
+
53
+ # Customize the parallel worker process count (defaults to auto-detected CPU count capped at 8)
54
+ deployproof check --full-repo --workers 8
55
+ ```
56
+
57
+ #### Real-World Timing Expectations:
58
+ | Scan Mode | Target Scope | Typical Duration | Intended Use Case |
59
+ | :--- | :--- | :--- | :--- |
60
+ | **`deployproof check`** | Git Diff (1–3 modified files) | **2 – 5 seconds** | Local pre-commit, active AI IDE coding loops, pre-push sanity checks. |
61
+ | **`deployproof check --workers 8`** | Large Multi-File Diff (100+ mutants) | **1 – 3 minutes** | Large feature branch reviews, refactors. |
62
+ | **`deployproof check --full-repo`** | Small Repo (< 100 mutants) | **30s – 2 minutes** | Initial repo onboarding, weekly audits. |
63
+ | **`deployproof check --full-repo`** | Medium Repo (200–500 mutants) | **5 – 15 minutes** | Release validation, scheduled CI jobs. |
64
+ | **`deployproof check --full-repo`** | Heavy / Network Lib (`requests`, 800 mutants) | **60 – 85 minutes** | Deep occasional audit sweeps. |
65
+
66
+ ### Parallel Multi-Worker Sandboxing (`--workers N`)
67
+
68
+ DeployProof includes a built-in multi-process execution engine (`ProcessPoolExecutor`) for parallel mutation testing. You can supply `--workers <N>` to both diff-scoped checks and full-repo audits:
69
+
70
+ ```bash
71
+ # Parallelize a large uncommitted diff across 8 worker processes
72
+ deployproof check --workers 8
73
+
74
+ # Full repository audit distributed across 8 worker processes
75
+ deployproof check --full-repo --workers 8
38
76
  ```
39
77
 
78
+ #### How Parallel Sandboxing Works:
79
+ 1. **Snapshot Creation**: DeployProof takes an initial atomic snapshot of your project into a clean temporary directory.
80
+ 2. **PID-Keyed Sandboxes**: Each worker process receives its own dedicated filesystem sandbox (`worker_<PID>`), with an independent `pytest` cache (`--override-ini=cache_dir=...`) and separate temporary directory (`--basetemp=...`).
81
+ 3. **Zero Mutation Leaks**: Mutants are generated and executed inside individual worker sandboxes in parallel. The working tree is untouched, and signal handlers (`SIGINT`, `SIGTERM`, `SIGBREAK`) ensure sandboxes are cleanly purged upon completion or interruption.
82
+
83
+ #### Advantages & When to Use:
84
+ * **Large Diff / Full Repo Speedup**: Near-linear execution scaling across CPU cores for batches with 50+ mutants, cutting 30-minute sweeps down to 5–8 minutes.
85
+ * **Process & Cache Isolation**: Eliminates cross-test state leakage, shared database locking, and `.pytest_cache` collisions between concurrent workers.
86
+
87
+ #### Trade-offs & When NOT to Use:
88
+ * **Small Daily Diffs (1–3 files)**: Do NOT use `--workers` for small 2-line edits. Spawning isolated sandboxes and copying file trees incurs ~1–2 seconds of snapshot overhead. Sequential in-place mutation executes in **2–5 seconds** with zero snapshot overhead.
89
+ * **Disk Space & I/O Overhead**: Running $N$ workers copies the repository snapshot $N$ times into temporary storage ($N \times \text{repo size}$ in `tempfile.gettempdir()`). On disk-constrained environments, use fewer workers (e.g. `--workers 2` or `--workers 4`).
90
+ * **Subprocess / Port Collisions**: If your test suite binds to fixed network ports (e.g. localhost:8080) without dynamic port selection, parallel workers running tests concurrently may trigger port conflicts. Use isolated ports or run sequentially in such environments.
91
+
40
92
  Output includes a section for each check — symlink scan, secrets scan, dependency scan, mock detection, control flow analysis, and mutation score — with a pass/fail line at the bottom.
41
93
 
42
94
  ### Exit Codes:
@@ -143,6 +195,8 @@ Pre-check clean: 100% of tested basic mutations caught.
143
195
  | `deployproof check --files <paths...>` | Explicitly evaluate specific files (bypasses git diff). |
144
196
  | `deployproof check --threshold <float>` | Minimum mutation score percentage required to pass (default: `80.0`). |
145
197
  | `deployproof check --base <ref>` | Base git ref (branch/commit/tag) to diff against. |
198
+ | `deployproof check --full-repo` | Audit all tracked files across the entire repository root (respecting `.gitignore`), using isolated parallel workers. |
199
+ | `deployproof check --workers <int>` | Set the number of isolated parallel worker processes for `--full-repo` scans (default: auto-detected CPU count capped at 8). |
146
200
  | `deployproof check --wsl` | Delegate mutation testing to `mutmut` inside WSL (Windows only). |
147
201
 
148
202
  > **Note:** `deployproof check --wsl` (Windows only) is newer and less battle-tested than the core checks — [file an issue](https://github.com/SVSPraveen/DeployProof/issues) if you hit something.
@@ -159,7 +213,7 @@ deployproof check --json
159
213
 
160
214
  ```json
161
215
  {
162
- "version": "0.2.1",
216
+ "version": "1.0.0",
163
217
  "status": "passed",
164
218
  "summary": {
165
219
  "target_files_count": 1,
@@ -240,9 +294,9 @@ deployproof check --json
240
294
  - **Control Flow and Error Handling** — AST-based detector for bare `except:` without re-raise, silently swallowed broad exceptions (`except Exception:` that only `pass` or log/print without re-raising or returning error indicators), and dead/unreachable code following unconditional `return`, `raise`, `break`, or `continue`, with an optional `--strict-error-handling` hard gate.
241
295
 
242
296
  ## What This Doesn't Do
243
-
297
+
244
298
  - **Test-only diffs are not yet caught.** If a diff modifies or weakens assertions in a test file without changing the corresponding source file, DeployProof currently sees zero modified source lines and passes with 0 mutants evaluated. This is a known gap — see [INVESTIGATION_blastradius.md](INVESTIGATION_blastradius.md) for the reverse-mapping approach being evaluated to close it. Until this lands, DeployProof does not protect against test suites being weakened directly.
245
- - **Not a full-repo audit.** Checks are scoped to files changed in the current session (git diff). Files you haven't touched are not re-evaluated.
299
+ - **Diff-scoping vs Full Audits:** Standard `deployproof check` is intentionally scoped strictly to files modified in the active git diff (for 2–5s speed). To audit every file in the entire repository, explicitly pass the `--full-repo` flag.
246
300
  - **Python only.** Mutation testing and import extraction currently support Python files only. Other languages are not scanned.
247
301
  - **No auto-fix.** DeployProof reports findings; it does not modify your code, rewrite imports, or suggest patches.
248
302
  - **No IDE plugin yet.** There is no VS Code extension or JetBrains plugin. The CLI is the interface. IDE integration is on the roadmap.
@@ -259,8 +313,8 @@ Fixtures cover weak test suites, zero-test orphan modules, planted OpenAI/AWS cr
259
313
 
260
314
  ## Status & Roadmap
261
315
 
262
- - **Current (v0.2.1):** Diff-scoped AST mutation testing (with recursive `test/`/`tests/` discovery, AST column-offset snippet reconstruction, and `SIGINT`/`SIGTERM`/`SIGBREAK` signal-safe disk restoration), baseline test-collection failure isolation with distinct exit code `2`, entropy-driven value-based secrets scanner (including unquoted .env values and prefix validation), GhostApproval symlink sandbox-escape detector, PyPI dependency hallucination / slopsquatting scanner (with import-to-distribution translation, recursive `-r` requirements scanning, and dynamic import detection via `importlib` / `__import__`), mock-introduction detector (`--strict-mocks`), control-flow / swallowed-exception scanner (`--strict-error-handling`), 11/11 launch-day stress test suite, 94 unit tests, machine-readable `--json` output, and Tier 2 CI verification via GitHub Actions (mutmut).
263
- - **Next:** Multi-language mutation support and expanded ecosystem rule packs.
316
+ - **Current (v1.0.0):** Diff-scoped AST mutation testing (with recursive discovery and `SIGINT`/`SIGTERM`/`SIGBREAK` signal-safe disk restoration), **Full Repository Audit Mode (`--full-repo`)** with isolated parallel multi-worker sandboxes and AST import-graph test discovery, baseline test-collection failure isolation with distinct exit code `2`, entropy-driven value-based secrets scanner, GhostApproval symlink sandbox-escape detector, PyPI dependency hallucination / slopsquatting scanner (with import-to-distribution translation, recursive `-r` requirements scanning, and dynamic import detection via `importlib` / `__import__`), mock-introduction detector (`--strict-mocks`), control-flow / swallowed-exception scanner (`--strict-error-handling`), 11/11 launch-day stress test suite, **111 unit tests**, live unbuffered progress streaming, and machine-readable `--json` output.
317
+ - **Next:** Reverse test-to-source dependency mapping (see `FUTURE_SCOPE.md`), SARIF 2.1.0 PR annotations, and multi-language mutation rule packs.
264
318
 
265
319
  ## Contributing
266
320
 
@@ -273,3 +327,4 @@ MIT. See [LICENSE](LICENSE).
273
327
  ---
274
328
 
275
329
  *Created by [SVS Praveen](https://github.com/SVSPraveen) · [Portfolio](https://svspraveen.vercel.app/) · [LinkedIn](https://www.linkedin.com/in/svs-praveen-s/)*
330
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deployproof"
7
- version = "0.2.1"
7
+ version = "1.0.0"
8
8
  description = "A deterministic AI-code deployability checker that catches what coverage misses."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,2 +1,2 @@
1
1
  """DeployProof - Deterministic AI-Code Deployability Checker."""
2
- __version__ = "0.2.1"
2
+ __version__ = "1.0.0"
@@ -6,9 +6,9 @@ from typing import List, Optional
6
6
  from deployproof import __version__
7
7
  from deployproof.dependencies import extract_all_new_dependencies, scan_dependencies
8
8
  from deployproof.control_flow import scan_session_files_for_control_flow
9
- from deployproof.diff import DiffScopeError, InvalidBaseRefError, NotAGitRepositoryError, get_git_root, is_test_file, resolve_changed_python_files, resolve_changed_session_files
9
+ from deployproof.diff import DiffScopeError, InvalidBaseRefError, NotAGitRepositoryError, get_git_root, is_excluded_mutation_target, is_test_file, resolve_changed_python_files, resolve_changed_session_files, resolve_full_repo_session_files
10
10
  from deployproof.mocks import scan_session_files_for_mocks
11
- from deployproof.mutator import run_mutation_tests
11
+ from deployproof.mutator import cleanup_stale_deployproof_temp_dirs, run_mutation_tests
12
12
  from deployproof.reporter import LARGE_FILE_LOC_THRESHOLD, format_json_report, format_report
13
13
  from deployproof.secrets import scan_session_files_for_secrets
14
14
  from deployproof.symlinks import scan_session_files_for_symlinks
@@ -20,6 +20,8 @@ def create_parser() -> argparse.ArgumentParser:
20
20
  parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {__version__}', help="Show program's version number and exit.")
21
21
  subparsers = parser.add_subparsers(dest='command', help='Available commands')
22
22
  check_parser = subparsers.add_parser('check', help='Run deployability verification checks against session changes.')
23
+ check_parser.add_argument('--full-repo', action='store_true', default=False, help='Scan all files in repository respecting .gitignore instead of diff-scoped session files.')
24
+ check_parser.add_argument('--workers', type=int, default=None, help='Number of parallel worker processes for --full-repo scan (default: auto, up to 8).')
23
25
  check_parser.add_argument('--base', type=str, default=None, help='Base git ref (branch/commit/tag) to diff against.')
24
26
  check_parser.add_argument('--files', nargs='+', type=str, default=None, help='Explicit files to evaluate (bypasses git diff).')
25
27
  check_parser.add_argument('--tests', nargs='+', type=str, default=None, help='Specific test file(s) or directories to execute.')
@@ -34,8 +36,10 @@ def create_parser() -> argparse.ArgumentParser:
34
36
 
35
37
  def handle_check(args: argparse.Namespace) -> int:
36
38
  """Handle the 'check' subcommand."""
39
+ cleanup_stale_deployproof_temp_dirs()
37
40
  cwd = Path.cwd().resolve()
38
41
  repo_root: Optional[Path] = None
42
+ is_full_repo = getattr(args, 'full_repo', False)
39
43
  try:
40
44
  if args.files:
41
45
  session_files = [Path(f).resolve() for f in args.files]
@@ -46,6 +50,14 @@ def handle_check(args: argparse.Namespace) -> int:
46
50
  repo_root = session_files[0].parent
47
51
  else:
48
52
  repo_root = cwd
53
+ elif is_full_repo:
54
+ try:
55
+ repo_root = get_git_root(cwd)
56
+ except DiffScopeError:
57
+ repo_root = cwd
58
+ if not getattr(args, 'json', False):
59
+ print('Notice: Full repo scan active — evaluating all repository files (this may take significantly longer than a diff-scoped check).\n')
60
+ session_files = resolve_full_repo_session_files(cwd=cwd)
49
61
  else:
50
62
  repo_root = get_git_root(cwd)
51
63
  session_files = resolve_changed_session_files(cwd=cwd, base=args.base)
@@ -69,20 +81,20 @@ def handle_check(args: argparse.Namespace) -> int:
69
81
  return 0
70
82
  symlink_result = scan_session_files_for_symlinks(session_files, repo_root=repo_root or cwd)
71
83
  secrets_result = scan_session_files_for_secrets(session_files)
72
- extracted_deps = extract_all_new_dependencies(session_files, root=repo_root or cwd, base=args.base)
84
+ extracted_deps = extract_all_new_dependencies(session_files, root=repo_root or cwd, base=args.base, full_repo=is_full_repo)
73
85
  dependency_result = scan_dependencies(extracted_deps)
74
- mock_result = scan_session_files_for_mocks(session_files=session_files, root=repo_root or cwd, base=args.base)
75
- control_flow_result = scan_session_files_for_control_flow(session_files=session_files, root=repo_root or cwd, base=args.base)
86
+ mock_result = scan_session_files_for_mocks(session_files=session_files, root=repo_root or cwd, base=args.base, full_repo=is_full_repo)
87
+ control_flow_result = scan_session_files_for_control_flow(session_files=session_files, root=repo_root or cwd, base=args.base, full_repo=is_full_repo)
76
88
  if args.files:
77
- non_test_files = [f for f in session_files if f.is_file() and f.suffix == '.py' and (not is_test_file(f))]
78
- target_files = non_test_files if non_test_files else [f for f in session_files if f.is_file() and f.suffix == '.py']
89
+ non_excluded_files = [f for f in session_files if f.is_file() and f.suffix == '.py' and (not is_excluded_mutation_target(f, repo_root or cwd))]
90
+ target_files = non_excluded_files if non_excluded_files else [f for f in session_files if f.is_file() and f.suffix == '.py']
79
91
  else:
80
- target_files = [f for f in session_files if f.is_file() and f.suffix == '.py' and (not is_test_file(f))]
92
+ target_files = [f for f in session_files if f.is_file() and f.suffix == '.py' and (not is_excluded_mutation_target(f, repo_root or cwd))]
81
93
  if not getattr(args, 'json', False):
82
94
  for f in target_files:
83
95
  try:
84
96
  loc = len(f.read_text(encoding='utf-8', errors='replace').splitlines())
85
- if loc < LARGE_FILE_LOC_THRESHOLD:
97
+ if loc >= LARGE_FILE_LOC_THRESHOLD:
86
98
  try:
87
99
  rel = f.relative_to(repo_root or cwd)
88
100
  except ValueError:
@@ -107,7 +119,7 @@ def handle_check(args: argparse.Namespace) -> int:
107
119
  elif not getattr(args, 'json', False):
108
120
  print(wsl_msg)
109
121
  print('-' * 68)
110
- result = run_mutation_tests(target_files=target_files, repo_root=repo_root or cwd, test_runner_timeout=args.timeout, extra_pytest_args=args.tests)
122
+ result = run_mutation_tests(target_files=target_files, repo_root=repo_root or cwd, test_runner_timeout=args.timeout, extra_pytest_args=args.tests, workers=getattr(args, 'workers', None), is_full_repo=is_full_repo, base=args.base, quiet=getattr(args, 'json', False))
111
123
  if getattr(args, 'json', False):
112
124
  report_text = format_json_report(result=result, target_files=target_files, secrets_result=secrets_result, symlink_result=symlink_result, dependency_result=dependency_result, mock_result=mock_result, control_flow_result=control_flow_result, strict_mocks=getattr(args, 'strict_mocks', False), strict_error_handling=getattr(args, 'strict_error_handling', False), repo_root=repo_root or cwd, threshold=args.threshold, version=__version__)
113
125
  else:
@@ -157,12 +169,12 @@ def main(argv: Optional[List[str]]=None) -> int:
157
169
  """Entry point for the DeployProof CLI."""
158
170
  if hasattr(sys.stdout, 'reconfigure'):
159
171
  try:
160
- sys.stdout.reconfigure(errors='replace')
172
+ sys.stdout.reconfigure(line_buffering=True, errors='replace')
161
173
  except Exception:
162
174
  pass
163
175
  if hasattr(sys.stderr, 'reconfigure'):
164
176
  try:
165
- sys.stderr.reconfigure(errors='replace')
177
+ sys.stderr.reconfigure(line_buffering=False, errors='replace')
166
178
  except Exception:
167
179
  pass
168
180
  parser = create_parser()
@@ -216,6 +216,7 @@ def scan_file_for_control_flow(
216
216
  file_path: Path,
217
217
  root: Path,
218
218
  base: Optional[str] = None,
219
+ full_repo: bool = False,
219
220
  ) -> List[ControlFlowFinding]:
220
221
  """Scan a Python file for bare excepts, swallowed exceptions, and unreachable code."""
221
222
  if not file_path.is_file() or file_path.suffix != ".py":
@@ -227,7 +228,10 @@ def scan_file_for_control_flow(
227
228
  except Exception:
228
229
  return []
229
230
 
230
- modified_lines = get_modified_line_ranges(file_path=file_path, root=root, base=base)
231
+ if full_repo:
232
+ modified_lines = None
233
+ else:
234
+ modified_lines = get_modified_line_ranges(file_path=file_path, root=root, base=base)
231
235
  source_lines = source_code.splitlines()
232
236
 
233
237
  scanner = ControlFlowScanner(
@@ -243,6 +247,7 @@ def scan_session_files_for_control_flow(
243
247
  session_files: List[Path],
244
248
  root: Path,
245
249
  base: Optional[str] = None,
250
+ full_repo: bool = False,
246
251
  ) -> ControlFlowScanSummary:
247
252
  """Scan all modified session Python files for control flow and error handling issues."""
248
253
  all_findings: List[ControlFlowFinding] = []
@@ -251,7 +256,7 @@ def scan_session_files_for_control_flow(
251
256
  for f in session_files:
252
257
  if f.is_file() and f.suffix == ".py":
253
258
  scanned_files.append(f)
254
- findings = scan_file_for_control_flow(file_path=f, root=root, base=base)
259
+ findings = scan_file_for_control_flow(file_path=f, root=root, base=base, full_repo=full_repo)
255
260
  all_findings.extend(findings)
256
261
 
257
262
  return ControlFlowScanSummary(
@@ -0,0 +1,14 @@
1
+ """Pytest plugin for DeployProof coverage-guided test selection.
2
+
3
+ Switches coverage context to the current test nodeid during pytest execution,
4
+ allowing DeployProof to map each source line to the exact test cases covering it.
5
+ """
6
+ try:
7
+ import coverage
8
+
9
+ def pytest_runtest_protocol(item, nextitem):
10
+ cov = coverage.Coverage.current()
11
+ if cov is not None:
12
+ cov.switch_context(item.nodeid)
13
+ except ImportError:
14
+ pass
@@ -229,6 +229,70 @@ STDLIB_MODULES: Set[str] = set(getattr(sys, "stdlib_module_names", set())) | {
229
229
  "zoneinfo",
230
230
  }
231
231
 
232
+ # Legacy Python 2 standard library modules (for compatibility shims and backports)
233
+ LEGACY_STDLIB_MODULES: Set[str] = {
234
+ "BaseHTTPServer",
235
+ "Bastion",
236
+ "CGIHTTPServer",
237
+ "ConfigParser",
238
+ "Cookie",
239
+ "DocXMLRPCServer",
240
+ "HTMLParser",
241
+ "MimeWriter",
242
+ "Queue",
243
+ "SimpleHTTPServer",
244
+ "SimpleXMLRPCServer",
245
+ "SocketServer",
246
+ "StringIO",
247
+ "UserDict",
248
+ "UserList",
249
+ "UserString",
250
+ "al",
251
+ "anydbm",
252
+ "cPickle",
253
+ "cStringIO",
254
+ "cd",
255
+ "commands",
256
+ "cookielib",
257
+ "copy_reg",
258
+ "dbhash",
259
+ "dumbdbm",
260
+ "dummy_thread",
261
+ "dummy_threading",
262
+ "fl",
263
+ "fm",
264
+ "fpformat",
265
+ "gdbm",
266
+ "gl",
267
+ "hotshot",
268
+ "htmlentitydefs",
269
+ "htmllib",
270
+ "httplib",
271
+ "imgfile",
272
+ "jpeg",
273
+ "md5",
274
+ "mimetools",
275
+ "mimify",
276
+ "mutex",
277
+ "new",
278
+ "posixfile",
279
+ "repr",
280
+ "rfc822",
281
+ "rotor",
282
+ "sets",
283
+ "sha",
284
+ "statvfs",
285
+ "sunaudio",
286
+ "sv",
287
+ "thread",
288
+ "urllib2",
289
+ "urlparse",
290
+ "whichdb",
291
+ "xmlrpclib",
292
+ }
293
+
294
+ STDLIB_MODULES = STDLIB_MODULES | LEGACY_STDLIB_MODULES
295
+
232
296
  # Mapping of common top-level Python import names to their PyPI distribution package names
233
297
  IMPORT_TO_PYPI_MAP: Dict[str, str] = {
234
298
  "yaml": "PyYAML",
@@ -500,6 +564,7 @@ def extract_new_imports_from_py_file(
500
564
  root: Path,
501
565
  local_modules: Set[str],
502
566
  base: Optional[str] = None,
567
+ full_repo: bool = False,
503
568
  ) -> List[ExtractedDependency]:
504
569
  """
505
570
  Extract newly added external imports from a modified or new Python file.
@@ -515,7 +580,7 @@ def extract_new_imports_from_py_file(
515
580
  except Exception:
516
581
  return []
517
582
 
518
- added_linenos = get_added_linenos_for_file(file_path, root, base=base)
583
+ added_linenos = None if full_repo else get_added_linenos_for_file(file_path, root, base=base)
519
584
  extracted: List[ExtractedDependency] = []
520
585
  seen: Set[str] = set()
521
586
 
@@ -778,6 +843,7 @@ def extract_new_manifest_dependencies(
778
843
  root: Path,
779
844
  local_modules: Optional[Set[str]] = None,
780
845
  base: Optional[str] = None,
846
+ full_repo: bool = False,
781
847
  ) -> List[ExtractedDependency]:
782
848
  """
783
849
  Extract external dependencies from modified manifest files (requirements.txt, pyproject.toml, etc.).
@@ -794,7 +860,7 @@ def extract_new_manifest_dependencies(
794
860
  if not (is_requirements or is_pyproject or is_setup_py or is_setup_cfg):
795
861
  return []
796
862
 
797
- added_linenos = get_added_linenos_for_file(file_path, root, base=base)
863
+ added_linenos = None if full_repo else get_added_linenos_for_file(file_path, root, base=base)
798
864
 
799
865
  if is_requirements:
800
866
  return _parse_requirements_file(
@@ -892,9 +958,10 @@ def extract_all_new_dependencies(
892
958
  session_files: List[Path],
893
959
  root: Path,
894
960
  base: Optional[str] = None,
961
+ full_repo: bool = False,
895
962
  ) -> List[ExtractedDependency]:
896
963
  """
897
- Extract all newly added external dependencies from modified Python files and manifests in session.
964
+ Extract all external dependencies from Python files and manifests.
898
965
  """
899
966
  local_modules = get_local_module_names(root)
900
967
  all_deps: List[ExtractedDependency] = []
@@ -902,14 +969,14 @@ def extract_all_new_dependencies(
902
969
 
903
970
  for p in session_files:
904
971
  if p.suffix == ".py":
905
- py_deps = extract_new_imports_from_py_file(p, root, local_modules=local_modules, base=base)
972
+ py_deps = extract_new_imports_from_py_file(p, root, local_modules=local_modules, base=base, full_repo=full_repo)
906
973
  for d in py_deps:
907
974
  key = f"{d.name}:{d.source_file}:{d.lineno}"
908
975
  if key not in seen_unique:
909
976
  seen_unique.add(key)
910
977
  all_deps.append(d)
911
978
  else:
912
- manifest_deps = extract_new_manifest_dependencies(p, root, local_modules=local_modules, base=base)
979
+ manifest_deps = extract_new_manifest_dependencies(p, root, local_modules=local_modules, base=base, full_repo=full_repo)
913
980
  for d in manifest_deps:
914
981
  key = f"{d.name}:{d.source_file}:{d.lineno}"
915
982
  if key not in seen_unique:
@@ -983,7 +1050,7 @@ def query_pypi_registry(
983
1050
  url = f"https://pypi.org/pypi/{canonical_pkg}/json"
984
1051
  req = urllib.request.Request(
985
1052
  url,
986
- headers={"User-Agent": "DeployProof/0.2.1 (https://github.com/SVSPraveen/DeployProof)"},
1053
+ headers={"User-Agent": "DeployProof/1.0.0 (https://github.com/SVSPraveen/DeployProof)"},
987
1054
  )
988
1055
 
989
1056
  try: