Summary
The validate sub-language covers per-property declarative rules with messages, plus a validate csharp escape hatch — and nothing in between. Validating a real Arc application's document against its source shows three recurring rule shapes that fall straight through to the escape hatch (or are lost entirely when the document is generated), even though they are the most important rules an application has:
- Conditional rules — FluentValidation's
.When(...): a rule that only applies under a condition.
- Cross-property comparison semantics —
endDate >= startDate. The parser already accepts a bare path as a rule operand (operands are mapping-source expressions), but nothing specifies that the path resolves against a sibling property, so the meaning is undefined for consumers like Stage.
- State-based business rules — the Arc/DCB idiom where a command validates against injected read-model state (
Must(_ => !state.IsStarted), "phase must be Contract", "candidate must exist on the work surface"). These are the rules that actually guard the domain, and they are inexpressible declaratively.
Evidence
From the Ada application (generated with Cratis.Arc.Screenplay 20.65.0):
- 18+ whole-command rules (
RuleFor(c => c).Must(...)) dropped with "'RuleFor(c => c)' does not name a property directly, so the rules declared on it were left out" — every one of them a domain business rule reading read-model state, e.g.:
RuleFor(c => c).Must(_ => !state.IsStarted)
.WithMessage(_ => HourRegistrationMessages.StartMonth_AlreadyStarted);
RuleFor(c => c).Must(_ => summary.Phase is RequestPhase.Contract);
.When(...) chains dropped (no counterpart in the rule grammar).
- 6 ×
SP0011 drops of the shape GreaterThanOrEqualTo(x => x.StartDate) — "carries no value to compare against" — where the natural emission endDate >= startDate parses today but has unspecified semantics.
The result is that a generated document's validate blocks contain the string-length/regex layer of validation while the domain-rule layer — the part a reader of the Screenplay actually needs to understand the behavior — is absent.
Suggested direction
- Specify property-path operands: state in the grammar docs that a bare path operand resolves against the validated artifact's properties (
endDate >= startDate), so emitters and Stage agree on the meaning. This one may be documentation-only.
- Rule-level conditions: an optional
when <condition> on a rule line or an indented condition block, reusing the existing produces when condition parser:
validate
newEndDate > endDate when isExtension == true message "…"
- State-based rules: a declarative form for command rules that read a read model, e.g.
validate
require <ReadModel>.<path> <op> <value> message "…"
syntax open — the ask is that the concept exists, so generated documents (and hand-written ones) can state "this command is rejected while the month is already started" without dropping to validate csharp.
Related: closed #8 added concept-level validation; this is the command/business-rule layer on top of it. Cratis.Arc.Screenplay currently drops all of these shapes with SP0011/SP0016 diagnostics — a companion issue on Cratis/Arc covers recovering them once the language can carry them.
Context
Found while validating a generated 5,348-line document from a production Arc application (643 SP0016 warnings in one generation run; the domain-rule layer of every command is missing from the document).
Summary
The
validatesub-language covers per-property declarative rules with messages, plus avalidate csharpescape hatch — and nothing in between. Validating a real Arc application's document against its source shows three recurring rule shapes that fall straight through to the escape hatch (or are lost entirely when the document is generated), even though they are the most important rules an application has:.When(...): a rule that only applies under a condition.endDate >= startDate. The parser already accepts a bare path as a rule operand (operands are mapping-source expressions), but nothing specifies that the path resolves against a sibling property, so the meaning is undefined for consumers like Stage.Must(_ => !state.IsStarted), "phase must be Contract", "candidate must exist on the work surface"). These are the rules that actually guard the domain, and they are inexpressible declaratively.Evidence
From the Ada application (generated with
Cratis.Arc.Screenplay20.65.0):RuleFor(c => c).Must(...)) dropped with "'RuleFor(c => c)' does not name a property directly, so the rules declared on it were left out" — every one of them a domain business rule reading read-model state, e.g.:.When(...)chains dropped (no counterpart in the rule grammar).SP0011drops of the shapeGreaterThanOrEqualTo(x => x.StartDate)— "carries no value to compare against" — where the natural emissionendDate >= startDateparses today but has unspecified semantics.The result is that a generated document's
validateblocks contain the string-length/regex layer of validation while the domain-rule layer — the part a reader of the Screenplay actually needs to understand the behavior — is absent.Suggested direction
endDate >= startDate), so emitters and Stage agree on the meaning. This one may be documentation-only.when <condition>on a rule line or an indented condition block, reusing the existingproduces whencondition parser:syntax open — the ask is that the concept exists, so generated documents (and hand-written ones) can state "this command is rejected while the month is already started" without dropping to
validate csharp.Related: closed #8 added concept-level validation; this is the command/business-rule layer on top of it.
Cratis.Arc.Screenplaycurrently drops all of these shapes with SP0011/SP0016 diagnostics — a companion issue on Cratis/Arc covers recovering them once the language can carry them.Context
Found while validating a generated 5,348-line document from a production Arc application (643 SP0016 warnings in one generation run; the domain-rule layer of every command is missing from the document).