Using the output

Report tour

Six pages, one file. This is what each one is for, what the numbers mean, and what to do when they're bad. Every screenshot here is SpecTracer's own self-report — real numbers, not a mockup.

Live sample report

See the real thing — no setup required. Click below to open a fresh tab with SpecTracer's actual HTML output from this repo, showing all of its pages. It's exactly what you'll get after running the tool on your own project.

Open live report in a new tab

The file itself

The report is a single self-contained HTML file. All CSS and JavaScript are inlined. By default the pages are rendered server-side into the markup; with render_mode: "client" the report JSON is embedded inline instead and the same pages are built from it in the browser. Either way, the only network request is a monospace webfont from a CDN, which degrades to the system monospace if you're offline.

1 · Overview

The page that answers "how are we doing" in about three seconds.

The Overview page: three headline tiles reading 50/55 declared tests matched, 23/28 scenarios fully matched, 319/319 tests passed; below them five health-check cards for Progress, Test pyramid, E2E runtime, Unlinked, and Unconfigured modules.
Overview — headline coverage plus the five health signals.

The three headline tiles

TileNumerator / denominatorRead it as
Declared tests matched Satisfied @require-* layer/module pairs, over all declared pairs. "How much of the coverage we said we needed do we actually have?" This is the headline metric and the one fail_on: ["progress"] gates on.
Scenarios fully matched Scenarios with every declared layer satisfied, over all scenarios. Strictly harder. A scenario needing three layers and having two contributes 2/3 to the first tile but 0 to this one.
Tests passed Passing results over all collected results. Plain suite health. Counts every result, linked or not.
Why the two coverage numbers differ so much In the screenshot above, 91% of declared tests are matched but only 82% of scenarios are fully matched. That gap is the normal shape of a partially-tagged codebase: coverage is spread thinly across many scenarios rather than completed on a few. Watch the gap close as you finish scenarios rather than start them.

The five health cards

Each card is green, amber, or red, and links through to the page that explains it. Thresholds come from health_checks in your config — see Health checks.

Progress green

Declared-tests-matched percentage against your green/amber thresholds. Amber means "below target but not alarming"; red means below the amber floor.

Test pyramid green

Green while unit tests outnumber integration + E2E combined. Amber at exact parity, red when inverted.

E2E runtime green

Total E2E wall time against your time budget. This is the check that catches a suite getting slowly, invisibly slower.

Unlinked red

Orphaned results. Green at zero, amber at 1–3, red at 4 or more. Visual only — this one cannot gate CI.

Unconfigured modules red

Count of declared @require-*:module requirements whose module isn't a registered config key for that layer at all. Binary — green at zero, red otherwise, no amber band. This flags a likely config typo, distinct from a genuine coverage gap; see Expanding a scenario below and Troubleshooting.

2 · Test Pyramid

Test count, duration, and pass rate for each layer, drawn as proportional bars.

The Test Pyramid page: three horizontal bars for E2E (56 tests, 5.0s), Integration (65 tests, 5.9s), and Unit (198 tests, 1.0s), each with pass, fail, and skip percentages.
Test Pyramid — execution mix across unit, integration, and E2E.

The shape is the point. A healthy pyramid has a long unit bar and short integration and E2E bars. What to look for:

Counts, not opinions The pyramid reflects what you registered under unit, integration, and e2e in the config. If your "unit" tests spin up a database, SpecTracer will happily report a beautiful pyramid — it can only classify what you tell it.

3 · Feature Breakdown

The traceability matrix, and where you'll spend most of your time. A searchable tree: feature → scenario → individual test result.

The Feature Breakdown page: an expandable tree of features with completion bars and result pills; a partially-completed scenario expanded to show its raw @tags, required-layer chips — UNIT OK, E2E OK, INTEGRATION MISSING — the full syntax-highlighted Gherkin steps, and the individual results that linked to it.
Feature Breakdown — a partially-completed scenario expanded, its INTEGRATION (linker) MISSING chip showing exactly what's left to write.

Row by row

ColumnWhat it shows
NameFeature or scenario name. Scenario rows also carry the scenario id pill (e.g. FC-002) and, on result rows, the discovery module.
CompletionA bar and a satisfied/declared fraction — green at 100%, amber below.
ResultRolled-up pass/fail/skip status.
DeclaredHow many @require-* pairs this row declares.
ActualHow many test results actually linked. This can exceed Declared — three unit tests can link to a scenario that declares one unit requirement.
DurationSummed wall time of the linked results.

Expanding a scenario

Expanding a scenario row reveals three things:

  1. Required-layer chips — one per declared requirement, showing the layer, the module in parentheses, and one of three states: OK, MISSING, or UNCONFIGURED. This is the single most actionable thing in the report: INTEGRATION (linker) MISSING tells you precisely what to go write. UNCONFIGURED (shown orange, distinct from red MISSING) means the requirement names a module that isn't even a registered config key for that layer — almost always a typo, not a coverage gap:
A scenario expanded showing three required-layer chips: UNIT (parsers) OK, E2E (parsers) OK, and an orange INTEGRATION (parsers) UNCONFIGURED chip, distinct from the red MISSING state.
An UNCONFIGURED chip — the integration config has no parsers key at all, so this can't be a genuine coverage gap.
  1. The scenario's tags, verbatim — every @tag from the feature file (including @require-* tags), shown below the required-layer chips exactly as they appear on the line above Scenario: in the source file.
  2. The full Gherkin text — the Given/When/Then as written, with keywords and quoted strings syntax-highlighted, so a reader who isn't in the code can still judge whether the scenario is worth the coverage it's demanding.
  3. The linked results — every test that claimed this scenario, with its layer, module, status, and duration.

Search filters the tree by name. Failure stack traces are deliberately not here — they live on the Failure Breakdown page so this view stays scannable.

3½ · Modules client mode

When your config registers two or more unit/integration modules, a Modules page appears next to Feature Breakdown. It splits the same tree one level down: one card per module, showing that module's completion, a unit/integration-only pyramid, and its unlinked count; the worst status (passed / skipped / failed) is shown as a colour stamp on the card.

The Modules page: a grid of per-module cards, each showing declared tests matched, a unit/integration pyramid, unlinked count, and a passed/no-results status pill.
Modules — one card per configured unit/integration module key, this repo's own dogfood config.

Clicking a card deep-links to #/modules/<key> and shows the Feature Breakdown tree scoped to that module: only the selected module's @require-unit / @require-integration chips and unit/integration results survive, so a service team can bookmark their own slice.

What doesn't make the cut Modules are derived from the unit / integration keys in the config — E2E is fleet-level, not service-level, so it never appears on a module card. The unscoped key "" isn't a module either. A report with fewer than two modules simply hides the page entirely.

4 · Failure Breakdown

Every failed test across all three layers, in one list, with feature and scenario context and the full stack trace on expand.

The value is not that it shows failures — your CI log does that. It's that a failure here arrives attached to the business scenario it broke, across layers that normally live in separate jobs. "Three failures, all under checkout" is a different triage conversation than three unrelated red lines in three log files.

5 · Unlinked Tests

Test results that carry tags but whose @scenario: values matched no scenario's @id:.

This page is your typo detector. On a first run, a large unlinked count and a near-zero coverage number almost always mean the same thing: the tags on one side don't match the tags on the other. Common causes:

Unlinked results still count toward the pyramid and the pass-rate tile — they ran, after all. They just don't count toward coverage.

The JSON twin

Set output_json and the same data is written as schema-validated JSON alongside the HTML. It's built from the same internal model as the HTML renderer, so the two physically cannot disagree. With render_mode: "client" the HTML embeds that exact JSON object itself, so the machine-readable report ships inside the single HTML file even without a separate output_json. The modules key holds the same per-module cards (completion, unit/integration pyramid, unlinked, worst status) that the Modules page renders.

reading the headline number in CI
jq -r '.summary.completion.percent' reports/spectracer-report.json
jq -r '.summary.health.status'       reports/spectracer-report.json
jq -r '.summary.health.reasons[]'    reports/spectracer-report.json

Use it for PR bots, custom gates the built-in checks don't cover, or posting trends to your own dashboard. Full field reference: Configuration → JSON report.

Using it in a standup

The headline numbers are designed to be said out loud. Unlike line coverage, they mean something to people who don't read code:

Track that trend properly by posting summary from the JSON report to whatever metrics system you already run — see Historical trends.