LLM-based mutation testing, portable to any codebase
Project description
Cerebrum
LLM-based mutation testing, portable to any codebase.
Cerebrum inserts small, realistic bugs ("mutations") into a codebase one at a time, runs the project's test suite, and reports whether the tests caught each mutation. Mutations that survive reveal gaps in the test suite — each one is a concrete bug that would ship silently.
The name is a nod to Cerebro, the machine in X-Men used to find mutants.
Why another mutation tester?
Traditional mutation testers (Stryker, PIT, mutmut, cosmic-ray) rely on a
per-language AST library with a fixed set of operators (+→-, <→<=, …).
Cerebrum uses an LLM as the mutation operator, which:
- works on any language without a per-language mutator, and
- produces domain-realistic bugs that resemble real developer mistakes, stressing the test suite more meaningfully than mechanical operators.
Core design: Engine + Adapter
- The Engine is language-agnostic. It knows git, patches, running a
command, and comparing exit codes — never
npm,jest, orpytest. - Everything codebase-specific lives in one
cerebrum.yamlcommitted to the target repo. Porting Cerebrum to a new codebase = writing that one file.
cerebrum.yaml (per target repo)
modules · install · test · globs
│ read by
┌────────────▼─────────────────────────────────────────┐
│ CEREBRUM ENGINE │
│ 1. BASELINE install → run suite → require green │
│ 2. TARGETING pick lines (coverage | changed | ...) │
│ 3. GENERATE LLM → ONE mutant patch + metadata │
│ 4. EXECUTE worktree: apply → build → test → class │
│ 5. REPORT score · survivors · suggested tests │
│ · severity-weighted trend across runs │
└───────────────────────────────────────────────────────┘
Key principles
- One mutant at a time in an isolated git worktree — enables correct attribution of a test failure to a specific mutation.
- Pure-LLM operator behind a validity gate: a mutant only counts if its patch applies, changes behavior, and builds/lints.
- Coverage-guided targeting — never mutate uncovered lines (they survive trivially and add noise).
- One targeting vocabulary:
cerebrum runsweeps a module using the strategy named in config (coverageby default); `cerebrum run --diff ..` mutates only lines changed in that range (PR gate). The CLI never invents its own strategy names — `--diff` only supplies the range a committed config can't.
Post-run hook
cerebrum.yaml accepts an optional top-level after_run: <command> — a single shell
command the engine runs, in the repo root, once a run's .cerebrum/ artifacts
(history.sqlite, runs/<run_id>/mutants.jsonl, runs/<run_id>/coverage.json) have been
written. Use it to push results somewhere (e.g. python scripts/push_run.py for Cerebrum
Cloud) without teaching the engine anything about the destination. It's best-effort: a
failing after_run command logs a warning to stderr but never fails the run or changes its
exit code or reported mutation score.
Setup
Install the engine from the repo root: pip install -e ".[dev]".
Cerebrum needs ANTHROPIC_API_KEY set in the process environment for mutation
generation and severity scoring — it only ever reads
os.environ["ANTHROPIC_API_KEY"] and has no opinion on how that variable gets
there.
Avoid putting the raw key in a plaintext .env — copy .env.example to
.env and put a secrets-manager reference in it instead, e.g. for
1Password:
ANTHROPIC_API_KEY=op://<vault>/<item>/credential
Then run through that tool so it resolves the reference into the cerebrum
child process only, never onto disk:
op run --env-file=.env -- cerebrum run -c cerebrum.yaml --module backend
The same pattern works with Doppler, Vault, or any other secrets manager — Cerebrum doesn't care which one, as long as the real value lands in the process environment before it runs. Don't bake a specific secrets tool into shared scripts or docs; each user or environment should be free to supply the key however they manage secrets.
Status
Early, but the core loop runs. The config adapter, baseline stage, and the
single-mutant lifecycle (cerebrum mutate) are implemented: it selects a
covered line, asks Claude to insert one bug, applies it in an isolated git
worktree, runs the suite, classifies the outcome, and appends a record to
.cerebrum/mutants.jsonl. Generating a real mutant needs ANTHROPIC_API_KEY (see Setup).
Targeting and sweeps (cerebrum run, --diff) are implemented: pick covered
lines via the config's strategy or a changed-lines diff range, then mutate
them in parallel across a pool of pre-installed, reused git worktrees
(runtime.parallelism) instead of one at a time. Reporting (cerebrum report)
is implemented: mutation score, a survivor report with LLM-suggested tests, and
a severity-weighted trend across runs. Still to come: llm-risk and all
strategies.
Mutant outcomes
mutation_score = KILLED / (KILLED + SURVIVED)
| Status | Meaning |
|---|---|
KILLED |
A test failed — the suite caught the mutation. |
SURVIVED |
All tests passed — a real test gap. |
TIMEOUT |
Tests hung (e.g. infinite loop); counts as killed. |
BUILD_ERROR |
Mutant didn't compile/lint — invalid, discarded. |
NO_COVERAGE |
Line has no covering tests — excluded. |
Trend tracking across runs
Each cerebrum run is a "run" in the trend sense: its mutant records land under
.cerebrum/runs/<run_id>/mutants.jsonl, and a summary row (score, kill/survive
counts, the git commit at the time, the average severity of that run's
survivors, and the module's code-coverage percentage) is appended to
.cerebrum/history.sqlite. A per-file coverage rollup for the run — covered vs.
instrumented line counts, coverage fraction, and the count and worst severity of
survivors per file — is also written to .cerebrum/runs/<run_id>/coverage.json,
so coverage can be trended over time and low-coverage files ranked by risk. Every
mutant now carries a
Claude-estimated severity (low/medium/high/critical) alongside its
mutation_type, so a declining score isn't the only signal — you can also see
whether the impact of what's surviving is trending up or down over time, not
just the count. cerebrum report --trend prints the last N runs; cerebrum report --survivors prints the current run's survivors (file:line, diff,
severity, how many consecutive runs it's persisted) with an LLM-suggested test
for each. cerebrum mutate (a one-off manual mutation) is not part of this —
it still writes to the legacy flat .cerebrum/mutants.jsonl with no run or
history entry. Target repos should add .cerebrum/ to their own .gitignore.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cerebrum_engine-0.2.0.tar.gz.
File metadata
- Download URL: cerebrum_engine-0.2.0.tar.gz
- Upload date:
- Size: 34.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88c4bec59dad724c76e035fe535ebe9dd5b9adb475ede101dbf936259fe12108
|
|
| MD5 |
852b3661740ac82cc479925cb5c0de3b
|
|
| BLAKE2b-256 |
7f56a49b65892f2a2957b4eefe988fa22c90372d08869b725ee3ec5b5c0ede29
|
Provenance
The following attestation bundles were made for cerebrum_engine-0.2.0.tar.gz:
Publisher:
publish.yml on islandhopper81/cerebrum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cerebrum_engine-0.2.0.tar.gz -
Subject digest:
88c4bec59dad724c76e035fe535ebe9dd5b9adb475ede101dbf936259fe12108 - Sigstore transparency entry: 2139363972
- Sigstore integration time:
-
Permalink:
islandhopper81/cerebrum@e33532d68d38b3ed2a0ce808788ddcb39a1ca33a -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/islandhopper81
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e33532d68d38b3ed2a0ce808788ddcb39a1ca33a -
Trigger Event:
release
-
Statement type:
File details
Details for the file cerebrum_engine-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cerebrum_engine-0.2.0-py3-none-any.whl
- Upload date:
- Size: 42.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9478a9cd33a250b4a235d6892ee237afbbb6aae02fb9e0ac01695df8f8e5ec9c
|
|
| MD5 |
f166d8e04ab1857055414f6f8eaa39e6
|
|
| BLAKE2b-256 |
de6dbc7857669404ecd94c947ecd71038fe107f5d27dbadf49536462c37123aa
|
Provenance
The following attestation bundles were made for cerebrum_engine-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on islandhopper81/cerebrum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cerebrum_engine-0.2.0-py3-none-any.whl -
Subject digest:
9478a9cd33a250b4a235d6892ee237afbbb6aae02fb9e0ac01695df8f8e5ec9c - Sigstore transparency entry: 2139364061
- Sigstore integration time:
-
Permalink:
islandhopper81/cerebrum@e33532d68d38b3ed2a0ce808788ddcb39a1ca33a -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/islandhopper81
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e33532d68d38b3ed2a0ce808788ddcb39a1ca33a -
Trigger Event:
release
-
Statement type: