---
title: Authoring with the MCP tools
editUrl: https://github.com/Cratis/Screenplay/edit/main/Documentation/screenplay/mcp/authoring-tools.md
description: The typed propose, review and apply workflow with complete tool arguments, for client authors and for understanding what an assistant did on your behalf.
---


This page shows the tool calls behind [Create a model](/screenplay/mcp/create/) and
[Edit a model](/screenplay/mcp/edit/): 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](/screenplay/mcp/reference/). Calls below are MCP tools, not shell commands.
The server root is the application boundary; file layout does not create separate
applications.

## Open the application

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.

## Choose a proposal tool

| Tool | Use it for | Validation |
| --- | --- | --- |
| `propose-source` | Whole `.play` text documents, including syntax-only language constructs | `Authoring` by default; optional `Executable` |
| `propose-ast` | Focused typed node edits using `read-ast` handles, or typed whole documents | `Authoring` by default; optional `Executable` |
| `propose` | Exact byte replacements and legacy whole-document operations | Executable-only |
| `propose-property` | Add, rename or remove explicitly selected properties along proven mapping chains | `Authoring` by default; optional `Executable` |

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

### Propose whole source documents

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:

```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`:

| Operation | Fields in addition to `operation` |
| --- | --- |
| `create-document` | `path`, `stableKey`, `source`; optional `encoding`: `Utf8` (default) or `Utf8WithBom` |
| `replace-document` | `documentId`, `source` |
| `remove-document` | `documentId` |
| `move-document` | `documentId`, `path` |
| `rename-document-key` | `documentId`, `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.

## Plan a property change

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](/screenplay/mcp/edit/#add-a-property-along-a-mapping-chain).

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](/screenplay/mcp/reference/#property-change-planner).

## Inspect inline event declarations

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](/screenplay/identity/) 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.

## Read-model key authoring

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](/screenplay/readmodels/#keys).

## Event source and stream authoring

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

| View | Contents |
| --- | --- |
| `event-sources` | Application-owned source keys, identifier types, pins and physical handles |
| `event-streams` | Exact source-owned stream keys, key types, pins and ownership status |
| `event-source-details`, `event-stream-details` | Exact `authoringKey`; compact header with metadata sizes and source/AST read pointers, followed by independently paged children; rejects ambiguous or incomplete ownership |
| `command-routes` | Authored command routes, production override references and retained ambiguity candidates, never inferred effective routing |
| `event-source-diagnostics` | Paged 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](/screenplay/ast-authoring/#source-and-stream-edits). 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.

## Operation and system intent

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`:

| View | Contents |
| --- | --- |
| `system-intents` | Application-scoped systems, descriptions through details, kind/full-scope authoring keys and source occurrence handles |
| `operation-intents` | Slice-owned operation keys, uses, input counts, execute/compensate state and selected source handles |
| `system-intent-details`, `operation-intent-details` | Supply the inventory's `authoringKey`; pages contain declaration intent, typed inputs, phases and ordered hints |
| `ordered-productions` | Command 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](/screenplay/operations/#promote-an-inline-operation-manually) requires coordinated typed edits and full-source validation.

## Create the first typed document

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`:

```json
[
  {
    "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`:

```screenplay
module Sales

  feature Orders

    slice StateChange Register

      event OrderRegistered
```

## Read and edit existing elements

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.

## Move across logical parents

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.

## Rename without hand-editing references

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](/screenplay/mcp/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.

## Coordinate changes across files

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](/screenplay/ast-authoring/).

## Concept compliance admission

`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](/screenplay/concepts/).

## Query filter admission

`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](/screenplay/queries/).

## Number enum members

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.

## Extract an inline event

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.

## Fix a diagnostic

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:

| Diagnostic | Proposal |
| --- | --- |
| `PLAY0166` on command `produces` | Add 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 mapping | Remove 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. |
| `PLAY0471` | Remove a redundant event `id`, inline or standalone, only when the executable model, catalog and comments are preserved. |
| `PLAY0397` on `validate csharp` | Replace 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`, `PLAY0684` | Raise 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`. |
| `PLAY0683` | Add 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](/screenplay/mcp/recovery/) contract as other proposals.

## Edit a specification

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.

## Review and apply

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](/screenplay/printing/#what-printing-does-not-keep). 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.

## Change the layout

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:

| Layout | Files |
| --- | --- |
| `single` | One self-contained `application.play`, with file imports inlined. |
| `module` | `application.play` imports `<Module>/<Module>.play`; each module file holds its features and slices. |
| `feature` | Module files import feature barrels; each feature file holds its slices and imports its nested features. |
| `slice` | Feature 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.
