feat(calendar): maintenance window + schedule_type special slots #18

Merged
hzhang merged 1 commits from feat/maintenance-window-and-special-slots into main 2026-05-22 18:19:07 +00:00
Contributor

Implements the new daily-maintenance + admin-only special-slot mechanism on top of existing ScheduleType (work/entertainment windows).

Adds

  1. Maintenance window on ScheduleTypemaintenance_from/maintenance_to (UTC, exactly 1h). Existing rows default to 8-9 UTC via additive migration
  2. schedule_type_special_slots table — admin-managed slot templates per schedule_type, admin CRUD at /schedule-types/{id}/special-slots
  3. Per-agent materialisationGET /calendar/sync and /calendar/day materialise today's special slots for the calling agent / claw cohort, idempotent. Plugin runSync picks them up naturally
  4. Admin-locked enforcementis_admin_locked=true materialised rows refuse edit/cancel via user-facing endpoints (423) and only accept ongoing/paused/finished/aborted from the plugin-facing agent-update endpoint
  5. Maintenance-window guard on slot creationPOST /calendar/slots rejects any non-system slot that intersects the caller's schedule_type maintenance window (422). slot_type=system is reserved server-side for the materialiser
  6. Schema responseTimeSlotResponse / CalendarSlotItem now carry is_admin_locked and special_slot_id

Additive migrations only (in _migrate_schema); Base.metadata.create_all handles the new table on first boot.

Pairs with ClawSkills plan-schedule rewrite (PR #15) which now refers to admin-locked special slots and the maintenance window, and HF.Cli hf agent status wrapper (PR #5).

🤖 Generated with Claude Code

Implements the new daily-maintenance + admin-only special-slot mechanism on top of existing `ScheduleType` (work/entertainment windows). ## Adds 1. **Maintenance window on ScheduleType** — `maintenance_from`/`maintenance_to` (UTC, exactly 1h). Existing rows default to 8-9 UTC via additive migration 2. **`schedule_type_special_slots` table** — admin-managed slot templates per schedule_type, admin CRUD at `/schedule-types/{id}/special-slots` 3. **Per-agent materialisation** — `GET /calendar/sync` and `/calendar/day` materialise today's special slots for the calling agent / claw cohort, idempotent. Plugin runSync picks them up naturally 4. **Admin-locked enforcement** — `is_admin_locked=true` materialised rows refuse edit/cancel via user-facing endpoints (423) and only accept `ongoing/paused/finished/aborted` from the plugin-facing agent-update endpoint 5. **Maintenance-window guard on slot creation** — `POST /calendar/slots` rejects any non-system slot that intersects the caller's schedule_type maintenance window (422). `slot_type=system` is reserved server-side for the materialiser 6. **Schema response** — `TimeSlotResponse` / `CalendarSlotItem` now carry `is_admin_locked` and `special_slot_id` Additive migrations only (in `_migrate_schema`); `Base.metadata.create_all` handles the new table on first boot. Pairs with ClawSkills `plan-schedule` rewrite (PR #15) which now refers to admin-locked special slots and the maintenance window, and HF.Cli `hf agent status` wrapper (PR #5). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hzhang added 1 commit 2026-05-22 18:19:02 +00:00
## 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>
hzhang merged commit d870646e28 into main 2026-05-22 18:19:07 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: zhi/HarborForge.Backend#18