# V7.0.0 — Automations v2 + Workers > **Statut** : ✅ Livré en v7.0.0 (2026-09-28) — `automation_steps` + 4 nouvelles actions + `services/workers.py` + `routers/workers.py`, migration 26, 31 tests verts · **Roadmap** : `ROADMAP.md § v7.0.0` > Notes d'implémentation : legacy sans steps = fallback inchangé (pas de migration de données) ; `delay` = sleep plafonné 300s (pas de re-queue) ; `agent_trigger` = run synchrone (timeout `agent_run_timeout_seconds`) ; éditeur visuel canvas en follow-up. > **Référence Notion** : Database automations (multi-triggers any/all, chaînes) + Buttons + Workers (custom code, credits dashboard, partage équipe, 07/2026). > **Existant** : `automations` + `automation_runs` (scheduler 60s), triggers `page.*/collection.*` + cron + bouton, conditions `eq/neq/contains/...`, actions `webhook/set_property/create_page/notify`, bloc `button`, webhooks v2 HMAC + retry. --- ## 1. Vision De `si X alors Y` à `quand (A ou B) et si C alors [Y1 → délai → Y2]`, plus `code custom` sandboxé : `trigger(s) → conditions → steps → runs avec logs par step`. ## 2. Modèle (migration 26) ```sql CREATE TABLE automation_steps ( id INTEGER PRIMARY KEY AUTOINCREMENT, automation_id INTEGER NOT NULL REFERENCES automations(id) ON DELETE CASCADE, kind TEXT NOT NULL, -- trigger|condition|delay|action position INTEGER NOT NULL DEFAULT 0, config_json TEXT NOT NULL DEFAULT '{}' -- trigger: {event: page.created|form.submitted|meeting.summarized|site.viewed|..., -- mode: any|all (au niveau automation)} -- condition: {property, operator, value} (AND entre steps condition) -- delay: {seconds|minutes|hours|days} -- action: {type: webhook|set_property|create_page|notify|slack|email|forge_issue|agent_trigger, -- ...params + interpolation [[prop]]/{{title}}} ); CREATE TABLE workers ( id INTEGER PRIMARY KEY AUTOINCREMENT, slug TEXT NOT NULL UNIQUE, workspace_id INTEGER REFERENCES workspaces(id), name TEXT NOT NULL, code_py TEXT NOT NULL DEFAULT '', schedule_cron TEXT, shared BOOLEAN NOT NULL DEFAULT 0, daily_budget_s INTEGER NOT NULL DEFAULT 60, created_by INTEGER REFERENCES users(id), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE worker_runs ( id INTEGER PRIMARY KEY AUTOINCREMENT, worker_id INTEGER NOT NULL REFERENCES workers(id) ON DELETE CASCADE, status TEXT NOT NULL, -- ok|error|timeout|over_budget logs TEXT NOT NULL DEFAULT '', duration_ms INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ALTER TABLE collection_properties ADD COLUMN button_automation_id INTEGER REFERENCES automations(id); ``` Compat : automations v5.1.0 existantes migrées en 1 step trigger + 1 step action. ## 3. Sémantique d'exécution - `mode any` (défaut) : n'importe quel step trigger déclenche ; `all` : tous les events doivent arriver dans la fenêtre 5 min (état `automation_runs`). - Nouveaux triggers : `form.submitted` (v6.8), `meeting.summarized` (v7.1), `site.viewed` (seuil 100 vues/jour), `agent.run.failed`. - Actions : `slack` (incoming webhook URL chiffrée), `email` (via `mailer.py`), `forge_issue` (Gitea/GitHub `owner/repo`, titre/body/labels), `agent_trigger` (`agent_id` + input), `delay` (re-queue run, pas de sleep). - Scheduler existant étendu : évalue steps en ordre, log `automation_runs` enrichi `{step_id, in, out, ms}`. ## 4. Bouton DB natif Propriété `button` : `{label, automation_id}` ; rendu cliquable en table/board/gallery ; `POST /api/automations/{id}/run` (déjà CSRF-exempt) + garde ACL écriture ligne ; optimistic toast. ## 5. Workers lite - Code Python restreint : pas d'import réseau/`os`/`subprocess`, timeout 30s, mémoire cap, allowlist `httpx` vers `Gitea/GitHub` + webhooks sortants uniquement. - Déclenchement : cron (`schedule_cron`) ou manuel ou `agent_trigger` ; `shared=1` → fork 1-clic (comme Notion Workers partagés). - Compteur usage : `SUM(duration_ms)/jour/workspace` affiché façon credits dashboard. ## 6. UI / sécu / tests - Settings → Automations : canvas vertical triggers → conditions → actions (Alpine.js, composants `components.css`), test-run avec payload sample, historique par step repliable. - Secrets (Slack URL, tokens) chiffrés Fernet comme `sso_config` ; champ vide = conserver. - Tests `tests/test_v70_automations_workers.py` (~25) : any/all, chaîne + delay (clock injectée), slack/email/forge/agent mocks, button ACL, worker timeout/over_budget/partage, migration v5 compat.