How CodeDelta works, what its reports mean, and how to use it from the GUI, the command line, and in automated jobs.
A comprehensive guide to how CodeDelta works, what its reports mean, and how to use it from the GUI, the command line, and in automated jobs. If a question isn't answered here, it's a candidate to be added — this FAQ is meant to be the first place to look.
CodeDelta compares two versions of a source-code tree — an old version and a new version — and measures what changed between them. It produces metrics (how much code was added, deleted, and changed), HTML reports, and optional CSV/XML exports, and it can record results over time in a database for trend analysis. It also includes AI-assisted analysis of the code.
It has a full side-by-side diff viewer inside it — but that’s the evidence layer, not the product. CodeDelta is a diff viewer, not a conventional diff tool. A diff tool helps you make a change — compare, merge, save the new version. CodeDelta never touches your code: it’s read-only by design. It looks at two versions after the fact and measures what happened between them — how much change, what kind, by whom it was authored versus generated, whether the build machinery moved — and shows you the diff as clickable evidence behind every number.
You need two folders: the old version of your project and the new version.
GUI: start the server and open it in your browser:
python3 codedelta_server.py
then go to http://localhost:7654, point it at your two folders, and run.
Command line:
codedelta ./project_v1 ./project_v2 -o report.html
This writes report.html (plus two companion files — see "What reports do I get?").
No — the GUI is a front-end wrapper around the same engine. Anything the GUI does,
the command-line codedelta binary does underneath. Use whichever you prefer; the
results are identical.
CodeDelta measures change from old to new. "Added" means present in new but not old; "deleted" means present in old but not new; "changed" means present in both but modified. Getting them the right way round matters — swapping them inverts adds and deletes.
A single run with -o report.html produces three HTML files:
- report.html — the main detailed report (per-file metrics)
- report_overview.html — a PM-friendly summary
- report_diff.html — the Code Browser: file tree, classes, and (for comparisons) the side-by-side diff
Optionally you can also export:
- --csv <file> — per-file metrics as CSV (for spreadsheets)
- --xml <file> — XML export (EPM-compatible format)
Wherever you point -o. If you don't specify, the default is
codedelta_report.html in the current folder. The two companion files
(_overview, _diff) are always written next to the main report.
All progress and result messages go to stderr; stdout is reserved for
machine-readable output (--help, --version, and future JSON modes). This means
you can pipe stdout cleanly in scripts without progress noise mixed in.
GitClear is a cloud analytics platform: it ingests your hosted git history (GitHub and similar) into its service and computes a proprietary productivity score from it. CodeDelta works on GitHub pull requests too — the Action comments and can gate merges in your own CI — and runs standalone: a local desktop app and CLI that compare any two directory trees, with or without git, offline, and your code never leaves your machine. Vendor drops, release audits, air-gapped environments — no hosted history required. And instead of a weighted score you must trust, CodeDelta reports plain counters (CHG/DEL/ADD/MOV) that the Code Browser’s diff lets you step through line by line.
Yes — that is the viewer's organising rule. Click any counter in the toolbar (CHG, DEL, ADD, MOV…) and it becomes a stepper that walks exactly the rows it counts. The Ledger button decomposes every counter into its individual rows, each one a clickable jump, with “Copy as text” exporting the lot as citable links. Double-click any line number to copy a link that reopens the report at exactly that line.
A moved statement (identical text, at least 12 characters, unique on both sides) paints violet where it left and where it landed, each end carrying a chip naming its counterpart (“→ L163”) — click the chip to jump. The stepper walks moves as pairs, and the moves panel lists every pair in the file.
Cross-file moves: statements that left one file and arrived in another, shown with dashed chips that jump across files. XMOV is a reporting overlay — the churn counters still count the deletion and the addition, so no file's numbers depend on another file.
AI provenance. When the scan can see git history (--git mode, or a scanned
directory inside a git work tree), lines authored in commits signed by an AI tool (Claude Code,
Copilot, Cursor, Aider and others) carry a teal mark on the line number; hovering names the tool,
and the AI% counter steps through them. It reads your local git history only — nothing is
uploaded. Like all the AI features, it is a pointer for review, not a verdict.
Two more git-derived overlays. The story line reports what share of the churned code was under 30 days old — rework of recent work. The overview's People table shows, per author, how many lines of their code were churned away and how many of the new lines they wrote. Neither changes a churn counter, and neither appears when scanning plain directories without git history.
Past about 1.5 million display rows the report embeds changed regions with context instead of whole files, so the page stays usable. The gaps render as labelled separators and the file's story line states the mode. Counts are unaffected — only how much unchanged text is embedded.
Chrome and Safari are verified for this release. Edge and Firefox are untested — the page is plain self-contained HTML and is expected to work, but we say so rather than claim coverage we have not checked.
CodeDelta classifies every line into one of four states (the C/A/D/U model inherited from EPM): - A — Added: new lines not in the old version - D — Deleted: lines removed from the old version - C — Changed: lines that were modified - U — Unchanged: lines identical in both (when all change metrics are zero, the file's status is Unchanged)
Each is measured across three line types, giving the column prefixes you see: - SLOC — Source Lines Of Code (physical lines, comments stripped) - LLOC — Logical Lines Of Code (statements; e.g. semicolon-delimited in C-like languages) - COM — Comment lines
So ADD_SLOC = source lines added, DEL_LLOC = logical lines deleted,
CHG_COM = comment lines changed, and so on.
Git shows you the diff, and it will happily count raw +/− lines
— but a raw line count is not a measurement. In git's numbers a reflowed comment counts as
code change, a moved function counts twice (once deleted, once added), and there is no notion of
a line being changed as opposed to deleted-here-and-added-there. Pure diff tools inherit that
foundation unchanged; git-analytics platforms refine it into productivity scores, but the
refinement happens in their cloud, on their weights, against your hosted history.
CodeDelta's numbers come from language parsers, not line arithmetic: comments are stripped before anything is counted, source lines (SLOC) and logical statements (LLOC) are counted separately across ~30 languages, comment churn is reported on its own, a similarity test decides whether a line was modified in place or genuinely replaced, and moved statements are paired so a relocation costs one unit instead of two. The result is churn that reflects what a programmer actually did, refined over many years of edge cases.
And unlike a dashboard figure, every number is inspectable: in the Code Browser’s diff each counter is clickable and steps through the exact lines it counted — the numbers arrive with their evidence attached.
CRN is churn — a combined measure of logical-line change (added + deleted +
changed logical lines). It's the single best "how much real change happened here"
number, which is why it's the metric the CI gate (--threshold-churn) watches.
The classic churn metrics measure how much changed; the shape metrics measure how it changed. REP_CHURN = (ADD_LLOC + DEL_LLOC) / CRN_LLOC — the share of churn that was insertion and removal. REWORK is exactly 1 − REP_CHURN (CHG_LLOC / CRN_LLOC): the share spent editing existing statements in place, shown as a percentage with a plain-English reading ("1 statement in 4 edited in place") because that is the honest way to compare — small differences near the top of a 0–1 scale hide large ratios. REWRITE counts co-located delete-and-add statement pairs that were rewritten beyond recognition where they stood; the remainders are pure deletions (removed, nothing in their place) and pure additions (growth).
REWORK measures editing; REWRITE counts replacement-in-place. When a statement is modified but recognisably survives (the old and new statement pass the similarity test), it counts as CHG — and REWORK is the share of all churn made of these in-place edits. When the statement at a location is torn out and something different is written in its place (the pair fails the similarity test), that is a REWRITE pair. Think of editing a novel: REWORK is touching up sentences; REWRITE is tearing out a paragraph and rewriting it where it stood. Note the units differ too — REWORK is a ratio (a percentage of total churn), REWRITE is a count (of replaced-statement pairs). Both come from the same alignment of old and new statements, split by one similarity test.
Yes — and you can check them on any scan:
- CHG + DEL + ADD = CRN (always)
- REWORK + REP_CHURN = 1 (they are complements; tiles round for display)
- REWRITE + pure deletions = DEL_LLOC, and REWRITE + pure additions = ADD_LLOC
- CHG + 2×REWRITE + pure deletions + pure additions = CRN_LLOC
The last one carries the only subtlety: each REWRITE pair consumes one deletion AND one addition — one tile, two statements of churn — so the composition numbers sum to CRN only when REWRITE is counted twice. If an identity ever fails on a real scan, that is a bug, and we would like to hear about it. Full algorithm disclosure (including the similarity threshold and its known soft edges) is in the user guide's metrics section.
SLOC counts physical source lines (after removing comments and blank lines). LLOC
counts logical statements. One physical line can hold several statements
(a=1; b=2; is one SLOC, two LLOC), and one statement can span several physical
lines (the reverse). For languages without statement delimiters (e.g. HTML, plain
text), LLOC is defined to equal SLOC.
HTML has no logical-line concept (no statement delimiters like semicolons), so by definition LLOC = SLOC for HTML. This is intentional and matches EPM behavior.
For changed files, CodeDelta reports the new file's line count (EPM convention) — the current state is what you usually care about. Deleted files are counted from the old version (the new doesn't exist).
Any non-comment, non-whitespace difference flags a file as Changed. Re-saving with
different line endings, reformatting, or a one-character edit all count. Use the
Code Browser (report_diff.html) to see exactly what differs.
Yes — --metric-set takes a comma-separated list of metric codes. With no
--metric-set, all metrics are reported.
TRUE_CHURN = CRN_LLOC minus generated churn: the statement churn your
developers actually authored, with mechanically-produced change subtracted. Generated files
are detected deterministically — lockfiles (package-lock.json,
yarn.lock, Cargo.lock…), minified bundles, codegen output
(.pb.go), and files stamped with generator markers such as
@generated or DO NOT EDIT — and every classified file is
named in the report with the rule that matched.
Generated churn is not a fourth churn class: a generated file’s
statements are classified CHG / DEL / ADD like any other file’s, inside the headline
columns, and subtotalled per component on the CSV GEN row. Nothing is deducted
from any existing number — TRUE_CHURN is a derived companion metric. The canonical
case, straight from the bundled demo: a one-line package.json dependency bump
regenerates the lockfile, and the run reads CHURN 926, generated 830, TRUE_CHURN
96 — the difference between what happened to the repository and what your
developers actually wrote.
CodeDelta recognizes a wide range by file extension, including: C/C++ (.c .h .cpp
.hpp .cc .cxx), C# (.cs), Java (.java), JavaScript/TypeScript (.js .jsx .ts
.tsx .mts .cts), Python (.py .pyw), PHP (.php), HTML (.html .htm), CSS,
XML, SQL/PL-SQL (.sql .pls .pks .pkb), Perl (.pl .pm), Visual Basic (.vb .bas
.cls .frm .vbs), Ada (.ada .adb .ads), VHDL (.vhdl .vhd), assembler (.asm
.s), Fortran (.f .f90 .f95 .for), Ruby (.rb), shell (.sh), batch (.bat),
ASP/JSP (.asp .aspx .jsp), PowerBuilder (.srd .srf .srs .sru .srw), IDL, and
plain text/readme files.
Yes — --ext lets you map extra extensions to a language for that run. For example,
to treat .inc files as C:
codedelta old new --ext "inc=c" -o report.html
(Check --help for the exact syntax in your build.)
Comment changes (COM metrics) are tracked apart from code (SLOC/LLOC) so that documentation churn doesn't inflate your code-change figures — and so you can see documentation effort on its own.
Pass -d <file> (or --db) to record each run into a SQLite database. It
appends — every run adds a new snapshot, it never overwrites previous data. Over
many runs this builds a longitudinal history you can chart (churn over time, growth,
etc.). The default database is codedelta.db.
It's a standard SQLite file. You can open it with any SQLite tool:
sqlite3 codedelta.db
…then run SQL queries, or use a GUI like DB Browser for SQLite. Because it's plain SQLite, you can also read it from Python, Excel (via ODBC), or any language with a SQLite driver.
No. The database is append-only by design — each run adds a snapshot. The history
is the point; it's what powers trend analysis. (If you ever want a fresh history,
point -d at a new filename or delete the old DB file deliberately.)
Generally no — it can grow large and changes every run. Keep it on disk (it's needed for trends), but exclude it from git. Back it up separately if the history matters to you.
The GUI keeps it in a permanent per-user folder
(~/Library/Application Support/CodeDelta/ on macOS,
~/.local/share/CodeDelta/ on Linux, %APPDATA%\CodeDelta\ on
Windows); the command-line tool uses whatever you pass to -d. Because the
database uses write-ahead logging, don't just copy the live .db
file — use SQLite's safe online backup, which works even mid-scan:
sqlite3 project.db ".backup '/backups/project.db'"
Schedule that after your scan and copy the result somewhere safe for redundancy. Keep the database on local disk, not a network share — SQLite can corrupt over NFS/SMB. Runtime integrity (atomic runs, crash safety) is handled for you; backups and off-machine copies are up to you. See the user guide's “Database location, size & backups” for detail.
Use the metadata flags, which are recorded in the DB and the overview report:
- --project <name> — the project name (defaults to the new directory's name)
- --old-label <str> / --new-label <str> — version labels (e.g. v1.0, a git
hash, a date)
- --note <str> — free text (e.g. "nightly cron run")
codedelta <old_dir> <new_dir> [options]
-o, --output <file> Main HTML report (default codedelta_report.html)
--csv <file> Also write per-file CSV
--xml <file> Also write XML (EPM-compatible)
-d, --db <file> SQLite DB for trending (appends)
--project <name> Project name
--old-label <str> Label for old version
--new-label <str> Label for new version
--note <str> Free-text note
--exclude <dirs> Directories to skip
--ext <map> Add file-extension → language mappings
--metric-set <list> Report only these metrics
--snapshot-date <d> Override the recorded snapshot date
--threshold-churn N Exit code 3 if total churn exceeds N (CI gating)
-v, --verbose Print every file processed
-q, --quiet Errors only (for cron)
-h, --help Show help
-V, --version Show version
Use --quiet (errors only) and point the DB and report at fixed paths. Example:
codedelta /path/yesterday /path/today \
--project Parky \
--old-label "$(date -d yesterday +%Y-%m-%d)" \
--new-label "$(date +%Y-%m-%d)" \
--note 'nightly cron' \
-d /var/log/codedelta/parky.db \
-o /var/log/codedelta/latest.html \
--quiet
Add that line to your crontab (crontab -e) with a schedule. Because --quiet
prints only errors, a clean run produces no output — ideal for cron.
Yes — that's what --threshold-churn N is for. If total churn (CRN_LLOC) exceeds
N, CodeDelta exits with code 3. Wire that into CI:
codedelta old new --threshold-churn 5000 -o report.html || echo "Too much churn!"
Your CI system can treat exit 3 as a failed gate.
--threshold-churn exceededUse --exclude with the directories to skip:
codedelta old new --exclude "node_modules,build,dist" -o report.html
Yes. The GitHub Quickstart walks you through it entirely in the GitHub website — add the license secret, create the workflow file, open a pull request, read the result — assuming no prior Actions experience.
A drop-in GitHub Action wires CodeDelta into any repository. Add a few lines to
.github/workflows/codedelta.yml — no secret is needed until 31 October 2026, the
licence is built into the Action — and every pull request is then scanned in your own CI runner,
with nothing installed on a developer's machine:
- uses: code-delta-app/action@v1
with:
mode: churn_agent # the default: churn + Agent Scan (both adds the ML audit)
fail-on-new: "true"
The scan exports SARIF (uploaded to the repository's code-scanning / Security tab, so flags appear inline on the diff) and posts a pull-request comment summarising churn and AI findings — reviewed where the review already happens, not in a separate dashboard nobody opens.
Use a baseline. --write-baseline records the currently
accepted state; commit that file, and --baseline with
--fail-on-new then fails the build (exit 3) only on findings newer
than the baseline — so an established codebase can adopt CodeDelta without a wall
of legacy flags.
If your employer provides your GitHub, an admin may have turned Actions off. Check
Settings › Actions › General; if it's missing or greyed out, ask your
org administrator to enable Actions for the repository. You can also run CodeDelta a different way
that doesn't need Actions at all — as a Docker container, or from any other CI — see the
CLI / CI page.
A secret is only needed to bring your own licence. Adding one needs admin permission on the repository. If you don't have it, ask a repo admin
to add a secret named CODEDELTA_LICENSE (the base64-encoded license), or to grant you
access. An org-level secret works too.
Yes. The pull-request comment works on any repo, public or private. The Security-tab (SARIF) view is free on public repos; on private repos it requires GitHub Advanced Security — but you don't need it, the comment carries the same findings.
It runs on your own GitHub Actions minutes, in your own account — there is no per-scan fee to us and nothing phones home. A typical scan is well under a minute. Codespaces, if you use that path, also runs on your own account's compute (within its free allowance).
No. The scan runs inside your own GitHub runner; your code never leaves your account, and nothing is sent to us. The runner is wiped after each run — no install persists.
Open the Actions tab and click the latest run to read its log. The most common cause
is the workflow not having permission to comment (it needs the default pull-requests: write
token, which is on by default for same-repo PRs). If you supplied your own licence, check that the
CODEDELTA_LICENSE secret is named exactly and holds the base64 of the file.
Yes — add workflow_dispatch for a manual “Run” button in the Actions
tab, or a schedule: for a nightly run. Same Action, no pull-request coupling. Examples are
on the CLI / CI page.
Yes. The workflow uses on: pull_request, never pull_request_target, so
pull requests from forks run without access to your secrets — a stranger's PR
cannot read your license.
CodeDelta includes AI-assisted analysis that examines the source and produces its own report (the AI Code Scan), separate from the change metrics. It can flag characteristics of the code using trained models. The detection settings (including per-language configuration) are available in the GUI's AI Detection settings.
CodeDelta can export an inventory of every AI provider, API endpoint, and hosting
jurisdiction your code reaches — in its native format or CycloneDX
— with --bom. A policy gate (--gate /
--gate-policy) fails the build on breaches such as egress to a foreign-hosted
model or an unapproved provider. It is the artifact AI-governance and compliance
programmes ask for — a software bill of materials for the AI your code calls.
AI% is the share of your code, measured in lines, that sits in files flagged HIGH or ELEVATED — not a claim about how much code an AI wrote. In a single-folder snapshot it is the source lines (SLOC) in flagged files divided by total source lines; in an old-vs-new comparison it is the added logical lines (ADD_LLOC) in flagged files divided by total added logical lines. So “AI% = 29” means about 29% of the codebase, by lines, lives in files worth reviewing.
Just as important is what AI% is not: it is not a statement that 29% of the code was written by AI, and not a probability or confidence score. CodeDelta treats a flag as “this file shows characteristics associated with AI generation” — a pointer for review, not a verdict on authorship. Whole files are flagged or not, and AI% is simply the line-share of the flagged ones.
See the Privacy section below. CodeDelta is designed to run locally.
This is a fair question of any AI-detection model, and CodeDelta addresses it directly with what we call header-blind models. The detector is deliberately built not to rely on signals a person could trivially fake.
The weakness we found and closed: when the Java model was first trained, its single strongest signal — roughly 28% of the model's entire decision — was whether a file carried an author/copyright header comment. We measured the effect, and a rich header pushes a file's AI-probability down (in one test, from 70.9% toward 63.8%) — a tidy header makes code look more “human” to the model. That is gameable: a team that mandates header comments as a coding standard would unintentionally make its AI-generated code look human-written, defeating detection through a formatting rule that has nothing to do with who actually wrote the code.
Rather than patch over the signal at scoring time, the model is retrained from scratch with the header feature removed entirely. Forced to ignore headers, it learns to detect AI from structural characteristics instead — code patterns, naming, spacing consistency, token entropy, annotation density — properties that reflect how the code is genuinely written and are far harder to fake.
We validate each header-blind model with a header sweep: take real files, force the header signal across its full range, and re-score. For the blind model the AI-probability does not move at all when the header changes — the lever is no longer connected — while it still cleanly separates AI code from human code on a large independent test corpus, with a low false-positive rate on human code. The trade-off is a small reduction in raw accuracy in exchange for detection that holds up when someone is actively trying to hide AI authorship. The approach is live for C++ and Java, where the header signal was most dominant, and is being extended to other languages where that signal is significant.
Agent Scan finds it. Every AI SDK import (OpenAI, Anthropic, LangChain, AWS Bedrock, Azure OpenAI, MCP agent kits, DeepSeek, GigaChat — the built-in list is long and you can extend it), every raw HTTP call to a known model endpoint, in Python, JavaScript/TypeScript, Java, C#, C++, PHP, Ruby, Go, Rust and Dart. No machine learning, no probabilities: curated signatures and deterministic rules, so the same tree gives the same answer every time — evidence you can put in front of an auditor, not a guess.
Yes — this is the Agent Infrastructure scan, and it is a different
question from “does my code call AI?”. Agents leave fingerprints in the tree:
instructions files (CLAUDE.md, .cursorrules), agent workspaces and
MCP configs (.claude/, .mcp.json) — and sometimes things that
should never be in a repo at all: residue of personal agents such as
OpenClaw (openclaw/, clawdbot/) or a committed
agent credential like gateway.auth.token, which the scan probes and labels
“contains a token-shaped value” or “empty placeholder”. Three tiers
— sanctioned, notable, review — each hit a named path. This is the
shadow-AI inventory security frameworks now demand, produced from source,
offline, in one scan.
The most dangerous construct in AI-era code: exec/eval applied to
model output — a program that lets an AI write code and then runs it, unread.
Agent Scan flags it wherever it appears, with the file and line. It is also a
gate-policy flag, so a CI merge can be blocked the day someone introduces it.
Every provider your code calls is mapped to its hosting jurisdiction. A call routed to a
China- or Russia-hosted model (DeepSeek, Qwen, GigaChat, YandexGPT…) is flagged as
data_egress with a sovereignty warning — before your prompts, and whatever
source code rides along in them, leave the building. The AI-BOM export turns the same
information into a compliance artefact (native or CycloneDX).
Only if you tell it to. Everything above is reported by default. The
--gate policy decides what blocks a merge: non-allied egress and the rogue
pattern out of the box; tier-3 agent artifacts are opt-in
("fail_on_agent_artifacts": true) because plenty of teams run agents such as
OpenClaw entirely deliberately. Report first, fail only on your policy — nothing breaks
a build you didn’t ask it to. The policy file accepts
deny_jurisdictions, allow_providers, deny_flags
(e.g. rogue_pattern, cost_risk), max_risk and
fail_on_agent_artifacts — a worked example is in the
user guide’s AI-BOM & policy gate section.
No. CodeDelta runs locally and analyzes your code on your own machine. It does not upload your source anywhere.
CodeDelta verifies a signed license file (codedelta.lic) offline — no license
server, no internet needed. See the separate Licensing Guide for installing and
activating your license, and for troubleshooting license messages.
See the Licensing Guide's troubleshooting section — in short, place codedelta.lic
in ~/.codedelta/. (Development builds don't require a license; release/customer
builds do.)
The path you gave doesn't exist or isn't a directory. Check the path; use absolute paths if unsure.
None of the files matched a known language. Either the folders are empty/wrong, or
your files use extensions CodeDelta doesn't map by default — use --ext to add
them (see Languages above).
You passed both. Pick one.
Make sure the server is running (python3 codedelta_server.py) and that nothing
else is using the port. If a previous server is stuck, stop it first:
pkill -f codedelta_server
then start it again.
Open the Code Browser (report_diff.html) to see line-by-line what CodeDelta
detected. If it still looks wrong, note the file and the metric and report it — the
diff view usually explains the discrepancy (e.g. reformatting counted as change,
comment vs code classification).