Skip to content

Render a context path against what the enclosing artifact receives - #33

Merged
woksin merged 2 commits into
mainfrom
fix/render-context-against-what-a-handler-has
Aug 13, 2026
Merged

Render a context path against what the enclosing artifact receives#33
woksin merged 2 commits into
mainfrom
fix/render-context-against-what-a-handler-has

Conversation

@woksin

@woksin woksin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixed

  • A $context path in a produces mapping is rendered against what the enclosing artifact actually receives, so a rendered application compiles. A command handler receives Arc's CommandContext, which carries none of Occurred, Identity, Tenant, CausedBy or Causation — those belong to Screenplay's own context type — and rendering them as members of it produced 15 compilation errors on the language's own invoicing.play. The handler now asks the runtime for what the document reads: the tenant from ITenantIdAccessor, the caller from IIdentityProvider, roles and claims from ICurrentPrincipalAccessor, the cause from ICausationManager, and $context.occurred from the clock. (Rendering a $context path emits members Arc's CommandContext does not have #32)
  • A context value the document maps onto a property that cannot hold it — the runtime's string tenant onto a Uuid concept — is dropped and reported instead of rendered into a conversion that does not exist. (Rendering a $context path emits members Arc's CommandContext does not have #32)
  • A command handler reading $eventContext or $eventSourceId, which it runs too early to have, is reported rather than rendered. (Rendering a $context path emits members Arc's CommandContext does not have #32)

woksin added 2 commits August 13, 2026 10:41
A rendered command handler is an Arc model-bound Handle(), and Arc's
CommandContext carries none of Occurred, Identity, Tenant, CausedBy or
Causation - those are on Screenplay's own CommandContext, a different
type a rendered application never receives. Rendering every context
expression as 'context.<PascalPath>' therefore emitted members that do
not exist, and the application did not compile.

The enclosing artifact now supplies the rendering. An event context is
still read straight off 'context'; a command handler asks the runtime
for what it needs through handler parameters, and reports the paths it
cannot reach rather than naming a member for them.
… names

The spec harness compiled rendered files against the implicit usings
alone, so it saw neither the FluentValidation the Cratis package brings
in nor the IIdentityProvider ambiguity that same set creates. It now
mirrors the package's global usings, which is what makes the new
compile assertion mean anything.
@woksin woksin added the minor label Aug 13, 2026
@woksin

woksin commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Reviewer context.

How this was found, and the number it moves. invoicing.play (17 slices, every construct the language has) was compiled with ScreenplayCompiler, rendered through CratisRenderer into InMemoryCodeOutput, and the result compiled in memory against the real Cratis assemblies. 30 errors before, 15 after. Every one of the 15 removed was the renderer's own rendering; the 15 that remain are authored csharp blocks in the document and an event the document references but never declares — both catalogued on #32 so they are not read as this fix falling short.

The harness was under-reporting and over-reporting at the same time. RenderedOutput compiled against the template's ImplicitUsings only, not the global usings the Cratis package contributes through Cratis.props. Without them the rendered validators looked broken (FluentValidation missing) and the IIdentityProvider ambiguity was invisible — that ambiguity exists because the package's set imports both Cratis.Arc.Identity and Cratis.Chronicle.Identities. The list is now mirrored in the harness, which is what makes should_render_an_application_that_compiles mean anything.

Why Cratis.Chronicle.Identities.IIdentityProvider is written in full in the generated signature: the short name is ambiguous in every rendered file whether or not the renderer adds a using of its own.

A spec defended the old shape. for_ExpressionRenderer/when_rendering_expressions asserted context.Identity.Id under the name should_render_a_context_path_against_the_fixed_context_parameter. Its assertion is correct — for an event context. Its premise, that one expression has one rendering everywhere, is what was wrong, so the premise moved and the assertion stayed.

Verified the new specs bite. Reverting CommandContextAccess.Resolve to the old context.<PascalPath> fails 22, including should_render_an_application_that_compiles. Reverting only the type-mismatch check fails that spec's 3. Full suite 254/254 green with both in place; Debug and Release both build with zero warnings.

Not verified: nothing was run against a live Chronicle or MongoDB. The four collaborators are types the Cratis runtime registers by convention (IFooFoo), which is how the rendered handler resolves them, but no rendered application was booted to confirm the resolution end to end.

Deliberately out of scope, on #32: whether a rendered application should reference Cratis.Screenplay so an authored csharp block compiles against the context it was written for. That is a dependency decision for the rendered output, not a rendering defect. Today such a block is emitted as written and now says so in a diagnostic.

@woksin
woksin merged commit 2ff1037 into main Aug 13, 2026
1 check passed
@woksin
woksin deleted the fix/render-context-against-what-a-handler-has branch August 13, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant