codesuture 1.0.0__tar.gz → 1.1.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 (97) hide show
  1. {codesuture-1.0.0 → codesuture-1.1.0}/MANIFEST.in +1 -1
  2. codesuture-1.1.0/PKG-INFO +396 -0
  3. codesuture-1.1.0/README.md +371 -0
  4. codesuture-1.1.0/assets/codesuture.webp +0 -0
  5. codesuture-1.1.0/codesuture/__init__.py +1 -0
  6. codesuture-1.1.0/codesuture/alerts/channels/__init__.py +1 -0
  7. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/channels/file_channel.py +138 -139
  8. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/channels/webhook_channel.py +97 -98
  9. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/config.py +80 -80
  10. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/router.py +125 -129
  11. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/cli.py +651 -570
  12. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/codesuture_fix.py +1 -1
  13. codesuture-1.0.0/codesuture/rewind.py → codesuture-1.1.0/codesuture/frame_rewind.py +6 -12
  14. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/guard_synthesizer.py +12 -22
  15. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/digest.py +131 -136
  16. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/incident.py +80 -87
  17. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/incident_log.py +77 -79
  18. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/severity.py +40 -45
  19. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/lifecycle.py +200 -203
  20. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/metrics.py +118 -124
  21. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/middleware_asgi.py +187 -193
  22. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/opcodes.py +0 -45
  23. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/pattern_matcher.py +3 -9
  24. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/persistence.py +20 -4
  25. codesuture-1.1.0/codesuture/rewind/__init__.py +17 -0
  26. codesuture-1.1.0/codesuture/rewind/buffer.py +97 -0
  27. codesuture-1.1.0/codesuture/rewind/formatter.py +78 -0
  28. codesuture-1.1.0/codesuture/rewind/persistence.py +113 -0
  29. codesuture-1.1.0/codesuture/rewind/tracer.py +174 -0
  30. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/rollback.py +3 -12
  31. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/shadow.py +232 -229
  32. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/suggest.py +275 -304
  33. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/tracer.py +223 -45
  34. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/utils.py +57 -61
  35. codesuture-1.1.0/codesuture.egg-info/PKG-INFO +396 -0
  36. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/SOURCES.txt +8 -4
  37. {codesuture-1.0.0 → codesuture-1.1.0}/pyproject.toml +1 -1
  38. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_alerts.py +228 -249
  39. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_alerts_hard.py +617 -649
  40. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_http_replay.py +99 -103
  41. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_incidents.py +227 -247
  42. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_incidents_hard.py +571 -601
  43. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_lifecycle.py +167 -182
  44. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_metrics.py +114 -115
  45. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_middleware.py +95 -97
  46. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_middleware_asgi.py +237 -249
  47. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_negative.py +128 -131
  48. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_new_guards.py +1 -1
  49. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_opcodes.py +0 -24
  50. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_persistence.py +2 -24
  51. codesuture-1.1.0/tests/test_rewind.py +342 -0
  52. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_rollback.py +1 -44
  53. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_sandbox.py +165 -166
  54. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_shadow.py +269 -287
  55. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_ship_gate.py +612 -682
  56. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_suggestions.py +274 -295
  57. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_thread_safety.py +2 -26
  58. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_tracer_integration.py +155 -160
  59. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_transparency.py +0 -15
  60. codesuture-1.0.0/PKG-INFO +0 -430
  61. codesuture-1.0.0/README.md +0 -405
  62. codesuture-1.0.0/assets/architecture.png +0 -0
  63. codesuture-1.0.0/assets/guards.png +0 -0
  64. codesuture-1.0.0/assets/hero.png +0 -0
  65. codesuture-1.0.0/codesuture/__init__.py +0 -1
  66. codesuture-1.0.0/codesuture/alerts/channels/__init__.py +0 -1
  67. codesuture-1.0.0/codesuture.egg-info/PKG-INFO +0 -430
  68. {codesuture-1.0.0 → codesuture-1.1.0}/CHANGELOG.md +0 -0
  69. {codesuture-1.0.0 → codesuture-1.1.0}/LICENSE +0 -0
  70. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/__main__.py +0 -0
  71. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/_eval_fix.py +0 -0
  72. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/__init__.py +0 -0
  73. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/audit.py +0 -0
  74. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/code_replacer.py +0 -0
  75. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/debuggee.py +0 -0
  76. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/diff_guard.py +0 -0
  77. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/explain.py +0 -0
  78. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/fingerprint.py +0 -0
  79. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/__init__.py +0 -0
  80. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/knowledge.py +0 -0
  81. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/middleware.py +0 -0
  82. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/plugins/__init__.py +0 -0
  83. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/plugins/autonomous.py +0 -0
  84. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/sandbox.py +0 -0
  85. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/watcher.py +0 -0
  86. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/dependency_links.txt +0 -0
  87. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/entry_points.txt +0 -0
  88. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/requires.txt +0 -0
  89. {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/top_level.txt +0 -0
  90. {codesuture-1.0.0 → codesuture-1.1.0}/requirements.txt +0 -0
  91. {codesuture-1.0.0 → codesuture-1.1.0}/setup.cfg +0 -0
  92. {codesuture-1.0.0 → codesuture-1.1.0}/setup.py +0 -0
  93. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_e2e.py +0 -0
  94. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_guard_synthesizer.py +0 -0
  95. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_harness.py +0 -0
  96. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_harness2.py +0 -0
  97. {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_pattern_matcher.py +0 -0
@@ -5,7 +5,7 @@ include LICENSE
5
5
  include requirements.txt
6
6
 
7
7
  recursive-include codesuture *.py
8
- recursive-include assets *.png
8
+ recursive-include assets *.webp
9
9
 
10
10
  # Exclude dev-only files from pip sdist
11
11
  prune stress
@@ -0,0 +1,396 @@
1
+ Metadata-Version: 2.4
2
+ Name: codesuture
3
+ Version: 1.1.0
4
+ Summary: Runtime Python bytecode patcher with guard knowledge base, persistence, and self-healing re-execution
5
+ License-Expression: MIT
6
+ Project-URL: Source, https://github.com/codesuture/codesuture
7
+ Keywords: bytecode,runtime,patching,self-healing,debugging,null-safety,resilience
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Debuggers
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: bytecode>=0.15.1
19
+ Provides-Extra: autonomous
20
+ Requires-Dist: llama-cpp-python; extra == "autonomous"
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest>=7.0; extra == "test"
23
+ Requires-Dist: pytest-asyncio; extra == "test"
24
+ Dynamic: license-file
25
+
26
+ <h1 align="center">CodeSuture</h1>
27
+
28
+ <p align="center">
29
+ <strong>Your Python app crashed at 3 AM. CodeSuture already patched it — and left you the one-line fix for the morning.</strong>
30
+ </p>
31
+
32
+ <p align="center">
33
+ <a href="https://pypi.org/project/codesuture/"><img src="https://img.shields.io/pypi/v/codesuture?style=for-the-badge&color=0d6efd" alt="PyPI"></a>
34
+ <a href="https://pypi.org/project/codesuture/"><img src="https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-10b981?style=for-the-badge" alt="Python"></a>
35
+ <a href="LINK_TO_CI"><img src="https://img.shields.io/badge/tests-436%20passing-10b981?style=for-the-badge" alt="Tests"></a>
36
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-6366f1?style=for-the-badge" alt="License"></a>
37
+ </p>
38
+
39
+ <p align="center">
40
+ <code>pip install codesuture</code>
41
+ </p>
42
+
43
+ ---
44
+
45
+ ## 30 seconds, zero code changes
46
+
47
+ ```bash
48
+ codesuture run your_app.py
49
+ ```
50
+
51
+ ```
52
+ [CodeSuture] Caught AttributeError: 'NoneType' object has no attribute 'bio'
53
+ [CodeSuture] Applying null_guard on 'profile' ...
54
+ [CodeSuture] Patch applied to get_bio().
55
+ [CodeSuture] Active Shield: Native frame rewound for get_bio() successfully.
56
+
57
+ Session summary:
58
+ Patches applied: 1
59
+ ```
60
+
61
+ Run it again — the patch loads from disk before the first call:
62
+
63
+ ```
64
+ [CodeSuture] Already healed: loaded persistent patch for get_bio
65
+ ```
66
+
67
+ And in the morning, it hands you the permanent fix:
68
+
69
+ ```
70
+ $ codesuture suggest
71
+
72
+ Function: get_bio
73
+ Guard: null_guard on 'profile' Confidence: LIKELY
74
+
75
+ - bio = user.profile.bio
76
+ + bio = user.profile.bio if user.profile is not None else ''
77
+ ```
78
+
79
+ **No decorators. No source file changes. No restart.**
80
+
81
+ <p align="center">
82
+ <img src="assets/codesuture.webp" alt="CodeSuture healing a live crash" width="100%">
83
+ </p>
84
+
85
+ ---
86
+
87
+ ## Why this exists
88
+
89
+ Sentry tells you your server crashed. Your logs tell you why. **Nothing keeps it alive while you sleep.**
90
+
91
+ When a Python program hits an `AttributeError`, `KeyError`, `ZeroDivisionError`, `IndexError`, or `TypeError`, CodeSuture intercepts the exception at the exact bytecode instruction, injects a minimal deterministic guard into the function's code object in memory, and retries execution.
92
+
93
+ Every patch is temporary by design: it carries a TTL, generates a source-level fix suggestion, and nags you until the root cause is fixed. CodeSuture is a safety net — built to make ignoring real bugs impossible.
94
+
95
+ ---
96
+
97
+ ## How It Works
98
+
99
+ CodeSuture operates in five stages:
100
+
101
+ | Stage | What happens |
102
+ |-------|-------------|
103
+ | **① Catch** | On Python 3.12+, `sys.monitoring` (PEP 669) fires **only when an exception is raised** — near-zero overhead on healthy code. On 3.11, a `sys.settrace` fallback is used |
104
+ | **② Analyze** | The pattern matcher disassembles the failing instruction chain and identifies the crashing variable, operation, and crash type |
105
+ | **③ Patch** | The guard synthesizer injects minimal bytecode. A semantic diff gate rejects patches that modify too much logic |
106
+ | **④ Rewind** | The frame is rewound and the patched function re-enters. On HTTP servers, **safe requests (GET/HEAD) are replayed in-place** — the client gets a real response instead of a dropped socket |
107
+ | **⑤ Persist** | The patch is serialized to `.codesuture_store/`, **HMAC-signed with a server-generated 256-bit key**, checksummed, and stamped with a TTL |
108
+
109
+ ---
110
+
111
+ ## Performance
112
+
113
+ On Python 3.12+ instrumentation activates **only at the moment an exception is raised**. Your happy path runs at native speed.
114
+
115
+ | Runtime | Engine | Healthy-path overhead |
116
+ |---------|--------|----------------------|
117
+ | Python 3.12 / 3.13 | `sys.monitoring` (PEP 669) | ~0% — exception-only callbacks |
118
+ | Python 3.11 | `sys.settrace` fallback | measurable — recommended for dev/staging |
119
+
120
+ Verify it yourself: `python benchmarks/overhead.py`
121
+
122
+ ---
123
+
124
+ ## The 11 Guards
125
+
126
+ Every guard is **deterministic and rule-based** — no AI deciding what your code does at runtime.
127
+
128
+ | Guard | Crash Type | Example | What It Does |
129
+ |-------|-----------|---------|--------------|
130
+ | `null_guard` | `AttributeError` on `None` | `user.profile.bio` | Inserts `if x is None: x = default` before attribute access |
131
+ | `key_guard` | `KeyError` | `config["timeout"]` | Wraps dict access with `.get(key, default)` |
132
+ | `subscript_guard` | `TypeError` subscripting `None` | `data["key"]` when `data` is `None` | Null-checks the container before subscript |
133
+ | `chain_subscript_guard` | Nested subscript failures | `resp["user"]["name"]["first"]` | Guards the entire chain from the root |
134
+ | `index_guard` | `IndexError` (variable index) | `items[i]` when `i >= len(items)` | Bounds-checks `i` against `len(items)` |
135
+ | `list_bound_guard` | `IndexError` (constant index) | `parts[3]` when `len(parts) < 4` | Checks `len(parts) > 3` before access |
136
+ | `division_guard` | `ZeroDivisionError` | `total / count` when `count` is `0` | Substitutes a safe divisor when denominator is zero |
137
+ | `str_coerce_guard` | `TypeError` on string concat | `"age: " + age` when `age` is `int` | Wraps non-str variable with `str()` |
138
+ | `type_coercion_guard` | `TypeError` on conversion | `int("not_a_number")` | Adds type validation before coercion |
139
+ | `file_guard` | `FileNotFoundError` | `open(path)` | Checks `os.path.exists()` before open |
140
+ | `callable_guard` | `TypeError` calling `None` | `callback()` when `callback` is `None` | Returns `None` for unknown callables |
141
+
142
+ ---
143
+
144
+ ## HTTP Recovery — Honest by Design
145
+
146
+ When a request handler crashes, CodeSuture patches the function mid-request. The client gets a response instead of a socket close — and is **never lied to** about it:
147
+
148
+ ```http
149
+ HTTP/1.0 200 OK
150
+ Content-type: application/json
151
+ X-CodeSuture: patched=1; guard=null_guard; target=get_profile
152
+
153
+ {"_degraded": true, "result": null, "patched": true}
154
+ ```
155
+
156
+ Three hard rules:
157
+
158
+ 1. **Mutating requests are never replayed.** POST, PUT, PATCH, and DELETE handlers are patched for *future* requests, but the failing transaction is never re-executed. No double charges. No duplicate writes. Ever.
159
+ 2. **Degraded responses say so.** Every patched response carries the `X-CodeSuture` header and an explicit `"_degraded": true` body flag.
160
+ 3. **Replay applies to safe methods only.** GET and HEAD are replayed in-place after patching.
161
+
162
+ ### Framework Middleware
163
+
164
+ ```python
165
+ # WSGI (Flask, Django, Bottle, etc.)
166
+ from codesuture.middleware import CodeSutureMiddleware
167
+ app = CodeSutureMiddleware(your_wsgi_app)
168
+
169
+ # ASGI (FastAPI, Starlette, etc.)
170
+ from codesuture.middleware_asgi import CodeSutureASGIMiddleware
171
+ app = CodeSutureASGIMiddleware(your_asgi_app)
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Built Paranoid
177
+
178
+ A tool that touches live bytecode has to earn trust. Every layer assumes something will go wrong:
179
+
180
+ | Defense | What it prevents |
181
+ |---------|-----------------|
182
+ | **HMAC-signed patches** | A random 256-bit key is generated per server; every persisted patch is signed. A malicious file dropped into `.codesuture_store/` is rejected on load — it can't carry a valid signature |
183
+ | **Semantic diff gate** | Patches that modify too many instructions are rejected. The engine never rewrites complex logic to fix a simple crash |
184
+ | **SHA-256 integrity** | Corrupted or tampered `.code` files are refused on load |
185
+ | **Bytecode validation** | Synthesized patches referencing variables not in `co_varnames` are rejected before injection |
186
+ | **Original code backup** | Pre-patch code objects are kept in memory — rollback restores them in the *running* process, no restart |
187
+ | **Patch TTL** | Every patch expires. An expired patch means you should have fixed the root cause by now — and CodeSuture will tell you so |
188
+ | **Mutating-method lockout** | POST/PUT/PATCH/DELETE transactions are never replayed |
189
+ | **Thread safety** | All shared state is lock-protected. Safe under free-threaded Python 3.13+ (no-GIL) |
190
+ | **Caller-aware propagation** | After patching, `gc.get_referrers` updates closures, bound methods, and partials |
191
+ | **CPython portability** | Version-aware opcode sets handle 3.11, 3.12, and 3.13+ instruction differences |
192
+
193
+ Audit everything at any time:
194
+
195
+ ```bash
196
+ codesuture audit # every active patch
197
+ codesuture explain # plain-language description of what each patch does
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Incident Intelligence
203
+
204
+ Every crash is logged as a structured incident with automatic severity classification:
205
+
206
+ | Severity | When |
207
+ |----------|------|
208
+ | **CRITICAL** | Callable replacement, sensitive modules (auth, payment, billing) |
209
+ | **HIGH** | First occurrence, crashes in mutating HTTP handlers, chain subscripts |
210
+ | **MEDIUM** | Standard guards (null, key, division, index) after first occurrence |
211
+ | **LOW** | Repeat patterns, file guards, string coercion |
212
+
213
+ ```bash
214
+ $ codesuture incidents
215
+
216
+ Time Severity Function Guard Status
217
+ ──────────────────── ────────── ───────────────────────── ────────────────────── ─────────
218
+ 2026-05-26T19:05:55 MEDIUM render_user_card null_guard patched
219
+ 2026-05-26T19:05:55 HIGH format_weather_report chain_subscript_guard patched
220
+ 2026-05-26T19:05:56 MEDIUM compute_metrics division_guard patched
221
+ ```
222
+
223
+ ```bash
224
+ $ codesuture digest
225
+
226
+ # CodeSuture Daily Incident Report — 2026-05-26
227
+
228
+ ## Summary
229
+ - **Total incidents:** 3
230
+ - **CRITICAL:** 0 | **HIGH:** 1 | **MEDIUM:** 2 | **LOW:** 0
231
+ - **Unique crash patterns:** 3
232
+ - **Functions patched:** 3
233
+ ```
234
+
235
+ **Alerts** route by severity — markdown files in `.codesuture_alerts/`, or webhooks (Slack, PagerDuty). Functions patched 5+ times in 24 hours are auto-escalated to CRITICAL.
236
+
237
+ ---
238
+
239
+ ## Shadow Execution — Prove the Patch Is Right
240
+
241
+ ```bash
242
+ codesuture run app.py --shadow
243
+ ```
244
+
245
+ Runs the original (unpatched) function alongside the patched version and compares results:
246
+
247
+ | Verdict | Meaning |
248
+ |---------|---------|
249
+ | **JUSTIFIED** | Original crashes, patched succeeds — the patch is necessary |
250
+ | **UNNECESSARY** | Identical results — consider removing the patch |
251
+ | **DIVERGENT** | Results differ — review before trusting |
252
+
253
+ Shadow-verified patches get upgraded to **VERIFIED** confidence in fix suggestions.
254
+
255
+ ---
256
+
257
+ ## Fix Suggestions
258
+
259
+ CodeSuture generates concrete source-code fixes for every active patch:
260
+
261
+ ```bash
262
+ $ codesuture suggest
263
+
264
+ Function: render_user_card
265
+ Guard: null_guard on 'profile'
266
+ Confidence: LIKELY
267
+
268
+ --- a/app.py
269
+ +++ b/app.py
270
+ @@ -15,1 +15,1 @@
271
+ - bio = user.profile.bio
272
+ + bio = user.profile.bio if user.profile is not None else ''
273
+ ```
274
+
275
+ - **VERIFIED** — Shadow execution confirmed the fix works
276
+ - **LIKELY** — Deterministic guard with high confidence
277
+ - **EXPERIMENTAL** — Complex guard, review recommended
278
+
279
+ ---
280
+
281
+ ## Lifecycle Management
282
+
283
+ Every patch transitions through a state machine — stale patches get flagged:
284
+
285
+ ```
286
+ DETECTED → PATCHED → PERSISTED → SUGGESTED → VERIFIED → FIXED
287
+ ↓ ↓
288
+ REPLAYED EXPIRED
289
+
290
+ ROLLED_BACK
291
+ ```
292
+
293
+ ```bash
294
+ $ codesuture lifecycle show
295
+
296
+ Function State Age TTL
297
+ get_bio PERSISTED 2d 7d
298
+ compute_ratio VERIFIED 1d 7d
299
+ parse_config EXPIRED 8d 7d ← needs attention
300
+ ```
301
+
302
+ ---
303
+
304
+ ## CLI Reference
305
+
306
+ ```bash
307
+ # Core
308
+ codesuture run app.py # run with live patching
309
+ codesuture run app.py --dry-run # preview patches without applying
310
+ codesuture run app.py --verbose --shadow --retries 5
311
+ codesuture watch server.py --max-restarts 10
312
+
313
+ # Inspection & governance
314
+ codesuture audit # all active patches
315
+ codesuture explain [func] # plain-language explanations
316
+ codesuture incidents [--since 2d] # crash log
317
+ codesuture digest # markdown incident report
318
+ codesuture suggest # source-level fix suggestions
319
+ codesuture metrics # Prometheus export
320
+ codesuture lifecycle show # patch state machine
321
+ codesuture alerts # unread alerts
322
+ codesuture alerts dismiss <incident_id> # dismiss resolved alerts
323
+
324
+ # Rollback
325
+ codesuture rollback <func> # disk files AND live process restore
326
+ codesuture rollback --dry-run # preview removal
327
+ codesuture rollback --all # remove everything
328
+ ```
329
+
330
+ ---
331
+
332
+ ## Prometheus Metrics
333
+
334
+ ```bash
335
+ $ codesuture metrics
336
+
337
+ # HELP codesuture_incidents_total Total incidents recorded
338
+ # TYPE codesuture_incidents_total counter
339
+ codesuture_incidents_total 20
340
+ codesuture_patches_total{guard_type="null_guard"} 12
341
+ codesuture_patches_total{guard_type="key_guard"} 5
342
+ codesuture_patches_total{guard_type="division_guard"} 3
343
+ ```
344
+
345
+ ---
346
+
347
+ ## Known Limitations
348
+
349
+ We'd rather you read these here than discover them in production:
350
+
351
+ | Limitation | Detail |
352
+ |-----------|--------|
353
+ | **Python 3.11+ only** | Depends on CPython bytecode structures introduced in 3.11 |
354
+ | **First crash propagates** | The initial exception reaches the caller. The patch prevents recurrence on subsequent calls |
355
+ | **Comprehensions skipped** | List/dict/set/generator comprehensions are anonymous nested code objects — logged and skipped |
356
+ | **Crashes only, not logic bugs** | Wrong results that don't raise an exception cannot be detected |
357
+ | **Per-process patching** | Patches apply per-process; `.codesuture_store/` is shared on disk for cross-restart persistence |
358
+ | **Async is experimental** | Standard `async def` works. Async generators and deep `await` chains may not be handled correctly |
359
+
360
+ ---
361
+
362
+ ## What CodeSuture Is Not
363
+
364
+ **Not a logger.** It doesn't record exceptions and move on. It patches the function and retries.
365
+
366
+ **Not a static analyzer.** It operates at runtime on live bytecode, not on source files.
367
+
368
+ **Not autonomous by default.** All patches are deterministic rule-based guards. An opt-in `--autonomous` flag exists for experimental LLM-powered suggestions — they are never auto-applied.
369
+
370
+ **Not a replacement for fixing bugs.** The `suggest` command tells you exactly what source code to change. The `lifecycle` system tracks patch age. Expired patches mean you should have fixed the root cause by now.
371
+
372
+ ---
373
+
374
+ ## Installation
375
+
376
+ ```bash
377
+ pip install codesuture
378
+ ```
379
+
380
+ Requires **Python 3.11+** and the [`bytecode`](https://pypi.org/project/bytecode/) library (installed automatically).
381
+
382
+ ```bash
383
+ pip install "codesuture[autonomous]" # optional: experimental LLM suggestions
384
+ ```
385
+
386
+ ---
387
+
388
+ ## License
389
+
390
+ MIT. See [LICENSE](LICENSE) for details.
391
+
392
+ ---
393
+
394
+ <p align="center">
395
+ <sub>Built with obsession, not sleep. If CodeSuture saved your server at 3 AM, consider giving it a ⭐.</sub>
396
+ </p>