PersonalAssistant

Specs/Meeting-Scheduler-Spec.md

Meeting Scheduler — Specification

Module 1 of the Personal Assistant Platform. Finds mutual availability, presents ranked options, and books calendar events with the correct conferencing link.

Skill: ~/.claude/skills/scheduler.md
Code: /Users/arijitchowdhury/AI-Development/Scheduler/
Tests: 42 unit tests, all passing (as of 2026-04-15)


Architecture

User request
    ↓
scheduler.md skill (orchestrator)
    ├── gws calendar freebusy  (Google Calendar API)
    ├── resolve_contact.py     (name → email + timezone)
    ├── find_slots.py          (mutual availability + ranking)
    └── render_options.py      (ASCII timeline + option cards)
    ↓
User picks option
    ↓
gws calendar events insert    (creates event as organizer)

Python helpers (src/)

File Purpose
resolve_contact.py Resolves name → {email, timezone, working_hours} from team.json or calendar subscriptions
find_slots.py Computes mutual free windows, ranks by tier (Green/Yellow/Red) and score
render_options.py Renders ASCII timeline + option cards for terminal display
calendar_client.py Thin wrapper around gws freebusy queries

Config files (config/)

File Purpose
team.json Canonical team directory: name, email, timezone, working hours
preferences.json User defaults: conferencing provider, working hours, avoid hours

Slot Ranking

Slots are tiered and scored:

Tier Condition
🟢 Green All participants free, within working hours for everyone
🟡 Yellow Outside working hours for at least one participant
🔴 Red Conflict or unavailable

Scoring factors: preferred hours, avoid hours, tier. max_results defaults to 6.


Time Zone Handling

  • Owner timezone: America/New_York (EDT/EST)
  • Working hours for global team calls: 09:00–23:59 (owner) to reach Asia-Pacific
  • All freebusy queries use UTC; local times rendered per participant
  • AEST = UTC+10 (April, no daylight saving)

Booking Behavior (v1.1 — corrected)

  • Organizer is NOT added to attendees array — as calendar owner, Arijit is implicit organizer; adding him caused a spurious "accept your own meeting" notification. Fixed 2026-04-15.
  • Conference default: Google Meet (hangoutsMeet) — see v2 gap below
  • sendUpdates: "all" — David and other invitees receive email notification
  • 10-minute popup reminder set on every event
  • Google Meet link auto-generated via conferenceData.createRequest

Known Gaps (v2 backlog)

ID Gap Notes
GAP-01 gws people returns 403 Need re-auth with contacts scope for name→email lookup outside team.json
GAP-06 Conferencing provider hardcoded to Google Meet Zoom, Meet, Gong Engage must be selectable. Zoom = personal URL in preferences or Zoom API OAuth. Gong = Gong Engage (outbound only). Google Calendar API only exposes hangoutsMeet — Zoom/Gong are add-ons, not API-native.
GAP-07 ~~Organizer added to attendees~~ ✅ Fixed 2026-04-15 — removed from skill template

v2 Conferencing Plan

Arijit needs three providers (what his Google Calendar UI offers today): - Google Meet — works today ✓ - Zoom — personal link stored in preferences, or Zoom API for unique links - Gong — Gong Engage only (external/prospect meetings, not internal team calls)

Phase 1 — Personal URL approach (build next)

No new APIs. Config + skill change only.

// preferences.json target schema
{
  "conferencing": {
    "default": "zoom",
    "providers": {
      "zoom": { "personal_url": "https://algolia.zoom.us/j/XXXXXXXXXX" },
      "meet": {},
      "gong": { "personal_url": "https://app.gong.io/book/XXXXXXXXXX", "note": "external only" }
    }
  }
}

Skill change (Step 6): 1. Read preferences.conferencing.default 2. If unset → ask: "Zoom, Meet, or Gong?" 3. Zoom → add personal_url to event description + location (no conferenceData block) 4. Meet → keep hangoutsMeet conferenceData as-is 5. Gong → add personal_url to description, flag as external-meeting tool

Trade-off: Zoom personal link is same every meeting (no unique room per call).

Phase 2 — Zoom API (later)

One-time setup: create Zoom OAuth app → store client_id/secret/refresh_token. At booking: POST /v2/users/me/meetings → unique link per meeting. Only worthwhile when the "same link every time" limitation becomes a real problem.


Landscape Research Summary (2026-04-15)

19 projects surveyed. Key findings: - No project matches our integrated loop — freebusy + slot ranking + ASCII visualization + booking loop is a unique combination - Best MCP candidate: nspady/google-calendar-mcp (1,100 stars, MIT, TypeScript, clean deps, March 2026) — NOT adopting yet. gws is working; gaps are in UX not plumbing. - gigamonkey/scheduler — closest in intent (Python, multi-party freebusy, CLI) but no LLM, no NLP, no visualization - calcom/cal.com (41k stars) — booking pages, not ad-hoc mutual scheduling. Different problem. - Security: Indirect prompt injection via calendar event content (CVSS 10, LayerX Feb 2026) applies to any calendar+LLM integration including ours. Anthropic declined to fix at platform level.


Session Log

Date What happened
2026-04-15 MVP v1 shipped. 42 tests passing. Skill installed.
2026-04-15 First live booking: 30 min with David Howden, Wed Apr 15 8–8:30 PM ET / Thu Apr 16 10–10:30 AM AEST
2026-04-15 Bug fixed: organizer removed from attendees array in skill template
2026-04-15 Landscape research: 19 projects surveyed, nspady/google-calendar-mcp evaluated, decision = keep gws
2026-04-15 v2 conferencing plan defined: Phase 1 (personal URLs) + Phase 2 (Zoom API). Phase 1 = next task.