Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt

Use this file to discover all available pages before exploring further.

Cross-Core Integration, Events, Automator & Workflow — Deep Dive & Recommendations

Version: 1.0
Last Updated: 2026-03-11
Status: Analysis and recommendations
This document provides a deep dive into cross-core integration strategy, event consumer/pub-sub, the automation engine, and workflow execution — with identified gaps and a consistent approach for improvement.

1. Cross-Core Integration Strategy

1.1 Current State

The platform uses three integration patterns (constitution §1.3, PLATFORM_INTEGRATION_LAYERS.md, CROSS_CORE_INTEGRATIONS.md):
PatternUse CaseImplementation
1. Platform Integration LayerShared capability used by multiple cores@/platform/* (forms, notifications, workforce, workflow canvas, realtime, events, etc.)
2. Event-Based IntegrationLoose coupling, async, audit trailDomain events via publishEvent()fw_domain_events; HTTP event-consumer; DB triggers → pg_notify (documented channels)
3. API ContractsSynchronous request-responseDocumented in API_CONTRACTS.md; many planned, few implemented as formal REST
Strengths:
  • Clear decision tree in .cursor/rules/integration-patterns.md and constitution.
  • Platform layers are well-documented (Forms, Notifications, Workforce, Realtime, Events).
  • Event channels and naming are defined in EVENT_CONTRACTS.md with canonical channels (domain_events, automation_trigger, fa_events, cl_pm_events, etc.).
  • Cross-core FK is scoped (ADR-002: CL may reference pm_encounters.id only).
  • Dependency graph in CROSS_CORE_INTEGRATIONS.md is up to date.

1.2 Gaps & Inconsistencies

GapDescription
Event delivery vs documentationEVENT_CONTRACTS and DATA_FLOW describe pg_notify → “Edge Function Listener.” Edge functions are HTTP-invoked; there is no in-repo pg_notify → HTTP bridge (e.g. Supabase Database Webhooks or a worker). So “listener” is either external or unimplemented.
Dual event pathsTwo distinct mechanisms: (A) Table-driven: publishEvent()fw_domain_events → trigger fw_process_domain_event() → inserts fw_workflow_executions (queued). (B) HTTP-driven: Client or service invokes event-consumer with event_type + payload. They are not unified; which to use when is not always obvious.
API contractsAPI_CONTRACTS lists many planned contracts (e.g. FA → RH episode balance, HR → RH employee lookup). Implementation status is mixed; no single “contract registry” with implementation status and version.
Platform layer indexPLATFORM_INTEGRATION_LAYERS has a good table; some entries are “Planned” or “In Progress.” A single “integration health” view (events, API, platform layers) would help.

2. Event Consumer & Pub/Sub

2.1 Current Mechanisms

Path A — Domain events table (FW automation / workflow):
  1. Cores call publishEvent({ event_name, organization_id, payload }) from @/platform/events.
  2. Event is inserted into fw_domain_events.
  3. DB trigger fw_process_domain_event() runs on INSERT:
    • Matches fw_automation_rules with trigger_type = 'event' (or pm_event) and event_config/trigger_config.
    • For each match, inserts a row into fw_workflow_executions with status = 'queued' and trigger_payload.
Path B — HTTP event-consumer:
  1. Caller (client or edge function) invokes supabase.functions.invoke('event-consumer', { body: { event_type, payload } }).
  2. event-consumer edge function:
    • Validates auth and body.
    • Runs Teams notifications (if pf_teams_notification_config has a rule for event_type).
    • Runs domain handlers (e.g. referral_accepted → CL transition + discharge checklist; other event types can be added in code).
  3. No subscription to PostgreSQL NOTIFY; invocation is push-by-caller only.
Path C — Realtime (Supabase):
  • Postgres Changes: Client subscribes to table changes (e.g. fw_workflow_executions, pf_notifications) via @/platform/realtime hooks. Used for live UI updates, not for “event routing” to server-side handlers.
  • Broadcast: Ephemeral, non-persisted; used for typing, presence, or in-app signals. Not used as the primary event bus for cross-core domain events.
Path D — pg_notify (documented, server-side consumer unclear):
  • EVENT_CONTRACTS and DATA_FLOW describe channels (domain_events, automation_trigger, fa_events, etc.) and say “Subscribers consume via edge functions that listen to pg_notify.”
  • In the repo there is no edge function that “listens” to pg_notify (edge functions are HTTP-triggered). So either:
    • A Supabase Database Webhook (or similar) maps NOTIFY to HTTP and calls an edge function, or
    • The intended “listener” was never implemented and form/DB-triggered flows rely on something else (e.g. client invoking automation-executor after form submit).

2.2 Gaps

GapDescription
No single “event bus” docTable-driven (fw_domain_events) vs HTTP (event-consumer) vs pg_notify are described in different places. One “Event delivery options” section with “when to use which” would reduce confusion.
event-consumer handler registryHandlers are hardcoded in event-consumer/index.ts. Adding a new event type requires code change and deploy. A small registry (e.g. event_type → handler name or config) could make it easier to extend.
Queued workflow executionsfw_process_domain_event() queues executions; no in-repo worker or cron that picks status = 'queued' and runs them (e.g. by calling automation-executor or a dedicated execution runner). So event-triggered rules may never run unless something else processes the queue.
Form submission → automationDATA_FLOW describes: form INSERT → trigger → pg_notify(‘automation_trigger’) → automation-executor. No migration defines trigger_automation_on_submission; automation-executor is invoked only via HTTP (e.g. dry-run). So either form-triggered automation is implemented elsewhere (e.g. client or webhook) or it is a gap.

3. Automator (FW-03)

3.1 Current State

  • Rules: fw_automation_rules with trigger_type, trigger_config, event_config, date_relative_config, conditions, status. Actions in fw_automation_actions.
  • Trigger types: form_submitted, form_updated, field_changed, schedule, webhook, manual, pm_event, event, date_relative.
  • Executor: Edge function automation-executor:
    • Invoked via HTTP with body { trigger_data, dry_run? }.
    • Validates JWT and org access (V2 pattern with pf_user_role_assignments).
    • For form triggers, expects trigger_data with trigger_type, submission_id, form_id, organization_id, submission_data, etc.
    • Fetches matching rules, evaluates conditions, executes actions (send_email, send_notification, update_record, create_record, call_webhook, run_function), supports visual workflow graph (FW-06 Phase 2) and variables (FW-18).
  • Event-triggered rules: When an event is inserted into fw_domain_events, fw_process_domain_event() only queues executions (inserts into fw_workflow_executions). The automation-executor is not invoked for those queued rows in the codebase (no cron or worker that reads fw_workflow_executions and calls the executor).

3.2 Gaps & Inconsistencies

GapDescription
Trigger config shapeDifferent trigger types use different columns: event_config, date_relative_config, trigger_config (for pm_event, form_submitted). Documented in FW AGENTS.md; edge function and UI must handle all consistently.
Who invokes executor for form submit?If there is no DB trigger → pg_net → automation-executor, then either the client must call automation-executor after form submit (with trigger_data), or a separate mechanism (e.g. cron polling fw_form_submissions) must exist. Not clearly documented.
Queued executions not processedEvent and pm_event triggers only create fw_workflow_executions with status queued. A workflow execution processor (cron or DB-triggered HTTP) that picks queued rows and runs them (via automation-executor or an internal runner) is missing or lives outside this repo.
PM-19 vs event registryPM-19 uses trigger_config with PMEventTriggerConfig; these events are not in fw_workflow_events. Documented as intentional; keep a single place that lists “event-triggered” vs “PM-internal” so automation UX stays consistent.

4. Workflow

4.1 Current State

  • Visual workflow (FW-06): Workflow builder (React Flow), definitions stored in DB; execution is driven by automation-executor when invoked with appropriate trigger_data (e.g. form_submitted or workflow_execution payload). Executions stored in fw_workflow_executions; realtime subscription used for monitoring.
  • XState machine (FW-18): createWorkflowMachine / useWorkflowMachine in @/cores/fw/machines — client-side step machine (form, approval, action, condition, notification, wait). Not wired to the server-side workflow definition or automation-executor; no production callers outside the machines module. FW AGENTS.md and FORMS_WIZARDS_WORKFLOWS_AUTOMATIONS_RECOMMENDATIONS.md mark it as experimental / reserved for future use.
  • Workflow vs automation: Workflow builder = graph of nodes (trigger, actions, conditions, approvals). Automation rules = single trigger + conditions + actions. Overlap in concept; different UX and data model.

4.2 Gaps

GapDescription
Two execution modelsServer-side: definition → automation-executor → fw_workflow_executions. Client-side: XState step machine. They are not connected; avoid spreading a second execution model until there is a concrete product need (e.g. approval chain UI).
Single “Create automation” entryRecommendation in FORMS_WIZARDS_WORKFLOWS_AUTOMATIONS_RECOMMENDATIONS: one entry point (“Simple rule” vs “Workflow”) to reduce confusion.
Platform workflow = visualization only@/platform/workflow (WorkflowCanvas, swim lane) is for visualization only; execution stays in FW. Document this so new features don’t add execution logic to platform.

5. Recommendations — Improvement, Gaps, Consistency

5.1 Cross-Core Integration

RecPriorityAction
R1HighUnify event delivery documentation. Add a single “Event delivery” section (e.g. in EVENT_CONTRACTS or a new docs/architecture/EVENT_DELIVERY.md) that describes: (1) Table-driven: publishEvent()fw_domain_events → trigger → queued workflow executions — use for FW automation/workflow triggers. (2) HTTP event-consumer: invoke('event-consumer', { event_type, payload }) — use for cross-core side effects (Teams, CL transitions, etc.) that need a single place to add handlers. (3) Realtime: Postgres Changes / Broadcast for UI only, not for server-side event routing. (4) pg_notify: Document current state: channels and DB triggers exist; server-side “listener” is either via Supabase webhooks/external worker or not yet implemented; clarify and document.
R2MediumIntegration health view. Add a short “Integration status” page or table: Platform layers (with status), API contracts (planned vs implemented), event channels (table vs HTTP vs pg_notify). Link from CROSS_CORE_INTEGRATIONS and PLATFORM_INTEGRATION_LAYERS.
R3LowAPI contract registry. In API_CONTRACTS or a companion file, add a table: contract name, provider, consumer, status (planned / in progress / implemented), version. Keeps API strategy and implementation status in one place.

5.2 Event Consumer & Pub/Sub

RecPriorityAction
R4HighImplement or document queued execution processing. Either: (A) Add a cron-driven edge function (or worker) that polls fw_workflow_executions for status = 'queued', then invokes automation-executor (or a dedicated “run-workflow-execution” function) with the execution id and trigger_payload; or (B) Use Supabase Database Webhooks to call an edge function on INSERT to fw_workflow_executions where status = ‘queued’. Document the chosen approach in EVENT_CONTRACTS and in automation-executor README.
R5HighForm submission → automation. Confirm intended flow: (1) Client invokes automation-executor after form submit with trigger_data, or (2) DB trigger on fw_form_submissions + pg_net (or webhook) to call automation-executor. If (2), add the trigger and webhook; if (1), document it in FW-03 and DATA_FLOW so implementers don’t assume a DB listener.
R6Mediumevent-consumer handler registry. Option A: Keep code-registered handlers but list them in a small table in EVENT_CONTRACTS (event_type → handler name, one-line description). Option B: Add a DB or config table (event_type → handler key) so new event types can be wired without code change. Prefer A for simplicity unless you need non-deploy configuration.
R7MediumIdempotency and retries. For event-consumer and any new execution processor, document idempotency (e.g. event_id or execution_id idempotency key) and retry/backoff so handlers are safe when invoked more than once. Rationale: Prevents duplicate side effects and data corruption when events are retried or delivered multiple times.

5.3 Automator

RecPriorityAction
R8HighTrigger config reference in one place. Keep and maintain the “Automation trigger config reference” (trigger_type → config location and shape) in FW AGENTS.md; ensure automation-executor and UI both reference it and handle all trigger types. Add a short “Trigger config” section in EVENT_CONTRACTS or FW-03 spec for event/date_relative/pm_event.
R9MediumSingle “Create automation” entry. From the Automations dashboard, offer “Simple rule” (AutomationRuleBuilder) and “Workflow” (visual builder). Reduces confusion between rules and workflows (already recommended in FORMS_WIZARDS_WORKFLOWS_AUTOMATIONS_RECOMMENDATIONS).
R10LowPM-19 and fw_workflow_events. Explicitly document that PM-19 event types are “PM internal” and not registered in fw_workflow_events; list them in a small table so event-driven automation behavior is predictable.

5.4 Workflow

RecPriorityAction
R11MediumXState role. Keep XState workflow machine as “experimental / reserved for future” unless a concrete feature (e.g. approval chain UI, submission wizard) adopts it. Document in FW AGENTS.md and form-libraries-advanced-extensions: “Do not use in new features without a product requirement.” Avoid a second, parallel execution model.
R12LowWorkflow vs automation in docs. In FW AGENTS.md or a short “Workflow and automation” doc, state: “Visual workflows = multi-step automation with graph/approvals; Automation rules = single trigger → one or more actions. Use workflows for branching/approvals; use rules for simple when-X-then-Y.”
R13LowPlatform workflow = visualization only. In PLATFORM_INTEGRATION_LAYERS and platform workflow README, state that WorkflowCanvas / swim lane are for visualization only; execution lives in FW (automation-executor, fw_workflow_executions).

5.5 Consistency Approach (Summary)

  1. One event narrative: Document the two main paths (table-driven for FW automation, HTTP event-consumer for cross-core handlers) and when to use each; clarify pg_notify and Realtime roles.
  2. Close the execution loop: Ensure “queued” workflow executions are processed (cron or webhook) and form submission triggers automation in a documented, consistent way.
  3. Single reference for triggers: One maintained “trigger config” and “event vs PM-internal” reference for automation and workflow.
  4. Unified automation UX: One entry point for creating automations (simple rule vs workflow) and clear guidance on workflow vs rule.
  5. No duplicate execution model: Keep XState experimental until a concrete feature uses it; execution remains server-side (automation-executor + fw_workflow_executions).

6. References