· English | 中文
An eraser against LLM-induced code & document entropy.
Long-lived LLM-assisted codebases drift the same way: the same function written twice, the same paragraph pasted into three files, files that only ever grow. CodeEraser stops that drift at the moment of writing and gates it in CI, with no model in the loop.
Two refusals happen before the content reaches disk: a write that would introduce an exact T1/T2 clone is denied at PreToolUse with the duplicated region named, and a write leaving a file over 750 lines (or its [[rules.class]] line) is denied the same way. Everything else is a report or a gate: the Stop audit refuses the turn, ce precommit / ce commitmsg refuse the commit, CI refuses the merge.
Scope. Judged: Python, TypeScript/TSX, Rust, Go, Haskell, C, C++, Java, Lua, R, Markdown and HTML (thirteen language codes, twelve tree-sitter grammars). Size-checked only: js/mjs/cjs/jsx, css/scss/less, vue, svelte, sh/bash, yml/yaml. Documentation duplication only: .txt. Faces: CLI · GUI (eleven screens) · Claude Code plugin (four hooks, one skill, one command, twenty-one read-only MCP tools) · pre-commit · CI.
| Without CodeEraser | With CodeEraser | |
|---|---|---|
| writes refused before they reached disk | 0 | 2 |
| duplicate clone blocks left behind | 4 | 0 |
| duplicated doc segments | 1 | 0 |
| removals still owed | 1 | 0 |
| check score | 871 | 979 |
One seven-step task, two identical copies of the seed; the only variable is whether CodeEraser is in the loop — the write-time guard, the Stop audit, and, once the audit refuses, the eraser acting on its own plan. Both runs still end red — not on the same things.
A scripted agent replays one seven-step task (discounts, a compact report, CSV and JSON output, money formatting) on demo/seed, a small invoicing service, once without CodeEraser and once with it. Every number above and every refusal below is ce's verbatim output.
A copied helper, refused before the file exists. Move 1 of the run above, on its own. The reason names the region the content duplicates and the ordering that would pass, so the refusal is actionable rather than a veto.
$ Write invoicer/discount.py
✗ ce: content for <work>/invoicer/discount.py duplicates 1 indexed region(s): invoicer/money.py:1-18 (89 tokens). Reuse the existing implementation instead of re-writing it. Moving it? Trim the source region first: the probe verifies against the current tree, and the same write then passes.One line, two mouths. ce.toml puts invoicer/** on file_lines_fail = 40. The write-time guard refuses the write that would cross it, and ce scan grades the same tree against the same number — one declaration, read by the hook and by CI.
$ Write invoicer/invoice.py
✗ ce: this write leaves <work>/invoicer/invoice.py at 93 lines, past the hard budget of 40 (plan §4.1). Split the file instead of growing it.
$ ce scan .
FAIL invoicer/invoice.py:1 file-lines = 51 (limit 40) [invoicer/invoice.py]
warn invoicer/report.py:1 file-lines = 35 (limit 30) [invoicer/report.py]
scanned 9 files / 19 functions — 1 warn, 1 fail -> FAIL (failed: hard_line)The full table, both transcripts and the JSON behind them are in demo/README.md, replayed byte for byte in CI; the first real interception is recorded in T1-INTERCEPT. The precision and recall points are frozen with their evaluation ledgers (EVAL-SET, EVAL-SET-LANGS).
Zoom and pan this diagram at codeeraser.dev/how/#verdict.
- Write-time clone guard: normalized tokens winnowed at k = 25, w = 26, so any shared run of 50+ tokens is caught; only novel duplication is charged (FPR-REPLAY).
- Near-miss (T3) clones: candidates from MinHash/LSH, judged in the core by tree edit distance at TSED ≥ 0.85.
- Documentation duplication that survives rewording: 5-word shingles, Jaccard ≥ 0.80 or a 50-word verbatim run.
- Liveness: per-language resolution ladders feed a reference graph; a four-way verdict comes back with a confidence code.
- Gate score: 0–1000 over seven axes, held by the ADR-006 shrink-only ratchet (growth needs
max(+2 %, +10 lines)or a named re-establish). - Deterministic: no model anywhere; text crosses only the local
ce→ce-corepipe, and the core opens no file.
The rest — same-role advice, Datalog queries and rules, intra-function dead code, merge suggestions, architecture, structure axes, tombstone residue, erase — is on codeeraser.dev/how and in the methodology booklets.
- Installer. Each release ships five GUI installers (NSIS / AppImage / dmg) bundling the GUI,
ceand thece-corejudgment core; the Windows installer runsce setup(AppImage / dmg users run it once), which wires Claude Code. The fifteen artifacts are unsigned by decision; verify withsha256sum -c --ignore-missing SHA256SUMS. - Homebrew / winget.
brew install skymanbp/codeeraser/codeeraser;winget install skymanbp.CodeEraseronce winget-pkgs merges. - Claude Code plugin.
/plugin marketplace add skymanbp/CodeEraser@release, then/plugin install codeeraser@codeeraser. - CLI only, or from source. Put
ceandce-coreside by side on PATH, orcargo install codeeraser, or build with GHC 9.14.1 + cabal;cefinds the core asCE_CORE_BIN→ ace-coresibling → PATH, and--core <path>wins. - Updating.
ce updatechecks the latest release against its two-phase pins and--yesreplaces the binaries (RELEASE).
| Command | What it reports / judges |
|---|---|
ce scan / ce dedup |
size and complexity metrics; T1/T2 clone blocks against the budget |
ce clone / ce docdup |
T3 near-miss clones; documentation duplication |
ce graph / ce deadcode |
reference sites and mentions; liveness verdicts |
ce churn / ce join / ce trend |
git-window churn; the three-signal join; score trajectory |
ce similar |
same-role units, ranked by the core; advice only |
ce query / ce rules |
Datalog over the index's facts; rules judged as a gate |
ce flow |
dead code inside functions, from the control-flow graph |
ce merge |
how a clone group would fold into one function; advice only |
ce arch |
directory layers, cycle cuts, clusters, change impact; advice only |
ce structure |
eight structure axes and split pricing |
ce check / ce baseline |
the ratchet and score floor, six named fail conditions |
ce erase |
the deterministic two-phase eraser; dry run by default |
ce update |
latest release vs this build; --yes installs it |
ce doctor / ce setup / ce eject / ce mcp |
machine state; Claude Code wiring; uninstall; the MCP server |
Output is English by default and Chinese under --lang zh, CE_LANG=zh or [ui] lang = "zh". [[rules.class]] in ce.toml gives one glob set its own lines and ratchet tolerance (ce.toml reference).
Every capability reaches the CLI, the GUI and the plugin with its MCP server, or is a named omission; the face_parity gate derives the sets from the code.
Capability × face table
| capability | report document (core catalogue) | CLI | GUI (screen · commands) | plugin (hooks · MCP · commands · skills) |
|---|---|---|---|---|
| size / complexity / readability metrics | scan |
ce scan |
reports, scan_report |
MCP scan |
| T1/T2 clone blocks | dedup |
ce dedup |
reports, candidates, dedup_report |
MCP check_duplication |
| T3 near-miss clones | clone |
ce clone |
reports, clone_report |
MCP clone |
| the unit universe T3 judges | clone-units |
ce clone --units |
reports, clone_report |
MCP clone |
| documentation duplication | docdup |
ce docdup |
reports, docdup_report |
MCP docdup |
| reference sites | sites |
ce graph --sites |
reports, sites_report |
MCP graph_sites |
| the mention universe | mentions |
ce graph --mentions |
— CLI only: the census behind the symbol advisory, which every face of deadcode carries |
— CLI only: the MCP deadcode tool carries the advisory the census feeds |
| liveness verdicts + symbol advisory | deadcode |
ce deadcode |
reports, deadcode_report |
MCP deadcode |
| graph screen (canvas + liveness) | graphscreen |
— GUI only: the canvas is a picture; the same judgment's CLI and MCP faces are deadcode |
graph, graphscreen_report |
— GUI only: the MCP face of the same judgment is deadcode |
| git-window churn | churn |
ce churn |
reports, churn_report |
MCP churn |
| three-signal join | join |
ce join |
candidates, join_report |
MCP join |
| tree-scale structure (split pricing) | structure |
ce structure |
structure, structure_report |
MCP structure |
| score trajectory | trend |
ce trend |
trend, trend_report |
MCP trend |
| score, ratchet and floor | check |
ce check |
score, check_report |
MCP check |
| same-role advisor (similar units, associative view) | similar |
ce similar |
similar, similar_report |
MCP similar_units |
| code query and architecture rules | query, rules |
ce query, ce query --prelude, ce rules |
query, query_report, query_prelude, rules_report |
MCP query, MCP rules |
| intra-function dead code (unreachable, dead stores, unused locals and parameters) | flow |
ce flow, ce flow --check |
reports, flow_report, flow_kinds |
MCP flow |
| clone merge suggestions (anti-unification) | merge |
ce merge |
reports, merge_report |
MCP merge_suggestions |
| architecture analysis (layers, cuts, clusters, impact) | arch |
ce arch |
reports, arch_report |
MCP architecture |
| baseline writes | — | ce baseline |
— CLI only: a machine surface never writes a baseline | — |
| erase plan | erase |
ce erase |
erase, erase_preview |
MCP erase, skill erase |
| erase apply | — | ce erase --apply |
erase, erase_apply |
— no MCP face: applying is a human act |
| erase audit log | erase-trail |
ce erase --log |
erase, erase_log_report |
MCP erase_log |
| machine state and guard-class tiers | — | ce doctor |
doctor, doctor_report |
MCP doctor |
| update check | — | ce update |
update, update_check |
MCP update_check, /codeeraser:update, hook SessionStart |
| update apply | — | ce update --yes |
update, update_apply |
— the plugin's copy is re-pinned by /plugin update codeeraser |
| write-time guard | — | ce probe --hook (guard · flow · tombstone) |
— hooks are the plugin's face; flow and tombstone judge the write inside the hook, so no report family of their own — their tiers ride every face's doctor | hook PreToolUse |
| asked-write settlement | — | ce settle --hook |
— hooks are the plugin's face | hook PostToolUse |
| stop audit / git hooks | — | ce audit --hook (guard · flow · tombstone), ce precommit (guard · flow · tombstone), ce commitmsg (guard · flow · tombstone) |
— hooks are the plugin's face; precommit and commitmsg are git's; tombstone judges the changeset inside them, its tier on every face's doctor | hook Stop |
| session health line | — | ce health --hook |
— hooks are the plugin's face | hook SessionStart |
| project daemon | — | ce daemon, ce ping |
— started lazily by every face | — |
| read-only report server | — | ce mcp |
— the plugin registers it | .mcp.json |
| uninstall | — | ce eject |
— CLI only | — |
| Claude Code wiring | — | ce setup, ce setup --unwire |
— CLI only: the Windows installer calls it, AppImage / dmg users run it once | — |
| root anchoring | — | — every command and hook anchors through root |
default_root, resolve_root |
— |
Zoom and pan this diagram at codeeraser.dev/#architecture.
- Rust 1.94.1 (edition 2024): tree-sitter 0.27 with twelve grammars, rusqlite 0.40 (index schema 17 / GRAPH_REV 23 / MENTION_REV 4), a per-project daemon.
- Haskell (GHC 9.14.1):
ce-core, every judgment family. Tauri 2: the GUI over the same crate. - One wire: NDJSON over stdio (proto 9.0.0, sixteen families); the daemon speaks its own protocol (2.3.0).
- Design rules: ADR-001 … ADR-009 (Rust frontend, parse layer in Rust, lazy daemon, cheap PreToolUse, two clone layers, shrink-only ratchet, pinned distribution, policy as Haskell data, derived documentation facts) in DEVELOPMENT_PLAN.
- Philosophy. Measure in Rust, decide and write in Haskell, print everywhere else. Nothing asks a model anything; hooks fail open and say so; documentation is generated or gated, including the nineteen-booklet methodology, and every push runs the seven product gates on this tree.
PreToolUse shapes behaviour; it is not a security wall (shell writes bypass it; the Stop audit and CI are the backstops). Hooks fail open on internal errors and record the degradation.
- Languages. AST judgment uses the twelve grammars above; Markdown, HTML and
.txtare judged by the documentation and graph rules; T4 clones are not promised. - Advice, never a verdict. Symbol-level liveness,
ce similar,ce mergeandce archnever fail a gate; a guard class staysobserveuntil it has its own false-positive record. - Lines not drawn.
cognitive_faildefaults to 0 (complexity only warns), andce structurehas no score floor. - Distribution. A release builds five targets;
ce setupwires nothing from an elevated account that is not the logged-in user (exit 13); judging this repository needs thecli/testssubmodule seated. - One wire line. A request line to the core is at most 134,217,728 bytes; larger requests split into batches, and an oversized item is refused by name, never truncated.
Scores are not comparable across a [[rules.class]] switch, across a named re-establish that moves softLine, or across these releases: v0.7.3 → v1.0.0, v1.2.0 → v1.3.0, v1.3.x → v1.4.0, v1.6.0 → v1.7.0, v1.7.4 → v1.8.0, v1.8.0 → v1.9.0 (1.9.1 is comparable with 1.9.0); the reasons are in CHANGELOG.
- CLI reference · ce.toml reference (generated from the binary and the config schema; drift reddens CI) · Methodology (nineteen booklets, cited to implementation lines) · structure axes · size advisory · erase contract · GUI reference · plugin · demo
- DEVELOPMENT_PLAN · EVAL-SET · EVAL-SET-SIMILAR · EVAL-SET-LANGS · EVAL-SET-FLOW · language-expansion · FIELD-TEST · PERF-BUDGET · FPR-REPLAY · FPR-TOMBSTONE · FPR-L2 · T1-INTERCEPT · contracts/VERSIONING.md · docs/RELEASE.md (wire SemVer and the two-phase release runbook)
- Website: codeeraser.dev · how it works · math · stack
Apache-2.0; see LICENSE. Third-party inventory in NOTICE (regenerated and gated in CI). The test suite lives in skymanbp/CodeEraser-tests; clone with --recurse-submodules. "CodeEraser"™ is a trademark of skymanbp; per Apache-2.0 §6 the license covers the code, not the name.