The when/then rule engine, project-scoped: a rule = one trigger → one flat condition group → an ordered action list, evaluated asynchronously off the channel-agnostic job events (Epics 2+5) through the Story 1.6 Inngest pipeline — the queued execution model the mirror runs (verified: Jira rules execute through a rule-processing queue, never inline). Actions execute through the SHIPPED services — workflowsService transitions, workItemsService field writes, watchersService, commentsService, labelsService, customFieldsService — never raw writes, so workflow legality, permissions, revision rows, and downstream notifications behave exactly as if a person did it.
The verified core sets (rung 1 — Atlassian cloud-automation docs, 2026-06-10). Triggers: work item created / transitioned (with optional from→to status narrowing) / field value changed (field picker; "assigned" is its assignee preset) / commented — Jira's four most-used issue-event triggers. Actions: transition, set field (built-ins + every 5.3 custom-field type), add watcher, add comment, add label — the stub's set grown to Jira's verified top tier. Conditions: ONE flat match all / match any group of 6.1 FilterAST rows evaluated against the triggering item — the issue-fields/JQL-condition analogue, reusing the 6.1.1/6.1.2 TOTAL operator registry + safe compiler wholesale (zero new predicate machinery; the registry stays the single predicate authority).
The rule actor (recorded deviation). Jira defaults rule attribution to a synthetic "Automation for Jira" user with a configurable actor; ours runs actions as the rule owner (the project admin who created it — Jira's own configurable-actor shape minus the synthetic default), because a per-workspace synthetic user would leak into every member-bounded surface the shipped reads enumerate (assignable members, mention pickers, boards) for no stated use case. The audit log + event provenance keep attribution honest; the dedicated system actor is the documented extension.
Loop prevention (the Jira default, hard-shipped). Every event emitted from an automation-executed action carries viaAutomationRuleId provenance, and the engine NEVER fires a rule off a provenance-carrying event — the verified Jira default (rules don't trigger rules). The "Allow rule trigger" opt-in chain + depth-10 loop detection is the documented extension when a chaining use case lands.
Real-product operations (finding #57 — bounded everywhere). A per-rule execution audit log (automation_rule_execution: success / failure / no-actions-performed, error detail, duration, the triggering item), paginated, with the Jira-verified 90-day retention enforced by a cron sweep; auto-disable at 10 consecutive failures (the Jira number) with the disabled state surfaced in the UI; an owner error email on the first failure after a success (the verified dedupe rule) via the 1.6 email pipeline; idempotent execution per (event × rule) so Inngest replays never double-fire actions; caps — 100 rules per project, 10 actions per rule, the 6.1 20-row condition cap — each a typed 422 with a designed state, never a silent truncation.
Out of scope (documented extension slots, each justified): scheduled/cron triggers (need a saved-filter item-selection substrate — Story 6.2 ships it; revisit after); branches + smart values (where a rule engine becomes a scripting language); global / multi-project rules (Jira gates these on site admins; Motir's admin substrate is project-scoped today); the "Allow rule trigger" chaining opt-in (above); send-email / webhook / create-item actions (each drags a new surface — templates, outbound HTTP policy, kind-parent rules — additive registry entries when a use case lands); monthly usage quotas (the open-core billing boundary — Epic 8.1 owns metering); a manual "run rule now" trigger (cheap but unowned by any use case yet).
pnpm install, pnpm prisma migrate dev (the 6.6.1 automation tables apply cleanly; re-run reports "No difference detected"), pnpm db:seed, pnpm dev (+ the Inngest dev server per the E2E harness notes).pnpm test:coverage — Vitest (real Postgres) over the registries, the engine (trigger × condition × action matrix, loop prevention, idempotency, auto-disable), and the CRUD service ≥90% per-file branch/fn/line.zhuyue@motir.co / !QAZ1qaz → project settings → Automation (matching design/projects/automation.mock.html). Create: "when an item transitions to Done, if Kind is any of (Bug), then add watcher Bo + comment 'Verify the fix'" — the editor builds when/if/then rows from the registries; the rule lists enabled with owner + last-run.pnpm test:e2e --grep automation — Playwright over the real stack: the author → fire → audit journey.--el-*, shape via element tokens.