Modules, Features and Slices
Module
Section titled “Module”The module is the top-level namespace and maps to a bounded context. One module per file is the convention but not enforced.
module <Name> [description "<text>"] [depends on <Name>]*
[<screen templates>] [<dialog templates>] [<features>]Screen templates and dialog templates declared at module level are described in Layouts and templates.
Features
Section titled “Features”Features are vertical slice groupings. They nest arbitrarily deep for sub-features.
feature <Name> [description "<text>"] [depends on <Name>]* [feature <Name>]* ← sub-features [slice <type> <Name>]+Declared dependencies
Section titled “Declared dependencies”Use depends on <Name> on a module or feature to state which other modules or features it intends to use. Write one target per line:
module Payroll description "Pays people for approved time" depends on Timesheets feature Handover depends on Timesheets.Approval depends on Timesheets.Reporting depends on Runs feature Runsmodule Timesheets feature Approval feature ReportingA bare name searches the declaring container’s siblings, then each ancestor’s siblings, then root modules. It does not search cousins or features in another module: qualify those targets. A dotted name matches an unambiguous trailing part of a container’s full address. Equally near matches raise PLAY0198 and name the candidates.
A sibling feature shadows a same-named root module. That root module has no longer qualifying address, so rename one of the containers to make it addressable. Self, ancestor, descendant and unresolved targets raise warning PLAY0554. Repeated declarations of the same resolved target (including different spellings such as Runs and Payroll.Runs) raise warning PLAY0555 and keep the first. Unresolved repeats compare by target text.
A container without declarations is not checked. Once you declare a dependency, that container’s own list must cover every counted explicit reference leaving it, including references from descendant features. A module target covers all its features; a feature target covers its descendants. Parent and child inventories are independent: neither satisfies the other. References within the checked container, or into an ancestor’s own slices, are ignored.
The check counts usesFactsFrom, reactsTo, decidesFrom, asks and shows, not specification (verifiedWith) or outside-context (outsideTheModel) edges. It reports one warning PLAY0552 per checked container and uncovered producer module, at the container header, naming the shared feature path when possible and listing source evidence. An unused valid declaration gets information PLAY0553 on its line. Two containers declaring each other get information PLAY0556 on each declaring line; this does not permit cycles or prohibit reverse coupling.
Ambiguous graph ownership never produces an undeclared warning. Any declaration covering a candidate is provisionally satisfied and is not reported as unused. The MCP declarations view shows this uncertainty. The graph covers explicit slice references only, not code, expressions or module-owned forms.
Declarations are optional authoring metadata, not execution or ordering rules: they add no executable model bytes and do not change revisions or identities. The printer places them after description, in authored order. Folder models accumulate them per owner; module and feature rename repairs proven targets and refuses unresolved or ambiguous targets that could name the renamed container, or a rename that would capture another target.
Slices
Section titled “Slices”The slice is the atomic unit of behavior, aligned with Event Modeling. A slice has a type and a name, and contains the constructs that implement the behavior.
slice <SliceType> <Name> [direction inbound|outbound] // Translate only [description "<text>"] [file <path>] <constructs>The optional file line names the repository relative file this declaration is realized by, so a document can be navigated back to the code it describes. It is additive - it never stands in for any part of the declaration. See File references.
Descriptions and documentation
Section titled “Descriptions and documentation”Use description for a human-readable summary. On a specification, it names the rule or case that scenario witnesses. Use documentation for longer reasoning: assumptions, boundaries and rejected alternatives that should stay with the model.
Modules, features, slices, commands, events, read models and reactions accept one nonempty documentation block with a markdown fence. It belongs directly in the declaration body, not under a reaction trigger. A bare documentation line never declares a property; documentation String still declares a typed property where properties are allowed.
module Invoicing description "Bills customers" documentation ```markdown ## Boundary Invoicing records billing decisions. Delivery is a separate workflow. ```Both fields are report-only authoring metadata (PLAY0270), not executable conditions. They do not add executable-model bytes or require a new ESM version. The printer keeps them on their owning declaration, and MCP declaration-details includes both fields in its summary wherever supported. In a folder model, the first module or feature documentation is kept; identical copies are accepted, while conflicting copies warn with PLAY0559.
Malformed, empty or repeated documentation on these new owners reports PLAY0558; events retain PLAY0477. Other kinds, including projections and screens, do not accept documentation. These diagnostics have no automatic repair: choose the text to retain, the correct fence language or the owning file explicitly.
Descriptions
Section titled “Descriptions”Modules, features, slices, personas, commands, read models, reactions, and specifications take an optional description as their first body line — a human-readable summary consumers such as Prologue surface when presenting the model. At most one per declaration.
module Invoicing description "Everything related to invoicing customers"
feature InvoiceManagement description "Registering and managing the lifecycle of invoices"
slice StateChange RegisterInvoice description "Registers a new invoice"When one line is not enough, use a fenced block — the same ``` convention as inline code blocks. The fenced text is kept verbatim:
module Invoicing description ```text Everything related to invoicing customers. Registration, lifecycle and payment tracking of invoices. ```Slice types
Section titled “Slice types”| Type | Description |
|---|---|
StateChange | A command → events flow; something that changes the system |
StateView | A query + projection + screen; something that reads the system |
Automation | A reaction or reducer; something that runs when something happens |
Translate | Translates outside occurrences to private local facts (inbound), or private facts to a public contract (outbound) |
Translation direction
Section titled “Translation direction”Declare direction inbound or direction outbound once directly inside a Translate
slice. Inbound includes existing captures of outside data and translation of another
application’s public events. Outbound describes publishing a local public contract.
Direction on any other slice type, an unknown value, or a repeated directive is an error
(PLAY0027, invalid slice declaration). Legacy directionless Translate slices remain
accepted and are interpreted as inbound; the printer does not insert a directive into them.
SliceSyntax.Direction (direction in TypeScript) is a nullable property preserving whether the author wrote
the directive; EffectiveDirection supplies inbound for legacy translations and null
for other slice types. Both compilers’ printers, typed JSON and the authoring workspace preserve the
explicit value, and Monaco and VS Code complete and validate it. An explicit direction selects ESM v9 and is part of the executable model; a legacy translation without one keeps its version and bytes. The reference evaluator implements publication as a recorded fact only: no subscription, delivery or Stage behavior. Public/private usage constraints and translation
cardinality are checked by PLAY0607-PLAY0620 (diagnostics); PLAY0614
has an editor and MCP repair that declares the one consistent direction.
An outbound slice publishes its one public event with a projection/reducer whose =>
target is that event (projecting to an event),
or with a reaction. An inbound slice may consume another application’s public events with
source events. Both select ESM v9.
What goes in a slice
Section titled “What goes in a slice”| Construct | Typical slice type | Page |
|---|---|---|
event | any | Events |
command | StateChange | Commands |
constraint | StateChange | Constraints |
query | StateView | Queries |
readmodel | StateView | Read models |
projection | StateView | Projections |
reducer | StateView | Read models |
screen | StateView | Screens |
reaction | Automation | Reactions |
capture | Translate | Captures |
Every one of these may appear as many times as the behavior needs — several events, several commands, several projections. Only description is limited to one. A slice is one behavior, not one artifact of each kind.
Example
Section titled “Example”module Invoicing
feature InvoiceManagement
slice StateChange RegisterInvoice command RegisterInvoice ... event InvoiceRegistered ...
slice StateView InvoiceList query ListInvoices => InvoiceListReadModel[] projection InvoiceList => InvoiceListReadModel ... screen InvoiceList ...