Baseline workflow
A baseline is a snapshot of every finding present at a chosen moment. cha analyze --baseline <path> filters out anything in that snapshot, so CI only fails on findings introduced after the snapshot was taken.
This recipe is the day-to-day rhythm: generate, compare, refresh.
Generate
Once, on the main branch, after deciding "everything currently here is grandfathered":
cha baseline
Default location: .cha/baseline.json. Override with -o:
cha baseline -o .cha/legacy-2026-Q1.json
The file is a list of fingerprints — (path, smell name, normalised location). Small, diff-friendly, commit it.
git add .cha/baseline.json
git commit -m "Cha baseline at adoption"
Use in CI
cha analyze --baseline .cha/baseline.json --fail-on warning
Fingerprints in the baseline are silent. Everything else surfaces. New issue on a touched line → CI fails. Old issue carried over → CI passes (silently).
A GitHub Actions step:
- name: cha
run: cha analyze --baseline .cha/baseline.json --fail-on warning
Compare
After a few weeks, see what changed:
cha baseline -o /tmp/now.json
diff -u .cha/baseline.json /tmp/now.json | less
Lines marked - (in baseline but not now) are findings that disappeared — debt paid. Lines marked + (in now but not baseline) shouldn't exist if --baseline was respected — investigate the CI config.
Refresh
When the team has paid down enough debt that the baseline is mostly stale, regenerate:
cha baseline # overwrites .cha/baseline.json
git diff .cha/baseline.json # see what shrank
git commit -am "Refresh Cha baseline (-32 entries)"
Refresh on a regular cadence (quarterly is common) or whenever a major refactor lands. The commit message should record how many entries dropped — that's a real number for retros.
Multi-package
In a monorepo, give each package its own baseline:
for pkg in packages/*/; do
( cd "$pkg" && cha baseline )
done
Each .cha/baseline.json lives next to its package. CI runs Cha per-package with its own baseline. See CI on a monorepo.
What baseline does not fix
- Wrong rules — if a smell is firing on code you genuinely don't want flagged, don't bury it in baseline. Tune the threshold in
.cha.toml, or use inline directives for the specific item, or disable the plugin if it's noise everywhere. - Drift — baseline silences existing findings. If new code is producing the same kind of finding, baseline lets it through only if its fingerprint matches an existing entry exactly. Identical-looking findings on new code surface normally.