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
38 changes: 38 additions & 0 deletions .changeset/9336-end-node-refusal-message-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
'@object-ui/app-shell': minor
---

Give the flow `end` node's inspector a typed control for `config.message`, the key a
refused outcome requires (objectui#9336).

`EndConfigSchema` cross-validates `outcome` and `message` in **both** directions:
`outcome: 'refused'` **requires** a `message` (a refusal nobody can explain is the shape
that contract exists to replace), and `outcome: 'completed'` — including an omitted
`outcome`, since the declared default resolves to `completed` — **refuses** one (a
completion renders nothing, so the key would be a silent no-op).

The form offered a typed control for `outcome` and none for `message`. Since
objectui#9278 made Outcome a closed two-option dropdown, `refused` is one click away —
and picking it, with nothing else done, produced a flow that **fails to load**, because
the one key that outcome requires was authorable only through the Advanced (JSON) block.
That was a blocked authoring path, not a missing nicety.

The `end` group now offers **"Why the run was refused"** — a `textarea`, gated on
`showWhen: { field: 'outcome', equals: ['refused'] }`. The kind, label, placeholder and
help are derived from the installed spec rather than chosen: `EndConfigSchema` describes
`message` as a `{token}` template "interpolated at run time exactly like a screen
`description`", and that field is a `textarea` in this same table (as is the sibling
`message` key on `notify`).

The gate is what makes the pair authorable in **both** directions. An unset `outcome`
resolves through the declared `completed` default, so the field stays off screen until
the author actually picks `refused`; a **stored** `message` re-shows it regardless
(objectui#6499's stored-value rule), which is the only way an author can clear a stale
message after switching back to `completed`. Clearing deletes the key rather than
storing `''` — the empty string is refused under *both* outcomes.

The Outcome field's help (English and the zh-CN overlay) no longer says the message is
"set in Advanced", since it no longer is.

Not addressed here: objectui#9335, the same group's `outputVariable` row — a key
`EndConfigSchema` refuses by name. That row is deliberately untouched and remains open.
8 changes: 7 additions & 1 deletion packages/app-shell/src/views/metadata-admin/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4105,9 +4105,15 @@ const FLOW_FIELD_ZH: Record<string, Record<string, FlowFieldZh>> = {
end: {
outcome: {
label: '结果',
help: '运行在此处如何结束。“已完成”是普通终态,也是省略该键时的取值。“已拒绝”把拒绝记为一等结果 —— 它是一次成功的评估,只是结论为否 —— 并要求给出拒绝理由 message({token} 模板),在“高级”中填写。',
help: '运行在此处如何结束。“已完成”是普通终态,也是省略该键时的取值。“已拒绝”把拒绝记为一等结果 —— 它是一次成功的评估,只是结论为否 —— 并要求在下方字段中给出拒绝理由 message({token} 模板)。',
opts: { completed: '已完成', refused: '已拒绝' },
},
// objectui#9336 —— 仅在“结果”选为“已拒绝”时出现(见 flow-node-config 的
// `showWhen`);契约两个方向都校验:已拒绝必须有 message,已完成则拒收它。
message: {
label: '拒绝原因',
help: '当结果为“已拒绝”时必填;结果为“已完成”时则被拒收 —— 已完成的结束节点不渲染任何文本,该键会成为静默的空操作。支持 {token} 模板(如 {record.name}),与屏幕节点的“描述”一致。',
},
outputVariable: { label: '输出变量' },
},
decision: {
Expand Down
Loading
Loading