Skip to content

Commit 6dcf2be

Browse files
committed
fix(service-automation): bind previous as null on the create leg; fix showcase notify recipient (#3427)
Dogfooding the record-after-write showcase flow in a live app surfaced two bugs: 1. The engine bound `previous` into the flow condition scope only when truthy, so on a record insert `previous` was an unknown CEL variable — the documented `previous == null` create-discrimination threw "Unknown variable: previous" and failed the whole start condition, dropping the run. `previous` is now always bound (null when there is no prior row), making the create/update discrimination the record-after-write docs + Studio designer advertise actually work. Verified end-to-end (integration test + a live showcase boot: the flow fires on create-urgent and on escalation, and correctly skips a non-urgent create). 2. The showcase `UrgentTaskAlertFlow` notify node had no recipient, so every run failed "at least one recipient is required". Now notifies the assignee, falling back to the triggering user (`{$User.Id}`) so an unassigned urgent task still pings someone. Live-verified: a `task.urgent` notification is delivered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018939yJ413zG3irLzcTtqaa
1 parent be7e242 commit 6dcf2be

3 files changed

Lines changed: 30 additions & 3 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"@objectstack/service-automation": patch
3+
---
4+
5+
fix(service-automation): bind `previous` (as null) on the create leg so start conditions can discriminate create vs update (#3427)
6+
7+
The engine bound `previous` into the flow condition scope only when it was
8+
truthy, so on a record insert (`record-after-create`, and the create leg of
9+
`record-after-write`) `previous` was an **unknown** CEL variable. Any reference to
10+
it — including the documented `previous == null` create-discrimination — threw
11+
`condition failed to evaluate as CEL: Unknown variable: previous`, failing the
12+
whole start condition and dropping the run.
13+
14+
`previous` is now always bound, to `null` when there is no prior row. So
15+
`previous == null` is the create leg and `previous != null` / `previous.<field>`
16+
the update leg — the pattern the `record-after-write` docs and the Studio flow
17+
designer advertise. Update-triggered flows are unaffected (`previous` was, and
18+
stays, the prior row there).

examples/app-showcase/src/automation/flows/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,11 @@ export const UrgentTaskAlertFlow = defineFlow({
15721572
label: 'Notify Assignee',
15731573
config: {
15741574
topic: 'task.urgent',
1575+
// Notify the assignee; fall back to whoever raised the priority
1576+
// (`{$User.Id}` = the triggering user) so an as-yet-unassigned urgent task
1577+
// still pings someone. Empty recipients are dropped, so the fallback only
1578+
// applies when `assignee` is unset.
1579+
recipients: ['{record.assignee}', '{$User.Id}'],
15751580
channels: ['inbox'],
15761581
severity: 'warning',
15771582
title: 'Urgent task: {record.title}',

packages/services/service-automation/src/engine.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,9 +1623,13 @@ export class AutomationEngine implements IAutomationService {
16231623
if (!variables.has(k)) variables.set(k, v);
16241624
}
16251625
}
1626-
if (context?.previous) {
1627-
variables.set('previous', context.previous);
1628-
}
1626+
// Always bind `previous` — to `null` on the create/insert leg (there is no
1627+
// prior row) — so a start condition can DISCRIMINATE create vs update on a
1628+
// `record-after-write` flow: `previous == null` is the create leg (#3427).
1629+
// Binding only-when-truthy left `previous` an unknown CEL variable on
1630+
// insert, so ANY reference to it (even `previous == null`) threw
1631+
// "Unknown variable: previous" and failed the whole condition.
1632+
variables.set('previous', context?.previous ?? null);
16291633

16301634
const runId = this.nextRunId();
16311635
// Expose the run id to executors (ADR-0019): a pausing node (e.g. Approval)

0 commit comments

Comments
 (0)