Skip to content

fix(automation,objectql): conditional & record-change flows silently skipping#1429

Merged
xuyushun441-sys merged 1 commit into
mainfrom
fix/objectql-attach-previous-update-hook
Jun 1, 2026
Merged

fix(automation,objectql): conditional & record-change flows silently skipping#1429
xuyushun441-sys merged 1 commit into
mainfrom
fix/objectql-attach-previous-update-hook

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Problem

Every flow with a start-node / edge condition silently skipped. Record-change triggers fired (engine.execute ran), but the flow body never executed — previous.* gates, budget > 100000, and status == "done" && previous.status != "done" all evaluated to false and the flow returned {skipped, condition_not_met}. Only condition-less flows (e.g. the interval schedule) worked.

Discovered while testing the showcase automation suite end-to-end in a browser.

Two independent root causes

1. service-automation — CEL engine unreachable in ESM (the universal blocker)

AutomationEngine.evaluateCondition loaded the formula engine lazily:

const { ExpressionEngine } = require('@objectstack/formula');

The package ships ESM ("type": "module"), where tsup compiles that require into a __require stub that throws Dynamic require of "..." is not supported. The surrounding catch { return false } swallowed it, so every CEL evaluation returned false — platform-wide, for all condition sites (start gates, decision nodes, edge predicates). Fixed with a static top-level import, which binds in both ESM and CJS builds.

2. objectql — prior record not exposed to update hooks

HookContext documents a previous snapshot "for update/delete", but engine.update never populated it — the row it fetched for validation (needsPriorRecord) stayed a local variable. So even once CEL worked, record-change conditions referencing previous.* had nothing to read. The engine now attaches the pre-update record to hookContext.previous for single-id updates whenever a validation rule needs it or an afterUpdate hook is registered.

Verification

End-to-end in the showcase (record-change triggers + capability tokens from #1426):

  • reassign taskassigned_notify completed → task.assigned inbox row, keyed to the resolved admin user id (email→userId from feat(messaging,cli): messaging + triggers capability tokens; notify-by-email resolves to user id #1426).
  • mark in_review → donerest_ping (REST connector_action GET /api/v1/health), completed (email), and slack (connector_action) all completed.
  • project budget → 250000budget_approval launched and paused at manager_review.
  • state-machine rule still enforced (in_progress→done rejected; in_review→done allowed).

Tests

  • @objectstack/objectql: 451 passed — new test asserts afterUpdate hooks receive ctx.previous, and that no prior-record fetch happens absent hooks/rules.
  • @objectstack/service-automation: 105 passed — new test asserts the CEL path evaluates record-change conditions (previous.* + bare fields).

🤖 Generated with Claude Code

…skipping

Every flow with a start-node / edge condition silently skipped — record-change
triggers fired but the flow body never ran, and `previous.*` / `budget > N`
gates all evaluated false. Two independent bugs:

service-automation — CEL engine unreachable in ESM
  The condition evaluator loaded the formula engine via a CommonJS
  `require('@objectstack/formula')`. In the package's ESM build
  ("type":"module") that compiles to tsup's throwing `__require` stub, so every
  CEL evaluation threw and the swallowing catch returned false. Replaced with a
  static top-level import — binds correctly in both ESM and CJS builds.

objectql — prior record not exposed to update hooks
  HookContext documents a `previous` snapshot for update/delete, but
  engine.update never populated it (the row fetched for validation stayed a
  local var). Record-change conditions like
  `status == "done" && previous.status != "done"` had no `previous` to read.
  The engine now attaches the pre-update record to hookContext.previous for
  single-id updates when a validation rule needs it or an afterUpdate hook is
  registered.

Verified end-to-end in the showcase: reassign → notify→inbox, mark-done →
REST/Slack connector_action + email, budget>100k → approval (paused at
manager_review). New unit tests cover both paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 1, 2026 1:40am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jun 1, 2026
@xuyushun441-sys xuyushun441-sys merged commit a6d4cbb into main Jun 1, 2026
12 checks passed
@xuyushun441-sys xuyushun441-sys deleted the fix/objectql-attach-previous-update-hook branch June 1, 2026 01:49
xuyushun441-sys added a commit that referenced this pull request Jun 1, 2026
Activates the showcase's automation chains now that the platform supports
them (capability tokens from #1426; conditional/record-change flows fixed in
#1429):

- requires: ['ui','automation','approvals','messaging','triggers','job'] so
  the notify node delivers and record-change/schedule flows auto-fire.
- Register the `rest` + `slack` connectors for the connector_action node.
  `rest` points at the running server (SHOWCASE_SELF_URL) so its call +
  response are observable with no external dependency.
- Two worked flows: ScheduledDigestFlow (interval schedule → notify → inbox)
  and TaskCompletedRestPingFlow (record-change → rest connector GET /health).
- Fix BudgetApprovalFlow's decision node: branch on edge `condition`
  (`budget > 500000` / `<= 500000`) per the flow spec, instead of the
  unevaluated node-level config + true/false labels — so budgets ≤ $500k
  correctly skip the executive approval step.
- Ambient `process` decl so `pnpm typecheck` stays green without @types/node.

Verified end-to-end in the browser: schedule digest, reassign→notify→inbox
(email→user-id resolved), mark-done→REST/Slack/email, budget→approval with
correct decision routing and approve→resume.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xuyushun441-sys pushed a commit that referenced this pull request Jun 1, 2026
feat(app-shell): repoint Console bell to sys_inbox_message + receipts (ADR-0030) (#1429)

objectui@80f97961772850e4792dcfcfe69676a84e518ede
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants