CreatorTrack Automations engine (generic trigger/condition/action)¶
URL: https://mkdocs.justinsforge.com/memory/general/reference_creatortrack_automations/
A GENERIC per-workspace automation engine: a rule is {trigger:{event,filters}, actions:[...]} evaluated after every task create/update. NOT a "dev mode" feature, the dev-pipeline rules (status to In Review, status to Ready to Merge => open PR) are just PRELOADED EXAMPLES of the same primitive a content creator uses ("status to Published => notify"). Built 2026-07-11 (Phase 2 of the automated-dev-pipeline plan) on branch feat/automations-github-pipeline.
Status: DEPLOYED to prod 2026-07-12 (merged to main 1b4a3b4, automations migration applied, creatortrack-prod restarted). Verified live: app.automations table + RLS present, task writes unaffected by the mutation-path hook. Security-reviewed before deploy (6 cautions fixed: XSS href allowlist, fan-out cap MAX_ACTIONS=50, zero-rule query cache, admin-gated authoring, outbound idempotency, uniform webhook 401).
Data model¶
- Table
app.automations(migration20260711T233000_feat_automations_automations_rule_engine.sql):id, workspace_id, name, enabled, trigger jsonb, actions jsonb, position, created_by, created_at, updated_at. Workspace RLS viacore.apply_workspace_rls. Indexautomations_ws_enabled_idx (workspace_id, enabled). - Storage is a real workspace-scoped table (not user prefs) because automations belong to the WORKSPACE: any member's task change fires them.
Engine (lib/automations.ts)¶
- Events:
task.created,task.updated. Conditions:{field, op, value}where op isequals|not_equals|changed|changed_to|contains|is_empty|is_not_empty; field is a core task key (status/priority/title/done/due/assignee/tags) OR a custom field NAME on the tasks db (e.g.Result). Filters are AND. - Actions: internal
set_status | set_priority | set_field | assign | add_subtask | notify(run throughupdateTask/createTask) + externalwebhook(delivers vialib/webhooks.ts deliverOutboundWebhook, SSRF-guarded, signed) +github(opens branch/PR/issue vialib/github.ts, see github integration). - Hooked into
lib/tasks.tscreateTask/updateTaskviafireAutomations(awaited, best-effort, never fails the task write). - LOOP GUARD (critical):
AsyncLocalStorageexecution-depth counter,MAX_DEPTH = 5. An action that updates a task re-enters the engine at depth+1; past MAX_DEPTH the engine stops and logs oneautomation_blockedactivity row instead of cascading forever. Verified: a no-filter self-cascading rule runs exactly 5 times then blocks, the request returns (no hang). - Each rule that fires logs an
automation_runactivity row (actorKindservice, labelAutomation) with per-action results, for the unified feed.
Builder UI (components/apps/automations/AutomationsSettings.tsx)¶
- CRUD API
app/api/automations/route.ts(+[id]), session-gated, RLS-enforced. GET returns rules + catalog (events/ops/fields/actions) + the workspace status options + custom field names + outbound webhooks. - Pane in Settings under Workspace ("Automations" section, added to
components/profile/SettingsModal.tsx). Per-workspace selector, enable toggles, a WHEN/IF/THEN rule editor rendered from the catalog, and "Add starter rules" (idempotent preload of the pipeline examples, disabled by default).
Verification¶
scripts/dev-verify-github-* and inline dev checks: rule fires (status changed_to In Review => set_priority High + notify), loop guard holds, builder CRUD + preload + headless render all pass. See Phase 2 results.
[Claude Code]