Skip to content

Commit 7be028a

Browse files
feat(approvals): annotate approver value + escalateTo as typed references (#1414)
Adds `xRef` annotations to two Approval-node config strings so the Studio flow designer renders pickers instead of free text (ADR-0018 §configSchema), mirroring the existing `approvalStatusField` annotation: - `approvers[].value` — a *polymorphic* reference (`kindFrom: 'type'`): the concrete picker follows the sibling `type` column (user / role / team / department / queue), or an object-field picker resolved from the flow's `$trigger` object when `type` is `field`. `manager` and any unmapped value carry no `value` and stay free text. - `escalation.escalateTo` — a `role` reference (the common case); free text is still accepted for a specific user id. Both fold the annotation and description into a single `.meta()` so neither is dropped from the published `z.toJSONSchema` config contract. Consumed by the companion objectui PR's reference pickers. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
1 parent 23c7107 commit 7be028a

1 file changed

Lines changed: 28 additions & 2 deletions

File tree

packages/spec/src/automation/approval.zod.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,27 @@ export const ApprovalNodeApproverSchema = lazySchema(() => z.object({
6767
* holding a user id (`field`), or queue id (`queue`). Omitted for `manager`
6868
* (resolved from the submitter's `manager_id`).
6969
*/
70-
value: z.string().optional().describe('User id / role / team / department / field / queue — per `type`'),
70+
// `xRef` marks this string as a *polymorphic* typed reference (ADR-0018
71+
// §configSchema): the concrete picker follows the sibling `type` column, so
72+
// the Studio designer shows a user/role/team/department/queue picker — or an
73+
// object-field picker (resolved from the flow's `$trigger` object) when
74+
// `type` is `field`. `manager` and any unmapped value carry no `value` and
75+
// stay free text. A single `.meta()` carries both description and annotation.
76+
value: z.string().optional().meta({
77+
description: 'User id / role / team / department / field / queue — per `type`',
78+
xRef: {
79+
kindFrom: 'type',
80+
objectSource: '$trigger',
81+
map: {
82+
user: 'user',
83+
role: 'role',
84+
team: 'team',
85+
department: 'department',
86+
field: 'object-field',
87+
queue: 'queue',
88+
},
89+
},
90+
}),
7191
}));
7292
export type ApprovalNodeApprover = z.infer<typeof ApprovalNodeApproverSchema>;
7393

@@ -80,7 +100,13 @@ export const ApprovalEscalationSchema = lazySchema(() => z.object({
80100
timeoutHours: z.number().min(1).describe('Hours before escalation triggers'),
81101
action: z.enum(['reassign', 'auto_approve', 'auto_reject', 'notify']).default('notify')
82102
.describe('Action on escalation timeout'),
83-
escalateTo: z.string().optional().describe('User id, role, or manager level to escalate to'),
103+
// Escalation hands the request to a role (the common case — e.g. a manager
104+
// role or an approvals queue owner); the Studio designer renders a role
105+
// picker, but free text is still accepted for a specific user id.
106+
escalateTo: z.string().optional().meta({
107+
description: 'User id, role, or manager level to escalate to',
108+
xRef: { kind: 'role' },
109+
}),
84110
notifySubmitter: z.boolean().default(true).describe('Notify the original submitter on escalation'),
85111
}));
86112
export type ApprovalEscalation = z.infer<typeof ApprovalEscalationSchema>;

0 commit comments

Comments
 (0)