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.
- {codesuture-1.0.0 → codesuture-1.1.0}/MANIFEST.in +1 -1
- codesuture-1.1.0/PKG-INFO +396 -0
- codesuture-1.1.0/README.md +371 -0
- codesuture-1.1.0/assets/codesuture.webp +0 -0
- codesuture-1.1.0/codesuture/__init__.py +1 -0
- codesuture-1.1.0/codesuture/alerts/channels/__init__.py +1 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/channels/file_channel.py +138 -139
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/channels/webhook_channel.py +97 -98
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/config.py +80 -80
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/router.py +125 -129
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/cli.py +651 -570
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/codesuture_fix.py +1 -1
- codesuture-1.0.0/codesuture/rewind.py → codesuture-1.1.0/codesuture/frame_rewind.py +6 -12
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/guard_synthesizer.py +12 -22
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/digest.py +131 -136
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/incident.py +80 -87
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/incident_log.py +77 -79
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/severity.py +40 -45
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/lifecycle.py +200 -203
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/metrics.py +118 -124
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/middleware_asgi.py +187 -193
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/opcodes.py +0 -45
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/pattern_matcher.py +3 -9
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/persistence.py +20 -4
- codesuture-1.1.0/codesuture/rewind/__init__.py +17 -0
- codesuture-1.1.0/codesuture/rewind/buffer.py +97 -0
- codesuture-1.1.0/codesuture/rewind/formatter.py +78 -0
- codesuture-1.1.0/codesuture/rewind/persistence.py +113 -0
- codesuture-1.1.0/codesuture/rewind/tracer.py +174 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/rollback.py +3 -12
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/shadow.py +232 -229
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/suggest.py +275 -304
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/tracer.py +223 -45
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/utils.py +57 -61
- codesuture-1.1.0/codesuture.egg-info/PKG-INFO +396 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/SOURCES.txt +8 -4
- {codesuture-1.0.0 → codesuture-1.1.0}/pyproject.toml +1 -1
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_alerts.py +228 -249
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_alerts_hard.py +617 -649
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_http_replay.py +99 -103
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_incidents.py +227 -247
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_incidents_hard.py +571 -601
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_lifecycle.py +167 -182
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_metrics.py +114 -115
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_middleware.py +95 -97
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_middleware_asgi.py +237 -249
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_negative.py +128 -131
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_new_guards.py +1 -1
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_opcodes.py +0 -24
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_persistence.py +2 -24
- codesuture-1.1.0/tests/test_rewind.py +342 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_rollback.py +1 -44
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_sandbox.py +165 -166
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_shadow.py +269 -287
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_ship_gate.py +612 -682
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_suggestions.py +274 -295
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_thread_safety.py +2 -26
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_tracer_integration.py +155 -160
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_transparency.py +0 -15
- codesuture-1.0.0/PKG-INFO +0 -430
- codesuture-1.0.0/README.md +0 -405
- codesuture-1.0.0/assets/architecture.png +0 -0
- codesuture-1.0.0/assets/guards.png +0 -0
- codesuture-1.0.0/assets/hero.png +0 -0
- codesuture-1.0.0/codesuture/__init__.py +0 -1
- codesuture-1.0.0/codesuture/alerts/channels/__init__.py +0 -1
- codesuture-1.0.0/codesuture.egg-info/PKG-INFO +0 -430
- {codesuture-1.0.0 → codesuture-1.1.0}/CHANGELOG.md +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/LICENSE +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/__main__.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/_eval_fix.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/alerts/__init__.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/audit.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/code_replacer.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/debuggee.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/diff_guard.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/explain.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/fingerprint.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/incidents/__init__.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/knowledge.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/middleware.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/plugins/__init__.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/plugins/autonomous.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/sandbox.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture/watcher.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/dependency_links.txt +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/entry_points.txt +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/requires.txt +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/codesuture.egg-info/top_level.txt +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/requirements.txt +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/setup.cfg +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/setup.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_e2e.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_guard_synthesizer.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_harness.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_harness2.py +0 -0
- {codesuture-1.0.0 → codesuture-1.1.0}/tests/test_pattern_matcher.py +0 -0
|
@@ -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>
|