Scheduling
Maturity: SPECIFIED | ID Prefix: SCHED | Dependencies: daemon-engine, agent-execution
Purpose
Scheduled agent execution: recurring or time-based task spawning. A nightly security review, a weekly dependency audit — results waiting when you check in.
Conceptual Model
Schedule: named recurring task (timing, trigger, enabled/disabled)
ScheduleDef: YAML file in .pu/schedules/ (local) or ~/.pu/schedules/ (global)
Decisions
! [SCHED-001] Log and continue, no retry — rationale: matches agent execution model where broken agents stay broken; user can re-enable or manually re-trigger
! [SCHED-002] No dependencies between tasks — rationale: YAGNI; adds complexity for a feature no one has asked for
! [SCHED-003] Part of the daemon — rationale: daemon already has tokio runtime, broadcast channels, and direct access to spawn/swarm logic; separate process would require its own IPC
! [SCHED-004] YAML files in .pu/schedules/ (local) and ~/.pu/schedules/ (global) — rationale: follows established template/agent/swarm pattern exactly
! [SCHED-005] All three: agent def, swarm def, inline prompt — rationale: user requested all three; matches existing spawn flexibility
! [SCHED-006] Engine tracks registered projects (populated by Init and any project-scoped request); scheduler scans those — rationale: daemon already knows about projects; no separate discovery needed
! [SCHED-007] Named presets stored in YAML, no raw cron expressions — rationale: UI uses presets (none/hourly/daily/weekdays/weekly/monthly); cron strings add complexity without value since we control both ends
! [SCHED-008] Yes, one-shot schedules auto-disable — rationale: prevents re-firing after daemon restart; clean lifecycle
Requirements
Storage
REQ-SCHED-001Given a schedule def YAML file in.pu/schedules/, should deserialize into ScheduleDef structREQ-SCHED-002Given local and global schedule dirs with same-named schedule, should prefer localREQ-SCHED-003Given a new schedule, should save as YAML to the scope-appropriate directoryREQ-SCHED-004Given a schedule name with invalid characters, should reject with validation errorREQ-SCHED-005Given a delete request for existing schedule, should remove YAML file and return trueREQ-SCHED-006Given a delete request for nonexistent schedule, should return false
Recurrence
REQ-SCHED-010Given recurrencenoneand base time in the future, should return base as next occurrenceREQ-SCHED-011Given recurrencenoneand base time in the past, should return None (one-shot expired)REQ-SCHED-012Given recurrencehourly, should compute next hour matching base minuteREQ-SCHED-013Given recurrencedaily, should compute next day at base timeREQ-SCHED-014Given recurrenceweekdayson Friday, should skip to MondayREQ-SCHED-015Given recurrenceweekdayson Saturday, should skip to MondayREQ-SCHED-016Given recurrenceweekly, should return same weekday next weekREQ-SCHED-017Given recurrencemonthlyon 31st, should skip months with fewer daysREQ-SCHED-018Given recurrencemonthly, should return same day-of-month at base time
Protocol
REQ-SCHED-020Given ListSchedules request, should return all schedules for project as ScheduleList responseREQ-SCHED-021Given GetSchedule request with valid name, should return ScheduleDetailREQ-SCHED-022Given GetSchedule request with unknown name, should return NOT_FOUND errorREQ-SCHED-023Given SaveSchedule request, should persist schedule and return OkREQ-SCHED-024Given DeleteSchedule request, should remove schedule and return OkREQ-SCHED-025Given EnableSchedule request, should set enabled=true, compute next_run, saveREQ-SCHED-026Given DisableSchedule request, should set enabled=false, clear next_run, save
Scheduler
REQ-SCHED-030Given enabled schedule with next_run in the past, should fire the triggerREQ-SCHED-031Given disabled schedule, should not fire regardless of next_runREQ-SCHED-032Given schedule with AgentDef trigger, should resolve agent def and spawn agentREQ-SCHED-033Given schedule with SwarmDef trigger, should call RunSwarmREQ-SCHED-034Given schedule with InlinePrompt trigger, should spawn with prompt textREQ-SCHED-035Given one-shot schedule after firing, should auto-disableREQ-SCHED-036Given recurring schedule after firing, should compute and persist next next_run
CLI
REQ-SCHED-040Givenpu schedule list, should display all schedules in tabular formatREQ-SCHED-041Givenpu schedule createwith agent-def trigger, should send SaveSchedule requestREQ-SCHED-042Givenpu schedule show <name>, should display schedule detailsREQ-SCHED-043Givenpu schedule delete <name>, should remove scheduleREQ-SCHED-044Givenpu schedule enable <name>, should enable scheduleREQ-SCHED-045Givenpu schedule disable <name>, should disable schedule
Swift UI
REQ-SCHED-050Given ScheduleState initialized, should load schedules from daemon (no mock data)REQ-SCHED-051Given schedule creation sheet submitted, should send SaveSchedule to daemonREQ-SCHED-052Given calendar view displayed, should show daemon-sourced events
Interfaces
ScheduleDef:
name: String
enabled: bool (default true)
recurrence: Recurrence (none|hourly|daily|weekdays|weekly|monthly)
start_at: DateTime<Utc>
next_run: Option<DateTime<Utc>>
trigger: ScheduleTrigger (agent_def|swarm_def|inline_prompt)
project_root: String
target: String (path scope, default "")
scope: String (local|global, skip serialization)
created_at: DateTime<Utc>
ScheduleTrigger (tagged enum):
AgentDef { name: String }
SwarmDef { name: String, vars: HashMap<String,String> }
InlinePrompt { prompt: String, agent: String }
IPC Requests: ListSchedules, GetSchedule, SaveSchedule, DeleteSchedule, EnableSchedule, DisableSchedule
IPC Responses: ScheduleList, ScheduleDetail (+ existing Ok, Error)
CLI: pu schedule {list|create|show|delete|enable|disable}
Storage: .pu/schedules/{name}.yaml (local), ~/.pu/schedules/{name}.yaml (global)
Edge Cases
- Schedule YAML with unknown fields: serde should ignore (allow forward compat)
- Monthly recurrence on day 31 in February: skip to next month with 31 days (March)
- Daemon restart with schedules whose next_run is in the past: fire immediately on first tick, then advance
- Multiple schedules fire in same tick: fire all, no ordering guarantee
- Schedule references deleted agent def: fire fails, schedule stays enabled, error logged
- Schedule with empty project_root: reject at save time