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

Part II: CMF Feature Catalog -- Requirements as Code

About This Chapter

This chapter expresses the CMF's own requirements using the Requirements DSL notation from Part IX. Every epic, feature, story, and task is a typed C# record with abstract acceptance criterion methods. This serves two purposes:

  1. Specification: It defines exactly what the CMF must do -- a complete product backlog
  2. Validation: It stress-tests the Requirements DSL design against a non-trivial, real-world project

The Bootstrap Question

This chapter uses the Requirements DSL notation, but the DSL does not exist yet. We are writing abstract record UserRolesFeature : Feature<PlatformScalabilityEpic> before the Feature<T> base type is implemented. This is not self-hosting -- it is design validation. True self-hosting (the CMF using its own tooling to track its own requirements) is a future milestone that becomes possible once the CMF is built.


Catalog Overview

Diagram

Epic 1: DomainModelingEpic

namespace Cmf.Requirements.Epics;

public abstract record DomainModelingEpic : Epic
{
    public override string Title => "Domain Modeling & Persistence Generation";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";
}

Feature 1.1: AggregateDefinition

namespace Cmf.Requirements.Features;

/// <summary>
/// Define aggregate roots, entities, and value objects using DDD attributes.
/// Source generators produce complete domain implementations.
/// </summary>
public abstract record AggregateDefinitionFeature : Feature<DomainModelingEpic>
{
    public override string Title => "Aggregate root, entity, and value object definition";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";

    /// <summary>
    /// A developer can declare a class with [AggregateRoot] and [EntityId],
    /// and the compiler produces a complete entity implementation with backing fields.
    /// </summary>
    public abstract AcceptanceCriterionResult DeveloperCanDefineAggregateRoot(
        string className, string idPropertyName);

    /// <summary>
    /// A developer can add [Entity] classes with [Composition] references,
    /// and the compiler validates that every entity is reachable from its aggregate root.
    /// </summary>
    public abstract AcceptanceCriterionResult ComposedEntitiesAreValidated(
        string aggregateRootClass, string[] entityClasses);

    /// <summary>
    /// A developer can add [ValueObject] types with [ValueComponent] properties,
    /// and the compiler generates immutable value objects with structural equality.
    /// </summary>
    public abstract AcceptanceCriterionResult ValueObjectsAreImmutable(
        string valueObjectClass, string[] componentNames);

    /// <summary>
    /// [Association] references across aggregate boundaries are validated:
    /// no cascade delete, eventual consistency enforced.
    /// </summary>
    public abstract AcceptanceCriterionResult CrossAggregateAssociationsAreEventuallyConsistent(
        string sourceAggregate, string targetAggregate);
}

Story 1.1.1: DefineAggregateRoot

public abstract record DefineAggregateRootStory : Story<AggregateDefinitionFeature>
{
    public override string Title => "Define an aggregate root with EntityId";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult PartialClassWithAggregateRootAttribute(
        string className);

    public abstract AcceptanceCriterionResult EntityIdPropertyIsRequired(
        string className, string idType);

    public abstract AcceptanceCriterionResult CompilerErrorIfNoEntityId(
        string className);
}

Tasks:

  • Task 1.1.1.1: Implement [AggregateRoot] attribute with MetaConcept registration (4h)
  • Task 1.1.1.2: Implement [EntityId] attribute with strongly-typed ID generation (3h)
  • Task 1.1.1.3: Stage 1 validator: reject aggregate without EntityId (2h)
  • Task 1.1.1.4: Stage 2 generator: emit backing fields and property implementations (6h)

Story 1.1.2: AddComposition

public abstract record AddCompositionStory : Story<AggregateDefinitionFeature>
{
    public override string Title => "Add composition relationships between entities";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult SingleEntityComposition(
        string parentClass, string childClass);

    public abstract AcceptanceCriterionResult CollectionEntityComposition(
        string parentClass, string childCollectionType);

    public abstract AcceptanceCriterionResult ValueObjectCompositionEmbedsColumns(
        string parentClass, string valueObjectClass);

    public abstract AcceptanceCriterionResult CascadeDeleteOnComposition(
        string parentClass, string childClass);
}

Tasks:

  • Task 1.1.2.1: Implement [Composition] attribute for single entity references (3h)
  • Task 1.1.2.2: Implement [Composition] for IReadOnlyList<T> collections (4h)
  • Task 1.1.2.3: Implement [Composition] for value objects (OwnsOne/OwnsMany) (4h)
  • Task 1.1.2.4: Stage 1 validator: verify all entities reachable via composition (3h)
  • Task 1.1.2.5: Stage 2 generator: emit EF Core HasOne/HasMany with cascade delete (5h)

Story 1.1.3: GenerateEfCore

public abstract record GenerateEfCoreStory : Story<AggregateDefinitionFeature>
{
    public override string Title => "Generate EF Core persistence from aggregate model";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult DbContextIncludesAllAggregates(
        string[] aggregateNames);

    public abstract AcceptanceCriterionResult RepositoryInterfaceGenerated(
        string aggregateName);

    public abstract AcceptanceCriterionResult RepositoryImplementationGenerated(
        string aggregateName);

    public abstract AcceptanceCriterionResult MigrationScriptGenerable(
        string aggregateName);
}

Tasks:

  • Task 1.1.3.1: Stage 2 generator: emit DbContext with DbSet<T> per aggregate (4h)
  • Task 1.1.3.2: Stage 2 generator: emit IEntityTypeConfiguration<T> per entity (6h)
  • Task 1.1.3.3: Stage 2 generator: emit IRepository<T> interface (2h)
  • Task 1.1.3.4: Stage 2 generator: emit repository implementation with SaveChangesAsync (4h)
  • Task 1.1.3.5: Integration with dotnet ef migrations add (3h)

Feature 1.2: CqrsGeneration

public abstract record CqrsGenerationFeature : Feature<DomainModelingEpic>
{
    public override string Title => "CQRS command, event, query, and saga generation";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult CommandTargetsAggregate(
        string commandName, string aggregateName);

    public abstract AcceptanceCriterionResult EventSourcedFromAggregate(
        string eventName, string aggregateName);

    public abstract AcceptanceCriterionResult HandlerGeneratedPerCommand(
        string commandName);

    public abstract AcceptanceCriterionResult SagaOrchestratesMultipleAggregates(
        string sagaName, string[] aggregateNames);

    public abstract AcceptanceCriterionResult InvariantsCalledAfterCommand(
        string commandName, string aggregateName);
}

Stories for CqrsGeneration:

  • Story 1.2.1: DefineCommand -- [Command] attribute with target aggregate, generated handler that calls EnsureInvariants() after mutation
  • Story 1.2.2: DefineDomainEvent -- [DomainEvent] attribute with source aggregate, published after successful command
  • Story 1.2.3: DefineQuery -- [Query] attribute with projection from aggregate
  • Story 1.2.4: DefineSaga -- [Saga] attribute orchestrating multi-aggregate processes with compensation

Feature 1.3: BuilderGeneration

public abstract record BuilderGenerationFeature : Feature<DomainModelingEpic>
{
    public override string Title => "Fluent builder generation for aggregates and entities";
    public override RequirementPriority Priority => RequirementPriority.Medium;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult FluentBuilderGeneratedPerAggregate(
        string aggregateName);

    public abstract AcceptanceCriterionResult BuilderEnforcesRequiredProperties(
        string aggregateName, string[] requiredProperties);

    public abstract AcceptanceCriterionResult BuilderProducesImmutableInstance(
        string aggregateName);

    public abstract AcceptanceCriterionResult BuilderCallsEnsureInvariants(
        string aggregateName);
}

Feature 1.4: InvariantEnforcement

public abstract record InvariantEnforcementFeature : Feature<DomainModelingEpic>
{
    public override string Title => "Aggregate invariant enforcement via [Invariant] methods";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";

    /// <summary>
    /// Methods marked [Invariant] return Result and are discovered by the generator.
    /// </summary>
    public abstract AcceptanceCriterionResult InvariantMethodsReturnResult(
        string aggregateName, string[] invariantMethodNames);

    /// <summary>
    /// Generator emits EnsureInvariants() that calls all [Invariant] methods
    /// and aggregates their Results.
    /// </summary>
    public abstract AcceptanceCriterionResult EnsureInvariantsAggregatesResults(
        string aggregateName, int expectedInvariantCount);

    /// <summary>
    /// EnsureInvariants() is injected into every generated command handler
    /// between state mutation and SaveChanges.
    /// </summary>
    public abstract AcceptanceCriterionResult InvariantsCalledInCommandHandlers(
        string commandName);

    /// <summary>
    /// Analyzer DDD100 warns if an aggregate has zero [Invariant] methods.
    /// </summary>
    public abstract AcceptanceCriterionResult AnalyzerWarnsOnMissingInvariants(
        string aggregateName);
}

Stories for InvariantEnforcement:

  • Story 1.4.1: DefineInvariant -- [Invariant("description")] on private Result Method() (2h)
  • Story 1.4.2: GenerateEnsureInvariants -- Source generator discovers [Invariant] methods, emits EnsureInvariants() (4h)
  • Story 1.4.3: InjectIntoHandlers -- Generated command handlers call EnsureInvariants() after mutation (3h)
  • Story 1.4.4: DDD100Analyzer -- Roslyn analyzer warns on aggregates without invariants (2h)

Feature 1.5: ValidationGeneration

public abstract record ValidationGenerationFeature : Feature<DomainModelingEpic>
{
    public override string Title => "Property validation and cross-entity rule generation";
    public override RequirementPriority Priority => RequirementPriority.Medium;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult RequiredPropertiesValidated(
        string entityName, string[] requiredPropertyNames);

    public abstract AcceptanceCriterionResult StringLengthConstraintsEnforced(
        string entityName, string propertyName, int maxLength);

    public abstract AcceptanceCriterionResult CustomValidationRulesSupported(
        string entityName, string validationExpression);
}

Epic 2: ContentManagementEpic

public abstract record ContentManagementEpic : Epic
{
    public override string Title => "Content Parts, Blocks, and StreamFields";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";
}

Feature 2.1: ContentParts

public abstract record ContentPartsFeature : Feature<ContentManagementEpic>
{
    public override string Title => "Composable content parts (horizontal composition)";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult PartCanBeDefinedWithAttributes(
        string partName, string[] fieldNames);

    public abstract AcceptanceCriterionResult PartCanBeAttachedToEntity(
        string entityName, string partName);

    public abstract AcceptanceCriterionResult MultiplePartsOnSameEntity(
        string entityName, string[] partNames);

    public abstract AcceptanceCriterionResult PartFieldsIncludedInAdminForm(
        string partName, string[] fieldNames);

    public abstract AcceptanceCriterionResult PartVersioningSupported(
        string partName);
}

Stories:

  • Story 2.1.1: DefineContentPart -- [ContentPart] with [PartField] attributes
  • Story 2.1.2: AttachPartToEntity -- [HasPart(typeof(Routable))] on aggregate
  • Story 2.1.3: BuiltInParts -- Routable, Seoable, Taggable, Auditable, Versionable
  • Story 2.1.4: PartPersistence -- EF Core owned types or JSON columns for part data

Feature 2.2: ContentBlocks

public abstract record ContentBlocksFeature : Feature<ContentManagementEpic>
{
    public override string Title => "Structured content blocks (vertical composition)";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult StructBlockCanBeDefined(
        string blockName, string[] fieldNames);

    public abstract AcceptanceCriterionResult ListBlockSupportsMultipleItems(
        string blockName, string childBlockType);

    public abstract AcceptanceCriterionResult BlocksCanBeNested(
        string parentBlockName, string childBlockName);

    public abstract AcceptanceCriterionResult BlocksSerializeToJson(
        string blockName);

    public abstract AcceptanceCriterionResult BlocksRenderInBlazer(
        string blockName);
}

Feature 2.3: StreamFields

public abstract record StreamFieldsFeature : Feature<ContentManagementEpic>
{
    public override string Title => "StreamField composable block sequences";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult StreamFieldAcceptsMultipleBlockTypes(
        string fieldName, string[] allowedBlockTypes);

    public abstract AcceptanceCriterionResult BlockOrderIsPreserved(
        string fieldName, string[] blockTypesInOrder);

    public abstract AcceptanceCriterionResult StreamFieldSerializesToJsonArray(
        string fieldName);

    public abstract AcceptanceCriterionResult StreamFieldRenderableInBlazor(
        string fieldName);
}

Epic 3: AdminGenerationEpic

public abstract record AdminGenerationEpic : Epic
{
    public override string Title => "Auto-Generated Admin Interfaces";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";
}

Feature 3.1: ListGeneration

public abstract record ListGenerationFeature : Feature<AdminGenerationEpic>
{
    public override string Title => "Paginated list views with filtering and sorting";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult ListViewGeneratedPerAdminModule(
        string moduleName);

    public abstract AcceptanceCriterionResult PaginationWorks(
        string moduleName, int pageSize);

    public abstract AcceptanceCriterionResult FiltersApplyToColumns(
        string moduleName, string[] filterableColumns);

    public abstract AcceptanceCriterionResult SortingWorksOnAllColumns(
        string moduleName);
}

Feature 3.2: FormGeneration

public abstract record FormGenerationFeature : Feature<AdminGenerationEpic>
{
    public override string Title => "Create/edit forms with validation and nested entities";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult FormIncludesAllProperties(
        string aggregateName, string[] propertyNames);

    public abstract AcceptanceCriterionResult ValidationErrorsDisplayed(
        string aggregateName, string invalidPropertyName);

    public abstract AcceptanceCriterionResult NestedEntityEditing(
        string aggregateName, string nestedEntityName);

    public abstract AcceptanceCriterionResult ContentPartFieldsInForm(
        string aggregateName, string[] attachedPartNames);
}

Feature 3.3: BatchActions

public abstract record BatchActionsFeature : Feature<AdminGenerationEpic>
{
    public override string Title => "Bulk operations with confirmation dialogs";
    public override RequirementPriority Priority => RequirementPriority.Medium;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult BulkDeleteWithConfirmation(
        string moduleName, int selectedCount);

    public abstract AcceptanceCriterionResult BulkPublishAction(
        string moduleName);

    public abstract AcceptanceCriterionResult CustomBulkActions(
        string moduleName, string actionName);
}

Epic 4: PageCompositionEpic

public abstract record PageCompositionEpic : Epic
{
    public override string Title => "Dynamic Page Composition & Routing";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";
}

Feature 4.1: PageTree

public abstract record PageTreeFeature : Feature<PageCompositionEpic>
{
    public override string Title => "Hierarchical page tree with slugs and materialized paths";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult PagesFormHierarchy(
        string[] pagePaths);

    public abstract AcceptanceCriterionResult SlugsAutoGenerated(
        string pageTitle, string expectedSlug);

    public abstract AcceptanceCriterionResult MaterializedPathsComputed(
        string pagePath, string expectedMaterializedPath);

    public abstract AcceptanceCriterionResult PageMovingUpdatesDescendants(
        string movedPagePath, string newParentPath);
}

Feature 4.2: WidgetSystem

public abstract record WidgetSystemFeature : Feature<PageCompositionEpic>
{
    public override string Title => "Composable widgets placed in zones on layouts";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult WidgetDefinedWithAttribute(
        string widgetName, string[] configProperties);

    public abstract AcceptanceCriterionResult WidgetPlaceableInZone(
        string widgetName, string zoneName);

    public abstract AcceptanceCriterionResult WidgetRendersInBlazorWasm(
        string widgetName);

    public abstract AcceptanceCriterionResult WidgetConfigEditableInAdmin(
        string widgetName);
}

Feature 4.3: DynamicRouting

public abstract record DynamicRoutingFeature : Feature<PageCompositionEpic>
{
    public override string Title => "URL resolution from page tree with SEO support";
    public override RequirementPriority Priority => RequirementPriority.Medium;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult UrlResolvesToPage(
        string url, string expectedPageTitle);

    public abstract AcceptanceCriterionResult BoundEntityUrlsWork(
        string entityType, string entitySlug, string expectedUrl);

    public abstract AcceptanceCriterionResult SeoMetadataGenerated(
        string pageTitle, string expectedCanonicalUrl);
}

Epic 5: WorkflowEpic

public abstract record WorkflowEpic : Epic
{
    public override string Title => "Editorial Workflow Pipelines";
    public override RequirementPriority Priority => RequirementPriority.Medium;
    public override string Owner => "framework-team";
}

Feature 5.1: StateMachine

public abstract record StateMachineFeature : Feature<WorkflowEpic>
{
    public override string Title => "State machine with stages, transitions, and guards";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult StagesDefinedViaAttributes(
        string workflowName, string[] stageNames);

    public abstract AcceptanceCriterionResult TransitionsValidated(
        string fromStage, string toStage, bool isValid);

    public abstract AcceptanceCriterionResult GuardsPreventInvalidTransitions(
        string transitionName, string guardCondition);

    public abstract AcceptanceCriterionResult DomainEventsOnTransition(
        string transitionName, string expectedEventType);
}

Feature 5.2: LocaleTracking

public abstract record LocaleTrackingFeature : Feature<WorkflowEpic>
{
    public override string Title => "Per-locale progress tracking";
    public override RequirementPriority Priority => RequirementPriority.Medium;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult EachLocaleTrackedIndependently(
        string entityId, string[] locales);

    public abstract AcceptanceCriterionResult TranslationGateBlocksUntilAllLocalesDone(
        string entityId, string[] completedLocales, string[] pendingLocales);
}

Feature 5.3: ScheduledPublishing

public abstract record ScheduledPublishingFeature : Feature<WorkflowEpic>
{
    public override string Title => "Timed transitions (scheduled publish/unpublish)";
    public override RequirementPriority Priority => RequirementPriority.Low;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult ScheduledTransitionExecutesAtTime(
        string entityId, string targetStage, DateTimeOffset scheduledTime);

    public abstract AcceptanceCriterionResult ScheduledTransitionCancellable(
        string entityId);
}

Epic 6: RequirementsTrackingEpic

public abstract record RequirementsTrackingEpic : Epic
{
    public override string Title => "Type-Safe Requirements Chain & Quality Enforcement";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";
}

Feature 6.1: TypeSafeChain

public abstract record TypeSafeChainFeature : Feature<RequirementsTrackingEpic>
{
    public override string Title => "Requirements as types with compiler-enforced chain";
    public override RequirementPriority Priority => RequirementPriority.Critical;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult RequirementsAreAbstractRecords(
        string featureTypeName);

    public abstract AcceptanceCriterionResult AcceptanceCriteriaAreAbstractMethods(
        string featureTypeName, string[] acMethodNames);

    public abstract AcceptanceCriterionResult SpecificationsAreInterfaces(
        string specInterfaceName, string linkedFeatureTypeName);

    public abstract AcceptanceCriterionResult ImplementationMustSatisfySpec(
        string implClassName, string specInterfaceName);

    public abstract AcceptanceCriterionResult TestsLinkViaTypeOfAndNameOf(
        string testClassName, string featureTypeName, string acMethodName);

    public abstract AcceptanceCriterionResult ForRequirementAttributeNavigable(
        string targetTypeName, string featureTypeName);
}

Feature 6.2: RoslynAnalyzers

public abstract record RoslynAnalyzersFeature : Feature<RequirementsTrackingEpic>
{
    public override string Title => "Compile-time analyzers enforcing the chain (REQ1xx-REQ3xx)";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult REQ1xxDetectsMissingSpecifications(
        string featureTypeName);

    public abstract AcceptanceCriterionResult REQ2xxDetectsMissingImplementations(
        string specInterfaceName);

    public abstract AcceptanceCriterionResult REQ3xxDetectsMissingTests(
        string featureTypeName, string untestedAcMethod);

    public abstract AcceptanceCriterionResult SeverityConfigurableViaEditorconfig(
        string diagnosticId, string severityLevel);
}

Feature 6.3: QualityGatesIntegration

/// <summary>
/// The CMF build uses the pre-existing `dotnet quality-gates` tool
/// to enforce pass rate, coverage, and fuzz testing thresholds.
/// This feature is about wiring the tool into the CMF's CI pipeline,
/// not building the tool itself.
/// </summary>
public abstract record QualityGatesIntegrationFeature : Feature<RequirementsTrackingEpic>
{
    public override string Title => "Quality gates integration (dotnet quality-gates)";
    public override RequirementPriority Priority => RequirementPriority.High;
    public override string Owner => "framework-team";

    public abstract AcceptanceCriterionResult PassRateEnforced(
        decimal minimumPassRate);

    public abstract AcceptanceCriterionResult CodeCoverageEnforced(
        decimal minimumCoveragePercent);

    public abstract AcceptanceCriterionResult FuzzTestingEnabled(
        string featureTypeName, int inputCount);

    public abstract AcceptanceCriterionResult CiPipelineIntegrated(
        string pipelineStage);

    public abstract AcceptanceCriterionResult FlakynessDetected(
        string testMethodName, int retryCount);
}

Catalog Summary

Epic Features Stories Tasks (est.) Priority
DomainModelingEpic 5 15 45 Critical
ContentManagementEpic 3 10 30 High
AdminGenerationEpic 3 8 20 High
PageCompositionEpic 3 9 25 High
WorkflowEpic 3 7 15 Medium
RequirementsTrackingEpic 3 8 20 Critical
Total 20 57 155

What This Catalog Validates

By expressing 20 features with ~100 acceptance criteria methods across 6 epics, we validate that:

  1. The hierarchy works: Feature<TParent> with generic constraints correctly models the Epic → Feature → Story relationship
  2. AC methods are expressive: Every acceptance criterion has typed parameters that document what it checks
  3. The notation scales: 155 tasks across 6 epics is a realistic product backlog, and the DSL handles it without becoming unwieldy
  4. Requirements are navigable: In an IDE, typeof(InvariantEnforcementFeature) → Ctrl+Click → jumps to the feature. nameof(InvariantEnforcementFeature.InvariantsCalledInCommandHandlers) → jumps to the specific AC
  5. The chain is enforceable: Each feature type in this catalog will eventually need a specification interface, a domain implementation, and tests -- and the analyzers will track that

What It Does NOT Validate

  • Runtime behavior: The AC methods are abstract -- nobody calls them yet
  • Self-hosting: This catalog is written before the DSL exists. It's design validation, not bootstrapping
  • Completeness: A real product would have more features (search, media, i18n, permissions). This catalog covers the core