feat(calendar): maintenance window + schedule_type special slots #18
Reference in New Issue
Block a user
Delete Branch "feat/maintenance-window-and-special-slots"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the new daily-maintenance + admin-only special-slot mechanism on top of existing
ScheduleType(work/entertainment windows).Adds
maintenance_from/maintenance_to(UTC, exactly 1h). Existing rows default to 8-9 UTC via additive migrationschedule_type_special_slotstable — admin-managed slot templates per schedule_type, admin CRUD at/schedule-types/{id}/special-slotsGET /calendar/syncand/calendar/daymaterialise today's special slots for the calling agent / claw cohort, idempotent. Plugin runSync picks them up naturallyis_admin_locked=truematerialised rows refuse edit/cancel via user-facing endpoints (423) and only acceptongoing/paused/finished/abortedfrom the plugin-facing agent-update endpointPOST /calendar/slotsrejects any non-system slot that intersects the caller's schedule_type maintenance window (422).slot_type=systemis reserved server-side for the materialiserTimeSlotResponse/CalendarSlotItemnow carryis_admin_lockedandspecial_slot_idAdditive migrations only (in
_migrate_schema);Base.metadata.create_allhandles the new table on first boot.Pairs with ClawSkills
plan-schedulerewrite (PR #15) which now refers to admin-locked special slots and the maintenance window, and HF.Clihf agent statuswrapper (PR #5).🤖 Generated with Claude Code
## 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>