“Send in your skeletons Sing as their bones go marchin’ in again The need you buried deep The secrets that you keep are ever ready Are you ready? I’m finished makin’ sense Done pleading ignorance, that whole defense Spinning infinity, boy The wheel is spinnin’ me, it’s never ending, never ending Same old story” — Foo Fighters
Getting Started
This guide walks you from a fresh install to a passing pretender check in
a real repository. It covers the six steps most teams need on day one.
Prerequisites
- Rust toolchain (stable) — install via rustup.rs
- Git ≥ 2.x — required for
--diff-onlyand hook installation - Internet access for the initial
cargo install
1. Install
cargo install pretender
Verify the install:
pretender --version
2. Initialise your repository
Run the interactive wizard from the root of your repository:
pretender init
The wizard will:
- Ask which languages your project uses
- Suggest threshold values based on project size
- Write
pretender.tomlto the repo root - Offer to install the pre-commit hook
- Offer to generate a CI workflow file
A minimal generated pretender.toml looks like:
[pretender]
mode = "tiered"
languages = ["auto"]
[thresholds]
cyclomatic_max = 10
cognitive_max = 15
See the Configuration reference for every available key.
3. Run your first check
pretender check .
pretender check . scans all files in the given paths. To check only files
changed relative to origin/main:
pretender check . --diff-only
Reading the output
⚠ src/parser.py — parse_token(): cyclomatic 14 > 10
role: test
parse_token: cyclomatic=14, cognitive=8, assertions=0, function_lines=15, params=2, nesting_max=2, abc=3.46
ADVISORY cyclomatic 14 > 10
| Symbol | Meaning |
|---|---|
⚠ | Advisory — metric exceeds the configured threshold (tiered mode) |
✗ | Violation — metric exceeds the threshold in gate mode |
In gate mode every violation exits non-zero. In the default tiered mode
advisories are printed but exit is still zero.
4. Fix a violation
Suppose parse_token() scores cyclomatic=14 against a threshold of 10.
Extract the per-branch logic into helpers to bring each function under the
threshold:
Before — one function with 14 branches:
def parse_token(token, context, strict):
if token.type == "string":
if strict and not token.value.startswith('"'):
raise ValueError("unquoted string")
return token.value.strip('"')
elif token.type == "number":
try:
return int(token.value)
except ValueError:
return float(token.value)
# … six more branches omitted for brevity
After — each helper is cyclomatic ≤ 3:
def _parse_string(token, strict):
if strict and not token.value.startswith('"'):
raise ValueError("unquoted string")
return token.value.strip('"')
def _parse_number(token):
try:
return int(token.value)
except ValueError:
return float(token.value)
def parse_token(token, context, strict):
if token.type == "string":
return _parse_string(token, strict)
elif token.type == "number":
return _parse_number(token)
# …
Recheck after refactoring:
pretender check src/parser.py
Adopting a gate baseline
Some violations are legitimate but hard to fix right now, and mechanical
changes (a tree-wide reformat, a generated file) shouldn’t read as “new”
violations forever. check --adopt writes the current scan’s violations into
a gate baseline — pretender-baseline.json, next to pretender.toml:
pretender check --adopt
# ✓ adopted 3 findings into pretender-baseline.json (file_lines 1, function_lines 2)
Commit that file. From then on, pretender check --mode gate fails only
on genuinely new violations — findings whose stable ID (path, function
name, rule — deliberately not line numbers) is not in the baseline. Known
violations pass with a summary line:
→ gate: 3 known (baselined), 0 new
Without a baseline file, gate mode keeps its fail-closed behavior: any
violation fails. A corrupt or unreadable baseline also fails the check
rather than silently degrading. Adopted findings still appear in reports
and resolution tracking — the baseline changes gate semantics, not
visibility. Refresh the baseline by re-running with --adopt.
5. Install the pre-commit hook
Keep violations from reaching the repository by running pretender before each commit:
pretender hooks install
This writes a .git/hooks/pre-commit script that runs pretender check --staged on the files you are about to commit. To remove it:
pretender hooks uninstall
6. Add CI integration
Generate a GitHub Actions workflow:
pretender ci generate github
This writes .github/workflows/pretender.yml. The workflow runs
pretender check on every pull request and, if mutation testing is
configured, gates merges on the mutation score defined by
thresholds.mutation_min in your pretender.toml.
For mutation testing details, see Mutation testing. To gate
on mutation score in CI, set thresholds.mutation_min in pretender.toml
— the generated workflow picks it up automatically.
Next steps
| Topic | Document |
|---|---|
All pretender.toml keys and defaults | Configuration reference |
| Mutation testing flags and JSON output | Mutation testing |
| Writing custom metric plugins | Writing plugins |
| Language-specific tracked nodes | languages/ |
Ecosystem
pretender is part of the charly-vibes Tool Ecosystem — the map shows how it relates to its sibling tools.