Public Access
Copy meeting projects to summary frontmatter
This commit is contained in:
@@ -56,6 +56,8 @@ public sealed class MeetingSummaryToolTests
|
|||||||
Assert.Contains("end_time: \"2026-05-20T10:30:00.0000000+02:00\"", summary);
|
Assert.Contains("end_time: \"2026-05-20T10:30:00.0000000+02:00\"", summary);
|
||||||
Assert.Contains("attendees:", summary);
|
Assert.Contains("attendees:", summary);
|
||||||
Assert.Contains("- Ada", summary);
|
Assert.Contains("- Ada", summary);
|
||||||
|
Assert.Contains("projects:", summary);
|
||||||
|
Assert.Contains("- Meeting Assistant", summary);
|
||||||
Assert.Contains("meeting: \"[[../Notes/meeting|Meeting Note]]\"", summary);
|
Assert.Contains("meeting: \"[[../Notes/meeting|Meeting Note]]\"", summary);
|
||||||
Assert.Contains("transcript: \"[[../Transcripts/transcript|Transcript]]\"", summary);
|
Assert.Contains("transcript: \"[[../Transcripts/transcript|Transcript]]\"", summary);
|
||||||
Assert.Contains("assistant_context: \"[[../Assistant Context/context|Assistant Context]]\"", summary);
|
Assert.Contains("assistant_context: \"[[../Assistant Context/context|Assistant Context]]\"", summary);
|
||||||
@@ -106,6 +108,49 @@ public sealed class MeetingSummaryToolTests
|
|||||||
Assert.DoesNotContain("- Grace", summary);
|
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]
|
[Fact]
|
||||||
public async Task ReadToolsSupportClampedLineRanges()
|
public async Task ReadToolsSupportClampedLineRanges()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ public sealed class MeetingArtifactFrontmatter
|
|||||||
|
|
||||||
public List<string>? Attendees { get; set; }
|
public List<string>? Attendees { get; set; }
|
||||||
|
|
||||||
|
public List<string>? Projects { get; set; }
|
||||||
|
|
||||||
public string Meeting { get; set; } = "";
|
public string Meeting { get; set; } = "";
|
||||||
|
|
||||||
public string Transcript { get; set; } = "";
|
public string Transcript { get; set; } = "";
|
||||||
@@ -39,6 +41,7 @@ public static class MeetingArtifactFrontmatterRenderer
|
|||||||
AppendDateTime(builder, "start_time", frontmatter.StartTime);
|
AppendDateTime(builder, "start_time", frontmatter.StartTime);
|
||||||
AppendDateTime(builder, "end_time", frontmatter.EndTime);
|
AppendDateTime(builder, "end_time", frontmatter.EndTime);
|
||||||
AppendListIfNotNull(builder, "attendees", frontmatter.Attendees);
|
AppendListIfNotNull(builder, "attendees", frontmatter.Attendees);
|
||||||
|
AppendListIfNotNull(builder, "projects", frontmatter.Projects);
|
||||||
AppendQuotedIfNotEmpty(builder, "meeting", frontmatter.Meeting);
|
AppendQuotedIfNotEmpty(builder, "meeting", frontmatter.Meeting);
|
||||||
AppendQuotedIfNotEmpty(builder, "transcript", frontmatter.Transcript);
|
AppendQuotedIfNotEmpty(builder, "transcript", frontmatter.Transcript);
|
||||||
AppendQuotedIfNotEmpty(builder, "assistant_context", frontmatter.AssistantContext);
|
AppendQuotedIfNotEmpty(builder, "assistant_context", frontmatter.AssistantContext);
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ public sealed class MeetingSummaryTools
|
|||||||
string.IsNullOrWhiteSpace(summaryTitle) ? "Meeting Summary" : summaryTitle,
|
string.IsNullOrWhiteSpace(summaryTitle) ? "Meeting Summary" : summaryTitle,
|
||||||
artifacts.SummaryPath);
|
artifacts.SummaryPath);
|
||||||
frontmatter.Attendees = await ResolveSummaryAttendeesAsync(meetingNote);
|
frontmatter.Attendees = await ResolveSummaryAttendeesAsync(meetingNote);
|
||||||
|
frontmatter.Projects = ResolveSummaryProjects(meetingNote);
|
||||||
await File.WriteAllTextAsync(
|
await File.WriteAllTextAsync(
|
||||||
artifacts.SummaryPath,
|
artifacts.SummaryPath,
|
||||||
MeetingArtifactFrontmatterRenderer.Render(frontmatter, markdown));
|
MeetingArtifactFrontmatterRenderer.Render(frontmatter, markdown));
|
||||||
@@ -591,6 +592,13 @@ public sealed class MeetingSummaryTools
|
|||||||
: meetingNote.Frontmatter.Attendees.ToList();
|
: 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()
|
private async Task<List<string>?> ReadExistingSummaryAttendeesAsync()
|
||||||
{
|
{
|
||||||
if (!File.Exists(artifacts.SummaryPath))
|
if (!File.Exists(artifacts.SummaryPath))
|
||||||
|
|||||||
@@ -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 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.
|
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
|
- **WHEN** the summary agent rewrites the summary
|
||||||
- **THEN** Meeting Assistant preserves the existing summary attendees
|
- **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
|
#### Scenario: Summary waits for pending screenshot OCR
|
||||||
- **GIVEN** screenshot OCR is still running for a meeting
|
- **GIVEN** screenshot OCR is still running for a meeting
|
||||||
- **WHEN** transcript processing is ready to run the automatic summary
|
- **WHEN** transcript processing is ready to run the automatic summary
|
||||||
|
|||||||
Reference in New Issue
Block a user