Skip to content

Cephalon.Behaviors.SourceGen

Maturity: M1 · Ownership: cephalon-managed · Family: behaviors · See audit, matrix.

Cephalon.Behaviors.SourceGen is the M5 compile-time tooling layer of the Adaptive Behavior Topology (ABT). It provides a Roslyn incremental source generator and diagnostic analyzer that validate behavior authoring conventions at build time and produce a compile-time-known registration hint file.

  • BehaviorSourceGenerator — combined Roslyn IIncrementalGenerator + diagnostic analyzer
    • Uses ForAttributeWithMetadataName for efficient incremental processing
    • Emits BehaviorRegistrationHints.g.cs listing all discovered [AppBehavior] IDs
    • Emits BehaviorAutoRegistration.g.cs for zero-reflection DI/type registration plus pre-built topology descriptors when compile-time extraction succeeds
    • Emits source-generated metadata-only REST profile hints through GetRestProfiles() when behaviors declare valid BehaviorRestProfileAttribute metadata
    • Extracts compile-time topology from ConfigureTopology(...) for pattern, transports, feature flags, and literal WithApiSurface(...) overrides
  • Reports ABT0010–ABT0027 diagnostics on invalid behavior declarations, metadata-only REST profile hints, malformed REST profile placeholder syntax, explicit REST binding metadata, and invalid preserved implicit query-fallback authoring before GetRestProfiles() is generated
CodeSeverityDescription
ABT0010Error[AppBehavior] class does not implement IAppBehavior<TIn, TOut>
ABT0011Error[AppBehavior] ID is null or empty
ABT0012Error[AppBehavior] class is abstract
ABT0013Error[AppBehavior] class is static
ABT0014ErrorREST is declared in behavior topology instead of a module-owned REST surface
ABT0015Error[BehaviorRestProfile] does not select a supported REST method
ABT0016Error[BehaviorRestProfile] uses an empty relative pattern
ABT0017Error[BehaviorRestProfile] uses a non-positive ApiVersionMajor
ABT0018Error[BehaviorRestProfile] uses a relative pattern that does not start with /
ABT0019Error[BehaviorRestBinding] does not name a target input property
ABT0020Error[BehaviorRestBinding] does not select a supported binding source
ABT0021Error[BehaviorRestBinding] metadata is declared for a scalar input or an input without public readable properties
ABT0022Error[BehaviorRestBinding] targets an input property that does not exist
ABT0023Error[BehaviorRestBinding] declares the same input property more than once
ABT0024Error[BehaviorRestBinding] uses Body on a REST method that does not accept a request body
ABT0025Error[BehaviorRestBinding] uses a route placeholder that is not declared in [BehaviorRestProfile(...)]
ABT0026Error[BehaviorRestProfile] uses malformed route placeholder syntax such as unbalanced or empty {...} segments
ABT0027Error[BehaviorRestProfile(PreserveImplicitQueryFallback = true)] is declared without any explicit [BehaviorRestBinding] metadata

BehaviorRegistrationHints.g.cs and BehaviorAutoRegistration.g.cs are emitted into the consuming project at build time:

// <auto-generated/>
// Generated by Cephalon.Behaviors.SourceGen — do not edit.
namespace Cephalon.Behaviors.Generated;
internal static class BehaviorRegistrationHints
{
internal static readonly IReadOnlyList<string> BehaviorIds =
[
"order.place",
"order.get",
];
}

When the generator can statically understand ConfigureTopology(...), it also emits zero-reflection registration and topology data, including literal WithApiSurface(...) overrides:

internal static class BehaviorAutoRegistration
{
internal static IReadOnlyList<BehaviorTopologyDescriptor> GetTopologyDescriptors()
{
return
[
new BehaviorTopologyDescriptor(
"catalog.lookup",
"cqrs",
new[] { "http.jsonrpc", "http.sse" },
apiSurface: new BehaviorApiSurfaceDescriptor("catalog/items", "lookup"))
];
}
}

When a behavior also declares a valid metadata-only REST profile, the generated registration type now emits future-facing REST profile hints without publishing any public REST routes:

internal static class BehaviorAutoRegistration
{
internal static IReadOnlyList<BehaviorRestProfileDescriptor> GetRestProfiles()
{
return
[
new BehaviorRestProfileDescriptor(
"catalog.lookup",
BehaviorRestMethod.Get,
"/{itemId}",
2,
[
new BehaviorRestBindingDescriptor(
"ItemId",
BehaviorRestBindingSource.Route,
"itemId")
])
];
}
}

For REST profile methods and binding sources, the generator now validates against the stable wire-name vocabularies instead of hardcoding enum member names: BehaviorRestMethod uses get, post, put, patch, and delete, while BehaviorRestBindingSource uses route, query, header, and body. Generated GetRestProfiles() hints still emit the resolved enum member names, so future package versions can rename those members without breaking valid metadata as long as the stable wire-name contracts stay intact.

The generator is automatically applied when Cephalon.Behaviors is referenced. No additional setup required. The Cephalon.Behaviors package references Cephalon.Behaviors.SourceGen as an analyzer, so the generator and diagnostics activate for any project that references Cephalon.Behaviors.

Compile-time topology extraction intentionally stays conservative. Literal WithApiSurface(...) arguments are supported, while more complex expressions fall back to runtime topology resolution so the generated surface stays truthful. Public REST is module-owned and therefore sits outside the behavior source-generator topology model; ABT0014 now rejects http.rest and ViaHttpRest(...) so authors map REST in a module with RestBehaviorModuleBase.ConfigureRestBehaviors(...), or with manual MapBehaviorRestGroup(...) wiring when they intentionally stay on the low-level REST module path. BehaviorRestProfileAttribute plus optional repeated BehaviorRestBindingAttribute declarations are now the shipped metadata-only bridge for future low-ceremony REST: the generator validates the core profile shape plus explicit binding metadata, preserved implicit query-fallback authoring, and emits GetRestProfiles() hints, including explicit binding descriptors and preserveImplicitQueryFallback: true when present, plus GetRestProfileBehaviorTypes() hints for the generated module-owned shorthand path, but that metadata still does not publish public REST routes by itself and does not override host OpenAPI document publication policy. Cephalon.Behaviors.Http now consumes those hints through the explicit module-owned MapProfile<TBehavior>(), MapGeneratedProfiles(...), and IRestBehaviorModuleBuilder.MapGeneratedProfileGroups(...) shorthands, preferring the generated hints and falling back only to the explicitly targeted behavior type’s attribute or to a bounded scan of the explicit owning module assembly when the current assembly lacks generated type hints. Generated REST profile and binding hints now resolve their enum member names from the actual attribute arguments instead of assuming fixed numeric ordinals. The build now rejects unsupported binding sources, malformed route placeholder syntax, missing or duplicate input-property targets, scalar-input misuse, body-binding verb restrictions, route-placeholder mismatches, and preserved implicit-query fallback without any explicit bindings earlier, while Cephalon.Behaviors.Http still re-checks the same contract when the runtime falls back to direct attribute resolution for MapProfile<TBehavior>() or to the bounded owner-assembly scan used by MapGeneratedProfiles(...) and MapGeneratedProfileGroups(...). Runtime normalization still lets ASP.NET Core route parsing stay authoritative for the final route-shape truth even after the generator moves the most common placeholder-shape mistakes and preserved- fallback authoring errors to compile time. Likewise, explicit module ownership through IBehaviorOwnerModule, BehaviorModuleBase, or RestBehaviorModuleBase remains a runtime-composition concern rather than a source-generated topology concern: the generator still focuses on behavior shape and topology, while the engine owns which module claims each behavior. When a behavior has no compile-time topology but does declare exactly one allowed pattern plus one or more allowed transports, the runtime synthesizes the attribute-only baseline descriptor from those attributes. If multiple allowed patterns are declared, runtime resolution still fails fast until another topology source selects one explicitly.

Status: ✅ Shipped — targeted source-generator tests 29/29

  • Cephalon.Behaviors — dispatcher, catalog, resolver (M1)
  • Cephalon.Behaviors.Http — HTTP transport bindings (M2)
  • Cephalon.Behaviors.Messaging — messaging transport bindings (M3)
  • Cephalon.Behaviors.Patterns — pattern execution strategies (M4)