Enriched Shadow Mode — Architecture Spec
Version: 2.0
Status: Approved for Implementation
Date: 2026-05-02
Parameter Lock (Director Constraints)
| Constraint |
Value |
Enforcement |
| Speak Policy |
Strict Mute |
SPEAK_ENABLED=False hard-coded, never overridden |
| Calendar Access |
Read-Only |
calendar.readonly scope at API credential level |
| Query Trigger |
Event-Driven |
Only on Tier 1 tripwire fire |
| Auto-Write |
Prohibited |
Even confirmed actions only log to staging.db |
| Exhaust Channel |
Admin DM |
Proposed actions sent to Matt's private Telegram DM |
Flow Diagram
Family Chat Message
│
▼
┌───────────────────┐
│ Tier 1: Tripwire │── Score ≥ 0.4? ──► Log silently (done)
│ (regex/keywords) │
└───────────────────┘
│ Yes
▼
┌───────────────────┐
│ Tier 2: LLM │── Extract who/what/when/where/confidence
│ Extraction │
└───────────────────┘
│
▼
┌───────────────────┐
│ [NEW] Calendar │── Query Google Calendar (readonly)
│ Validation │── Fuzzy match event title
│ (Event-Driven) │── Check person/time/location overlap
└───────────────────┘
│
├─ MATCH ───► Log: "Would have confirmed existing event"
│ (No DM needed, silent)
│
├─ NO_MATCH ─► Admin DM: "I would have added [Sully Pickup]
│ to calendar for tomorrow 8 AM. Accurate? [Yes] [No]"
│ ► [Yes] → Log to staging.db (not real calendar)
│ ► [No] → Log to staging.db with rejection flag
│
└─ CONFLICT ─► Admin DM: "Calendar shows 'Sully haircut' at 9 AM,
but message says 8 AM. Which is correct? [9 AM] [8 AM]"
► Either answer → Log to staging.db for tuning
Google Calendar Integration
API Scope
- Required:
https://www.googleapis.com/auth/calendar.readonly
- Forbidden:
calendar (read-write), calendar.events
- Enforcement: OAuth consent screen + API key restrictions
Query Strategy
# Event-driven only — called from shadow_bot.py after Tier 2 extraction
calendar.check_event(
date_range=(extracted_date - 1 day, extracted_date + 1 day),
query_text=extracted_what, # "Sully haircut"
family_member=extracted_who, # "Sully"
)
Fuzzy Matching
- Event title similarity ≥ 0.6 (fuzzy string match)
- Date overlap (same day)
- Optional: time overlap if extracted
NO_MATCH Alert
🔍 Shadow Mode — Proposed Calendar Action
Message: "I've got Sully tomorrow"
Extracted: Pickup Sully, tomorrow, unspecified time
Calendar: ❌ No matching event found
I would have added:
📅 "Sully Pickup" — tomorrow (all day)
Accurate? [Yes] [No] [Edit]
⚠️ This is a simulation. Your calendar has not been modified.
CONFLICT Alert
🔍 Shadow Mode — Calendar Conflict Detected
Message: "Sully's haircut at 8"
Extracted: Sully haircut, tomorrow 8:00 AM
Calendar: ✅ "Sully Haircut" — tomorrow 9:00 AM
Conflict: Time mismatch (8 AM vs 9 AM)
Which is correct?
[📅 Keep Calendar: 9 AM]
[💬 Use Message: 8 AM]
[❓ I'm Not Sure]
⚠️ This is a simulation. Your calendar has not been modified.
MATCH Confirmation (Silent — No DM)
Logged silently to staging.db:
"Sully pickup" — matched existing event "Sully Pickup" (tomorrow 3 PM)
Action: None needed
Staging Database Schema Update
-- Add calendar validation columns to shadow_extractions
ALTER TABLE shadow_extractions ADD COLUMN calendar_check_status TEXT; -- match | no_match | conflict
ALTER TABLE shadow_extractions ADD COLUMN calendar_event_id TEXT; -- Google Calendar event ID
ALTER TABLE shadow_extractions ADD COLUMN calendar_event_title TEXT; -- Matched event title
ALTER TABLE shadow_extractions ADD COLUMN calendar_event_time TEXT; -- Matched event time
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
ALTER TABLE shadow_extractions ADD COLUMN would_have_written BOOLEAN; -- What we would have done
Implementation Plan
| Phase |
Task |
Owner |
ETA |
| 1 |
Read gog skill for calendar.readonly auth |
Wadsworth |
15 min |
| 2 |
Add CalendarValidator class to shadow_bot.py |
Socrates |
30 min |
| 3 |
Implement fuzzy matching logic |
Socrates |
45 min |
| 4 |
Add Admin DM exhaust (Telegram DM routing) |
Wadsworth |
30 min |
| 5 |
Update staging.db schema |
Socrates |
15 min |
| 6 |
Integration test with real calendar queries |
Wadsworth |
20 min |
| 7 |
Deploy and monitor |
Wadsworth |
10 min |
Security Safeguards
API Credential Scoping
{
"client_id": "...",
"scopes": ["https://www.googleapis.com/auth/calendar.readonly"],
"restrictions": {
"allowed_ips": ["Tailscale IPs only"],
"rate_limit": "100 queries/day"
}
}
Write Protection
gog skill must fail if write scope requested
- Code review: No
events.insert() or events.update() calls in shadow mode
- Runtime check: Assert
readonly in token scope before any query
Rate Limiting
- Max 1 calendar query per tripwire fire
- Max 50 queries/day total (Google quota protection)
- Exponential backoff on 429 errors
Metrics to Track
| Metric |
Why |
| Calendar match rate |
How often extractions match existing events |
| False positive rate |
How often NO_MATCH alerts are rejected by Matt |
| Conflict resolution |
Which source Matt trusts more (calendar vs message) |
| Admin response time |
How quickly Matt replies to DM exhaust |
| Daily query volume |
Stay under Google's radar |
Failure Modes
| Scenario |
Behavior |
| Google API 429 |
Skip calendar check, log "API rate limited" |
| Token expired |
Skip calendar check, alert Wadsworth in logs |
| Calendar unavailable |
Skip check, treat as NO_MATCH (safe default) |
| Fuzzy match ambiguous |
Log both candidates, treat as CONFLICT |
| Admin DM timeout (5 min) |
Log "timeout", default to no action |
Files to Modify
| File |
Change |
services/icarus/core/observer/shadow_bot.py |
Add CalendarValidator class, integrate into flow |
services/icarus/core/observer/shadow_database.py |
Add calendar columns to schema |
services/icarus/core/observer/shadow_telegram.py |
Add Admin DM exhaust method |
services/icarus/calendar_adapter.py |
[NEW] Google Calendar wrapper (readonly) |
services/icarus/shadow.env |
Add calendar credentials path |
services/icarus/requirements-shadow.txt |
Add google-api-python-client |
Approved by: Matt (Director)
Date: 2026-05-02
Next Action: Wadsworth to read gog skill, then dispatch Socrates for implementation