Copy meeting projects to summary frontmatter

This commit is contained in:
2026-05-27 12:55:20 +02:00
parent 12832dde84
commit 4f0b52d7d0
4 changed files with 61 additions and 1 deletions
@@ -56,6 +56,8 @@ public sealed class MeetingSummaryToolTests
Assert.Contains("end_time: \"2026-05-20T10:30:00.0000000+02:00\"", summary);
Assert.Contains("attendees:", summary);
Assert.Contains("- Ada", summary);
Assert.Contains("projects:", summary);
Assert.Contains("- Meeting Assistant", summary);
Assert.Contains("meeting: \"[[../Notes/meeting|Meeting Note]]\"", summary);
Assert.Contains("transcript: \"[[../Transcripts/transcript|Transcript]]\"", summary);
Assert.Contains("assistant_context: \"[[../Assistant Context/context|Assistant Context]]\"", summary);
@@ -106,6 +108,49 @@ public sealed class MeetingSummaryToolTests
Assert.DoesNotContain("- Grace", summary);
}
[Fact]
public async Task WriteSummaryCopiesCurrentMeetingProjectsOverExistingSummaryProjects()
{
var root = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
var artifacts = new MeetingSessionArtifacts(
MeetingNotePath: Path.Combine(root, "Meetings", "Notes", "meeting.md"),
TranscriptPath: Path.Combine(root, "Meetings", "Transcripts", "transcript.md"),
AssistantContextPath: Path.Combine(root, "Meetings", "Assistant Context", "context.md"),
SummaryPath: Path.Combine(root, "Meetings", "Summaries", "summary.md"));
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.MeetingNotePath)!);
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.SummaryPath)!);
await File.WriteAllTextAsync(
artifacts.MeetingNotePath,
"""
---
title: Meeting
projects:
- Current Project
- Second Project
---
""");
await File.WriteAllTextAsync(
artifacts.SummaryPath,
"""
---
title: Existing
projects:
- Stale Project
---
Old summary.
""");
var tools = new MeetingSummaryTools(artifacts);
await tools.WriteSummary("# Summary");
var summary = await File.ReadAllTextAsync(artifacts.SummaryPath);
Assert.Contains("projects:", summary);
Assert.Contains("- Current Project", summary);
Assert.Contains("- Second Project", summary);
Assert.DoesNotContain("- Stale Project", summary);
}
[Fact]
public async Task ReadToolsSupportClampedLineRanges()
{
@@ -12,6 +12,8 @@ public sealed class MeetingArtifactFrontmatter
public List<string>? Attendees { get; set; }
public List<string>? Projects { get; set; }
public string Meeting { get; set; } = "";
public string Transcript { get; set; } = "";
@@ -39,6 +41,7 @@ public static class MeetingArtifactFrontmatterRenderer
AppendDateTime(builder, "start_time", frontmatter.StartTime);
AppendDateTime(builder, "end_time", frontmatter.EndTime);
AppendListIfNotNull(builder, "attendees", frontmatter.Attendees);
AppendListIfNotNull(builder, "projects", frontmatter.Projects);
AppendQuotedIfNotEmpty(builder, "meeting", frontmatter.Meeting);
AppendQuotedIfNotEmpty(builder, "transcript", frontmatter.Transcript);
AppendQuotedIfNotEmpty(builder, "assistant_context", frontmatter.AssistantContext);
@@ -107,6 +107,7 @@ public sealed class MeetingSummaryTools
string.IsNullOrWhiteSpace(summaryTitle) ? "Meeting Summary" : summaryTitle,
artifacts.SummaryPath);
frontmatter.Attendees = await ResolveSummaryAttendeesAsync(meetingNote);
frontmatter.Projects = ResolveSummaryProjects(meetingNote);
await File.WriteAllTextAsync(
artifacts.SummaryPath,
MeetingArtifactFrontmatterRenderer.Render(frontmatter, markdown));
@@ -591,6 +592,13 @@ public sealed class MeetingSummaryTools
: meetingNote.Frontmatter.Attendees.ToList();
}
private static List<string>? ResolveSummaryProjects(MeetingNote meetingNote)
{
return meetingNote.Frontmatter.Projects.Count == 0
? null
: meetingNote.Frontmatter.Projects.ToList();
}
private async Task<List<string>?> ReadExistingSummaryAttendeesAsync()
{
if (!File.Exists(artifacts.SummaryPath))
+5 -1
View File
@@ -61,7 +61,7 @@ After writing the meeting summary, the summary agent SHALL update existing proje
The assistant context note SHALL keep `title`, `start_time`, `end_time`, `meeting`, `transcript`, `summary`, `state`, and `agenda` in frontmatter. Meeting Assistant SHALL write `title` and `start_time` when the assistant context note is created, update `title` when later meeting metadata is discovered, and write `end_time` when transcript processing stops. When a Teams appointment is detected at recording start, the assistant context note SHALL also keep `scheduled_end` in frontmatter. The `state` value SHALL be one of `collecting metadata`, `transcribing`, `speaker recognition`, `summarizing`, `finished`, or `error`.
The summary note SHALL keep frontmatter links to meeting artifacts and SHALL copy `title`, `start_time`, `end_time`, and `attendees` from the meeting note when available. If the meeting note has no title, the summary agent SHALL be able to provide a title when writing the summary.
The summary note SHALL keep frontmatter links to meeting artifacts and SHALL copy `title`, `start_time`, `end_time`, `attendees`, and `projects` from the meeting note when available. If the meeting note has no title, the summary agent SHALL be able to provide a title when writing the summary.
When the summary note already has an `attendees` frontmatter property, Meeting Assistant SHALL preserve the existing summary attendees instead of overwriting them from the meeting note.
@@ -88,6 +88,10 @@ The summary agent SHALL be able to read and write the assistant context body as
- **WHEN** the summary agent rewrites the summary
- **THEN** Meeting Assistant preserves the existing summary attendees
#### Scenario: Summary copies final meeting projects
- **WHEN** the summary agent writes a summary note after meeting projects have changed
- **THEN** the summary note frontmatter includes the final meeting note projects
#### Scenario: Summary waits for pending screenshot OCR
- **GIVEN** screenshot OCR is still running for a meeting
- **WHEN** transcript processing is ready to run the automatic summary