Skip to content

calendar.yaml

Source: config/calendar.yaml

# Google Calendar integration configuration
# See docs/scheduling.md and slices/slice_04_calendar.md
#
# Calendar IDs can be overridden via environment variables:
#   GCAL_PERSONAL_ID, GCAL_WORK_ID, GCAL_FAMILY_ID

calendars:
  personal:
    calendar_id: "primary"    # "primary" resolves to the authenticated user's main calendar
    access: "read_write"
  work:
    calendar_id: ""           # set via GCAL_WORK_ID env var
    access: "read_only"
  family:
    calendar_id: ""           # set via GCAL_FAMILY_ID env var
    access: "read_only"

sync:
  poll_interval_seconds: 300  # 5 minutes; change here, not in code
  lookahead_days: 7
  lookbehind_days: 1

scheduling:
  slot_step_minutes: 15        # search granularity
  default_duration_minutes: 60 # fallback when task has no estimated_duration
  search_horizon_days: 14      # give up if no slot found within this window

# User timezone — all time_windows hours are interpreted in this zone.
timezone: America/New_York

# Time windows define when each domain's tasks may be scheduled.
# Blackout (12am–6am) is absolute — no exceptions, not even priority 5.
# Quiet hours (10pm–12am) block new scheduling except for priority 5.
# All hours are local time (interpreted via timezone above).
time_windows:
  blackout:
    start_hour: 0     # 12:00 AM
    end_hour: 6       # 6:00 AM — absolute, no scheduling, no notifications
    days: [0, 1, 2, 3, 4, 5, 6]
  quiet_hours:
    start_hour: 22    # 10:00 PM
    end_hour: 24      # midnight — soft block; priority 5 may still fire
    days: [0, 1, 2, 3, 4, 5, 6]
  work:
    start_hour: 8     # 8:00 AM
    end_hour: 17      # 5:00 PM
    days: [0, 1, 2, 3, 4]  # Monday–Friday
  personal:
    start_hour: 17    # 5:00 PM
    end_hour: 20      # 8:00 PM
    days: [0, 1, 2, 3, 4, 5, 6]  # any day
  weekend:
    start_hour: 6     # 6:00 AM
    end_hour: 20      # 8:00 PM
    days: [5, 6]      # Saturday–Sunday

priority:
  deadline_warning_days: 3        # hard deadline within N days → floor priority to 4
  deadline_critical_days: 1       # hard deadline within N days → floor priority to 5
  workload_threshold_per_day: 5   # if more than N tasks same day, apply pressure
  escalation_after_reschedules: 1 # bump priority after this many reschedules under pressure

# Scheduling negotiation loop ("Plan 2", Slice A).
# See docs/superpowers/specs/2026-06-12-scheduling-negotiation-design.md §4 and
# spec_v3.md §6.1.2 (conflict table) / §6.3 (Minimize Rescheduling / Get It Done).
# Conservative defaults: propose-and-confirm only — moves are NEVER applied
# silently (2026-06-05 confirmation invariant). All knobs are config, not code.
negotiation:
  enabled: true
  auto_apply: false                   # DEFAULT OFF — propose-and-confirm
  min_displacer_priority: 3           # only P3+ hard-deadline tasks may displace
  min_lead_minutes: 60                # a blocker starting sooner is immovable
  max_displacements_per_placement: 1  # slice A; 2 in the full loop
  max_cascade_depth: 3                # slice C (cascade-shift); unused in slice A
  max_auto_moves_per_task_per_day: 2  # anti-thrash cap per displaced task
  proposal_ttl_hours: 4               # how long a pending proposal stays live
  notify_min_priority_moved: 4        # §6.1.2 "none unless priority 4–5"
  cost_weights:
    priority: 10
    hard_deadline: 20
    slack: 8
    reschedule: 5
    imminence: 2
  overrun:                            # slice C overrun detector; disabled here
    enabled: false
    extension_step_minutes: 15
    max_extension_minutes: 60

credentials:
  client_secrets_path: "google_credentials.json"   # OAuth2 client credentials file
  token_path: "token.json"                   # persisted token (created on first auth)
  scopes:
    - "https://www.googleapis.com/auth/calendar"
    - "https://www.googleapis.com/auth/calendar.readonly"