Public Access
303 lines
7.5 KiB
Markdown
303 lines
7.5 KiB
Markdown
# Meeting Workflow Engine
|
|
|
|
Meeting Assistant has a small local workflow engine for meeting-specific automation. It is intended for rules that are too personal or environment-specific to hard-code, such as adding default attendees, cleaning meeting titles, binding projects, or adding context notes when known speakers are detected.
|
|
|
|
The workflow engine is deliberately narrow. It runs from a local YAML file, evaluates rules against the current meeting note read from disk, and applies a small set of meeting-safe mutations.
|
|
|
|
## Configuration
|
|
|
|
The rules file path is configured through `MeetingAssistant:Automation:RulesPath`.
|
|
|
|
```json
|
|
{
|
|
"MeetingAssistant": {
|
|
"Automation": {
|
|
"RulesPath": "meeting-rules.local.yaml"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`meeting-rules.local.yaml` is the default local rules file and is ignored by git. The path may be absolute, relative to the process working directory, or use environment variables.
|
|
|
|
If the configured path is empty, missing, or points to a blank file, the workflow engine does nothing.
|
|
|
|
## Execution Model
|
|
|
|
The engine runs when `MeetingRecordingCoordinator` emits a meeting workflow event:
|
|
|
|
- `created`: after the meeting note and assistant context artifact are created.
|
|
- `state_transition`: after the assistant context state moves forward.
|
|
- `speaker_identified`: when live or final speaker identification reports a display name.
|
|
|
|
For every event, the engine:
|
|
|
|
1. Loads the current rules file.
|
|
2. Reads the latest meeting note from disk.
|
|
3. Evaluates every matching rule in file order.
|
|
4. Applies each matching rule's steps in order.
|
|
5. Rebuilds the template model after every step, so later steps can see earlier mutations.
|
|
6. Saves the meeting note once if any meeting-note step changed it.
|
|
7. Updates assistant-context frontmatter links/title from the saved meeting note after note changes.
|
|
|
|
Rules are best-effort automation. Invalid expressions, unknown steps, or unsupported properties currently fail the workflow event and should be covered by tests before the engine is broadened.
|
|
|
|
## YAML Shape
|
|
|
|
The top-level YAML document contains a `rules` array.
|
|
|
|
```yaml
|
|
rules:
|
|
- name: add-default-attendee
|
|
on:
|
|
- created: {}
|
|
if:
|
|
- condition: meeting.attendees.count = 0
|
|
steps:
|
|
- uses: add_attendee
|
|
value: Manuel
|
|
```
|
|
|
|
Each rule has:
|
|
|
|
- `name`: log-friendly rule name.
|
|
- `on`: one or more triggers. At least one must match.
|
|
- `if`: optional list of conditions. All top-level conditions must pass.
|
|
- `steps`: ordered actions to run when the rule matches.
|
|
|
|
## Triggers
|
|
|
|
### `created`
|
|
|
|
Runs after a meeting note is created.
|
|
|
|
```yaml
|
|
on:
|
|
- created: {}
|
|
```
|
|
|
|
### `state_transition`
|
|
|
|
Runs when the assistant context state transitions. `from` and `to` are optional filters; omitted filters match any value.
|
|
|
|
```yaml
|
|
on:
|
|
- state_transition:
|
|
from: collecting metadata
|
|
to: transcribing
|
|
```
|
|
|
|
Supported state names are:
|
|
|
|
- `collecting metadata`
|
|
- `transcribing`
|
|
- `speaker recognition`
|
|
- `summarizing`
|
|
- `finished`
|
|
- `error`
|
|
|
|
### `speaker_identified`
|
|
|
|
Runs when speaker identification reports a display name. `name` is optional; when supplied, it matches case-insensitively.
|
|
|
|
```yaml
|
|
on:
|
|
- speaker_identified:
|
|
name: Ada
|
|
```
|
|
|
|
## Conditions
|
|
|
|
Conditions use NCalc expressions. Dotted workflow variables may be written directly; the engine rewrites them into NCalc parameters internally.
|
|
|
|
```yaml
|
|
if:
|
|
- condition: meeting.attendees.count = 0
|
|
```
|
|
|
|
Available condition variables:
|
|
|
|
- `meeting.attendees.count`
|
|
- `meeting.attendees`
|
|
- `meeting.projects`
|
|
- `meeting.title`
|
|
- `meeting.state`
|
|
- `event.type`
|
|
- `state.from`
|
|
- `state.to`
|
|
- `speaker.name`
|
|
|
|
Available helper functions:
|
|
|
|
- `contains(haystack, needle)`: case-insensitive string or collection contains.
|
|
- `starts_with(value, prefix)`: case-insensitive string prefix check.
|
|
- `ends_with(value, suffix)`: case-insensitive string suffix check.
|
|
|
|
Nested boolean groups are supported:
|
|
|
|
```yaml
|
|
if:
|
|
- and:
|
|
- condition: contains(meeting.title, '[External]')
|
|
- not:
|
|
condition: contains(meeting.projects, 'Internal')
|
|
```
|
|
|
|
Top-level `if` entries are combined with `and`.
|
|
|
|
## Step Templating
|
|
|
|
Step `value` fields can be plain strings or Razor templates. Razor templates receive this model:
|
|
|
|
- `Model.Meeting.Title`
|
|
- `Model.Meeting.Attendees`
|
|
- `Model.Meeting.Projects`
|
|
- `Model.Meeting.State`
|
|
- `Model.Event.Type`
|
|
- `Model.Event.From`
|
|
- `Model.Event.To`
|
|
- `Model.Speaker.Name`
|
|
|
|
Example:
|
|
|
|
```yaml
|
|
steps:
|
|
- uses: add_context
|
|
value: "Known speaker identified: @Model.Speaker.Name"
|
|
```
|
|
|
|
The engine only invokes Razor when the value contains `@`.
|
|
|
|
## Steps
|
|
|
|
### `add_attendee`
|
|
|
|
Adds a normalized attendee display name to meeting note frontmatter if it is not already present case-insensitively.
|
|
|
|
```yaml
|
|
steps:
|
|
- uses: add_attendee
|
|
value: Manuel
|
|
```
|
|
|
|
### `remove_attendee`
|
|
|
|
Removes attendees whose normalized display name matches the rendered value.
|
|
|
|
```yaml
|
|
steps:
|
|
- uses: remove_attendee
|
|
value: "Guest"
|
|
```
|
|
|
|
### `set_property`
|
|
|
|
Sets a supported meeting property. The initial supported property is `title` or `meeting.title`.
|
|
|
|
```yaml
|
|
steps:
|
|
- uses: set_property
|
|
property: title
|
|
value: "@Model.Meeting.Title.Replace('[External] ', '')"
|
|
```
|
|
|
|
### `add_context`
|
|
|
|
Appends rendered text to the assistant context artifact body. This step does not count as a meeting-note mutation and does not cause a meeting-note save by itself.
|
|
|
|
```yaml
|
|
steps:
|
|
- uses: add_context
|
|
value: "Ada was identified; check project ownership notes."
|
|
```
|
|
|
|
### `add_project`
|
|
|
|
Adds a project name to meeting note frontmatter if it is not already present case-insensitively.
|
|
|
|
```yaml
|
|
steps:
|
|
- uses: add_project
|
|
value: Meeting Assistant
|
|
```
|
|
|
|
## Examples
|
|
|
|
Add a default attendee only for empty meetings:
|
|
|
|
```yaml
|
|
rules:
|
|
- name: add-me
|
|
on:
|
|
- created: {}
|
|
if:
|
|
- condition: meeting.attendees.count = 0
|
|
steps:
|
|
- uses: add_attendee
|
|
value: Manuel
|
|
```
|
|
|
|
Remove a title marker after metadata collection:
|
|
|
|
```yaml
|
|
rules:
|
|
- name: clean-external-marker
|
|
on:
|
|
- state_transition:
|
|
from: collecting metadata
|
|
if:
|
|
- condition: starts_with(meeting.title, '[External] ')
|
|
steps:
|
|
- uses: set_property
|
|
property: title
|
|
value: "@Model.Meeting.Title.Replace(\"[External] \", \"\")"
|
|
```
|
|
|
|
Add context when a speaker is identified:
|
|
|
|
```yaml
|
|
rules:
|
|
- name: note-ada
|
|
on:
|
|
- speaker_identified:
|
|
name: Ada
|
|
steps:
|
|
- uses: add_context
|
|
value: "Ada was identified. Check whether the architecture decision log needs an update."
|
|
```
|
|
|
|
Add context when metadata gathering ends with a specific attendee:
|
|
|
|
```yaml
|
|
rules:
|
|
- name: note-review-owner
|
|
on:
|
|
- state_transition:
|
|
from: collecting metadata
|
|
if:
|
|
- condition: contains(meeting.attendees, 'Manuel')
|
|
steps:
|
|
- uses: add_context
|
|
value: "Manuel is attending; include implementation follow-ups in the summary."
|
|
```
|
|
|
|
## Implementation Notes
|
|
|
|
Core files:
|
|
|
|
- `MeetingAssistant/Workflow/MeetingWorkflowEngine.cs`
|
|
- `MeetingAssistant/Workflow/MeetingWorkflowModels.cs`
|
|
- `MeetingAssistant/Workflow/MeetingWorkflowEvent.cs`
|
|
- `MeetingAssistant/Workflow/FileMeetingWorkflowRulesProvider.cs`
|
|
- `MeetingAssistant/Recording/MeetingRecordingCoordinator.cs`
|
|
- `MeetingAssistant.Tests/MeetingWorkflowEngineTests.cs`
|
|
|
|
When extending the workflow engine:
|
|
|
|
1. Update OpenSpec requirements if behavior changes.
|
|
2. Add behavior tests through `MeetingWorkflowEngine` or the recording coordinator event surface.
|
|
3. Keep the supported trigger, condition, template model, and step lists in this document current.
|
|
4. Keep `README.md` as the short operational overview and link back here for details.
|
|
5. Keep the local rules file ignored by git; do not commit personal automation rules.
|
|
|