Estimate: 32m · Depends on: 6.1.1
The rule model and the authoring surface's backend. Pure backend — no UI, no engine yet.
Schema (FKs as Prisma relations, both sides): automation_rule — projectId, name, enabled, triggerType + typed triggerConfig JSON, conditionAst JSON (the 6.1 FilterAST shape, row-capped 20), actions JSON (ordered, max 10), ownerId (the rule actor — the recorded deviation), consecutiveFailureCount, timestamps; automation_rule_execution — ruleId, status enum (success / failure / no_actions), triggering workItemId (nullable — the item may be deleted later), error text, durationMs, createdAt; indexed [ruleId, createdAt] for the paged log + the retention sweep.
lib/automation/registry.ts — TWO TOTAL registries (mistake #29). Triggers: created / transitioned (optional from/to status-id narrowing) / field-changed (built-in-field picker config; assignee is the "assigned" preset) / commented — each entry = config schema + validate + the event name it consumes + the UI editor kind. Actions (this subtask: the shipped-substrate entries): transition (target status id) / set-field (built-ins the shipped workItemsService.update accepts — assignee, priority, due date, story points) — each entry = config schema + validate + execute fn signature + editor kind. Unknown trigger/action/field ids → typed 422; the enumeration test fails on any registry gap. Epic-5 entries (watcher / comment / label / custom-field) land in 6.6.3 as registry EXTENSIONS — same pattern as 6.1.1→6.1.2.
automationRulesService + routes (4-layer): create / update / enable / disable / delete / list / get, gated by the shipped 6.4 manage-project permission (viewers get 403/404 per the 6.4 read rules); validation composes the trigger/action registries + the 6.1.1 condition-AST validation (built-in fields here); caps — 100 rules/project, 10 actions/rule, 20 condition rows — as typed 422s; enable resets consecutiveFailureCount; delete cascades the execution log (Prisma onDelete).
pnpm test:coverage ≥90% on the new files.lib/filters/ast.ts + registry (the condition substrate + the totality/422 pattern to mirror)lib/services/projectAccessService.ts + the 6.4 role checks (the admin gate); workflowsService (status ids for transition configs)motir-core/CLAUDE.md (4-layer, FK-as-relation, required-tx)