Skip to content

tepyd check: the CI gate

Silent when healthy. Prints one error block and exits 1 when not.

Every other command reports; check decides. It is the only one whose exit status means something. It drops into a pipeline without further configuration.

$ tepyd check
$ echo $?
0

It writes nothing to stdout, nothing to stderr, and exits 0. When something is wrong:

$ tepyd check
tepyd check: 1 problem(s)
  ✗ billing: no tests — 80 LOC of source, no tests at any tier.
    fix: Start at the base — add unit tests for its pure functions and core types.
         Even a handful turns silent regressions into caught ones.
$ echo $?
1

What counts as a failure

check runs the same analysis as report and gates on its problem-severity findings only:

Finding Fires when
untested_unit A unit of ≥ 50 LOC has no tests at any tier.
inverted_unit A unit's shape is ▼: its most expensive tier holds the most test code.
no_unit_tier A unit of ≥ 100 LOC is tested, but nothing sits in the unit tier.
flattened_pyramid Codebase-wide unit share is below the first tier's target_share.

Warnings (a thin unit base, a light test ratio) and info findings do not fail the build, because a gate that fires on advice is a gate people learn to disable. Run tepyd report to see everything.

Since it shares report's analysis, everything that tunes the report tunes the gate too. That includes expects, which stops a layered architecture from failing CI for being correctly layered, and exclude, for code that is genuinely not meant to be tested.

What it does not run

check is static. It never executes your suite, so it is safe to run anywhere and costs no measurable time.

That means reach and cover are not part of it. reach is a ranked heuristic whose clean result is not a proof, so it cannot gate a build. cover needs your project's own environment and roughly a full suite run; if you want to gate on it, run tepyd cover --json as a separate step and apply your own thresholds.

Two failures that are not findings

Nothing to analyse. Exit 1:

tepyd check: nothing to analyse under /repo/src/app.

Your src_root or units is wrong. A gate that passes because it found no code is not a gate.

Cannot assess. Exit 1:

tepyd check: cannot assess — only 0% of test code (0 of 120 LOC) could be tied
to a source package. Run `tepyd report` for the explanation and the fix.

Your suite is organised by tier, with no per-package folders for Tepyd to mirror, so it cannot say which package any test belongs to. See when Tepyd cannot attribute your tests. Again the gate fails, because it could not look.

Flags

Flag Meaning
-C/--root DIR Analyse the named project. Defaults to the current directory.
--min-src N Skip units below this LOC (default 20).
--exclude NAME Skip this unit, on top of config exclusions. Repeatable.

In a pipeline

Add Tepyd as a dev dependency and run it alongside your other checks:

- name: Test pyramid
  run: uv run tepyd check

Adopting it on an existing codebase

A mature suite will usually fail check on the first run. Start by scoping your tiers with expects: on a layered codebase, most first-run problems are correct by design. Then use exclude, with its required reason, for whatever remains and is intentional. The remainder is your real backlog.