An eraser against LLM-induced code & document entropy. Support if you like it!
20
stars
584
commits
Rust
primary language
Sep 10, 2026
updated
· English | 中文
An eraser against LLM-induced code & document entropy.
Long-lived LLM-assisted codebases drift the same way: the same function implemented twice, the same paragraph pasted into three files, updates that arrive as appends, 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 anywhere. Two refusals happen at write time, before the content reaches disk. A write that would introduce an exact T1/T2 clone (duplication the replaced content did not already carry) is denied at PreToolUse, with the region it duplicates named and the ordering that passes taught; a write leaving a file over 750 lines, or over the line its [[rules.class]] declares, is denied the same way. Everything else is a report or a gate: the Stop audit refuses the turn, ce precommit and ce commitmsg refuse the commit, and the CI exit codes refuse the merge.
Scope. Judged languages: Python, TypeScript/TSX, Rust, Go, Haskell and Markdown (seven language codes over six tree-sitter grammars). Size-only arm: js/mjs/cjs/jsx, css/scss/less, html/htm, vue, svelte, sh/bash, yml/yaml. They enter the size gates, the hard budget and the ratchet, never a semantic verdict. Faces: CLI · GUI (eleven screens) · Claude Code plugin (three hooks, one skill, one command, sixteen read-only MCP tools) · pre-commit · CI.
ID, literals → LIT, comments dropped) are winnowed with k = 25, w = 26, so any shared run of 50+ tokens is guaranteed a shared fingerprint. The fingerprints live in a SQLite WAL index kept by a lazy per-project daemon; the PreToolUse probe answers in 48 ms p50 / 58 ms p95 on a two-file fixture, and the whole plugin chain in 0.50 s p95 as last measured on 2026-08-29, before the tombstone leg joined it. The guard charges only novel duplication: matches the replaced content already carried are subtracted, so on the live-stream reading it misfires on none of 719 production probes (0.00 per 500); the 2,761-event replay's full-file-write reading charges the 32 split-a-file intermediate states at 7.03 per 500; both readings are ledgered in FPR-REPLAY.--widen adds the in-repo PPMI associative view. Advisory by construction: no exit code, no gate, no hook block.CE_ACCEPT_BASELINE=1), and a knob edit stops ce check by name instead of moving every line.(removed) label, or a sentence carrying a backward-looking mark (no longer, used to, 曾经 …) together with the erased name. Rust measures the changeset's surfaces (the names it erased, its new labels, its new sentences); the core judges them sentence by sentence (a mark and an erased name together; a label needs the name alone) against a floor and the class's [tombstone] budget. PreToolUse, the Stop audit and ce precommit / ce commitmsg speak it, each only at the class's [tombstone] tier and only when the core answers over. Changelog-role documents are exempt by path, by ledger shape, by a segment with three version witnesses or by a [tombstone] ledger declaration, and the false-positive rate is replayed on git history before any default moves (FPR-TOMBSTONE).| 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.
The task (add discounts, a compact report, CSV and JSON output, money formatting in the API) is replayed by a scripted agent on demo/seed, a small invoicing service in Python and TypeScript. The seed is measured first, so every finding below was written by the task. Each loop then runs to its end; with nothing in the loop nothing refuses anything, so that one ends at the last write. Every verdict is the verbatim output of ce, and both trees are measured by the same six commands.
| Without CodeEraser | With CodeEraser | |
|---|---|---|
| The seed, by the same six gates: clone blocks · doc twins · dead files | 0 · 0 · 0 | 0 · 0 · 0 |
| Writes that landed | 7 of 7 | 5 of 7 |
| Denied at PreToolUse | 0 | 2 |
| Stop audit | not in the loop | blocked — this session's edits leave 2 duplicate block(s) touching changed files (net +105 LOC)… |
| The repair the audit named | — | written, and the audit goes silent |
ce erase --apply | — | 1 row removed: the verbatim doc twin |
ce check score (ratchet) | 871/1000 — FAIL: ratchet_over, discrete_added | 979/1000 — FAIL: ratchet_over |
T1/T2 clone blocks (ce dedup --check, budget 0) | 4 (FAIL) | 0 (pass) |
near-miss clone pairs (ce clone) | 4 | 0 |
duplicated doc segments (ce docdup --check) | 1 (FAIL) | 0 (pass) |
dead files (ce deadcode --check) | 3 (FAIL) | 2 (FAIL) |
provably-safe removals still planned (ce erase --check) | 1 (FAIL) | 0 (pass) |
The two denied writes are copies of an existing helper. The compact renderer that slipped past is the honest boundary, since a full-file rewrite copies its own blocks and nothing is novel at write time. It is what the Stop audit refuses the turn over, naming both blocks; the repair that answers it is the only write made because a gate asked rather than because the task did. What stays red is what a person has to settle: invoicer/invoice.py is 93 lines against a tolerated ceiling of 61, which the ratchet holds open for a named re-establish instead of absorbing silently, and two files are unreferenced: the new page nothing links to, and the renderer the CLI stopped importing on its way to JSON. Both transcripts, both SVGs and the JSON behind this table are generated by demo/run.js and re-checked byte for byte in CI (demo_replay). The first real interception, recorded the day it happened: T1-INTERCEPT.
Two moments from close up on the same seed; the second adds one ce.toml declaration.
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)
| percentile | check_warm | deadcode_warm | dedup_cold | dedup_warm | docdup_warm | hook_probe | scan |
|---|---|---|---|---|---|---|---|
| p50 ms | 2387 | 1166 | 10293 | 495 | 1408 | 48 | 683 |
| p95 ms | 2396 | 2873 | 10326 | 509 | 1428 | 58 | 2622 |
Every value is generated from contracts/bench/bench.json; the test rejects hand edits to this block. The current release, v1.7.1, ships the same measured code as the newest row and gets none of its own. Full replay notes and per-version series · Complete website dashboard
Latency rows are release-build replays on one fixed host, comparable version to version only. The precision and recall points are frozen with their evaluation ledgers (EVAL-SET) and rendered on BENCH; comparators (jscpd, similarity-*) are named with the exact version measured.
Installer. Each release ships five GUI installers (NSIS setup.exe / AppImage / dmg) bundling the GUI, ce and the ce-core judgment core. The Windows installer puts the install dir on PATH and runs ce setup; AppImage and dmg users run ce setup once themselves; it finds Claude Code, wires the plugin below and says whether that ce's directory is on PATH. The fifteen artifacts (ce and ce-core per target plus the installers) and SHA256SUMS are unsigned by decision; verify with sha256sum -c --ignore-missing SHA256SUMS.
Homebrew / winget. The release generates a Homebrew formula and the three winget manifests under packaging/ from the same pinned manifest it verified, never edited by hand, and publishes them when the repository's tap and winget tokens are configured: brew install skymanbp/codeeraser/codeeraser (macOS and Linux, ce + ce-core from the pinned assets) and winget install skymanbp.CodeEraser (the Windows installer, once winget-pkgs merges).
Claude Code plugin. ce setup registers the marketplace at this repository's release branch (fast-forwarded to every published tag, so an install follows releases, not main) and installs the plugin; by hand: /plugin marketplace add skymanbp/CodeEraser@release, then /plugin install codeeraser@codeeraser. The starter resolves ce and ce-core by pin: a matching local or PATH copy, then a pinned download, then an unverified PATH binary that says so.
CLI only, or from source. Download ce-<ver>-<platform> and ce-core-<ver>-<platform> (x86_64-windows / x86_64-linux / aarch64-macos / x86_64-macos / aarch64-linux), rename them ce / ce-core and put them side by side on PATH; or cargo install codeeraser and place a ce-core beside it; or build both with the pinned Rust toolchain (rust-toolchain.toml) and GHC 9.14.1 + cabal: cd core && cabal build all && export CE_CORE_BIN=$(cabal list-bin ce-core), then cd .. && cargo install --path cli. Core resolution is one chain everywhere: CE_CORE_BIN → a ce-core sibling → PATH; --core <path> wins.
| Command | What it reports / judges |
|---|---|
ce scan / ce dedup | size / complexity / readability metrics graded against the file's own lines; T1/T2 clone blocks, --check against the budget, a digest-keyed warm cache; both --format sarif |
ce clone / ce docdup | T3 near-miss clones; documentation duplication |
ce graph / ce deadcode | reference sites and the mention universe; liveness verdicts + the symbol advisory |
ce churn / ce join / ce trend | git-window churn; the three-signal join; score trajectory (progress on stderr) |
ce similar | the same-role advisor: the units most like one unit (--at file:line, --unit) or a text, in the core's order with its role bit; --widen adds the in-repo PPMI associative view. Advisory, never a verdict |
ce structure | eight axes; --split-candidates prices the best seam of every file past the soft line |
ce check / ce baseline | the ADR-006 ratchet and score floor, six fail conditions each named on the console; baseline persists only at the root and under a named act |
ce erase | the deterministic two-phase eraser; dry-run default, --apply behind clean-worktree preconditions, --log reads the applied-erase trail |
ce update | latest release vs this build, exit 0 / 1 / 2; --yes replaces ce + ce-core after both pins verify, --installer saves the verified GUI installer |
ce doctor / ce setup / ce eject / ce mcp | machine state; wire this machine's Claude Code to the plugin (exit 0 wired / 5 kept / 10 no Claude Code / 11–12 failed / 13 elevated account is not the logged-in user; --unwire removes only what it added); per-project uninstall; the read-only MCP server |
Console output, --help and the hooks' own refusal sentences are English by default and Chinese under --lang zh, CE_LANG=zh or the project's [ui] lang = "zh" in ce.toml, in that order of precedence, and --help reads only the first two; JSON schemas and the FAIL/pass vocabulary are never translated. [[rules.class]] in ce.toml gives one glob set its own size and complexity lines and ratchet tolerance (0 = may not grow), and the same line is read by the score, the ce scan ladder and the PreToolUse budget (ce.toml reference).
Updating. Release builds are two-phase: draft assets hashed, the pins committed to plugin/bin/manifest.env, and only then the tag verifies the same bytes (RELEASE), which is what ce update and the tag job's installer check both read. ce update reads the latest tag and that tag's committed manifest.env; the verdict is the exit code, and --yes acts only where nothing else keeps a ledger of the binary. A copy the plugin bound is re-pinned by /plugin update codeeraser; a cargo install by cargo install codeeraser; the GUI app itself by the installer --installer saves. The plugin's SessionStart line announces a newer release once a day (CE_UPDATE_CHECK=0 turns that off); the GUI has an update screen; /codeeraser:update runs the check from Claude Code.
Every capability is claimed once in this table, the sets are derived from the code (clap's enum, the Tauri roster, the MCP catalog, hooks.json, plugin/commands, plugin/skills), and a CI gate (face_parity) refuses a face nobody wrote down or a claim nobody shipped. Deliberate omissions are rows, not silence.
| capability | CLI | GUI (screen · commands) | plugin (hooks · MCP · commands · skills) |
|---|---|---|---|
| size / complexity / readability metrics | ce scan | reports, scan_report | MCP scan |
| T1/T2 clone blocks | ce dedup | reports, dedup_report | MCP check_duplication |
| T3 near-miss clones | ce clone | reports, clone_report | MCP clone |
| documentation duplication | ce docdup | reports, docdup_report | MCP docdup |
| reference sites and the mention universe | ce graph | reports, sites_report | MCP graph_sites |
| liveness verdicts + symbol advisory | ce deadcode | graph, graphcanvas_report, deadcode_report | MCP deadcode |
| git-window churn | ce churn | candidates, churn_report | MCP churn |
| three-signal join | ce join | candidates, join_report | MCP join |
| tree-scale structure (split pricing) | ce structure | structure, structure_report | MCP structure |
| score trajectory | ce trend | trend, trend_report | MCP trend |
| score, ratchet and floor | ce check | score, check_report | MCP check |
| same-role advisor (similar units, associative view) | ce similar | similar, similar_report | MCP similar_units |
| baseline writes | ce baseline | — CLI only: a machine surface never writes a baseline | — |
| erase plan | 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 | ce erase --log | erase, erase_log_report | MCP erase_log |
| machine state | 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 | — hooks are the plugin's face | hook PreToolUse |
| stop audit / git hooks | ce audit --hook, ce precommit, ce commitmsg | — hooks are the plugin's face; precommit and commitmsg are git's | 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 | — |
| bench dashboard | — compiled-in series; README and site carry the same block | bench, bench_doc | — |
| root anchoring | — every command and hook anchors through root | default_root, resolve_root | — |
codeeraser crate carries tree-sitter 0.27 with six grammars, rusqlite 0.40 (bundled SQLite, WAL, index schema 16 / GRAPH_REV 15 / MENTION_REV 2), the ignore walker, interprocess named pipes / Unix sockets, clap, serde, sha2 for the updater's pins.-Wall -Werror): ce-core, every judgment family, a frozen dependency graph.ce and ce-core as sidecars.interprocess; a protocol-major skew is a named refusal, never a guess.deny only with its own false-positive record in CHANGELOG. Documentation is generated or gated: the CLI and config references, the fifteen-booklet methodology with machine-checked citations, the numbers this page derives from the code, the two diagrams above, the bench block, the demo, the site's terminal block and its GUI screenshots, the parity table, the NOTICE. This repository is its own first user, and every push runs the six product gates on this tree.v1.7.0 scope. The v2.29 plan re-opened, item by item, the deferred work the 2026-08-31 close-out had ruled not-done (plan v2.22, 45 items across three bundles), keeping only what earns its cost. The limits below are the positions that survived.
Limits. 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. Binaries are unsigned.
/// are comments, not docstrings; T4 clones are not promised. churn, join and trend are minute-scale.ce deadcode says so on its own last line. The same-role advisor is an advisor and never a verdict: ce similar, MCP similar_units and the GUI similar screen rank and rebut nothing. ce similar always exits 0, ce check never reads the family, and the Stop audit's advisory row only reaches the observe ledger. A guard class stays observe until it carries its own false-positive record.cognitive_fail defaults to 0, so a tangled function warns and nothing more until a repository declares one, and the write-time hook never judges complexity. ce structure has no score floor, so that family reports and gates nothing.x86_64-windows, x86_64-linux, aarch64-macos, x86_64-macos, aarch64-linux); on the two newer ones a plugin starter reading a manifest older than that finds an empty pin and falls back to a PATH ce or a source install. ce setup wires nothing when the account running it is not the logged-in user (exit 13); run it unelevated from your own account. Judging this repository needs the cli/tests submodule seated (it is a reader of the tree, never a measured part).[tombstone] terms list is the way out. It measures CJK names only at word boundaries: a wide Chinese name is seen where it stands alone as a heading, a list lead or an identifier, and is bound in prose by substring.[[rules.class]] switch, across the v0.7.3 → v1.0.0 density-charge rescoring, across the v1.2.0 → v1.3.0 test-submodule move, across the v1.3.x → v1.4.0 recursion increment, or across the v1.6.0 → v1.7.0 re-denomination of the clone and docdup axes (files touched over each axis's own universe, with docdup pairs reaching ce check for the first time). ce structure's own 0–1000 is likewise not comparable across v1.6.0 → v1.7.0: the modularity axis is new, and every axis enters the equal-weight fold. Scores are also not comparable across a named re-establish that moves the baseline's softLine: the size axis charges against that repo-relative line rather than a constant, and this repository's has moved with every named re-establish since 304 at v0.7.3 (372 at v1.4.1, three points apart when both lines are applied to the v1.4.1 tree).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.
579 commits
5 commits
Rust
74.5%
Haskell
14.9%
JavaScript
7.8%
CSS
1.3%
An eraser against LLM-induced code & document entropy. Support if you like it!
20
stars
584
commits
Rust
primary language
Sep 10, 2026
updated
· English | 中文
An eraser against LLM-induced code & document entropy.
Long-lived LLM-assisted codebases drift the same way: the same function implemented twice, the same paragraph pasted into three files, updates that arrive as appends, 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 anywhere. Two refusals happen at write time, before the content reaches disk. A write that would introduce an exact T1/T2 clone (duplication the replaced content did not already carry) is denied at PreToolUse, with the region it duplicates named and the ordering that passes taught; a write leaving a file over 750 lines, or over the line its [[rules.class]] declares, is denied the same way. Everything else is a report or a gate: the Stop audit refuses the turn, ce precommit and ce commitmsg refuse the commit, and the CI exit codes refuse the merge.
Scope. Judged languages: Python, TypeScript/TSX, Rust, Go, Haskell and Markdown (seven language codes over six tree-sitter grammars). Size-only arm: js/mjs/cjs/jsx, css/scss/less, html/htm, vue, svelte, sh/bash, yml/yaml. They enter the size gates, the hard budget and the ratchet, never a semantic verdict. Faces: CLI · GUI (eleven screens) · Claude Code plugin (three hooks, one skill, one command, sixteen read-only MCP tools) · pre-commit · CI.
ID, literals → LIT, comments dropped) are winnowed with k = 25, w = 26, so any shared run of 50+ tokens is guaranteed a shared fingerprint. The fingerprints live in a SQLite WAL index kept by a lazy per-project daemon; the PreToolUse probe answers in 48 ms p50 / 58 ms p95 on a two-file fixture, and the whole plugin chain in 0.50 s p95 as last measured on 2026-08-29, before the tombstone leg joined it. The guard charges only novel duplication: matches the replaced content already carried are subtracted, so on the live-stream reading it misfires on none of 719 production probes (0.00 per 500); the 2,761-event replay's full-file-write reading charges the 32 split-a-file intermediate states at 7.03 per 500; both readings are ledgered in FPR-REPLAY.--widen adds the in-repo PPMI associative view. Advisory by construction: no exit code, no gate, no hook block.CE_ACCEPT_BASELINE=1), and a knob edit stops ce check by name instead of moving every line.(removed) label, or a sentence carrying a backward-looking mark (no longer, used to, 曾经 …) together with the erased name. Rust measures the changeset's surfaces (the names it erased, its new labels, its new sentences); the core judges them sentence by sentence (a mark and an erased name together; a label needs the name alone) against a floor and the class's [tombstone] budget. PreToolUse, the Stop audit and ce precommit / ce commitmsg speak it, each only at the class's [tombstone] tier and only when the core answers over. Changelog-role documents are exempt by path, by ledger shape, by a segment with three version witnesses or by a [tombstone] ledger declaration, and the false-positive rate is replayed on git history before any default moves (FPR-TOMBSTONE).| 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.
The task (add discounts, a compact report, CSV and JSON output, money formatting in the API) is replayed by a scripted agent on demo/seed, a small invoicing service in Python and TypeScript. The seed is measured first, so every finding below was written by the task. Each loop then runs to its end; with nothing in the loop nothing refuses anything, so that one ends at the last write. Every verdict is the verbatim output of ce, and both trees are measured by the same six commands.
| Without CodeEraser | With CodeEraser | |
|---|---|---|
| The seed, by the same six gates: clone blocks · doc twins · dead files | 0 · 0 · 0 | 0 · 0 · 0 |
| Writes that landed | 7 of 7 | 5 of 7 |
| Denied at PreToolUse | 0 | 2 |
| Stop audit | not in the loop | blocked — this session's edits leave 2 duplicate block(s) touching changed files (net +105 LOC)… |
| The repair the audit named | — | written, and the audit goes silent |
ce erase --apply | — | 1 row removed: the verbatim doc twin |
ce check score (ratchet) | 871/1000 — FAIL: ratchet_over, discrete_added | 979/1000 — FAIL: ratchet_over |
T1/T2 clone blocks (ce dedup --check, budget 0) | 4 (FAIL) | 0 (pass) |
near-miss clone pairs (ce clone) | 4 | 0 |
duplicated doc segments (ce docdup --check) | 1 (FAIL) | 0 (pass) |
dead files (ce deadcode --check) | 3 (FAIL) | 2 (FAIL) |
provably-safe removals still planned (ce erase --check) | 1 (FAIL) | 0 (pass) |
The two denied writes are copies of an existing helper. The compact renderer that slipped past is the honest boundary, since a full-file rewrite copies its own blocks and nothing is novel at write time. It is what the Stop audit refuses the turn over, naming both blocks; the repair that answers it is the only write made because a gate asked rather than because the task did. What stays red is what a person has to settle: invoicer/invoice.py is 93 lines against a tolerated ceiling of 61, which the ratchet holds open for a named re-establish instead of absorbing silently, and two files are unreferenced: the new page nothing links to, and the renderer the CLI stopped importing on its way to JSON. Both transcripts, both SVGs and the JSON behind this table are generated by demo/run.js and re-checked byte for byte in CI (demo_replay). The first real interception, recorded the day it happened: T1-INTERCEPT.
Two moments from close up on the same seed; the second adds one ce.toml declaration.
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)
| percentile | check_warm | deadcode_warm | dedup_cold | dedup_warm | docdup_warm | hook_probe | scan |
|---|---|---|---|---|---|---|---|
| p50 ms | 2387 | 1166 | 10293 | 495 | 1408 | 48 | 683 |
| p95 ms | 2396 | 2873 | 10326 | 509 | 1428 | 58 | 2622 |
Every value is generated from contracts/bench/bench.json; the test rejects hand edits to this block. The current release, v1.7.1, ships the same measured code as the newest row and gets none of its own. Full replay notes and per-version series · Complete website dashboard
Latency rows are release-build replays on one fixed host, comparable version to version only. The precision and recall points are frozen with their evaluation ledgers (EVAL-SET) and rendered on BENCH; comparators (jscpd, similarity-*) are named with the exact version measured.
Installer. Each release ships five GUI installers (NSIS setup.exe / AppImage / dmg) bundling the GUI, ce and the ce-core judgment core. The Windows installer puts the install dir on PATH and runs ce setup; AppImage and dmg users run ce setup once themselves; it finds Claude Code, wires the plugin below and says whether that ce's directory is on PATH. The fifteen artifacts (ce and ce-core per target plus the installers) and SHA256SUMS are unsigned by decision; verify with sha256sum -c --ignore-missing SHA256SUMS.
Homebrew / winget. The release generates a Homebrew formula and the three winget manifests under packaging/ from the same pinned manifest it verified, never edited by hand, and publishes them when the repository's tap and winget tokens are configured: brew install skymanbp/codeeraser/codeeraser (macOS and Linux, ce + ce-core from the pinned assets) and winget install skymanbp.CodeEraser (the Windows installer, once winget-pkgs merges).
Claude Code plugin. ce setup registers the marketplace at this repository's release branch (fast-forwarded to every published tag, so an install follows releases, not main) and installs the plugin; by hand: /plugin marketplace add skymanbp/CodeEraser@release, then /plugin install codeeraser@codeeraser. The starter resolves ce and ce-core by pin: a matching local or PATH copy, then a pinned download, then an unverified PATH binary that says so.
CLI only, or from source. Download ce-<ver>-<platform> and ce-core-<ver>-<platform> (x86_64-windows / x86_64-linux / aarch64-macos / x86_64-macos / aarch64-linux), rename them ce / ce-core and put them side by side on PATH; or cargo install codeeraser and place a ce-core beside it; or build both with the pinned Rust toolchain (rust-toolchain.toml) and GHC 9.14.1 + cabal: cd core && cabal build all && export CE_CORE_BIN=$(cabal list-bin ce-core), then cd .. && cargo install --path cli. Core resolution is one chain everywhere: CE_CORE_BIN → a ce-core sibling → PATH; --core <path> wins.
| Command | What it reports / judges |
|---|---|
ce scan / ce dedup | size / complexity / readability metrics graded against the file's own lines; T1/T2 clone blocks, --check against the budget, a digest-keyed warm cache; both --format sarif |
ce clone / ce docdup | T3 near-miss clones; documentation duplication |
ce graph / ce deadcode | reference sites and the mention universe; liveness verdicts + the symbol advisory |
ce churn / ce join / ce trend | git-window churn; the three-signal join; score trajectory (progress on stderr) |
ce similar | the same-role advisor: the units most like one unit (--at file:line, --unit) or a text, in the core's order with its role bit; --widen adds the in-repo PPMI associative view. Advisory, never a verdict |
ce structure | eight axes; --split-candidates prices the best seam of every file past the soft line |
ce check / ce baseline | the ADR-006 ratchet and score floor, six fail conditions each named on the console; baseline persists only at the root and under a named act |
ce erase | the deterministic two-phase eraser; dry-run default, --apply behind clean-worktree preconditions, --log reads the applied-erase trail |
ce update | latest release vs this build, exit 0 / 1 / 2; --yes replaces ce + ce-core after both pins verify, --installer saves the verified GUI installer |
ce doctor / ce setup / ce eject / ce mcp | machine state; wire this machine's Claude Code to the plugin (exit 0 wired / 5 kept / 10 no Claude Code / 11–12 failed / 13 elevated account is not the logged-in user; --unwire removes only what it added); per-project uninstall; the read-only MCP server |
Console output, --help and the hooks' own refusal sentences are English by default and Chinese under --lang zh, CE_LANG=zh or the project's [ui] lang = "zh" in ce.toml, in that order of precedence, and --help reads only the first two; JSON schemas and the FAIL/pass vocabulary are never translated. [[rules.class]] in ce.toml gives one glob set its own size and complexity lines and ratchet tolerance (0 = may not grow), and the same line is read by the score, the ce scan ladder and the PreToolUse budget (ce.toml reference).
Updating. Release builds are two-phase: draft assets hashed, the pins committed to plugin/bin/manifest.env, and only then the tag verifies the same bytes (RELEASE), which is what ce update and the tag job's installer check both read. ce update reads the latest tag and that tag's committed manifest.env; the verdict is the exit code, and --yes acts only where nothing else keeps a ledger of the binary. A copy the plugin bound is re-pinned by /plugin update codeeraser; a cargo install by cargo install codeeraser; the GUI app itself by the installer --installer saves. The plugin's SessionStart line announces a newer release once a day (CE_UPDATE_CHECK=0 turns that off); the GUI has an update screen; /codeeraser:update runs the check from Claude Code.
Every capability is claimed once in this table, the sets are derived from the code (clap's enum, the Tauri roster, the MCP catalog, hooks.json, plugin/commands, plugin/skills), and a CI gate (face_parity) refuses a face nobody wrote down or a claim nobody shipped. Deliberate omissions are rows, not silence.
| capability | CLI | GUI (screen · commands) | plugin (hooks · MCP · commands · skills) |
|---|---|---|---|
| size / complexity / readability metrics | ce scan | reports, scan_report | MCP scan |
| T1/T2 clone blocks | ce dedup | reports, dedup_report | MCP check_duplication |
| T3 near-miss clones | ce clone | reports, clone_report | MCP clone |
| documentation duplication | ce docdup | reports, docdup_report | MCP docdup |
| reference sites and the mention universe | ce graph | reports, sites_report | MCP graph_sites |
| liveness verdicts + symbol advisory | ce deadcode | graph, graphcanvas_report, deadcode_report | MCP deadcode |
| git-window churn | ce churn | candidates, churn_report | MCP churn |
| three-signal join | ce join | candidates, join_report | MCP join |
| tree-scale structure (split pricing) | ce structure | structure, structure_report | MCP structure |
| score trajectory | ce trend | trend, trend_report | MCP trend |
| score, ratchet and floor | ce check | score, check_report | MCP check |
| same-role advisor (similar units, associative view) | ce similar | similar, similar_report | MCP similar_units |
| baseline writes | ce baseline | — CLI only: a machine surface never writes a baseline | — |
| erase plan | 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 | ce erase --log | erase, erase_log_report | MCP erase_log |
| machine state | 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 | — hooks are the plugin's face | hook PreToolUse |
| stop audit / git hooks | ce audit --hook, ce precommit, ce commitmsg | — hooks are the plugin's face; precommit and commitmsg are git's | 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 | — |
| bench dashboard | — compiled-in series; README and site carry the same block | bench, bench_doc | — |
| root anchoring | — every command and hook anchors through root | default_root, resolve_root | — |
codeeraser crate carries tree-sitter 0.27 with six grammars, rusqlite 0.40 (bundled SQLite, WAL, index schema 16 / GRAPH_REV 15 / MENTION_REV 2), the ignore walker, interprocess named pipes / Unix sockets, clap, serde, sha2 for the updater's pins.-Wall -Werror): ce-core, every judgment family, a frozen dependency graph.ce and ce-core as sidecars.interprocess; a protocol-major skew is a named refusal, never a guess.deny only with its own false-positive record in CHANGELOG. Documentation is generated or gated: the CLI and config references, the fifteen-booklet methodology with machine-checked citations, the numbers this page derives from the code, the two diagrams above, the bench block, the demo, the site's terminal block and its GUI screenshots, the parity table, the NOTICE. This repository is its own first user, and every push runs the six product gates on this tree.v1.7.0 scope. The v2.29 plan re-opened, item by item, the deferred work the 2026-08-31 close-out had ruled not-done (plan v2.22, 45 items across three bundles), keeping only what earns its cost. The limits below are the positions that survived.
Limits. 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. Binaries are unsigned.
/// are comments, not docstrings; T4 clones are not promised. churn, join and trend are minute-scale.ce deadcode says so on its own last line. The same-role advisor is an advisor and never a verdict: ce similar, MCP similar_units and the GUI similar screen rank and rebut nothing. ce similar always exits 0, ce check never reads the family, and the Stop audit's advisory row only reaches the observe ledger. A guard class stays observe until it carries its own false-positive record.cognitive_fail defaults to 0, so a tangled function warns and nothing more until a repository declares one, and the write-time hook never judges complexity. ce structure has no score floor, so that family reports and gates nothing.x86_64-windows, x86_64-linux, aarch64-macos, x86_64-macos, aarch64-linux); on the two newer ones a plugin starter reading a manifest older than that finds an empty pin and falls back to a PATH ce or a source install. ce setup wires nothing when the account running it is not the logged-in user (exit 13); run it unelevated from your own account. Judging this repository needs the cli/tests submodule seated (it is a reader of the tree, never a measured part).[tombstone] terms list is the way out. It measures CJK names only at word boundaries: a wide Chinese name is seen where it stands alone as a heading, a list lead or an identifier, and is bound in prose by substring.[[rules.class]] switch, across the v0.7.3 → v1.0.0 density-charge rescoring, across the v1.2.0 → v1.3.0 test-submodule move, across the v1.3.x → v1.4.0 recursion increment, or across the v1.6.0 → v1.7.0 re-denomination of the clone and docdup axes (files touched over each axis's own universe, with docdup pairs reaching ce check for the first time). ce structure's own 0–1000 is likewise not comparable across v1.6.0 → v1.7.0: the modularity axis is new, and every axis enters the equal-weight fold. Scores are also not comparable across a named re-establish that moves the baseline's softLine: the size axis charges against that repo-relative line rather than a constant, and this repository's has moved with every named re-establish since 304 at v0.7.3 (372 at v1.4.1, three points apart when both lines are applied to the v1.4.1 tree).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.
579 commits
5 commits
Rust
74.5%
Haskell
14.9%
JavaScript
7.8%
CSS
1.3%