cody-event
v1.0.0Cody Event Skill - Specification for creating Event element details in prooph board. Defines schema, state apply rules, and configuration metadata for business facts that became true.
cody-event
Define Event element details for the Cody Engine low-code platform.
Overview
This skill teaches AI agents how to create detailed specifications for Event elements in Cody Engine, the low-code platform behind prooph board.
Important: This is a Cody Engine-specific skill. It is only useful if you work with the Cody Engine to generate applications from Event Models. If you use prooph board purely for Event Modeling without Cody Engine, this skill does not apply to you.
An Event in Cody Engine represents a business fact that became true as the result of a command. The skill covers how to define the event payload schema, state apply rules that update the aggregate, and configuration metadata.
What This Skill Covers
- Event Type Declaration — Aggregate event classification
- Schema — Event data structure using
cody-schemablocks - State Apply Rules — How the event updates aggregate state using
cody-apply-rulesblocks and the Rule Engine - Configuration Metadata — Event visibility and aggregate flags
Why This Skill
- Correct state transitions — Guides the agent to choose the right state apply pattern (merge, partial update, complete replace)
- Consistent schemas — Ensures event payloads follow the same type system as commands
- Rule Engine knowledge — Teaches proper use of
assignactions and JEXL expressions in apply rules
When to Use
| ✅ Use This Skill | ❌ Skip It |
|---|---|
| Modeling event details for Cody Engine applications | Using prooph board for Event Modeling only (no Cody Engine) |
| Defining how events mutate aggregate state | General code generation for other frameworks |
| Without knowledge of the Cody Engine Rule Engine |
Usage
Once installed, your AI agent will know how to create structured event specifications with schema, state apply rules, and metadata.
Examples
Prerequisites
- Familiarity with the Cody Engine low-code platform
- Understanding of Event Sourcing and aggregate state
- Knowledge of the Rule Engine and JEXL expressions
Cody Event Skill - Event Element Details Specification
This document describes the structure and patterns for creating Event element details in the Cody/prooph board system.
Overview
Events in this system represent business facts that became true. They are the result of commands and represent immutable records of what happened in the domain.
Event details contain structured markdown with code blocks that define:
- Event Type - Aggregate event classification
- Schema - Event data structure
- State Apply Rules - How the event updates aggregate state
- Configuration - Event visibility metadata
Related Documentation
- Rule Engine — Details on the rule engine used for state apply rules (
cody-apply-rules) - JEXL Expressions — Documentation for JEXL expressions used in
cody-apply-rulesandcody-schemacode blocks
Structure Template
```markdown
## Aggregate Event
of aggregate: [AggregateName]
## Schema
```cody-schema
{
"fieldName": "type|constraints",
"optionalField?": "type",
"refField": "/Service/Entity:identifier"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
]
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"aggregateEvent": true,
"public": false
}
```
Detailed Specifications
1. Event Type Declaration
Required for all events
Events are classified by their relationship to aggregates:
```markdown
## Aggregate Event
of aggregate: [AggregateName]
Examples:
```markdown
## Aggregate Event
of aggregate: Order
```markdown
## Aggregate Event
of aggregate: Customer
Note: Some events may have an empty aggregate name (for domain events that don't update state):
```markdown
## Aggregate Event
of aggregate:
2. Schema Definition
Required for all events - defines event payload structure
Schema Syntax
{
"requiredField": "type|constraints",
"optionalField?": "type",
"enumField": "enum:value1,value2,value3",
"refField": "/Service/Entity:identifier",
"nestedObject": {
"field": "type"
}
}
Type Reference
| Type | Description |
|---|---|
string |
Text value |
| `string | format:uuid` |
| `string | format:email` |
| `string | format:date` |
| `string | format:date-time` |
boolean |
True/false |
integer |
Whole number |
enum:v1,v2,v3 |
Enumerated values |
/Service/Entity:id |
Reference to entity |
Schema Examples
Simple Schema:
{
"leadId": "string|format:uuid",
"notes?": "string"
}
Schema with Enum:
{
"leadId": "string|format:uuid",
"status": "enum:took_place,no_show,cancelled"
}
Schema Reference:
{
"$ref": "/App/Lead"
}
{
"$ref": "/App/PreparedContract"
}
{
"$ref": "/App/Contract"
}
Schema with Reference Type:
{
"leadId": "/App/Lead:leadId",
"locationId": "/App/Location:locationId"
}
3. State Apply Rules
Required for all events - defines how event updates aggregate state
State apply rules use the Rule Engine
assignaction to update the aggregate state. Theinformationvariable holds the current state and is replaced with the new value.
Rule Structure
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": { /* state transformation */ }
}
}
}
]
State Transformation Patterns
1. Simple Merge (Event data overwrites state)
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
]
}
}
}
}
]
2. Merge with Additional Fields
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event",
{
"status": "$> 'new'",
"channel": "$> 'website'",
"inquirySubmittedAt": "$> eventCreatedAt"
}
]
}
}
}
}
]
3. Replace State with Event
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": "$> event"
}
}
}
]
4. Update Specific Field
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information"
],
"status": "$> 'contract_handed_over'"
}
}
}
}
]
5. Update Nested Object
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information"
],
"viewingAppointment": "$> information|get('viewingAppointment', {})|set('status', 'took_place')|set('notes', event.notes)"
}
}
}
}
]
6. Set Boolean Flag
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
],
"signed": "$> true"
}
}
}
}
]
4. Configuration Metadata
Required for all events - defines event visibility
Configuration Properties
| Property | Type | Description |
|---|---|---|
aggregateEvent |
boolean | Is this an aggregate event |
public |
boolean | Is this event public (default: false) |
Configuration Examples
With Aggregate Event Flag:
{
"aggregateEvent": true,
"public": false
}
Minimal Configuration:
{
"public": false
}
Expression Syntax
Expressions use the JEXL expression language. The
$>prefix marks a JEXL expression in Cody code blocks.
Available Variables
| Variable | Description |
|---|---|
$> event |
The event data |
$> information |
Current aggregate state |
$> eventCreatedAt |
Timestamp when event was created |
Common Expressions
Property Access:
$> event.leadId
$> information.status
State Transformation:
$> information|get('viewingAppointment', {})
$> information|get('viewingAppointment', {})|set('status', 'took_place')
String Interpolation:
$> 'website'
$> 'new'
$> 'reached'
$> 'not_reached'
$> 'contract_handed_over'
$> 'contract_signed'
Boolean Values:
$> true
$> false
Timestamp:
$> eventCreatedAt
Complete Examples
Example 1: Simple Event with Merge
```markdown
## Aggregate Event
of aggregate: Lead
## Schema
```cody-schema
{
"$ref": "/App/Lead"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
]
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
Example 2: Event with Status Update
```markdown
## Aggregate Event
of aggregate: Lead
## Schema
```cody-schema
{
"leadId": "string|format:uuid",
"firstName": "string",
"lastName": "string",
"email": "string|format:email",
"phone?": "string",
"termsAccepted": "boolean",
"locationId": "/App/Location:locationId"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event",
{
"channel": "$> 'website'",
"status": "$> 'new'",
"inquirySubmittedAt": "$> eventCreatedAt"
}
]
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
Example 3: Event with Nested Object Update
```markdown
## Aggregate Event
of aggregate: Lead
## Schema
```cody-schema
{
"leadId": "string|format:uuid",
"notes?": "string"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information"
],
"viewingAppointment": "$> information|get('viewingAppointment', {})|set('status', 'took_place')|set('notes', event.notes)"
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
Example 4: Event with Boolean Flag
```markdown
## Aggregate Event
of aggregate: Contract
## Schema
```cody-schema
{
"$ref": "/App/Contract"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
],
"signed": "$> true"
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
Example 5: Event that Replaces State
```markdown
## Aggregate Event
of aggregate: Lead
## Schema
```cody-schema
{
"$ref": "/App/Lead"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": "$> event"
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
Example 6: Event with Status Change Only
```markdown
## Aggregate Event
of aggregate: Lead
## Schema
```cody-schema
{
"leadId": "string|format:uuid"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information"
],
"status": "$> 'contract_signed'"
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
Example 7: Event with Reference Type
```markdown
## Aggregate Event
of aggregate:
## Schema
```cody-schema
{
"leadId": "string|format:uuid",
"locationId": "/App/Location:locationId"
}
```
## State Apply Rules
```cody-apply-rules
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
]
}
}
}
}
]
```
## Configuration
```cody-metadata
{
"public": false
}
```
State Apply Patterns
Pattern 1: Full Merge
Merges event data into existing state:
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event"
]
}
}
}
}
]
Pattern 2: Merge with Computed Fields
Adds computed or derived fields:
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event",
{
"status": "$> 'new'",
"timestamp": "$> eventCreatedAt"
}
]
}
}
}
}
]
Pattern 3: Partial Update
Updates only specific fields:
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information"
],
"status": "$> 'reached'"
}
}
}
}
]
Pattern 4: Nested Object Update
Updates nested object with get/set pattern:
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information"
],
"nestedField": "$> information|get('nestedField', {})|set('property', event.value)"
}
}
}
}
]
Pattern 5: Complete Replace
Replaces entire state with event:
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": "$> event"
}
}
}
]
Common Status Values
Based on the Lead Gen domain:
| Status | Description |
|---|---|
new |
Newly created lead |
reached |
Lead was reached by phone |
not_reached |
Lead could not be reached |
contract_handed_over |
Contract was given to lead |
contract_signed |
Lead signed the contract |
abandoned |
Lead was abandoned |
Viewing Appointment Status
| Status | Description |
|---|---|
scheduled |
Appointment is scheduled |
took_place |
Appointment happened |
no_show |
Lead didn't show up |
cancelled |
Appointment was cancelled |
Event Naming Conventions
Events should be named in past tense as they represent facts that happened:
Good:
Lead SubmittedLead reached by phoneContract SignedViewing Appointment took placeLead Did Not Show Up to Viewing Appointment
Avoid:
Submit Lead(present tense - sounds like command)Signing Contract(continuous - not a fact)
Checklist for Creating New Events
- Determine aggregate name
- Define schema with all event fields
- Use past tense for event name
- Choose appropriate state apply pattern
- Add status updates if needed
- Handle nested object updates with get/set
- Set configuration metadata
- Mark as
public: falsefor internal events - Use
eventCreatedAtfor timestamps
Comparison: Command vs Event
| Aspect | Command | Event |
|---|---|---|
| Tense | Imperative (Submit Lead) | Past (Lead Submitted) |
| Purpose | Request action | Record fact |
| Handler | cody-rules with record |
cody-apply-rules with assign |
| Validation | Can throw errors | Always succeeds |
| State | Triggers state change | Applies state change |
Special Variables in Apply Rules
| Variable | Context | Example |
|---|---|---|
information |
Current aggregate state | $> information.status |
event |
Event payload | $> event.leadId |
eventCreatedAt |
Event timestamp | $> eventCreatedAt |
Advanced Patterns
Pattern: Conditional Status Based on Event Data
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event",
{
"status": "$> event.channel == 'phone' ? 'reached' : 'new'"
}
]
}
}
}
}
]
Pattern: Preserve Existing Data While Updating
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
{
"viewingAppointment": "$> information|get('viewingAppointment', {})|set('status', event.status)"
}
]
}
}
}
}
]
Pattern: Merge Event with Override
Event data takes precedence, then add computed fields:
[
{
"rule": "always",
"then": {
"assign": {
"variable": "information",
"value": {
"$merge": [
"information",
"event",
{
"updatedAt": "$> eventCreatedAt",
"updatedBy": "$> meta.user.id"
}
]
}
}
}
}
]