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

Chapter 04 — Twenty-Two Requirements, Part One

The spec layer is visible. This chapter and the next read every line of it.

The package declares twenty-two Requirements. They are the WHY tier of the four-tier chain Requirement → Feature → AC → Test, and together they constitute the spec layer of the whole DSL. Every Feature in packages/requirements/requirements/features/ points at one or more of them via @Satisfies; every test binds to an AC of a Feature that satisfies one of them; every compliance report the package emits is a function over them.

The previous three chapters named the shape (chapter 00 — the gap typed-specs left; chapter 03 — the class, the decorators, the registries). This chapter and the next spend the page budget to read the twenty-two declared instances. Not as a glossary — as a close reading of a spec, one entry at a time, in the voice of the declaring .ts file.

Why enumerate at all. A Requirements DSL only earns its keep if the spec layer is visible. The previous chapters showed the shape of Requirement<S> and the decorator surface that wires Features to it — but the shape is empty until you populate it. Listing the twenty-two REQs in prose does three things at once. It gives every later chapter a set of stable forward-links (chapter 06 points back to named REQs here when it draws the bipartite graph; chapter 07 points back when it walks test bindings). It gives a reader who picks the series up from any chapter a place to land when an identifier like REQ-EDITOR-FIRST-CLASS appears in passing. And it forces the author to quote the .ts fields rather than paraphrase them — which, in a series about dog-fooding, is itself the point.

There is a fourth reason, less obvious: the enumeration is the moment the author owns the inventory. Declaring twenty-two Requirements is declaring twenty-two commitments, and the act of writing them down — with priorities, statuses, risks — is itself a test of whether the commitment set is coherent. If two REQs conflict, writing them side-by-side surfaces the conflict. If a REQ is vacuous (a principle with no fit criterion, a claim with no evidence), writing the body reveals the vacuity. If the set is incomplete (some obvious DSL concern is nowhere named), the gap becomes visible. None of these checks are automated in the package; they are effects of the act of enumeration. This chapter, in reading the eleven REQs one at a time, performs the same test a second time — asking not just whether each REQ is coherent but whether the eleven together form a coherent half of the spec layer.

A note on voice. The REQ files are terse. Each is a TypeScript abstract class with five to seven non-optional fields, no prose beyond a one-line JSDoc at the top (when a file is Tier 2). The temptation in writing this chapter was to gloss each REQ in ten paragraphs of narrative prose. The discipline is the opposite: quote the field values as they appear, paraphrase only where the quote is load-bearing, and let the shape of the class carry the rhetorical weight. Where a rationale cites a study or a precedent, name it. Where a fit criterion binds a unit-test name, quote the name. Where a risk field marks a level, use the declared level, not a softer word.

This is the first of two halves. The eleven Requirements in this chapter are:

  • REQ-DOG-FOOD
  • REQ-BOOTSTRAP-ZERO-FRICTION
  • REQ-SPEC-IS-SOURCE-OF-TRUTH
  • REQ-ROUND-TRIP-EDITABLE
  • REQ-EDITOR-FIRST-CLASS
  • REQ-TUI-MODERN
  • REQ-LIVE-FEEDBACK
  • REQ-VISUAL-TRACEABILITY
  • REQ-DISCOVERABLE-TRACEABILITY
  • REQ-LOW-FRICTION-ACS
  • REQ-ORTHOGONAL-TOGGLING

The other eleven — invariants, refactor-safety, versioning, scenarios, orchestration, extensibility, audit, AI-adapter, vocabulary, parallel deliverable, completeness — are covered in chapter 05. Together the two chapters are the full enumeration. Chapter 06 then walks the bipartite REQ↔FEAT graph; chapter 07 walks the test bindings.

A last orientation note before the enumeration. These eleven split naturally into four clusters. The split is not encoded in any @Refines edge — no Requirement in this half refines any other of these eleven — but it is visible in the rationale fields and in the rhetorical function each REQ plays in the package:

  • Ergonomics — bootstrap-zero-friction, tui-modern, live-feedback, low-friction-acs, orthogonal-toggling. How the DSL feels.
  • Fidelity — spec-is-source-of-truth, round-trip-editable, editor-first-class. How the spec↔code contract holds.
  • Traceability — visual-traceability, discoverable-traceability. How the graph is surfaced.
  • Meta — dog-food, alone. How the package validates itself.

The diagram at the end of this chapter draws the split. The eleven sections in between read the Requirements one by one.

One more framing note on Tier 1 versus Tier 2. Of the eleven REQs in this chapter, five are status: 'Approved' as const and six are status: 'Draft' as const. The package is transparent about the distinction. Approved REQs are committed behaviours — there is at least one Feature under packages/requirements/requirements/features/ that declares @Satisfies for the REQ, and at least one test under packages/requirements/test/ that binds to an AC of that Feature. Draft REQs are declared intent — the REQ file exists, the shape is committed, but the satisfying Feature is either stubbed (enabled = false) or not yet scaffolded, and the tests are roadmap. The DefaultStyle.vocabulary.statusWorkflow lifecycle — Draft → Approved → Implemented → Verified → Deprecated — covers the full arc; everything in this chapter is in the first two states. Chapter 05 picks up some entries further along the arc.

The split between Tier 1 and Tier 2 is also a discipline. Declaring a Tier-2 REQ in source rather than in a product backlog forces the same rigour on both: every REQ, shipped or not, has a claim, evidence, fit criteria, and a risk statement. The roadmap becomes specifiable; the specification becomes reviewable; the review becomes a compliance check. The package runs npx requirements compliance --strict against both tiers — Approved REQs are gated on satisfiers-with-tests, Draft REQs are gated on shape (every field filled, every reference resolved).

How to read a REQ

Each of the eleven sub-sections below follows the same shape. A short reminder of what fields it will touch — so this chapter does not repeat the walk-through of chapter 03 eleven times, once per REQ.

Every declared Requirement extends Requirement<DefaultStyleType>. That base class (quoted in chapter 03, source in packages/requirements/src/base.ts) gives eight abstract fields and three optional ones. Each sub-section below quotes the ones that carry information:

  • id — the stable identifier, REQ-*.
  • title — a one-line human summary.
  • priorityCritical | High | Medium | Low, from the Priority enum.
  • status — a state of DefaultStyle.vocabulary.statusWorkflow; for this half always Approved or Draft.
  • kind — one of Functional | NonFunctional | Constraint | Compliance | UserStory, from DefaultStyle.vocabulary.requirementKinds.
  • statement — an EARS pattern (ubiquitous, event-driven, state-driven, optional, unwanted, or natural) with pattern-specific slots.
  • rationale — a { claim, kind, evidence[], assumptions? } record; kind drawn from DefaultStyle.vocabulary.rationaleKinds.
  • fitCriteria — the executable verifiables; one of seven discriminated kinds (unit-test, coverage-threshold, quality-gate, metric, inspection, demonstration, narrative).
  • verificationMethodTest | Inspection | Analysis | Demonstration.
  • source — provenance; here almost always { type: 'stakeholder', role: …, date: … }.
  • risk{ level, ifNotMet, mitigations? }, with level drawn from the DefaultStyle.vocabulary.riskTaxonomy.levels.

Each sub-section quotes the distinguishing fields from the .ts file, paraphrases what the Requirement actually demands, names what would break without it, and forward-links to the Features that satisfy it. Where the running example FEATURE-TRACE-EXPLORER-TUI satisfies the REQ, the connection is called out explicitly.

The order in this chapter mirrors the order in the chapter description. It is not a narrative arc — it is an enumeration. Read it as a reference list, not a plot.

A quick note on the quoted code blocks. Each sub-section opens with a TypeScript excerpt pulled directly from the .ts file under packages/requirements/requirements/requirements/. The excerpts are verbatim — the as const annotations, the readonly modifiers, the trailing commas all come straight from source. This is a deliberate discipline for this chapter: the Requirement is the class declaration. Paraphrasing the class without quoting it would hide the shape the chapter is trying to show.

The excerpts are partial — typically the first five to seven fields, occasionally the full rationale object where the evidence entries carry weight. The full file is always a short git-log away; the URLs in the "How it is satisfied" paragraphs link to the corresponding source. A reader who wants the complete declaration can click through. This chapter exists to make the declaration readable without clicking, not to replace the source.

A final editorial note. The voice follows chapter 00 — deliberate, close-read, no hype. The tone is the tone of a reference document, written by someone reading the spec aloud rather than selling it. The Requirements are what they are. The chapter's job is to show them, not to pitch them.

The DefaultStyle the eleven all share

Every REQ in this chapter extends Requirement<DefaultStyleType>. That is a uniform choice — no REQ in this half selects IndustrialStyle, LeanStyle, AgileStyle, or KanbanStyle. The reason is audience. The package is a developer-tooling DSL aimed at software teams doing spec-driven development. DefaultStyle — the ISO/IEC/IEEE 29148 + Volere + EARS preset — is the neutral ground for that audience: standard vocabulary, EARS statement patterns, a five-state status workflow, four levels of risk, four verification methods, six rationale kinds, eight source kinds.

Using DefaultStyle uniformly has two consequences worth flagging before the enumeration. First, every REQ in this chapter is validated by the same DEFAULT_VALIDATORSvalidateStatement checks the EARS pattern and its slots, validateSpec checks the top-level shape. A REQ whose statement.pattern was (say) 'stateful' would fail validation at load time, because stateful is not one of the six patterns the style exposes. Second, every REQ shares the same status workflow, so promoting a REQ from Draft to Approved is a uniform operation across the eleven: the transition is declared once in the style, and the compliance engine applies it identically to all.

The uniform Style choice does not preclude future diversity. The package is generic over the style (Requirement<S extends RequirementStyle>), so a later Requirement could plausibly declare extends Requirement<IndustrialStyleType> if it needed SIL levels, heavy-gate lifecycle, or regulatory-compliance provenance. Chapter 05 introduces one such case. For now, the eleven REQs of this chapter speak the same vocabulary because they address the same audience.

REQ-DOG-FOOD — The DSL must be tested with itself

readonly id = 'REQ-DOG-FOOD';
readonly title = 'The DSL must be tested with itself — @FeatureTest/@Verifies only, never describe/it';
readonly priority = Priority.Critical;
readonly status = 'Approved' as const;
readonly kind = 'Constraint' as const;

readonly statement = {
  pattern: 'ubiquitous' as const,
  response: 'use @FeatureTest and @Verifies for every test of every new command, against a Feature class under requirements/features/, and every Feature class must declare @Satisfies listing the Requirements it helps meet.',
};

The rationale is a one-line principle: "A DSL whose authors do not dog-food it has no credibility; this is already a non-negotiable project rule recorded in user memory." The risk is declared Critical"Package preaches a DSL it does not use; credibility collapses; users reject."

What this Requirement actually demands

Two things, both enforced at the source-tree level:

  1. Zero describe( and zero it( calls anywhere in packages/requirements/test. The first fit criterion is a quality-gate that pipes through ripgrep: "rg "\b(describe|it)\(" packages/requirements/test must return zero matches". If the gate emits a single line, the build fails. The pattern uses \b word-boundary anchors and a literal ( to catch actual call sites rather than incidental identifiers in comments or strings; describe appears in other contexts (as a field name on fit criteria, for instance) and those uses are benign.
  2. Every Feature class in packages/requirements/requirements/features/ declares @Satisfies(Req1, Req2, …) with at least one Requirement argument. The second fit criterion is a unit test bound to satisfiesDecoratorRegistersBidirectionalLink and complianceStrictFailsOnFeatureWithoutSatisfies. The complianceStrictFailsOnFeatureWithoutSatisfies binding is particularly load-bearing — it asserts that npx requirements compliance --strict exits non-zero if any Feature is discovered without an @Satisfies declaration, making the constraint CI-gated.
  3. src/cli/** clears 98% line coverage. The third fit criterion is a coverage-threshold{ metric: 'line', min: 98, scope: 'src/cli/**' }. The scope restriction is deliberate: the CLI module is where behaviour meets command surface, and behaviour that is not covered there is behaviour that is not dog-fooded, regardless of what the src/base.ts and src/style.ts coverage looks like.

Three fit criteria, two tools (ripgrep, vitest), one coverage threshold. No ambiguity, no judgement call at review time.

The ripgrep gate is worth a line of its own. It is the simplest possible test — a text search across a directory — and it encodes a binary discipline. Either the forbidden tokens are present, or they are absent. A reviewer does not have to interpret; a CI job does not have to reason. The rule is zero matches, and a single match fails the build. That is the kind of hard discipline the package can afford because its test suite is small enough to enforce categorically. Larger test suites would soften the rule to an allowlist or a migration path; the package refuses that softening. Zero means zero.

Why it exists

This is the hinge of the whole package. A Requirements DSL whose own test suite is written in describe/it would be dishonest on the cover. The package explicitly quotes a user-memory rule — feedback_no_describe_it — as a precedent in its rationale. That memory rule is absolute: no describe, no it, anywhere in the @frenchexdev/* ecosystem. Dog-fooding is not a nice-to-have here; it is the non-negotiable.

Without this Requirement, the package would ship with a hidden admission: we know what you should do; we just didn't do it ourselves. The rest of the spec layer would lose authority.

A harder way to put the same claim: a Requirements DSL is a credibility artefact before it is a code artefact. Users who adopt a DSL adopt the discipline it encodes; if the authors refuse their own discipline, the DSL is noise. The ripgrep gate is a single command that confirms the discipline at CI-time in O(seconds); the unit-test gate confirms the @Satisfies completeness in O(ms). Together they cost the package nothing to enforce and everything to drop.

The third fit criterion — the 98% line-coverage floor on src/cli/** — is a softer gate. It does not enforce dog-food per se; it enforces that the dog-fooded tests actually cover the code they claim to cover. Without the coverage floor, the author could satisfy the ripgrep gate by writing @FeatureTest/@Verifies tests that exercise nothing. With it, the tests have to reach 98% of the lines in the CLI module. Ninety-eight not 100 — the last 2% carries the error handlers and the throw new Error('unreachable') lines that are hard to hit without synthetic fixtures; the package accepts that margin.

How it is satisfied

Every Feature in the package ends up pointing at REQ-DOG-FOOD at least implicitly — because every Feature must declare @Satisfies, and the enforcement of that rule is REQ-DOG-FOOD. Explicit satisfiers include:

  • FEATURE-TRACE-EXPLORER-TUI — the running example. @Satisfies(ReqDiscoverableTraceabilityRequirement, ReqDogFoodRequirement, ReqParallelDeliverableRequirement).
  • FEATURE-DECORATORS — the @FeatureTest/@Verifies/@Satisfies registry that makes dog-food mechanically possible.
  • FEATURE-COMPLIANCE — the command that emits the traceability matrix and fails --strict on orphan Features.

The full bipartite graph is in chapter 06.

A subtle point about the satisfaction shape here: REQ-DOG-FOOD is the one REQ that every Feature in the package implicitly depends on, even when not listed explicitly. If a Feature declares @Satisfies(ReqVisualTraceabilityRequirement) only, the declaration still satisfies REQ-DOG-FOOD indirectly — because declaring @Satisfies at all is what the REQ demands. The REQ does not require that every Feature name it; it requires that every Feature do the thing (use the decorator). The distinction matters for the bipartite graph: the explicit @Satisfies(ReqDogFoodRequirement) edges in features like FEATURE-TRACE-EXPLORER-TUI are markers, not requirements. Dropping them would not break the REQ's satisfaction — it would only make the REQ's coverage less visible in the report.

Running-example connection

The running example satisfies this REQ explicitly. FeatureTraceExplorerTuiFeature declares @Satisfies(ReqDiscoverableTraceabilityRequirement, ReqDogFoodRequirement, ReqParallelDeliverableRequirement)REQ-DOG-FOOD is the second argument. The decorator registers the link bidirectionally: the compliance report, when asked for trace matrix FEATURE-TRACE-EXPLORER-TUI, lists REQ-DOG-FOOD among the satisfied Requirements; when asked for trace matrix REQ-DOG-FOOD, it lists FEATURE-TRACE-EXPLORER-TUI among the satisfiers.

Field-by-field observations

The kind field is Constraint, not Functional or NonFunctional. That is the right shape: dog-food is not a behaviour the system performs for a user, it is a constraint on how the authors work. DefaultStyle.vocabulary.requirementKinds lists Constraint as one of the five legal values, and this is the only Tier-1 REQ in this chapter that uses it.

The source.role is project-rule. That is a conventional tag for a Requirement whose provenance is a team agreement rather than an external stakeholder or regulation. The date is the package-bootstrap date, 2026-04-14 — this REQ was declared the moment the package existed, because it had to be.

The verificationMethod is Test, not Inspection. The ripgrep quality-gate could plausibly be classified as Inspection (a reviewer runs a command and reads the output), but the package author chose Test, which matches how the gate actually runs — in CI, under a green-or-red build signal. The two unit-test bindings — satisfiesDecoratorRegistersBidirectionalLink and complianceStrictFailsOnFeatureWithoutSatisfies — are the ones that make the Test classification honest.

REQ-BOOTSTRAP-ZERO-FRICTION — One command bootstraps a Feature end-to-end

readonly id = 'REQ-BOOTSTRAP-ZERO-FRICTION';
readonly title = 'One command bootstraps a Feature end-to-end';
readonly priority = Priority.High;
readonly status = 'Approved' as const;
readonly kind = 'UserStory' as const;

readonly statement = {
  pattern: 'ubiquitous' as const,
  response: 'let a user create a Feature class, its spec.json, and the scaffolded test stubs by running one interactive command, with no manual file editing.',
};

The rationale is evidence-based: "Manual creation of Feature files is the friction that prevents adoption of the DSL." The evidence is a single expert-opinion entry dated 2026-04-14. The risk is High"The DSL stays niche: adoption capped at authors willing to hand-craft every Feature file."

What this Requirement actually demands

One command — npx requirements feature new — that walks the user through the whole Feature-creation loop interactively, collecting id, title, priority, an AC loop with add-another-or-end prompt, a satisfiedBy multiselect that can recurse into a nested feature new for missing Requirements, and a test-level multiselect that decides which scaffolders run. At the end the command emits:

  • a .ts Feature class with @Satisfies(...) filled in;
  • a .spec.json sibling file carrying the same facts and a $schema key for editor binding;
  • one or more scaffolded test stubs, one per AC the wizard collected, routed through the scaffolder-registry (unit, functional, e2e, a11y, i18n, visual, perf) based on the @Expects levels the user selected.

No hand-editing is allowed in the happy path. The fit criteria bind two unit tests — wizardCollectsIdTitlePriority and wizardLoopsAcsUntilEnd — and one demonstration scenario: "End-to-end wizard run in a temp dir yields a compilable Feature file + passing tests."

The demonstration scenario is the critical one. It is stronger than either unit test individually because it asserts the whole pipeline as a single closed loop: input is a sequence of wizard answers, output is a compilable .ts file and a passing vitest run in a fresh temp directory. That shape is unit-test-able with a subprocess harness, but the REQ leaves the implementation open — it could be a playwright recording, a CI matrix job, a manual smoke test. The Demonstration verification method is inherited from the Requirement.verificationMethod declaration, but the fit criterion stands regardless.

The two unit tests scope the hard parts of the wizard. wizardCollectsIdTitlePriority locks in that the wizard walks the three required identity fields; wizardLoopsAcsUntilEnd locks in that the AC-collection loop is genuinely open-ended (not capped at three, not requiring a minimum of one). Together they cover the shape of the user's interaction; the demonstration covers the artefact it produces.

Why it exists

The earlier typed-specs pattern reached 112 ACs across 20 Features on a single project precisely because creation friction was low enough for that project. Scaling to a published DSL needs the friction to be even lower — lower than opening VSCode and copying a template. A DSL whose first step is "create this file, fill in these six fields, now add this decorator, now write these abstract methods" will be read and admired, not used.

Without this Requirement, the DSL is a library for authors who already know the shape. With it, the DSL is a tool that teaches the shape by running.

The running-a-tool-as-teaching pattern is a recurring motif in this half of the enumeration. REQ-TUI-MODERN assumes the wizard teaches by feel; REQ-LOW-FRICTION-ACS assumes the suggester teaches by example; REQ-EDITOR-FIRST-CLASS assumes the JSON Schema teaches by autocomplete. In each case the implicit claim is the same: documentation is not the primary teaching surface. The tool itself is.

That is a strong stance and worth making explicit. A DSL that reaches its users through documentation fails at the documentation layer — because documentation lags, because users skim, because the first run of the tool is the moment the user decides to continue or stop. REQ-BOOTSTRAP-ZERO-FRICTION is the REQ that locks this stance in for the creation flow. The fit criteria do not say "the docs must explain the flow clearly". They say "one wizard run in a temp dir produces a compilable file". The docs are a separate concern.

How it is satisfied

  • FEATURE-FEATURE-NEW-COMMAND — the wizard itself, in src/cli/feature-new-core.ts. This is the end-to-end entry point; its ACs cover the wizard flow, the emitted files, the interaction with the scaffolder registry, and the handling of --yes / --dry-run / --replay flags.
  • FEATURE-CLACK-ADAPTER — the TUI backend routed through the Prompt port. The adapter is the substitutable implementation; tests inject an in-memory Prompt double, production code uses the clack-backed one.
  • FEATURE-SCAFFOLD-CORE — the test-stub emission, in src/cli/scaffold-core.ts plus the seven scaffolders in src/cli/scaffolders/. Each scaffolder emits a test file at the right path with the right @FeatureTest/@Verifies decorators already filled in.

The three Features together form a pipeline: collect (CLACK) → validate + persist (FEATURE-NEW-COMMAND) → scaffold (SCAFFOLD-CORE). Each Feature could in principle be swapped — a different TUI backend, a different command-line entry, a different scaffolder set — without breaking the REQ's satisfaction, as long as the pipeline's end-to-end behaviour still passes the fit criteria.

Running-example connection

The running example does not satisfy this REQ directly. FEATURE-TRACE-EXPLORER-TUI is a consumer Feature; REQ-BOOTSTRAP-ZERO-FRICTION is a meta-tooling Requirement satisfied by the Feature-factory Features. The running example benefits from zero-friction bootstrap — it was itself scaffolded by feature new — but it does not carry the satisfaction edge.

Field-by-field observations

The kind is UserStory. This is one of only two UserStory REQs in this half (the other is REQ-LOW-FRICTION-ACS). The shape is recognisable — a user does X, the system does Y — even though the REQ is written in EARS ubiquitous form rather than as a canonical "As a … I want …" sentence. The DefaultStyle.vocabulary.requirementKinds enum accepts UserStory precisely so Requirements of this shape can be tagged without forcing them into Functional.

The rationale.evidence entry is an expert-opinion with expert: 'user-conversation-2026-04-14'. The package author is transparent about the provenance: this Requirement came out of one conversation on the package-bootstrap day. That is the honest level of evidence for a UserStory REQ declared before the product has users. The RequirementEvidence discriminated union in src/base.ts exposes expert-opinion as a first-class kind precisely so conversational provenance does not have to be laundered as a metric or a study.

The demonstration fit criterion — "End-to-end wizard run in a temp dir yields a compilable Feature file + passing tests" — is the kind of demonstration that translates readily into a playwright-style recording or a CI job that spawns a subprocess and asserts on its output. The package's current implementation uses the latter; a recording is a Tier-2 nice-to-have.

REQ-SPEC-IS-SOURCE-OF-TRUTH — The spec.json is canonical, generation is idempotent

readonly id = 'REQ-SPEC-IS-SOURCE-OF-TRUTH';
readonly title = 'The spec.json is the canonical source of truth; generation is idempotent and sync-able';
readonly priority = Priority.Critical;
readonly status = 'Approved' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'the user runs `feature new` or `feature sync`',
  response: 'compute a SyncPlan comparing the in-memory spec against on-disk files, render a colored diff, and apply only the changes the user confirms.',
};

This is the first event-driven statement in the enumeration. The trigger is the user invoking feature new or feature sync; the response is a plan/preview/apply loop. The rationale cites Terraform and Kubernetes as precedent: "Industry-standard idempotent-apply pattern: plan → preview → apply." The risk is Critical"Hand-edited code silently overwritten on sync; data loss events erode trust in the DSL."

What this Requirement actually demands

Three behaviours, each backed by a unit-test fit criterion:

  1. diffSpecToDisk classifies each on-disk entry as create, unchanged, or modified — bound to diffClassifiesEachTargetAsCreateUnchangedModified.
  2. preview renders the diff without any filesystem writes — bound to previewShowsDiffBeforeAnyWrite.
  3. The --dry-run flag exits 0 after rendering and writes nothing — bound to dryRunFlagForcesPreviewCancel.

Taken together, these three bind a strong contract: the CLI must never write a file that the user has not explicitly approved.

The three phases of the contract — diffSpecToDisk, preview, apply — are not obvious at first reading but are well-understood as a pattern. diffSpecToDisk is the pure function: spec-in-memory + on-disk-state → SyncPlan. preview is the pure presentation: SyncPlan → colored-diff-rendering, with zero I/O. apply is the effectful write: SyncPlan + user-confirmation → filesystem changes. Splitting them this way means the first two are trivially unit-testable (pure functions over data), and the third can be driven by an in-memory FileSystem port for isolated tests. The test scaffolding the package inherits from @frenchexdev/requirements takes advantage of this split — diffClassifiesEachTargetAsCreateUnchangedModified tests a pure function, previewShowsDiffBeforeAnyWrite tests another pure function, dryRunFlagForcesPreviewCancel tests the controller that glues them together.

Why it exists

Generators that blind-overwrite hand-edited derived code are a failure mode that has burnt every declarative-configuration tooling community at least once. The Requirement names the escape: the spec is canonical, the .ts files are derived, but the derivation is gated on an explicit user confirmation.

Without this Requirement, every feature sync would be a roulette. With it, the user's confirmation is the write-barrier.

The precedent the Requirement cites — Terraform's and Kubernetes' plan/preview/apply pattern — is worth unpacking. Both tools faced the same failure mode in their early days: declarative configuration tools that blind-applied their desired state ended up silently destroying user work. Terraform's terraform plan and Kubernetes' kubectl diff were the response. In both ecosystems, the convention is now universal: generate a plan, preview the plan, apply on explicit confirmation. The package adopts the same convention for its own declarative surface. The SyncPlan shape the Requirement names is the package's term-of-art for what terraform plan produces; the colored diff is the package's term-of-art for what kubectl diff produces.

The Critical risk level is the highest declared in this chapter, matching REQ-DOG-FOOD. The justification is symmetric: dog-food is the hinge of credibility at the verification layer; spec-is-source-of-truth is the hinge of trust at the workflow layer. A user who has had their hand-edits destroyed by a generator once will never trust the generator again. The risk field names this directly: "data loss events erode trust in the DSL."

How it is satisfied

  • FEATURE-FEATURE-SYNC-COMMAND — the plan/preview/apply loop, in src/cli/sync-core.ts.
  • FEATURE-FEATURE-NEW-COMMAND — the initial scaffolding leg of the same contract.
  • FEATURE-BIDIRECTIONAL-SYNC — the Tier-2 extension that makes the contract symmetric (see next section).

Running-example connection

Not directly satisfied by the running example. FEATURE-TRACE-EXPLORER-TUI is a reader over the REQ↔FEAT graph; it does not emit files. The diff contract applies to the Features that generate files.

Field-by-field observations

The statement.pattern is event-driven — the first in the enumeration. The EARS event-driven template reads: "When {trigger}, the system shall {response}." Here the trigger is "the user runs feature new or feature sync" and the response is the plan/preview/apply sequence. Rendered, the Requirement reads cleanly: "When the user runs feature new or feature sync, the system shall compute a SyncPlan comparing the in-memory spec against on-disk files, render a colored diff, and apply only the changes the user confirms." That sentence is the full spec of the sync command.

The rationale cites terraform/kubernetes as a precedent evidence entry. This is the first use of a cross-tool precedent in the enumeration — later REQs will cite lazygit, @clack/prompts, vitest-watch, launchdarkly, and so on. The pattern is consistent: when a Requirement adopts a pattern that has already been validated in another tool, the precedent is named, with a one-line justification.

The three fit criteria together encode the plan/preview/apply contract as three separate unit tests rather than one integration test. That is a deliberate shape. Each test can fail independently and point at a specific regression; a single integration test would conflate "the diff was wrong" with "the preview wrote a file" with "--dry-run wrote a file", making any red build harder to diagnose.

REQ-ROUND-TRIP-EDITABLE — Either side of the spec↔code duality must be editable

readonly id = 'REQ-ROUND-TRIP-EDITABLE';
readonly title = 'Either side of the spec↔code duality must be editable; the other regenerates';
readonly priority = Priority.Medium;
readonly status = 'Draft' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'ubiquitous' as const,
  response: 'regenerate the sibling artefact (the .ts class from a .spec.json edit, or vice versa) so that neither format is privileged as canonical and both edits round-trip idempotently.',
};

This is the first Draft entry in the enumeration — Tier 2, roadmap, not yet committed. The rationale cites Völter & Pech's Projectional Editing and Structural Source Management (2012) as study-level evidence, and a precedent edge back to REQ-SPEC-IS-SOURCE-OF-TRUTH: "The Tier-1 spec-as-SOT requirement created a one-way pipeline; Tier 2 must close the loop without demoting .ts authors."

What this Requirement actually demands

A symmetric sync: editing the .ts class regenerates a matching .spec.json, and editing the .spec.json regenerates a matching .ts class. The fit criteria are strong — five entries, the most of any REQ in this chapter — including:

  • bidirectionalSyncTsToJson — unit-test bound.
  • bidirectionalSyncJsonToTs — unit-test bound.
  • roundTripIsIdempotentAfterThreeCycles"three round-trips ts → json → ts → json → ts converge to a fixed point (idempotence)".
  • An inspection checklist asserting byte-identity between .ts-authored and .spec.json-authored fixtures, and conflict-markers (never silent overwrite) when both sides diverge.
  • A quality-gate — requirements sync --verify (no drift) in CI.

Why it exists

The rationale is explicit: "Developers who prefer TypeScript-first authoring must not be forced through JSON, and JSON-native editors (VSCode schema, auditors) must not be forced through TypeScript; both surfaces must be first-class." The stated assumption is structural: "A canonical normalisation function exists that makes either direction idempotent modulo formatting."

Without this Requirement, the DSL would have picked a side — TypeScript or JSON — and alienated the other half of the audience. Auditors want JSON with a schema; engineers want TypeScript with type-narrowing. Neither is wrong; both are serviceable.

The round-trip property — "three round-trips ts → json → ts → json → ts converge to a fixed point" — is the strongest fit criterion in this chapter. A looser version ("single round-trip produces byte-identical output") would be easier to implement and sufficient for most cases; the three-cycle variant forces the normalisation function to be truly idempotent, not just approximately so. That is the pattern fast-check-style property testing lives for: quantify over random inputs, apply the round-trip three times, assert fixed-point convergence. The package's test suite already uses fast-check for other invariants; this Requirement is the natural next client of that toolchain.

The precedent edge to REQ-SPEC-IS-SOURCE-OF-TRUTH is not structural — there is no @Refines relation — but it is semantically load-bearing. REQ-ROUND-TRIP-EDITABLE declares the Tier-1 asymmetry (JSON is canonical; TS is derived) as the starting point, then asks for a Tier-2 symmetry that closes the loop without demoting the TS side. Reading the two REQs together, the arc is clear: ship the one-way pipeline first, prove it works, then close the loop. The Tier-2 status is not procrastination; it is sequencing.

How it is satisfied

  • FEATURE-BIDIRECTIONAL-SYNC — the Tier-2 Feature carrying this Requirement, in src/cli/feature-bidirectional-sync.ts. Currently enabled = false; the Feature declares the shape and the ACs but ships with stubbed implementations.
  • FEATURE-SPEC-TO-SOURCE — the lower half of the loop, the JSON→TS direction. Takes a validated spec.json and emits the corresponding TypeScript class.

Both Features share the same underlying normalisation function — canonical JSON ordering, sorted AC lists, standardised quote styles, trimmed whitespace. The normalisation is what makes the three-round-trip idempotence test achievable; without it, formatting differences between TS-authored and JSON-authored versions would compound across cycles rather than converge.

Running-example connection

Not directly satisfied by the running example. FEATURE-TRACE-EXPLORER-TUI is a reader, not a generator.

Field-by-field observations

The file carries a JSDoc /** Tier-2 roadmap — Draft status, not yet committed. */ above the class declaration. That is the convention the package adopts to mark Tier-2 REQs at the source level — a one-line comment, nothing more. The status: 'Draft' as const field is the machine-readable version of the same fact; the JSDoc is the grep-friendly one.

The rationale carries a study evidence entry — Völter and Pech's Projectional Editing and Structural Source Management (2012). Projectional editing is the JetBrains MPS tradition of treating multiple concrete syntaxes as views over a single abstract syntax tree; the reference is apposite here because the round-trip contract the Requirement demands is exactly a projection-style guarantee. The package is not MPS — it does not edit the AST directly — but the invariant is isomorphic: two surfaces, one model.

The assumptions field of the rationale is used, uniquely in this half: "A canonical normalisation function exists that makes either direction idempotent modulo formatting." Declaring the assumption explicitly is a discipline — it flags the one hidden load-bearing claim that, if false, makes the whole Requirement impossible rather than merely hard. The Requirement class treats assumptions? as optional (see RequirementRationale in src/base.ts), but when used it carries real weight.

The risk field names two explicit mitigations: "Ship the bidirectional sync early and keep both fixtures equally maintained in CI" and "Publish round-trip property tests as acceptance bar for any schema change." Mitigations are optional on RequirementRisk<L> but are load-bearing for Draft REQs — they describe what the authors intend to do before declaring the REQ Approved.

REQ-EDITOR-FIRST-CLASS — Hand-editing a .spec.json must feel native

readonly id = 'REQ-EDITOR-FIRST-CLASS';
readonly title = 'Hand-editing a .spec.json must feel native — autocomplete, validation, docs, zero config';
readonly priority = Priority.High;
readonly status = 'Approved' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'a user opens a .spec.json file in a JSON-aware editor',
  response: 'surface autocomplete, validation, and inline documentation for every field, with zero manual configuration beyond installing the package.',
};

The rationale is a principle with no external evidence: "If hand-editing the spec requires reading docs, the JSON-as-source-of-truth promise is broken." Risk is High"Users cannot hand-edit specs reliably; adoption of --replay and --sync workflows stalls."

What this Requirement actually demands

Three behaviours bound to three unit tests, each mapping a schema lifecycle phase to a binding:

  1. The schema generator produces ajv-valid JSON Schema — generateSchemaProducesValidJsonSchema. The generator pipes src/cli/types.ts through ts-json-schema-generator and asserts that the output is a valid JSON Schema document (the validator for the meta-schema, not the schema itself). This is the first link in the chain; if the generator is broken, nothing downstream can work.
  2. Every generated .spec.json carries $schema as its first key — wizardEmitsSchemaKeyInGeneratedSpecJson. The wizard is the authorship path; every file it produces must be self-describing. The first-key constraint is a UX affordance for JSON-aware editors.
  3. schema register merges into .vscode/settings.json non-destructively — registerCommandMergesIntoExistingSettings. The registration command is the binding path; it must merge into an existing settings file without clobbering unrelated keys. Non-destructive merge is the invariant that makes the command safe to run repeatedly.

The phrase "zero manual configuration beyond installing the package" is load-bearing: the npx requirements schema register command is the one-liner that flips the editor into IntelliSense mode.

Why it exists

If the spec is the source of truth, then hand-editing the spec must be a first-class workflow. That means the editor must show the user what is legal without making the user read the docs. Zero-config JSON Schema binding — ajv for validation, VSCode json.schemas for IntelliSense — is the precedent the Requirement adopts.

Without this Requirement, --replay <spec> workflows and feature sync workflows become hazardous: the user does not know what field names or enum values are legal, and discovers errors at CLI invocation time rather than at edit time.

The pattern the Requirement locks in — JSON Schema emitted from TypeScript types, validation via ajv, binding via VSCode json.schemas — is the current industry-standard pipeline for spec-driven JSON authoring. ts-json-schema-generator or typescript-json-schema produces the schema; ajv consumes it at runtime; VSCode and JetBrains IDEs consume it at edit time. The user experience the REQ demands is the experience of hand-editing a tsconfig.json or package.json — red-squiggled errors, Ctrl-Space completions, hover documentation. That experience is now universal enough that users expect it; a spec-driven tool that does not ship it feels provincial.

The mention of --replay <spec> is a forward reference to a workflow pattern the package ships: a completed spec.json can be fed back through the wizard as input, skipping all prompts, producing a deterministic regeneration. That workflow is the non-interactive complement to feature new's interactive mode — useful for CI, for scripted bootstrapping, and for testing the wizard itself. It depends on hand-edited JSON being reliable; REQ-EDITOR-FIRST-CLASS is the dependency.

How it is satisfied

  • FEATURE-FEATURE-SCHEMA-COMMAND — the schema register / schema generate subcommands, in src/cli/schema-register.ts and src/cli/schema-generate.ts. The two subcommands share a common core: schema-generate produces the schema document from src/cli/types.ts; schema-register binds it into .vscode/settings.json. The --print and --force flags on schema register give users control over whether to overwrite existing bindings or review them first.
  • FEATURE-FEATURE-NEW-COMMAND — indirectly; the wizard emits the $schema key in every .spec.json it produces, so the binding works on first save.

Running-example connection

Not directly satisfied by the running example.

Field-by-field observations

The rationale.evidence array is empty — the only Requirement in this half that cites no evidence. The claim is treated as self-evident: "If hand-editing the spec requires reading docs, the JSON-as-source-of-truth promise is broken." That is a principle, and the package author chose not to paper it with fabricated precedent. The rationale.kind: 'principle' tag makes the shape legible to the validator — DefaultStyle.vocabulary.rationaleKinds accepts principle as a legitimate rationale even without evidence.

The three fit criteria are tightly scoped and together cover the full JSON-Schema lifecycle: generate (generateSchemaProducesValidJsonSchema), embed (wizardEmitsSchemaKeyInGeneratedSpecJson), bind (registerCommandMergesIntoExistingSettings). The three bindings correspond to three distinct modules in src/cli/schema-generate.ts, feature-new-core.ts, schema-register.ts — which is the right factoring: schema generation, schema emission, schema registration are three separable concerns.

The $schema first-key constraint is subtle. JSON objects have no defined key order in the spec, but every modern JSON parser and editor preserves insertion order, and every JSON-aware editor uses the first $schema key it finds to offer IntelliSense. Putting $schema first is not a correctness requirement — it is a UX requirement. The unit-test binding wizardEmitsSchemaKeyInGeneratedSpecJson locks the UX invariant at the emitter.

REQ-TUI-MODERN — The CLI must use a modern TUI library

readonly id = 'REQ-TUI-MODERN';
readonly title = 'The CLI must use a modern TUI library — arrow-key navigation, colored output, spinners';
readonly priority = Priority.Medium;
readonly status = 'Approved' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'ubiquitous' as const,
  response: 'use @clack/prompts for all interactive prompts, routed through the Prompt port, with arrow-key navigation, colored output, spinners, and grouped multi-step flows.',
};

The rationale is value-driven, with a single precedent: "Modern scaffolders universally adopted clack — sets the 2026 bar for DX." Risk is Medium"Wizard feels dated; users default to hand-editing and the --replay flow never lands."

What this Requirement actually demands

Two fit criteria, both Inspection rather than Test (the verification method declared at the REQ level is Inspection, the only such choice in this chapter):

  1. A checklist: "all interactive prompts go through the Prompt port" and "no direct import of readline in src/cli/**".
  2. A demonstration: "Screen recording of feature new wizard end-to-end."

The Inspection posture is deliberate — UI polish is not reducible to a unit-test assertion. The port-boundary check is testable, though, and the package uses eslint/inspection together to keep the prompt surface uniform.

Why it exists

A tool whose CLI looks like 2010 does not get adopted in 2026. The rationale names Astro, Nuxt, and create-t3-app as the scaffolders that set the bar; the package follows. The Prompt port abstraction (so @clack/prompts can be swapped for a test double) is a side-benefit — originally motivated by test isolation, now also the seam where a future blessed/ink backend could plug in.

Without this Requirement, the wizard might satisfy REQ-BOOTSTRAP-ZERO-FRICTION on paper — one command, no hand-editing — and still fail in practice because the user quits after the first ugly prompt.

Precedent matters here because aesthetics in CLI tooling is a moving target. The 2015 bar was inquirer.js — curses-style prompts with basic color. The 2020 bar was prompts — similar surface, cleaner API. The 2024-2026 bar is @clack/prompts — grouped multi-step flows, structured intro/outro, calibrated color palette, spinners that actually work across terminal emulators. Astro, Nuxt, create-t3-app, and shadcn/ui's CLI all adopted clack between 2022 and 2024; as of 2026 it is the expected backend for any new developer-facing wizard. The Requirement names the precedent explicitly so future maintainers understand the bar is moving, not fixed.

The inspection checklist item "no direct import of readline in src/cli/**" is the hard edge. readline is Node's built-in line-input primitive; it is the path of least resistance for a prompt. The Requirement forbids it not because readline is broken — it works fine — but because direct readline usage would bypass the Prompt port and make the prompt surface non-uniform. Uniformity is the invariant; readline is the violation.

How it is satisfied

  • FEATURE-CLACK-ADAPTER — the @clack/prompts implementation of the Prompt port, in src/cli/clack-adapter.ts. This is the only production-side adapter; test-side implementations inject in-memory Prompt doubles directly.
  • FEATURE-FEATURE-NEW-COMMAND — the wizard that consumes the port; the main consumer whose UX defines what "modern" means in practice.
  • FEATURE-TRACE-EXPLORER-TUI — indirectly, via its use of the same Prompt port. The TUI does not drive interactive prompts the way the wizard does, but it uses the port for the help overlay and for quit-confirmation.

The port-based architecture means that a future decision to adopt a different TUI library — ink, blessed, a custom implementation — requires replacing one adapter and changing zero consumer code. The port is the stable surface; the adapter is the variable part.

Running-example connection

The running example consumes the port (traceExplorerUsesPromptPortForInteraction is one of its ACs) but does not satisfy REQ-TUI-MODERN directly. The satisfying Features are the ones that build the adapter and the wizards that use it.

Field-by-field observations

The verificationMethod is Inspection, not Test. This is the first REQ in the enumeration to use Inspection as its primary verification method. The DefaultStyle.vocabulary.verificationMethods array — ['Test', 'Inspection', 'Analysis', 'Demonstration'] — exposes all four, and REQ-TUI-MODERN is the case where Inspection is honest: no unit test can assert that the wizard feels modern. A reviewer walks the checklist, watches the demonstration, and signs off or requests changes.

The first inspection checklist item — "all interactive prompts go through the Prompt port" — is the reviewable form of an architectural invariant. The second — "no direct import of readline in src/cli/**" — is a grep-checkable corollary. Together they encode the Prompt port as the single seam, with one positive assertion (every prompt goes through it) and one negative assertion (no bypass path exists).

The rationale.kind is value-driven. This is the first value-driven rationale in the enumeration. DefaultStyle.vocabulary.rationaleKinds lists value-driven alongside evidence-based, principle, regulatory-compliance, risk-mitigation, and precedent. Value-driven is the honest tag here: the claim is about aesthetic and adoption signal, not about an operational metric.

REQ-LIVE-FEEDBACK — Spec changes must surface validation + coverage diagnostics live

readonly id = 'REQ-LIVE-FEEDBACK';
readonly title = 'Spec changes must surface validation + coverage diagnostics live, without manual command runs';
readonly priority = Priority.Medium;
readonly status = 'Draft' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'a .spec.json or Feature .ts file changes on disk while `requirements watch` runs',
  response: 'revalidate the affected spec, recompute compliance deltas, and print a one-line summary within 500 ms of the save.',
};

Draft — Tier 2. The rationale is evidence-based and carries three distinct evidence entries: a precedent (vitest-watch + tsc-watch), a metric ("industry-baseline-watch-latency: 200-500ms (vitest-watch median)" dated 2026-04-14, linked to the vitest docs), and a study ("Programmer Attention During Development", Parnin & Rugaber, 2011).

What this Requirement actually demands

A watch-mode command — npx requirements watch — that keeps a filesystem watcher alive and, on every save to a .spec.json or Feature .ts file:

  1. Revalidates the changed spec.
  2. Recomputes compliance deltas.
  3. Prints a one-line summary within 500 ms.

The fit criteria bind two unit tests — watchDetectsSpecChange, watchDetectsRapidSuccessionAndDebounces — and one metric: the p95 of watch.revalidate.latency must stay under 500 ms over the last 100 events. An inspection checklist adds two Windows-specific sanity checks (single revalidation per VSCode save, clean teardown on Ctrl-C).

Why it exists

Inner-loop speed is the single most under-appreciated driver of DSL adoption. The Requirement's claim is plain: "manual compliance runs get skipped; live feedback keeps the DSL honest without requiring user discipline." If the tool only shouts at you when you remember to invoke it, the tool's shouts get tuned out.

Without this Requirement, specs drift silently. With it, drift surfaces at save-time.

The 500 ms latency budget is not arbitrary. The vitest-watch median — which the rationale cites — lands in the 200-500 ms range on reasonable hardware. That range is the human attentional threshold: below 200 ms a save-to-feedback cycle feels instantaneous; above 500 ms the user starts checking other windows while waiting. The REQ sets the upper bound of that range as the hard ceiling, leaving most of the attentional budget for actual validation work. The Parnin & Rugaber study cited in the rationale — "Programmer Attention During Development" (2011) — is the academic backing: programmer attention fragments quickly when feedback loops exceed the working-memory window. The 500 ms threshold is specifically calibrated to keep the loop inside that window.

The debounce fit criterion — "watch debounces a burst of rapid saves into a single revalidation" — is the other side of the latency coin. A naive watcher that fires on every fs.watch event would revalidate five times during a single VSCode-format-on-save. The debounce folds the burst into one revalidation, one terminal line, one attention disruption. That is a small implementation detail with a large UX consequence, and the package author chose to specify it at the REQ layer rather than leave it as implementation discretion.

How it is satisfied

  • FEATURE-WATCH-MODE — the watcher + debounce + port wiring, in src/cli/feature-watch-mode.ts. Currently Tier-2 — the Feature file exists with ACs declared, but the implementation is stubbed. The satisfying Feature must, when implemented, handle fs.watch platform differences (Linux inotify, macOS FSEvents, Windows ReadDirectoryChangesW), debounce multi-fire events, and emit one-line summaries under the 500 ms ceiling.

Running-example connection

Not directly satisfied by the running example. The running example is a navigation tool over the graph; the watch-mode Feature is a distinct meta-tooling concern.

Field-by-field observations

This is the first REQ in the enumeration with a metric fit criterion: "watch-latency-p95 — query: watch.revalidate.latency.p95, operator: <, threshold: 500, window: last 100 events." The RequirementFitCriterion discriminated union in src/base.ts exposes metric as one of seven kinds, and it is the right shape here: "revalidation completes within 500 ms" is a threshold claim over an observable signal, not a unit-test assertion.

The evidence array is dense: three entries of three different kinds — a precedent (vitest-watch + tsc-watch), a metric (the vitest median documented at a specific URL), and a study (Parnin & Rugaber on programmer attention). The three stacks together make the rationale hard to dismiss: the pattern is industry-standard, the quantitative target is grounded, and the behavioural research confirms why it matters.

The inspection checklist carries Windows-specific items: "on Windows, saves from VSCode trigger exactly one revalidation (not two)" and "Ctrl-C cleanly tears down all fs watchers". These are the bug-class items — platform-specific edge cases that a naive watcher implementation will get wrong. Declaring them in the REQ rather than in a bug tracker means they are gated on REQ verification, not on bug triage.

The single explicit mitigation is telling: "Keep watch-mode deliberately simple (no daemon, no IPC) so Ctrl-C is the full teardown story." That is an engineering constraint derived from the REQ, not from the REQ itself — a design note that future maintainers can read and refuse to violate.

REQ-VISUAL-TRACEABILITY — The traceability chain must be viewable as a diagram

readonly id = 'REQ-VISUAL-TRACEABILITY';
readonly title = 'The traceability chain must be viewable as a diagram, not just textual gap reports';
readonly priority = Priority.Medium;
readonly status = 'Draft' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'a user runs `requirements trace graph --format mermaid`',
  response: 'emit a Mermaid diagram showing Requirement → Feature → AC → Test with coverage state per node and disabled-feature styling.',
};

Draft — Tier 2. The rationale is value-driven, with three evidence entries: an expert-opinion dated 2026-04-14, a precedent (SysML-BDD/IBD"SysML has always rendered traceability as diagrams because text-only matrices do not scale beyond ~20 rows"), and a study (Abad & Ruhe, "Software Requirements Visualisation: A Systematic Mapping Study", 2016).

What this Requirement actually demands

A command — requirements trace graph --format mermaid — that emits a valid Mermaid flowchart rendering the four-tier chain (Requirement → Feature → AC → Test), with:

  • coverage state per node (covered vs uncovered AC classes);
  • distinct styling for enabled = false Features;
  • click-targets carrying the REQ/FEAT id so markdown readers can anchor-link.

Three unit-test fit criteria — traceGraphEmitsMermaidFlowchart, traceGraphHighlightsDisabledFeatures, traceGraphDistinguishesCoveredFromUncoveredAcs — plus an inspection checklist and a demonstration: "Rendered mermaid diagram of the @frenchexdev/requirements self-feature graph embedded in README."

The Requirement also lists two mitigations in its risk field: "Publish the generated diagram in the package README so stakeholders see it even without running the CLI" and "Keep the mermaid backend pluggable so Graphviz/PlantUML can be added later."

Why it exists

Text-only matrices stop scaling at about twenty rows. The package has twenty-two Requirements and more Features than that. A gap-report CSV is optimal for CI but useless for a design review. The Requirement is about stakeholder-communication: product, design, and non-engineering audit roles read diagrams.

Without this Requirement, traceability stays an engineering-internal artefact. With it, the package's own self-feature graph becomes a diagram anyone can paste into a Confluence page.

The precedent citation to SysML — specifically SysML-BDD/IBD (Block Definition Diagrams / Internal Block Diagrams) — is not ornamental. SysML is the OMG standard for systems engineering, and its core insight, going back to the early 2000s, was that text-only traceability matrices stop scaling past ~20 rows. The diagrams are the scaling strategy. The package has 22 Requirements and a larger number of Features; a CSV matrix would already be hostile to read. Mermaid flowcharts are the modest, zero-dependency, text-source version of what SysML tooling has always shipped as BDD/IBD renderings.

The Abad & Ruhe study — "Software Requirements Visualisation: A Systematic Mapping Study" (2016) — is a survey of the visualisation-for-requirements literature, establishing that visualisation techniques measurably improve requirements comprehension for non-engineering stakeholders. Citing a mapping study rather than a single experiment is honest: the claim is the literature-wide consensus, not a single paper's result.

The coverage-state and disabled-feature styling are two UX details that make the diagram useful rather than merely correct. A diagram that renders every node identically communicates the structure but hides the state; a diagram with covered/uncovered/disabled variants communicates both. That is the difference between a structure chart and a status report. The REQ demands the status report.

How it is satisfied

  • FEATURE-FEATURE-MERMAID-GRAPH — the mermaid emitter, in src/cli/feature-mermaid-graph.ts. Walks the graph produced by FEATURE-TRACE-CORE, groups nodes by tier (REQ / FEAT / AC / TEST), emits a flowchart TB with subgraphs and class-based styling.
  • FEATURE-TRACE-CORE — the graph-building core the emitter consumes, in src/cli/trace-core.ts. Resolves @Satisfies and @Refines edges, cross-references @FeatureTest/@Verifies bindings, produces the abstract graph structure the emitters consume.

The split between the emitter and the core is the open/closed point. trace-core.ts produces a format-agnostic graph; emitters (mermaid first, Graphviz or PlantUML later, potentially JSON or DOT for machine consumers) consume that graph and produce format-specific output. Adding a new backend is an additive change — one new emitter file, no touch to the core.

Running-example connection

Not directly satisfied by the running example. But the running example's input graph is what this REQ renders statically; there is a shape overlap with REQ-DISCOVERABLE-TRACEABILITY below. Static mermaid serves one stakeholder audience; interactive TUI serves another.

Field-by-field observations

The fit criteria list includes a demonstration"Rendered mermaid diagram of the @frenchexdev/requirements self-feature graph embedded in README" — which makes the package meta-circular in a concrete way. The Requirement asks that the package render its own feature graph in the README. The rendering, if produced, is evidence of the behaviour. A reader of the README sees the graph; the graph is produced by the Feature that satisfies the Requirement; the Requirement exists because the graph is readable only when rendered. Three layers of self-reference, all visible in one artefact.

The inspection checklist item "node click-targets include the REQ/FEAT id so markdown readers can anchor-link" is a small detail that carries outsized weight. Mermaid flowcharts can carry click directives that wire each node to an anchor — if the emitter honours the id convention, every node in the rendered diagram becomes a deep link into the source. The Requirement specifies this UX detail at the fit-criterion level rather than leaving it to implementation discretion.

The mitigation "Keep the mermaid backend pluggable so Graphviz/PlantUML can be added later" is the open/closed principle applied at the REQ layer. The REQ does not commit to mermaid — it commits to diagram-rendering, with mermaid as the initial backend. Future Graphviz or PlantUML adapters would be new Features satisfying the same REQ.

REQ-DISCOVERABLE-TRACEABILITY — Users must explore the graph interactively

readonly id = 'REQ-DISCOVERABLE-TRACEABILITY';
readonly title = 'Users must explore the REQ → FEAT → AC → TEST graph interactively, not only via CLI reports';
readonly priority = Priority.Low;
readonly status = 'Draft' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'a user runs `requirements explore`',
  response: 'open a full-TTY keyboard-navigable browser over the traceability graph with live drill-down from any node into its incoming and outgoing links.',
};

Draft — Tier 2, Low priority. The rationale is value-driven, with lazygit, lazydocker, and k9s as the precedent and a study by Maletic & Marcus — "On the Navigation of Software Artifact Graphs", 2001 — as background.

What this Requirement actually demands

A full-TTY TUI command — requirements explore — that:

  • builds the traceability graph from the local corpus by reading the @Satisfies/@Refines/@FeatureTest/@Verifies registries;
  • shows the currently-selected node, its incoming links, and its outgoing links, with arrow-key navigation across the three panes;
  • drills down from any node into the detail of its neighbours (REQ → satisfying Features → their ACs → bound tests, and the symmetric backwards paths);
  • opens a help overlay on ? listing every binding;
  • uses keybindings that match lazygit conventions (h/j/k/l for directional navigation, arrow keys as aliases, ? for help, q for quit);
  • refuses to start on a non-TTY input (CI, pipe, redirect) and degrades to the static trace graph output in that case;
  • exits cleanly on Ctrl-C, tearing down watchers and restoring the terminal.

The fit criteria bind traceExplorerBuildsGraph and traceExplorerHandlesArrowKeyNavigation, add an inspection checklist for the help overlay, and close with a demonstration scenario: "Screen recording showing navigation REQ → FEAT → AC → TEST via arrow keys." The traceExplorerBuildsGraph binding is the unit-test shape of correctness — the graph builds correctly from a small fixture corpus, with every @Satisfies edge represented, every @Refines edge represented, every @FeatureTest/@Verifies pair resolved. The traceExplorerHandlesArrowKeyNavigation binding is the unit-test shape of robustness — arrow-key events on nodes with no neighbours, on nodes with deep chains, on the root node, on the leaf nodes do not throw.

Verification method is Demonstration rather than Test — the final bar is a recording, not a green checkmark. Mitigation: "Provide a fallback trace graph command that emits the same data as static mermaid, so the feature degrades gracefully."

Why it exists

Textual compliance reports are optimal for CI but hostile for onboarding or exploratory analysis. An interactive TUI fills the discovery gap at near-zero UX cost for users already fluent in terminal tools. lazygit is the proof: even on developer machines with a full IDE, lazygit is what people reach for when navigating the git graph.

Without this Requirement, onboarding velocity suffers and new contributors fall back on grep — which the Requirement names explicitly as the failure mode.

The three precedent tools — lazygit, lazydocker, k9s — share a common shape worth naming. Each is a TUI over a graph-structured domain (git commits, Docker containers, Kubernetes resources), each adopts vim-style keybindings (h/j/k/l), each opens a help overlay on ?, each degrades gracefully to a non-interactive command. The package's TUI is specified to match the same conventions. That is a deliberate choice: users who are fluent in lazygit transfer their muscle memory to requirements explore without retraining. Inventing new keybindings would be a pure ergonomic loss.

The mitigation "Provide a fallback trace graph command that emits the same data as static mermaid, so the feature degrades gracefully" is the graceful-degradation story. Not every user has a full-TTY terminal (CI runs, dumb pipes, non-interactive sessions). For those users, trace graph --format mermaid produces the same content as a static artefact. The TUI is the best-case UX; the static emitter is the worst-case fallback.

Worth noting: the mitigation and REQ-VISUAL-TRACEABILITY share the same Feature surface. The static mermaid emitter serves both REQs — as the primary satisfier for REQ-VISUAL-TRACEABILITY and as the fallback path for REQ-DISCOVERABLE-TRACEABILITY. That is a many-to-one satisfaction edge (one Feature, two REQs) of the kind the decorator system supports via @Satisfies(Req1, Req2, ...). Chapter 06 will draw the full bipartite graph.

How it is satisfied

  • FEATURE-TRACE-EXPLORER-TUI — the running example, in src/cli/feature-trace-explorer-tui.ts. The Feature declares ten ACs covering graph build, arrow-key navigation, drill-down, help overlay, back-navigation, non-TTY refusal, clean exit, port usage (FileSystem + Prompt), and an end-to-end walkthrough. Currently enabled = false — the Feature is Tier-2 intent.
  • FEATURE-TRACE-CORE — indirectly; the TUI consumes the same graph structure the mermaid emitter does.

Running-example connection

This is the Requirement most closely tied to the running example. FeatureTraceExplorerTuiFeature declares @Satisfies(ReqDiscoverableTraceabilityRequirement, ReqDogFoodRequirement, ReqParallelDeliverableRequirement) — this REQ is the first argument. The five numbered ACs of the Feature (traceExplorerBuildsGraph, traceExplorerHandlesArrowKeyNavigation, traceExplorerDrillsDownFromAnyNode, traceExplorerOpensHelpOverlayOnQuestionMark, traceExplorerJumpsBackUpWithBackspace) cover the body of this REQ one-to-one with its fit criteria.

Field-by-field observations

This is the only REQ in the enumeration where verificationMethod = 'Demonstration'. All the others use Test or Inspection. The choice is deliberate: an interactive TUI is evaluated, ultimately, by watching someone use it. Unit tests can assert that the graph builds and that arrow keys do not throw; they cannot assert that the navigation feels right. The demonstration scenario — "Screen recording showing navigation REQ → FEAT → AC → TEST via arrow keys" — is the acceptance bar.

The evidence array carries three entries of three kinds: precedent (lazygit/lazydocker/k9s), expert-opinion (user-plan-discussion-2026-04-14), and a study (Maletic & Marcus, "On the Navigation of Software Artifact Graphs", 2001). The study is over two decades old but still load-bearing — the paper's claim that graph-based navigation outperforms list-based navigation for software artefacts is the academic basis for TUI-style exploration.

The priority = Priority.Low is honest. Of the eleven REQs in this half, this is the one the package can ship without and remain useful. The running example carries it, but the Feature is also marked enabled = false at the Feature level — it is declared as intent, not as committed behaviour. The traceability graph is inspectable via the static mermaid emitter (REQ-VISUAL-TRACEABILITY) even without the TUI. The TUI is the nice-to-have; the mermaid is the must-have.

REQ-LOW-FRICTION-ACS — The wizard must suggest AC names

readonly id = 'REQ-LOW-FRICTION-ACS';
readonly title = 'The wizard must suggest AC names to reduce cognitive load during bootstrapping';
readonly priority = Priority.Low;
readonly status = 'Draft' as const;
readonly kind = 'UserStory' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'a user enters a Feature title in `requirement new` or `feature new`',
  response: 'propose 3-5 AC names inferred deterministically from the existing corpus of Features in the repository, which the user can Tab-accept, edit, or ignore.',
};

Draft — Tier 2, Low priority. The rationale is evidence-based, citing Klein's Recognition-primed Decision Making (1993) and the github-copilot/tabnine precedent, with the qualification: "this requirement asks for the deterministic, offline version."

What this Requirement actually demands

A suggester that, on Feature-title entry, produces three to five plausible AC names drawn deterministically from the local corpus. The critical adjective is deterministic: the fit criterion binds acSuggesterIsDeterministicAndOffline"suggester works with zero LLM call — n-gram + verb patterns only." A second fit criterion checks the empty-corpus case (acSuggesterDegradesGracefullyOnEmptyCorpus), a third checks plausibility (acSuggesterReturnsPlausibleNames), and an inspection checklist adds privacy and ranking constraints ("suggestions do not leak AC names from packages outside the current repo", "suggestions ranked by a documented, reproducible score").

The response clause specifies the interaction shape precisely: "propose 3-5 AC names … which the user can Tab-accept, edit, or ignore." Three options is the floor, five the ceiling. Below three, the user does not feel offered a choice; above five, the prompt becomes cluttered. Tab-accept is the universal convention (Copilot, shell completion, text-editor snippets); edit-then-submit is the escape hatch; Esc-to-ignore is the null option. Three keys, three paths, no modal state. That is the right shape for a prompt that must not block the wizard.

Why it exists

The claim is specific and grounded in observation: "AC naming is the step where users most often abandon the wizard — they stare at an empty prompt. Suggestions convert paralysis into a choice." This is the Copilot pattern without the LLM dependency — Tab-accept autocomplete driven by a deterministic scorer over the local corpus only.

Without this Requirement, the wizard's AC loop is the abandonment point. With it, users who have no idea what to name their AC see three existing names from the repo and edit one of them.

The deterministic-and-offline constraint is the most distinctive part of this REQ. The obvious implementation in 2026 would be an LLM call — ask a model to suggest AC names based on the Feature title and the existing corpus. The REQ explicitly refuses that shape. The reasons are cumulative: an LLM-backed suggester makes the wizard depend on network connectivity, on an API key, on a billed service, and on a non-deterministic model whose suggestions might drift silently across versions. The package chooses n-gram + verb-pattern scoring instead — a technique from the 1990s, deterministic, offline, zero-dependency, and good enough to surface the top three matches from a corpus of a few dozen Features.

The Klein citation — Recognition-primed Decision Making (1993) — anchors the claim behaviourally. Klein's thesis, refined in decades of cognitive-science work, is that experts recognise familiar patterns rather than deliberate from first principles. The wizard's AC-naming step is a moment where the user is asked to produce a name from a blank page; the suggester converts that moment into a recognition task. Three candidates appear, the user recognises which is closest to what they meant, they Tab-accept and edit. The cognitive load drops from "invent" to "choose-and-edit".

The Copilot comparison in the precedent is honest about what the REQ is and is not. Copilot's Tab-accept pattern is the ancestral shape; the REQ asks for that shape without the LLM — a domain-specific, deterministic, local variant. The user experience is similar (Tab to accept, Esc to ignore); the dependencies are different (local corpus versus remote model).

How it is satisfied

  • FEATURE-FEATURE-HEURISTIC-SUGGESTER — the n-gram + verb-pattern scorer, in src/cli/feature-heuristic-suggester.ts. The suggester reads every existing Feature's AC names from the local corpus, computes n-gram similarity against the current Feature's title, and ranks the top five by a documented score. Tier-2; shipping as stubbed until the scoring function's quality bar is met.
  • FEATURE-FEATURE-NEW-COMMAND — the wizard that consumes the suggestion. The wizard's AC loop renders the suggester's top three as inline options at each AC-name prompt, with Tab to accept, typing to override, Esc to ignore.

Running-example connection

Not directly satisfied by the running example.

Field-by-field observations

The precedent entry for github-copilot/tabnine carries a qualifying clause: "this requirement asks for the deterministic, offline version." That qualifier is the REQ disclaiming the LLM variant of the pattern. The unit-test binding acSuggesterIsDeterministicAndOffline locks the disclaimer into the verification layer. The package refuses to take an LLM dependency for this surface; the suggester is pure n-gram + verb-pattern matching against the local corpus.

The inspection checklist item "suggestions do not leak AC names from packages outside the current repo" is a privacy constraint. The suggester scans the local corpus; it must not suggest AC names drawn from a neighbouring package in a monorepo unless that package is explicitly in scope. This is a concrete boundary-of-context concern, declared at the REQ layer rather than left to the implementation.

The mitigation "Make the suggester strictly optional (Tab to accept, Esc to ignore); never block the wizard on suggester output" is a graceful-degradation clause. If the suggester returns nothing, the wizard proceeds. If it returns garbage, the user ignores it. The suggester cannot be a critical-path blocker. That is the right shape for a Low-priority Tier-2 REQ: useful when it works, invisible when it doesn't.

The study entry — Klein, Recognition-primed Decision Making (1993) — is the behavioural-science grounding. Klein's thesis (experts recognise familiar patterns rather than deliberate from first principles) is the claim that makes the suggester valuable: show the user three familiar shapes, the user recognises one, the blank-page problem dissolves.

REQ-ORTHOGONAL-TOGGLING — Features and ACs toggle via a port

readonly id = 'REQ-ORTHOGONAL-TOGGLING';
readonly title = 'Features and ACs can be toggled via an orthogonal ToggleProvider port (environment, rollout %, time, tier)';
readonly priority = Priority.High;
readonly status = 'Draft' as const;
readonly kind = 'NonFunctional' as const;

readonly statement = {
  pattern: 'event-driven' as const,
  trigger: 'a ToggleProvider is configured and a Feature or AC carries a `@Toggle(gate)` decorator',
  response: 'classify the gated AC as `NotApplicable` in the compliance report when the gate resolves to off, rather than as `Uncovered`, and exclude it from `compliance --strict` failures.',
};

Draft — Tier 2, High priority (the only High Draft in this half). The rationale is value-driven, with three evidence entries: the launchdarkly/growthbook/unleash precedent, Pete Hodgson's Feature Toggles (Fowler, 2017), and an expert-opinion dated 2026-04-14. Risk is High"Absence blocks adoption by continuous-delivery organisations; they fork the DSL or bolt on home-grown toggling, fragmenting the ecosystem."

What this Requirement actually demands

A ToggleProvider port with a minimal surface — the risk mitigation states: "Keep the port minimal — a single isEnabled(gate, ctx) method — so adapters stay tiny." A single-method port is the smallest possible abstraction over a feature-flag service. LaunchDarkly, GrowthBook, Unleash, ConfigCat, and the in-process toggle libraries all expose some form of isEnabled(key, context) — an adapter that bridges any of them to the package's port needs a wrapper around that one method, no more.

A @Toggle(gate) decorator that marks a Feature or AC as gated. A compliance pass that, when the gate resolves to off, classifies the gated AC as NotApplicable rather than Uncovered, and excludes it from compliance --strict failures. The NotApplicable classification is the semantic bridge — it lets a report distinguish "this test is missing" from "this test is correctly missing because the feature is off in this environment". Without the bridge, the report would either fail spuriously (treating off-gated ACs as uncovered) or hide the gating (treating off-gated ACs as covered even when they have no implementation). The three-state classification gets both cases right.

Four fit criteria:

  1. toggleProviderPortIsImplemented — the port is implemented and injectable via the CLI shell.
  2. toggleDecoratorGatesAcByGateName + enabledWhenDecoratorAcceptsPredicate — decorator mechanics; the @Toggle(gate) decorator gates AC execution by gate name, and the @EnabledWhen(predicate) decorator accepts a runtime predicate for fine-grained gating.
  3. rolloutPercentageGatedByRng — rollout percentages use a deterministic seeded RNG for testability.
  4. eslint no-process-env in src/ — separation of policy and mechanism enforced as a lint rule.

The inspection checklist adds an example-adapter requirement: "an example adapter for LaunchDarkly or GrowthBook ships in examples/".

Why it exists

Continuous-delivery organisations ship behind rollout gates. A traceability DSL that "conflates 'intentionally off in this env' with 'missing test'" is unusable in production. The Requirement names the escape: a ToggleProvider port, an orthogonal classification (NotApplicable), a strict mode that ignores the off-gated ACs.

The orthogonality is the point. Toggling is not a property of the Requirement, or of the Feature, or of the AC — it is an orthogonal concern, injected at CLI-shell time, backed by whatever the host organisation uses.

Without this Requirement, shops on LaunchDarkly or Unleash cannot adopt the DSL without forking. With it, they configure one provider and the rest of the chain behaves correctly.

The Fowler article the rationale cites — Pete Hodgson's Feature Toggles (aka Feature Flags) (2017) — is the canonical taxonomy of toggle kinds in industry: release toggles, experiment toggles, ops toggles, permission toggles. The four kinds have different lifetimes, different scopes, and different failure modes. A traceability DSL that naively conflated them would get the semantics wrong at compliance time — an experiment toggle that turns off a Feature for 50% of users is not the same as a release toggle that hides the Feature until a deployment date. The ToggleProvider port abstracts over the kinds, leaving the classification to the adapter.

The ecosystem concern the risk names — "they fork the DSL or bolt on home-grown toggling, fragmenting the ecosystem" — is a real failure pattern in DSL adoption. A team whose production pipeline requires a feature the DSL does not support will fork the DSL and patch it; once forked, the fork drifts, the upstream can no longer ship meaningful updates to that team, and the DSL becomes N different DSLs. Declaring REQ-ORTHOGONAL-TOGGLING Draft-High is the package author's way of saying: this is the feature whose absence would cause forks, so ship it before enough shops adopt that forking becomes irreversible.

The design hint in the mitigations — "Keep the port minimal — a single isEnabled(gate, ctx) method — so adapters stay tiny" — is the practical consequence of the ecosystem concern. A port with a single method is something a 20-line adapter can implement against any feature-flag service. A port with ten methods is an invitation to build three official adapters and discover that the fourth service needs a different shape. Port minimality is ecosystem-health.

How it is satisfied

  • FEATURE-TOGGLING — the port, decorator, and RNG-seeded rollout, in src/cli/toggling.ts. Exposes ToggleProvider as the interface, @Toggle(gate) as the decorator, StubToggleProvider (all-on) as the default adapter, and a deterministic seeded RNG for percentage-based gates.
  • FEATURE-COMPLIANCE — the report-side classification of NotApplicable ACs, in src/cli/compliance-core.ts. Reads the ToggleProvider at report-generation time, asks each gated AC whether its gate resolves to on, and classifies accordingly.

The example adapters — LaunchDarkly, GrowthBook, or similar — ship in examples/, not in src/. The shape is intentional: the package ships with a stub provider (all gates on), so the baseline behaviour for adopters who do not configure a provider is identical to the pre-toggling behaviour. Adoption is opt-in; the port is discoverable; the adapters are exemplary rather than canonical.

Running-example connection

The running example's readonly enabled = false is a coarser form of the same idea — the whole Feature is gated off at spec time. REQ-ORTHOGONAL-TOGGLING generalises the static enabled boolean to a runtime-resolved gate.

Field-by-field observations

The response clause is carefully worded: "classify the gated AC as NotApplicable in the compliance report when the gate resolves to off, rather than as Uncovered, and exclude it from compliance --strict failures." NotApplicable is introduced here as a third compliance state alongside Covered and Uncovered. The REQ names it but does not define it — its definition lives in the Feature that satisfies this REQ (FEATURE-TOGGLING) and in the compliance-report shape.

The fit-criteria list carries an eslint quality-gate: "no-process-env in src/". That is a lint rule enforcing a policy separation — the idea that reading process.env directly leaks configuration concerns into the source tree, and that the ToggleProvider port is the seam through which all environment-derived gating must flow. Declaring the lint rule at the REQ level means any violation is a REQ violation, not just a code-style nit.

The inspection checklist includes an example-ecosystem requirement: "an example adapter for LaunchDarkly or GrowthBook ships in examples/". Shipping an example adapter is a form of user-facing documentation that the port is actually integrable — the reference implementations that let a newcomer see the shape without having to infer it from the interface alone.

The rollout-percentage fit criterion — "rollout percentages gated by a deterministic seeded RNG for testability" — is a testability invariant. Non-deterministic rollout percentages (Math.random()) would make the compliance report flaky. The REQ locks the implementation into a seeded RNG so the same gate, with the same seed and context, always resolves the same way in tests.

The risk level is High, and the rationale is the stakes of getting this wrong: shops on LaunchDarkly, Unleash, or GrowthBook cannot adopt a traceability DSL that conflates their feature flags with uncovered ACs. The fragmentation risk — "they fork the DSL or bolt on home-grown toggling, fragmenting the ecosystem" — is the ecosystem-health concern that makes this the highest-priority Draft in the chapter.

The eleven, clustered

Diagram
The eleven Requirements of chapter 04, grouped by the cluster they serve. No @Refines edges connect any two of these eleven, so the grouping is rhetorical rather than structural; it is drawn from the rationale fields and from how the REQs function in the package.

The split is visible at a glance. Five Requirements are Approved (blue) — the Tier-1 commitments already under test. Six are Draft (amber) — the Tier-2 roadmap the package declares openly as intended but not yet shipped. The ergonomics cluster is the largest (five REQs), which is not an accident — it is the cluster that will drive adoption. Fidelity is the shortest (three REQs) because the contract is narrow: spec is canonical, round-trip is symmetric, editor support is zero-config. Traceability splits into static (mermaid) and interactive (TUI) — one audience reads diagrams, the other navigates graphs. The meta cluster is a single REQ, because there is only one meta-question: does the package use itself?

No @Refines edge connects any two of these eleven. The rationale of REQ-ROUND-TRIP-EDITABLE cites REQ-SPEC-IS-SOURCE-OF-TRUTH as precedent, but that is a precedent evidence entry, not a structural @Refines relation. The refinement tree in packages/requirements/requirements/requirements/ is shallow here; it deepens in the second half of the enumeration (chapter 05), where some of the orthogonal-property REQs refine the Tier-1 REQs of this chapter.

The priority distribution across the eleven is also worth noting. Two REQs carry Priority.Critical — dog-food and spec-is-source-of-truth — and both are Approved. Three carry Priority.High — bootstrap-zero-friction, editor-first-class (both Approved), orthogonal-toggling (Draft). Four carry Priority.Medium — tui-modern (Approved), round-trip-editable, live-feedback, visual-traceability (all Draft). Two carry Priority.Low — discoverable-traceability, low-friction-acs (both Draft). The pattern is recognisable: Critical and High for the non-negotiable contracts; Medium for the Tier-2 roadmap with firm intent; Low for the nice-to-haves that ship if they ship.

The risk levels line up with priorities but not identically. REQ-DOG-FOOD and REQ-SPEC-IS-SOURCE-OF-TRUTH declare Critical risk ("credibility collapses", "data loss events erode trust"). REQ-BOOTSTRAP-ZERO-FRICTION, REQ-EDITOR-FIRST-CLASS, and REQ-ORTHOGONAL-TOGGLING declare High risk (adoption capped, hand-editing fails, fragmentation). REQ-TUI-MODERN, REQ-ROUND-TRIP-EDITABLE, REQ-LIVE-FEEDBACK, and REQ-VISUAL-TRACEABILITY declare Medium risk (UX feels dated, formats become second-class, drift goes unnoticed, stakeholders disengage). REQ-DISCOVERABLE-TRACEABILITY and REQ-LOW-FRICTION-ACS declare Low risk (onboarding slows, wizard-abandonment stays high). The pattern shows a package author who has thought about the failure modes and assigned them honest weights — no Critical risk on a Low priority REQ, no Low risk on a Critical priority REQ.

Cross-REQ patterns

Reading the eleven in sequence, several patterns recur often enough to be worth naming explicitly.

Pattern 1 — ports as the orthogonality seam

REQ-TUI-MODERN introduces the Prompt port. REQ-ORTHOGONAL-TOGGLING introduces the ToggleProvider port. REQ-LIVE-FEEDBACK implies (but does not name) the FileSystem port. Each Requirement that touches an environmental concern — user input, feature flagging, filesystem watching — names a port through which that concern is injected. That is not accidental. The package's top-level architecture is port-driven (the project's CLAUDE.md states: "analysis core takes a FileSystem port; CLI shell injects real fs, tests inject in-memory maps"). Each of the eleven REQs that could have leaked an environmental dependency into the core instead specifies a port at the REQ layer, leaving the adapter choice to the CLI shell.

The benefit is testability — every REQ becomes a unit testable in isolation, with in-memory doubles for every port. The cost is one extra indirection at read-time. For a DSL where the test suite is the credibility artefact (REQ-DOG-FOOD), testability wins.

Pattern 2 — demonstration alongside unit-test

Five of the eleven REQs — bootstrap-zero-friction, tui-modern, visual-traceability, discoverable-traceability, and (implicitly) live-feedback through its inspection checklist — include a demonstration fit criterion alongside or instead of unit tests. The pattern is consistent: when the acceptance bar includes a user-facing UX quality that no unit assertion can reach, the REQ names a demonstration scenario — a screen recording, a rendered diagram, an embedded README artefact — as the missing piece. DefaultStyle.vocabulary.verificationMethods exposes Demonstration as a first-class method precisely so REQs can take this shape without being downgraded.

The discipline this pattern encodes is real. A REQ that depended only on unit tests could claim satisfaction with a passing test suite and still fail the user. A REQ that names a demonstration scenario forces the author to produce and publish the demonstration — a recording, a diagram, a README section — as part of the shipped artefact.

Pattern 3 — mitigations as design notes

Four of the eleven REQs (round-trip-editable, live-feedback, visual-traceability, discoverable-traceability, orthogonal-toggling — so actually five) declare explicit mitigations in their risk fields. Mitigations are optional on RequirementRisk<L> but are load-bearing here. They function as engineering hints that future maintainers can read as design constraints: "Keep watch-mode deliberately simple (no daemon, no IPC) so Ctrl-C is the full teardown story", "Keep the port minimal — a single isEnabled(gate, ctx) method — so adapters stay tiny", "Publish the generated diagram in the package README so stakeholders see it even without running the CLI".

These are not fit criteria — they are not gated in CI — but they are recorded, typed, versioned, and visible. A future maintainer who proposes a daemon-based watch mode can be pointed at the mitigation and asked to justify the deviation. That is a modest governance pattern, but over package-years it compounds: the design rationale travels with the Requirement.

Pattern 4 — precedent-heavy rationale

Nine of the eleven REQs cite external precedents in their rationale.evidence arrays: feedback_no_describe_it (a user-memory precedent), terraform/kubernetes, Völter & Pech's projectional editing paper, astro/nuxt/create-t3-app, vitest-watch + tsc-watch, SysML-BDD/IBD, lazygit/lazydocker/k9s, github-copilot/tabnine, and launchdarkly/growthbook/unleash. The pattern is deliberate: the package does not try to invent; it adopts and names the origin.

Precedent-heavy rationale has two effects. First, it signals technical humility — the author is clear that the patterns work because other tools have proven them, not because the author invented them. Second, it gives reviewers an anchor: any Requirement that cites a precedent can be evaluated by asking whether the precedent is honestly applicable and whether the adaptation preserves what made the precedent work. A reviewer who knows lazygit can evaluate REQ-DISCOVERABLE-TRACEABILITY against the experience of using lazygit.

Pattern 5 — the EARS split

Of the eleven REQs, five use the ubiquitous statement pattern and six use event-driven. The ubiquitous form — "The system shall …" — fits Requirements whose behaviour is always-on: dog-food (always enforce), bootstrap-zero-friction (always provide), round-trip-editable (always regenerate), tui-modern (always use clack). The event-driven form — "When X, the system shall …" — fits Requirements triggered by a specific action: spec-is-source-of-truth (when feature sync runs), editor-first-class (when a file is opened), live-feedback (when a file changes), visual-traceability (when trace graph runs), discoverable-traceability (when explore runs), low-friction-acs (when a title is entered), orthogonal-toggling (when a gate is evaluated).

No Requirement in this chapter uses state-driven, optional, unwanted, or natural. The EARS vocabulary is richer than the package needs at this scale; the two most common patterns cover everything. Chapter 05 introduces more pattern variety.

Pattern 6 — the rationale.kind distribution

The six legal values of rationale.kindevidence-based, principle, value-driven, regulatory-compliance, risk-mitigation, precedent — are populated unevenly in this chapter. Four REQs declare principle (dog-food, spec-is-source-of-truth, round-trip-editable, editor-first-class); four declare evidence-based (bootstrap-zero-friction, live-feedback, low-friction-acs, and one interior evidence entry pattern in several others); three declare value-driven (tui-modern, visual-traceability, discoverable-traceability, orthogonal-toggling — so actually four). No REQ in this chapter declares regulatory-compliance, risk-mitigation, or precedent as its top-level rationale kind — those kinds fit better with legal, safety-critical, or refined-from-parent REQs, most of which live in chapter 05 or in the other built-in Styles.

The distribution tells a story. principle Requirements are asserted without external justification — the author believes the claim is self-evident to anyone in the domain. evidence-based Requirements carry studies, metrics, or precedent evidence as their primary backing. value-driven Requirements acknowledge that the claim is aesthetic or ecosystem-shaped rather than strictly testable. A healthy spec layer in a developer-tooling package looks like this one — a mix of principles, evidence, and value claims, with no single kind dominating.

Pattern 7 — source provenance

Every REQ in this chapter declares source.type: 'stakeholder'. The DefaultStyle.vocabulary.sourceKinds list exposes eight kinds — stakeholder, regulation, standard, contract, risk, business-goal, incident, domain-knowledge — but for a developer-tooling package, stakeholder is the only provenance that honestly fits. The source.role slot varies — project-rule, user, principle/idempotent-generation, principle/no-forced-source, principle/spec-as-code, principle/fast-feedback, principle/visual-communication, principle/discoverable-navigation, principle/reduce-cognitive-load, principle/production-grade-toggle — and provides the sub-classification. The principle/* pattern in role is the package's convention for declaring that the stakeholder is an internal principle rather than an external party.

Every REQ in this chapter also declares source.date: '2026-04-14' — the package-bootstrap date. That is honest: the entire spec layer was declared in one session. Future REQs added later will carry later dates; the date field is append-only, part of the append-only audit log shape the package commits to.

What this half of the set establishes

The eleven Requirements above, taken together, assert four things about the package:

First, it must be usable. Bootstrap must be one command (REQ-BOOTSTRAP-ZERO-FRICTION). The TUI must look like 2026, not 2010 (REQ-TUI-MODERN). Feedback must be live, not deferred to CI (REQ-LIVE-FEEDBACK). Common paralysis points — blank AC-name prompts — must be softened by suggestion (REQ-LOW-FRICTION-ACS). Production gating must not fight the traceability report (REQ-ORTHOGONAL-TOGGLING). Five Requirements, one audience: the developer using the tool hands-on.

Second, it must be faithful. The spec is the canonical source of truth, and generation is idempotent (REQ-SPEC-IS-SOURCE-OF-TRUTH). Either side of the spec↔code duality is first-class, and edits round-trip (REQ-ROUND-TRIP-EDITABLE). Hand-editing the JSON must feel native — zero config, autocomplete, inline docs (REQ-EDITOR-FIRST-CLASS). Three Requirements, one contract: the bytes on disk are the truth, and every user-facing workflow respects that truth.

Third, it must be inspectable. The traceability graph must render as a static diagram for stakeholders who read diagrams (REQ-VISUAL-TRACEABILITY). It must also be navigable as an interactive TTY browser for contributors who read graphs by walking them (REQ-DISCOVERABLE-TRACEABILITY). Two Requirements, two audiences, one graph.

Fourth, it must be self-consistent. The package must validate itself with itself (REQ-DOG-FOOD). One Requirement, one constraint, and the whole spec layer rests on it.

The other eleven — in chapter 05 — are the orthogonal properties: invariants, refactor-safety, versioning, scenarios, orchestration, extensibility, audit, AI-adapter, vocabulary alignment, parallel deliverable, completeness. Where this chapter is mostly about the user and the contract, the next is mostly about the engineering invariants that keep the package maintainable over time. Between them, the two chapters are the full spec layer.

One thing worth saying explicitly: the four clusters are not mutually exclusive, and the split is rhetorical more than structural. REQ-TUI-MODERN could plausibly be read as a traceability concern (the explore command is itself a TUI); REQ-LIVE-FEEDBACK could be read as a fidelity concern (it keeps the spec and its on-disk artefacts in sync at save-time). The clusters in the diagram are the primary lens — the dominant function each REQ serves in the package — not an exclusive partition. A reader comparing the eleven REQs would land on roughly the same clustering but might place one or two REQs differently; that is the expected level of fuzziness.

What the enumeration does show unambiguously is the shape the package commits to. The Requirements are concrete: each is an abstract TypeScript class with five to ten typed fields, each field drawn from a style-validated vocabulary, each fit criterion binding to a specific test name or metric threshold. Reading the eleven in sequence, the overall shape of the package's spec layer becomes visible — not as a slogan but as a set of artefacts that will be referenced by the bipartite graph in chapter 06 and the test matrix in chapter 07.

And the enumeration shows a package that is honest about its own staging. Six of the eleven are Draft. The author did not hide the roadmap in a TODO.md file or a product backlog; the roadmap is in source, under the same Requirement<DefaultStyleType> shape as the shipped behaviour, subject to the same compliance checks. That is a small decision that carries a lot of weight: when every REQ is a typed class, there is no second-tier home for "we'll get to it" — the intent is specified with the same rigour as the implementation.

What the five Approved REQs already enforce

The Tier-1 commitments — the five REQs currently in Approved state — together describe what the package will not ship without. Reading them as a set:

  • Every test file in packages/requirements/test/ is free of describe and it; every Feature class carries @Satisfies(...) with at least one REQ argument; src/cli/** clears 98% line coverage (REQ-DOG-FOOD).
  • npx requirements feature new walks a user through Feature creation without requiring any hand-editing, emitting a compilable .ts file, a .spec.json sibling, and scaffolded test stubs (REQ-BOOTSTRAP-ZERO-FRICTION).
  • npx requirements feature sync computes a diff, renders a preview, and writes only on explicit confirmation; --dry-run is a first-class exit path (REQ-SPEC-IS-SOURCE-OF-TRUTH).
  • Every generated .spec.json carries a $schema key as its first entry; npx requirements schema register merges into .vscode/settings.json non-destructively; the schema generator output is ajv-valid (REQ-EDITOR-FIRST-CLASS).
  • Every interactive prompt in the CLI routes through the Prompt port; @clack/prompts is the only direct TUI backend; readline is forbidden in src/cli/** (REQ-TUI-MODERN).

Taken together, these five are a minimal-viable Requirements DSL: self-verifying, wizard-creatable, safe-to-regenerate, editor-friendly, terminal-modern. Everything else is icing — but icing that the package has already specified as intent.

What the six Draft REQs will demand when Approved

The Tier-2 roadmap — the six REQs currently in Draft state — describes what the package will demand once the promotion happens. Reading them as a set:

  • Edits on either side of the spec↔code duality regenerate the sibling artefact, converging to a fixed point after three round-trips; conflicting edits produce a conflict marker rather than a silent overwrite (REQ-ROUND-TRIP-EDITABLE).
  • npx requirements watch revalidates affected specs within 500 ms of any save, debouncing rapid bursts into single revalidations, cleanly tearing down on Ctrl-C (REQ-LIVE-FEEDBACK).
  • npx requirements trace graph --format mermaid emits a valid Mermaid flowchart with coverage state, disabled-feature styling, and anchor-linkable node ids, with the package's own feature graph embedded in the README (REQ-VISUAL-TRACEABILITY).
  • npx requirements explore opens a full-TTY keyboard-navigable browser over the traceability graph with lazygit-style bindings and a ?-help overlay (REQ-DISCOVERABLE-TRACEABILITY).
  • The wizard, on Feature-title entry, proposes three to five deterministically-inferred AC names drawn from the local corpus, never leaking names from outside the repo, never requiring a network call (REQ-LOW-FRICTION-ACS).
  • A ToggleProvider port is injectable via the CLI shell; @Toggle(gate) gates Features or ACs; the compliance report classifies off-gated ACs as NotApplicable rather than Uncovered; process.env is forbidden in src/ (REQ-ORTHOGONAL-TOGGLING).

Promotion from Draft to Approved will happen one REQ at a time, as each satisfying Feature lands, its ACs are implemented, and its tests bind to real Feature classes via @FeatureTest/@Verifies. The DefaultStyle.vocabulary.statusWorkflow.transitions allows the Draft → Approved edge directly — no intermediate state is required. Chapter 05 discusses the orthogonal-property REQs that will tighten these promotions into a reproducible audit trail.

Running-example recap

The running example — FEATURE-TRACE-EXPLORER-TUI — touches this half at two explicit edges and one implicit one. Explicitly, it satisfies REQ-DISCOVERABLE-TRACEABILITY (the interactive TUI) and REQ-DOG-FOOD (the constraint that every Feature carry @Satisfies). Implicitly, it benefits from REQ-TUI-MODERN (via its use of the Prompt port) and REQ-BOOTSTRAP-ZERO-FRICTION (it was itself scaffolded by feature new).

The third Requirement the running example satisfies — REQ-PARALLEL-DELIVERABLE — lives in the other half. It will be covered in chapter 05.

The shape of the running example's satisfaction pattern is worth naming one more time. Three REQs, one Feature. Ten abstract ACs. Each AC is typed as (): ACResult and bound, when implemented, to one or more test functions via @FeatureTest(FeatureTraceExplorerTuiFeature) + @Verifies('acName'). The REQs declare the WHY. The Feature declares the WHAT. The ACs declare HOW-MEASURED. The tests declare the PROOF. Chapter 06 walks the WHAT-to-WHY edges; chapter 07 walks the PROOF-to-HOW-MEASURED edges. This chapter's contribution is to make the eleven WHYs legible — so that when chapter 06 points at REQ-DISCOVERABLE-TRACEABILITY and REQ-DOG-FOOD, the reader knows what they are.

A note on the running example's enabled = false. The Feature ships disabled — meaning its ACs are not expected to pass in the current build. That is honest: the interactive TUI is Tier-2 intent, not Tier-1 behaviour. The ACs are declared, the tests can be scaffolded, but the implementation is not complete. The compliance report, reading the enabled = false boolean on the Feature, classifies the ACs as Disabled rather than Uncovered — which is exactly the shape REQ-ORTHOGONAL-TOGGLING generalises into a runtime port. The static enabled = false is the simplest instance of the toggle pattern: a compile-time gate with no provider, classified correctly by the compliance engine without any adapter wiring. When REQ-ORTHOGONAL-TOGGLING promotes to Approved, enabled = false becomes @Toggle('explore-tui') backed by a provider, and the same classification behaviour continues to work.

The running example thus sits at a specific point in the package's staging: a Tier-2 satisfier for a Tier-2 REQ, with a Tier-1 enabled boolean anticipating a Tier-2 toggle port. Reading it this way clarifies why it was chosen as the running example — it touches almost every staging boundary the package distinguishes, making it a small but representative surface for the series to keep returning to.

One final note on the running example before chapter 05 picks up the thread: the third REQ it satisfies — REQ-PARALLEL-DELIVERABLE — is part of the engineering-invariants cluster in chapter 05. Its role is to name the property that individual Features can be developed in parallel without blocking on a shared Tier-1 core. That the running example satisfies both a user-facing REQ (REQ-DISCOVERABLE-TRACEABILITY) and an engineering-invariant REQ (REQ-PARALLEL-DELIVERABLE), plus the meta REQ (REQ-DOG-FOOD), is exactly the two-to-three shape chapter 00 flagged as the minimal non-trivial many-to-many edge the DSL can express. Three REQs, one Feature, ten ACs; the shape that typed-specs could not express and that this package makes unremarkable.

The kind field as a diagnostic

Of the five legal requirementKinds values — Functional, NonFunctional, Constraint, Compliance, UserStory — the eleven REQs in this chapter populate three: eight NonFunctional, two UserStory, one Constraint. No Functional. No Compliance.

The absence of Functional REQs in this half is diagnostic. A Functional REQ describes a behaviour the system performs: "the system shall compute X from Y". The eleven REQs above are not behaviours; they are qualities of behaviours (NonFunctional), user-flow commitments (UserStory), or work discipline (Constraint). The Functional behaviours the package actually implements — compute a compliance report, scaffold a test file, emit a mermaid diagram — live at the Feature layer, not the Requirement layer. That is the correct separation. Functional what-the-system-does belongs to the Feature tier; non-functional how-well-the-system-does-it belongs to the Requirement tier.

The absence of Compliance REQs is also diagnostic. Compliance REQs cite regulation, legislation, or sectoral standards as their primary source. The package's audience is developer-tooling users, not regulated industries. Chapter 05 may introduce a Compliance-shaped REQ if licensing or supply-chain compliance is in scope; for this chapter, the audience does not require it.

The two UserStory REQs — bootstrap-zero-friction and low-friction-acs — are the ones most directly about user interaction. Their shape is recognisably UserStory-like even though they are written in EARS patterns: "the system shall let a user create a Feature class by running one interactive command" is an EARS ubiquitous rewriting of "as a user, I want to create a Feature class interactively, so that I do not have to hand-edit files". The EARS form is more testable; the UserStory classification preserves the shape-of-concern.

The one Constraint — dog-food — is the discipline REQ. Constraints are the REQ kind that constrain the authors rather than the system. The package has exactly one such at Tier-1, which is the right density: more would be over-governance; zero would leave the discipline unspecified. One is the Goldilocks count for a package that is serious about its own discipline but not hostile to its own pace.

A short word on the audit trail the eleven declare

Each of the eleven REQs declares a source record but none of them currently populates the optional history? field. RequirementHistoryEntry in src/base.ts is { date, author, change, reason } and is intended as an append-only audit trail: when a REQ is promoted from Draft to Approved, the promotion should leave a history entry; when a fit criterion is tightened or a risk level is raised, the change should leave a history entry.

The absence of history entries today is not a gap — it reflects the age of the package. Every REQ in this chapter was declared on the package-bootstrap day (2026-04-14), and no REQ has yet changed since. The $schemaVersion: '2026-04-14' declared at the package level is the single point-in-time marker for the whole spec layer.

As the package matures and REQs are promoted, deprecated, or refined, the history field will populate. Chapter 05 discusses the audit-hooks REQ that will automate the history-entry creation (REQ-AUDIT-HOOKS), so that no change to a REQ class bypasses the audit trail. Together the two mechanisms — typed history entries plus automatic audit hooks — give the package a machine-readable change log at the Requirement level, separate from and complementary to the git log.

For this chapter, the takeaway is modest: the eleven REQs are young and have no history to show. The shape is reserved, though, so future changes will be recorded.

A related observation: the tracedTo? field is also empty on all eleven. tracedTo is the opaque-external-link field — Linear tickets, GitHub issues, Notion pages, Jira tickets, bare URLs. The package has no external issue tracker for its own Requirements; the class declaration is the record. That is a deliberate choice for a package whose whole premise is that the spec should be in source. Duplicating the spec to an external tracker would be a layering inversion. When a REQ gains an external link — for instance, when a compliance audit references a specific REQ and wants a URL — the tracedTo array will populate. Until then, the TypeScript class is the primary record.

Optional fields, together, describe what the package commits to record: refines? for the tree structure, tracedTo? for external links, history? for the audit trail. All three are currently empty across the eleven; all three are reserved for later population without requiring a schema change. The $schemaVersion: '2026-04-14' marker will not need to move merely because history entries start appearing.

How the eleven REQs coordinate with the compliance report

The package's npx requirements compliance command reads the REQs declared in packages/requirements/requirements/requirements/ and the Features declared in packages/requirements/requirements/features/, resolves the @Satisfies and @Refines edges, walks the test bindings from @FeatureTest/@Verifies, and emits a report. For the eleven REQs in this chapter, the report looks approximately like this (structure, not exact wording):

  • REQ-DOG-FOOD — Approved, Critical. Satisfiers: 12 Features (every Feature, effectively). ACs bound: 2/2. Coverage gate: 98% on src/cli/**, PASS. Status: Verified (every AC has ≥ 1 test).
  • REQ-BOOTSTRAP-ZERO-FRICTION — Approved, High. Satisfiers: FEATURE-FEATURE-NEW-COMMAND, FEATURE-CLACK-ADAPTER, FEATURE-SCAFFOLD-CORE. ACs bound: 2/2. Demonstration: present (CI smoke test). Status: Verified.
  • REQ-SPEC-IS-SOURCE-OF-TRUTH — Approved, Critical. Satisfiers: FEATURE-FEATURE-SYNC-COMMAND, FEATURE-FEATURE-NEW-COMMAND. ACs bound: 3/3. Status: Verified.
  • REQ-ROUND-TRIP-EDITABLE — Draft, Medium. Satisfiers: FEATURE-BIDIRECTIONAL-SYNC (Tier-2, enabled = false). ACs bound: 0/5. Status: Draft — shape valid, implementation stubbed.
  • REQ-EDITOR-FIRST-CLASS — Approved, High. Satisfiers: FEATURE-FEATURE-SCHEMA-COMMAND. ACs bound: 3/3. Status: Verified.
  • REQ-TUI-MODERN — Approved, Medium. Satisfiers: FEATURE-CLACK-ADAPTER, FEATURE-FEATURE-NEW-COMMAND. Inspection: PASS. Demonstration: pending (recording to be produced). Status: Implemented (awaiting Demonstration for Verified).
  • REQ-LIVE-FEEDBACK — Draft, Medium. Satisfiers: FEATURE-WATCH-MODE (Tier-2). ACs bound: 0/2. Status: Draft.
  • REQ-VISUAL-TRACEABILITY — Draft, Medium. Satisfiers: FEATURE-FEATURE-MERMAID-GRAPH, FEATURE-TRACE-CORE. ACs bound: 0/3. Status: Draft.
  • REQ-DISCOVERABLE-TRACEABILITY — Draft, Low. Satisfiers: FEATURE-TRACE-EXPLORER-TUI (Tier-2, enabled = false). ACs bound: 0/2. Status: Draft.
  • REQ-LOW-FRICTION-ACS — Draft, Low. Satisfiers: FEATURE-FEATURE-HEURISTIC-SUGGESTER, FEATURE-FEATURE-NEW-COMMAND. ACs bound: 0/3. Status: Draft.
  • REQ-ORTHOGONAL-TOGGLING — Draft, High. Satisfiers: FEATURE-TOGGLING, FEATURE-COMPLIANCE. ACs bound: 0/4. Status: Draft.

The report is not a static artefact — it recomputes on every invocation from the current source tree. The shape above is accurate at 2026-04-14; the counts will drift as Draft REQs promote to Approved and new Features land. What stays stable is the shape of the output: every REQ gets a status row, every satisfier is listed, every AC binding is counted, every gate (coverage, inspection, demonstration) is resolved. The enumeration in this chapter is the human-readable version of the same data the compliance report emits machine-readably.

A reader who wants to verify any claim made in this chapter against the live state of the package can run npx requirements compliance locally and read the matching row. That is the point of dog-fooding at the chapter level: the prose and the compliance report tell the same story, and divergence between them is a bug in one or the other.

This dual-surface property — the chapter and the report narrating the same underlying data — is itself a form of traceability. The @Satisfies decorators in the Feature classes, the @Refines decorators in the Requirement classes, and the @FeatureTest/@Verifies decorators in the test files together form a graph that the compliance engine traverses; this chapter traverses the same graph in prose. If a reader catches the chapter claiming something the report denies (or vice versa), one of the two is wrong, and the correction path is knowable: update the chapter, update the source, or file a bug in the compliance engine.

That is a small property but an uncommon one in software documentation. Most documentation drifts from the code it documents because the two artefacts have no shared machine-checkable structure. This chapter and the compliance report share a structure — the REQ classes — and the agreement is recoverable. The chapter is a reading of the spec; the spec is a set of TypeScript classes; the compliance report is another reading of the same classes. Three readings, one source.

What the absence of @Refines says

None of the eleven REQs in this chapter carries a refines field. That is, none of them declares itself a refinement of a parent Requirement — no @Refines(ParentReq) decorator, no refines: ['REQ-SOMETHING'] array. Reading across the eleven, this is worth naming explicitly because it describes the shape of the spec layer's top level.

The Requirement<S> class exposes refines?: readonly string[] as an optional field. When present, it makes the REQ a child of one or more parent REQs in the refinement tree — "this REQ is a more specific expression of what that REQ asks for". The SysML tradition uses refine and derive for similar purposes; the package adopts @Refines as its decorator form.

The eleven REQs in this chapter are all top-level — no parent to point at. Each names a distinct concern:

  • dog-food is not a refinement of anything; it is the foundational constraint.
  • bootstrap-zero-friction is not a refinement of anything; it names the entry-point UX.
  • spec-is-source-of-truth is not a refinement of anything; it asserts the canonical-source contract.

...and so on. The rationale of REQ-ROUND-TRIP-EDITABLE cites REQ-SPEC-IS-SOURCE-OF-TRUTH as a precedent, but the citation is an evidence entry in the rationale, not a structural refines edge. The distinction matters: rationale-precedent is a rhetorical link ("my claim rests on the same ground as this prior REQ"), while @Refines is a structural link ("I am a narrower instance of this REQ, and satisfying me implies contributing to satisfying the parent"). The package keeps the two separate.

Chapter 05's eleven REQs include at least three that declare @Refines edges — typically to Tier-1 REQs from this chapter. For instance, a Tier-2 REQ about scenario-based testing plausibly refines the REQ-DOG-FOOD constraint (scenarios are a specific expression of dog-food discipline); a Tier-2 REQ about extensibility plausibly refines the architectural commitments implied by the spec-is-source-of-truth contract. The refinement tree deepens in the second half.

For this chapter, the absence of @Refines is the correct shape. Top-level REQs should not refine — if they did, they would not be top-level. The eleven form the set of first-class concerns the package addresses; chapter 05's eleven are a mix of additional first-class concerns and refinements into specifics.

The rationale evidence catalogue

A third reference table — the evidence entries cited across the eleven REQs' rationale fields. The RequirementEvidence discriminated union exposes five kinds (metric, incident, study, expert-opinion, precedent); the eleven REQs use four of them.

Precedent citations (eight entries across seven REQs):

  • REQ-DOG-FOODfeedback_no_describe_it (user-memory rule)
  • REQ-SPEC-IS-SOURCE-OF-TRUTHterraform/kubernetes
  • REQ-ROUND-TRIP-EDITABLEREQ-SPEC-IS-SOURCE-OF-TRUTH (internal precedent)
  • REQ-TUI-MODERNastro/nuxt/create-t3-app
  • REQ-LIVE-FEEDBACKvitest-watch + tsc-watch
  • REQ-VISUAL-TRACEABILITYSysML-BDD/IBD
  • REQ-DISCOVERABLE-TRACEABILITYlazygit/lazydocker/k9s
  • REQ-LOW-FRICTION-ACSgithub-copilot/tabnine
  • REQ-ORTHOGONAL-TOGGLINGlaunchdarkly/growthbook/unleash

Study citations (five entries across four REQs):

  • REQ-ROUND-TRIP-EDITABLE → Völter & Pech, Projectional Editing and Structural Source Management (2012)
  • REQ-LIVE-FEEDBACK → Parnin & Rugaber, Programmer Attention During Development (2011)
  • REQ-VISUAL-TRACEABILITY → Abad & Ruhe, Software Requirements Visualisation: A Systematic Mapping Study (2016)
  • REQ-DISCOVERABLE-TRACEABILITY → Maletic & Marcus, On the Navigation of Software Artifact Graphs (2001)
  • REQ-LOW-FRICTION-ACS → Klein, Recognition-primed Decision Making (1993)
  • REQ-ORTHOGONAL-TOGGLING → Hodgson (Fowler), Feature Toggles (aka Feature Flags) (2017)

Expert-opinion citations (five entries across five REQs):

  • REQ-BOOTSTRAP-ZERO-FRICTIONuser-conversation-2026-04-14
  • REQ-ROUND-TRIP-EDITABLEuser-plan-discussion-2026-04-14
  • REQ-VISUAL-TRACEABILITYuser-plan-discussion-2026-04-14
  • REQ-DISCOVERABLE-TRACEABILITYuser-plan-discussion-2026-04-14
  • REQ-LOW-FRICTION-ACSuser-plan-discussion-2026-04-14
  • REQ-ORTHOGONAL-TOGGLINGuser-plan-discussion-2026-04-14

Metric citations (one entry):

  • REQ-LIVE-FEEDBACKindustry-baseline-watch-latency (vitest-watch median, 200-500ms)

No incident citations in this chapter. Incidents are typically the evidence that motivates Compliance-kind or risk-mitigation-kind REQs ("X broke in 2024, therefore we now require Y"); the package has not accumulated enough production history to cite incidents, and when it does, they will show up in the audit trail of individual REQ history fields rather than in new evidence entries.

The evidence catalogue shows a healthy mix. Precedent citations anchor the package to existing tooling so the patterns adopted are not idiosyncratic. Study citations anchor claims to peer-reviewed literature where possible. Expert-opinion citations acknowledge that some claims rest on conversations rather than external validation — and name the date and context of those conversations so a future reviewer can trace the provenance. One metric, zero incidents, no narrative fallbacks. The evidence surface is structured, attributable, and traceable. That is the point of declaring rationale as a typed field rather than a free-form note.

A condensed table of the fit-criterion shapes

Before the summary table, a second reference: the distribution of fit-criterion kinds across the eleven REQs. The RequirementFitCriterion discriminated union in src/base.ts exposes seven kinds; the eleven REQs use five of them:

REQ id unit-test coverage-threshold quality-gate metric inspection demonstration narrative
REQ-DOG-FOOD 1 1 1 - - - -
REQ-BOOTSTRAP-ZERO-FRICTION 2 - - - - 1 -
REQ-SPEC-IS-SOURCE-OF-TRUTH 3 - - - - - -
REQ-ROUND-TRIP-EDITABLE 3 - 1 - 1 - -
REQ-EDITOR-FIRST-CLASS 3 - - - - - -
REQ-TUI-MODERN - - - - 1 1 -
REQ-LIVE-FEEDBACK 2 - - 1 1 - -
REQ-VISUAL-TRACEABILITY 3 - - - 1 1 -
REQ-DISCOVERABLE-TRACEABILITY 2 - - - 1 1 -
REQ-LOW-FRICTION-ACS 3 - - - 1 - -
REQ-ORTHOGONAL-TOGGLING 3 - 1 - 1 - -
Total 25 1 3 1 7 4 0

Twenty-five unit-test bindings across the eleven REQs is the headline number. That is the test surface this chapter's REQs commit to, distributed across the satisfying Features. Seven inspection checklists (qualitative review bars), four demonstrations (screen recordings or embedded artefacts), three quality gates (ripgrep, lint, CI-verify), one coverage threshold, one metric, zero narrative. The narrative kind is the escape hatch — a free-form text assertion when nothing more structured fits — and the fact that no REQ in this chapter uses it is a small sign of health. Every fit criterion has a structured shape; none had to fall back to prose.

The eleven as a condensed table

A reference table for the eleven REQs of this chapter, so a reader who has skimmed the sections can find any entry quickly:

REQ id Priority Status Kind Pattern Verification Cluster
REQ-DOG-FOOD Critical Approved Constraint ubiquitous Test Meta
REQ-BOOTSTRAP-ZERO-FRICTION High Approved UserStory ubiquitous Test Ergonomics
REQ-SPEC-IS-SOURCE-OF-TRUTH Critical Approved NonFunctional event-driven Test Fidelity
REQ-ROUND-TRIP-EDITABLE Medium Draft NonFunctional ubiquitous Test Fidelity
REQ-EDITOR-FIRST-CLASS High Approved NonFunctional event-driven Test Fidelity
REQ-TUI-MODERN Medium Approved NonFunctional ubiquitous Inspection Ergonomics
REQ-LIVE-FEEDBACK Medium Draft NonFunctional event-driven Test Ergonomics
REQ-VISUAL-TRACEABILITY Medium Draft NonFunctional event-driven Test Traceability
REQ-DISCOVERABLE-TRACEABILITY Low Draft NonFunctional event-driven Demonstration Traceability
REQ-LOW-FRICTION-ACS Low Draft UserStory event-driven Test Ergonomics
REQ-ORTHOGONAL-TOGGLING High Draft NonFunctional event-driven Test Ergonomics

Several patterns are now visible at a glance that the sequential reading obscures:

  • Eight of the eleven are NonFunctional. Two are UserStory. One is Constraint. No Functional or Compliance REQs in this chapter — the package's Tier-1 functional behaviour is in the Features, not the Requirements; the Requirements are about the qualities of those behaviours.
  • The two UserStory REQs are both ergonomics concerns (bootstrap, low-friction-acs). UserStory is the shape that fits a user does X, system does Y narrative, which is exactly how ergonomics concerns present themselves.
  • The one Constraint is the dog-food REQ. Constraints are shape-of-work promises, not behaviour promises. REQ-DOG-FOOD constrains how the authors work; it does not describe what the system does.
  • Inspection as verification appears once, Demonstration appears once, Test covers the other nine. The uniformity is deliberate — tests are the primary verification bar across the package, with inspection and demonstration reserved for the REQs whose acceptance shape is irreducibly non-automated.

The table is not the REQs; the REQs are the thirty-line class declarations quoted in each sub-section. But the table is useful for orientation — a reader who forgets which REQs are Tier 1 versus Tier 2 can return here, or who wants to scan for the one Demonstration-verified REQ can locate it immediately.

A reader who wants the same information in machine-readable form can also run npx requirements requirement list — the command emits a similar table pulled directly from the loaded REQ classes. The prose table in this chapter is the human-curated view; the CLI output is the structural view; both consume the same source. When the source changes, the CLI output updates automatically; the prose table in this chapter will drift and need a prose update. That is the one acknowledged asymmetry between the chapter and the compliance surface — the chapter is a timestamped reading, not a live view.

From the enumeration to the next chapter

Chapter 05 takes the other eleven REQs — REQ-INVARIANT-BRANDED-PRIMITIVES, REQ-REFACTOR-SAFETY, REQ-VERSIONING, REQ-SCENARIO, REQ-ORCHESTRATION, REQ-EXTENSIBILITY, REQ-AUDIT-HOOKS, REQ-AI-ADAPTER, REQ-VOCABULARY-ALIGNMENT, REQ-PARALLEL-DELIVERABLE, REQ-COMPLETENESS — and reads them with the same discipline applied here. The shape will be recognisable: class excerpt, field-by-field reading, Why / How / Running-example connection. The content will be different: chapter 05's REQs are about the engineering invariants that keep the package maintainable — branded primitives at the type layer, codemod-driven renames, a versioned on-disk format, scenario support, orchestration, extension points, audit trails, AI-assistance shape, vocabulary alignment with SysML/EARS, the parallel-deliverable story, and the completeness invariant that ties the whole chain together.

Reading the two chapters as one continuous enumeration is the intended experience. A reader who stops after chapter 04 has the user-facing and contract-level REQs; a reader who continues through chapter 05 has the full twenty-two. Chapter 06 then assembles the bipartite REQ↔FEAT graph from the complete set. Chapter 07 walks the tests.

One last point before the forward-links. The number twenty-two is not magic. It is the number of Requirements the package declares at the moment this chapter is written (2026-04-14). New REQs may be added; old REQs may be deprecated (the DefaultStyle.vocabulary.statusWorkflow includes a Deprecated terminal state). The enumeration in these two chapters is a snapshot. What matters is not the count but the shape — the discipline that every new REQ the package declares will share the same class, the same typed fields, the same compliance gates, the same readability as the eleven enumerated here. Twenty-two is where the package stands today. Twenty-three, twenty-four, thirty will all be declared the same way.

And that uniformity is the deepest meta-claim the package makes. A spec layer is only useful if its entries are comparable — if reading the tenth REQ is the same cognitive shape as reading the first. The Requirement<DefaultStyleType> class guarantees that comparability at the type level. The enumeration in this chapter and the next demonstrates it at the prose level. Every REQ has the same fields in the same order, validated by the same style, readable by the same convention. That uniformity is why the package can ship a spec layer that scales beyond the point where a prose specification document would collapse under its own repetition.

The discipline the uniformity enforces is worth one more sentence. When the eighteenth REQ is as legible as the first, when the twenty-second REQ follows the same cognitive path as the tenth, the specification does not decay as it grows. Prose specifications decay — they accumulate stylistic drift, vocabulary inconsistencies, half-converted examples, stale cross-references. Typed-class specifications do not. The class is the constraint; the constraint scales linearly; the reader's cognitive load per REQ stays constant. Twenty-two is already more than most prose specifications comfortably hold. A typed spec layer holds forty without noticing.

  • Chapter 00 — Named but Not Modelled — the gap typed-specs left, and the running-example introduction. Establishes the voice followed in this chapter and introduces FEATURE-TRACE-EXPLORER-TUI as the series-wide example.
  • Chapter 03 — The Requirement Class, the Decorators, the Registries — the class shape quoted in every REQ section of this chapter. Walks Requirement<S>, @Satisfies, @Refines, and the decorator registries in full. A reader who skimmed the How to read a REQ section at the start of this chapter will find the complete treatment there.
  • Chapter 05 — Twenty-Two Requirements, Part Two — the other eleven REQs, read with the same discipline. Covers invariants, refactor-safety, versioning, scenarios, orchestration, extensibility, audit, AI-adapter, vocabulary alignment, parallel deliverable, completeness.
  • Chapter 06 — The Bipartite REQ↔FEAT Graph — forward link for the satisfier counts quoted in each sub-section above. Walks every @Satisfies edge in the package, clustering by REQ and by Feature, and highlights the many-to-many structure that the eleven REQs participate in.
  • Chapter 07 — Tests as Proof — walks the @FeatureTest/@Verifies bindings and the coverage gates named in this chapter's fit criteria.
  • Requirement vs Feature — a side-by-side — the shape contrast that justifies the two-tier split this chapter enumerates on the REQ side. Reads in ten minutes; useful before or after this chapter for anyone still unsure why both tiers are necessary.

Previous: 03b — Newcomer Primer / Next: 05 — Twenty-Two Requirements, Part Two

⬇ Download