CodeEraser

The analysis track

The analysis track lets the reader ask, over the same index and judged in the same core.

Under the same commitments

The families on the how page each judge one shape of entropy the tree already carries: a clone, a dead file, a duplicated paragraph, a unit that plays another's part. The analysis track lets the reader ask — over the same index, judged in the same core, under the same commitments: every answer is arithmetic over measured facts, there is no model and no floating point between the evidence and the verdict, and every constant is cited to the source line that implements it.

Its families continue the numbering from the fifteenth and get a booklet each; the short form is below, the full derivation lives in docs/reference/methodology.md, which every heading here links into.

The analysis families: questions the index can answer

A judgment family measures a fact and compares it with a threshold that is written down. An analysis family takes a program — a question, or a file of rules — and answers it over the facts the index already holds, so the threshold is the reader's own sentence. The division of labour does not move: Rust measures and labels, the Haskell core judges, and nothing but integers crosses the wire between them.

16Code query and architecture rules — Datalog over the index's facts

Answers a question in CE Datalog over the facts the index already holds — files, directories, references, units, complexity, judged clone and docdup pairs, mentions, path classes — with every answer's derivation, and judges a rules file's assertions as a gate: a violation is a witness row with its proof, and ce rules exits 1 on the first.

clause := atom ':-' body '.'  |  '?-' body '.'  |  'assert' atom ':-' body '.'
lit    := atom | 'not' atom | expr op expr | Var '=' expr | Var '=' agg(Vars : body)
prelude: entry(N) :- role(N, _).   reach(M) :- reach(N), ref(N, M, K, _), K != asset, K != refdef.
         dead(F) :- file(F), not reach(F).   depends · same_dir · dir_ref
strata : a head sits above every negated or aggregated body predicate; a negative edge inside an SCC is a program error
eval   : semi-naive per stratum over lazy bitmask indexes; the first derivation of a tuple is its provenance
Derivation and constants

The measuring side lexes the text into a [kind, value] token stream, assembles only the fact tables the program names from its own index, and sends both; names go up as fnv1a64 hashes and request-local ids, never as text, and come back the same way to be labelled through the request's own tables. The core parses (a hand-written recursive descent), checks arity, sorts (a union-find over variables, predicate positions and constant sorts), safety (range restriction: a variable is read only after a positive atom or a binding bound it) and stratification, evaluates one stratum at a time, and answers every goal's tuples, every assertion's violations, the proof rows and the program's errors — each error at a token the measuring side maps back to line:column. A proof is a whole tree or nothing: under the node budget a tree that would not fit is left out and counted, never cut. Tokens and fact rows are priced before judging and answer a complete degraded reply; derived tuples abort the evaluation whole; a core without the family is a named non-judgment in the document. The built-in dead(F) is ce deadcode's file-tier verdict restated as a rule, and this repository judges itself with nine assertions in ce.rules as CI's seventh dogfood leg.

  • tokenCap 65536
  • factCap 4194304
  • derivedCap 2097152
  • proofCap 16384
  • idbFloor 1000
  • schema ce.query-report/0.1.0
  • since proto 7.3.0
  • wire query/1

17Intra-function dead code — the flow graph, reachability and liveness

Finds the dead code inside one function: statements no path reaches, stores no path reads, locals nothing reads, and parameters nothing reads as advice. ce flow lists them, the guard counts the ones a write brings in, and the Stop line records them without ever blocking.

tables : stmts [u, seq, parent, kind, flags, aux] · vars [u, v, declSeq, flags] · uses [u, seq, v, mode]
cfg    : structured kinds unfolded; a try body's every statement may reach each catch; one finally hub per try
kind 0 : unreachable  = no path from the entry, one finding per maximal run of seqs
kind 1 : dead_store   = a write no path reads before the next write or the exit (backward liveness)
kind 2 : unused_local = no read · kind 3 : unused_param = no read (advice, never judged)
skip   : a unit with a dynamic statement is not judged in part, and counted
Derivation and constants

The measuring side lowers every unit of ten languages (Python, TypeScript and TSX, Rust, Go, C, C++, Java, Lua, R) into four integer tables by a FlowSpec table per language; no name, path or source text crosses. The core builds the control-flow graph, walks reachability from the entry and backward liveness to a fixpoint, and answers [unit, kind, seq, var, seqEnd] rows the measuring side labels again. Every approximation only adds paths — a finally is one hub whose successors are the union of the pending targets, expression-position exits are not lowered — so a finding can be missed, never invented. A finding is judged only in a language whose precision exam passed (blind review, no false positive in any kind the gate reads). All ten are in: the docs regenerated on the fixed lowering (a second exam generation for C++, R, Rust and TypeScript) read judged for Python, TypeScript, TSX, Rust, Go, C, C++, Java, Lua and R with no false positive in the gated kinds; an unused parameter is advice everywhere. The guard class [flow] tier ships at observe, counting novel findings as a multiset difference on (unit, kind, variable) so a moved finding is not the write's doing.

  • rowCap 524288
  • schema ce.flow-report/0.1.0
  • since proto 7.4.0
  • wire flow/1

18Clone merge suggestions — anti-unification over the clone families' trees

Reads how each clone group would fold into one function: the places its members differ become parameters, and the answer is the parameter count, the member worth keeping, the lines the merge would save, and whether it is feasible — with the reason when it is not. Advice: no gate reads it, ce merge exits 0 with a document.

groups : T1/T2 dedup families (whole units when one unit fills ≥ 9/10 of each member, else fragments trimmed to the members' common shape) · T3 judged pairs
trees  : clone/1's postorder (lab, lld), extras (comments) left out, + leaf = fnv1a64 of a leaf's bytes (0 inside) + slot 0 stmt · 1 expr · 2 type · 3 name · 4 other
align  : T1/T2 isomorphic, holes = differing leaves; T3 the tree-edit mapping narrowed top-down, relabels and unequal gaps are holes
params : one per distinct value vector; feasible iff every hole is an expression or a name, params ≤ 6 and savings > 0
reason : 3 a gap across statements, 1 position (a statement or other), 2 a type position, 4 more than six parameters, 5 no line saved
kept   : the member whose file has the greatest in-degree; savings = Σ lines − (skeleton lines + members × 1), a fragment's lines its kept run's
Derivation and constants

The measuring side gathers the groups, builds each member's tree in clone/1's encoding with two more integer columns — a leaf's source hash and its position class, read off one table per language that reads the flow tables — and labels the answer back with every member's text at every parameter, a gap's whole forest included. Its trees leave comments out, and a fragment family keeps only the run of top nodes every member holds, so a boundary line cut mid-statement does not make two shapes, and the merge is priced by the kept run's lines. The core lines the members up, finds the holes, numbers the parameters and prices the merge; no name, path or source text crosses the wire. A group the core would refuse — Markdown and HTML, with no function to fold into, a tree not built, members not one shape — is counted by why and never sent. A T3 skeleton's line count is an honest integer estimate, never a measured one. The suggestion sets of this repository at one pinned commit and of four pinned external corpora are frozen, and a hundred of them are drawn for a blind audit of the feasibility.

  • groupCap 4096
  • treeNodeCap 131072
  • paramCap 6
  • schema ce.merge-report/0.1.0
  • since proto 7.5.0
  • wire merge/1

19Architecture analysis — layers, cuts, clusters, impact

Reads how the directories depend on each other: which sit at the bottom, which arcs to cut to break their cycles most cheaply, which files would sit better beside the files they talk to, each directory's fan-in, fan-out and instability, and what a change to a file reaches. Advice: no gate reads it, ce arch exits 0 with a document.

arcs   : dir(F) → dir(G) per file reference, dir(F) → D per package reference; inside one directory uncounted
cuts   : per SCC, a subset programme when it has ≤ 14 directories (exact 1), else Eades–Lin–Smyth + redundancy pass (exact 0)
layers : level(d) = 0 when nothing leaves d, else 1 + max level of what d points at, the cut arcs removed
cluster: deterministic Louvain, gains in integers, two levels; misplaced = its cluster holds strictly more files elsewhere
impact : breadth-first over the references read backwards, the focus at depth 0
metrics: fanIn, fanOut, instability = ⌊1000 · out ÷ (in + out)⌋, −1 when nothing touches the directory
Derivation and constants

The measuring side sends five integer tables — the measured files with their directory and line count, the directory tree, the file-to-file references, the file-to-package references at the package's directory, and the --impact files — and labels the answer back: every cut arc carries the file references behind it. The core folds the references onto the directory graph, finds its feedback arc set one strongly connected component at a time, layers what is left, clusters the file graph, walks the impact and measures every directory. A cut from the greedy road is minimal, not proven minimum, and says so. This repository's reading of one pinned commit is frozen, so a drift means the measurement or the core moved.

  • fileCap 131072
  • refCap 524288
  • exactVertexCap 14
  • instabilityScale 1000
  • schema ce.arch-report/0.1.0
  • since proto 7.6.0
  • wire arch/1