---
inclusion: manual
---

# /log-calendar-meetings — Bulk Log Outlook Calendar Meetings to Salesforce

This steering file automates the end-to-end process of logging external customer meetings from Outlook calendar into Salesforce (AWSentral). It should be triggered when the user says `/log-calendar-meetings`.

## Prerequisites

- Outlook MCP connected (for calendar access)
- AWSentral MCP connected (for Salesforce operations)
- A document or file containing your territory accounts (Excel, CSV, JSON, or even a pasted list) with at minimum: **Account Name** and **SFDC Account ID**. Optionally: tier, segment, and other metadata.

---

## STEP 0: Gather User Context (First Run Only)

On the FIRST invocation, ask the user for the following. Cache these for the session so you don't ask again:

1. **Territory accounts data:** Ask the user to provide their territory accounts. Accept any of:
   - A file path (Excel, CSV, JSON, or markdown)
   - A pasted list of account names and SFDC IDs
   - A Salesforce territory name to look up via `list_territory_accounts`
   - A user alias to look up via `get_registry_assignments` + `list_territory_accounts`
2. **Their SFDC User ID:** Ask the user for their Salesforce User ID. If they don't know it, look it up using `get_my_personal_details` or `search_users` with their alias.
3. **Their alias:** Ask for their Amazon alias (used for ownership checks).
4. **Known partner companies (optional):** Ask if there are partner companies they frequently meet with that are NOT customers (e.g., consulting partners, ISVs, resellers). These will be classified as partner meetings.

Once gathered, confirm the data back to the user before proceeding.

---

## STEP 1: Load Territory Accounts Context

Based on what the user provided in Step 0:

- **If file path:** Read the file via OneDrive MCP or filesystem and parse the account list.
- **If pasted list:** Parse directly from the conversation.
- **If territory/alias lookup:** Use AWSentral MCP to pull the account list from Salesforce.

Build an in-memory mapping of: `Company Name → SFDC Account ID` (plus any extra metadata like tier or segment).

Confirm with the user: "I've loaded **X** territory accounts. Ready to proceed?"

---

## STEP 2: Ask for Time Range, Then Pull External Meetings from Outlook Calendar

**Time range:** ALWAYS ask the user how far back they want to go before pulling calendar data. Examples: "2 weeks", "1 month", "3 months", "since March 1st". Do NOT assume a default — wait for the user to specify.

**Method:** Write and run a Python script using `win32com.client` to extract calendar items. The script must:
- Set `IncludeRecurrences = True` — this is CRITICAL to capture recurring meetings (bi-weeklies, monthlies, etc.)
- Sort items by `[Start]` BEFORE setting IncludeRecurrences (Outlook COM requires this order)
- Filter by date range using `Items.Restrict` with `[Start]` and `[End]` conditions
- Add a hard cap: break after processing 1000 items to avoid infinite loops
- Add a per-item timeout: wrap each item access in a try/except and skip items that error
- Skip meetings whose subject starts with "Canceled:"
- For each meeting, check all Recipients for external email addresses (any `@` address NOT containing `amazon`)
- Collect: subject, start datetime, end datetime, external attendees (name + email), organizer, body preview (first 300-500 chars)
- Output as JSON
- Clean up the temp script file after execution

**Important:** Only collect meetings that have at least one external (non-Amazon) attendee.

**Why IncludeRecurrences = True matters:** Many customer meetings are recurring (bi-weekly syncs, monthly cadences). Setting this to False will silently skip ALL recurring meeting instances, causing significant gaps in logging.

**Note:** Rescheduled meetings (moved forward after they occurred) may not appear in the calendar history. Be aware some meetings could be missing from the extraction for this reason.

---

## STEP 3: Classify Meetings — Customer vs Partner vs Skip

For each external meeting found, determine if it belongs to a territory customer:

1. **Search territory accounts** from the loaded data for each company name extracted from meeting subjects or attendee email domains.
2. **Classification rules:**
   - **Customer meeting:** The company name (from subject or attendee email domain) matches a territory account → log under that account's SFDC ID
   - **Partner meeting referencing a customer:** The external attendees are from a known partner company BUT the meeting subject or body references a territory customer → log under the referenced customer's SFDC account
   - **Partner meeting with no customer reference:** Skip (e.g., partner 1:1s, partner enablement sessions, partner team meetings)
   - **Not in territory:** If the company is not found in territory accounts, ASK the user if it's their customer and for the SFDC ID. Do not assume it should be skipped.
   - **Internal-only meetings:** Skip (team meetings, office hours, all-hands, admin hours, etc.)

---

## STEP 4: Check Salesforce for Already-Logged Events

Before creating any event, check if it's already been logged:

1. **Search by subject** using `search_events` with queryTerm matching the company name
2. **Search by account ID** using `search_events` with condition filtering on `whatId` = account SFDC ID and `activityDate` within the relevant date range
3. **If an event already exists** for that account on that date with a similar subject → SKIP it
4. **If no matching event found** → proceed to log it

---

## STEP 5: Check and Create Contacts

For EACH external attendee in a meeting that will be logged:

1. **Search for the contact in Salesforce** using `search_contacts` with:
   - Filter by `accountId` matching the customer's SFDC ID
   - Query by attendee name or email
2. **If contact exists:** Note their SFDC Contact ID for event creation
3. **If contact does NOT exist:**
   - **Research their title using this priority chain:**
     1. **LinkedIn MCP (if available):** Use `search_people` with keywords `"<firstName> <lastName> <companyName>"`. From the results, find the matching person and use `get_person_profile` with their LinkedIn username to get their exact title. This is the most reliable source.
     2. **Web search (if no LinkedIn MCP):** Use web search for `"<firstName> <lastName>" "<companyName>" title role` — but do NOT search LinkedIn directly (no access without the MCP). Look for company websites, press releases, or other public sources.
     3. **Contextual inference (if no results from above):** Infer a reasonable title from the meeting context (e.g., if the meeting was about EKS migration, the person is likely a developer or engineer; if it was a strategic/commercial meeting, likely a manager or director). Present the assumption to the user for confirmation before creating.
   - If none of the above yields a confident title, ASK the user directly.
   - **Create the contact** using `create_contact` with:
     - `accountId`: the customer's SFDC account ID
     - `firstName`, `lastName`: parsed from attendee name
     - `email`: from the meeting attendee list
     - `title`: from research above or user input
     - `ownerId`: the user's SFDC User ID (from Step 0)

---

## STEP 6: Create Salesforce Events — ONE PER CONTACT

**Critical rule: Create one Salesforce Event per external contact per meeting.**

If a meeting had 3 external attendees, that's 3 separate Salesforce Events, each linked to a different contact via `whoId`.

**Event fields:**
- `subject`: `"Meeting"` (always)
- `type`: `"Meeting"` (always)
- `activityDateTime`: meeting start time in ISO format (from Outlook)
- `endDateTime`: meeting end time in ISO format (from Outlook)
- `description`: Original meeting subject from Outlook + brief agenda/context from body preview. Include names of all attendees.
- `whatId`: The customer's SFDC Account ID from territory data
- `whoId`: The specific contact's SFDC Contact ID (different for each event copy)
- `ownerId`: the user's SFDC User ID (from Step 0)

**Do NOT set:** `status` field (Events don't have status in Salesforce — that's for Tasks)

---

## STEP 7: Handle Errors and Retries

- **UNABLE_TO_LOCK_ROW:** This is a transient Salesforce error. Wait a few seconds and retry (up to 3 times).
- **Authentication errors (401/mwinit):** Inform the user they need to run `mwinit` and retry.
- **Empty error messages:** Retry once. If it fails again, skip and report to user.

---

## STEP 8: Present Summary to User

After all events are created, present a structured summary:

### Events Logged
Table with: Date | Account | Meeting Context | Contacts (one row per meeting, listing all contacts)

### Already Logged (Skipped)
List of meetings that were already in Salesforce

### Skipped (Not Customer)
List of meetings skipped because they were partner-only or internal

### Contacts Created
Table of any new contacts created with: Name | Account | Title | Email

### Errors
Any events that failed after retries
