Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>

<!--
Compare every packable assembly with the latest released baseline, 0.15.0. The 0.1.0 core and
Compare every packable assembly with the latest released baseline, 0.16.0. The 0.1.0 core and
0.5.0 Vogen ancestry remain covered by unchanged legacy consumer binaries. Package validation protects
the current public floor while those binaries prove that older compatible entry points still execute.
-->
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>0.15.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>0.16.0</PackageValidationBaselineVersion>
<!-- Compatible additions are intentional; removals and signature changes remain errors. -->
<EnableStrictModeForBaselineValidation>false</EnableStrictModeForBaselineValidation>

Expand Down
51 changes: 39 additions & 12 deletions Documentation/guides/build-source-adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ Keep runtime packages for the framework you analyze out of the adapter whenever
Reference one version across all directly referenced Screenplay Generation packages.

:::note
`v0.15.0` is the current public release and package-validation baseline. Exact method signatures, complete bounded source-value extraction, and authoritative invocation and assignment enumeration are included in that lockstep package set. The described-adapter execution boundary is additive on `main`.
`v0.16.0` is the current public release and package-validation baseline. Described adapters, atomic admission, deterministic runner snapshots, and final fact dispositions are included in that lockstep package set. Granular type-use derivation is additive on `main`.
:::

| Capability | Released `0.15.0` | Current `main` |
| Capability | Released `0.16.0` | Current `main` |
| --- | ---: | ---: |
| Adapter, context, neutral fact, evidence, and diagnostic contracts | Yes | Yes |
| Stable source identity, fixed source snapshots, and strict placement | Yes | Yes |
Expand All @@ -52,11 +52,14 @@ Reference one version across all directly referenced Screenplay Generation packa
| Overload-safe subjects, alternate source owners, and flat compatibility placement | Yes | Yes |
| Authoritative invocation and assignment enumeration | Yes | Yes |
| Legacy `IDotNetScreenplayAdapter` | Yes | Yes |
| Descriptors, structured probes, and atomic public admission | No | Yes |
| Explicit modern/legacy registration and deterministic .NET runner | No | Yes |
| Immutable adapter-run snapshots and `Generate(snapshot)` | No | Yes |
| Per-fact generation dispositions | No | Yes |
| Vogen modern descriptor/probe with legacy contribution parity | No | Yes |
| Descriptors, structured probes, and atomic public admission | Yes | Yes |
| Explicit modern/legacy registration and deterministic .NET runner | Yes | Yes |
| Immutable adapter-run snapshots and `Generate(snapshot)` | Yes | Yes |
| Per-fact generation dispositions | Yes | Yes |
| Vogen modern descriptor/probe with legacy contribution parity | Yes | Yes |
| Granular artifact/member/type-use/binding/role facts | No | Yes |
| Fixed-snapshot derivation rule and input/evidence lineage | No | Yes |
| Exact nested .NET type-use fact emission | No | Yes |

## Implement the adapter contract

Expand Down Expand Up @@ -92,7 +95,12 @@ public sealed class AcmeScreenplayAdapter :
AdapterHostCapability.SemanticAnalysis
],
RequiredApiCapabilities = [_commandDeclarationApi],
EmittedFactCapabilities = [GenerationFactCapability.Artifact]
EmittedFactCapabilities =
[
GenerationFactCapability.Artifact,
GenerationFactCapability.ArtifactMemberDeclaration,
GenerationFactCapability.ArtifactMemberTypeUse
]
};

// Legacy compatibility surface.
Expand Down Expand Up @@ -177,10 +185,11 @@ public sealed class AcmeScreenplayAdapter :
Key = key,
Name = type.Name,
File = evidence.Source?.Path,
Properties = DotNetTypeShapes.PropertiesOf(type, context)
Properties = DotNetTypeShapes.PropertiesOf(type)
},
Evidence = evidence
});
facts.AddRange(DotNetTypeUseFacts.Emit(type, key, context, evidence));
}
}

Expand Down Expand Up @@ -332,15 +341,23 @@ Fact IDs must be globally stable and unique. Prefix them with the adapter and se

Use the smallest fact vocabulary that says what the source proves:

- `ArtifactFact` — a command, event, read model, projection, reaction, message, handler, concept, or another supported role;
- `ArtifactFact` — the compatibility aggregate for a command, event, read model, projection, reaction, message, handler, concept, or another supported role;
- `ArtifactDeclarationFact` and `ArtifactMemberDeclarationFact` — independent artifact metadata and one ordered member declaration without repeating a complete property list;
- `ArtifactMemberTypeUseFact` — one exact use-site type shape and observed source subject;
- `TypeUseBindingFact` — an exact member-to-artifact binding, normally produced by fixed-snapshot derivation;
- `ArtifactMemberRoleFact` — an explicitly established typed identifier or event-source-identifier role;
- `ArtifactPlacementFact` — module, feature, slice, and independently established slice kind;
- `RelationshipFact` — handles, reads, produces, consumes, builds, returns, cascades, publishes, starts or appends streams, or document persistence;
- concept representation, attribute, and validation facts;
- specification scenario, step, and typed value facts.

Do not overload a nearby role. A published message is not a persisted event. A document is not an event-built read model unless source evidence proves the projection. A response is not a cascade.

Use `TypeReferenceDefinition.Subject` when a property targets an exact discovered type or concept. `DotNetTypeShapes.PropertiesOf(type, context)` and `TypeReferenceFor(type, context)` preserve project-qualified type subjects.
Keep compatibility aggregate properties unbound with `DotNetTypeShapes.PropertiesOf(type)` when another adapter may declare their target concepts. Append `DotNetTypeUseFacts.Emit(type, artifact, context, evidence)` so each member independently records declaration order, exact use-site shape, and the terminal project-qualified source subject. Fixed-snapshot derivation then emits a granular binding without rewriting the aggregate.

`DotNetTypeShapes.TypeUseFor(type, context)` orders shape nodes from the outermost wrapper to the terminal `Named` node. This distinguishes `Collection(Optional(Named))` from `Optional(Collection(Named))` and preserves nested collections. The current Screenplay grammar lowers only the shapes it can express exactly; unsupported distinctions remain diagnosed rather than flattened.

Pass a `roleFor` callback to `DotNetTypeUseFacts.Emit(...)` only when source-framework semantics establish `ArtifactMemberRoleKind.Identifier` or `EventSourceIdentifier`. Never infer either role from a property name or primitive type. `EventSourceIdentifier` lowers through the existing identifier syntax; the distinct ordinary `Identifier` role remains provenance until Screenplay has separate syntax for it. A framework that already knows the exact target inside one adapter may continue setting `TypeReferenceDefinition.Subject` directly with `PropertiesOf(type, context)` or `TypeReferenceFor(type, context)`.

## Nominate declared concepts

Expand Down Expand Up @@ -524,7 +541,17 @@ var legacy = DotNetAdapterRunner.Run(

The modern descriptor has category `Concepts`, source language `CSharp`, requires authored source, stable source locations, semantic analysis, and exact Vogen declaration API evidence, and declares its concept fact families. Its probe distinguishes no declarations, safely applicable declarations, and unsafe mappings. Run the modern and legacy registrations separately: both use the `vogen` identity, so placing both in one roster is a deliberate duplicate rejection. When each path is safely applicable, their contribution facts and diagnostics are identical.

This execution snapshot is not a history model. It does not implement issue #19 adapter or fact lineage. It also has no issue #24 serializer or stable fingerprints; keep snapshots in process and compare canonical generated bytes when determinism matters.
### Derive facts from one fixed admitted snapshot

`GenerationFactDerivation.Derive(...)` runs the closed built-in rule set once over `AdapterRunSnapshot.Facts`. Every rule sees the same deeply frozen base array. A rule never consumes another rule's output, inspects adapter registrations or instances, or reopens source-language state.

The type-use binding rule joins an `ArtifactMemberTypeUseFact` to an exact declared `ArtifactKey`, including its role and subject. It does not join by display name and does not replace the owning artifact's complete property list. Its derived `TypeUseBindingFact` remains separate from the admitted base facts under `AdapterRunSnapshot.Derivation`; the resolved compatibility reference retains the role in `TypeReferenceDefinition.TargetArtifactKind` so a same-subject non-concept can never be substituted as a concept.

Each derived `GenerationFactRecord` carries `GenerationFactLineage`: the stable derivation rule identity and version, canonical input `FactId` references, and complete input evidence. `GenerationDerivationRuleRecord` records the fixed inputs, outputs, and diagnostics for that rule execution. Directly invoking derivation leaves fact dispositions unknown because disposition is a later generation decision. `Generate(snapshot, options)` attaches the derivation result, propagates its diagnostics, resolves admitted and derived granular facets together, and applies an exact member binding as an overlay without publishing a replacement aggregate fact.

Exact subjects can come from any source frontend. A C# member type use can bind a declaration contributed by another adapter, while a source-independent or non-.NET adapter can contribute the same neutral contracts without Roslyn or Screenplay-layout dependencies. Missing, malformed, ambiguous, conflicting, or currently unrepresentable member evidence omits the affected artifact instead of lowering a flattened legacy fallback.

The execution and derivation snapshots are not a history model. They have no issue #24 serializer or stable fingerprints; keep them in process and compare canonical generated bytes when determinism matters.

Adapters never call the runner, resolver, lowerer, printer, or compiler themselves. Adopt a newly required API in this order:

Expand Down
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,30 @@ See [Build a .NET source adapter](Documentation/guides/build-source-adapter.md)

## Public baseline and current main

`0.15.0` is the current public lockstep release and package-validation baseline. The adapter execution boundary is additive on `main`.
`0.16.0` is the current public lockstep release and package-validation baseline. Granular type-use derivation is additive on `main`.

| Capability | Released `0.15.0` | Current `main` |
| Capability | Released `0.16.0` | Current `main` |
| --- | ---: | ---: |
| Adapter, context, neutral fact, evidence, and diagnostic contracts | Yes | Yes |
| Stable source identity, fixed source snapshots, and strict placement | Yes | Yes |
| Exact method signatures and bounded scalar, payload, and collection extraction | Yes | Yes |
| Authoritative invocation and assignment enumeration | Yes | Yes |
| Legacy `IDotNetScreenplayAdapter` | Yes | Yes |
| Descriptors, structured probes, and atomic public admission | No | Yes |
| Explicit modern/legacy registration and deterministic .NET runner | No | Yes |
| Immutable adapter-run snapshots and `Generate(snapshot)` | No | Yes |
| Per-fact generation dispositions | No | Yes |
| Vogen modern descriptor/probe with legacy contribution parity | No | Yes |
| Descriptors, structured probes, and atomic public admission | Yes | Yes |
| Explicit modern/legacy registration and deterministic .NET runner | Yes | Yes |
| Immutable adapter-run snapshots and `Generate(snapshot)` | Yes | Yes |
| Per-fact generation dispositions | Yes | Yes |
| Vogen modern descriptor/probe with legacy contribution parity | Yes | Yes |
| Granular artifact/member/type-use/binding/role facts | No | Yes |
| Fixed-snapshot derivation rule and input/evidence lineage | No | Yes |
| Exact nested .NET type-use fact emission | No | Yes |

## Architecture

```text
source adapter
-> typed facts and evidence
-> admitted typed facts and evidence
-> fixed-snapshot derivation and lineage
-> resolved application graph
-> lowerable Screenplay model
-> Screenplay AST
Expand All @@ -62,7 +66,7 @@ Duplicate adapter IDs are rejected before probe or analysis. Invalid descriptors

A modern `SourceIndependent` adapter with no host requirements can run against an empty .NET context. A source adapter that declares `StableSourceLocations` requires every authored tree to have an authoritative `DotNetProjectSourceContext` mapping, and every located probe, fact, and diagnostic must use that mapping. The modern Vogen path requires stable locations; its legacy interface remains available for compatibility.

Pass the frozen snapshot to `ScreenplayDefinitionGenerator.Generate(snapshot, options)` to preserve runner diagnostics and receive final fact dispositions: `Lowered`, `ProvenanceOnly`, `OmittedWithDiagnostic`, or `Conflicted`. This snapshot records one run only. It does not add issue #19 adapter/fact lineage, and it does not add issue #24 serialization or fingerprints.
Pass the frozen snapshot to `ScreenplayDefinitionGenerator.Generate(snapshot, options)` to preserve runner diagnostics and receive final fact dispositions: `Lowered`, `ProvenanceOnly`, `OmittedWithDiagnostic`, or `Conflicted`. Generation runs one closed derivation pass over the fixed admitted base facts, attaches stable rule/input/evidence lineage under `AdapterRunSnapshot.Derivation`, and applies exact member bindings as granular overlays. The snapshot records one run only; it does not add issue #24 serialization or fingerprints.

### Adapter syntax robustness

Expand Down Expand Up @@ -139,7 +143,15 @@ Generated members never provide primary evidence. The adapter never infers ident

Adapters can contribute `ArtifactKind.Concept` together with independently proven `ConceptRepresentationFact`, `ConceptAttributeFact`, and `ConceptValidationRuleFact` assertions. Primitive/enumeration representations, named attributes, and named external predicate rules resolve deterministically and lower to top-level Screenplay concepts without module placement.

`TypeReferenceDefinition.Subject` binds an artifact property to the exact concept subject rather than a simple display name. Missing, conflicting, unsupported, or same-named concept definitions produce stable diagnostics; generation never falls back to `String`.
`TypeReferenceDefinition.Subject` continues to bind an aggregate artifact property directly when one adapter proves the exact target. Missing, conflicting, unsupported, or same-named concept definitions produce stable diagnostics; generation never falls back to `String`.

## Granular type uses and derivation

Adapters that establish different facets independently can emit `ArtifactDeclarationFact`, `ArtifactMemberDeclarationFact`, `ArtifactMemberTypeUseFact`, and `ArtifactMemberRoleFact` without repeating a complete `ArtifactFact`. `TypeUseDefinition.Shape` orders optional and collection wrappers from outermost to the terminal `Named` node, so optional elements, optional collections, and nested collections remain distinct.

Generation runs the built-in `cratis.screenplay.type-use-binding@1.0.0` rule once over one fixed admitted base snapshot. It joins only exact artifact keys and subjects, never display names, adapter IDs, registration order, Roslyn symbols, or another adapter instance. A derived `TypeUseBindingFact` retains canonical input `FactId` values and complete evidence in `GenerationFactLineage`; resolved compatibility references retain the exact target role in `TypeReferenceDefinition.TargetArtifactKind`. Conflicting, incomplete, foreign-owned, malformed, or unsupported inputs omit the affected artifact without a winner, flattened fallback, or partial output.

.NET adapters keep compatibility aggregate properties unbound with `DotNetTypeShapes.PropertiesOf(type)` and append `DotNetTypeUseFacts.Emit(...)`. `TypeUseFor(...)` preserves exact nested use-site shape and terminal source subject; an optional role callback emits only roles explicitly established by source-framework semantics. `EventSourceIdentifier` lowers through the existing identifier syntax, while the distinct ordinary `Identifier` role remains provenance until Screenplay owns separate syntax for it. Non-.NET and source-independent frontends contribute the same contracts directly.

Concept validation stays independent from identity, representation, attributes, and optionality. A rule uses an adapter-authored `RuleIdentity` for deterministic resolution, while `Predicate` is the authored predicate name emitted by lowering. Adapters contribute framework-neutral data and provenance only; they never reference Screenplay syntax:

Expand Down Expand Up @@ -187,7 +199,7 @@ dotnet pack Screenplay.Generation.slnx --no-build --configuration Release -o Art
./scripts/verify-package-consumers.sh 9999.0.0 Artifacts/NuGet
```

Package validation runs during pack against the latest released API baseline, `0.15.0`, for all four packages. Baseline strict mode remains disabled so intentional compatible additions are accepted while removals and signature changes still fail; no compatibility diagnostics are suppressed. The sentinel version must be applied to both the Release build and the no-build pack so package and assembly versions agree. The consumer smoke keeps clean legacy binaries compiled against the `0.1.0` core and `0.5.0` Vogen ancestry and runs them unchanged with current packages. A separate clean current-source consumer compiles only against the candidate packages and verifies the current authored-source, shared symbol helpers, declared concept nomination, neutral-fact, resolver, Vogen, adapter-composition, and deterministic compiler-verified generation APIs.
Package validation runs during pack against the latest released API baseline, `0.16.0`, for all four packages. Baseline strict mode remains disabled so intentional compatible additions are accepted while removals and signature changes still fail; no compatibility diagnostics are suppressed. The sentinel version must be applied to both the Release build and the no-build pack so package and assembly versions agree. The consumer smoke keeps clean legacy binaries compiled against the `0.1.0` core and `0.5.0` Vogen ancestry and runs them unchanged with current packages. A separate clean current-source consumer compiles only against the candidate packages and verifies the current authored-source, shared symbol helpers, declared concept nomination, neutral-fact, resolver, Vogen, adapter-composition, and deterministic compiler-verified generation APIs.

All builds require zero errors and zero warnings. Generated Screenplay output must compile and remain stable through print/compile/print.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,17 @@ public enum AdapterContributionAdmissionDiagnosticCode
/// <summary>
/// A required API capability occurs more than once.
/// </summary>
DuplicateApiCapability = 21
DuplicateApiCapability = 21,

/// <summary>
/// A type-use shape is empty or does not terminate in exactly one named type.
/// </summary>
InvalidTypeUseShape = 22,

/// <summary>
/// An artifact member declaration has a negative declaration order.
/// </summary>
InvalidDeclarationOrder = 23
}

/// <summary>
Expand Down
Loading
Loading