Repository navigation
Open .play files on the event model board in VS Code - #323
Conversation
Introduce @cratis/screenplay-compiler, the start of a TypeScript port of the C# Screenplay parser so the VS Code extension can compile .play documents itself. This first step carries the offside-rule foundations - the line splitter, the line reader and the parser context - mirroring their C# counterparts, plus a fence-aware skip for constructs the TypeScript compiler deliberately does not model. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Port the C# ScreenplayParser for what the event model board draws first: domain, import, concept, type, module, nested features, slices with their type, events and read models, and descriptions including fenced ones. Nodes carry the C# record's kind and its camelCase member names, and toSyntaxJson writes the C# SyntaxJson form narrowed to the members this compiler models. Constructs it does not model are skipped whole rather than reported, so the C# compiler and the editor diagnostics stay the authority on their validity. C# patterns are copied through a helper that keeps .NET's Unicode meaning of \w. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Port the slice members the event model board draws: a command's properties and declarative validation rules, queries with their parameters, the events each projection block consumes, reaction trigger sources (when, every, at) and unique constraints. What these constructs do beyond that - produces mappings, handlers, projection keys and mappings, trigger bodies - is recognized and skipped rather than modeled. A spec reads every .play document the repository ships and expects no errors, since each of them compiles with the C# compiler. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Port the specification parser for what the board shows of a specification: given events and read models, the command or appended event it acts with, and the events, read models and errors it expects, each with the values it states. Caller fixtures, denial, query and absence assertions are recognized and skipped. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Give consumers the same hooks the C# compiler offers for turning a syntax tree into artifacts: ApplicationSyntaxVisitor and its module, feature, slice, constraint and specification siblings, a compile(source, visitor) entry point, and a ScreenplaySyntaxWalker that visits every modeled node in the order the C# walker does. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Port PlayFolderMerge for what the TypeScript compiler models: a folder's documents are read in ordinal path order and combined, modules and features with the same name merging, and a concept, type or slice declared in two files reported and dropped. File reading stays with the caller, so the compiler runs wherever its text comes from. The canonical corpus folder merges into the same syntax as its single document, slice order aside. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
The TypeScript compiler writes the syntax it reads for a shared corpus of .play documents as golden SyntaxJson, narrowed to the members it models, and a spec holds it to those files. A C# spec holds the C# compiler to the same files: every member present in a golden file must read the same in the C# syntax, so the two compilers can only drift apart by failing one side. The corpus is the canonical corpus documents, both invoicing samples and a document exercising every modeled construct. Run the TypeScript specs with SCREENPLAY_REGENERATE_SYNTAX_VECTORS=1 to rewrite the golden files. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Nothing checked the JavaScript packages before a merge to main. Build the compiler, the language service and the extension, then lint and test every workspace. The .NET build now also runs when the golden syntax changes, since the C# compiler is held to it. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Add @cratis/screenplay-event-models, an ApplicationSyntaxVisitor that turns the TypeScript compiler's syntax into the JSON document @cratis/event-models draws - the counterpart of Studio's EventModelSyntaxVisitor. Every module goes into one collection, a State View shows the read model its projection builds, and a slice consuming an event points back at the State Change slice that produces it and carries its shape. Commands carry the validation rules the board has an equivalent for, events their unique constraints, automations what sets them off, and slices their specifications with literal values. Ids are derived from where each element sits in the model, so recompiling after an edit leaves untouched elements with the ids the board remembers their state by. Every repository sample maps into a document the published board schema accepts. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Register a custom editor that is the default for *.play: a webview drawing the document on @cratis/event-models' board, compiled with the TypeScript compiler and the event model mapping. The board redraws when the text changes, wherever it is edited, and lists the compiler's errors above the board with a link to the line, while still drawing everything that could be read. Show Source and Open Event Model Board move between the board and the text, which stays the source of truth. The webview bundles React, the board and its stylesheets and fonts, and runs under a nonce-only content security policy; Pixi compiles its shaders through pixi.js/unsafe-eval so the page never has to allow eval. The board is drawn in its dark theme whatever the editor theme is: its labels are colored for a dark surface, which is also why Studio's viewer is dark only. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
The declarations were written as a .d.ts, which the repository ignores, so the previous commit left the webview without them and its typecheck would fail on a clean checkout. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
A document in a folder application is one file's share of the model, so the board now compiles the application it belongs to: every .play file beneath the nearest folder holding an application.play, which is what the C# compiler writes when it expands an application into folders. Unsaved text is compiled for every open file of the folder, the board redraws when any of them changes, is saved, created or deleted, and a problem in another file opens that file. A document outside a folder application is shown on its own, as before. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
The extension now bundles the TypeScript compiler and the event model mapping, so the Marketplace job builds both before bundling it. Both packages are private for now: the extension carries them, and publishing them to npm needs trusted publishing set up for each new package first. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Add a page for the extension's board - moving between the board and the text, folder applications, errors and theme - and one for the TypeScript compiler: parsing a document and a folder, its visitors and walker, what it reads and skips, and how it is held to the C# compiler. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
|
Notes for reviewers What was verified, and how:
Not verified here:
Decisions worth a look:
Generated by Claude Code |
The editor demo pinned monaco-editor 0.52 while the language service it consumes builds against 0.55, so its typecheck saw two incompatible monaco APIs and the root yarn build failed. Align the editor on 0.55. A language service spec asserted on a possibly undefined value, which failed the root yarn compile. The JavaScript workflow now runs the root build, compile, lint and test scripts, so every workspace is checked on every pull request. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
…reports
Event tags are now part of the syntax, and an inline '{...}' or '[...]'
value is read into object and list expressions as the C# compiler reads
it, keeping the first of two members with the same name. The parser also
reports what the C# parser reports inside the constructs it reads: a
reason for a missing or already explained concept attribute, an empty
validate in an enumeration, an entering event claimed by two variants,
invalid tags, invalid structured values and unknown $context paths.
The walker visits tags and the items and members of structured values.
The conformance document exercises tags and structured values, and the
golden syntax is regenerated with them.
Part of #322.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Add 112 invalid documents with the diagnostics they are reported with - code, line and order - covering every error and warning the TypeScript parser can report. A TypeScript spec and a C# spec each hold their compiler to the same file, so an error path cannot drift on one side without failing. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Add specs for what the compiler skips at every level, every trigger and validation form, structured values valid and invalid, folder merges of domains, imports and agreeing descriptions, string-aware splitting and unescaping, and the JSON writer. The walker is held to visiting exactly the nodes the tree holds, kind by kind. Remove what no input can reach: the unknown validation rule branch, whose operands all have a kind, a default in trigger parsing the caller never takes, and an unused location helper. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Cover every trigger form an automation shows - event, custom, every interval unit and schedule - slices missing their command, read model or trigger, an application without modules, and the shape of every kind of property type, recursive types included. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
Both packages now run their specs with coverage and fail below their thresholds - 99% of lines and 96% of branches for the compiler, every line for the mapping - so new code arrives with its specs. The compiler's documentation says how its diagnostics are held to the C# compiler and how coverage is enforced. Part of #322. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MrnFQW3Zx9PqPJgwE4b7Kc
|
Update for reviewers Root workspace fixed.
Diagnostics held to C#.
Syntax, tags and structured values. Event tags and Coverage, enforced. Both packages run their specs with coverage thresholds, and a planted uncovered file fails the run.
The walker spec checks that the walker visits exactly the nodes the tree holds, kind by kind. Still untested by specs: the extension's VS Code glue ( Generated by Claude Code |
The Screenplay VS Code extension now opens
.playfiles on the Cratis event model board, the board Cratis Studio draws. A new TypeScript compiler for Screenplay draws it.Added
.playfile opens on the event model board by default and redraws as you edit. Show Source opens the text beside the board, Open Event Model Board goes back, and Reopen Editor With… still offers the text editor. See VS Code extension. (Open .play files on the event model board in the VS Code extension #322)application.play, shows the whole application on the board. This includes unsaved edits to any of its files..playdocuments and folders into the same syntax tree as the C# compiler, with the same visitors and walker. It reports the same diagnostics as the C# compiler for the constructs it reads. See TypeScript compiler.