Skip to content

Stage.Contracts cannot express the Screenplay 2.x constructs, so they cannot survive a round trip #23

Description

@woksin

Summary

Cratis.Stage.Contracts is the intermediate representation the whole authoring pipeline passes through, and it is now narrower than the language it carries. Screenplay 2.x added constructs the contract cannot hold, so they are lost on import and unrenderable on output — no matter what the compiler, the importer or the renderer do.

The pipeline

.play → Screenplay compiler → Studio importer → Stage.Contracts.EventModel → Stage renderer → C#

Studio does not generate code itself: Source/Core/Program.cs:148 registers
Cratis.Stage.Rendering.Cratis.CratisRenderer.CreateDefault() as the IRenderer. Every construct has to
survive a round trip through EventModel in both directions.

What the contract cannot express

Source/Contracts/Slice.cs:

public record Slice(
    Guid Id, string Name, SliceType SliceType,
    IReadOnlyList<EventDefinition> Events,
    CommandDefinition? Command,        // one
    ReadModelDefinition? ReadModel,    // one
    IReadOnlyList<Specification> Specifications);

Missing entirely — no type in Source/Contracts mentions any of them:

Screenplay 2.x Contract
readmodel <Name> standing alone
reducer <Name> => <ReadModel> with on rules
reads <ReadModel> [by <property>]
require <condition>
invokes <Command> on a reactor
scoped to <scope> on a query
for <expression> in a produces body
authorize no authorization member on CommandDefinition

Two cardinality limits also now diverge: a slice may declare several commands and several
projections in Screenplay, and the contract holds one of each.

Why this is the gating item

Everything else in the chain is either done or mechanical:

  • Screenplay 2.1.0 is released and expresses all of the above.
  • Studio's importer already warns about what it drops (Cratis/Studio#1074) — it knows the gap exists.
  • Stage's renderer can be extended once there is something to render from.

But a construct that cannot be held in EventModel cannot survive the round trip, so extending the
importer or the renderer first would have nowhere to put the data.

Suggested direction

Design the contract first, once, and let both sides encode against it. If the importer and the renderer
each grow their own understanding of a construct, they will disagree, and a round trip is exactly where
that surfaces — there is already one such asymmetry today: specifications and constraints are exported
by Studio but dropped on import
(noted on Cratis/StudioIssues#52).

Worth deciding explicitly, as part of that design, which Screenplay constructs are deliberately not
carried to generated code. That is a product decision, and making it explicit is better than discovering
it as a silent drop later.

Sequencing

This is blocked behind a Cratis.Stage release built against Screenplay 2.x — 1.2.0 still bakes
Cratis.Screenplay.Secrets.ISecretsFiles into its generated convention-binding table, which throws
TypeLoadException on every AddCratisArcCore when Screenplay 2.x is on the graph. That release also
unblocks Cratis/Studio's absorption.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions