Author SHA1 Message Date
renovate-bot 1d6995fb9f chore(deps): update dependency naudio to v3
PR and Push Build/Test / build-and-test (push) Failing after 7m49s
PR and Push Build/Test / build-and-test (pull_request) Failing after 6m16s
2026-08-19 02:41:26 +00:00
codex dc37719d46 Merge pull request 'chore(deps): update dependency microsoft.aspnetcore.mvc.testing to 10.0.11' (#41) from renovate/microsoft.aspnetcore.mvc.testing-10.x into main
PR and Push Build/Test / build-and-test (push) Successful in 10m35s
2026-08-18 13:13:40 +02:00
renovate-bot ee453e170e chore(deps): update dependency microsoft.aspnetcore.mvc.testing to 10.0.11
PR and Push Build/Test / build-and-test (push) Successful in 15m3s
PR and Push Build/Test / build-and-test (pull_request) Successful in 15m31s
2026-08-18 02:40:54 +00:00
codex d51463321b Merge pull request 'chore(deps): update dependency microsoft.net.test.sdk to 18.9.0' (#44) from renovate/microsoft.net.test.sdk-18.x into main
PR and Push Build/Test / build-and-test (push) Successful in 14m38s
2026-08-17 09:05:30 +02:00
renovate-bot 41ae47fbfe chore(deps): update dependency microsoft.net.test.sdk to 18.9.0
PR and Push Build/Test / build-and-test (push) Successful in 16m29s
PR and Push Build/Test / build-and-test (pull_request) Successful in 16m20s
2026-08-16 02:49:14 +00:00
codex f7ad2ce41a Merge pull request 'chore(deps): update dependency system.drawing.common to 10.0.11' (#43) from renovate/system.drawing.common-10.x into main
PR and Push Build/Test / build-and-test (push) Successful in 11m9s
2026-08-13 10:53:04 +02:00
renovate-bot bc76151926 chore(deps): update dependency system.drawing.common to 10.0.11
PR and Push Build/Test / build-and-test (pull_request) Successful in 15m43s
PR and Push Build/Test / build-and-test (push) Successful in 15m52s
2026-08-13 02:41:36 +00:00
codex af9f5b34dc Merge pull request 'chore(deps): update dependency microsoft.entityframeworkcore.sqlite to 10.0.11' (#42) from renovate/microsoft.entityframeworkcore.sqlite-10.x into main
PR and Push Build/Test / build-and-test (push) Successful in 11m25s
2026-08-12 13:19:26 +02:00
renovate-bot f3489f8e95 chore(deps): update dependency microsoft.entityframeworkcore.sqlite to 10.0.11
PR and Push Build/Test / build-and-test (push) Successful in 10m37s
PR and Push Build/Test / build-and-test (pull_request) Successful in 10m0s
2026-08-12 02:49:08 +00:00
codex 39591eef12 docs: refresh Meeting Assistant runtime boundaries
PR and Push Build/Test / build-and-test (push) Failing after 10m29s
2026-08-11 09:15:16 +02:00
codex b3aeef24eb Merge PR #34: update setup-dotnet to v6
PR and Push Build/Test / build-and-test (push) Failing after 16s
Attended CI control-plane merge after exact-head success and local combined validation; final main run will be monitored.
2026-08-05 13:03:50 +02:00
codex c91d956beb Merge PR #38: update NCalcSync to 7.0.2
PR and Push Build/Test / build-and-test (push) Failing after 16s
Attended major-version merge after API migration review and combined current-main workflow-expression test validation.
2026-08-05 13:03:39 +02:00
codex 4c858170d9 Merge PR #36: update Microsoft Agent Framework OpenAI
PR and Push Build/Test / build-and-test (push) Failing after 16s
Attended merge after upstream compatibility review and combined current-main validation.
2026-08-05 13:03:30 +02:00
codex 0b6643d87f Merge PR #37: update Microsoft Speech to 1.51.1
PR and Push Build/Test / build-and-test (push) Failing after 4m10s
Attended merge after fixing MAS model asset discovery and validating both targets plus 440 tests.
2026-08-05 13:03:18 +02:00
codex 44ce419c49 fix: copy available MAS model assets
PR and Push Build/Test / build-and-test (push) Successful in 11m38s
PR and Push Build/Test / build-and-test (pull_request) Failing after 11m41s
2026-08-05 13:01:50 +02:00
codex a087ff0cbf Merge remote-tracking branch 'origin/main' into renovate/microsoftspeechversion 2026-08-05 13:00:56 +02:00
codex 8dc1b70f8a test: stabilize summary retry background wait
PR and Push Build/Test / build-and-test (push) Successful in 11m32s
2026-08-05 12:35:00 +02:00
codex aaee123b9a merge: attach calendar metadata to active meetings
PR and Push Build/Test / build-and-test (push) Failing after 18m6s
2026-08-05 11:59:35 +02:00
renovate-bot e886fac311 Update dependency NCalcSync to v7
PR and Push Build/Test / build-and-test (push) Failing after 16s
PR and Push Build/Test / build-and-test (pull_request) Failing after 16s
2026-08-05 02:40:11 +00:00
renovate-bot e35ccf661f Update dependency Microsoft.Agents.AI.OpenAI to 1.17.0
PR and Push Build/Test / build-and-test (push) Failing after 3m0s
PR and Push Build/Test / build-and-test (pull_request) Failing after 15s
2026-08-05 02:39:47 +00:00
renovate-bot 20eed38477 Update actions/setup-dotnet action to v6
PR and Push Build/Test / build-and-test (push) Successful in 10m25s
PR and Push Build/Test / build-and-test (pull_request) Successful in 10m46s
2026-08-04 02:39:34 +00:00
renovate-bot ada4a40f62 Update MicrosoftSpeechVersion to 1.51.1
PR and Push Build/Test / build-and-test (push) Successful in 10m0s
PR and Push Build/Test / build-and-test (pull_request) Successful in 11m11s
2026-08-04 02:39:30 +00:00
5 changed files with 85 additions and 131 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v7
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"
@@ -9,8 +9,8 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.11" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
@@ -189,7 +189,9 @@ public sealed class MeetingSummaryRetryRunnerTests
return;
}
await stateChanged.Task.WaitAsync(TimeSpan.FromMilliseconds(100));
await Task.WhenAny(
stateChanged.Task,
Task.Delay(TimeSpan.FromMilliseconds(100)));
}
throw new TimeoutException($"Expected {count} state changes.");
+7 -8
View File
@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PreserveCompilationContext>true</PreserveCompilationContext>
<MicrosoftSpeechVersion>1.50.0</MicrosoftSpeechVersion>
<MicrosoftSpeechVersion>1.51.1</MicrosoftSpeechVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
@@ -20,16 +20,16 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.13.0" />
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.17.0" />
<PackageReference Include="DiffPlex" Version="1.9.0" />
<PackageReference Include="Microsoft.CognitiveServices.Speech" Version="$(MicrosoftSpeechVersion)" />
<PackageReference Include="Microsoft.CognitiveServices.Speech.Extension.MAS" Version="$(MicrosoftSpeechVersion)" ExcludeAssets="build" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
<PackageReference Include="NAudio" Version="2.3.0" />
<PackageReference Include="NCalcSync" Version="6.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.11" />
<PackageReference Include="NAudio" Version="3.0.1" />
<PackageReference Include="NCalcSync" Version="7.0.2" />
<PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
<PackageReference Include="System.Drawing.Common" Version="10.0.10" />
<PackageReference Include="System.Drawing.Common" Version="10.0.11" />
<PackageReference Include="Whisper.net" Version="1.9.1" />
<PackageReference Include="Whisper.net.Runtime" Version="1.9.1" />
<PackageReference Include="YamlDotNet" Version="18.1.0" />
@@ -50,8 +50,7 @@
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<None Include="$(NuGetPackageRoot)microsoft.cognitiveservices.speech.extension.mas\$(MicrosoftSpeechVersion)\contentFiles\any\any\models\aec_v1.fpie" Link="runtimes\win-x64\native\MASmodels\aec_v1.fpie" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<None Include="$(NuGetPackageRoot)microsoft.cognitiveservices.speech.extension.mas\$(MicrosoftSpeechVersion)\contentFiles\any\any\models\pns_avg4.fpie" Link="runtimes\win-x64\native\MASmodels\pns_avg4.fpie" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<None Include="$(NuGetPackageRoot)microsoft.cognitiveservices.speech.extension.mas\$(MicrosoftSpeechVersion)\contentFiles\any\any\models\*.fpie" Link="runtimes\win-x64\native\MASmodels\%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
+72 -119
View File
@@ -1,170 +1,123 @@
# Meeting Assistant
Meeting Assistant is Manuel's local .NET meeting capture and knowledge service. It runs on the Windows workstation, records microphone plus system audio, writes Obsidian meeting artifacts, transcribes with speaker attribution, enriches meetings from local context, and drives summary/project agents without depending on Teams, Zoom, or another meeting-platform API for the primary flow.
Meeting Assistant is Manuel's local Windows meeting capture and knowledge service. It records microphone plus system audio, maintains meeting artifacts in an Obsidian vault, enriches them from local context, and runs transcription, speaker attribution, screenshot OCR, and agentic summarization without depending on a meeting-platform API for the primary flow.
## What This Repo Owns
## Scope And Runtime Boundary
This repository owns the application source, tests, OpenSpec requirements, configuration examples, local runtime documentation, and CI build/test workflow for Meeting Assistant. It does not own a homelab deployment stack, Traefik routing, Docker Compose deployment, or production image tag selection.
This repository owns the application source, tests, OpenSpec requirements, checked-in configuration, and CI validation. It does not own workstation startup automation, a homelab deployment stack, Traefik routing, Docker Compose, or a deployed image tag.
The app is intentionally local-first:
The Windows target provides audio capture, the tray icon, global hotkeys, Outlook Classic COM access, active-window screenshots, notifications, and the workflow-agent window. The cross-platform target keeps the HTTP and testable service surface but substitutes unavailable/no-op implementations for Windows integrations.
- Windows builds provide the tray icon, global hotkeys, NAudio capture, Outlook Classic COM enrichment, active-window screenshots, and notifications.
- Non-Windows builds keep the server/testable service surface but compile out Windows-only integrations.
- The normal runtime endpoint is local HTTP on port `5090`, with `/health` and `/recording/status` as the safe first checks.
- Behavior changes are OpenSpec-driven; current accepted requirements live under `openspec/specs`.
Accepted requirements live under `openspec/specs`. Relevant active changes under `openspec/changes` can describe implemented behavior that has not yet been folded into the accepted specs.
## Quick Start
For a development build:
The project requires the .NET 10 SDK. Build and test it with:
```powershell
dotnet restore MeetingAssistant.slnx
dotnet build MeetingAssistant.slnx
dotnet test MeetingAssistant.slnx
dotnet run --project MeetingAssistant
```
For the durable local background instance, use the snippets restart helper:
Do not start a second copy over the live workstation instance. Check the local control surface first:
```powershell
Invoke-RestMethod http://127.0.0.1:5090/health
Invoke-RestMethod http://127.0.0.1:5090/recording/status
```
For a foreground Windows development run when port `5090` is free:
```powershell
dotnet run --project MeetingAssistant --framework net10.0-windows10.0.19041.0
```
The durable workstation instance is published and replaced through automation owned by `Manuel/snippets`:
```powershell
powershell -ExecutionPolicy Bypass -File C:\Manuel\snippets\restart-meeting-assistant.ps1
```
The helper publishes `MeetingAssistant\MeetingAssistant.csproj` for `net10.0-windows10.0.19041.0` into timestamped folders below `C:\Manuel\meeting-assistant\tmp\meeting-assistant-runtime`. It stops the old port-`5090` instance only after publishing succeeds, starts the published `MeetingAssistant.exe`, waits for `/health`, and writes process logs plus the last PID to:
The helper publishes before stopping the old process, starts the timestamped Windows publish on loopback port `5090`, waits for `/health`, and records stdout, stderr, and the PID under `%LOCALAPPDATA%\MeetingAssistant\Logs`. Always confirm `/recording/status` is idle before invoking it.
```text
%LOCALAPPDATA%\MeetingAssistant\Logs\meeting-assistant.out.log
%LOCALAPPDATA%\MeetingAssistant\Logs\meeting-assistant.err.log
%LOCALAPPDATA%\MeetingAssistant\Logs\meeting-assistant.pid
```
## Recording And Control
Before restarting, always check the running process:
```powershell
Invoke-RestMethod http://127.0.0.1:5090/recording/status
```
Do not restart while a meeting is recording, transcribing, finalizing speaker recognition, waiting on OCR, or summarizing unless the user explicitly approves the interruption.
## Runtime Behavior
Recording can be controlled through global hotkeys, the Windows tray icon, or local HTTP endpoints. The default hotkeys are:
The default global controls are:
- `Ctrl+Alt+M`: toggle the default recording profile.
- `Ctrl+Alt+L`: toggle or switch to the configured `english` launch profile.
- `Ctrl+Alt+Z`: abort the active recording and delete that run's artifacts.
- `Ctrl+Alt+S`: capture the active window into the current meeting context.
- `Ctrl+Alt+L`: start or switch to the configured `english` profile.
- `Ctrl+Alt+Z`: abort the active run and delete its artifacts.
- `Ctrl+Alt+S`: capture the active window into the meeting context.
The main local endpoints are:
The tray presents `Finish meeting` as the primary action during capture; microphone selection, cancel/discard, and profile switching remain separate controls. `Exit` is always available and requires confirmation while any meeting is recording or processing.
- `GET /health`
- `GET /recording/status`
- `POST /recording/start`, `/recording/stop`, `/recording/toggle`, `/recording/abort`
- `POST /profiles/{launchProfile}/recording/start`, `/stop`, `/toggle`, `/abort`
- `POST /asr/transcribe-file` and `/asr/diarize-file` for diagnostic WAV checks
- `POST /diagnostics/workflow/reload`
- `POST /diagnostics/settings-and-logs/show`
- `POST /diagnostics/workflow/rules-editor/show`
- `POST /meetings/current/summary/run`
- `POST` or `GET /meetings/summary/retry`
- `POST` or `GET /meetings/screenshot-ocr/retry`
The loopback HTTP surface exposes health and recording status, recording start/stop/toggle/abort operations, profile-specific equivalents, and diagnostics/retry operations. It has no application authentication. Some generated retry links use `GET` while starting work, so port `5090` must remain a trusted loopback-only control surface.
Stopping a recording stops new audio capture but lets that run continue transcription drain, speaker processing, meeting-note image OCR, screenshot OCR waits, summary generation, and final artifact updates. A later recording can start while an older stopped run is still finalizing; each run keeps isolated artifact paths and options.
Stopping capture lets buffered transcription, speaker work, meeting-note image OCR, screenshot OCR, and summary generation finish. Another meeting can start while an older stopped run finalizes; each run retains isolated options and artifact paths.
During an active run, microphone creation failures and disconnects are retried every second with fresh endpoint selection. The meeting and system-loopback capture stay active, with microphone silence mixed in until capture resumes. This recovery does not cover a failed system-loopback source.
Outlook enrichment selects an unambiguous current or imminent appointment. A scheduled prompt shown during an active recording can apply that exact appointment's title, eligible attendees, agenda, and scheduled end without interrupting capture; explicit prompt metadata wins over a slower background lookup.
## Data And Side Effects
Meeting Assistant writes durable meeting knowledge to the configured Obsidian vault:
Meeting Assistant writes meeting notes, transcripts, assistant context, summaries, and project knowledge into the configured Obsidian vault. Assistant context is persistent meeting-specific memory: the summarizer records problems and assumptions, and the interactive agent can read it and append later repairs and conclusions.
- meeting notes
- transcripts
- assistant context notes
- summary notes
- project knowledge files that agents may read or update
- optional dictation words used as speech-recognition phrase hints
Agents are intentionally stateful. Depending on the invoked tools, they can change workflow rules and appsettings, create or update project and meeting files, change frontmatter, merge or delete speaker identities and samples, run diagnostics, and trigger transcription or summary work. Screenshot OCR can add recognized attendee names to the meeting note after workflow transformation; OCR of images already embedded in the meeting note does not add attendees or modify that note.
It also writes local runtime state outside the vault:
Local runtime state outside the vault includes:
- `%LOCALAPPDATA%\MeetingAssistant\Recordings`: temporary mixed WAV files during active/finalizing runs; stale files are deleted at startup and completed runs delete their temporary audio.
- `%LOCALAPPDATA%\MeetingAssistant\SpeakerIdentity\speaker-identities.db`: local SQLite speaker identity database, aliases, meeting references, and bounded voice snippets.
- `%LOCALAPPDATA%\MeetingAssistant\FunASR\models`: persistent FunASR model and hotword cache when the managed FunASR backend is enabled.
- `%LOCALAPPDATA%\MeetingAssistant\Pyannote\models`: persistent pyannote/Hugging Face/torch cache when pyannote diarization or validation is enabled.
- `%TEMP%\MeetingAssistant\Logs\meeting-assistant.log`: application-owned rotating logs; the snippets starter separately captures stdout/stderr under `%LOCALAPPDATA%`.
- `%LOCALAPPDATA%\MeetingAssistant\Recordings`: mixed WAV files are normally deleted after completion, and unqueued stale files are deleted at startup. If an Azure stop cannot drain within `Recording:StopProcessingTimeout`, the WAV plus a JSON item under `offline-transcription-backlog` are retained and retried every minute. Both are removed only after successful replay, transcript finalization, and summary processing.
- `%LOCALAPPDATA%\MeetingAssistant\SpeakerIdentity\speaker-identities.db`: SQLite identities, aliases, meeting references, and bounded voice snippets.
- `%LOCALAPPDATA%\MeetingAssistant\FunASR\models` and `%LOCALAPPDATA%\MeetingAssistant\Pyannote\models`: persistent model, hotword, Hugging Face, and torch caches for optional local backends.
- `%TEMP%\MeetingAssistant\Logs\meeting-assistant.log`: application log with four rotated predecessors. Paths, transcript text, agent diagnostics, and provider errors can make these logs sensitive.
The repo intentionally ignores local rule files, local appsettings overrides, models, recordings, runtime caches, build output, and temporary publish folders.
Abort is destructive: it removes the active run's note, transcript, context, summary, and linked screenshot attachments and skips summarization. A normal stop below `Recording:MinimumCompletedMeetingDuration`, or an otherwise content-empty stop, can also remove generated artifacts.
## External Data Boundaries
“Local” describes control and durable storage, not every processing step:
- The default `azure-speech` provider sends mixed meeting audio and dictation phrase hints to Azure AI Speech. Azure-backed speaker matching also sends selected voice audio.
- Summary, screenshot OCR, and interactive-agent requests go to the configured OpenAI-compatible Responses endpoint. They can include meeting/transcript/project text, screenshots, configuration, logs, and speaker samples when corresponding tools are used. The checked-in endpoint is a loopback proxy; its ultimate provider, data path, and retention policy are outside this repository.
- Outlook Classic access is local COM and reads appointment metadata; it does not provide the primary capture path.
- A managed FunASR run pulls its configured image, removes any same-named container, starts a disposable container privileged by default, publishes the configured host port, and mounts the model/hotword cache. Pyannote may build a local image and starts disposable containers with the input WAV mounted read-only and its model cache read/write. These paths require Docker Desktop or a compatible Docker CLI and may download images/models from external registries.
## Configuration
Configuration is normal .NET configuration under `MeetingAssistant`; the checked-in `MeetingAssistant/appsettings.json` is the canonical example. Keep secret values out of source and use the configured environment-variable names instead.
`MeetingAssistant/appsettings.json` is the canonical configuration example. Keep secret values out of tracked JSON and local rule/config files out of source control.
Important settings:
The settings with the largest operational effect are:
- `Vault`: controls the Obsidian vault root and the relative folders for notes, transcripts, summaries, assistant context, project knowledge, and dictation words.
- `Recording:TranscriptionProvider`: selects `azure-speech`, `funasr`, or `whisper-local`.
- `Recording:MicrophoneDeviceId`: optionally pins the Windows microphone endpoint; blank follows the Windows default, and the tray icon can override it for later starts.
- `Recording:TemporaryRecordingsFolder`: controls temporary mixed WAV storage.
- `Recording:InactivitySafeguard`: prompts and then auto-stops forgotten silent recordings without aborting artifacts.
- `LaunchProfiles`: overlays named profile settings onto the default profile; profile hotkeys must be distinct.
- `Automation:RulesPath`: points to the local YAML workflow-rules file. The default `meeting-rules.local.yaml` is ignored by git.
- `CalendarRecordingPrompts`: enables Outlook Classic Teams-start prompts on Windows.
- `Screenshots`: controls the capture hotkey, attachment folder, and optional OCR/vision model.
- `Agent`: configures the OpenAI-compatible summary/project agent endpoint, model, retries, output limits, and compaction.
- `WorkflowRulesEditor`: optionally overrides the agent settings for the tray-launched assistant.
- `Vault`: selects the durable vault and artifact/project locations.
- `Recording:TranscriptionProvider`: selects `azure-speech`, `funasr`, or `whisper-local`; the latter requires a local Whisper model file.
- `Recording:MicrophoneDeviceId`, mix gains, stop timeout, minimum duration, and temporary folder: control capture selection, audio, cleanup, and Azure backlog behavior.
- `Recording:InactivitySafeguard`: prompts and can auto-finish a run after no new transcript text; it is not an audio-silence detector.
- `LaunchProfiles`: overlay named recording/ASR/agent settings and require distinct hotkeys.
- `Automation:RulesPath`: points to the local YAML rules file, normally ignored `meeting-rules.local.yaml`.
- `CalendarRecordingPrompts` and `Screenshots`: control Outlook prompts, capture, attachments, and configured OCR.
- `Agent` and `WorkflowRulesEditor`: select the Responses endpoint/model, streaming or non-streaming transport, reasoning, retry, output, and compaction behavior; the available tools are defined by the application.
Required or commonly used secrets:
- `AZURE_SPEECH_KEY` for Azure Speech live transcription and speaker matching.
- `LITELLM_API_KEY` for summary, OCR, and workflow editor agents when their effective endpoint requires an API key.
- `HF_TOKEN` for pyannote model access when pyannote diarization or validation is enabled.
See `docs/meeting-assistant-configuration.md` for the full configuration reference.
## Integrations
- **Obsidian vault**: primary durable store for notes, transcripts, summaries, assistant context, project files, and generated links.
- **Outlook Classic on Windows**: optional COM metadata lookup and scheduled Teams-meeting start prompts.
- **Azure AI Speech**: default checked-in ASR path, live diarized conversation transcription, and speaker identity matching.
- **FunASR**: optional WebSocket streaming ASR. When managed backend startup is enabled, the app pulls and runs the configured Docker image as `meeting-assistant-funasr` on port `10095`.
- **Whisper.NET plus pyannote**: optional local Whisper fallback and Docker-backed final diarization.
- **LiteLLM/OpenAI-compatible Responses endpoint**: summary generation, screenshot OCR fallback, project tools, the tray-launched assistant, and retry flows.
- **Docker Desktop or compatible Docker CLI**: required only for managed FunASR and pyannote paths.
## Workflow Rules And Agents
Meeting-specific automation lives in a local YAML file, not in committed personal rules. Rules can trigger on meeting creation, assistant-context state transitions, identified speakers, transcript-line writes, and added attendees. They can add/remove attendees, set supported properties, add context, add projects, rewrite a transcript line, and transform an attendee name before it is stored.
The tray menu exposes `Open agent`, which opens the `Meeting Summary Agent` window. It can edit workflow rules with validation, inspect logs and health/status, manage speaker identities and samples, run ASR diagnostics, and read/write scoped meeting/project artifacts through explicit tools.
Detailed workflow syntax and extension guidance live in `docs/meeting-workflow-engine.md`.
Common secret environment variables are `AZURE_SPEECH_KEY`, `LITELLM_API_KEY`, and `HF_TOKEN`. See `docs/meeting-assistant-configuration.md` for the full setting reference and `docs/meeting-workflow-engine.md` for rule syntax and safety behavior.
## Development And CI
The repo builds against .NET 10 and targets both `net10.0` and `net10.0-windows10.0.19041.0` for the app. Tests target `net10.0`.
Behavior changes are OpenSpec-driven and test-first: update the relevant requirement/scenario, add a failing public behavior test, implement the smallest passing change, run focused tests and then the justified broader suite, and validate the active change with `openspec validate <change-id> --strict`. Documentation-only maintenance does not need a new OpenSpec change.
The Gitea workflow `.gitea/workflows/pr-push-build-and-test.yaml` runs on pull requests, pushes, and manual dispatch. It restores/builds the Windows target on an Ubuntu runner, installs Wine, downloads a matching Windows .NET SDK, and runs the test project through the Windows dotnet host under Wine.
The Gitea workflow runs for pull requests, pushes, and manual dispatch. It builds the Windows target on an Ubuntu runner, installs Wine plus a matching Windows .NET SDK, and runs the test project through the Windows host under Wine. It validates source; it does not publish or deploy the workstation application.
Before behavior changes:
## Operations And Limitations
1. Read `AGENTS.md`, this README, `openspec/config.yaml`, and relevant specs under `openspec/specs`.
2. Add or update the OpenSpec requirement/scenario for the behavior.
3. Add a failing behavior test through the public surface.
4. Implement the smallest passing change.
5. Run the narrowest useful tests, then broader tests when the blast radius justifies it.
6. Run `openspec validate <change-id> --strict` for active spec changes.
Documentation-only README maintenance does not need a new OpenSpec change.
## Operational Notes
- Treat the app as live user work. Check `/recording/status` before restarting, killing processes, deleting runtime files, or running scripts that might take over port `5090`.
- Abort is destructive for the active run: it removes that meeting's note, transcript, assistant context, summary if present, and linked screenshot attachments, and it skips summary generation.
- Too-short or empty normal stops can also delete generated artifacts instead of producing summaries.
- Summary and screenshot OCR retry links use `Api:PublicBaseUrl`, defaulting to `http://localhost:5090`.
- The workflow reload endpoint reloads configuration for future workflow reads, but a meeting run that already captured options may continue with those captured options.
- Public hostnames and homelab ingress are intentionally out of scope for this repo.
- Treat recording, transcription drain, speaker finalization, OCR, and summarization as live user work. Never restart, kill, or clean runtime files until `/recording/status` is idle unless interruption is explicitly intended.
- Summary and screenshot retry links use `Api:PublicBaseUrl`, which defaults to `http://localhost:5090`.
- Workstation startup is external to this repository. On `NA-EXC765X84` as of 2026-08-11, the installed Windows Startup shortcut invokes the restart helper at sign-in, so it republishes and replaces the process; it differs from the start-only shortcut tracked in `Manuel/snippets`, and the reason for that local deviation is not documented.
- No public hostname, homelab ingress, always-on deployment, or remote-service availability contract is owned here.
## More Documentation
- `docs/meeting-assistant-configuration.md`: full configuration reference.
- `docs/meeting-workflow-engine.md`: workflow rules engine reference.
- `docs/meeting-assistant-configuration.md`: complete configuration and backend behavior.
- `docs/meeting-workflow-engine.md`: workflow triggers, conditions, templates, steps, and editor behavior.
- `openspec/specs`: accepted behavioral requirements.
- `openspec/changes/archive`: archived change proposals and designs behind the accepted specs.
- `openspec/changes`: active change specs and designs, including implemented work not yet archived.
- `openspec/changes/archive`: historical proposals and decisions behind accepted behavior.