📄 HANDOFF-SOCRATES-v4.md 7,170 bytes May 02, 2026 📋 Raw

Handoff: Enriched Shadow Mode — CalendarValidator Implementation

What: Implement CalendarValidator class that queries Google Calendar (read-only) on Tier 2 tripwire fire, fuzzy-matches extracted events against calendar events, and reports MATCH | NO_MATCH | CONFLICT status.

Why: Service account authenticated, calendar read-only verified, test event confirmed. Ready for calendar validation logic.

Files:
- /home/hoffmann_admin/.openclaw/workspace/services/icarus/docs/ENRICHED-SHADOW-SPEC.md — Full architecture spec
- /home/hoffmann_admin/.openclaw/workspace/services/icarus/core/observer/shadow_bot.py — Main observer (integrate CalendarValidator)
- /home/hoffmann_admin/.openclaw/workspace/services/icarus/core/observer/shadow_database.py — Add calendar validation columns
- /home/hoffmann_admin/.openclaw/workspace/services/icarus/core/observer/shadow_telegram.py — Add Admin DM exhaust method

Current State:
- Service account: family-calendar-sync@hoffmann-family-manager.iam.gserviceaccount.com
- Calendar ID: hoffmann.family.manager@gmail.com (primary, shared with service account)
- Scope: calendar.readonly (verified — write returns 403)
- Test query: ✅ Working, returns events
- Command: gog calendar events hoffmann.family.manager@gmail.com --from <ISO> --to <ISO> --account family-calendar-sync@hoffmann-family-manager.iam.gserviceaccount.com --json

What You Need to Implement:

1. CalendarValidator Class

# services/icarus/core/observer/calendar_validator.py

class CalendarValidator:
    """Read-only calendar validation for extracted events."""

    def __init__(self, calendar_id: str, account: str):
        self.calendar_id = calendar_id
        self.account = account

    def check_event(self, extracted_event: dict) -> CalendarCheckResult:
        """
        Query calendar for events matching extracted_event.

        Returns:
            MATCH: Event exists with similar title/time
            NO_MATCH: No similar event found
            CONFLICT: Event exists but details differ
        """

    def fuzzy_match_score(self, extracted_title: str, calendar_title: str) -> float:
        """Fuzzy string matching (0.0-1.0)"""

    def query_calendar(self, date_start: str, date_end: str) -> list:
        """Execute gog calendar events query"""

2. Integration Points

In shadow_bot.py after Tier 2 extraction:

# After LLM extraction
if extraction_result and extraction_result.confidence >= 0.7:
    # [NEW] Calendar validation
    calendar_check = self.calendar_validator.check_event(
        extraction_result.raw_extraction
    )

    if calendar_check.status == "MATCH":
        # Event exists — log silently, no DM needed
        self.db.update_calendar_check(
            shadow_msg_id=shadow_msg_id,
            status="match",
            matched_event_id=calendar_check.event_id,
            matched_event_title=calendar_check.event_title,
        )
    elif calendar_check.status == "NO_MATCH":
        # Event not found — queue Admin DM
        self._send_admin_dm_no_match(
            shadow_msg_id=shadow_msg_id,
            extracted_event=extraction_result.raw_extraction,
        )
    elif calendar_check.status == "CONFLICT":
        # Event exists but details differ — flag conflict
        self._send_admin_dm_conflict(
            shadow_msg_id=shadow_msg_id,
            extracted_event=extraction_result.raw_extraction,
            calendar_event=calendar_check.matched_event,
        )

3. Fuzzy Matching Logic

# Use difflib.SequenceMatcher or rapidfuzz for fuzzy matching
# Score thresholds:
# - ≥ 0.7: Strong match (same event)
# - 0.4-0.7: Possible match (check time/location)
# - < 0.4: No match

def fuzzy_match_score(self, extracted: str, calendar_title: str) -> float:
    from difflib import SequenceMatcher
    return SequenceMatcher(None, extracted.lower(), calendar_title.lower()).ratio()

4. Admin DM Exhaust Methods

# In shadow_telegram.py or shadow_bot.py

def _send_admin_dm_no_match(self, shadow_msg_id, extracted_event):
    """DM Matt about proposed calendar action."""
    message = f"""
🔍 Shadow Mode — Proposed Calendar Action

Message ID: {shadow_msg_id}
Extracted: {extracted_event['what']}
When: {extracted_event['when']}
Who: {', '.join(extracted_event['who'])}

Calendar: ❌ No matching event found

I would have added:
  📅 "{extracted_event['what']}" — {extracted_event['when']}

Accurate? [Yes] [No] [Edit]

⚠️ This is a simulation. Calendar not modified.
    """
    self._send_dm_to_admin(message)

def _send_admin_dm_conflict(self, shadow_msg_id, extracted_event, calendar_event):
    """DM Matt about calendar conflict."""
    message = f"""
🔍 Shadow Mode — Calendar Conflict Detected

Message ID: {shadow_msg_id}
Extracted: {extracted_event['what']} at {extracted_event['when']}
Calendar: 📅 "{calendar_event['summary']}" at {calendar_event['start']}

Conflict: {calendar_event['conflict_description']}

Which is correct?
[📅 Keep Calendar]
[💬 Use Message]
[❓ I'm Not Sure]

⚠️ This is a simulation. Calendar not modified.
    """
    self._send_dm_to_admin(message)

5. Database Schema Update

-- Add to shadow_extractions table
ALTER TABLE shadow_extractions ADD COLUMN calendar_check_status TEXT;  -- match | no_match | conflict
ALTER TABLE shadow_extractions ADD COLUMN calendar_event_id TEXT;
ALTER TABLE shadow_extractions ADD COLUMN calendar_event_title TEXT;
ALTER TABLE shadow_extractions ADD COLUMN calendar_event_time TEXT;
ALTER TABLE shadow_extractions ADD COLUMN fuzzy_match_score REAL;
ALTER TABLE shadow_extractions ADD COLUMN admin_dm_sent BOOLEAN DEFAULT FALSE;
ALTER TABLE shadow_extractions ADD COLUMN admin_response TEXT;  -- yes | no | edit | timeout

Scope:
- ✅ DO: CalendarValidator class with fuzzy matching
- ✅ DO: gog command execution for calendar queries
- ✅ DO: Integration into shadow_bot.py flow
- ✅ DO: Admin DM exhaust methods
- ✅ DO: Database schema updates
- ❌ DON'T: Write to calendar (read-only only)
- ❌ DON'T: Modify tripwire logic (that's already tuned)
- ❌ DON'T: Add frontend UI (Daedalus handles that)

Success Criteria:
- CalendarValidator queries calendar on tripwire fire
- Fuzzy matching returns score 0.0-1.0
- Status correctly identified: MATCH | NO_MATCH | CONFLICT
- Admin DM format matches spec
- Database stores calendar validation results
- No calendar write operations (verified by 403 test)

Dependencies:
- gog CLI: Already configured with service account
- difflib: Python standard library (no install needed)

Calendar Query Command:

gog calendar events hoffmann.family.manager@gmail.com \
  --from <start_iso> \
  --to <end_iso> \
  --account family-calendar-sync@hoffmann-family-manager.iam.gserviceaccount.com \
  --json

ETA: TBD — provide your own after review

Coordination:
- After implementation, Wadsworth will test with real messages
- Admin DM routing will be configured
- Integration tests against test event "Test" on May 3