Guides
Process files
Name the steps of a process and the order you expect. The editor catches a wrong name.
A process file describes one process in your data: the activities you care about, and the order you expect them in. ocelkit counts what really happened and draws it as a map.
Here's the payments file from the introduction, with one more pair:
import { defineProcess } from 'ocelkit'// Do customers pay once we confirm their order, or only after a reminder?export default defineProcess({ name: 'Order payments', activities: { confirmOrder: 'confirm order', paymentReminder: 'payment reminder', payOrder: 'pay order', }, flows: { orders: [ ['confirmOrder', 'payOrder'], ['confirmOrder', 'paymentReminder'], ['paymentReminder', 'paymentReminder'], ['paymentReminder', 'payOrder'], ], },})Let's break this down:
- The file lives in your
processesfolder. Every.tsfile there is one process, and its default export is the result ofdefineProcess. nameis the title of the map.activitiesnames the event types you care about.flowslists the pairs of activities you expect, for each type of object.
defineProcess doesn't change your file. It tells your editor which activities you declared, so a typo in a flow is underlined right away. The defineProcess reference lists every field.
Name
name is the title of the map, so write it for the people who'll read it:
name: 'Order payments',ocelkit knows the process by its file name. processes/payments.ts is the process payments, and that's how the workspace lists it.
Activities
activities gives each event type you care about a short key:
activities: { confirmOrder: 'confirm order', paymentReminder: 'payment reminder', payOrder: 'pay order',},The value is the event type, spelled exactly as it is in your data. The key is yours: it's the name you use in the flows. Every activity is a box on the map, with the number of its events.
To see the event types in your log, run ocelkit describe:
$ npx ocelkit describeEvent types confirm order 2000 create package 1128 failed delivery 311 item out of stock 1544 package delivered 1128 pay order 2000 payment reminder 566 pick item 7659 place order 2000 reorder item 1544 send package 1128Object types customers 15 employees 18 items 7659 orders 2000 packages 1128 products 20The Commands reference has more on ocelkit describe and its flags.
A value that isn't an event type in the data is an error. The check tells you the closest one:
processes/payments.ts error activities.payOrder "pay ordr" is not an event type in the data. Closest: "pay order".The map counts by event type, so each event type can have only one activity. Two keys for the same event type is an error too:
activities: { payOrder: 'pay order', paid: 'pay order',},processes/payments.ts error activities.paid Another activity already names "pay order". Each event type can have one activity.Flows
flows says what each line counts. You write an object type from your data, then a list of pairs of activities:
flows: { orders: [['confirmOrder', 'payOrder']],},A line counts each connection between an instance of the first event type and an instance of the second event type, where an instance of the object type is present in both, and no other activity of the flow comes between them. Here, a confirm order instance connects to a pay order instance when the same order is present in both. Each connection adds 1 to the line.
Here's that on five events from two orders. Press Next to follow each order:
- 09:00confirm orderorder A
- 09:30confirm orderorder B
- 10:00payment reminderorder A
- 10:15pay orderorder B
- 11:00pay orderorder A
- order ANot followed yet
- order BNot followed yet
Five events from two orders. The line counts orders: how many went from confirm order straight to pay order. To find out, ocelkit reads the log one order at a time.
The best way to see what that means is to build a flow up, one step at a time.
Start with one pair
Here's a file with two activities and one pair:
import { defineProcess } from 'ocelkit'export default defineProcess({ name: 'Order payments', activities: { confirmOrder: 'confirm order', payOrder: 'pay order', }, flows: { orders: [['confirmOrder', 'payOrder']], },})- orders
All 2,000 orders went from confirm order to pay order.
Some of them got a payment reminder in between. The flow doesn't list reminders, so ocelkit skips over them. Open Not on the map in the workspace, and you'll find payment reminder and place order under Activities left out: the orders have these events, but the flow doesn't list them.
Add an activity
Add the payment reminder, and the two pairs around it:
activities: { confirmOrder: 'confirm order', paymentReminder: 'payment reminder', payOrder: 'pay order',},flows: { orders: [ ['confirmOrder', 'payOrder'], ['confirmOrder', 'paymentReminder'], ['paymentReminder', 'payOrder'], ],},- orders
1,557 orders went straight from confirm order to pay order, and the other 443 got a reminder first.
The first count fell from 2,000 to 1,557. Now that the flow lists reminders, an order that got one no longer goes straight from confirm order to pay order. When you add an activity, the counts around it change.
Add what's missing
Not on the map now has a pair under Pairs not in the file: payment reminder → payment reminder, 123 times. Some orders got another reminder after the first. The data has that pair, but the flow doesn't, so the map doesn't draw it.
Add the pair to the flow:
orders: [ ['confirmOrder', 'payOrder'], ['confirmOrder', 'paymentReminder'], ['paymentReminder', 'paymentReminder'], ['paymentReminder', 'payOrder'],],- orders
A pair from an activity to itself is a loop. Now the map has everything the orders did between confirm order and pay order.
More than one type of object
An event can involve more than one object. ocelkit describe --links shows which object types the events of each type are attached to. Here are three of its lines:
confirm order customers 2000, employees 2000, items 2000, orders 2000, products 2000 pay order items 2000, orders 2000, products 2000 pick item employees 7659, items 7659, products 7659Confirming an order involves the order and its items. Picking an item involves the item, but not its order. To follow the items too, give them their own flow:
import { defineProcess } from 'ocelkit'export default defineProcess({ name: 'After confirmation', activities: { confirmOrder: 'confirm order', payOrder: 'pay order', pickItem: 'pick item', }, flows: { orders: [['confirmOrder', 'payOrder']], items: [['confirmOrder', 'pickItem']], },})- orders
- items
Each object type has its own line color. The line to pick item counts items, not orders: 5,717 items were picked after their order was confirmed. The other 1,942 were picked before, so Not on the map lists pick item → confirm order, 1,942 times.
The same events can be in the story of more than one object. Here are both flows on four events of one order and its two items:
- 09:00confirm orderorder Aitem 1item 2
- 09:40pick itemitem 1
- 10:00pay orderorder Aitem 1item 2
- 10:20pick itemitem 2
- order ANot followed yet
- item 1Not followed yet
- item 2Not followed yet
Four events of one order and its two items. Some events involve all three. ocelkit still reads one object at a time, each with the flow of its own type.
That's why you write an object type in flows: it says which object the two events of a pair must share. The confirm order event at 09:00 involves order A, item 1 and item 2. Under orders, it pairs with the pay order event of order A, and adds 1 to the orders line. Under items, it pairs with the pick item event of item 1, and with the pick item event of item 2, and adds 2 to the items line. One event is in three pairs, because it involves three objects.
Every activity of a flow needs events attached to its object type. Pick item has none attached to an order, so in the orders flow, a pair with it can never happen. The check warns you:
orders: [ ['confirmOrder', 'payOrder'], ['payOrder', 'pickItem'],],processes/confirmed.ts warn flows.orders[1] No "pick item" event links to an object of type "orders".A warning doesn't stop the map. The workspace shows it above the map until you fix it.
When a name is wrong
A pair can only use the keys of activities. Use any other, and your editor underlines it before you save:
orders: [['confirmOrder', 'payOrdr']],Type '"payOrdr"' is not assignable to type '"confirmOrder" | "payOrder"'. Did you mean '"payOrder"'?The object type of a flow must be one in your data. The check suggests the closest one:
processes/payments.ts error flows.order "order" is not an object type in the data. Closest: "orders".Writing good process files
These are habits that make a map easier to read. They're not rules: ocelkit draws any file that passes the check, and your question may need a different shape.
Ask one question per file
A map that answers one question is quick to read. Write the question in a comment at the top of the file, where you, your team and your agent will see it:
// Do customers pay on time? Do packages arrive? Who picks the items?// Do customers pay once we confirm their order, or only after a reminder?Got a second question? Write a second file. Each file is its own map.
A file that maps every activity in your log has its place too: it's a good first look at all your data. It's just hard to read one answer from it.
Start small
Start with the two or three activities your question is about. Then open Not on the map, and add only what the question needs, as you did with the reminders above.
Name keys after their event types
The flows are what people and agents read, so a key should say what it is:
activities: { a1: 'confirm order', a2: 'pay order' },flows: { orders: [['a1', 'a2']] },activities: { confirmOrder: 'confirm order', payOrder: 'pay order' },flows: { orders: [['confirmOrder', 'payOrder']] },List pairs in the order they happen
The map keeps the order of your file where it can. List the pairs from start to end, and the map reads the same way.
Next steps
Now that you can write a process file, learn to read its map in Reading the map, and to check it against your data in Checking your files.