From 6354da37c173a3804f2b1a76e8d02d6f6ebb8771 Mon Sep 17 00:00:00 2001 From: FNS Service Date: Mon, 27 Apr 2026 23:13:16 +0800 Subject: [PATCH] Update from Sync Service --- Prompts/Meeting Research Prompt.md | 177 +++++++++++ .../Meeting Transcript Summarizer Prompt.md | 217 +++++++++++++ Prompts/Speaker Auto-Tag Prompt.md | 291 ++++++++++++++++++ 3 files changed, 685 insertions(+) create mode 100755 Prompts/Meeting Research Prompt.md create mode 100755 Prompts/Meeting Transcript Summarizer Prompt.md create mode 100755 Prompts/Speaker Auto-Tag Prompt.md diff --git a/Prompts/Meeting Research Prompt.md b/Prompts/Meeting Research Prompt.md new file mode 100755 index 0000000..f87b1a2 --- /dev/null +++ b/Prompts/Meeting Research Prompt.md @@ -0,0 +1,177 @@ +# Meeting Research Prompt + +Standalone prompt for pre-meeting research. Reads selected vault notes (People profiles, prior meeting notes, project docs) and the meeting note, then writes a structured research briefing into the meeting note so the user is prepared before the meeting starts. + +**Invocation pattern:** +``` +claude --dangerously-skip-permissions 'Follow the instructions in /abs/path/Meeting Research Prompt.md. Meeting note: Meetings/2026-03-25 - Weekly Team Sync.md. Research notes: People/Alice Smith.md, Meetings/2026-03-18 - Weekly Team Sync.md, Projects/Alpha.md.' +``` + +**Optional parameters:** +- `People Folder:` — folder name for People notes (default: `People`) +- `Additional instructions:` — free-text user instructions appended to the invocation + +--- + +## Step 1: Parse Invocation Parameters + +Extract from the calling message: +- `Meeting note:` → vault-relative path to the meeting note file +- `Research notes:` → comma-separated list of vault-relative paths to context notes +- `People Folder:` → folder for People notes (optional) +- `Additional instructions:` → free-text guidance from the user (optional) + +**Vault root:** The current working directory is the vault root. All vault-relative paths in the calling message resolve from there. + +**Path resolution:** Try each path as-is first. If not found, prepend vault root and try again. If a research note is not found, skip it with a warning rather than failing — the user may have renamed or moved it. + +--- + +## Step 2: Read Meeting Note + +Use the **Read tool** to open the meeting note. Extract from frontmatter: + +| Field | Purpose | +|-------|---------| +| `meeting_subject` | Meeting title for context | +| `meeting_date` | Meeting date | +| `meeting_start` | Start time | +| `meeting_end` | End time | +| `meeting_invitees` | Attendee list (wikilinks) | +| `is_recurring` | Whether this is a recurring meeting | + +**Error conditions:** +- File does not exist → error: "Meeting note not found: [path]. Check the path and try again." + +--- + +## Step 3: Read Research Notes + +Use the **Read tool** to open each research note. For each note, capture: + +1. **Frontmatter metadata** — any structured data (role, title, email, dates, etc.) +2. **Body content** — the full note content + +Categorize each note by type: +- **Person note** — has frontmatter with name/role/email fields, or is in the People folder +- **Meeting note** — has `meeting_subject` and `meeting_date` in frontmatter +- **Project/other note** — everything else + +--- + +## Step 4: Gather Additional Context + +If a `People Folder` was provided, cross-reference meeting invitees with People notes: + +1. Parse `meeting_invitees` wikilinks from the meeting note +2. For any invitee whose People note was NOT already included in the research notes, do a quick read of their note (if it exists) to get their role/title +3. This provides baseline context on all attendees without requiring the user to manually select every person + +--- + +## Step 5: Follow Additional Instructions + +If `Additional instructions` were provided, incorporate them into the research. These may include: +- Specific questions to answer about attendees or topics +- Directions to focus on particular aspects (e.g., "focus on project status updates from the last month") +- Requests to cross-reference with other vault content +- Context about why this research is needed + +Treat additional instructions as high-priority guidance that shapes the research output. + +--- + +## Step 6: Generate Research Briefing + +Create a structured research briefing with these sections: + +### Briefing Structure + +```markdown +## Research + +### Attendees + +| Name | Role | Context | +|------|------|---------| +| [[Name]] | Title / Team | Key context from People note or prior meetings | + +### Background + +[2-4 paragraphs synthesizing relevant context from the research notes. Connect the dots between attendees, prior meetings, and project context. Focus on what the user needs to know going into this meeting.] + +### Prior Meeting Outcomes + +- **YYYY-MM-DD — Meeting Subject** — Key outcomes, open action items, unresolved topics +- [One bullet per prior meeting note provided] + +### Open Questions + +- [Questions the user might want to raise based on the research] +- [Unresolved items from prior meetings] +- [Gaps in context that could be clarified in this meeting] +``` + +### Briefing Guidelines + +- **Be actionable:** Focus on information that helps the user prepare, not exhaustive summaries +- **Wikilink all names:** Wrap ALL person names in `[[Name]]` wikilinks +- **Connect the dots:** Don't just summarize each note in isolation — identify relationships, continuity from prior meetings, and evolving topics +- **Highlight changes:** If a person's role or a project's status has changed since the last meeting, call it out +- **Respect the user's time:** Keep it scannable. Use tables for structured data, bullets for lists, and bold for emphasis +- **Omit empty sections:** If there are no prior meetings to reference, skip "Prior Meeting Outcomes". If the attendee list is trivial (1:1 with someone well-known), keep the table minimal +- **Additional instructions first:** If the user provided specific instructions, make sure those are addressed prominently in the briefing + +### Quality Checklist + +Before writing the briefing, verify: +- [ ] ALL person names wrapped in `[[Name]]` wikilinks +- [ ] Attendee table includes all meeting invitees (not just those in research notes) +- [ ] Prior meeting outcomes include any open action items still relevant +- [ ] Additional user instructions have been addressed +- [ ] No important context from the research notes is missing + +--- + +## Step 7: Write Research to Meeting Note + +Use the **Edit tool** to insert the research briefing into the meeting note. + +**Placement:** Insert the `## Research` section after the frontmatter closing `---` and any existing template content (like attendee lists or meeting links). Place it BEFORE any `## Notes`, `## Summary`, or other user/LLM-generated sections. + +**If a `## Research` section already exists:** Replace it entirely with the new briefing. Preserve all other sections. + +**Strategy:** +1. Read the current meeting note content +2. Identify the insertion point (after frontmatter/template header, before other content sections) +3. If `## Research` already exists, replace only that section +4. If it doesn't exist, insert the new section at the appropriate location +5. Use a single Edit call to write the briefing + +--- + +## Step 8: Update Frontmatter + +After successfully writing the research briefing, update the meeting note frontmatter: + +The `research_notes` field should already be set by the plugin (listing the selected notes as wikilinks). No additional frontmatter changes are needed. + +--- + +## Step 9: Report Results + +Summarize to the user: + +``` +Meeting research complete for: [meeting note filename] + +Research sources: +- [N] vault notes analyzed +- [N] attendees profiled + +Sections written: +- Attendees ([N] people) +- Background ([N] paragraphs) +- Prior Meeting Outcomes ([N] meetings referenced) +- Open Questions ([N] items) +``` diff --git a/Prompts/Meeting Transcript Summarizer Prompt.md b/Prompts/Meeting Transcript Summarizer Prompt.md new file mode 100755 index 0000000..0be94d3 --- /dev/null +++ b/Prompts/Meeting Transcript Summarizer Prompt.md @@ -0,0 +1,217 @@ +# Meeting Transcript Summarizer Prompt + +Standalone prompt for summarizing a meeting transcript and writing the summary into the meeting note. Reads the meeting note to find the linked transcript, extracts content, generates a structured summary, and writes it back into the meeting note. + +**Invocation pattern:** +``` +claude --dangerously-skip-permissions 'Follow the instructions in /abs/path/Meeting Transcript Summarizer Prompt.md. Meeting note: Meetings/2026-03-05 - Weekly Team Sync.md.' +``` + +--- + +## Step 1: Parse Invocation Parameters + +Extract from the calling message: +- `Meeting note:` → vault-relative path to the meeting note file + +**Vault root:** The current working directory is the vault root. All vault-relative paths in the calling message resolve from there. + +**Path resolution:** Try the path as-is first. If not found, prepend vault root and try again. If still not found, error with: "Meeting note not found: [path]. Check the path and try again." + +--- + +## Step 2: Read Meeting Note + +Use the **Read tool** to open the meeting note. All meeting metadata (subject, date, times, invitees) is already in the frontmatter — the LLM sees it directly when reading the file. + +The only fields you need to explicitly extract and act on: + +| Field | Purpose | +|-------|---------| +| `transcript` | Wiki-link to follow (e.g. `[[Transcripts/...]]`) | +| `pipeline_state` | Gate check in Step 4 | +| `is_recurring` | Meeting type detection in Step 5 | + +**Error conditions:** +- File does not exist → error as described in Step 1 +- No `transcript` field in frontmatter → error: "Meeting note has no linked transcript. Link a recording first." + +**Resolve transcript:** Strip `[[` and `]]` from the transcript link, then read the transcript file. + +--- + +## Step 3: Read Transcript + +Use the **Read tool** to open the transcript file. Extract from frontmatter: + +| Field | Notes | +|-------|-------| +| `speakers` | Array with name, id, confidence, evidence per speaker | +| `pipeline_state` | Current transcript pipeline state | +| `confirmed_speakers` | Array of People note links for confirmed speakers | +| `meeting_note` | Backlink to the meeting note | + +Read the full transcript body content — this is the source material for summarization. + +**Identify the microphone user:** Find the speaker entry where `evidence` contains `microphone` or where `original_name` is `Microphone`. That speaker is the user who recorded the meeting. + +--- + +## Step 4: Pipeline State Check + +Read `pipeline_state` from the **meeting note** frontmatter: + +- `pipeline_state: summarized` → warn user: + ``` + This meeting has already been summarized (pipeline_state: summarized). Re-run summarization anyway? + ``` + Use AskUserQuestion with options "Re-run" / "Cancel". Stop if user selects Cancel. +- `pipeline_state: titled` → warn user: + ``` + Speakers have not been tagged yet (pipeline_state: titled). Summarization works best with tagged speakers. Proceed anyway? + ``` + Use AskUserQuestion with options "Proceed" / "Cancel". Stop if user selects Cancel. +- `pipeline_state: tagged` → proceed normally. + +--- + +## Step 5: Analyze Meeting Context + +Before summarizing, gather context: + +1. **Meeting type detection:** Determine from subject line and attendee count: + - 1:1 meeting (2 attendees) + - Small group (3-5 attendees) + - Large meeting (6+ attendees) + - Recurring meeting (check `is_recurring` in meeting note frontmatter) + +2. **Speaker roles:** Map speakers to their roles based on People notes (if `confirmed_speakers` links exist, read those notes for role/title information). + +3. **Microphone user perspective:** The summary should be written from the perspective of the microphone user — they are the note-taker. Identify what they said, what was directed at them, and what action items they were assigned. + +--- + +## Step 6: Generate Summary + +Create a structured summary with these sections: + +### Summary Structure + +```markdown +## Summary + +[2-4 sentence executive summary of the meeting. Lead with the most important outcome or decision. Written in past tense. Use [[Name]] wikilinks for all people.] + +## Key Decisions + +- [Decision 1 — [[who]] decided, what was decided] +- [Decision 2] + +## Action Items + +- [ ] Task description — [[Owner]] / [[Requestor]] — YYYY-MM-DD — 🔺 +- [ ] Task with same owner/requestor — [[Owner]] — YYYY-MM-DD +- [ ] Task for microphone user — [[Dan Loomis]] — YYYY-MM-DD + +## Key Discussion Points + +- **Topic Label** - Detailed explanation with [[Name]] references, specifics, and context +- **Topic Label** - Detailed explanation with [[Name]] references, specifics, and context +``` + +### Summary Guidelines + +- **Be concise:** Each section should capture the essence, not transcribe the conversation +- **Attribute decisions:** Always note who made or approved a decision +- **Wikilink all names:** Wrap ALL person names in `[[Name]]` in Summary, Key Decisions, Action Items, and Key Discussion Points +- **Action item format:** `- [ ] Task — [[Owner]] / [[Requestor]] — YYYY-MM-DD — 🔺` (see Action Item Rules below) +- **Discussion points format:** Each bullet starts with a **bold topic label** (2-5 words), then a dash, then detailed explanation with `[[Name]]` references, specific details, and context +- **Skip small talk:** Don't include greetings, weather chat, or off-topic banter +- **Preserve technical details:** Keep specific numbers, dates, names, and technical terms accurate +- **Flag uncertainty:** If the transcript is unclear on a point, note it with [unclear] rather than guessing + +### Action Item Rules + +**Format:** `- [ ] Task — [[Owner]] / [[Requestor]] — YYYY-MM-DD — 🔺` +- If requestor same as owner, omit `/ [[Requestor]]` +- Fields separated by ` — ` (space-em-dash-space) +- Priority emoji at end (omit if standard priority) + +**Due Dates:** +- Default: Meeting Date + 7 calendar days +- "tomorrow" → Meeting Date + 1 +- "next week" → Meeting Date + 7 +- "end of week" → Next Friday from Meeting Date +- "ASAP" / "urgent" → Meeting Date + 2 + +**Priority:** +- ⏫ High: Blocking others, executive request, time-sensitive, security/compliance +- 🔼 Medium: Important but flexible +- (omit): Standard work, no urgency +- 🔽 Low: Nice-to-have, backlog + +**Meeting Ownership (determines quantity):** +- Microphone user IS facilitator → capture ALL action items +- Microphone user is NOT facilitator → ALL microphone user items + up to 5-7 important others +- Default to "not facilitator" when unclear + +### Quality Checklist + +Before writing the summary, verify: +- [ ] ALL person names wrapped in `[[Name]]` wikilinks +- [ ] Every action item uses `— [[Owner]] — YYYY-MM-DD` format with due date +- [ ] Every Key Discussion Point starts with a **bold topic label** +- [ ] Key decisions are captured with context +- [ ] No important topics from the transcript are missing +- [ ] Meeting ownership correctly determined for action item quantity + +--- + +## Step 7: Write Summary to Meeting Note + +Use the **Edit tool** to insert the summary into the meeting note. + +**Placement:** Insert the summary content after the frontmatter closing `---` and any existing template content (like attendee lists or meeting metadata). If the note already has a `## Summary` section, replace it entirely. + +**Preserve existing content:** The meeting note may contain hand-written notes (e.g. a `## Notes` section), Teams dial-in blocks, or other user-added content. Any content that is NOT part of the generated summary sections (`## Summary`, `## Key Decisions`, `## Action Items`, `## Key Discussion Points`) must be preserved. Place the generated summary sections between the header/template content and any existing user content. + +**Strategy:** +1. Read the current meeting note content +2. Identify any user-written content (sections other than Summary/Key Decisions/Action Items/Key Discussion Points, such as `## Notes`, meeting links, etc.) +3. Find the insertion point (after frontmatter and any header/metadata sections) +4. If a `## Summary` section already exists, replace only the generated sections (Summary through Key Discussion Points), preserving everything before and after +5. If no summary exists, insert the generated sections after the last frontmatter/template content and before any user-written sections +6. Use a single Edit call to write the summary + +--- + +## Step 8: Update Pipeline State + +> **CRITICAL — DO NOT SKIP THIS STEP.** The plugin UI relies on `pipeline_state: summarized` to mark the Summary pill as complete. If you do not update both files, the user will see an incomplete workflow and have to fix it manually. This step is mandatory even if the summary was short or the meeting was trivial. + +After successfully writing the summary, you MUST update **both** files: + +1. **Meeting note:** Replace `pipeline_state: tagged` with `pipeline_state: summarized` in the frontmatter +2. **Transcript:** Replace `pipeline_state: tagged` with `pipeline_state: summarized` in the frontmatter + +**Edit strategy:** Use the Edit tool to make a targeted find-and-replace of the `pipeline_state: tagged` line in each file's YAML frontmatter. Do this as two separate Edit calls — one for the meeting note, one for the transcript. Do NOT proceed to Step 9 until both edits are confirmed successful. + +**Verification:** After editing, re-read the frontmatter of both files and confirm `pipeline_state: summarized` is present. If either edit failed, retry before continuing. + +--- + +## Step 9: Report Results + +Summarize to the user: + +``` +Meeting summary complete for: [meeting note filename] + +Sections written: +- Summary (X sentences) +- Key Decisions (X items) +- Action Items (X items, Y assigned to you) +- Key Discussion Points (X topics) + +Pipeline state → summarized +``` diff --git a/Prompts/Speaker Auto-Tag Prompt.md b/Prompts/Speaker Auto-Tag Prompt.md new file mode 100755 index 0000000..c1daf54 --- /dev/null +++ b/Prompts/Speaker Auto-Tag Prompt.md @@ -0,0 +1,291 @@ + + +Performance-optimized version of Speaker Auto-Tag Prompt with batched People note resolution and context-based confidence refinement. + +Self-contained prompt for tagging unidentified speakers in one transcript stub using vault data (People notes, calendar attendees). Returns proposed speaker mappings; the caller handles review and writes. + +**CRITICAL:** Never read the full transcript in a single Read call. Transcripts exceed the 10k token file-read limit. Use chunked reads (limit=500 per chunk). See Step 2. + +**OPTIMIZATION FOCUS:** Read People notes deterministically from the parent meeting invitees list (no broad name-based glob), batch all invitee reads in parallel, build a comprehensive context table before analysis, and use role/context matching for confidence refinement. + +--- + +## Step 1: Setup + +If Read, Glob, Grep, and Bash are not already available, load them: ToolSearch("select:Read,Glob,Grep,Bash") + +Extract from the calling message: +- Transcript — path to the transcript stub file +- Microphone user — full name of the person at the microphone +- Calendar Attendees — pre-fetched attendee context (optional; skips Step 4 if provided) +- People Roster — pre-built People context table (optional; skips Step 3 if provided — use as the People context table directly) +- People Folder — folder name for People notes (default: People) +- Output format — expected output format for the mapping + +Path resolution: try the Transcript path as-is. If not found, error with the path. + +Vault root derivation: extract vault root from the transcript path by removing the transcript folder suffix. Use this root combined with People Folder for all lookups. + +Derived values (from filename, no read needed): +- MEETING_TITLE — strip date prefix (first 13 chars for "YYYY-MM-DD - " or first 11 chars for "YYYY-MM-DD ") and " - Transcript" suffix +- SANITIZED_SUBJECT — take MEETING_TITLE, replace /, \, : with hyphens. Used for cache Glob in Step 2. + +--- + +## Step 2: Read Transcript + Glob Roster Cache + Glob People Notes + +**Round 1 — issue in a single parallel batch:** +- Read(transcript_path, limit=60) — frontmatter only +- Bash: wc -l transcript_path — total line count +- Glob("Caches/Speaker Rosters/SANITIZED_SUBJECT.md") — roster cache for Step 5 + +Extract from frontmatter: +- session_id (or macwhisper_session_id for WhisperCal stubs) +- speakers — array with name, id, stub flag, line_count per speaker +- meeting_note — wiki-link to parent meeting note (used in Step 3a) +- calendar_event — event title, "none", or absent +- calendar_attendees — array of plain string names (or invitees for WhisperCal, or meeting_invitees for wiki-link format like "- [[Name]]") +- meeting_subject — used to validate cache match +- is_recurring — enables roster cache path +- pipeline_state — current pipeline state +- tags — detect WhisperCal stubs (tags: [transcript]) + +Pipeline state: any value (tagged, extracted, summarized, titled) or absent means proceed. + +**Round 2 — transcript body chunks in a single parallel batch.** Use the line count from Round 1. Read in chunks of 500 lines, starting after the frontmatter: +- Read(transcript_path, offset=61, limit=500) — chunk 1 +- Read(transcript_path, offset=561, limit=500) — chunk 2 +- Read(transcript_path, offset=1061, limit=500) — chunk 3 if needed +- Continue until total lines are covered. + +Most transcripts need 1 to 3 chunks. The full body across all chunks is the primary input for Step 6. + +--- + +## Step 3: Read Invitees from Parent Meeting Note (OPTIMIZED) + +**Skip this entire step if** `People Roster` was provided in the invocation parameters. Use the provided roster as the People context table directly and proceed to Step 4. + +**CRITICAL:** If not skipped, do NOT skip this step. Complete it before Step 4 to maximize cache hits and build context early. + +### 3a. Read Parent Meeting Note + +The transcript frontmatter contains `meeting_note` field with a wiki-link to the parent meeting note (e.g., `[[2026-04-04 - Test Transcript]]`). + +- Extract the meeting note path from the wiki-link +- Read the meeting note (limit=100 to capture frontmatter + invitees section) +- Extract the `meeting_invitees` field — this contains an array of People note wiki-links or plain attendee names + +Example invitees field: +``` +meeting_invitees: + - "[[People/Joe Jackson]]" + - "[[People/Tanner Bragg]]" + - "[[People/Gregory Porter]]" + - "[[People/Andrew Davis]]" +``` + +Or plain names: +``` +meeting_invitees: + - Joe Jackson + - Tanner Bragg + - Gregory Porter +``` + +### 3b. Parse Invitees & Build File Paths (DETERMINISTIC) + +For each invitee: +- If wiki-link format `[[People/Name]]`: extract the path as `People/Name.md` +- If plain name: construct path as `People/{name}.md` + +Issue **ALL** Read calls in a single parallel batch. This is deterministic — no glob phase. + +Example (if 4 invitees): +``` +Read(People/Joe Jackson.md, limit=60) +Read(People/Tanner Bragg.md, limit=60) +Read(People/Gregory Porter.md, limit=60) +Read(People/Andrew Davis.md, limit=60) +``` + +### 3c. Build People Context Table (Phase 2) + +From each People note read in Phase 3b, extract: +- full_name +- nickname (if present) +- role_title +- company/org + +Build a Context value per person as: +- `"role_title, company/org"` (both present) +- `"role_title"` (org empty) +- `"company/org"` (role empty) +- `""` (both empty) + +Build a People context table with these fields per person: +| Full Name | Nickname | Role/Context | People Note Filename | Source | + +Source values: "meeting_invitee", "calendar", "microphone_user", "vocative_recovery" + +**This table is the foundation for Step 6 confidence refinement. Do not skip to Step 4 until this is complete.** + +### 3d. Roster Cache Merge (If Cache Hit) + +If a cache was found in Step 2: +- Compare table from Phase 3c against cached roster +- If all invitees are present in cache: use cached full context +- If new names found: merge Phase 3c results into cache, update cache generated date + +--- + +## Step 4: Calendar Attendees + +Skip if Calendar Attendees was provided in invocation (use it directly). Also skip if People Roster was provided. + +- calendar_event is "none" — skip calendar lookups entirely (ad-hoc meeting) +- calendar_attendees, invitees, or meeting_invitees populated — use directly (strip [[ ]] wrappers if present) +- No calendar data — proceed without. Calendar context improves confidence but is not required. + +Add all calendar attendees to the People context table from Step 3c with Source="calendar". + +--- + +## Step 5: Vocative-to-Speaker Mapping + +### 5a. Vocative Scanning (Pre-Step 6) + +Scan the full transcript body (from Step 2) for direct address patterns: +- "[Name], go ahead" +- "Thanks, [Name]" +- "[Name], what do you think?" +- "[Name], can you..." +- "Over to you, [Name]" +- "[Name]?" (standalone calling) + +### 5b. Match Against People Context + +For each vocative detected, check the People context table (Step 3c): +- Match against Full Name (exact or first word) +- Match against Nickname +- Multiple detections for the same name strengthen signal + +### 5c. Unmatched Vocative Recovery Batch (OPTIMIZED) + +If any vocative names do not match People context: + +Collect all unmatched names (e.g., "Kev", "Chew", "Alex", "Joe"). + +Issue recovery Globs in a single parallel batch: +``` +Glob(People Folder/*Kev*.md) +Glob(People Folder/*Chew*.md) +Glob(People Folder/*Alex*.md) +Glob(People Folder/*Joe*.md) +``` + +For each glob hit: +- Issue a Read in a single parallel batch (Phase 2 repeat) +- Extract full_name, nickname, role_title, company/org +- Add to People context table with Source="vocative_recovery" + +For misses: flag the unmatched_vocative in the final evidence field for that speaker. + +--- + +## Step 6: Speaker Identification Analysis + +Analyze the full transcript body (from Step 2), speaker stubs (frontmatter), and **complete** People context table (from Step 3c + 5c) using the rules below. Higher-priority rules override lower ones. + +**CRITICAL — duplicate assignments are expected and correct.** Transcription engines frequently split one real person across multiple speaker tags (e.g., Speaker 1 and Speaker 3 are both the same person). Propose the best match for each tag independently. Do NOT enforce a one-to-one constraint between people and tags. If the evidence says Speaker 1 is "Jane Doe" and Speaker 3 is also "Jane Doe", propose "Jane Doe" for both. + +### Rule 1: Microphone Speaker — CERTAIN + +Assign the Microphone user to the "Microphone" label. If no "Microphone" label exists, assign to the speaker with the most lines. Evidence: "microphone". Never overridden. + +### Rule 2: Calendar Attendees — CERTAIN or HIGH + +- Calendar + vocative match (see Rule 3) = CERTAIN +- Calendar + one other transcript signal (style, topic expertise, vocative response) = HIGH +- Calendar alone with no transcript evidence = do not assign. Invitees may be absent. + +### Rule 3: Vocative Scanning & Matching + +Vocatives matched in Step 5 now resolve to full names via the People context table. + +- Vocative directly matches a single People note = CERTAIN or HIGH +- Multiple independent vocatives resolve to same person = CERTAIN +- Unmatched vocative (recovery failed) = flag but do not assign + +### Rule 4: Vocative-to-Response Mapping + +The speaker who talks immediately after being called by name is likely that person. If Speaker A says "Tom, go ahead" and Speaker 3 speaks next, Speaker 3 is likely Tom. + +- Multiple vocative-responses for the same mapping = CERTAIN +- Single vocative-response = HIGH +- Conflicting mappings = LOW + +### Rule 5: First Name to Full Name Resolution + Context Matching (OPTIMIZED) + +Resolve first names to full names: + +1. Check calendar attendees — if exactly one has that first name, use them. +2. Check People context Full Name and Nickname columns. + +**First-name collision (multiple candidates share first name): try disambiguation in order:** + +1. **Context match** (NEW) — if transcript topic/discussion matches one candidate's Role/Context value, resolve at HIGH confidence with evidence "role/context match: [Context]" + - Example: Speaker discusses "VMware patching" and context table has "Gregory Kanis - VMware Administrator, Platform Team" → HIGH match + - Example: Speaker discusses "observability deployment" and context table has "Tanner Bragg - SRE, Platform Team" and "Tanner Smith - Finance" → HIGH match to Bragg + +2. Calendar preference — if exactly one candidate is a calendar attendee, use them. +3. Neither resolves — flag as LOW with all candidates and their Context values listed. + +### Rule 6: Alias / Transcription Error Handling + +For unresolved stubs, check Nicknames for phonetically similar matches to words spoken near that speaker. Confidence: LOW unless corroborated by Role/Context. + +### Confidence Levels (REFINED) + +- **CERTAIN:** microphone user, calendar + vocative, multiple vocative-responses, or multiple independent signals agreeing +- **HIGH:** calendar + single signal, single vocative-response, vocative + context match, or calendar + role/context alignment +- **LOW:** single weak signal, phonetic guess, ambiguous match, or unresolved collision +- **null:** no evidence found + +### Build Proposed Mapping + +For each speaker, record: +- index — 0 for Microphone, N for Speaker N +- original_name — stub label from transcript +- proposed_name — resolved full name, or null (the same person may appear for multiple tags — this is correct) +- confidence — CERTAIN, HIGH, LOW, or null if unresolved +- evidence — brief signal description (include "role/context match: [matching field]" if used) + +Do not downgrade confidence or skip a match because the same person was already proposed for a different tag. Evaluate each tag on its own evidence. + +--- + +## Step 7: Return Results + +Return the mapping in the output format specified by the caller. Do not write changes to the transcript. + +--- + +## Caching & Performance Notes + +**Roster Cache Strategy:** +- Cache is eligible if meeting_subject exists AND is_recurring is true +- Cached People context remains valid for 14 days +- On cache hit: skip Step 3 Phase 2 reads entirely (major time savings) +- On cache miss or new names: merge results into cache for future runs + +**Tool Call Batching Summary:** +- Step 2: 3 parallel calls (Read transcript frontmatter, wc, Glob roster cache) + transcript body chunks in parallel +- Step 3: Read parent meeting note → Parse invitees → Read all invitee People notes in parallel (deterministic, no glob) +- Step 5c: Recovery Glob batch (vocative recovery only) → Wait → Recovery Read batch (if needed) +- Total batches: ~4-5 (vs ~10+ in non-optimized version; eliminates broad name-based glob phase) + +**Expected Impact:** +- With fresh People context build: 1-2 minute baseline +- With roster cache hit: 30-40% faster (skips Phase 2 reads) +- With context matching: 15-20% fewer unresolved speakers vs. non-optimized