CodeEraser refuses a duplicate write before the file reaches disk
An eraser against LLM-induced code & document entropy — clone/duplication/deadcode/structure judgment (Haskell core, Rust frontend) with Claude Code guard hooks, MCP, CI gates and a Tauri GUI
At a glance
- What is it?
- CodeEraser is a Rust frontend over a Haskell core that intercepts writes to a code or document file and denies two specific things: a new exact T1/T2 clone, and a file pushed past its line limit. Everything else it computes is a report or a gate, and no model participates in any of it.
- Who is it for?
- CodeEraser fits a team that already reviews with a language model and is tired of reviewing the same function a third time. It is built for a repository with a declared layout, class based rules, and a pre-commit hook that everyone actually runs, and it is a poor fit if you want a one line lint or if your editor rewrites whole files.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Star history
- skymanbp/CodeEraser20 ★ · -16
| Repository | 2026-08-22 | 2026-10-22 |
|---|---|---|
| skymanbp/CodeEraser | 36 | 20 |
-16 Stars in the last 61 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Two refusals happen before the write reaches disk
Only two things are hard refusals, and both fire at the moment of writing, ahead of anything reaching the filesystem. The first is a write that would introduce an exact T1/T2 clone, meaning duplication the replaced content did not already carry, and the denial names the region being duplicated plus the ordering that would have passed. The second is size: a write leaving a file over 750 lines, or over whatever line its `[[rules.class]]` declares, is refused the same way. That limit is not written as prose in the documentation. It sits wrapped in markers of the form `<!--ce:gate:size.file_lines_fail#digits-->750<!--/ce-->`. Everything else the tool produces is softer by design. The Stop audit refuses the turn, `ce precommit` and `ce commitmsg` refuse the commit, and CI exit codes refuse the merge, but those are downstream of content that already exists.
The documentation keeps its own counts inside HTML comment markers
Several numbers in the write-up are not typed by hand. The word thirteen before language codes, twelve before tree-sitter grammars, four hooks, one skill, one command, nineteen read-only MCP tools, twelve screens, and Eight before axes are each wrapped in comment markers that name what they count. The same trick wraps the gate limit of 750 lines and the two latency figures on the hook probe. The consequence is that the documentation and the tool can be made to agree, and a reader can tell which figures are machine restated and which are prose. It also means a plain reading of the page understates how much of it is generated. The visible text does not explain who refreshes those values or when, so treat the restated figures as accurate to whatever build produced the page you are looking at.
T1 and T2 run hot while T3 goes cold through tree edit distance
Exact clones are caught on the hot path. Every parsed code file has its tokens normalized, with identifiers rewritten to `ID`, literals to `LIT`, and comments dropped, then winnowed with k = 25 and w = 26, so any shared run of 50 or more tokens is guaranteed to produce a shared fingerprint. Structural similarity is the cold path. Structural fingerprints plus MinHash and LSH with 128 permutations and 32 bands of 4 rows generate candidates without discarding a pair that could pass, and the Haskell core computes Zhang-Shasha tree edit distance, accepting at TSED of 0.85 or above in exact integer arithmetic. Since 1.8.0 those verified pairs feed the clone axis of `ce check` and the similarity leg of `ce join` as their own kind, and a verdict for a pair of trees is remembered in the index, asked once and replayed until either tree, the core, or a knob changes.
The duplicate rate depends on whether the editor streams or rewrites
The two published false positive readings are not in conflict, they measure different editors. On the live stream reading, where content arrives incrementally, the guard charges only duplication the replaced text did not already carry, and it misfires on none of 719 production probes, which is 0.00 per 500. On the replay of 2,761 events where each write replaces the whole file, the same logic charges the 32 intermediate states produced by splitting a file, which comes to 7.03 per 500. Both are kept in a file at docs/FPR-REPLAY.md, and since 1.8.0 that file also carries one row per language plan, with six corpora of 400 commits each and 0 false intercepts. The practical reading is that a streaming editor sees almost no false refusals, while an editor that rewrites whole files will be refused during refactors. The recorded probe latency is 50 ms p50 and 57 ms p95 on a two file fixture, and the whole plugin chain at 0.50 s p95 as last measured on 2026-08-29, before the tombstone leg joined it.
Plain text joins the documentation corpus and nothing else
Documentation duplication is judged in a way that survives rewording. Words are NFC normalized, turned into 5 word shingles, reduced to MinHash and LSH candidates, and then confirmed by an exact Jaccard of 0.80 or above, or by a 50 word verbatim run, with the arithmetic done in the core using exact rationals. The `.txt` extension is the narrowest arm in the whole tool. Plain text enters the documentation duplication corpus and nothing else, so it gets no size gate, no budget, and no other verdict. The three exceptions called out by name are CMakeLists.txt, compile_flags.txt, and robots.txt, which are treated as machine formats rather than prose, so they skip the corpus even though they end in `.txt`. If your documentation is mostly loose text files, that is the only check they will ever see.
Size-only languages enter the gates but never a semantic verdict
There is a middle tier between judged languages and plain text. js, mjs, cjs, jsx, css, scss, less, vue, svelte, sh, bash, yml, and yaml are the size-only arm. They participate in the size gates, in the hard budget, and in the ratchet, which is the mechanism that tightens a limit rather than loosening it, and they never produce a semantic verdict. That means a stylesheet can be blocked for growing too long without the tool having any opinion about what is inside it. The judged languages are the wider set: Python, TypeScript and TSX, Rust, Go, Haskell, C, C++, Java, Lua, R, Markdown, and HTML, which is thirteen language codes over twelve tree sitter grammars, with one deliberate wrinkle, a `.h` file read as C++. The gap between the two tiers is the thing to check first, since it decides whether a file is judged or merely counted.
Same role advice is advisory by construction, and mentions are stored as hashes
The suggestion engine borrows nothing from a language model. Facts already in the index, names, shapes, callees, docs, structure, and literals, become term bags scored with integer BM25 at k1 of 6/5 and b of 3/4, and a role bit only holds when the name, callee, and shape channels agree. The `--widen` flag adds an in repository PPMI associative view. The result is advisory by construction: no exit code, no gate, no hook block, so it cannot refuse anything. The unmentioned declaration check works the same way and never turns a gate red. It rests on a mention universe built from every identifier in every text file, stored only as fnv1a64 hashes, which keeps raw names out of storage. The contrast with the two write time refusals is the design in one line: advice is computed, refusals are precise.
ce query and ce rules read the index as Datalog, and flow needs a passed exam
The index is also queryable. A Datalog program runs over its own facts, files, references, units, clones, mentions, and path classes, so `ce query` answers a single question and shows the derivation of every answer, while `ce rules` judges the assertions in a rules file and reports a violation as a witness row with its proof. The core parses, checks sorts, safety, and stratification, evaluates semi naively, and proves, while Rust does the lexing, assembles the fact tables, and labels ids back so no name crosses the wire. Dead code inside a function is handled the same way: Rust lowers each unit of a language its FlowSpec table knows into four integer tables covering units, statements, variables, and accesses, and the core builds the control flow graph to answer unreachable statements, dead stores, and unused locals and parameters. A flow finding becomes a verdict only in a language whose blind reviewed precision exam passed, and an unused parameter is always advice.
Editorial conclusion
CodeEraser fits a team that already reviews with a language model and is tired of reviewing the same function a third time. It is built for a repository with a declared layout, class based rules, and a pre-commit hook that everyone actually runs, and it is a poor fit if you want a one line lint or if your editor rewrites whole files. Before trusting a number, check which reading you are on: the stream reading and the full file write reading of the duplicate rate differ by an order of magnitude, and the timings on record were last measured on 2026-08-29.
Frequently asked questions
How do you use eraser AI with CodeEraser, and does it call a model?
CodeEraser is not a model based tool, and no model is in the loop anywhere in it. Verdict arithmetic happens in a Haskell core using exact integers and exact rationals, with a Rust frontend in front of it.
Is Eraser.io free, and what license does CodeEraser use?
That question is about a different product. CodeEraser is licensed Apache-2.0, its homepage is codeeraser.dev, and the surfaces it describes are ones you run yourself: ce precommit, ce commitmsg, a CI gate, and Claude Code hooks.
Which languages does CodeEraser judge, and how many are there?
Thirteen language codes over twelve tree sitter grammars: Python, TypeScript and TSX, Rust, Go, Haskell, C, C++, Java, Lua, R, Markdown, and HTML, with a .h file read as C++.
What exactly does CodeEraser refuse, and at what point?
Two refusals, both at write time before content reaches disk: a write that would introduce an exact T1/T2 clone the replaced content did not already carry, and a write leaving a file over 750 lines or over the line its [[rules.class]] declares.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/skymanbp-codeeraser)Star history chart for your README
[](https://hysenlabs.com/projects/skymanbp-codeeraser)