Skip to content

Repository files navigation

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
# 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:

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.)

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 (76s, real-time output) · ▶ Live session, unedited + its wall-clock-stamped terminal log (every command shown, breaks included — 3 repairs and 1 refusal in 6.5s of machine time) · Beginner quickstart · Step-by-step tested instructions — every step enforced by tests/test_demo_walkthrough.py in CI.

Watch it (each under 30 seconds, real runs)

Live from PyPI install + exhaustive selfcheck
Deploy, break, forget guard repairs and commits in 0.7s
Real library 455-test suite, byte-exact restore
Teach a class once refused -> register() -> repaired
One hard example, whole class 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; the corpus, injector, and recorded baselines are from 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.

¹ 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 (verdict: minimal in D∩I); the loop discipline is fluid-router2's EMIT/ADVANCE/HALT, exhaustively verified on all 256 mask states. Run the proofs yourself, offline, in seconds:

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:

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 allcguard 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:

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 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 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.

About

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.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages