docs: add spec for scheduled synthesis generation with email delivery
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>master
parent
e43a4d2180
commit
96f138bd61
@ -0,0 +1,145 @@
|
|||||||
|
# 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`
|
||||||
Loading…
Reference in New Issue