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

One Package, One Responsibility

A walkthrough of the @frenchexdev/requirements-* TypeScript corpus — seventeen single-responsibility packages plus two cross-cutting ones, organised in four hexagonal tiers from a zero-dependency shared kernel up to a thin Commander CLI. Each page covers one package: what it reifies, the public surface it exposes, the tier it lives in, and the SOLID/DRY argument that forced its extraction from the former monolithic requirements-lib barrel.

This series is the prose companion to the working roadmap work/ROADMAP-REQ-ECOSYSTEM-DECOMPOSITION.md. Where the roadmap is a phased migration plan written for the maintainer, the series is a guided tour written for the reader who wants to understand why a "requirements" DSL warrants seventeen npm packages.


The thesis: requirements-as-code, dog-fooded to its limit

Every canonical concern in the corpus ships as its own npm package. The runtime decorators, the AST scanner, the compliance reporter, the mutation-testing harness, the scaffolders, the sync engine, the wizards, the CLI — each is a separate packages/requirements-* directory with its own package.json, its own tsc --noEmit gate, its own coverage thresholds, and its own npx requirements compliance --strict self-check.

The keystone is @frenchexdev/requirements-requirements. That package contains no logic. Its src/ is a list of req-*.ts files — each file an exported class extending Requirement from the shared kernel — that ARE the requirements of the requirements engine itself. req-dog-food.ts declares the rule that the DSL must be tested with itself; req-dsl-complete.ts declares that the DSL must track Requirements, not just Features; req-spec-is-source-of-truth.ts declares that generation is idempotent and sync-able. The package preaches what it lives.

Requirements-as-code stops being a slogan when the spec of the engine is an importable TypeScript module. SOLID's Single Responsibility, DRY's "every piece of knowledge has one representation", and Dog-fooding's credibility test all converge on the same physical artefact: a src/ directory full of typed Requirement subclasses, re-exported by index.ts, scanned by the same requirements-scanner that scans every other package.


How the tiers stack

Four tiers, dependency arrows pointing inward only:

  • Tier 0 — Shared Kernel. The base types (Feature, Requirement<S>, Priority, TestLevel, ACResult), the decorators (@FeatureTest, @Verifies, @Satisfies, @Refines, @Expects, @Exclude, @TypeCheckOnly), the RequirementStyle abstraction, branded IDs, and the FileSystem port. Zero runtime dependencies. Every other package depends on this.
  • Tier 1 — Domain Services (Analyzers). Stateless operations over the domain: the AST scanner for test bindings, the compliance reporter, the test-smell detector, the behavioural-check mutation runner, the trace graph builder, the spec serialiser, the content-hashing versioner.
  • Tier 2 — Application Services (Use Cases). Orchestrated workflows the CLI exposes: scaffold a test suite, sync a spec to disk, run an interactive wizard.
  • Tier 3 — Inbound Adapter. A single thin commander + @clack/prompts + picocolors shell, the requirements bin. It owns no domain types; it translates CLI input into Tier-2 use-case invocations.

Cross-cutting, outside the tier stack: requirements-styles (five presets — Default, Industrial, Lean, Agile, Kanban — plus a registry) and requirements-styles-demo (a scenario-driven CLI player). They depend on Tier 0 only and can be ignored by anyone who is happy with the default style.

The full dependency graph and the DDD mapping (Bounded Context, Ubiquitous Language, Shared Kernel, Domain Services, Application Services, Inbound Adapter) live in Part 02.


Part 01: The Monolithic Barrel

What requirements-lib looked like before the split — a single src/ mixing analyzers, application cores, and port-driven utilities. The cyclic-deps WARN that surfaced the underlying problem. Why a barrel is a smell once a family grows past three concerns.

Part 02: DDD/Hexagonal Tiers

The four hexagonal tiers and the two cross-cutting packages, with the acyclic dependency graph rendered as a Mermaid diagram. The DDD mapping (Shared Kernel, Domain Services, Application Services, Inbound Adapter) and the SOLID enforcement the layering makes physical.

Part 03: Requirements as Code

The thesis article. Why @frenchexdev/requirements-requirements/src/req-*.ts is the requirements engine's own specification — and why that is dog-fooding's logical endpoint, not its parody.

Part 04: requirements-shared-kernel

The zero-dependency Tier-0 contract: base classes, six decorators, the RequirementStyle abstraction, branded IDs, and the FileSystem port. The only universal contract in the family.

Part 05: requirements-requirements

The domain vocabulary package — Requirement<S> and Feature concrete classes, plus the self-hosted req-*.ts files that specify the entire ecosystem. The keystone of requirements-as-code.

Part 06: requirements-scanner

The AST scanner for @FeatureTest and @Verifies bindings. Walks *.test.ts files, resolves symbol targets in src/**, produces a Feature×AC → SymbolTarget[] manifest.

Part 07: requirements-compliance

Coverage aggregation, orphan detection, traceability matrix, console + JSON renderers. The package behind npx requirements compliance --strict — the strict gate every other package runs in CI-equivalent.

Part 08: requirements-test-smells

AST-level gap detection between declared @Verifies targets and the test bodies that claim to verify them. Where AST grounding stops and behavioural grounding begins.

Part 09: requirements-behavioral-check

Mutation testing per Feature.AC via Stryker. Three pure layers — plan, build-config, classify-verdict — wrap the orchestration; the package is the only one in the family that owns its mutation-runner dependency.

Part 10: requirements-trace

The REQ → FEAT → AC → TEST query engine and the Mermaid / DOT graph renderer. The package that turns the manifest into a navigable picture.

Part 11: requirements-spec-io

Bidirectional serialisation between the TS source-of-truth and the JSON schema, with ajv validation and ts-morph source-rewriting. Where the spec stops being TypeScript-only.

Part 12: requirements-versioning

Content-hashing of specs (node:crypto only), version pins, drift detection, history bookkeeping. The audit trail of every breaking change.

Part 13: requirements-scaffolders

The port-driven TestScaffolder registry plus seven built-in scaffolders (unit / functional / e2e / a11y / i18n / visual / perf). Zero CLI dependencies — pure ports — so any Tier-3 shell can consume it.

Part 14: requirements-sync

SyncPlan diff of a generated spec against disk, coloured unified diff rendering (diff + picocolors), and all / per-file / dry-run application modes. The terraform-style preview/apply pattern, scoped to a DSL.

Part 15: requirements-wizards

The feature new and requirement new interactive cores plus orchestrate-spec-wizard. Pure ports — no Commander, no clack — so the same cores drive the CLI today and an IDE extension tomorrow.

Part 16: requirements (the CLI)

The only Tier-3 package: a thin commander + @clack/prompts + picocolors shell exposing the requirements bin and the ./analysis / ./cli / ./ports subpath exports. The roadmap's Phase 4 will shrink it further.

Part 17: requirements-styles

Five style presets — Default, Industrial, Lean, Agile, Kanban — plus a StyleRegistry. The open/closed seam of the RequirementStyle abstraction; everyone picks their vocabulary without forking the kernel.

Part 18: requirements-styles-demo

The rsd scenario-driven demo player. A three-FSM orchestrator (per-project-lifecycle, per-feature, per-AC) showing how each preset behaves. Why a demo CLI deserves its own package.

Part 19: How to Add a Tier-N Package

The cookbook. Naming convention, package.json shape, allowed dependencies per tier, the exports map, and how to dog-food the new package's own requirements by adding a req-*.ts to requirements-requirements/src/. Step-by-step from empty directory to wired-in.

Part 20: Roadmap — Phases 4–6

Where the split is heading: Phase 4 CLI slim-down, Phase 5 rename requirements-corerequirements-sharedkernel (1,263-file codemod), Phase 6 sunset of the requirements-lib barrel. The decisions still open and the verification gates at each phase boundary.

⬇ Download