Metadata-Version: 2.4
Name: fluidfix
Version: 0.13.0
Summary: Zero-token repair decisions for mechanical single-line bugs: a machine-authored routing kernel decides, your test suite judges, and an LLM is only ever the eyes.
Author-email: Devieswar Kancheti <devkancheti4@gmail.com>
Maintainer-email: Devieswar Kancheti <devkancheti4@gmail.com>
License-Expression: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/devkancheti4-design/fluidfix
Project-URL: Documentation, https://github.com/devkancheti4-design/fluidfix/blob/master/docs/TEACHING.md
Project-URL: Kernel provenance, https://github.com/devkancheti4-design/fluid-router
Project-URL: Measured results, https://github.com/devkancheti4-design/fluidfix/blob/master/docs/BENCHMARK.md
Keywords: automated-program-repair,debugging,fault-localization,mutation,llm,claude
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: COMMERCIAL.md
Provides-Extra: llm
Requires-Dist: anthropic>=1.0; extra == "llm"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-cov>=7; extra == "dev"
Requires-Dist: build; extra == "dev"
Dynamic: license-file

# fluidfix

**Zero-token repair decisions for mechanical single-line bugs.** A
machine-authored four-instruction kernel decides which repair to try, your
test suite judges it, and a language model — when you use one at all — is only
ever the eyes. Every output is either a repair your suite accepts — measured
byte-exact in 26 of 26 accepted repairs on the benchmark — or an explicit
refusal. There is no "plausible fix" branch.

```
pip install --upgrade fluidfix            # core: zero runtime dependencies
pip install "fluidfix[llm]"     # adds the Claude Opus 5 observer
```

```bash
# commit-and-forget: watch the suite, restore what breaks, refuse what is novel
fluidfix guard path/to/project --python path/to/venv/bin/python --interval 900 --commit

# or one-shot on a known defect file
fluidfix repair path/to/project --file pkg/module.py --python path/to/venv/bin/python
```

**How fast will it be on your repo?** Repair time is *suite runs x your
suite's own runtime* — test count barely matters. Ask before you commit:

```bash
fluidfix estimate .
```

```
  1990 passed, 25 skipped in 4.87s
  suite runtime: 5.60s  (+~0.5s pytest startup per run)

EXPECTED REPAIR TIME on this repo
  typical in-vocabulary defect (2-10 suite runs):  12.2s - 61s
```

(Those bounds are real: the same repo repaired in 36s and 50s.)


```python
from fluidfix import Oracle, build_packet, MechanicalObserver, repair

oracle = Oracle("path/to/project", python="path/to/venv/bin/python")
packet = build_packet(oracle, "pkg/module.py")          # mechanical, 0 tokens
observations = MechanicalObserver().observe([packet])[0]
print(repair(oracle, "pkg/module.py", observations).summary())
```


**[▶ Full demo video](docs/media/demo.mp4)** (76s, real-time output) · **[▶ Live session, unedited](docs/media/live-session.mp4)** + its **[wall-clock-stamped terminal log](docs/media/live-session.log)** (every command shown, breaks included — 3 repairs and 1 refusal in 6.5s of machine time) · **[Beginner quickstart](docs/QUICKSTART.md)** · **[Step-by-step tested instructions](docs/DEMO.md)** — every step enforced by `tests/test_demo_walkthrough.py` in CI.

## Watch it (each under 30 seconds, real runs)

| | |
|---|---|
| [Live from PyPI](docs/media/clip_s1.gif) | install + exhaustive selfcheck |
| [Deploy, break, forget](docs/media/clip_s2.gif) | guard repairs and commits in 0.7s |
| [Real library](docs/media/clip_s3.gif) | 455-test suite, byte-exact restore |
| [Teach a class once](docs/media/clip_s4.gif) | refused -> register() -> repaired |
| [One hard example, whole class](docs/media/clip_s5.gif) | one dictionary fixes 3 different files |

## Measured, not promised

All numbers below are from a live benchmark on 33 injected single-line bugs in
five real PyPI libraries (humanize, inflection, natsort, parse, wcwidth), each
library's own suite as the oracle, run 2026-08-30. Per-bug data and
methodology ship with this package in [docs/BENCHMARK.md](docs/BENCHMARK.md);
the corpus, injector, and recorded baselines are from
[fluid-router](https://github.com/devkancheti4-design/fluid-router)
`benchmark/`.

|                             | fluidfix (Claude Opus 5 as eyes) | full Claude Opus 5 debugging |
|-----------------------------|-----------------------------------|------------------------------|
| in-vocabulary byte-exact    | **26/27**                         | 22/27 (5 green-only)         |
| out-of-vocabulary           | 6/6 refused honestly              | 5/6 exact                    |
| silently wrong repairs      | **0**                             | —                            |
| tokens¹                     | **125,402 total (3,800/bug)**     | 1,322,802 (40,084/bug)       |
| decision cost after that    | **0 tokens²**                     | ~40k tokens each time        |

- The observer named the correct defective line **33/33** from lean packets
  averaging ~1,060 tokens.
- The mechanical localiser (no model at all) put the true defective line in
  the packet for **33/33** bugs, at zero tokens.
- The kernel's decisions are invariant under all 16 renumberings of the act
  vocabulary: **432/432** live decisions correct; a frozen lookup table
  scores 27/432 on the same test.
- With no model anywhere, fluid-router's recorded blind-search kernel scores
  17/27 byte-exact at zero tokens — without the localisation this package
  adds. fluidfix's mechanical mode is validated end-to-end but not yet
  corpus-scored; see [docs/BENCHMARK.md](docs/BENCHMARK.md).

¹ Fleet-level harness measurement, all agent context included; the two token
columns use different accounting and support the ~10× ratio, not a precise
figure. Provenance: `docs/data/lean_arm_tokens.json`.
² fluid-router2's C verifier measures its kernels at 1.55 ns/decision; this
package's pure-Python reference is ~0.6 µs. Either way: no tokens.

## The guard: deploy, commit, forget

Most deployed software is not being actively developed — it is being kept
alive. `fluidfix guard` is built for exactly that. It needs no defect file:
when the suite goes red it finds the fault file mechanically (traceback
frames, else failing-test coverage ranking), repairs it, and — with
`--commit` — records the restoration:

```
$ fluidfix guard . --commit
[16:43:06] billing.py: repaired line 2 in 4 suite runs (0.7s):
  - return p * (1 - rate)
  + return p * (1 + rate)
  committed
```

Run it one-shot in CI (exit 0 green/repaired, exit 2 refused), or under cron
with `--interval`. Green suite: it touches nothing. Novel fault class: it
refuses, leaves the tree byte-identical, and writes
`.fluidfix/last_refusal.json` — the teach-me signal for `register()` below.
`.fluidfix/` is the guard's state directory: add it to your `.gitignore`.

## How it works

```
suite fails ──► localise (frames ∪ failing-test coverage ∪ AST spans; 0 tokens)
            ──► observe (mechanical regexes, or one batched Claude call)
            ──► EMIT names the fault      m & (-m)
            ──► route() picks the act     15 & ((x>>4)+((x>>8)-x))   ◄ the brain
            ──► apply & run the suite     green → HALT
            ──► ADVANCE and try the next  m - (m & (-m))
            ──► empty mask → refuse, never guess
```

The routing expression was authored by a program-synthesis engine and is
vendored verbatim from [fluid-router](https://github.com/devkancheti4-design/fluid-router)
(verdict: `minimal in D∩I`); the loop discipline is
[fluid-router2](https://github.com/devkancheti4-design/fluid-router2)'s
EMIT/ADVANCE/HALT, exhaustively verified on all 256 mask states. Run the
proofs yourself, offline, in seconds:

```bash
fluidfix selfcheck
```

### What should we test first?

fluidfix maintains exactly what your tests cover, so that is the question
worth asking before adopting it — and your repository already knows, because
every bug fix in its history names the file that broke:

```bash
fluidfix hotspots .
```

Measured on Box2D's own history: guarding **60% of its past defects** costs
testing **58 files, not 60% of the engine**. Defects cluster, so coverage
aimed at the cluster is worth roughly twice coverage spread evenly.

### Languages

| language | command | judge | status |
|---|---|---|---|
| Python | `fluidfix guard` | pytest | stable |
| C / C++ | `fluidfix cguard` | your test binary, plus the **compiler** as a cheap first oracle | alpha |
| C# / .NET | `fluidfix cguard --build-cmd "dotnet build" --test-cmd "dotnet test --no-build"` | `dotnet test` (xunit measured) | alpha |
| Java | `fluidfix jguard` | JUnit via Maven | alpha |

C# needed **no adapter at all** — `cguard` is generic over its build and test
commands. Supporting it cost one source extension (`.cs`) and one regex for
the `Failed Class.Method [2 ms]` shape that dotnet prints. Measured: a
cross-product sign flip in a .NET class library repaired **byte-exact in 14
suite runs (20.8s)**, warm cycle 1.63s.

**Unity is not the same as C#.** The language works; Unity's own test
framework generally needs the Editor in batch mode to run tests, which is a
harder oracle and is untested here.

Adding a language is an **adapter**, not a rewrite — the kernels route
integers and edit lines of text, and never learn the language. Java took 223
lines, C took ~450. Measured on two real game repos, all byte-exact:
Box2D's `b2Cross` sign flip in 46 suite runs (82.8s), cglm's `glm_vec3_add`
in 100 runs, and a pointer-arithmetic defect in Box2D's `contact_solver.c` —
with no traceback frame, no discriminating literal and no name affinity — in
428 runs once the gcov tier was supplying executed-line evidence.

`selfcheck` re-derives **five** machine-authored laws with no network and no
dependencies: fluid-router (which repair), fluid-router2 (which candidate
next), the engine law (what to do when blocked — one controller that governs
four different jobs, 0 of 256 situations ruling differently), the ranking law
(which line first) and the SIGHT law (which file first — two tiers, where
pointing evidence outranks circumstantial evidence *algebraically*).

## Safety properties

- **Refuses on a green suite.** Searching without a failing test has been
  measured to corrupt working code while reporting success; `repair()` checks
  first and returns "nothing to repair".
- **Refuses outside its vocabulary.** and/or confusion, flipped booleans,
  `*`/`/` swaps have no act — the mask comes back empty and fluidfix says so,
  loudly, instead of guessing.
- **Restores byte-exactly on failure.** Every rejected candidate is rolled
  back; a refused repair leaves the tree untouched.
- **Bounded candidates.** A candidate that will not terminate is a failed
  candidate, not a hung pipeline.
- **The oracle is defended.** Stale-bytecode, plugin-collision,
  exit-first-swallows-coverage, and truncated-node-id failure classes are all
  encoded in `oracle.py`, each one a scar from a measured harness defect.

## Teaching it new classes — the maintenance loop

The four shipped acts are a starter dictionary, not a ceiling. A fault class
is teachable the moment its repair can be expressed as a mechanical transform
of the defective line:

```python
import re
from fluidfix import register

register(4, "logic-flip", 'an "and" that should be "or", or vice versa',
         re.compile(r"\b(?:and|or)\b"),
         lambda line, obs: (line.replace(" or ", " and ", 1) if " or " in line
                            else line.replace(" and ", " or ", 1)))
```

One registration, once — the router is never edited: it infers the new
class's act code from the same single worked example
(`tests/test_regressions.py` shows an and/or bug going from refused to
repaired with exactly this snippet). That is the deployment story fluidfix is
built for: ship it on a maintained codebase, let the known classes repair
themselves for free, and when an update introduces a novel fault class, hand
that class to fluidfix once and maintenance is free again.

The honest boundary: classes whose repair needs information absent from the
defective line — a wrong variable, a missing guard, a different algorithm —
are not transforms, and are refused rather than attempted. (Measured share of
real one-line fixes that are single-token substitutions: ~16%; see
fluid-router's `benchmark/domain/`.) The refusal is your signal to spend a
frontier model exactly once, on the class, never again on its instances.

## Teach it your bugs

[docs/TEACHING.md](docs/TEACHING.md) is the full guide: how to turn a refusal
into a registered fault class, what makes a transform safe, and how the router
infers the new act from one worked example.
[examples/company_rules.py](examples/company_rules.py) is a ready-to-adapt
dictionary of registrations to copy into your own deployment.

## Licensing

AGPL-3.0-or-later. Section 13 (network use) applies. If AGPL does not suit
your use — proprietary products, SaaS without source offer — commercial
licenses are available: see [COMMERCIAL.md](COMMERCIAL.md).
