Skip to content

Add grammar support for projection variants - #200

Merged
einari merged 3 commits into
mainfrom
feat/projection-variant-grammar
Sep 17, 2026
Merged

einari merged 3 commits into
mainfrom
feat/projection-variant-grammar

Conversation

@einari

@einari einari commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Adds the textual PDL grammar for Cratis/Chronicle's read model variants: a variant <Name>
block declares one of several mutually exclusive named read models sharing a projection's
logical identity, and enters on <EventType> declares the event that activates it. A block
declared at the projection level, outside every variant, is a shared handler applied to every
variant that has the mapped property. No kernel/engine changes are needed on the Chronicle
side for this — it maps onto the same From/Join/RemovedWith shape the Fluent and
ModelBound authoring styles already produce.

Added

  • variant <Name> and enters on <EventType> grammar in the Projection Declaration Language (Grammar support for projection variants (mutually exclusive read-model variants) #185)
  • Five new diagnostics (PLAY0277-PLAY0281) for invalid/missing/nested/duplicate variant declarations
  • Monaco language support for variant/enters on — syntax highlighting, completions, hover
    text, and property/event validation scoped per variant — consumed by Chronicle's Workbench
    ProjectionEditor via @cratis/screenplay-language/projection

Changed

  • A projection with variant blocks now builds one read model per variant (named after the
    variant) instead of one named after the projection's own identity, so a command's reads
    and other read-model references resolve variant names correctly

Adds the textual PDL surface for Cratis/Chronicle#3956's read model
variants: a variant <Name> block introduces one of several mutually
exclusive named read models sharing a projection's logical identity,
and enters on <EventType> declares the event that activates it.

A block declared at the projection level, outside every variant, is
a shared handler applied to every variant that has the property it
maps - there is no separate keyword for that; being outside a
variant block is what makes it global, mirroring the ModelBound
[GlobalFor<T>] and Fluent equivalents already shipped in Chronicle.

- ProjectionVariantSyntax / ProjectionEntersOnSyntax carry the new
  shape; renamed with a Projection prefix because Variant/EntersOn
  already exist for the unrelated arrangement (layout) grammar.
- ProjectionParser recognizes variant/enters on, rejects a variant
  with no entering event, rejects nesting a variant inside another,
  and rejects two variants of one projection sharing a name.
- ScreenplayPrinter and ScreenplaySyntaxWalker round-trip and visit
  the new nodes exactly like every other projection block.
- ScreenplayValidator now knows a projection with variants builds one
  read model per variant - named after the variant - rather than one
  named after the projection's own identity, so a command's reads (or
  any other read-model reference) resolves a variant name correctly.
- Five new PLAY02xx diagnostic codes, documented in diagnostics.md.

The semantic model binder (ESM v1) already rejects any projection
block that is not a plain 'from' through its existing generic
'not admitted by the first ESM v1 vertical' catch-all, so a variant
is correctly deferred there without needing a special case.
Adds the Variants topic page (pdl examples, best practices), extends
the EBNF grammar reference with VariantBlock/EntersOnDecl and the
five new validation rules, updates the projection vocabulary table
and topic index, and lists the five new PLAY02xx diagnostic codes.
Chronicle's Workbench ProjectionEditor consumes @cratis/screenplay-language/projection
directly, so the editor needs to know about variant/enters on independently of the
.NET compiler:

- language.ts: keyword tokens, indent-increase and folding on variant.
- HoverProvider.ts: hover text for variant and enters.
- CompletionProvider.ts: variant suggests known read model names (a
  variant names the read model it produces, same as the projection
  header's '=> ReadModel'); enters on suggests event types; bare
  'enters' suggests the 'on' continuation.
- Validator.ts: a variant line resolves its own read model schema for
  property validation inside the block (mirrors the projection-header
  handling) and reports 'not found'/'is a draft' exactly like the
  header does, which also makes the existing CodeActionProvider's
  generic 'Read model not found' quick-fix work for a variant name
  with no code changes there. 'enters on' validates its event type
  exactly like a 'from' line.

New for_projectionValidator specs cover a known variant, an unknown
one, a property missing from the variant, an unknown 'enters on'
event, and two sibling variants each keeping their own property
scope.
@einari einari added the minor New features, non-breaking additions label Sep 17, 2026
@einari
einari merged commit b642e89 into main Sep 17, 2026
3 of 4 checks passed
@einari
einari deleted the feat/projection-variant-grammar branch September 17, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features, non-breaking additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant