Shows trigger coverage as reported by Threadline.Health.trigger_coverage/1,
with a three-section table (default) or JSON output (--json).
Viewer by default (exits 0); --strict turns :error-severity findings into exit 1.
Uncovered tables never fail --strict; use mix threadline.verify_coverage for the positive-list gate.
Usage
mix threadline.health.coverage
mix threadline.health.coverage --json
mix threadline.health.coverage --schema=NAME
mix threadline.health.coverage --strict
mix threadline.health.coverage --strict --json
mix threadline.health.coverage --all-schemas
mix threadline.health.coverage --all-schemas --jsonDefault output: a three-section TABLE / STATUS / SOURCE table followed by
a Coverage: N covered, M uncovered, K expected uncovered summary line.
--json emits a JSON object with keys covered, expected_uncovered,
findings, schema, uncovered. The expected_uncovered value is a list of
{"table": ..., "source": "baseline" | "config"} objects so adopters can
filter via jq '.expected_uncovered[] | select(.source == "config")'.
findings is a list of Threadline.Health.trigger_findings/1 plus
Threadline.Health.legacy_key_findings/1 results, each with keys code,
severity, schema, table, message, details (code and severity
as strings). This key is additive: every other key keeps its existing
shape, and --json stdout stays exactly one pure JSON document — the
--strict status line below never reaches it.
Default output also gains a FINDINGS section after the existing table,
with columns SEVERITY, CODE, TABLE, MESSAGE.
--strict gates the --schema schema (default "public"); after the
normal output it prints one status line to stderr via
Mix.shell().error/1 — strict: FAILED — N error finding(s) ... and
exit({:shutdown, 1}) when any in-scope :error finding is present, or
strict: passed (W warning(s) not gated) otherwise. Uncovered tables and
:warning findings are never gated.
If the :unresolved_legacy_keys probe (from legacy_key_findings/1) times
out — typically a missing row-history index — the task prints a one-line
hint to stderr pointing at
Step 4 and
continues with the trigger findings only; a timeout never fails --strict.
A malformed config :threadline, :trigger_capture stops the task with
Mix.raise/1 before any findings are checked.
--schema=NAME validates NAME at the edge (regex + pg_namespace lookup)
and raises with Mix.raise/1 on bad input. NAME must match
~r/\A[a-z_][a-z0-9_]{0,62}\z/ (PostgreSQL identifier, conservative subset)
AND exist in pg_namespace. Default "public".
An unknown or invalid switch (for example --stict, --jsn, or --schema
with no value), or any stray positional argument (for example a dropped
leading --, as in schema=public instead of --schema=public), raises
Mix.raise/1 naming the offending switch or argument, before the repo
starts.
--all-schemas
--all-schemas checks every reportable schema in one batched catalog
snapshot instead of one --schema. It cannot be combined with --schema
(including an explicit --schema=public) — both raise Mix.raise/1 with
"threadline.health.coverage: --schema and --all-schemas cannot be used
together. Use --schema=NAME for one schema or --all-schemas for every
schema." before the repo starts.
A schema is reportable when it is not information_schema, not a pg_%
system schema, and not itself a member of a PostgreSQL extension (for
example a schema attached with ALTER EXTENSION ... ADD SCHEMA) — the
storage schema stays included. A schema with zero reportable tables and
zero findings is omitted from the output entirely; a schema is still
reported if it has findings but no reportable tables, so a finding never
disappears.
Default (table) output gains a leading SCHEMA column on the main table,
then a per-schema rollup (SCHEMA COVERED UNCOVERED EXPECTED FINDINGS),
then a grand total Coverage: N covered, M uncovered, K expected uncovered across J schemas, then the unchanged FINDINGS section.
--all-schemas --json emits an envelope instead of the single-schema
object: {"schemas": {"<name>": <single-schema payload>, ...}, "summary": {"schemas", "covered", "uncovered", "expected_uncovered", "error_findings", "warning_findings"}}. Each value under "schemas" is the exact same
object --schema=NAME --json would emit for that schema — .schemas.public
always equals --schema=public --json's output. The schemas object keys
are in sorted order, encoded so the order survives JSON encoding past 32
keys (a plain map would fall back to hash order at that size). Output
without --all-schemas is completely unaffected: it keeps its exact
existing shape.
--strict --all-schemas gates the union of every reported schema: it
exits 1 if any reported schema has an :error finding, and never gates on
uncovered tables or warnings in any schema, same as --strict alone.
--all-schemas emits exactly one [:threadline, :health, :checked]
telemetry event per run, with grand totals across every reported schema —
never one event per schema.