modernx-admin — AI automation builder: trigger params
The natural-language automation builder (POST /api/ai/build-automation,
implemented in lib/automations/ai/) turns a merchant’s plain-English
description into a structured automation graph via
spec.ts (the model’s output contract) → compile.ts
(compileSpecToGraph, deterministic) → validateAutomationGraph. As of
2026-08-03 the trigger half of that pipeline can carry params, matching how
action/condition nodes already worked.
The bug this closed
Section titled “The bug this closed”Action and condition nodes were always compiled as
{ ...def.defaultParams(), ...spec.params }, but the trigger node was
hardcoded to params: {} regardless of its type’s defaultParams(). This was
invisible in production because both triggers registered at the time
(orderPlaced, orderStatusChanged) have empty param schemas. It becomes a
real bug the moment any trigger requires non-empty params — e.g.
auto.trigger.cartAbandoned ({ minutes: number, 15..10080 }, landing on the
sibling epic modernx-admin-noi): an AI-drafted cart automation would compile
with invalid trigger params and always fail validation until the merchant
fixed it by hand in the canvas.
Two independent fixes landed together, because either alone is insufficient:
compile.tsmerges trigger defaults.compileSpecToGraphnow builds the trigger node the same way as action/condition nodes:{ ...def.defaultParams(), ...(spec.trigger.params ?? {}) }. An unregistered trigger type still compiles — defaults fall back to{}— socompileSpecToGraphstill never throws.spec.ts’s trigger gained aparamsfield.AiAutomationOutputSchema’s trigger is nowz.object({ type: z.enum(TRIGGER_TYPES), params: z.record(z.string(), z.unknown()).optional() }).strict(), so the model can express trigger configuration the description states (e.g. “abandoned for 3 hours” →{ minutes: 180 }). It’s optional, not required like action/conditionparams— most triggers are paramless, and this keeps every existing construction site (stubAutomation, fixtures, the route’s repair round) valid unchanged. Model-supplied params win overdefaultParams()per key, not as an all-or-nothing replacement.
buildSystemPrompt() (lib/automations/ai/build.ts) documents each
param-carrying trigger’s shape on its line (mirroring how condition/action
shapes are already rendered) via a new TRIGGER_PARAM_SHAPES map, and
instructs the model to set trigger params from the description instead of
leaving them at their defaults.
The inert cartAbandoned entry
Section titled “The inert cartAbandoned entry”TRIGGER_PARAM_SHAPES is seeded with an auto.trigger.cartAbandoned entry
even though that trigger isn’t registered yet as of this writing — it ships on
modernx-admin-noi. It’s harmless to seed early because prompt lines are
rendered by iterating the live TRIGGER_TYPES from the registry, not this
map, so the entry does nothing until the trigger itself is registered. This
was deliberate: modernx-admin-noi independently edits the same
trigger-line-rendering code and adds its own TRIGGER_PARAM_SHAPES map (with
a comment saying trigger params can’t be set — no longer true after this
epic). Whichever branch merges second resolves the lib/automations/ai/build.ts
conflict by keeping this epic’s rendering logic plus noi’s cartAbandoned
entry — both halves are wanted, and the fix here is written generically
against NodeTypeDef.defaultParams() so it needs no further change once
cartAbandoned (or any future param-carrying trigger) lands.
Why tests use a synthetic trigger, not cartAbandoned
Section titled “Why tests use a synthetic trigger, not cartAbandoned”compile.test.ts builds a local new NodeRegistry() with a fake
test.trigger.windowed type ({ minutes: int 15..10080 },
defaultParams: () => ({ minutes: 60 })) rather than asserting against the
real cartAbandoned, so this epic’s tests don’t depend on modernx-admin-noi
having merged first.
lib/automations/ai/ only — no REST contract change, no Magento-side change,
no UI change. The canvas already renders trigger config panels from node
params like any other node, and the route already compiles → validates →
offers the model one repair round for an out-of-range value (e.g. a
5-minute window below cartAbandoned’s 15-minute floor).
See also Automations engine entity model
for the runtime side these compiled graphs execute against, and
Scan-trigger infrastructure for
the periodic-scan mechanism cartAbandoned itself will run on once
registered.
Source: modernx-admin PR #405.