Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

What it reifies

@frenchexdev/requirements-compliance is the largest single capability in the family and the one with the highest contact with day-to-day workflow. Every package in the ecosystem runs npx requirements compliance --strict as its req:compliance script; that command is, end-to-end, this package's generateReport function plus a renderer. Its job is to take the scanner's BindingsManifest, the kernel's Feature and Requirement registries, and the kernel's Satisfaction links, and produce — in one pass — a four-tier traceability matrix that flags every orphan, every unbound AC, every Feature without a @Satisfies link, and every FSM-Feature mismatch.

The package's package.json description: "Compliance core + compliance report — Feature/Requirement scanning, runtime-coverage aggregation, orphan detection, traceability matrix renderer."

The public surface

Two layers. The core (compliance-core) computes the report; the renderer (compliance-report) turns it into console table, by-test view, or JSON payload. The src/index.ts re-exports both, with a small DRY-violation note that the roadmap flags for later cleanup:

export * from './compliance-core';
// Explicit re-exports from compliance-report (avoids TestLevel collision with
// compliance-core — pre-existing DRY violation flagged in the decomposition roadmap).
export {
  hasBareDescribeOrIt,
  validateFsmFeatureLinks,
  scanTestFiles,
  computeBindingsSummary,
  crossReference,
  buildJsonPayload,
  generateReport,
  glyphFor,
  formatRuntimeCell,
  formatBindingsCell,
  traceabilityGlyph,
  formatBindingsCellColored,
  renderConsole,
  renderByTest,
  buildByTestJson,
} from './compliance-report';
export type {
  TestRef,
  ACCoverage,
  BindingsStatus,
  FeatureBindingsSummary,
  FeatureCoverage,
  MachineLink,
  FsmFeatureLinkReport,
  GenerateReportOptions,
  GeneratedReport,
  RenderConsoleOptions,
  TableColumn,
} from './compliance-report';

The named re-exports look verbose, but each entry pays for itself: hasBareDescribeOrIt enforces the req-dog-food ban on describe/it; validateFsmFeatureLinks enforces that every state machine is reachable from a Feature; scanTestFiles is the per-package test-walker; crossReference joins the manifest with the registries; generateReport is the orchestrator that callers actually invoke.

Where it sits

Tier 1, the second-most-dependent analyzer. Depends on requirements-scanner (for the manifest) and requirements-requirements (for the vocabulary it cross-references). Consumed by requirements-trace (which enriches the report with graph nodes), requirements-spec-io (which round-trips the report to JSON Schema), requirements-scaffolders (which scaffolds tests for the orphans the report finds), and the CLI (which is what the user actually sees).

Two things the package must not do:

  • Render to anything but data structures. Console output is text on stdout; JSON output is a JSON-stringifiable payload; the byTest view is its own typed payload. The package never writes files, never opens sockets, never invokes process.exit. The CLI (Part 16) decides what to do with the exit code.
  • Mutate the registries. Reading is the only operation. A pure compliance run produces no side effects beyond rendering.

A concrete call-site

The CLI's compliance subcommand is the canonical caller. The shape, with types from this package:

import {
  generateReport,
  renderConsole,
  buildJsonPayload,
  type GenerateReportOptions,
  type GeneratedReport,
} from '@frenchexdev/requirements-compliance';
import { scanTestBindings } from '@frenchexdev/requirements-scanner';
import { getFeatureRegistry, getRequirementRegistry, getSatisfactionLinks }
  from '@frenchexdev/requirements-shared-kernel';
import { fs } from '@frenchexdev/requirements-core/ports';

const options: GenerateReportOptions = {
  testDir: 'test',
  srcDir: 'src',
  strict: true,
  fsmFeatureLinks: true,
};

const bindings = await scanTestBindings(fs, options);
const report: GeneratedReport = generateReport({
  bindings,
  features: getFeatureRegistry(),
  requirements: getRequirementRegistry(),
  satisfactions: getSatisfactionLinks(),
  options,
});

renderConsole(report, { coloured: true });

if (options.strict && report.violations.length > 0) {
  process.exit(1);
}

The boundary is clean. The scanner produces the manifest; the kernel exposes the registries; generateReport cross-references them and returns a GeneratedReport. The renderer is a pure function over the report. The CLI decides the exit code. Each layer can be tested in isolation — the core with a synthetic manifest and registries, the renderer with a synthetic report, the CLI with a stub of both.

The strict flag is what npx requirements compliance --strict (feedback_requirements_compliance_command.md) wires. In strict mode the renderer reports the same matrix, but any violation — orphan Feature, unbound AC, missing @Satisfies, FSM link mismatch, bare describe/it in a test file — becomes an exit-code-1 failure. Every package in the family runs the strict mode as its dog-fooding gate; the gate must pass before the package can publish.

Why it is its own package

Three arguments, each more pointed than the previous.

First, the report is the integration boundary between scanner, kernel, and CLI. Three separate concerns — what the tests bind to, what the spec declares, what the user wants to see — meet here. Keeping the meeting point in its own package means the three concerns can evolve independently: the scanner can change its emit format, the kernel can grow new registries, the CLI can add new subcommands, and only the cross-reference logic needs to be updated. In the pre-split state, all three concerns lived in requirements-lib, so changing the scanner forced re-testing the renderer.

Second, the report is consumed by both the CLI and the build pipeline. ts-codegen-pipeline calls crossReference directly to validate the scanner output before emitting generated source. If compliance lived in the CLI package, the build pipeline would have to depend on commander and @clack/prompts — two libraries it never needs. Extracting compliance to its own package means the build pipeline depends only on requirements-compliance and requirements-scanner, and skips the CLI entirely.

Third, the renderer's polymorphism wants room to breathe. Three render modes today (console table, by-test JSON, top-level JSON), more planned: HTML for the CV's blog pipeline, SVG for status badges, Markdown for PR comments. The renderer is the natural future home of those modes; keeping it in a package whose only job is reporting means each new mode is a new exported function rather than a new branch in a monolithic file.

The package's coverage threshold is the strictest in the family. Every Tier-1 sibling can have its own gates, but requirements-compliance itself is the gate, so its own gate must be unimpeachable: per-file ≥98% line / branch / function / statement, no exceptions, and npx requirements compliance --strict against its own self-Features must pass clean before any change is merged.

The next page covers the smallest Tier-1 sibling — requirements-test-smells — which closes the gap that pure compliance reporting cannot cover.

⬇ Download