You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A notify node's message templates (title / body) cannot traverse lookup or formula fields of the triggering record. {record.full_name} (a formula field) and {record.account.name} (a lookup hop) render as empty strings, silently producing notifications like "New lead to assign: " with the name missing.
On an object, define a formula field, e.g. crm_lead.full_name = F\record.first_name + " " + record.last_name``.
In a record_change flow's notify node, use it in a template:
title: 'New lead to assign: {record.full_name}',body: '{record.full_name} from {record.company}',
Trigger the flow (insert a lead) and read the resulting sys_inbox_message.
Actual behaviour
The inbox message title is "New lead to assign: " — {record.full_name} resolved to empty.
Same for a lookup hop like {record.crm_account.name}.
Non-computed scalar columns on the same record ({record.company}, {record.first_name}) interpolate correctly.
Expected behaviour
The record seeded into a record-change run should expose the same computed/related fields the record has when read via the data API — at minimum formula fields, ideally single-hop lookups. If traversal genuinely isn't supported, templates referencing an unresolvable path should be documented as unsupported and ideally warn at build time, rather than silently emitting blanks.
Impact
Medium. Notifications and alerts are a primary flow output; silently blank names/values make them look broken to end users and there's no design-time signal.
Workaround
Recompose from raw scalar columns in the template, e.g. {record.first_name} {record.last_name} instead of {record.full_name}. Works for formula fields built from local columns; doesn't help for lookup traversal.
No design-time signal for unresolvable template paths
New os validate rule validateFlowTemplatePaths — warns on unknown-field (flow-template-unknown-field) and lookup-traversal (flow-template-lookup-traversal) refs instead of a silent blank; plus re-read guards (skip when no formula field, memoize per write)
Opt-in single-hop expansion: a flow declares expand: ['<lookup>'] on its start node and the engine re-reads the relation as the run's own identity (runAs:'user' ⇒ the triggering user, RLS/FLS enforced), grafting it onto the record so the template resolves
Lookup expansion is opt-in per relation and read as the triggering user, so it never exposes fields that user couldn't read directly, and non-declared relations keep their scalar id (bare {record.<lookup>} and multiple-lookup arrays, [P2] record-change context does not hydrate multi-lookup (junction) fields #1872, are unchanged). Once a relation is declared in config.expand, the flow-template-lookup-traversal warning is suppressed.
Activity
fix(trigger-record-change): hydrate read-time formula fields onto the…
os-zhuang commentedon Jul 24, 2026
进展:formula 字段部分已修复(#3445 已合并)
已合并 PR #3445(squash 进
main,1dc94f0),解决了本 issue 的 formula 公式字段 部分。根因:
formula是读取时(read-time)虚拟字段——引擎只在find/findOne取数后计算它,写入路径既不计算也不落库。而 record-change 触发器喂给流程的种子record直接来自生命周期钩子的原始行(ctx.result叠加输入 doc),这一行根本不含公式字段,所以{record.full_name}被插值成空字符串。方案:
RecordChangeTrigger(record的生产者)在组装种子记录后,对afterInsert/afterUpdate以系统身份回读刚写入的记录,让种子record与「数据 API 默认读取」拿到相同的计算字段。修复在触发器层,因此整条流程(开始条件、所有节点、notify 的title/body)都受益,而非只给 notify 打补丁。刻意保守:原始字段优先合并、只补齐缺失的公式虚拟字段、任何失败都回退原始记录。改动文件:
packages/triggers/trigger-record-change/src/record-change-trigger.ts(核心为新增的hydrateComputedFields);测试见record-change-trigger.test.ts与新增的formula-context.test.ts。仍待跟进(留给后续 agent)
本 issue 暂不关闭,以下两点尚未处理:
1. lookup 跨表遍历
{record.account.name}有意未在本次一并做:默认数据 API 读取本身也不展开关联;若在种子记录里默认展开,
record.account会从标量外键 id 变成对象,反而破坏那些把它当 id 用的模板/条件(例如 #1872 的{record.target_channels.0})。可选方向:expand配置(显式声明展开哪些关联),回读时带上expand传给ObjectQL.findOne(引擎已支持expandmap,见packages/objectql/src/engine.ts的expandRelatedRecords)。record.<lookup>.<field>做惰性解析(需给template.ts的插值器注入数据引擎句柄)。2. 构建期告警(issue 里的兜底诉求)
对模板引用了不可解析路径(尤其是 lookup 跨表)的情况,在 flow lint / publish gate 里加校验,把当前的静默空白变成显式告警。此项独立于 1,可先落地。
Generated by Claude Code
fix(lint,trigger-record-change): warn on unresolvable flow templates …
feat(automation): opt-in single-hop lookup expansion for record-chang…
os-zhuang commentedon Jul 25, 2026
Resolved — all three asks landed on
mainClosing: every part of this report is now fixed.
{record.full_name}(formula field) renders blankos validaterulevalidateFlowTemplatePaths— warns on unknown-field (flow-template-unknown-field) and lookup-traversal (flow-template-lookup-traversal) refs instead of a silent blank; plus re-read guards (skip when no formula field, memoize per write){record.account.name}(lookup hop) renders blankexpand: ['<lookup>']on its start node and the engine re-reads the relation as the run's own identity (runAs:'user'⇒ the triggering user, RLS/FLS enforced), grafting it onto the record so the template resolvesNotes:
{record.<lookup>}andmultiple-lookup arrays, [P2] record-change context does not hydrate multi-lookup (junction) fields #1872, are unchanged). Once a relation is declared inconfig.expand, theflow-template-lookup-traversalwarning is suppressed.{record.first_name} {record.last_name}) is no longer needed since fix(trigger-record-change): 让 record-change 流程种子记录携带公式(formula)字段 (#3426) #3445.main. The original report was on@objectstack/*16.1.0 — these reach that install only in the next release cut.Multi-hop traversal and a typed start-node schema for
expanddiscoverability are possible future enhancements, out of scope here.fix(lint,cli): a filter reference that cannot resolve fails the build…