Skip to content

Record-change trigger plugin loads but flows never fire on data writes (7.4.1) #1491

@xuyushun441-sys

Description

@xuyushun441-sys

Summary

With the triggers capability enabled, @objectstack/plugin-trigger-record-change loads (appears in the started-plugins list) but record-change flows are never launched when records are created/updated through the REST data API. Object-level L2 lifecycle hooks fire correctly on the same writes, so the ObjectQL hook pipeline itself works — the gap is specifically in the trigger-plugin → automation wiring.

Scheduled flows are likely affected by the same wiring (couldn't be confirmed — cron-bound).

Environment

  • @objectstack/runtime 7.4.1
  • @objectstack/cli 7.4.1
  • @objectstack/service-automation 7.4.1
  • @objectstack/plugin-trigger-record-change 7.4.1
  • @objectstack/plugin-trigger-schedule 7.4.1
  • @objectstack/objectql 7.4.1
  • Driver: sqlite-wasm · Node v25.9.0 · launched via objectstack start
  • Repro app: https://github.com/objectstack-ai/hotcrm (flow lead_assignment)

Repro

  1. App requires: [..., 'triggers', ...]RecordChangeTriggerPlugin + ScheduleTriggerPlugin appear in the "Plugins: N loaded" banner.
  2. Define a record_change flow whose start node config matches the documented contract resolved by resolveTriggerBinding():
    { id: 'start', type: 'start',
      config: { objectName: 'crm_lead', triggerType: 'record-after-create' } }
    Body: a decision + update_record that stamps a field (e.g. next_followup_date).
  3. POST /api/v1/data/crm_lead to create a record.
  4. Poll the record for up to 30s.

Expected

The flow binds to the ObjectQL afterInsert hook (triggerTypeToHookEvent('record-after-create') === 'afterInsert') and runs, stamping the field.

Actual

The field is never written; updated_at is unchanged after 30s. The flow does not run. Same result for record-after-update flows (case escalation, opportunity approval, CSAT followup).

Evidence that the rest of the stack works

  • L2 object hooks DO fire on REST writes: a beforeInsert hook on crm_lead recomputed rating (sent rating: "5", stored rating: "1"), proving ObjectQL lifecycle hooks run on the REST data path.
  • Build/registration is clean: 10 flows register; Studio reports zero metadata-validation issues.
  • triggerType values map correctly: triggerTypeToHookEvent('record-after-create') → 'afterInsert', 'record-after-update' → 'afterUpdate'.
  • The data engine exposes registerHook (confirmed at runtime: objectql engine = OK registerHook? function).

What I could trace (read-only, from dist)

  • service-automation resolveTriggerBinding() requires start.config.triggerType starting with record- (or flow.type === 'schedule'). ✅ satisfied.
  • registerFlow()activateFlowTrigger(); and registerTrigger() re-binds already-registered flows, so plugin/flow registration ordering should be handled.
  • RecordChangeTriggerPlugin.start() registers the trigger on the kernel:ready hook via automation.registerTrigger(...), then start(binding, cb) calls engine.registerHook(hookEvent, handler, { object, packageId }).

So the binding should occur, but empirically the handler never runs.

Blocker for diagnosis

packages/cli serve forces the kernel logger to level: 'silent' (const loggerConfig = { level: 'silent' }). This suppresses every logger.info/logger.warn from the automation engine and trigger plugin — including:

  • Trigger registered: record_change
  • [record-change] bound flow '<name>' → afterInsert
  • RecordChangeTriggerPlugin: ... record-change trigger NOT installed

so it's impossible to tell from objectstack start output whether the trigger registered, whether any flow bound, or whether it failed. Please consider honoring an env var (e.g. OS_LOG_LEVEL) or a --log-level flag to override the silent default, independent of this bug.

Hypotheses to check

  1. kernel:ready hook in RecordChangeTriggerPlugin.start() not firing, or ctx.getService('automation') returning a different instance than the one that registered the flows.
  2. engine.registerHook('afterInsert', …, { object }) attaching to a hook channel that the REST/ObjectQL write path does not emit (event-name or per-object scoping mismatch between the trigger plugin and objectql 7.4.1).
  3. Flow runs but update_record is rejected under the system/anonymous context used by the trigger (RLS), failing silently.

Suggested next step

Add an integration test in packages/plugins/plugin-trigger-record-change that boots a LiteKernel with automation + record-change trigger + a trivial flow, inserts a row, and asserts the flow executed — with logger at info so the bind/exec path is visible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions