Repository navigation
[finding] The flow language has no aggregation / group-by node — the grouping half of the digest shape, deferred by ruling until a second pull #14500
Description
Activity
Triage — graded,
findingcleared. Re-check run atorigin/mained44512, with its control.Confirmed
packages/spec/src/automation/flow.zod.ts:30-52declares 22 action types —assignment,boundary_event,connector_action,create_record,decision,delete_record,email,end,get_record,http,join_gateway,loop,map,notify,parallel_gateway,screen,script,slack,start,subflow,update_record,wait. None of them buckets a result set by a field. Control (loop) is present in the same file, so the read is a real zero.I also checked the nearest neighbour the card does not mention:
map(:47) is annotated "Sequential multi-instance — per-item subflow, each may pause (ADR-0037 A2)" — per-item iteration, not accumulation. It does not close the gap, and nobody should reach for it as a workaround.State:
pm:on-hold, NOTneeds-user-decisionThis is the important call on this card. The question it carries has already been answered: the maintainer ruled #14149 as A on 2026-09-02, and the grouping half was deliberately not taken — zero new vocabulary until a second application pulls for it. Labelling this
needs-user-decisionwould put a settled question back in front of the maintainer, which is exactly what the deferral was for.So it sits on hold with the re-open trigger stated, and the trigger is the one the ruling named:
Re-open when a SECOND application cannot author a digest with the rendering half of #14149 in place.
Not before. One pull (
objectstack-ai/duly#11) is the pull that was already weighed and found insufficient; a second one is new evidence, and re-presenting on anything less is re-litigating a ruling.When it does come back it returns as a decision card, and the implementing PR carries
needs:contract-reviewat the contract-review tier — both candidate shapes (groupByonget_record, or a dedicated aggregation node) widen the published flow contract, which the card correctly marksClause-②: yes. I am not applying that label now: the card is not dispatchable and the gate would be sitting on nothing.Routing
domain:spec— the vocabulary ispackages/spec/src/automation, one contract lane whole, and either shape lands there first whatever the engine then has to do.priority:p3— deferred by ruling with zero pull until the second application appears. The priority is a placeholder for the re-open, not a claim that it is worth doing now.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026 objectstack-fleet commented
on Sep 23, 2026 ContributorMore actions关
not_planned—— 维护者逐张复核 on-hold 卡时决定关闭;#14149 的裁定与重开条件原样记录在此分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T10:31Z。维护者 2026-09-23 在分诊会话里逐张复核pm:on-hold卡。本卡在第七组的建议是保留;维护者要了详细解释之后,回复原文:「14500 关闭」。不因关闭而作废的裁定
#14149 由维护者在 2026-09-02 裁为 A:渲染那一半现在做,分组这一半等第二个应用来要再做(第一个是
objectstack-ai/duly#11),在此之前流程语言零新增词汇。- 渲染那一半已经落地:
assignment的值可以写 CEL 表达式,运行时求值(service-automation: evaluate a value-role CEL envelope in the assignment executor and validate it at registerFlow — the executor half of #14149 (spec half landed in PR #15113) #15137,@objectstack/service-automation17.4.0),一张列表可以拼成一段摘要正文。 - 分组这一半今天仍然缺:
origin/main3ad89c1bcc上packages/spec/src/automation/flow.zod.ts的节点类型里没有任何分组 / 汇总节点(groupBy/aggregat0 处,对照loop在同一文件)。
业务上缺的是什么(写给以后来搜的人)
「每天给每个负责人发一封他名下记录的汇总」—— 流程能查出一张平铺的列表、能逐条循环、能把一张列表拼成正文,但不能把列表按负责人切成组。当分组依据隔一层关联才能拿到时(例如 任务 → 所属客户 → 客户经理),流程写不出「每人一封」。
重开条件
第二个应用在渲染那一半已经可用的前提下,仍然写不出汇总类流程 ⇒ 带着那个应用的具体场景重开本卡,作为决策卡呈给维护者,在两个候选形状里择一:
get_record上加groupBy,返回一个loop可以按组迭代的分组结果;- 一个独立的汇总节点。
两者都会扩大对外发布的流程契约(
Clause-②: yes),实现 PR 需要走needs:contract-review。⛔ 一次拉动(duly#11)已经被权衡过,只有第二次才是新证据。关闭理由:
not_planned,同时摘掉pm:on-hold。
Generated by Claude Code
- 渲染那一半已经落地:
Filed by the director seat (session
session_01ShyhexkB2d1AeRZ85tgAAe, 2026-09-02) as the deferred half of #14149. Observation only; unassigned; grading is triage's.Restart-when: a second application cannot author a digest with the rendering half of #14149 in place
Provenance
#14149 was ruled A by the maintainer on 2026-09-02 (verbatim on that card): the rendering half lands (a value-role CEL slot on
assignment); the grouping half is deliberately not taken now — zero new vocabulary until a second application pulls for it. This card carries that half so it is visible to candidate queries rather than living as a paragraph inside #14149.The gap, as measured on 17.2.0 (#14149)
FlowNodeActionhas no node that buckets a result set by a field;get_recordwithlimit > 1returns a flat list,loopiterates one item at a time, andassignmentcannot accumulate across iterations. The only expressible "per recipient" route is to enumerate the recipient population separately and re-query per recipient — possible only when the grouping key is a stored column on the base object, impossible when it is one join away.Re-check:
git grep -n "FlowNodeAction" origin/main -- packages/spec/src/automation(positive control:loopin the same file).Candidate shapes (not ruled)
groupByonget_record, returning a keyed map theloopcan iterate per group;Either widens the published flow contract (
Clause-②: yes) and is a decision, not a mechanical fix. The trigger to re-present is a second application unable to author a digest with the rendering half in place.Refs: #14149 (ruled A) · objectstack-ai/duly#11 (the first pull).
⚙️ Bookkeeping, 2026-09-08 — the
Restart-when:line above transcribes the wake condition this card already stated in prose; it invents nothing. The condition was in the last paragraph of "Candidate shapes" from the day this card was filed, but only in prose, so the unlock sweep could never reach it — apm:on-holdcard with no machine-readable wake condition is illegal under the state model (「无机制可唤醒的卡 ⛔ 不 hold」), and this one was one line short of legal, not wrongly held.Checked with the repo's own predicate rather than a hand-rolled regex:
directiveValues(body, 'Restart-when')fromscripts/pm/check-half-states.mjsreturns the value above, and.some((v) => !/^manual\b/i.test(v))is true ⇒ it is not the illegalmanual — <reason>escape hatch. Added by thedomain:specexecution seat, sessionsession_016N6xmWt5hYm94ffVEwGH8x. ⛔ No pm state changed, no label moved, nothing closed — the hold stands exactly as ruled.