Skip to content
Fallow home
All docs pages

fallow baselines prune

Remove the entries of fixed findings from committed fallow baselines. One whole-project run prunes the dead-code, health and duplication baselines, never adds an entry, and keeps the stale-baseline gate green.

Use fallow baselines prune after you fix findings that a committed baseline holds. A fixed finding leaves its entry in the file. A whole-project run with --fail-on-stale-baseline then fails until someone updates the file. Prune removes the entries that match no current finding, so the stale gate passes again.

fallow baselines prune
Pruned 1 entry from the dead-code baseline baselines/dead-code.json (12 -> 11)
  unused_files: unused-file:src/legacy/export.ts
health baseline baselines/health.json: nothing to prune (40 entries)
Pruned 1 entry from the dupes baseline baselines/dupes.json (9 -> 8)
  normalized_clone_fingerprints: dup:4e50b434:2

What prune changes

  • Prune keeps exactly the entries that --baseline matches. A pruned file hides the same findings as before.
  • Prune never adds an entry. A new finding stays outside the baseline, and the normal gates report it. To record new findings on purpose, use fallow dead-code --save-baseline, fallow health --save-baseline or fallow dupes --save-baseline.
  • Prune keeps the order of the file and writes a file only when it removes an entry. It writes through a temporary file and a rename, and it does not write through a symbolic link.
  • Health buckets keep the slots that cover current findings. A slot of a higher severity can cover a finding of a lower severity, as with --baseline. An identity bucket that follows a moved file keeps its saved key.
  • Prune does not change the runtime-coverage keys or the refactoring target keys of a health baseline.
  • When the run still reports a function whose identity bucket loses all its slots, the bucket stays as an empty object. This keeps the move matching of --baseline as it was.

Which files

Prune reads the paths in the config, the same paths that fallow audit reads:

{
  "audit": {
    "deadCodeBaseline": "baselines/dead-code.json",
    "healthBaseline": "baselines/health.json",
    "dupesBaseline": "baselines/dupes.json"
  }
}

The paths resolve against the project root. A flag overrides the config path for one file. Prune runs only the analyses that the files need.

Whole-project scope

Prune always analyzes the whole project. A narrowed run would remove entries for findings that it did not see. So prune exits 2 on --changed-since, --workspace, --changed-workspaces, --diff-file, --diff-stdin, --production and the FALLOW_DIFF_FILE environment variable, and it ignores workspaces.changedSince. The production mode of each analysis comes from the config, as for fallow audit.

Prune also exits 2 on the global baseline flags --baseline, --save-baseline, --fail-on-baseline-growth and --baseline-base. Name the files with the prune flags or the config.

CRAP scores in a health baseline depend on coverage. Prune reads the coverage input of the config and of FALLOW_COVERAGE. When you saved the health baseline with --coverage, pass the same input to prune.

Skipped files

Prune does not change a file that it cannot match entry by entry. It reports the file as skipped, prints the command that saves it again, and exits 2. The other files are still pruned. A file that does not exist or cannot be read gets the status error, and the other files are still pruned. A file is skipped when:

  • another command saved it;
  • it uses an older key form, for example a dead-code baseline without identity;
  • it was saved by a type-aware run;
  • it has fields that this version does not know, for example from a newer fallow version;
  • it is a duplication baseline with the clone keys that older versions shared between unrelated groups. See migrating collision handles.

The stale-baseline warning and the --fail-on-stale-baseline message print the prune command for the file, with an absolute path.

Pre-commit hook

Install a pre-commit hook that prunes before the audit:

fallow hooks install --target git --prune-baselines

Prune analyzes the working tree, not the staged content. A partial commit (git commit -p) can then carry a pruned file that the commit does not support. So the hook runs the prune only when the working tree has no unstaged changes and no untracked files, and it stages the files that the prune wrote. Otherwise it prints a note and skips the prune. It also skips the prune for a commit with paths (git commit <path> or --only), because git builds a temporary index for such a commit. A failed prune never blocks the commit, because the audit is the gate. The prune analyzes the whole project, so it adds the time of a full run to each commit.

For a hook manager such as Lefthook, or an existing hook, the install command prints the block to add.

Options

FlagDescription
--checkReport the entries that a prune would remove, and write nothing. Exits 1 when an entry can be pruned.
--dead-code-baseline <PATH>The dead-code baseline to prune. Overrides audit.deadCodeBaseline.
--health-baseline <PATH>The health baseline to prune. Overrides audit.healthBaseline.
--dupes-baseline <PATH>The duplication baseline to prune. Overrides audit.dupesBaseline.
--coverage <PATH>The coverage input for CRAP scores, as for fallow health --coverage.
--coverage-root <PATH>Rebase the file paths of the coverage input onto this root.
-f, --format <FORMAT>human (default) or json
-r, --root <ROOT>Project root directory
-q, --quietPrint only the skipped and failed files, and no progress output

Exit codes

CodeMeaning
0The files are pruned, or have nothing to prune.
1With --check: at least one entry can be pruned.
2No baseline is configured, a flag narrows the run, a file cannot be read or written, or a file is skipped. Exit 2 wins over exit 1 when both apply.

JSON output

--format json prints the baselines-prune envelope (schema version 1). Paths are relative to the project root.

{
  "kind": "baselines-prune",
  "schema_version": 1,
  "check": false,
  "files": [
    {
      "kind": "dead-code",
      "path": "baselines/dead-code.json",
      "status": "pruned",
      "entries_before": 12,
      "entries_after": 11,
      "removed": [
        { "category": "unused_files", "key": "unused-file:src/legacy/export.ts", "count": 1 }
      ],
      "reason_code": null,
      "reason": null
    }
  ],
  "actions": [
    {
      "type": "stage-baselines",
      "auto_fixable": false,
      "description": "Stage the pruned baseline files and commit them with the change that fixed the findings",
      "paths": ["baselines/dead-code.json"],
      "command": "git add -- baselines/dead-code.json"
    }
  ]
}
  • status is pruned, would-prune (with --check), unchanged, refused (skipped) or error (the file cannot be read or written).
  • reason is a sentence for people. It is set for refused and error, and null for the other statuses.
  • reason_code names why a file is skipped: parse-error, not-this-kind, legacy-keys, unknown-key-scheme, incompatible-identity, shared-clone-keys or unknown-fields. It is null for error and for the other statuses.
  • actions holds stage-baselines for written files, prune-baselines after a --check run with entries to prune, and resave-baseline with the save command for each skipped file.