Usage & Configuration

SpecTracer is configured entirely through a JSON file — no CLI flags. Drop a config at your project root and run.

CLI Usage

The tool follows an ESLint-style convention: all configuration lives in a JSON file, and the CLI just runs it.

Basic usage
# Auto-discover spectracer.config.json in current directory
uv run python build_pyramid.py

# Point at an explicit config file
uv run python build_pyramid.py path/to/other-config.json

That's the entire CLI surface — one command, one optional argument. No --help flags, no subcommands, no environment variables.

⚠ Shell note Use forward slashes (./config.json) or a bare filename. A leading .\ (PowerShell-style) can be mangled by POSIX-style shells (Git Bash, WSL) since backslash is their escape character.

Exit Codes

CodeMeaningWhen
0SuccessReport generated, no errors
1FailureConfig error, parse error, or error_on_failure: true with test failures

Configuration File

By default, SpecTracer looks for spectracer.config.json in the current directory. Here's a complete example:

spectracer.config.json
{
  "features": ["./features"],
  "unit": {
    "": ["./reports/unit.xml"],
    "billing": ["./reports/billing-unit.xml"]
  },
  "integration": {
    "": ["./reports/integration.xml"]
  },
  "e2e": ["./reports/e2e.json"],
  "output": "./report.html",
  "error_on_failure": false,
  "health_checks": {
    "progress_threshold_green": 80,
    "progress_threshold_amber": 50,
    "e2e_duration_amber_seconds": 600,
    "e2e_duration_red_seconds": 1800
  }
}

Tagging Convention

Feature files and test results connect through shared tags on scenarios. There are two kinds:

Linking Tags

Tags like @FC-42 or @regression appear on both the Gherkin scenario and the test result. Any tag match links the test to the scenario (OR logic).

Layer Requirement Tags

Tags like @require-unit, @require-integration, or @require-e2e declare which layers must have coverage. These are excluded from linking — they only affect the report's "Required Layers" status.

Example feature file
Feature: User Login

  @FC-42 @regression @require-unit:auth @require-integration:auth @require-e2e
  Scenario: Successful login with valid credentials
    Given the user is on the login page
    When they enter valid credentials
    Then they should be redirected to the dashboard

  @FC-43
  Scenario: Login with invalid password shows error
    Given the user is on the login page
    When they enter an invalid password
    Then an error message should be displayed

Matching Rules

RuleDetail
Exact string match@FC-42 matches @FC-42 only, not @FC-4 or @FC-42-smoke
OR logicA scenario tagged @FC-42 & @regression links to a test carrying just @regression
Scenario tags onlyTags on the Feature: line are not inherited by scenarios
No linking for @require-*Layer requirement tags are excluded from linking logic
CollisionsIf two scenarios share a tag, one matching test links to both

Where the Tool Looks for Tags

FormatTag Locations
JUnit XMLname attribute, classname attribute, or <properties><property> elements
Cucumber JSONNative scenario-level tags array

Full Configuration Reference

KeyRequiredTypeDescription
features Yes string[] Gherkin .feature file/directory paths. Directories are searched recursively.
unit No {string: string[]} Module-keyed JUnit XML paths. Key "" for unscoped results. Matches @require-unit / @require-unit:<module>.
integration No {string: string[]} Same shape as unit. Matches @require-integration / @require-integration:<module>.
e2e No string[] Cucumber JSON file/directory paths. Not module-scoped.
output Yes string Output path for the HTML report. Parent directories are auto-created.
error_on_failure No boolean Exit non-zero when any test fails. Default: false. Health checks never affect exit code.
health_checks No object Threshold overrides for health indicators.

Health Checks

Health checks are visual indicators on the report dashboard. They never affect the exit code — only error_on_failure does.

CheckConditionPassWarnFail
Progress % of scenarios with at least one linked test ≥ green threshold ≥ amber threshold < amber threshold
Pyramid Ratio Unit count vs Integration + E2E count Unit > I+E Unit ≤ I+E
E2E Runtime Total E2E duration in seconds ≤ amber ≤ red > red
Unlinked Tests Number of orphaned test results 0 1–3 4+

Default thresholds:

Module-Scoped Requirements

@require-unit and @require-integration accept an optional :modulename suffix (e.g. @require-unit:auth). This pairs with module-keyed entries in the config:

Module-scoped config
{
  "unit": {
    "": ["./reports/unit.xml"],          // unscoped — matches bare @require-unit
    "auth": ["./reports/auth-unit.xml"]     // module "auth" — matches @require-unit:auth
  }
}
⚠ Strict matching A module-scoped requirement (@require-unit:auth) is only satisfied by a result registered under that exact module key. An unscoped result (config key "") never satisfies it. A bare @require-unit (no module) is satisfied by any linked unit result regardless of module.

@require-e2e does not accept a module suffix — E2E scenarios typically span multiple modules.

Configuration Examples

Minimal setup (E2E only)

spectracer.config.json
{
  "features": ["./features"],
  "e2e": ["./reports/e2e.json"],
  "output": "./report.html"
}

Full stack (three layers)

spectracer.config.json
{
  "features": ["./features"],
  "unit": { "": ["./reports/unit.xml"] },
  "integration": { "": ["./reports/int.xml"] },
  "e2e": ["./reports/e2e.json"],
  "output": "./report.html",
  "error_on_failure": true
}

Multi-module with health check thresholds

spectracer.config.json
{
  "features": ["./features", "./modules/billing/features"],
  "unit": {
    "": ["./reports/unit.xml"],
    "billing": ["./modules/billing/reports/unit.xml"],
    "auth": ["./modules/auth/reports/unit.xml"]
  },
  "integration": {
    "": ["./reports/int.xml"],
    "billing": ["./modules/billing/reports/int.xml"]
  },
  "e2e": ["./reports/e2e.json"],
  "output": "./report.html",
  "health_checks": {
    "progress_threshold_green": 90,
    "progress_threshold_amber": 70,
    "e2e_duration_amber_seconds": 300,
    "e2e_duration_red_seconds": 900
  }
}