agentsclimarketplace

Coverage gate

Skill xiaolai/tdd-guardian-for-claude/skills/tdd-guardian/coverage-gate

Enforce coverage thresholds AND test quality — coverage without behavioral assertions is meaningless.From its SKILL.md

Install
npx -y skills add xiaolai/tdd-guardian-for-claude --skill coverage-gate

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 10 stars10 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.6 KB, as published. Nobody here has run it

Coverage Gate

Gate 1: Coverage thresholds

  1. Run every lane whose gateOn includes the active trigger.
  2. Collect the report from each lane with coverage: "include".
  3. Merge them (see "Multi-lane coverage" below).
  4. Verify the merge measured something — a report with zero measurable lines scores 100% under the 0/0 convention and must FAIL, not pass. A silent no-op coverage run looks identical to a perfect one.
  5. Enforce thresholds against the merged totals based on coverageMode:

Coverage modes

"absolute" (default): Current behavior — all metrics must meet configured thresholds.

"no-decrease": Blocks only if coverage decreased from a recorded baseline.

  • On first run (or branch change), records current coverage as the baseline and passes.
  • On subsequent runs, compares against baseline:
    • Decreased → block (with delta details)
    • Equal or improved → pass
  • Baseline is per-branch. Switching branches records a new baseline automatically.
Scenarioabsoluteno-decrease
Coverage 72%, threshold 100%BLOCKPASS (if baseline ≤ 72%)
Coverage dropped 72% → 70%BLOCKBLOCK (decreased)
Coverage improved 72% → 75%BLOCKPASS (improved)

Default threshold policy (absolute mode): 100 for all metrics.

Multi-lane coverage

Thresholds apply to the merged total across every contributing lane, never to a single lane. Two merge methods exist and the gate reports which one it used:

MethodWhenAccuracy
singleOne contributing laneExact
unionEvery report carries per-line detailExact — a line hit by any lane counts once
weightedAt least one report is summary-onlyApproximate — a line hit by two lanes is counted twice

To get an exact union, have every contributing lane emit a per-line format: LCOV, Cobertura, JaCoCo, coverage.py JSON, or coverage-final.json. Summary-only formats (coverage-summary.json) force the weighted fallback. When the gate falls back, it says so in the report — do not quote a weighted number as if it were a union.

Give every contributing lane its own coverageSummaryPath. Two lanes writing the same file means the second overwrites the first and coverage is silently undercounted.

Unmeasured metrics are not zero

A metric the report format does not track is null. With a non-zero threshold that produces a WARNING, never a FAILURE:

  • coverage.py and go-cover measure no functions
  • go-cover and SimpleCov measure no branches by default
  • LCOV measures functions and branches only when the producing tool emits FNF/FNH and BRF/BRH

Set those thresholds to 0 rather than living with a permanent warning. Treating null as zero would fail every Go and Python project for a dimension their tools cannot report.

If coverage fails:

  1. List exact metric deltas (against thresholds in absolute mode, against baseline in no-decrease mode), including the covered/total counts, not just percentages.
  2. Identify uncovered branches/functions by file.
  3. Name the lane each gap belongs in, per lane-policy. An uncovered error path in a DB adapter is an integration-lane gap; do not close it with a mock in the unit lane.
  4. Add missing tests, then rerun the full gate.

Gate 2: Test quality (new — enforced alongside coverage)

Coverage alone is insufficient. A test that touches every line but only asserts expect(mock).toHaveBeenCalled() provides zero regression safety.

Follow the assertion hierarchy and mock rules defined in the policy-core skill.

Quality scan procedure

For each test file in the coverage report:

  1. Count assertion types per test using the assertion hierarchy from policy-core:

    • Level 1-5 (behavior): return value checks, error checks, output checks, state checks, integration checks
    • Level 6-7 (wiring): mock call args, mock call counts
  2. Flag violations:

    • FAIL: Any it() block where ALL assertions are Level 6-7
    • WARN: Any it() block where Level 6-7 assertions outnumber Level 1-5 assertions by 3:1 or more
    • PASS: At least one Level 1-5 assertion exists
  3. Report format:

    Test Quality Summary:
    ✓ 45/48 tests have behavioral assertions
    ✗ 3 tests are wiring-only:
      - docker/container.test.ts: "applies security defaults" (line 52) — mock args only
      - cli/lifecycle.test.ts: "stops container" (line 48) — mock-was-called only
      - docker/network.test.ts: "creates network" (line 28) — mock args only
    
  4. Gate result: FAIL if any wiring-only tests exist in changed files. WARN (non-blocking) for existing wiring-only tests in unchanged files.

Gate 3: Coverage ignore directive audit

Scan all source files (not test files) for V8 coverage ignore comments. Flag misuse:

  • FAIL: /* v8 ignore next */ or /* v8 ignore next N */ — silently fails on ??, ternaries, catch bodies, and short-circuit operators (&&, ||). Replace with /* v8 ignore start */ / /* v8 ignore stop */.
  • PASS: /* v8 ignore start */ / /* v8 ignore stop */ range pairs.
Coverage Ignore Audit:
✗ 2 files use unreliable /* v8 ignore next N */:
  - src/config.ts:42 — covers ?? expression, will silently fail
  - src/handler.ts:88 — covers ternary, will silently fail
Fix: replace with /* v8 ignore start */ / /* v8 ignore stop */ range comments

Gate 0: Is the merge complete enough to measure against?

Runs before any threshold is compared, because a threshold applied to part of the project is a wrong number stated confidently.

SituationResult
Every lane with coverage: "include" produced a reportContinue
A contributing lane ran and produced no report, while coverage is enforcedFAIL, naming the lane. The merge would cover a subset
That lane is optional: trueStill fails. optional means "do not block on its tests", not "measure the project against whatever is left"
That lane is in bootstrapSkipped — a lane with no tests has nothing to report yet
No lane is bound to the trigger at all, while coverage or critical paths are configuredFAIL. Enforcement was configured and is structurally unreachable

The fix is a config change, not a threshold change: repair the lane, or set coverage: "none" on it if it was never meant to contribute.

Gate 4: Critical-path thresholds

A repo-wide threshold is one number for code with wildly different consequences of failure. criticalPaths adds a second, stricter bar scoped by glob — see the value heuristic in policy-core.

Evaluated from the merged report's per-file entries, so it works across every format the plugin reads.

SituationResult
Matched files meet the entry's thresholdsPASS
Matched files fall belowFAIL, naming the glob, the metric, and the shortfall
Entry omits thresholdsInherits coverageThresholds
Glob matches zero filesFAIL, naming the glob. Unlike a lane in bootstrap there is no legitimate steady state: either the glob is wrong, or the named code has no measured coverage. Both are what the entry exists to catch
Merged report has no per-file dataFAIL — a configured strict rule that cannot be evaluated must not report success
A metric the format does not measureWARNING, never FAIL — null is not zero
The merge was weighted rather than a unionEvery critical-path percentage is flagged APPROXIMATE — a shared file is counted once per lane
A threshold is set on a dimension the merge could not compute exactlyFAIL. Functions and branches have no per-item identity across reports, so a multi-lane merge keeps the strongest lane — which can report 1/1 = 100% for a file with 100 functions. Emit one authoritative report for those files, or set that threshold to 0 on the entry

Critical paths are absolute bars in both coverage modes. Under no-decrease the repo-wide comparison is against a moving baseline, but a critical path holds its fixed number regardless — and a failing critical path does not bank a new baseline, which would freeze the failure in as the new normal.

Critical Paths:
✗ src/payments/**  (7 files)  lines: 98.21% < 100.00% (110/112)
✓ src/auth/**      (4 files)  lines=100.00%, branches=100.00%
✗ src/paymnets/**  matched no file — check the glob against your reporter's paths

How to fix wiring-only tests

Current assertionAdd thisExample
expect(mockCreate).toHaveBeenCalledWith(opts)Verify return valueexpect(result.id).toMatch(/^mx-/)
expect(mockStop).toHaveBeenCalled()Verify formatted outputexpect(formatter.success).toHaveBeenCalledWith("Mecha stopped.")
expect(mockCreate).toHaveBeenCalledWith(securityOpts)Add integration testconst info = await inspect(container); expect(info.HostConfig.ReadonlyRootfs).toBe(true)

Scope

Covers threshold enforcement: coverage modes, multi-lane merging, null-metric handling, critical-path thresholds, the test-quality scan, and the coverage-ignore directive audit.

Does NOT cover:

QuestionWhere
How is each report format parsed?commands/shared/parse-coverage.md
Which lanes contribute coverage, and why?tdd-guardian:lane-policy
What coverage tool does language X use?tdd-guardian:tooling-catalog
How is test strength measured beyond coverage?tdd-guardian:mutation-gate
How strong is the claim a test makes?tdd-guardian:policy-core (S1-S6)

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.