Commit Graph

4 Commits

Author SHA1 Message Date
hanghang zhang
2cbf6445eb feat(calendar): maintenance window + schedule_type special slots
## What this adds

1. **Maintenance window on ScheduleType**
   - New columns: maintenance_from / maintenance_to (UTC hours, 0-23)
   - Invariant: window is exactly 1 hour (validated in pydantic;
     maintenance_to must equal (maintenance_from + 1) % 24)
   - Default applied via additive migration: 8:00-9:00 UTC for existing
     rows so deployments don't crash on first boot

2. **ScheduleTypeSpecialSlot** — admin-managed slot template
   - New table schedule_type_special_slots
   - Admin (schedule_type.manage) CRUD via
     /schedule-types/{id}/special-slots
   - Fields: name, description, minute_in_window (0-59 inside the
     parent maintenance window), estimated_duration, priority,
     event_data (JSON merged into materialised slot), is_active
   - Unique constraint (schedule_type_id, name) — name is the stable
     human-readable identifier per cohort

3. **Per-agent materialisation**
   - New service app/services/special_slot_materialiser.py
   - GET /calendar/sync calls materialise_special_slots_for_claw
     (idempotent, one row per agent per template per date)
   - GET /calendar/day calls materialise_special_slots_for_user
   - Materialised rows are slot_type=system, event_type=system_event,
     is_admin_locked=true, special_slot_id pointing back to template
   - Plugin's runSync picks them up like any other due slot via the
     normal real-slots query path

4. **Admin-locked enforcement**
   - New TimeSlot columns: is_admin_locked, special_slot_id (FK to
     schedule_type_special_slots, ON DELETE SET NULL)
   - PATCH /calendar/slots/{id}: refuses any edit on admin-locked
     slots (423)
   - POST /calendar/slots/{id}/cancel: refuses cancel on admin-locked
     (423)
   - PATCH /calendar/slots/{id}/agent-update: admin-locked accept only
     ongoing/paused/finished/aborted statuses (423 on other transitions)

5. **Maintenance-window guard on slot creation**
   - POST /calendar/slots: rejects slot_type=system outright (only
     materialiser may create system slots) and rejects any non-system
     slot whose [scheduled_at, +duration] intersects the calling
     user's schedule_type maintenance window (422). Handles 23->0 wrap

6. **Schema response**
   - TimeSlotResponse / CalendarSlotItem now include is_admin_locked
     and special_slot_id so clients can render the lock indicator and
     trace back to the template

## Migration

Additive only — no destructive changes. Lives in _migrate_schema()
in app/main.py; the new schedule_type_special_slots table is created
by Base.metadata.create_all() on first boot.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 19:18:42 +01:00
755c418391 feat: auto-trigger Discord wakeup when slot becomes ONGOING 2026-04-05 09:37:14 +00:00
zhi
a9b4fa14b4 BE-CAL-002: Add SchedulePlan model with period hierarchy constraints
- Add DayOfWeek and MonthOfYear enums for plan period parameters
- Add SchedulePlan model with at_time/on_day/on_week/on_month fields
- Add DB-level check constraints enforcing hierarchy:
  on_month requires on_week, on_week requires on_day
- Add application-level @validates for on_week range (1-4),
  on_month hierarchy, and estimated_duration (1-50)
- Add is_active flag for soft-delete (plan-cancel)
- Add bidirectional relationship between SchedulePlan and TimeSlot
- All existing tests pass (29/29)
2026-03-30 19:16:16 +00:00
zhi
3dcd07bdf3 BE-CAL-001: Add TimeSlot model with SlotType/SlotStatus/EventType enums
- New calendar.py model file with TimeSlot table definition
- SlotType enum: work, on_call, entertainment, system
- SlotStatus enum: not_started, ongoing, deferred, skipped, paused, finished, aborted
- EventType enum: job, entertainment, system_event
- All fields per design doc: user_id, date, slot_type, estimated_duration,
  scheduled_at, started_at, attended, actual_duration, event_type, event_data (JSON),
  priority, status, plan_id (FK to schedule_plans)
2026-03-30 17:45:18 +00:00