You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ai_synth/docs/superpowers/specs/2026-03-27-scheduled-genera...

146 lines
4.8 KiB
Markdown

# Design: Scheduled Synthesis Generation with Email Delivery
**Date**: 2026-03-27
**Scope**: Per-theme scheduling (days + time), automatic generation + email to up to 3 addresses
---
## Context
Users want automated weekly syntheses delivered by email without manually triggering generation. Each theme can have its own schedule (e.g. "AI News every Mon/Wed/Fri at 8am, emailed to 3 people").
The app already runs as a Docker daemon (`restart: unless-stopped`), so no external cron or daemon script is needed — the scheduler is an internal background task.
---
## 1. Data Model
### New table: `theme_schedules`
```sql
CREATE TABLE theme_schedules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
theme_id UUID NOT NULL UNIQUE REFERENCES themes(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
enabled BOOLEAN NOT NULL DEFAULT true,
days JSONB NOT NULL DEFAULT '[]',
time_utc TEXT NOT NULL DEFAULT '08:00',
emails JSONB NOT NULL DEFAULT '[]',
last_run_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_theme_schedules_enabled ON theme_schedules(enabled) WHERE enabled = true;
```
- `theme_id` is UNIQUE — one schedule per theme (1:1)
- `days`: JSON array of day codes `["mon","tue","wed","thu","fri","sat","sun"]`
- `time_utc`: execution time in UTC, format `"HH:MM"`
- `emails`: JSON array of up to 3 email addresses
- `last_run_at`: prevents double runs on the same day
---
## 2. API Endpoints
- `GET /api/v1/themes/:id/schedule` — get schedule for a theme (null if none)
- `PUT /api/v1/themes/:id/schedule` — upsert schedule
- `DELETE /api/v1/themes/:id/schedule` — remove schedule
### PUT body
```json
{
"enabled": true,
"days": ["mon", "wed", "fri"],
"time_utc": "08:00",
"emails": ["user@example.com", "colleague@example.com"]
}
```
### Validation
- `emails`: max 3, each must be valid email format
- `days`: each must be one of `mon, tue, wed, thu, fri, sat, sun`
- `time_utc`: must match `HH:MM` format (00:00-23:59)
- Theme must belong to the authenticated user
---
## 3. Backend Scheduler
### Background task in `main.rs`
```rust
// Scheduled synthesis generation (check every 60 seconds)
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(60));
loop {
interval.tick().await;
scheduler::run_scheduled_jobs(&state).await;
}
});
```
### `services/scheduler.rs`
**`run_scheduled_jobs(state)`:**
1. Get current UTC time and day code (e.g. `"wed"`)
2. Query `theme_schedules` where:
- `enabled = true`
- `days` contains today's day code
- `time_utc <= current_time`
- `last_run_at` is NULL or `last_run_at::date < today`
3. For each due schedule:
- Check `job_store.has_active_job(user_id)` — skip if manual generation in progress
- Load theme settings
- Run `run_generation_inner` with a dummy watch channel (no SSE client)
- On success: send synthesis email to all addresses in `emails`
- Update `last_run_at = now()`
- Log success/failure
**Error handling:** If generation or email fails, log the error and continue to the next schedule. Don't retry — the schedule will trigger again at the next interval if `last_run_at` wasn't updated.
**Concurrency:** Jobs run sequentially (one at a time) to avoid overwhelming LLM rate limits.
---
## 4. Frontend — Schedule Section in ThemeManager
New "Planification" section in the theme page, below sources.
### UI components:
- **Enable toggle**: checkbox to enable/disable
- **Day selector**: 7 buttons (L M M J V S D), toggle on click, selected = highlighted
- **Time picker**: `<input type="time">`
- **Email list**: up to 3 inputs with add/remove buttons
- **Auto-save**: changes sent immediately via `PUT /api/v1/themes/:id/schedule`
### Section hidden when no theme is selected.
---
## 5. Files
### Backend
- **Create:** `backend/migrations/20260327000030_create_theme_schedules.sql`
- **Create:** `backend/src/models/schedule.rs`
- **Create:** `backend/src/db/schedules.rs`
- **Create:** `backend/src/handlers/schedules.rs`
- **Create:** `backend/src/services/scheduler.rs`
- **Modify:** `backend/src/models/mod.rs`, `db/mod.rs`, `handlers/mod.rs`, `services/mod.rs`
- **Modify:** `backend/src/router.rs` — schedule routes
- **Modify:** `backend/src/main.rs` — spawn scheduler task
- **Modify:** `CLAUDE.md` — migration count
### Frontend
- **Create:** `frontend/src/api/schedules.ts`
- **Create:** `frontend/src/components/settings/SettingsSchedule.tsx`
- **Modify:** `frontend/src/pages/ThemeManager.tsx`
- **Modify:** `frontend/src/i18n/fr.ts`
- **Modify:** `frontend/src/types.ts`
### Tests
- **Create:** `backend/tests/api_schedules_test.rs`
- **Modify:** `e2e/tests/themes.spec.ts`