Public Access
Archive summary refinements and profile switching
PR and Push Build/Test / build-and-test (push) Successful in 6m37s
PR and Push Build/Test / build-and-test (push) Successful in 6m37s
This commit is contained in:
@@ -47,9 +47,11 @@ public sealed class MeetingSummaryToolTests
|
||||
Assert.Equal("User note line.", await tools.ReadUserNotes());
|
||||
Assert.Equal("", await tools.ReadGlossary());
|
||||
|
||||
Assert.False(tools.SummaryWasWritten);
|
||||
var result = await tools.WriteSummary("# Summary\n\n- Done.");
|
||||
|
||||
Assert.Equal(artifacts.SummaryPath, result);
|
||||
Assert.True(tools.SummaryWasWritten);
|
||||
var summary = await File.ReadAllTextAsync(artifacts.SummaryPath);
|
||||
Assert.Contains("title: Meeting", summary);
|
||||
Assert.Contains("start_time: \"2026-05-20T10:00:00.0000000+02:00\"", summary);
|
||||
@@ -151,6 +153,278 @@ public sealed class MeetingSummaryToolTests
|
||||
Assert.DoesNotContain("- Stale Project", summary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsCanAddAndRemoveMeetingAttendees()
|
||||
{
|
||||
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)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.MeetingNotePath,
|
||||
"""
|
||||
---
|
||||
title: Meeting
|
||||
attendees:
|
||||
- Ada
|
||||
- Grace Hopper <grace@example.com>
|
||||
projects:
|
||||
- Meeting Assistant
|
||||
transcript: "[[../Transcripts/transcript|Transcript]]"
|
||||
assistant_context: "[[../Assistant Context/context|Assistant Context]]"
|
||||
summary: "[[../Summaries/summary|Summary]]"
|
||||
---
|
||||
|
||||
User note line.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
Assert.Equal("Added attendee Manuel.", await tools.AddAttendee("Manuel"));
|
||||
Assert.Equal("Attendee Ada already exists.", await tools.AddAttendee("Ada"));
|
||||
Assert.Equal("Removed attendee Grace Hopper.", await tools.RemoveAttendee("Grace Hopper"));
|
||||
|
||||
var meetingNote = await File.ReadAllTextAsync(artifacts.MeetingNotePath);
|
||||
Assert.Contains("- Ada", meetingNote);
|
||||
Assert.Contains("- Manuel", meetingNote);
|
||||
Assert.DoesNotContain("Grace Hopper", meetingNote);
|
||||
Assert.Contains("User note line.", meetingNote);
|
||||
Assert.Contains("transcript: \"[[../Transcripts/transcript|Transcript]]\"", meetingNote);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsOverrideSpeakerInTranscriptAndRecordOverride()
|
||||
{
|
||||
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.TranscriptPath)!);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.AssistantContextPath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.TranscriptPath,
|
||||
"""
|
||||
# Meeting Transcript
|
||||
|
||||
[00:00:01] Guest-01: Hello from Sabrina.
|
||||
[00:00:02] Guest-02: Hello from someone else.
|
||||
""");
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.AssistantContextPath,
|
||||
"""
|
||||
---
|
||||
state: summarizing
|
||||
---
|
||||
|
||||
Existing context.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.OverrideSpeaker("Guest-01", "Sabrina");
|
||||
|
||||
Assert.Equal("Overrode speaker Guest-01 with Sabrina.", result);
|
||||
var transcript = await File.ReadAllTextAsync(artifacts.TranscriptPath);
|
||||
Assert.Contains("[00:00:01] Sabrina: Hello from Sabrina.", transcript);
|
||||
Assert.Contains("[00:00:02] Guest-02: Hello from someone else.", transcript);
|
||||
var context = await File.ReadAllTextAsync(artifacts.AssistantContextPath);
|
||||
Assert.Contains("state: summarizing", context);
|
||||
Assert.Contains("meeting-assistant:speaker-override", context);
|
||||
Assert.Contains("\"from\":\"Guest-01\"", context);
|
||||
Assert.Contains("\"to\":\"Sabrina\"", context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsRefuseOverrideWhenAssistantContextIsMissing()
|
||||
{
|
||||
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.TranscriptPath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.TranscriptPath,
|
||||
"""
|
||||
# Meeting Transcript
|
||||
|
||||
[00:00:01] Guest-01: Hello from Sabrina.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.OverrideSpeaker("Guest-01", "Sabrina");
|
||||
|
||||
Assert.StartsWith("Refused: assistant context file does not exist", result, StringComparison.Ordinal);
|
||||
Assert.False(File.Exists(artifacts.AssistantContextPath));
|
||||
var transcript = await File.ReadAllTextAsync(artifacts.TranscriptPath);
|
||||
Assert.Contains("[00:00:01] Guest-01: Hello from Sabrina.", transcript);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsRefuseOverrideWhenReplacementSpeakerAlreadyExistsWithoutMerge()
|
||||
{
|
||||
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.TranscriptPath)!);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.AssistantContextPath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.TranscriptPath,
|
||||
"""
|
||||
# Meeting Transcript
|
||||
|
||||
[00:00:01] Guest-01: I am probably Sabrina.
|
||||
[00:00:02] Sabrina: I am already in the transcript.
|
||||
""");
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.AssistantContextPath,
|
||||
"""
|
||||
---
|
||||
state: summarizing
|
||||
---
|
||||
|
||||
Existing context.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.OverrideSpeaker("Guest-01", "Sabrina");
|
||||
|
||||
Assert.Equal(
|
||||
"Refused: transcript already contains speaker Sabrina. Call override_speaker with merge=true only when you are certain both speakers are the same identity.",
|
||||
result);
|
||||
var transcript = await File.ReadAllTextAsync(artifacts.TranscriptPath);
|
||||
Assert.Contains("[00:00:01] Guest-01: I am probably Sabrina.", transcript);
|
||||
Assert.Contains("[00:00:02] Sabrina: I am already in the transcript.", transcript);
|
||||
var context = await File.ReadAllTextAsync(artifacts.AssistantContextPath);
|
||||
Assert.DoesNotContain("meeting-assistant:speaker-override", context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsMergeOverrideWhenReplacementSpeakerAlreadyExistsWithMerge()
|
||||
{
|
||||
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.TranscriptPath)!);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.AssistantContextPath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.TranscriptPath,
|
||||
"""
|
||||
# Meeting Transcript
|
||||
|
||||
[00:00:01] Guest-01: I am Sabrina.
|
||||
[00:00:02] Sabrina: I am already in the transcript.
|
||||
""");
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.AssistantContextPath,
|
||||
"""
|
||||
---
|
||||
state: summarizing
|
||||
---
|
||||
|
||||
Existing context.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.OverrideSpeaker("Guest-01", "Sabrina", merge: true);
|
||||
|
||||
Assert.Equal("Merged speaker Guest-01 into Sabrina.", result);
|
||||
var transcript = await File.ReadAllTextAsync(artifacts.TranscriptPath);
|
||||
Assert.Contains("[00:00:01] Sabrina: I am Sabrina.", transcript);
|
||||
Assert.Contains("[00:00:02] Sabrina: I am already in the transcript.", transcript);
|
||||
Assert.DoesNotContain("Guest-01:", transcript);
|
||||
var context = await File.ReadAllTextAsync(artifacts.AssistantContextPath);
|
||||
Assert.Contains("meeting-assistant:speaker-override", context);
|
||||
Assert.Contains("\"from\":\"Guest-01\"", context);
|
||||
Assert.Contains("\"to\":\"Sabrina\"", context);
|
||||
Assert.Contains("\"merge\":true", context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsDeleteIdentityInTranscriptAndRecordDeletion()
|
||||
{
|
||||
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.TranscriptPath)!);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(artifacts.AssistantContextPath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.TranscriptPath,
|
||||
"""
|
||||
# Meeting Transcript
|
||||
|
||||
[00:00:01] Sabrina: This was incorrectly matched.
|
||||
[00:00:02] Removed-1: An earlier removed speaker.
|
||||
[00:00:03] Guest-02: Hello from someone else.
|
||||
[00:00:04] Sabrina: Another incorrect line.
|
||||
""");
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.AssistantContextPath,
|
||||
"""
|
||||
---
|
||||
state: summarizing
|
||||
---
|
||||
|
||||
Existing context.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.DeleteIdentity("Sabrina");
|
||||
|
||||
Assert.Equal("Deleted identity Sabrina and relabeled transcript speaker mentions to Removed-2.", result);
|
||||
var transcript = await File.ReadAllTextAsync(artifacts.TranscriptPath);
|
||||
Assert.Contains("[00:00:01] Removed-2: This was incorrectly matched.", transcript);
|
||||
Assert.Contains("[00:00:02] Removed-1: An earlier removed speaker.", transcript);
|
||||
Assert.Contains("[00:00:03] Guest-02: Hello from someone else.", transcript);
|
||||
Assert.Contains("[00:00:04] Removed-2: Another incorrect line.", transcript);
|
||||
Assert.DoesNotContain("Sabrina:", transcript);
|
||||
var context = await File.ReadAllTextAsync(artifacts.AssistantContextPath);
|
||||
Assert.Contains("state: summarizing", context);
|
||||
Assert.Contains("meeting-assistant:speaker-identity-deletion", context);
|
||||
Assert.Contains("\"identity\":\"Sabrina\"", context);
|
||||
Assert.Contains("\"replacement\":\"Removed-2\"", context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsRefuseDeleteIdentityWhenAssistantContextIsMissing()
|
||||
{
|
||||
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.TranscriptPath)!);
|
||||
await File.WriteAllTextAsync(
|
||||
artifacts.TranscriptPath,
|
||||
"""
|
||||
# Meeting Transcript
|
||||
|
||||
[00:00:01] Sabrina: This was incorrectly matched.
|
||||
""");
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.DeleteIdentity("Sabrina");
|
||||
|
||||
Assert.StartsWith("Refused: assistant context file does not exist", result, StringComparison.Ordinal);
|
||||
Assert.False(File.Exists(artifacts.AssistantContextPath));
|
||||
var transcript = await File.ReadAllTextAsync(artifacts.TranscriptPath);
|
||||
Assert.Contains("[00:00:01] Sabrina: This was incorrectly matched.", transcript);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadToolsSupportClampedLineRanges()
|
||||
{
|
||||
@@ -236,6 +510,23 @@ public sealed class MeetingSummaryToolTests
|
||||
Assert.Contains("replacement\ninserted\nline two", context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsRefuseWriteContextWhenAssistantContextIsMissing()
|
||||
{
|
||||
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"));
|
||||
var tools = new MeetingSummaryTools(artifacts);
|
||||
|
||||
var result = await tools.WriteContext("new context");
|
||||
|
||||
Assert.StartsWith("Refused: assistant context file does not exist", result, StringComparison.Ordinal);
|
||||
Assert.False(File.Exists(artifacts.AssistantContextPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolsWriteAssistantContextStripsAccidentalFrontmatterFromReplacementBody()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user