# The event log

A meeting is not stored as a transcript. It is stored as the list of things that happened: a line was heard, someone said who spoke it, a line was edited, that was undone. The transcript and the receipt are what you get by replaying the list. That is why a bundle loses nothing: it holds the history, not a summary of it.

## Every event

```json
{
  "id": "01927f3a-0000-7000-8000-000000001006",
  "meetingId": "01927f3a-0000-7000-8000-000000000001",
  "at": 1791561609000,
  "type": "SpeakerAssigned",
  "lineIds": [
    "01927f3a-0000-7000-8000-000000000101"
  ],
  "participantId": "01927f3a-0000-7000-8000-000000000011"
}
```

| Field | Type | Meaning |
| --- | --- | --- |
| `id` | string | This event's id |
| `meetingId` | string | The meeting it belongs to. The same on every event in a file |
| `at` | number | When it happened, in milliseconds since 1970 (UTC) |
| `type` | string | One of the types below |

**Ids** are UUIDs written in lowercase with hyphens: `8-4-4-4-12` hex digits. Chitmonk makes version 7 UUIDs, which sort by time. Any lowercase UUID is accepted.

**Two clocks.** `at` is the wall clock. `startMs` and `endMs` on a line are milliseconds into the recording, with pauses left out, so they line up with the audio file.

**Extra fields** are allowed on any event, kept, and written back out.

## Replaying a log

1. Collect the `targetId` of every `EventUndone`. Those events did not happen: skip them. An undo is final: undoing an `EventUndone` does not bring its target back.
2. Apply the rest in the order they appear in the file.
3. The result is the meeting as it stands.

Two rules decide what you may show as fact:

- **A speaker is confirmed only by a person.** A line has a confirmed speaker if a `SpeakerAssigned` named it, or if it arrived with a `speakerId` (the person had already chosen who was talking). A `suggestedSpeakerId` is the app's guess from the sound of the voice. Show it as a guess: Chitmonk writes it as `Maya?`.
- **An action item or decision is accepted only by a person.** Until an `ActionAccepted` or `DecisionAccepted`, it is a suggestion found by phrase rules.

## The events

### The meeting

| Type | Fields | What it does |
| --- | --- | --- |
| `MeetingCreated` | `schema`, `title`, `language`, `privacyMode`, `engine`, `vocabulary`, `ephemeral`? | Always first. Sets the title and settings; its `at` is when the meeting was created |
| `MeetingRenamed` | `title` | Replaces the title |
| `RecordingStarted` | | Recording begins, or resumes after a pause |
| `RecordingPaused` | | Recording pauses |
| `MeetingEnded` | | Recording stops for good; its `at` is when the meeting ended |
| `MeetingDeleted` | | Marks the meeting deleted. Not normally found in an export |
| `EventUndone` | `targetId` | The event with that id is skipped when replaying |

In `MeetingCreated`: `schema` is `1`. `language` is `auto` or one of `en`, `de`, `fr`, `es`, `it`, `nl`, `pl`. `privacyMode` is `transcript-only` or `audio-retained`. `engine` says what made the transcript, as `{ "stt", "sttVersion", "runtime", "vad", "speaker" }`, all strings except `speaker`, which is a string or `null`. `vocabulary` is up to 200 words the meeting was told to listen for.

The meeting's length is the time between each `RecordingStarted` and the `RecordingPaused` or `MeetingEnded` that follows it, added up.

### People

| Type | Fields | What it does |
| --- | --- | --- |
| `ParticipantAdded` | `participantId`, `name`, `pronouns`? | Adds a person, after the ones already there |
| `ParticipantUpdated` | `participantId`, `name`?, `pronouns`? | Changes what is given. `pronouns: null` removes them |
| `ParticipantReordered` | `order` | The new order, as a list of participant ids. Anyone left out goes last |
| `ParticipantRemoved` | `participantId` | The person leaves the list, but lines they spoke keep their name |

A person's position in the list is the number key that means them during a meeting.

### The transcript

| Type | Fields | What it does |
| --- | --- | --- |
| `SegmentCommitted` | `lineId`, `text`, `words`, `startMs`, `endMs`, `language`, `turnId`, `suggestedSpeakerId`?, `speakerId`? | Adds a line at the end of the transcript |
| `SpeakerAssigned` | `lineIds`, `participantId` | A person says who spoke those lines. `null` means "nobody on the list", on purpose |
| `TranscriptLineEdited` | `lineId`, `text` | Replaces the line's words. The words first heard are still in its `SegmentCommitted` |
| `TranscriptLineDeleted` | `lineId` | Removes the line |

In `SegmentCommitted`: `language` is the language heard on that line. `turnId` groups lines from one unbroken stretch of the same voice. `words` is a list of `{ "w", "s", "e", "p" }`: the word, its start and end in recording milliseconds, and the model's probability for it. It may be empty.

`SpeakerAssigned` also clears any guess on those lines. If an action item took its owner from the speaker of one of them, the owner changes with it.

### Action items and decisions

The two families are the same shape. Replace `Action` with `Decision`.

| Type | Fields | What it does |
| --- | --- | --- |
| `ActionSuggested` | `itemId`, `lineId`, `text`, `ownerId`?, `ownerFromSpeaker`?, `dueText`?, `dueDate`? | Adds an item, not yet accepted |
| `ActionEdited` | `itemId`, `text`?, `ownerId`?, `dueText`?, `dueDate`? | Changes what is given. `null` clears an owner or a due date |
| `ActionAccepted` | `itemId` | A person accepts it |
| `ActionDeleted` | `itemId` | Removes it |

`lineId` is the line it was found in, or `null` for one a person typed. `dueText` is the words as spoken ("by Friday"). `dueDate` is `YYYY-MM-DD`, set only when the words pin down a single day. `ownerFromSpeaker: true` means the owner is whoever spoke the line, and follows a later `SpeakerAssigned`; an `ActionEdited` that sets `ownerId` ends that.

### Side notes

| Type | Fields | What it does |
| --- | --- | --- |
| `NoteAdded` | `noteId`, `lineId`, `text` | Adds a note beside a line, or beside the meeting if `lineId` is `null` |
| `NoteEdited` | `noteId`, `text` | Replaces its words |
| `NoteDeleted` | `noteId` | Removes it |

A note never changes the line it sits beside.

## The sample, start to finish

This is the whole event log in the [sample bundle](/sample.chit). It has a confirmed speaker, a guessed one, an unknown one, an undo, an edit, a note, an accepted action item and a suggested one.

```json
{
  "schema": 1,
  "events": [
    {"id":"01927f3a-0000-7000-8000-000000001000","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561600000,"type":"MeetingCreated","schema":1,"title":"Launch review","language":"en","privacyMode":"transcript-only","engine":{"stt":"Cactus Whistle","sttVersion":"2.0.0","runtime":"Cactus needle3 (WebAssembly)","vad":"Silero VAD v5","speaker":"3D-Speaker CAM++"},"vocabulary":["Chitmonk"]},
    {"id":"01927f3a-0000-7000-8000-000000001001","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561601000,"type":"ParticipantAdded","participantId":"01927f3a-0000-7000-8000-000000000011","name":"Maya"},
    {"id":"01927f3a-0000-7000-8000-000000001002","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561602000,"type":"ParticipantAdded","participantId":"01927f3a-0000-7000-8000-000000000012","name":"Leo"},
    {"id":"01927f3a-0000-7000-8000-000000001003","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561603000,"type":"ParticipantAdded","participantId":"01927f3a-0000-7000-8000-000000000013","name":"Sam","pronouns":"they/them"},
    {"id":"01927f3a-0000-7000-8000-000000001004","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561604000,"type":"RecordingStarted"},
    {"id":"01927f3a-0000-7000-8000-000000001005","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561608000,"type":"SegmentCommitted","lineId":"01927f3a-0000-7000-8000-000000000101","text":"Okay, let us get started. Thanks everyone for joining the launch review.","words":[],"startMs":1000,"endMs":6000,"language":"en","turnId":"01927f3a-0000-7000-8000-000000000201"},
    {"id":"01927f3a-0000-7000-8000-000000001006","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561609000,"type":"SpeakerAssigned","lineIds":["01927f3a-0000-7000-8000-000000000101"],"participantId":"01927f3a-0000-7000-8000-000000000011"},
    {"id":"01927f3a-0000-7000-8000-000000001007","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561613000,"type":"SegmentCommitted","lineId":"01927f3a-0000-7000-8000-000000000102","text":"Sounds good. The checkout tests passed last night, so the build is ready.","words":[],"startMs":7000,"endMs":12000,"language":"en","turnId":"01927f3a-0000-7000-8000-000000000202"},
    {"id":"01927f3a-0000-7000-8000-000000001008","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561614000,"type":"SpeakerAssigned","lineIds":["01927f3a-0000-7000-8000-000000000102"],"participantId":"01927f3a-0000-7000-8000-000000000012"},
    {"id":"01927f3a-0000-7000-8000-000000001009","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561618000,"type":"SegmentCommitted","lineId":"01927f3a-0000-7000-8000-000000000103","text":"Great. I will send the revised deck by friday.","words":[],"startMs":13000,"endMs":16000,"language":"en","turnId":"01927f3a-0000-7000-8000-000000000203","suggestedSpeakerId":"01927f3a-0000-7000-8000-000000000011"},
    {"id":"01927f3a-0000-7000-8000-000000001010","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561619000,"type":"ActionSuggested","itemId":"01927f3a-0000-7000-8000-000000000301","lineId":"01927f3a-0000-7000-8000-000000000103","text":"I will send the revised deck by Friday.","ownerId":"01927f3a-0000-7000-8000-000000000011","ownerFromSpeaker":true,"dueText":"by Friday"},
    {"id":"01927f3a-0000-7000-8000-000000001011","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561620000,"type":"SpeakerAssigned","lineIds":["01927f3a-0000-7000-8000-000000000103"],"participantId":"01927f3a-0000-7000-8000-000000000011"},
    {"id":"01927f3a-0000-7000-8000-000000001012","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561621000,"type":"TranscriptLineEdited","lineId":"01927f3a-0000-7000-8000-000000000103","text":"Great. I will send the revised deck by Friday."},
    {"id":"01927f3a-0000-7000-8000-000000001013","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561622000,"type":"NoteAdded","noteId":"01927f3a-0000-7000-8000-000000000401","lineId":"01927f3a-0000-7000-8000-000000000103","text":"Ask Leo for the old deck"},
    {"id":"01927f3a-0000-7000-8000-000000001014","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561626000,"type":"SegmentCommitted","lineId":"01927f3a-0000-7000-8000-000000000104","text":"We decided to launch on Tuesday.","words":[],"startMs":17000,"endMs":20000,"language":"en","turnId":"01927f3a-0000-7000-8000-000000000204"},
    {"id":"01927f3a-0000-7000-8000-000000001015","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561627000,"type":"DecisionSuggested","itemId":"01927f3a-0000-7000-8000-000000000302","lineId":"01927f3a-0000-7000-8000-000000000104","text":"We decided to launch on Tuesday."},
    {"id":"01927f3a-0000-7000-8000-000000001016","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561628000,"type":"SpeakerAssigned","lineIds":["01927f3a-0000-7000-8000-000000000104"],"participantId":"01927f3a-0000-7000-8000-000000000012"},
    {"id":"01927f3a-0000-7000-8000-000000001017","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561629000,"type":"EventUndone","targetId":"01927f3a-0000-7000-8000-000000001016"},
    {"id":"01927f3a-0000-7000-8000-000000001018","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561630000,"type":"SpeakerAssigned","lineIds":["01927f3a-0000-7000-8000-000000000104"],"participantId":"01927f3a-0000-7000-8000-000000000013"},
    {"id":"01927f3a-0000-7000-8000-000000001019","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561634000,"type":"SegmentCommitted","lineId":"01927f3a-0000-7000-8000-000000000105","text":"Sam, can you update the release notes before the launch?","words":[],"startMs":21000,"endMs":25000,"language":"en","turnId":"01927f3a-0000-7000-8000-000000000205","suggestedSpeakerId":"01927f3a-0000-7000-8000-000000000012"},
    {"id":"01927f3a-0000-7000-8000-000000001020","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561635000,"type":"ActionSuggested","itemId":"01927f3a-0000-7000-8000-000000000303","lineId":"01927f3a-0000-7000-8000-000000000105","text":"Sam, can you update the release notes before the launch?","ownerId":"01927f3a-0000-7000-8000-000000000013"},
    {"id":"01927f3a-0000-7000-8000-000000001021","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561639000,"type":"SegmentCommitted","lineId":"01927f3a-0000-7000-8000-000000000106","text":"Yes, that works for me.","words":[],"startMs":26000,"endMs":28000,"language":"en","turnId":"01927f3a-0000-7000-8000-000000000206"},
    {"id":"01927f3a-0000-7000-8000-000000001022","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561640000,"type":"ActionAccepted","itemId":"01927f3a-0000-7000-8000-000000000301"},
    {"id":"01927f3a-0000-7000-8000-000000001023","meetingId":"01927f3a-0000-7000-8000-000000000001","at":1791561641000,"type":"MeetingEnded"}
  ]
}
```

Replayed, it reads:

```text
Launch review
People: Maya, Leo, Sam (they/them)

[0:01] Maya: Okay, let us get started. Thanks everyone for joining the launch review.
[0:07] Leo: Sounds good. The checkout tests passed last night, so the build is ready.
[0:13] Maya: Great. I will send the revised deck by Friday.
[0:17] Sam: We decided to launch on Tuesday.
[0:21] Leo?: Sam, can you update the release notes before the launch?
[0:26] Unknown speaker: Yes, that works for me.

Action items
- I will send the revised deck by Friday. (Maya, due by Friday)
- Sam, can you update the release notes before the launch? (Sam, suggested)

Decisions
- We decided to launch on Tuesday. (suggested)

Notes
- [0:13] Ask Leo for the old deck
```
