Coverage

SkillDev tools

Lets your agent run unit-test coverage checks and read the results to fix failing coverage checks in a repo.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Coverage skill

About this capability

Run and read unit-test coverage for this repo and satisfy the codecov/patch and codecov/changes gates. Use when a PR check fails on coverage, after editing gated production code under sources/ or scripts/, or when the user mentions coverage, Codecov, patch, or uncovered lines.

What this skill tells your AI

The instructions your AI receives, as published by liberatedpixelcup/universal-lpc-spritesheet-character-generator in .agents/skills/coverage/SKILL.md and read by ahel’s review.

Two separate, non-overlapping reports. Pick by the path you edited.

EditedCommandReport
sources/npm run test:browser:coveragecoverage/browser/index.html
scripts/npm run test:node:coveragecoverage/node/index.html

The flags in codecov.yml scope browser to sources/ and node to scripts/. The wrong command reports nothing for your file — it does not fail, it just shows no data. Run both only if you edited both trees.

Both are full-suite runs. There is no per-file coverage run; see run-one-spec for isolation while iterating, then one coverage run at the end.

npm run test:browser:coverage launches Chrome and Firefox (testem.cjs launch_in_ci). If Firefox is not installed locally, confirm with Chrome only:

VITE_COVERAGE=true node ./node_modules/testem/testem.js ci --launch Chrome

merge-browser-coverage.ts merges whatever JSON is already in coverage/browser/. Firefox-only lines will read as uncovered.

Before you conclude anything

Check whether the path is in the ignore: block of codecov.yml first. Ignored paths owe no test, and a coverage run tells you nothing about them. Currently ignored: tests/**, dist/**, spritesheets/**, sheet_definitions/**, scripts/coverage/**, scripts/computed-style/**, scripts/imageProcessing/**, scripts/zip/**, scripts/profile/**, several individual scripts/*.ts files, vite/**, vite.config.ts, testem.cjs, sources/performance-profiler.ts, sources/utils/debug.ts, and all CSS/SCSS. Read the file rather than trusting this list.

Reading the report

Open the HTML and confirm every new or edited production line is marked hit. Do not wait for Codecov to tell you. The terminal summary is enough for a quick check, but it reports per-file percentages, not which line was missed.

Browser collection is off unless VITE_COVERAGE=true; the test:browser:coverage script sets it. Plain npm test and npm run test:server are uninstrumented, so they will never show coverage.

A spec that is not imported from tests/tests.js never runs. That produces uncovered lines with no test failure — the most common cause of a surprising patch failure on a browser change.

Hole vs miss

A DA:0 is a hole when a neighbor or the block body already has a hit and an existing spec already asserts the behavior. Do not add another spec for that line. Concise wrappers such as span.async("name", () => fn()) and span.sync(...) in a finally are the usual cases that look uncovered.

mark-non-executable-lines.js promotes those holes when they are a straight-line statement in an entered function or try / finally / catch, or a control-flow header (if ( / while ( / for ( / do) whose body already has a recorded hit. It does not promote a then-body just because if ( ran, or a catch-only return just because the function or try was entered.

If the marker still leaves DA:0 on a wrapper, extract a named helper so those lines become a function body. fillStraightLineSourceMapHoles already covers that.

A DA:0 is a real miss when no neighbor or body hit exists, or no spec asserts the behavior. Add a spec.

Patch miss table

Do not wait for the Codecov PR comment. It still will not list line numbers.

After test:browser:coverage / test:node:coverage, the same scripts print file:line plus the trimmed source for every remaining patch DA:0. Those commands stay green so CI can upload lcov to Codecov. The codecov/patch check is what fails on a miss. Read that table (or the Test browsers job log when the local HTML is stale).

Standalone, against reports that already exist (exit 1 if any miss):

npm run coverage:patch

--base overrides the merge-base ref. Precedence is --base, then GITHUB_BASE_REF (as origin/<name>), then origin/master.

The gates

  • codecov/patch — 100% of new or edited production lines under sources/ (browser flag) or scripts/ (node flag) must be executed, except ignored paths. Threshold is 0%, so one missed line fails it.
  • codecov/changes — previously covered lines must not lose hits. Deleting or weakening a test trips this even when your new code is fully covered.
  • There is no project-percentage gate, so adding a large file does not fail a PR on the average.

Both use if_not_found: success, so they pass silently when there is no base report to compare against.

Why comment-only diffs still pass

mark-non-executable-lines.js rewrites the uploaded lcov.info to mark blank lines and comment-only lines as hit. It uses the TypeScript scanner, so a trailing comment on a code line does not count as non-executable. Both pipelines apply it: test:node:coverage runs it directly, and the browser suite calls markNonExecutableLinesInLcov from merge-browser-coverage.ts in the Testem on_exit hook.

So a documentation-only or comment-only diff is expected to pass the 100% patch gate. If you see comments counted as missed lines, the post-processing step did not run — check that the coverage script finished rather than adding a test.

Patch failed on a line local DA already marks hit

Two upload problems have caused this. Check both before adding a test or editing the marker's line classifier.

  1. codecov-action@v5 searches the tree unless disable_search: true. files: is additive. Without that flag the browser step also uploaded coverage/browser/coverage-final.json and coverage/node/tmp/*.json.
  2. Istanbul BRDA / FN rows use a worse source map than DA. They land on type aliases and function foo( / name: Type, lines with zeros. Codecov then reports a miss or partial even when DA is hit. The marker drops those records so the uploaded lcov is line coverage only.

Do not widen mark-non-executable-lines.js for a signature line already in nonExecutableLineNumbers. Do not add a test for the parameter list. Extracting a named type (RootViewRef) is fine for readability; it does not satisfy the gate by itself. The marker may promote entered try / finally statements and headers whose body already has a hit; it still must not promote then-bodies or catch-only returns.

Full description of the CI wiring: CONTRIBUTING.md.

Signals

GitHub stars
2k
Forks
580
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
coverage-liberatedpixelcup
Source
github.com/liberatedpixelcup/universal-lpc-spritesheet-character-generator