Skip to content

Captures

Captures convert external data into events — change data capture as a first-class construct. The body of a capture block is written in the Change Data Capture Language (CDL) — an embedded sub-grammar the Screenplay parser delegates to (see Sub-language Pluggability). Captures live inside Translate slices.

capture <Name>
<CDL grammar>
capture LegacyInvoiceCapture
source api
api LegacyInvoicingApi
route /invoices
poll 5m
key id
map
status = status translate
"utkast" => draft
"sendt" => sent
"betalt" => paid
"kansellert" => cancelled
split contactName by ","
firstName
lastName
append InvoiceStatusChanged
when status
invoiceId = $.id
status = $.status
changedAt = $context.occurred
append InvoicePaidFromSent
when status from "sent" to "paid"
invoiceId = $.id
paidAt = $context.occurred
children lineItems identified by lineNumber
map
productName = name
append InvoiceLineItemAdded
when added
invoiceId = $.id
lineNumber = $.lineNumber
quantity = $.quantity
unitPrice = $.unitPrice
append InvoiceLineItemRemoved
when removed
invoiceId = $.id
lineNumber = $.lineNumber
nested billingContact
map
contactName = name
append BillingContactUpdated
when email
invoiceId = $.id
email = $.email

An inbound Translate slice turns the public events of another application into private events of its own. The capture reads them with source events, and the captured item is the incoming event’s content:

slice Translate TrackShipments
direction inbound
event OrderShipped
orderId String
capture ShipmentTracking
source events
from ShipmentDispatched
from ShipmentDelivered
key orderId
append OrderShipped
when status from "pending" to "dispatched"
orderId = $.orderId

One capture reads one origin: all its from events must have the exact same origin string (PLAY0687); split events from different origins into separate captures. Every from event must be a public event with an origin, an append event must be a private event of this application, and source events belongs only to an explicitly inbound slice (PLAY0610, PLAY0612, PLAY0616, PLAY0619, PLAY0620). The form selects ESM v9 and binds to an executable capture over events. The reference evaluator has no outside records to present to such a capture, so a when capture specification over events reports a typed Unsupported outcome instead of passing or failing. Rename follows a from line; see the diagnostics for the full list.

ConstructMeaning
source eventsReads the public events of another application instead of an external system; each from <Event> line names one consumed event.
source api / source webhook / source messageWhere the captured data comes from, with source-specific settings (api/route/poll for api, path for webhook, topic for message).
key <property>Which source property identifies an instance.
mapMaps, splits and translates source values before events are appended.
<property> = <source>Renames a source value onto a property, or assigns a `template` built from source values.
translateMaps source values to Screenplay values ("utkast" => draft).
split <source> by "<separator>"Splits one source value into several target properties.
append <EventType>Appends an event when the source changes, guarded by a when trigger.
tag <value>Attaches a tag to the appended event — a static identifier or string, or a $context. expression.
when <property>Appends when the named property changes.
when <property> or <property> / when <property> and <property>Appends when any (or) or all (and) of the named properties change.
when <property> from <value> to <value>Appends when the named property transitions between two specific values.
when `<expression>`Appends when a raw template-literal expression evaluates to true.
when added / when removedAppends when an item appears in / disappears from the source.
children <collection> identified by <key>Captures changes in a child collection, with its own optional map.
nested <property>Captures changes to a single nullable child object, with its own optional map.
$.<property>A value from the current source item.
$context.occurredThe capture timestamp.

Captures bind to the executable semantic model as ESM v6 (decision 0022). The reference evaluator is handed records - given capture and when capture in a specification - and never contacts a source, which stays realization metadata. For each record:

  • map operations apply in order, each to the record as the ones before it left it. A field map does not touch passes through unchanged.
  • A condition compares the mapped record with the mapped record the capture last saw for the same key. A field the earlier record lacked has changed when the new one has it, so a first record has changed in every field it carries. when status from "sent" to "paid" compares mapped values.
  • when `<expression>` holds a small portable grammar: fields, string, number, boolean and null literals, ==, !=, <, <=, >, >=, &&, ||, ! and parentheses. Ordering compares numbers only.
  • $.<field> reads the item being evaluated - the record, a child or the nested record - and falls back to the enclosing record when the item lacks the field. $context.occurred is the scenario’s given clock.
  • Everything is appended to the event source the record’s key names. Its type is the one the event’s commands give it, and text when no command produces the event.
  • The record’s own appends run first, then each children collection’s - current children in order, then the removed ones in their earlier order - then each nested record’s.

The reference checks record shapes, event-source keys and child identities (including duplicates) before appending anything. After those preconditions pass, it evaluates each reached append in order, enforces its constraints, projects it and settles its reactions before evaluating the next capture effect. The first rejection or Unsupported stops the scenario. Facts and projections from earlier accepted appends remain; the failed append adds neither. A later unsupported mapping or guard cannot mask an earlier constraint rejection. This is not a capture-wide transaction; invoked commands keep their own atomicity.

given capture supplies immutable last-seen input for one specification. Running the specification does not store a new last-seen record or acknowledge an external source, on success or failure. Supply the baseline explicitly in each subsequent specification. Retained prefix facts do not imply successful source processing or acknowledgement. A present object or collection used as a scalar mapping is Unsupported, not a missing value or a passing then error.

A capture without a key, or a condition outside the grammar above, does not bind (PLAY0268).

The authoritative CDL grammar - including the split, nested and richer when forms above - is published as the Capture Declaration Language grammar, mirroring how the Projection Declaration Language grammar documents PDL.