Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/remove-record-change-trap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@object-ui/app-shell": patch
---

fix(app-shell): remove the never-firing `record-change` option from the flow trigger picker (#3427)

The Studio flow designer's start-node trigger picker offered "Record changed
(any)" (`record-change`), but the runtime routes it to the record-change trigger,
which maps it to no ObjectQL hook — so the flow binds yet **never fires**. Authoring
it produced a silently-dead flow. Removed the option (and dropped `record-change`
from the scope resolver's record/previous sets and the zh-CN labels). The common
"created or updated" case is covered by `record-after-write`; a companion
`@objectstack/lint` rule flags any hand-authored `record-change` at `os validate`
time.
1 change: 0 additions & 1 deletion packages/app-shell/src/views/metadata-admin/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3213,7 +3213,6 @@ const FLOW_FIELD_ZH: Record<string, Record<string, FlowFieldZh>> = {
'record-after-write': '记录创建或更新后',
'record-before-update': '记录更新前',
'record-after-delete': '记录删除后',
'record-change': '记录变更(任意)',
schedule: '定时(cron)',
time_relative: '相对时间(日期扫描)',
manual: '手动 / 自动启动',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ describe('start node trigger-field gating (#5)', () => {
expect(isFieldVisible(condition, node, fields)).toBe(true);
});

it('does NOT offer the never-firing `record-change` bare-noun option (#3427)', () => {
// The runtime routes `record-change` to the record-change trigger, which maps
// it to no hook — it never fires. Removed so the designer can't author a
// silently-dead flow; the `os validate` lint flags any hand-written one.
const triggerType = fields.find((f) => f.id === 'triggerType')!;
expect(triggerType.options?.some((o) => o.value === 'record-change')).toBe(false);
});

it('shows for a schedule trigger too', () => {
const node = { id: 'start', type: 'start', config: { triggerType: 'schedule' } };
expect(isFieldVisible(objectName, node, fields)).toBe(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,6 @@ const FLOW_NODE_CONFIG: Record<string, FlowConfigField[]> = {
{ value: 'record-after-write', label: 'Record created or updated' },
{ value: 'record-before-update', label: 'Record before update' },
{ value: 'record-after-delete', label: 'Record deleted' },
{ value: 'record-change', label: 'Record changed (any)' },
{ value: 'schedule', label: 'Schedule (cron)' },
{ value: 'time_relative', label: 'Time-relative (date sweep)' },
{ value: 'manual', label: 'Manual / autolaunched' },
Expand All @@ -262,12 +261,12 @@ const FLOW_NODE_CONFIG: Record<string, FlowConfigField[]> = {
ref: { kind: 'object' },
placeholder: 'crm_lead',
help: 'Target object for record / scheduled-scan triggers.',
showWhen: { field: 'triggerType', equals: ['record-after-create', 'record-after-update', 'record-after-write', 'record-before-update', 'record-after-delete', 'record-change', 'schedule', 'webhook', 'event'] },
showWhen: { field: 'triggerType', equals: ['record-after-create', 'record-after-update', 'record-after-write', 'record-before-update', 'record-after-delete', 'schedule', 'webhook', 'event'] },
}),
cfg('condition', 'Entry condition', 'expression', {
placeholder: 'status == "qualifying" && previous.status != "qualifying"',
help: 'CEL predicate — the flow runs only when this is true (for time-relative sweeps it gates each matched record). Leave empty to run on every event. On a "created or updated" trigger, `previous == null` selects the create path.',
showWhen: { field: 'triggerType', equals: ['record-after-create', 'record-after-update', 'record-after-write', 'record-before-update', 'record-after-delete', 'record-change', 'schedule', 'time_relative', 'webhook', 'event'] },
showWhen: { field: 'triggerType', equals: ['record-after-create', 'record-after-update', 'record-after-write', 'record-before-update', 'record-after-delete', 'schedule', 'time_relative', 'webhook', 'event'] },
}),
// Schedule descriptor — author the canonical nested `config.schedule` object
// the runtime actually reads (resolveTriggerBinding → normalizeSchedule). This
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ const RECORD_TRIGGER_TYPES = new Set([
'record-before-write',
'record-before-update',
'record-after-delete',
'record-change',
]);
/** Trigger types that carry a meaningful `previous` snapshot of the record.
* `record-*-write` fires on update too, so `previous` is offered (empty on the
Expand All @@ -96,7 +95,6 @@ const PREVIOUS_TRIGGER_TYPES = new Set([
'record-before-update',
'record-after-write',
'record-before-write',
'record-change',
]);

function asArray(v: unknown): unknown[] {
Expand Down
Loading