Metadata-Version: 2.4
Name: mutaprobe
Version: 0.1.0
Summary: Detect silent data mutations when values cross format boundaries. Zero false positives by construction.
Author: patchwright
License: MIT
Project-URL: Homepage, https://github.com/patchwright/mutaprobe
Keywords: data-integrity,serialization,round-trip,mutation,fuzzer,testing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# mutaprobe

**Detect silent data mutations when values cross format boundaries. Zero false positives by construction.**

When you serialize, deserialize, cast, transfer, or convert data, some values silently change — precision loss, type coercion, sign-bit collapse, range overflow. These mutations don't crash; they corrupt data quietly. mutaprobe finds them.

## How it works

mutaprobe generates a fixed set of adversarial edge-case values (IEEE 754 specials, precision boundaries, arbitrary-precision integers, Unicode variants, NULL-vs-empty, types serializers reject), pushes each through your conversion function, and flags any value whose type or value changed — by **exact comparison**.

Every flag is a real mutation. There is no probabilistic detection, no threshold tuning, no false positives. The comparison is deterministic and type-strict; the flag fires if and only if `strict_equal(input, output)` is `False`.

## Install

```bash
pip install mutaprobe
```

## Use

Probe a conversion function:

```python
from mutaprobe import probe

def my_conversion(v):
    # serialize -> deserialize, cast, transfer, etc.
    return v

mutations = probe(my_conversion)
for m in mutations:
    print(m)
```

Or via CLI:

```bash
# Exit 0 = clean, exit 1 = mutations found
mutaprobe probe mymodule:convert_function

# CI mode (exit code only, no output)
mutaprobe probe mymodule:convert_function --quiet
```

## Example

```python
import json
from mutaprobe import probe

def json_roundtrip(v):
    return json.loads(json.dumps({"v": v}))["v"]

for m in probe(json_roundtrip):
    print(m)
```

Output:
```
[type-change] tuple: (1, 2, 3) -> [1, 2, 3]  (tuple became list)
[crash] decimal: Decimal('99999999999999999999.999999') -> TypeError: ...  (conversion raised an exception)
[crash] bytes: b'hello' -> TypeError: ...  (conversion raised an exception)
```

## What it catches

- **Type mutations** — `tuple` → `list`, `int` → `float`, `Decimal` → `float`
- **Silent value changes** — `0.1` → `0.10000000149011612` (float64 → float32), precision loss
- **Range overflow** — finite → `inf` when a value exceeds the target's range
- **Sign-bit collapse** — `-0.0` → `0.0` (the sign bit is data)
- **Crashes on edge values** — `Decimal`, `bytes`, `NaN` that serializers reject

## What it does NOT catch

- **Semantic drift** — a column keeping its name/type but changing meaning. That requires probabilistic reasoning (NLP/LLM-as-judge), which is inherently false-positive-prone and outside mutaprobe's zero-FP class.
- **Distribution shifts** — value distributions changing over time. Use a data observability tool.
- **Logic errors in your conversion** — if your function is wrong but *consistent*, mutaprobe won't flag it (it detects mutations, not bugs).

## The zero-FP guarantee

The flag fires iff `strict_equal(input, output)` is `False`. `strict_equal` is:
- **Type-strict** — `1 != 1.0`, `True != 1`
- **NaN-aware** — `NaN → NaN` is "survived" (IEEE 754 `NaN != NaN` special-cased)
- **Sign-aware** — `-0.0 != 0.0` (the sign bit is data)
- **Code-point-exact** — no Unicode normalization

Because the comparison is deterministic, every flag corresponds to a real type or value difference. There is no tuning, no threshold, no "maybe."

## Scope

mutaprobe probes **one conversion function at a time**. It does not generate schema-aware values (pass a conversion that takes dicts if you want to probe structured data). It does not test pipelines (wrap your pipeline as a single function). Keeping the scope narrow is the point — one feature, done precisely.

## License

MIT
