Skip to content

Authoring with the MCP tools

This page shows the tool calls behind Create a model and Edit a model: the exact arguments an MCP client sends. If you are asking an assistant to do the work, those guides give you prompts instead. Use this page when you are building a client, debugging a proposal, or checking what an assistant did.

Use this procedure with an MCP client connected to the Screenplay server. Calls below are MCP tools, not shell commands. The server root is the application boundary; file layout does not create separate applications.

Call open-workspace. Supply an applicationName for a new model; existing identity state provides its saved name and identities. A root can contain one or many .play files, or be empty when you are starting a new model.

Retain the returned revision and catalogRevision. They describe the exact snapshot you are editing. Do not reuse them after applying changes.

For an existing application, call read-workspace with view: "documents" and expectedRevision. Each document includes its identity, path and root handle. Use describe-application for the logical hierarchy rather than reconstructing it from directory names.

ToolUse it forValidation
propose-sourceWhole .play text documents, including syntax-only language constructsAuthoring by default; optional Executable
propose-astFocused typed node edits using read-ast handles, or typed whole documentsAuthoring by default; optional Executable
proposeExact byte replacements and legacy whole-document operationsExecutable-only
propose-propertyAdd, rename or remove explicitly selected properties along proven mapping chainsAuthoring by default; optional Executable

These tools return proposals to review with read-proposal before explicit apply. Acceptance never writes files or executes specifications.

Send propose-source with expectedRevision, expectedCatalogRevision, explicit formatting consent and a documents array. For example, this array creates a valid source-only model without building typed JSON:

[
{
"operation": "create-document",
"stableKey": "application",
"path": "application.play",
"source": "concept Month : Int\neventsource Account\n stream Transactions\n streamId Month\n"
}
]

Choose the formatting mode explicitly:

  • PreserveExactSource writes your supplied text verbatim, including comments, alignment, blank lines, CRLF and a missing final newline. Structural edits such as adding an import or appending a feature are supported. Replacements retain the original document’s UTF-8 BOM policy; creations use the requested encoding. No canonicalization warning (PLAY0288) is emitted.
  • CanonicalizeTouchedDocuments reprints touched documents canonically, normalizes whitespace and reports dropped comments.
  • PreserveTrivia patches supported bounded changes in the original text. It refuses structural changes; use PreserveExactSource to keep your supplied text.

For example, add import "Notifications/Notifications.play" to the complete source of an application.play replacement, and create the imported document in the same documents array. With formatting: "PreserveExactSource", the comment above the imports, aligned columns and blank lines stay exactly as you supplied them. The server still parses in final import placement, checks the intended AST, compiles the merged source and enforces identity continuity, references and source-transition obligations. Exact mode does not waive validation. propose-ast has no source witness and still refuses edits requiring printing in exact mode.

validation defaults to "Authoring"; this example is accepted with executableReady: false, while "Executable" rejects it with PLAY0268. referencePolicy defaults to "Safe"; "Draft" reports deliberate unresolved reference debt without waiving parsing or identity continuity.

The document operations use the same names as propose-ast.documents:

OperationFields in addition to operation
create-documentpath, stableKey, source; optional encoding: Utf8 (default) or Utf8WithBom
replace-documentdocumentId, source
remove-documentdocumentId
move-documentdocumentId, path
rename-document-keydocumentId, stableKey

source is a UTF-8 text string, not base64 or a typed node. There is no node operations array on this tool. Each document may be targeted once per batch. Parsing resolves imports and fragment placement against the complete final document set, including unchanged files. You can create a barrel and its imported fragments in one proposal, in either array order. A parse failure returns success: false, failureKind: "SourceParseFailed" and located authoringDiagnostics, with no proposalId. Fix the text and make a new proposal.

Whole-source replacement uses the same typed authoring transaction and continuity rules as propose-ast: existing IDs survive unchanged addresses; removing assigned declarations requires explicit retiredSemanticAddresses and, for event contracts, retiredEventAddresses. Renames require coordinated semanticRenames and eventRenames. Replacement preserves the document’s encoding policy, but printing may normalize whitespace. Review exact before/after bytes, dropped comments and introducedExecutableErrors before applying.

For add, read the owner declaration handle with read-ast and supply action: "add", owner, expectedOwnerName, name and type, plus both revision guards. Select declaration hops with {at, name} and mapping hops with {at, value}. AutoMap can satisfy a same-named read-model hop. A required property needs fixtureValue literal source text at every existing fixture, example and assertion; optional additions do not edit fixtures. Missing sources or values refuse, never infer. Event additions, even optional ones, use the same explicit evolution choice as rename/remove. See adding a property.

For rename/remove, read the original property handle with read-ast, then call propose-property with expectedRevision, expectedCatalogRevision, action: "rename" or "remove", target and expectedName. Rename also requires newName. Each optional alsoRename or alsoRemove entry is { "target": <handle>, "expectedName": <name> }; select only the fields you intend to change. Chain membership proves eligibility, not permission to change additional fields. Off-chain selections refuse atomically.

Renaming just a command input repairs its mappings without renaming the event payload. Broken AutoMap links become explicit mappings. Removal requires no remaining readers and discloses removed assertion values. When the specifications that inherit a removed example value cannot be determined, for example because the example or a specification using it has errors, removal refuses with AmbiguousPropertyReference instead of reporting no weakened assertion. Opaque or incomplete links, historical generations, inline events, keys and unsupported spans refuse with original candidate locations rather than guessing.

Select exactly one event mode when any selected field belongs to an event: newGeneration: true appends N+1; eventNeverPersisted: true edits current N in place, preserving renamed IDs. Both true is invalid parameters. The flags select no extra events. Default formatting is PreserveTrivia; structural changes require explicit CanonicalizeTouchedDocuments consent. Reference policy is always Safe.

Inspect property in the result and retained read-proposal, including semantic diff. generations is always an array, ordered by canonical event address and deduplicated by selected owner. Each entry has event (a canonical address), from, to and mode. Different events keep their own revision pairs; in-place edits have equal revisions. [] describes no accepted planned event edits and is not a success verdict. No singular alias is emitted. deployableWithoutTransformation and esmVersionKnown describe the candidate, independently of source acceptance. New generations still require transformations before Chronicle deployment.

Only apply writes the retained source and identity plan. See the complete planner reference.

An event declared with produces event appears as an Event in declaration queries at its slice-owned address, not beneath the command. declaration-details exposes its typed properties and authoring documentation; the command’s produces view retains InlineEvent. Reference queries include declares relationships from the command and slice, alongside the command’s produces relationship. The visualization counts and draws the event like a standalone declaration. Workspace syntax entries assign the event and its properties the same stable addresses and catalog identities as their standalone equivalents in that slice. Event and containing-slice rename proposals preserve those assignments.

The top-level identity block is also authoring metadata. syntax-schema exposes its typed detail/source nodes; describe-application declarations and search views include Identity and IdentityDetail, with query-source references available for navigation. It adds no ESM member. Executable $identity.<detail> use reports PLAY0268 pending #600; the model and affected declarations’ execution-readiness views report the same pending admission. A model with only the block, and no executable detail reads, stays ready. There is no dedicated detail rename or automatic identity-source repair; propose a reviewed AST edit and update affected caller paths explicitly.

Descriptions, documentation, and optional rename-only id remain syntax metadata. They do not replace the workspace identity catalog or the portable hashed event contract id. Workspace repair and rename proposals remain separate, explicit transactions; querying or visualizing an inline event never applies a repair.

Typed ASTs retain PropertySyntax.isKey, QuerySyntax.byParts and ReadsSyntax.byParts. Single and block lookup forms are mutually exclusive. Declaration details and fixtures retain every named part. A single explicit key binds without changing ESM bytes. Composite keys remain authoring-only and report PLAY0268 citing #599. See Read-model keys.

After opening the workspace, use these paged read-workspace views with expectedRevision:

ViewContents
event-sourcesApplication-owned source keys, identifier types, pins and physical handles
event-streamsExact source-owned stream keys, key types, pins and ownership status
event-source-details, event-stream-detailsExact authoringKey; compact header with metadata sizes and source/AST read pointers, followed by independently paged children; rejects ambiguous or incomplete ownership
command-routesAuthored command routes, production override references and retained ambiguity candidates, never inferred effective routing
event-source-diagnosticsPaged physical parser/import and whole-assembly diagnostics, including errorful and unresolved files

Continuation also requires expectedCatalogRevision. The immutable physical inventory retains partial declarations, duplicate routes and route/property candidates from all parsed files, independently of editable AST eligibility. inventoryComplete, authoringDiagnosticsView and authoringDiagnosticsCount disclose parser/import and whole-assembly evidence. Unknown extent reports ownership: "incomplete"; details refuse with IncompleteSource. Duplicate physical parents make every child ambiguous, including parents in errorful files. Unresolved-placement entries name refused documents, and retained nodes in those files have a null authoringKey rather than a fabricated owner. Keys include application, declaration kind, full owner path and name; they are not persistent semantic identities. ESM v8 admits source and stream declarations, scalar/composite direct-property or literal routes, and specification routes. Inventories report construct readiness independently of whole-workspace binding: executionAvailable is true only when that binding succeeds. A command/specification/slice readiness message describes that member’s constructs and command dependencies; model readiness also includes unrelated source declarations.

The detail boundary is compact-header-v1, not full typed syntax. No header embeds all child stream subtrees. Child items follow the actual physical parent’s authored order. Pages honor item and serialized-byte budgets; continue from nextOffset, not offset + limit. Pins and descriptions above 4096 UTF-8 bytes are explicitly omitted with exact sizes and a read-document byte-page pointer. Use a separate read-ast content request for the full node when it fits; a single oversized identity cannot be narrowed by reducing the item count.

Generic source details, dependency resolution and editor navigation share physical source confidence, including unresolved placement candidates and unknown root extent. Unique unrelated event and operation references retain their existing resolution rules.

Generic EventSource/EventStream declaration queries and dependencies include source identifier/stream-id type references and separate commandEventSource/commandStream links. Stream references accept exactly Source.Stream, not arbitrary suffixes. Combined type/declaration collisions remain blocking and are not navigated confidently.

For edits, read the actual read-ast handles and syntax-schema kinds, then use Authoring Add/Replace/Remove with preview and explicit apply. The member paths and candidate-selection rules are in AST authoring. Use propose-rename for source and stream renames with bound command, production, observer-filter and specification reference repair. Catalog identities are enrolled and migrate atomically with source/stream renames; route selection remains an explicit authoring decision. PLAY0664 has a typed repair that removes an identical production override. Other route diagnostics have no automatic repair. Property-path routes and handler commands still refuse with PLAY0268. Executable validation requires every construct in the workspace to bind.

ProducesSyntax.stream carries a production override. ReactionSyntax.from and ReducerSyntax.from carry observer filters. These constructs select ESM v10. Each override replaces the whole command route. A filter matches stored source and optional stream names, never stream ids or unrouted facts.

Systems and operations are syntax-only, not admitted by any supported executable model (ESM) version yet (PLAY0268). After open-workspace, call read-workspace with the current expectedRevision:

ViewContents
system-intentsApplication-scoped systems, descriptions through details, kind/full-scope authoring keys and source occurrence handles
operation-intentsSlice-owned operation keys, uses, input counts, execute/compensate state and selected source handles
system-intent-details, operation-intent-detailsSupply the inventory’s authoringKey; pages contain declaration intent, typed inputs, phases and ordered hints
ordered-productionsCommand scope/handle and every production occurrence in authored order, including repeated events, resolved kind, candidates and mappings

Echo expectedRevision for every page. Stale snapshots, wrong declaration kinds, ambiguous keys and unresolved placement refuse details rather than selecting an occurrence. Inventory includes unresolved-placement document entries so you can repair conflicting/cyclic imports. Authoring keys are name-derived logical keys, not admitted SemanticId or RequirementId; use revision-local handles for physical edits. Attachment states describe model selection only; no source is executed or confirmed.

Declaration search/details and dependency traversal also include systems, operations and operation specification references. declaration-details supports inputs and phases on Operation; syntax returns explicit typed content. Produces links resolve event/operation kinds together and report ambiguity without guessing. Authoring read views work without ESM binding.

Use read-ast includeContent, syntax-schema and ordinary typed propose-ast add/replace/remove operations with validation: "Authoring" to edit inputs, phases, source or hints. Each phase alone owns file/code; the wrapper owns hints. validation: "Executable" refuses with PLAY0268. Existing catalog assignments are not replaced by operation keys. Automatic operation extraction and operation/system rename are not available; manual promotion requires coordinated typed edits and full-source validation.

Call syntax-schema for ApplicationSyntax, ModuleSyntax, FeatureSyntax, SliceSyntax and EventSyntax to inspect their member contracts.

Use the following complete value for the documents argument of propose-ast:

[
{
"operation": "create-document",
"stableKey": "application",
"path": "application.play",
"node": {
"kind": "ApplicationSyntax",
"modules": [
{
"kind": "ModuleSyntax",
"name": "Sales",
"isPlacement": false,
"features": [
{
"kind": "FeatureSyntax",
"name": "Orders",
"isPlacement": false,
"slices": [
{
"kind": "SliceSyntax",
"type": "StateChange",
"name": "Register",
"events": [
{ "kind": "EventSyntax", "name": "OrderRegistered" }
]
}
]
}
]
}
]
}
}
]

isPlacement is required on every module and feature. Use false for one written in this document; it is true only when a document was imported into the module, so its top level is the module’s body. Call syntax-schema to see every required member of a kind; a proposal that omits one is rejected with the path of the missing property.

Also supply the returned workspace values as expectedRevision and expectedCatalogRevision, plus formatting: "CanonicalizeTouchedDocuments". The default AST validation is Authoring. Empty collections initialize empty; source locations are supplied by the server.

Reference policy defaults to Safe: new unresolved or ambiguous model references are rejected. For an intentionally incomplete draft, choose referencePolicy: "Draft" and inspect the reported debt. Draft never waives parse, identity or binding-safety checks.

The proposal creates one new document holding a module, a feature, a slice and an event. It does not write the file yet. Treat the readiness it reports as a property of this proposal, not a promise about the model you will build on it. Continue with the review and apply steps below.

Applying the proposal writes application.play:

module Sales
feature Orders
slice StateChange Register
event OrderRegistered

Call read-ast with the current expectedRevision, kind: "SliceSyntax", name: "Register", and includeContent: true. Select the intended occurrence using its source location and parent/document information.

Copy its returned typed node, change description, and submit a replace operation in propose-ast:

  • target: the returned handle, unchanged.
  • node: the updated typed node.
  • expected: optionally the original typed node for an explicit structural expectation. Otherwise the handle’s exact base revision supplies it.

If a document has parser errors, it has no editable occurrence handles. Retrieve its document ID through read-workspace, construct a valid ApplicationSyntax, and use a replace-document entry in propose-ast.documents with documentId and node, or supply corrected .play text as source in propose-source.documents. Both preserve its document identity without a text patch.

Use add with a parent handle and typed member such as events to add an element. Use remove for a selected occurrence and move for a different existing parent or document. Read the member schema before choosing a destination slot.

Node handles identify occurrences in one revision. A module or feature split across files has several occurrences, not one magic writable location. For a logical header rename, update all fragments in one batch; a partial rename is rejected.

Use propose-move rather than a manual AST move when moving a slice to another feature or a feature to another module or feature. Copy its target and newParent {kind, parts} addresses from the workspace identity inventory and supply expectedRevision and expectedCatalogRevision. Optional occurrence handles are guards, not substitutes for the logical addresses. Declaration moves between slices are deferred.

The planner moves all fragments, computes every assigned semantic and event migration and repairs qualified references, including depends on. It does not rename events, change their pins, or move files. It refuses identity loss, capture, collisions, descendant destinations, inherited authorization and screen interaction changes. Placing imports must be literal paths with one destination; a split destination needs a single own-named file. Globs are not rewritten by guessing.

Review moveReport.identityMigrations, the empty retired list, referenceRepairs and fragmentsMoved in the response or read-proposal. Formatting defaults to PreserveTrivia and never falls back to canonical output; choose CanonicalizeTouchedDocuments explicitly if needed and inspect dropped comments. Apply with the existing apply tool. Run expand-layout separately to realign files after the move.

For a supported declaration, call propose-rename with its handle, exact expectedName, newName, and both workspace revisions. The default formatting is PreserveTrivia: verified identifier patches leave comments, line endings, BOM and unrelated text intact. Event renames update typed consumers, including constraint released by references and screen/behavior on event triggers.

Event pins are carried as storedName by ESM v16; consumers must explicitly admit that version. The compatibility eventPinWarnings array is empty. See the rename reference and review the candidate’s binding diagnostics.

Event sources and streams, examples and specifications are also rename targets. Source and stream renames repair bound command and specification routes and migrate catalog entries atomically, including a source’s owned streams. Existing pins stay unchanged. No pins are added automatically; add id with a typed edit before renaming a declaration with stored events. An unpinned rename preserves catalog identity but changes its stored name. Renaming an example updates only the steps resolved to that declaration, including qualified names and when append; same-named examples in other scopes remain unchanged. Renaming its underlying event, command or read model updates the example’s type reference. Renaming a composite-type property updates structured keys in example bodies and step overrides. Examples remain front-end syntax and receive no ESM identity. Use find-fixtures to inspect effective values and origins before editing an example shared by several specifications.

The planner coordinates logical fragments, repairs proven typed references and preserves assigned descendant/event identities. It refuses ambiguous targets, name capture, opaque impact and unsupported spans. Do not treat a refusal as permission to perform a global text replacement. Use explicit typed operations and migrations only after resolving the uncertainty.

A structured value such as lines = [{"sku":"A-1","quantity":2}] has typed object keys. You can rename the declared composite type property sku; the planner rewrites only matching keys bound to that type, preserving other keys, string values and source trivia. Other property declarations are not automatic rename targets. Opaque expressions, code blocks and imports still block a rename only if their text contains the old or new name as a whole identifier, including inside a string or key. The conflict message gives the file, line and column, for example 'Shop/Orders/PlaceOrder/PlaceOrder.play(22,11)', and the name it matched.

Put related node edits in one operations array and document creations/moves in documents. Every handle refers to the same base snapshot. Independent edits in one file are combined before printing, and the final complete application is validated once the batch is composed.

To create a destination document containing an existing element, use its typed subtree in create-document and remove its original occurrence in the same proposal. No intermediate, broken source is published.

When working below the automatic rename planner, assigned declaration changes require explicit identity migrations:

  1. Read semantics and, for events, eventContracts through read-workspace.
  2. Include the corresponding previousAddress/currentAddress objects in semanticRenames and eventRenames.
  3. Update affected typed references and assigned descendant addresses in the same batch. No tool globally replaces names in descriptions, code or literals.

Use retiredSemanticAddresses and retiredEventAddresses when removing assigned declarations. An event has both assignments: preserve it in both semanticRenames and eventRenames, or retire it in both retirement arrays. Include every assigned descendant, not just the parent whose address changed.

An InvalidIdentityMigration refusal returns identityMigrationIssues. Each item contains the exact address (kind and typed parts, in the input schema’s shape) and arguments, the arrays to correct. A stale rename names both endpoints and its rename array; an invalid retirement names its retirement array. An unexplained removed assignment lists the rename and retirement arrays as alternatives: choose a rename to preserve identity, or a retirement only for a removed declaration. The conflict message also names these addresses and arrays. No proposal is retained and no source is written on refusal.

If edits overlap, replace their common containing subtree instead of sending conflicting parent/child operations. See the authoring contract.

syntax-schema describes concept pii/personal and secret markers as admitted by ESM v13, including personal special/criminal qualifiers and secret scope. Preserve the AST wire names pii and sensitive. Reasons are report-only. Personal takes precedence over secret, without a stored secret scope next to personal. export-executable-model exports v13 only after binding succeeds: identity/enum compliance and events combining personal or explicitly subject-scoped secret values with an explicit subject mark are refused. Subject marks have no ESM bytes, and reference specifications retain their outcomes. Consumers must explicitly admit v13; see Concepts.

syntax-schema describes caller-supplied equality filters on collection queries as admitted by ESM v14. Filter names match scalar primitive/concept/enum read-model properties; their types match ignoring optionality. Optional filters may be omitted, but supplied null is refused. Filters combine with by using AND; policies still see only by. Context-sourced and single-result filters, explicit scopes and performers remain refused with PLAY0268. Query specifications match collection rows by read model and key without regard to order. Consumers must explicitly admit v14; see Queries.

Use read-ast to find an unnumbered enum’s ConceptSyntax handle, then call propose-number-enum-members with subject, expectedRevision, expectedCatalogRevision and formatting: "CanonicalizeTouchedDocuments". It writes = 0..n-1 in current declaration order and never renumbers numbered, mixed or invalid enums. This opt-in refactoring is suggested by Information hint PLAY0671 for stored enums, reported only when screenplay_diagnostics selects checks: "enum-pinning", PLAY0671 or all; it is not a diagnostic-backed correctness repair. Numbering errors PLAY0667–PLAY0670 need a deliberate author decision and have no automatic repair.

The proposal requires unchanged catalog assignments, retained comments and unchanged executable bytes when the original model binds. Review with read-proposal, then explicitly apply; proposing alone writes nothing. Positional pins keep the executable version, bytes and revision. Non-positional numbers bind as ESM v12; consumers must explicitly admit v12 before accepting these models.

Use read-ast to find the inline EventSyntax handle, then call propose-extract-inline-event with subject, expectedRevision, expectedCatalogRevision and formatting: "CanonicalizeTouchedDocuments". This is an explicit refactoring, not a diagnostic repair. The declaration moves into the owning slice; an omitted inline destination becomes for <identifier>. An existing destination stays unchanged. Event metadata and tags move with the declaration, while production and mapping comments remain attached to their intent.

Extraction requires byte-identical canonical ESM, unchanged catalog assignments and every comment preserved exactly once. It refuses unsupported or unbindable models rather than guessing. Extract before adding generation; parser-invalid inline generations cannot be edited through occurrence handles. There is no reverse inlining operation. Review and apply the returned proposal as any other edit.

Call read-workspace with view: "diagnostics" to inspect source diagnostics, then view: "repairs" at the same current revision. read-ast reports parser diagnostics only; compilation diagnostics belong to the paged diagnostics view. Available typed repairs include:

DiagnosticProposal
PLAY0166 on command producesAdd an event declaration in the producing slice. Types come from command property paths, retaining concepts, or from $context.occurred as DateTime. Uncertain types, conflicting producer shapes, imported/already-declared events and cross-file producers have no repair. Parser errors in any workspace document also block inference.
PLAY0478 (Information)Replace a plain production with an explicit for <identifier>. This deliberately selects the identifier rather than preserving allocated-identity routing. Optional or collection identifiers have no repair. Both models must be executable; a change to the language/semantic version or any other production’s effective destination refuses the repair.
PLAY0469 on an inline mappingRemove the payload property and its mapping together, retiring the property address. The label says “changes the event contract”; canFixAll is false. This narrow repair requires an executable model without other consumers of that event (including constraint releases, projection subscriptions and on event triggers) or opaque syntax/attachments. It refuses changed routing or lost comments. Plain productions receive guidance only.
PLAY0471Remove a redundant event id, inline or standalone, only when the executable model, catalog and comments are preserved.
PLAY0397 on validate csharpReplace the validation with itself so canonical printing migrates its legacy fence. Other forms of PLAY0397 have no individual repair.
PLAY0653 (Warning)Remove duplicate compliance markers from one concept header, keeping the first occurrence of each marker, all body settings and trivia. Use the discovered concept subject and PreserveTrivia; no document-root repair is offered.
PLAY0565 (Information)Migrate one legacy compliance line or the entire document to bare pii/secret, retaining quoted notes and trivia. For one line, pass its discovered location.line as line alongside the concept subject.
PLAY0479 (Information)Write optional after the type. An occurrence repair changes one type; a document repair contains all spelling changes in one transaction. Both preserve syntax structure.
PLAY0683, PLAY0684Raise NanoID size to the smallest size reaching 64 random bits for its alphabet, or bytes count to 8. Use the concept subject, even across documents, with CanonicalizeTouchedDocuments.
PLAY0683Add a unique constraint in the command’s slice only when exactly one production maps the generated command property to exactly one event property. Use the command-property subject; identifiers have no constraint repair.
PLAY0516 (Information)Move a sibling declaration or explicit file import, or pin an already placed file before a retained glob. The proposal removes the selected backward edge without new timeline findings. Own-sub-feature findings, cycle groups, unranked members and mixed/different-parent boundaries have no repair.

PLAY0661 completes a partial command-read key when every missing part has one same-named compatible source. It preserves authored part order and requires canonical formatting consent. Queries and trigger reads are not repaired. PLAY0660 and PLAY0662 have no automatic repair. propose-property handles non-key read-model properties. Key-part rename remains unsupported; propose-rename still refuses all read-model property handles.

Discovery verifies listed PLAY0166, PLAY0478, PLAY0469, PLAY0471, PLAY0479, PLAY0516, PLAY0563, strict-condition PLAY0564, PLAY0614, PLAY0653, PLAY0661, PLAY0683 and PLAY0684 repairs. The generation repairs require individual review and canonical formatting and do not support pinned evidence; the concept and command-property subjects distinguish the two PLAY0683 actions. PLAY0614 adds the one direction inbound|outbound consistent with the slice’s events and constructs; it is offered only when exactly one direction fits and requires individual review. Interaction repairs expand a one-line alternative or replace opaque where with one block-form when, respectively; both require individual review and refuse trailing-comment relocation. It checks authoring acceptance and comment preservation, plus routing safety for PLAY0478, consumer/routing impact and executable readiness for PLAY0469, and executable-model/catalog preservation for PLAY0471. For example, an inferred event that conflicts with a specification’s asserted fields is not listed. PLAY0479 verifies all spellings together once per document and snapshot; occurrence repairs are offered only when that document migration passes. PLAY0516 preserves existing catalog assignments, executable readiness, documents, placements, comments and simulated presentation ranks. When both models bind, their ESM bytes must match. When neither binds, merged syntax must match modulo only sibling modules/features/slices/import order and approved explicit import pins; admission diagnostics must retain the same code/severity multiset. One-sided model availability and new errors/warnings are refused. Only already placed files at the same placement may be pinned, and the glob remains. A prefix can require several pins; no other nodes may be added. A multi-pin proposal is one typed replacement of the import’s parent container that keeps every existing node and comment and only inserts the pins before the glob. In a fresh workspace, a proposal may establish missing document assignments using only the documents’ existing IDs and stable keys. Every existing assignment, origin and event contract revision must remain unchanged; any other catalog change refuses the repair. Discovery and proposals do not persist these assignments; only apply does. Rediscover after each applied move or pin instead of combining recipes from one snapshot. canFixAll is true, but each proposal is still verified. The MCP pinned-evidence path remains unsupported for PLAY0516 (UnsupportedRepair); there is no TypeScript quick fix or VS Code pinned-evidence action for it.

PLAY0397 discovery identifies the recipe only; its proposal may still be refused. Only acceptance and conflicts for verified repairs are cached per subject on the current immutable workspace snapshot for discovery reuse; diagnostics are never cached. Paging or rereading reuses the verdict; a new snapshot requires fresh verification. propose-repair always verifies only the selected subject in one fresh authoring transaction and returns its full diagnostics, even after discovery.

An unknown code, subject or recipe returns the UnknownRepair argument error. A matched repair that fails verification instead returns success: false with typed conflicts, authoringDiagnostics and executableDiagnostics, without a proposal ID. InvalidOperation remains a transaction conflict, including refusal to change another production’s routing or the model version; it does not mean the repair is unknown.

Pass the selected repair’s diagnosticCode and subject to propose-repair with both current revisions and the returned requiredFormatting. For PLAY0479, PLAY0565 and PLAY0653, this is "PreserveTrivia": only selected type or compliance spellings change. PLAY0565 line repairs require the discovered line when several diagnostics share a concept subject; a document-root subject migrates every legacy compliance line. Notes and legal text never move. Choose scope: "document" in the discovered results to migrate the whole document using its root subject handle; pass that handle, not a scope argument, to propose-repair. All splices are verified together in one transaction. The server reparses the candidate, requires unchanged syntax structure, and checks that the selected diagnostics have disappeared. Comments, alignment, line endings and the UTF-8 BOM are retained. "CanonicalizeTouchedDocuments" remains an explicit opt-in.

Other listed repairs require "CanonicalizeTouchedDocuments" and reprint the entire touched file, so whitespace and other legacy fences can change. A proposal that would drop any comment, or duplicate one, is refused. No file is written until you review the before/after bytes with read-proposal and explicitly call apply. After external edits, reopen and rediscover repairs rather than reusing stale handles. Applying a repair uses the same identity state and recovery contract as other proposals.

Use the ordinary typed AST transaction to add or change scenarios; no separate specification-edit operation is needed. Open the workspace first and keep both its workspace and catalog revisions.

  1. Call read-ast with expectedRevision, kind: "SpecificationSyntax", name and includeContent: true. If the name has several matches, select the intended document and parent from the returned handles rather than guessing. Read view: "children" with the specification’s documentId and path to get handles for its steps. syntax-schema describes their typed members.
  2. Use propose-ast add/replace/remove operations at those handles. The specification’s given and givenReadModels hold prior facts and state; givenCaller, givenCallerPersona, givenClock and givenCaptures hold other preconditions. when, whenAppended, whenQuery, whenClock, whenTrigger, whenCapture and whenRedelivered describe the action. Expectations live in thenEvents, thenErrors, thenReadModels, thenAbsentReadModels, thenQueries, thenResults, thenNoResult, thenReturns, thenDenied and thenNoEvents. Table scenarios use parameters and cases; case values are typed PropertyMappingSyntax assignments. examples holds retained document-scoped example declarations; event steps reference a type or example through eventType, command steps through commandType, and read-model steps through name. Their values (events/commands) or properties (read models) hold authored overrides. For one value, replace its PropertyMappingSyntax with formatting: "PreserveTrivia" to splice only the changed expression. For structural edits, use the formatting mode the transaction supports.
  3. When removing an assigned specification, include its semantic address in retiredSemanticAddresses; include every assigned derived case address for a table. Removing source does not silently retire its catalog identities.
  4. Review read-proposal with the returned proposalId and view: "semantic-diff". Look at the paged change items whose section is specifications for additions, removals and changed expected outcomes. This is a static expectation comparison, not execution; also inspect the changed source and diagnostics.
  5. Call run-specifications with proposalId, optionally selecting a specification address/semantic id or scope. It runs the retained candidate and its catalog, not the current model. Continue with the same proposal ID and pin its returned sourceRevision as expectedSourceRevision. An unbound candidate returns executable diagnostics; failed and unsupported scenarios are not passes.
  6. Once the candidate passes and you have reviewed the change, call apply with proposalId and the before workspace/catalog revisions. Check success, status and recovery, then read fresh handles.

Candidate execution never applies, writes or mutates the proposal. An externally changed base does not replace the retained snapshot, but it still makes apply stale. Reopening the workspace discards outstanding proposals.

An accepted proposal returns a proposalId, before/after revisions, changed-file count, normalization disclosure, the number of dropped comments and executable readiness. Acceptance is not a filesystem effect.

To change a specification value or a produces mapping without touching anything else, replace its PropertyMappingSyntax with formatting: "PreserveTrivia". Changing channel = "web" to "store" rewrites only "web"; every comment, blank line and declaration stays where it was. CanonicalizeTouchedDocuments reprints the whole document instead: attached comments stay with their syntax owners, while blank lines are normalized. Existing members from the same parsed document retain their authored order, including when an edited member is replaced through typed JSON. Newly authored members without comparable source positions follow the canonical insertion rule; see Printing and generating. The dropped-comments view lists only comments that cannot be placed. An edit that keeps all comments reports zero dropped comments.

Every proposal result carries a one-paragraph summary, also returned as a second text block after the JSON. It says which documents change, which modules, features, slices, commands, events, read models, screens and reactions the proposal adds or removes, what the authoring checks reported, how many comments are dropped and whether the result is executable. Use it to report a proposal; review the details below before apply.

The proposal’s authoringDiagnosticCount and the after.readiness counts answer different questions. authoringDiagnosticCount counts the checks run while proposing: printing, parsing, import placement and references. It includes notices such as PLAY0288, the warning that a touched document was printed canonically. after.readiness counts the diagnostics of the resulting source alone, which is what apply reports. authoringDiagnosticSeverities splits the proposal count into errors, warnings and information and lists the distinct codes; call read-proposal with view: "diagnostics" to page them.

  1. Call read-proposal with its ID and view: "changes". Check the proposal’s droppedCommentCount; when it is not zero, call read-proposal with view: "dropped-comments" to see each comment a changed document loses, with its path, line, column and text.
  2. For each changed document, retrieve before and after byte pages by documentId. Base64 pages reconstruct exact source bytes, including BOM and Unicode. Inspect authoring and executable diagnostics separately. The proposal’s introducedExecutableErrors lists only the executable-model errors this proposal adds, each with code, message, path, line and column; errors the model already had are not repeated. When the list is not empty, executableGuidance says to fix them in a new proposal before apply.
  3. Inspect stateChange and, if needed, retrieve exact before/after identity state with workspace-state, the proposal ID and corresponding state revision.
  4. Call apply with the proposal ID and the before workspace/catalog revisions.
  5. Check success, status and recovery. Source and .screenplay/identities.json are installed together under the recovery envelope. Keep both in source control.

Canonical export-workspace is available for transport or backup. It is no longer required for a normal fresh server session to retain identities.

An external edit or stale revision rejects apply. Interrupted operations retain .screenplay/pending.json. Inspect workspace-state and explicitly call recover-workspace with the reported operation ID when rollback is safe. Unexpected external bytes block recovery rather than being overwritten. This is not simultaneous crash-atomic visibility across filesystem paths.

After a successful apply, old handles and outstanding proposals are stale. Read fresh handles against the returned revision. A later session loads the root-local identity state automatically. A conflicting import or corrupt state is rejected, not silently replaced.

Call recommend-layout for advice, then expand-layout with layout set to single, module, feature or slice (the default). Use the current revisions and explicit formatting consent. For full-language models, set validation: "Authoring".

The existing layout values also select how finely to split the model:

LayoutFiles
singleOne self-contained application.play, with file imports inlined.
moduleapplication.play imports <Module>/<Module>.play; each module file holds its features and slices.
featureModule files import feature barrels; each feature file holds its slices and imports its nested features.
sliceFeature barrels additionally import <Slice>/<Slice>.play; each slice file contains only its slice, with no module or feature restatement.

Split layouts use quoted imports instead of the older merge-only scope restatements. Parameters and destination paths are unchanged: use layout: "slice" for one file per slice, not a separate flag. Each parent imports its children explicitly in declaration order rather than relying on alphabetical glob expansion. The Commerce sample shows the same barrel idea with flat slice files. The generated layout puts slices in <Feature>/<Slice>/<Slice>.play and inlines shared imports into application.play. TimeTracking demonstrates inline feature barrels in module files. Validate the whole folder or the generated application.play, not a slice fragment in isolation. There is no CLI expand-layout command; expansion is a reviewable MCP proposal.

Expansion selects the same ordering root as the timeline: the sole document when there is one, otherwise an importing application.play, or the unique importing document that is not itself imported. An import-less application.play does not hide a lone importing barrel beside it. Before proposing a layout, expansion checks that each scope keeps its relative module, feature and slice sibling order; a container declared in several files ranks where its owner file places it: the root if the root declares it, otherwise a file that declares it and is named after the container or one of its ancestors, the outermost such name winning. Without such a file it ranks at its first declaration in import order. Repeated sibling names count once. A mismatch fails without creating a proposal. With no ordering root, that guard is skipped and the proposal’s review text says that path order was used. Order is presentation metadata, not executable-model bytes or identity. A child imported into a container keeps its parent-relative position among contributions, templates and other members when the layout is collapsed. Leading and trailing header comments stay on the physical declarations, including the first line of placed files.

Review and apply the resulting proposal exactly like a node edit. Reorganization normalizes source formatting and checks structural equivalence; it keeps annotations such as // @public beside the declaration they describe, even when a declaration moves to a new document. Existing file imports are replaced by the new composition; comments attached to those old imports are retained in their owning scope. Semantic and event identities must pass the same continuity checks as any other proposal. The dropped-comments view compares comments across the whole plan, not just documents changed in place. Expansion does not copy module forms or contributions into scaffolding. The read tools continue to see one logical application regardless of the chosen layout.