Skip to content

Declarative language additions: reactor outputs, multiple projections, observable queries, attribute reasons and conditional rules - #45

Open
woksin wants to merge 6 commits into
mainfrom
feat/declarative-language-additions
Open

Declarative language additions: reactor outputs, multiple projections, observable queries, attribute reasons and conditional rules#45
woksin wants to merge 6 commits into
mainfrom
feat/declarative-language-additions

Conversation

@woksin

@woksin woksin commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Five language decisions that let a .play document describe what the application it models actually does, instead of losing it to a file reference or an escape hatch. Each addition is additive — an existing document keeps its meaning.

Added

Changed

Fixed

woksin added 4 commits July 29, 2026 11:30
Five gaps where a document could not say something the application it
describes actually does.

A reactor trigger required a 'file' or an inline block, so the one construct
whose meaning lived entirely in the escape hatch could not be written before
the code existed. The body is now optional, and 'produces <Event>' and
'executes <Command>' state what the reaction does - resolved against the
document's events and commands the same way command produces always has been.

A slice could declare at most one projection, so a slice that keeps its
decision model beside its view model had to drop one or invent a slice that
does not exist. SliceSyntax.Projection becomes Projections.

A query could not say whether it is live, though a reader binding to it has to
build something different in each case. An 'observable' directive says so;
unmarked stays one-shot. A second 'by' parameter silently overwrote the first
and now reports.

Concept attributes were bare markers, so the reason a value is personal data -
purpose, lawful basis - had nowhere to go. Any attribute takes an optional
quoted argument, and a bare attribute stays valid.

A validation rule could not say that it only applies sometimes, and a bare
path operand parsed without any stated meaning. Rules take a 'when' condition
reusing the produces-when grammar, 'today' becomes a distinct node rather than
a path indistinguishable from a property of that name, and a path operand is
specified to resolve against the validated artifact's sibling properties.
@woksin

woksin commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Reviewer notes — this PR is deliberately left open for a call on the design, not merged.

Labelled major, not minor. Two public syntax nodes change shape:

  • SliceSyntax.Projection (ProjectionSyntax?) → SliceSyntax.Projections (IEnumerable<ProjectionSyntax>)
  • ConceptSyntax.Attributes (IEnumerable<string>) → IEnumerable<ConceptAttributeSyntax>

Both break anything constructing or reading those nodes — Cratis.Arc.Screenplay most immediately. Everything else is additive: new optional trailing record parameters (ReactorTriggerSyntax.Produces/Executes, QuerySyntax.IsObservable, ValidationRuleSyntax.When) and new syntax that unmarked documents never hit.

#38 syntax differs from the issue's sketch. The issue proposed an indented pii reason "…" line; this implements the argument form @pii("…"). It keeps the attribute on one line where the rest of the attributes already are, applies uniformly to any @word without enumerating known attributes, and needs no new block. Happy to switch to the indented form if you prefer it.

#32 is partial — this is (a) and (b) only. The state-based require <ReadModel>.<path> form is not implemented here; it should be co-designed with #35, and I have commented on #32 with the analysis.

Notes on the choices that were not forced:

  • Reactor output ordering when printing is producesexecutesfile/inline block, putting the declarative description above the realization pointer.
  • today is only a keyword in validation-rule operands, not in general mapping sources — narrowing it there avoids changing the meaning of an existing x = today mapping.
  • The rule/when split is done on whitespace-separated words rather than a regular expression, so a when inside a quoted operand (matches "^when$") is not mistaken for the keyword.

Verificationdotnet build Debug and Release both 0 errors / 0 warnings (Release treats warnings as errors); dotnet test 648 passing, 0 failing; yarn lint, yarn build and yarn compile clean. Debug carries one pre-existing IDE0305 warning in for_ScreenplayWriter/when_inspecting_assembly_visibility.cs that predates this branch and is untouched here.

The keywords were added to the tokenizer but not to the completion lists or
the hover documentation, so 'observable' inside a query body and 'produces' /
'executes' inside a reactor trigger highlighted correctly while the editor
still refused to suggest or explain them.
'@today' worked as a rule operand but not as the rule's subject, so a reader
who learned the escape in one position hit an error using it in the other.
No validate-block directive collides with a property name, so the escape is
not strictly needed there - but it costs nothing to accept and removes the
inconsistency.

Also covers attribute reasons holding characters that matter to the grammar -
a close paren, an at sign, a literal backslash - and two attributes that both
carry an argument, none of which the round-trip specs exercised.
@woksin

woksin commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Merge ordering note. See the note on #49 — that PR and this one each merge cleanly into main but conflict with each other in four files, since both extend the validation rule line. Whichever lands second needs a small manual resolution; the two features are additive and compose. #48 is independent of both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

major Breaking changes to public APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant