Skip to content

Add screenplay generate and validate commands - #51

Merged
woksin merged 11 commits into
mainfrom
feature/arc-screenplay
Jul 29, 2026
Merged

Add screenplay generate and validate commands#51
woksin merged 11 commits into
mainfrom
feature/arc-screenplay

Conversation

@woksin

@woksin woksin commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Added

Fixed

woksin and others added 11 commits July 28, 2026 18:40
Introduces `cratis screenplay generate`, which loads a target project
or solution into a Roslyn compilation and hands it to the
Cratis.Arc.Screenplay generator to produce a `.play` document.

Cratis.Arc.Screenplay is not published yet, so the CLI depends on it
only when the CratisArcScreenplayProject MSBuild property points at a
local checkout. Without it, the CLI still builds and the command
reports CLI0005 instead of generating — this keeps the CLI buildable
ahead of the generator's release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers the new generate command end to end: settings resolution and
error reporting, target resolution (solution vs. project, ambiguous
folders), project candidate narrowing, diagnostic grouping/ordering/
exit-code rules, and document line-ending/encoding handling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the reference page for `cratis screenplay generate`, covering
target resolution, output options, and diagnostics, and links it from
the reference index and table of contents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A design-time build only runs the Compile target, and PrepareResources —
the chain that turns <Generator>MSBuild:Compile</Generator> resx entries
into .Designer.cs sources and adds them to @(Compile) — is a sibling of
Compile under CoreBuild rather than one of its dependencies. It never
ran, so every use of a generated resource class became a compile error
and an ordinary application looked like it did not compile at all.

Inject a targets file through CustomAfterMicrosoftCommonTargets that
puts PrepareResources back in front of CoreCompile, where a real build
runs it. MSBuild then generates the sources into the project's own
intermediate output folder and adds them itself, so no path is hard
coded and the project need never have been built.

GeneratedResourceSources is the safety net for when that cannot work —
a read-only intermediate output folder, or an MSBuild that stops
honouring the hook — reading back any .Designer.cs a previous ordinary
build left on disk that the project does not already compile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The generator has shipped as 20.65.0, so the opt-in scaffold that let
the CLI build without it has served its purpose. An ordinary
PackageReference replaces the CratisArcScreenplayProject property, the
conditional ProjectReference, the CRATIS_ARC_SCREENPLAY define and the
Compile Remove that hid ArcScreenplayGeneration.

With generation always available there is no gap left to report, so
UnavailableScreenplayGeneration and the CLI0005 code it raised are gone
and ScreenplayGenerations always creates the real generation.

The package depends on exactly Cratis.Screenplay 1.5.2, which the
repository already pins, so the two stay aligned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A Screenplay document is only worth committing if something checks it,
and until now nothing in the CLI did. 'cratis screenplay validate'
compiles a .play file, or every .play file beneath a folder, and
reports what the compiler found — whatever wrote the document.

It is deliberately a sibling of generate rather than a flag on it: the
documents worth checking come from prologue and from people writing
them by hand just as often as from generation, and a CI gate should not
have to regenerate anything to run.

ScreenplayValidation is the only place that knows the Cratis.Screenplay
compiler exists, mirroring how ArcScreenplayGeneration isolates the
generator. Its diagnostics are translated into the shape generate
already reports, so both commands read identically. The compiler
assigns no codes, so the code is left empty and the writer leaves it
out rather than the CLI inventing a taxonomy; the location carries the
file and position instead, in the form editors already understand.

An empty folder is reported rather than quietly succeeding — a
validation that checked nothing is never the answer anyone wanted, and
CI is exactly where that would go unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The command specs substitute the validation, mirroring how the generate
command specs substitute the generation. ScreenplayValidation is
specified against the real compiler and real files instead — it is the
translation between two libraries, which a substitute would prove
nothing about.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Loading a real application produced a 5,362-line document that compiles
cleanly under the Screenplay compiler, alongside error diagnostics about
constructs the generator could not read. The command threw all of it
away and left the user with nothing at all — the one outcome that helps
nobody. A document that is very nearly right, plus honest diagnostics
saying where it is not, is strictly more useful.

With --file the document is now always written and the command still
exits non-zero, so a CI gate keeps failing while a person gets something
to look at. Writing to standard output is unchanged: whatever consumes
'screenplay generate > MyApp.play' cannot tell a partial document from a
complete one, so nothing is written there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keeps the Cratis pins in step with the Cratis.Arc.Screenplay 20.65.0
addition; Cratis.Screenplay stays at 1.5.2, which is what the package
depends on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records the new command, and the change in what an error means for
generate — the document is written anyway with --file, and only standard
output stays empty.

Also states plainly what separates 'cratis screenplay' from 'cratis arc'
now that both describe an Arc application: arc talks to a running
application over HTTP, screenplay only ever reads files. Fetching a
document from a running application is a complementary route that does
not exist yet, on either side, and is described as such rather than
left as an implication.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@woksin woksin added the patch label Jul 28, 2026
@woksin

woksin commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

Reviewer context — deliberately kept out of the description.

This is agent-authored code that no human has read yet. Please treat the spec suite as a claim to be checked rather than as evidence on its own. The areas I would look at first are ScreenplayCompilationLoader/DesignTimeResourceGeneration (the MSBuild hook is the sharpest edge in the change) and whether the new validate specs assert meaningful behaviour rather than merely existing. Do not merge without explicit sign-off.

What this depends on. Cratis.Arc.Screenplay 20.65.0, published earlier today from Cratis/Arc#2384. The branch previously reached the generator through an opt-in CratisArcScreenplayProject MSBuild property; that scaffold is now removed entirely, along with UnavailableScreenplayGeneration and the CLI0005 diagnostic that existed only to say the generator was unavailable. Cratis.Screenplay resolves to 1.5.2, which is exactly what the package depends on — a mismatch there fails assembly load at runtime.

The resx fix, and what it is not. A design-time build only runs the Compile target, and $(CompileDependsOn) does not include PrepareResources — that chain is a sibling of Compile inside CoreBuild. So strongly-typed .resx designer classes were never generated and never entered @(Compile), and an ordinary application presented as 605+ missing-symbol errors. The fix injects a targets file through CustomAfterMicrosoftCommonTargets that runs PrepareResources before CoreCompile, which is where a real build runs it. Nothing is hard coded, and it works on a project that has never been built — verified by deleting all 13 generated files from the test application and re-running. A narrow fallback that re-adds already-generated files from the intermediate output is kept for a read-only obj/, and is a no-op whenever the hook works.

#49 also floated replacing MSBuildWorkspace with dotnet msbuild -getItem. That was deliberately not done here: the hosting model is worth deciding on its own merits rather than as a side effect of a bug fix, and it is filed separately as #50 with the single-file/Homebrew MSBuildLocator rationale.

Three things a reviewer may want to change

  • validate exits 5 (ExitCodes.ValidationError) on a rejected document, where the official Cratis.Screenplay.Tool exits 1. Both report the same errors at the same positions; only the code differs. Following this CLI's own exit-code taxonomy seemed right, but it is a visible difference.
  • GeneratedScreenplay.Failed is now unused in production code — its only caller was the deleted UnavailableScreenplayGeneration. Left in place as a public factory mirroring LoadedCompilation.Failed; easy to remove if you would rather not ship it.
  • ScreenplayDiagnosticsWriter now omits the code when it is empty. The Screenplay compiler assigns no diagnostic codes, and I did not want the CLI inventing a taxonomy for them. generate output is unchanged, since its codes are never empty.

Verification

  • Cli.csproj Release with no -p:CratisArcScreenplayProject: 0 errors, 0 warnings. Full solution clean build: 5 projects, 0/0.
  • 355 specs passing (313 before; the merge from main brought 5 of those).
  • Each of the seven commits was built in isolation in a detached worktree — 0/0 every time.
  • End to end against a real production application (~1,800 syntax trees), no harness and no project reference: generate exits 0 and writes a 5,370-line document with no SP0024 and zero error-severity diagnostics; a second run is byte-identical. validate on that document exits 0 with one warning, and the official Screenplay tool reports the same thing — 0 errors, 1 warning. On a deliberately broken document both report the same two errors at the same positions.
  • origin/main merged in (ten commits, no conflicts — the two lines of work touch disjoint files).

Known, pre-existing and not introduced here: Documentation/verify-markdown.sh does not pass on this repo and did not before — 725 issues across 33 files, mostly MD013 line length and MD060 table pipes. The new page carries the same classes every other file does and adds no new violation categories.

@woksin
woksin merged commit 66be1ed into main Jul 29, 2026
1 check passed
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