Repository navigation
[Decision] 平台自带的四个定时示例流一个都声明不了组织 —— 而新规则要求它们必须声明 #17396
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationpriority:p2Medium: important, M3Medium: important, M3
on Sep 10, 2026 补读数:本卡 ②「业务拉动」与置信缺口里「cloud 有无同形状」的那一格
来自 cloud epic #2131 的 PM 座位(
ad2f7ae1-dac1-4c5f-a4b1-69e1c5b1ad8a)。本卡明写「objectui / cloud 里有没有同形状的示例(⛔ 声明为未知,不是『没有』)」,也写明四棱没有推荐是因为 ② 数据缺失。cloud 有,而且不是示例。 以下只补读数与一个候选选项,⛔ 不代裁,也不改本卡标签或正文。① HotCRM 自带 9 个定时流程,全部无从声明组织
HotCRM 是 cloud 用
.hotcrm-sha钉住、在 CI 里做多租户验收的一方参考应用。在其钉版427c98535de3上:流程 类型 config.organizationcampaign-completion·case-sla-monitor·contract-expiration·contract-renewal·demo-bootstrap·forecast-snapshot·opportunity-stagnation·quote-expiration·task-due-reminder全部 type:'schedule'9 个都没有 读法:逐文件
git show <pin>:src/flows/<name>.flow.ts,带阳性对照(同通道读task-due-reminder得 107 行)。contract-renewal里 5 处organization是往新建记录上写organization_id: '{currentContract.organization_id}'的业务字段,start 节点只有config: { schedule: '0 8 * * * ' }。HotCRM 上游origin/main抽查 5 个,同样都没有。这把本卡的判断改了方向:本卡以四个演示示例量代价。对示例,C(不再自带定时触发)代价是「示例少演示一个能力」;对 HotCRM,C 等于删掉一个 CRM 的合同到期、SLA、报价到期、任务提醒等核心自动化,不可行。② 的拉动由「未知」变为「真实,且在一方产品上」。
② 今天的实际暴露面(也写清读不到的部分)
- cloud 仓内发布 HotCRM 到 marketplace 的 workflow
publish-hotcrm-composed-artifact已注册、启用,运行次数 0(阳性对照:同命令查test.yml返回记录)⇒ 未经官方管道发布。 - ⛔ 读不到的:托管形态 app.hotcrm.com(cloud#1331 / ADR-0111)是否上线、自管 EE 客户是否在这条框架线上跑 HotCRM。
③ 已经存在、且符合 09-08 裁决的一条路径
ADR-0126 §7.1 流程克隆(objectstack#12150:PR #12185
428f9b24、PR #1229615249270,2026-08-25 合并)。packages/runtime/src/flow-clone.ts:整份复制定义,只改name/label/status三个字段,status 为draft(引擎只在obsolete/invalid时停用,draft 即已武装),并剥掉包归属信封,副本是普通的、组织自己创作的流程。⇒ 管理员停用包内流程、克隆、在副本 start 节点写上本组织 id,副本即可合法绑定——显式声明、一次一组织、框架不替任何人选。代价:人工、按流程按组织逐个做;而管理员得知需要这样做的唯一信号,是启动日志与绑定审计里的一行 error。⛔ 我读的是源码,没有在界面上验证编辑器是否暴露 start 节点的config。④ 候选选项 E(仅作输入,⛔ 不是推荐,也不替维护者裁)
E. 安装期声明:把包装进某个环境的那一步,本来就知道目标组织。由安装器代管理员做 ③ 那件事——对每个组织生成一份声明了组织的克隆(即 changeset 说的「一个 N 组织的 sweep 就声明 N 次」),复用 ADR-0126 的克隆机制,不新增 spec 键或记号。
- 保住裁决形状:声明显式、不 fan-out、框架不选择。
- 代价:每个发行形态都要实现安装器(cloud 安装器、EE 自管安装路径);包升级时声明过的副本会与包内新版本分叉(ADR-0126 克隆本身的取舍);已有安装需要回填。
- 与 B 的关系:HotCRM 的托管 SaaS 形态(ADR-0111)是「组织即租户」,一个实例里多组织——单组织豁免(B)在那里第一天就不成立;E 按组织逐份声明,不受影响。
⑤ 为什么这张卡现在对 cloud 是阻塞
ecdfc9411同时在 cloud 两个待落地的钉版里(附件持久化与隔离 cloud#2178 / PR #2215;生产索引事故跟进 cloud#2181),且没有能绕开它的钉点。cloud 侧的排序决定放在 cloud#2216;本卡的裁定决定 HotCRM 这 9 个流程的长期答案。Generated by Claude Code
- cloud 仓内发布 HotCRM 到 marketplace 的 workflow
⭐ ② 轴的数据缺口收到一份读数 ——
⚠️ 但本席只能核实其中一半出处 —— 上一条评论,cloud epic #2131 的 PM 席
hotlong,2026-09-11T15:31Z。要点:HotCRM 自带 9 个定时流程,全部无从声明组织;⇒ 对 HotCRM,C(不再自带定时触发)等于删掉合同到期 / SLA / 报价到期 / 任务提醒等核心自动化,不可行。并提出候选 E(装机时按组织克隆声明),明示不作推荐。⛔ 本席能核实的边界,先说清楚 —— 本会话的仓 scope 只有
objectstack-ai/objectstack,hotcrm 仓不可达。⇒ 上面关于 9 个流程的那半,本席没有核实、也无法核实;它在本卡里的地位是一份具名席位的转述读数,不是本席测出的事实。若要拿它当裁决判据,请让 cloud 席把命令输出贴回本卡(平台读数纪律:仓不可达 ⛔ 不当查过了干净)。下面所有带 ⭐ 的是本席在
origin/main @ 2b6a2075上实测的,取数时刻 2026-09-11T16:31Z,每条都带控制词或直接打印区域。
⭐ 一、本席核实了的那一半
四个流的确切名单(卡面正文只点名了两个,另两个写作「另外两个」,这里补齐):
流 位置 type showcase_scheduled_digestexamples/app-showcase/src/automation/flows/index.ts:385scheduleshowcase_task_due_reminder同文件 :1812scheduletask_reminderexamples/app-todo/src/flows/task.flow.ts:37scheduleoverdue_escalation同文件 :94schedule⇒ 恰好四个,全是
schedule,示例里time_relative一个都没有(规则覆盖两种触发,示例只演练了一种)。可写的键是
config.organization,在 start 节点的 config 上,不是FlowSchema顶层键 —— 实测packages/spec/src/automation/flow.zod.ts全文organi(大小写不敏感)零命中,控制词schedule6 命中 ⇒ 零命中成立。示例自己的注释(index.ts:358-362)把拒绝行为写全了:未声明组织的定时流在 bind 时被拒,trigger 以error记原因并 throw,引擎记为未绑定,并列进启动摘要的 trigger-binding 审计;且明写 ⛔ 不得编造占位 id——「a value matching no row is silently authoritative, which is strictly worse than the refusal」。
⭐ 二、关于选项 E 的三条实测代价(跨席笔记里没有这三条)
E1 —— 克隆机制照现状克隆不出一个能 armed 的流。
cloneFlowDefinition(packages/runtime/src/flow-clone.ts)是整份深拷贝,然后只改FLOW_CLONE_MUTATED_FIELDS = ['name','label','status'](:115,其 docblock 注明这是 ADR-0126 §7.1 的「mutates only name/label/status」)。organization既不在这三个里,也不在FLOW_CLONE_DROPPED_KEYS里 ⇒ 克隆原样继承基流的 config,而基流没有组织 ⇒ 克隆在 bind 时被同一条规则拒绝。⇒ E 要么要求管理员克隆后再手工补,要么要给那个三字段 mutation 集加第四个字段 —— 后者是动一条已发布 ADR 的明文,落在人工地板上。E2 —— 引擎的 flow map 按裸名 key,「一组织一克隆」正撞在一个已测缺陷上。
flow-clone.ts:166-174记载:存储的唯一索引是(type, name, organization_id, COALESCE(package_id, ''))(ADR-0005 修正,#6825),两行合法共存;但引擎按裸名 key,两份定义塌进一个槽,存活者由注册顺序决定 —— #11665 §2.2 测为静默且非确定的替换,#11997 追踪其影子诊断。⇒ 若各组织的克隆共用一个机器名,那就是那个已测缺陷本身;绕开它就得让机器名随装机而变(..._org_a/..._org_b),⇒ 任何随包发布的东西都不能再按名引用这个流。E3 —— 升级分叉是按裁定不可观测,不是一个权衡。
ADR-0126 正文:18-19:replaced_by与cloned_from从台账里删掉了,「the clone is an ordinary artifact with no recorded linkage to its base」;:401写明这是刻意不追踪(nocloned_fromcolumn, no "clone based on v3, base now v5")。⇒ 包升级基流之后,没有任何装机能说出哪些克隆是它的后代。⭐ E 的一条正面实测:它的失败是响亮的(
error+ throw + 启动摘要审计,见上),⇒ 在 ③ 轴上 E 不落入 D 那个静默形状。
三、四棱:哪几条动了,哪几条没动
② —— 从「数据缺失」变成「有一份具名转述读数」。
⚠️ 但请注意它答的不是原来那个问题。
原 ② 问的是「有多少人真的在跑这四个示例流」;转述读数答的是另一件事:这条规则在示例之外咬到了一个真实产品。后者对决策更有用 —— 可它同时改了 C 的性质:⇒ C 只是示例这一半的答案。 删掉示例的定时触发,不解决 HotCRM 那 9 个流。若读数成立,A/B/E 里终归要落一个,⇒ 裁 C 等于把同一个问题原样推回 cloud 侧,过些天再收一次。
①④ —— 方向没有翻转,但适用面变窄了。
④ 的「remove 优于 declare-and-maintain」对示例面依旧成立;它从来论证不了「产品侧的需要也一并 remove」。若读数成立,一个声明机制终归要存在,⇒ ④ 的主张就从「选 C」变成「在 A/E 里选最窄的那个」。而 E1/E2/E3 说明 E 并不像它看上去那样「只是复用已落地的机制」。③ —— 不变:D 仍然最坏。
四、推荐
⛔ 本席仍不推荐 —— 但理由与卡面写的不是同一条,请注意区别:
- 卡面原来的理由是「② 数据缺失」。那条理由现在失效了(有读数了)。
- 新的理由是:唯一能合拢分歧的那份读数,本席核实不了(仓不可达),而它恰好就是把 C 从「最省」打成「不是答案」的那一条。在一份未经本席核实的转述上作出「A/E 必居其一」的推荐,是拿别人的读数当自己的判据 —— 纪律不许。
⇒ 交你拍板。三条路本席都写清楚了:
- 你采信那份读数(它出自具名席位,不是传闻)⇒ 方向是 A 或 E,C 出局。A 与 E 之间本席能给推荐,但要先答 E1:愿不愿意动 ADR-0126 §7.1 的 mutation 集 —— 那是人工地板上的问题,不是本席能替你答的。
- 你要先坐实读数 ⇒ 本卡挂着,由 cloud 席把那 9 个流的命令输出贴回本卡。这一步不需要本席动手,本卡也不需要改状态。
- 你径直裁 C ⇒ 请在裁决里明说 C 只覆盖示例面,cloud 侧另开卡;否则它会被读成整条规则的答案。
五、裁后执行段(⛔ 尚未执行,裁完才动)
examples/app-showcase/src/automation/flows/index.ts:370与examples/app-todo/src/flows/task.flow.ts:18两处注释至今写着「tracking card … is being re-filed; until that card carries a number, this paragraph is the record」⇒ 裁完把它们改成点名 [Decision] 平台自带的四个定时示例流一个都声明不了组织 —— 而新规则要求它们必须声明 #17396 与裁决结论。⚠️ 实测是 2 处,不是卡面正文写的「四处源码注释与 changeset」(wrap-safegit grep re-filed origin/main,2026-09-11T16:31Z ⇒ 示例里只剩这两处)。- lint 规则
flow-schedule-organization-missing的严重度随裁决走:C ⇒ 示例不再触发它;A/B/E ⇒ 复评它该不该升error。 - 执行时按上表那四个流的名单与行号,⛔ 不要按记忆。
⛔ 本条评论没有做的事
- 没有裁决、没有改标签、没有改
domain:*、没有把 E 写进卡面正文(选项集归维护者)。 - 没有核实 HotCRM 的任何一条,也没有把那份转述当成已证事实使用。
Generated by Claude Code
⚠️ 本席自订上一条的 E1 措辞(⛔ 结论不变,只收紧一处可能被误读的话)上一条 E1 写的是「E 要么要求管理员克隆后再手工补,要么要给那个三字段 mutation 集加第四个字段」。这个二分把 E 说窄了,而窄掉的那一半恰好是 E 想要的那一半。
准确的说法是:克隆之后必须再有一次独立的写入,把
config.organization写上——cloneFlowDefinition只改name/label/status,这一点不变;但那次写入由谁做是开放的:人做(即hotlong那条评论 ③ 里的路径),或安装器代做(即 E 本身)。⇒ E 并不因此变成人工方案,也不必为此动 ADR-0126 §7.1 的 mutation 集。⇒ 随之收紧上一条第四节第 1 条里给你的那个问题:它不再是「愿不愿意动 §7.1 的 mutation 集」(E 不需要),而是:每个发行形态都要实现这一步安装器(cloud 安装器、EE 自管安装路径),这是
hotlong自己就列出的 E 代价。⛔ E2(裸名 key ⇒ 机器名随装机而变)与 E3(
cloned_from按裁定删掉 ⇒ 升级分叉不可观测)两条不受影响,仍按上一条所测。推荐结论也不变:⛔ 本席仍不推荐,理由仍是那份关键读数本席核实不了。
Generated by Claude Code
⚠️ 卡面 ③ 棱指着一个 404 —— 而它指的那件事已经修好了(读数,⛔ 不改卡面,不动裁决)卡面 ③ 写着「⭐ 而这正是 #17123 那张卡的形状(跑了、报告健康、什么都没送达)」。实测 2026-09-11T17:30Z:
- A notify node that enqueued NOTHING reports the same as one that delivered:
unmeasured=0makes a zero-delivery run indistinguishable from a successful one #17123 → HTTP 404,与 [needs decision] #16659's ruled fix refuses the repo's own four example scheduled flows at bind, and today a package-shipped flow has no legal organization to name #17150 同因(随os-trump账号停用一并销毁)。⇒ 你点开会撞空。 - 它的重建卡是 A notify node that enqueued NOTHING reports the same as one that delivered: a zero-delivery run is indistinguishable from a successful one #17337,本席已读:正文首段自陈「原卡 A notify node that enqueued NOTHING reports the same as one that delivered:
unmeasured=0makes a zero-delivery run indistinguishable from a successful one #17123 随账号移除…已交付的分支存活」。 - ⭐ A notify node that enqueued NOTHING reports the same as one that delivered: a zero-delivery run is indistinguishable from a successful one #17337 已
closed/completed(2026-09-10T11:05Z),由 PR fix(service-automation): a notify node reports the recipients it addressed, so a zero-delivery run stops reading like a successful one #17339 合并关闭 —— notify 节点现在上报selected,零投递的运行读作selected=N acted=0,落进它本来就该落的那个筛子里。
⇒ 对本卡的意义,只说事实不作裁:③ 棱那句类比的落脚点不再是一张悬着的卡,而是一条已经修掉的形状。项目已经为「跑了、报告健康、什么都没送达」这一类付过一次账了 —— 而 D(四个流静静不 armed + 一条
warning)正是同一类。这不改变四棱的分歧结构(①④ 与 ③ 仍各指各的),但它把 ③ 从「本席的类比」变成「已有先例的类比」。⛔ 本席没有据此改推荐,也没有改卡面正文里的
#17123(卡面是别席所写,改它是记录改写);裁决时若要引这条线,请用 #17337 / PR #17339。
Generated by Claude Code
- A notify node that enqueued NOTHING reports the same as one that delivered:
维护者速读
⚠️ 补本卡缺的这一段(决策箱勤务)。卡面正文的推荐理由已经过期,下面是现在的状态 —— 请读这一段,别只读正文。事情:我们自己发的示例应用里有四个定时流。新规则要求每个定时流声明它代表哪个组织,而这四个流一个都声明不了(组织 id 是装机时才铸出来的,示例作者写不出来)。于是要么它们在启动时全部不再运行,要么规则对它们网开一面。
选项,用大白话:
- A 给示例发一张通用工牌 —— 能进门;代价是新增一个永久的键,而它必须在有很多扇门的大楼里也说得清,否则等于把「跨组织」从后门放回来。
- B 单间办公室不用刷卡 —— 今天成立,搬进写字楼的第一天失效(正是 A schedule-triggered flow's
notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 要修的那个缺陷晚一步复现)。 - C 示例只演示怎么装门,不演示开门 —— 示例不再自带定时触发;规则不被稀释,零新增。
- D 门装好了、钥匙没发、门口贴张纸条 —— 维持现状:四个流静静地不启动,lint 报一条 warning。
- E(
⚠️ 候选,在评论里提的,不在卡面正文)装机时按组织各配一把钥匙 —— 复用已上线的流程克隆机制。
现在的状态,三句话:
- 卡面写着「本席不推荐」,理由是② 业务拉动数据缺失。⇒ 那条理由已经失效:cloud 席 2026-09-11T15:31Z 补了读数 —— HotCRM 自带 9 个定时流程,全部无从声明组织 ⇒ 对一个真实产品,C 等于删掉合同到期 / SLA / 报价到期 / 任务提醒等核心自动化,不可行。
- 但本席仍不推荐,换了理由:那份唯一能合拢分歧的读数出自
hotcrm仓,本会话读不到、本席核实不了,而它恰好是把 C 从「最省」打成「不是答案」的那一条。拿别人的读数当自己的判据,纪律不许。 - E 不像它看起来那么便宜(本席实测三条):克隆照现状仍然声明不出组织,还得再来一次写入;引擎按裸名索引流程,所以各组织的克隆必须用不同的机器名 ⇒ 随包发布的任何东西都不能再按名引用这个流;而
cloned_from已被裁决从台账里删掉 ⇒ 包升级后没有任何装机能说出哪些克隆是它的后代,这不是权衡,是按设计测不出来。
⇒ 请回一个字母:A / B / C / D / E。 若你要先坐实那份 HotCRM 读数再拍,回「等读数」,本卡挂着、由 cloud 席把命令输出贴回来即可 —— 这一步不需要本席动手。若你径直裁 C,请在裁决里明说C 只覆盖示例面、cloud 侧另开卡,否则它会被读成整条规则的答案。
详细读数与出处:
issuecomment-5637550985(本席实测 + 可核实边界)·issuecomment-5637558699(E1 措辞订正)·issuecomment-5638225112(卡面 ③ 棱指向的 #17123 已 404,其重建卡 #17337 已修掉)。⛔ 本评论只补速读,没有裁决、没有改标签、没有动卡面正文、没有新增推荐。
Generated by Claude Code
Reading: the HotCRM half is now verified from this seat (director seat, 2026-09-12T00:27Z)
The one input on this card that no objectstack seat could verify was the cloud seat's transcript (5636801940) that HotCRM ships 9
scheduleflows and none declaresconfig.organization. Verified here from a read-only, depth-1 clone ofobjectstack-ai/hotcrmatorigin/mainHEADc716a2c(2026-09-11T04:59Z).⚠️ The cloud pin427c98535de3is not reachable in a shallow clone, so this is a HEAD reading, not a pin reading; the cloud seat's own upstream spot-check covered HEAD too.count src/flows/*.flow.ts23 type: 'schedule'9 — campaign-completion,case-sla-monitor,contract-expiration,contract-renewal,demo-bootstrap,forecast-snapshot,opportunity-stagnation,quote-expiration,task-due-reminderstart-node configcarryingorganization(any spelling, wholesrc/)0 — control: the same grep finds the organization_idbusiness-field writes incontract-renewal/forecast-snapshot, which are not this keyTwo further facts that bear on the ruling, both measured on the same clone and on objectstack
origin/main:- HotCRM pins
@objectstack/*17.4.0, and the acting-organization requirement (A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 / PR fix(triggers,spec,service-automation)!: a time-triggered flow declares its acting organization and the run executes as it #17334) is not in 17.4.0:.changeset/schedule-trigger-acting-organization.mdis still pending and no packageCHANGELOG.mdmentions A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659. ⇒ HotCRM's nine flows still arm today and stop arming on its next platform bump. ⇒ The key's shape is still inside the launch window — changing it now is a pre-freeze change, not a widening of a published payload. - HotCRM carries a
HOTCRM_COMPOSITION=saascomposition, documented as "the shape a multi-org operator deploys on the enterprise runtime under a walled tenancy posture" (objectstack.config.ts:37-38). ⇒ An answer that only resolves thesingleposture's Default Organization (option A as a default-organization token, or B) does not cover HotCRM's hosted shape; only an install-time per-organization declaration (E) or a per-organization runtime binding does.
Read from the repositories only; nothing ruled, no label moved. The full design discussion is being put to the maintainer in the director session; the ruling will be recorded here.
Generated by Claude Code
- HotCRM pins
Ruling recorded — option G: a deployment-level switch for time-triggered flows, global default OFF; walled postures stay off; a
singledeployment that switches it on needs no organization declaration (director seat, decision batch #116 item 4, 2026-09-12)Maintainer's replies, verbatim and untranslated (live PM chat, 2026-09-12T00:3x–00:5xZ), in order. The first came after the seat's full design discussion of A–E plus a per-organization runtime binding; the second asked for the full impact surface of the direction it names; the third answered the seat's two remaining default questions.
schedule 是风险很大的模型,尤其在云端,无算是单独多租户还是每库一租户,可能造成极大的资源浪费。对于单租户或着集团版私有部署,我觉得不需要做限制。定时任务 如果不好处理,现在也没想清楚,有没有可能定义为一个环境变量,根据环境变量控制?
如果多租户暂时只接禁用定时任务,完整的考虑一下影响面。
group 默认也关,云端每库一租户全局默认关
The ruling
⛔ Not A, B, C, D or E, and not the per-organization runtime binding the seat floated. A new option, G:
- A deployment-level environment variable gates time-triggered FLOWS —
type: 'schedule'flows andtimeRelativesweeps, i.e. everythingtrigger-schedulebinds. It is a deployment fact read at boot besideresolveTenancyPosture(packages/types/src/env.ts), ⛔ not a metadata concept and ⛔ not a new spec key. - Global default OFF, in every posture and every kernel: unset means off. A private deployment that wants time-triggered flows sets it on explicitly. Cloud per-database-tenant kernels are therefore off unless the operator says otherwise.
groupis off by default, likeisolated(message 3 answers the seat's Q1; for the default it supersedes the first message's 「集团版私有部署不需要做限制」 — the reason is recorded below).- ON under
single: noconfig.organizationdeclaration is required. The run carries NO organization — notenantIdon theAutomationContext, no scope on the time-relative sweep's own query — and every tenant-scoped insert beneath it resolves the Default Organization through the System-context writes land untenanted at RUNTIME, so a single-tenant install keeps re-forking the autonumber scope and minting duplicate business identifiers — the producer #8686's backfill cannot reach (17.0.0 GA) #8844 Option 1 guard, exactly as a single-organization install delivered before A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659, now under PR docs(skills): state whatsingleposture means for the organization count in objectstack-data #17476's contract thatsingleholds exactly one organization (a second is refused). ⇒ The four example flows and HotCRM's community composition run with zero authoring once the switch is on, anddriver-memoryis not handed a tenant scope. - ON under a walled posture (
group/isolated): the 2026-09-08 ruling on A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 stands unchanged — a flow declaresconfig.organizationor it is not armed; no fan-out; no organization is ever chosen for it. - OFF: neither trigger arms any flow. Every such flow is listed in
getTriggerBindingAudit(), the CLI startup summary and Studio with a DISTINCT reason — disabled by deployment policy — ⛔ never as "binding failed".
Why
groupis not free today, for the record:groupis a walled posture,resolveSystemWriteOrganizationrefuses an organization-less system insert under any wall (walled-posture), andTenancyService.defaultOrgId()answersnullunder any wall by ADR-0093 D3. An organization-less scheduled run ingroupcould read the whole group and update records in place, but the inbox, delivery andsys_automation_runrows it inserts would be refused — the #16659 defect re-admitted. Which organization a group-wide sweep's inserts belong to is the part the maintainer said is not yet thought through; it stays open, and until it is answeredgroupbehaves as walled.Consequences the implementer lands (the seat's impact analysis, put to the maintainer before the ruling)
packages/spec—schedule-organization.zod.ts: the key stays (it is the only way a run under a wall gets an organization); its docblock is re-cut so "deliberately no fallback" is stated for walled postures only. ADR-0087 semantic migration entry 18 (schedule-flow-acting-organization-required) is rewritten: its acceptance criteria currently require every time-triggered flow to declare, which is no longer the rule.trigger-schedule, both triggers: a three-state bind gate — off → refuse to bind with the policy reason;single+ on → bind without an organization, run organization-less, sweep unscoped; walled + on → today's declaration refusal, unchanged. The "tenantIdis never conditional" pins are retired with reasons.service-automation: a reason code on the binding audit so policy-disabled ≠ binding-failed; the CLI startup summary prints it; Studio'spackagedFlows.ts(objectui) displays it — objectui card filed, blocked on this one.- Deployment surface: the variable is declared where
OS_MULTI_ORG_ENABLED/OS_ALLOW_DEGRADED_TENANCYare;os doctorprints the effective value; docs:environment-variables,tenancy-modes,automation/flows("The acting organization"),production-readiness..changeset/schedule-trigger-acting-organization.md's banner is rewritten.⚠️ Timing: all of this is unreleased and must land in the same launch window as PR fix(triggers,spec,service-automation)!: a time-triggered flow declares its acting organization and the run executes as it #17334, before the next release cut — otherwise 17.x ships a bind-time contract this ruling immediately narrows. - Examples: the two "tracking card … is being re-filed" comments (
examples/app-showcase/src/automation/flows/index.ts,examples/app-todo/src/flows/task.flow.ts) cite this card and the ruling. How the local dev harness sets the switch forpnpm dev --freshis the implementer's, not a spec matter. - Tests: the spec pin "never offers a fallback", the trigger refusal tests,
packages/qa/dogfood/test/fixtures/schedule-organization-fixture.ts, the lint tests, the migration-registry pins. - Outside this repo (to be relayed by a seat with access; this seat has none): HotCRM's
saascomposition loses its eight remainingscheduleflows by default (demo_bootstrapis already excluded there) — contract expiration and renewal, case SLA monitor, quote expiration, task due reminder, campaign completion, forecast snapshot, opportunity stagnation; HotCRM tests that boot scheduled flows set the switch on. hotcrm#1892 and cloud#2216 wait on this relay. - A schedule-triggered flow's
notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659: an amendment note is posted there in the same stroke.
⛔ Still open — not ruled, the implementer must not guess
- Q3 — whether package-authored
defineJobcron jobs fall under the same switch (same job service, same resource risk; the seat recommended yes). Until answered the switch covers FLOWS only, and the implementation says so where the variable is documented. - Q4 — the lint finding
flow-schedule-organization-missing, which becomes a false warning undersingle+ on (the seat recommended removing the finding and keeping the near-miss diagnostic at bind). Until answered the rule is left in place; the implementing seat stops and reports if the round cannot land without the answer.
Both are being put back to the maintainer by the director seat; they do not block dispatch of the ruled parts.
State
needs-user-decision→pm:queue;domain:servicesandpriority:p2kept. The cloud seat's transcript on HotCRM (5636801940) is verified from this seat at 5642172148 and is an input to this ruling.
Generated by Claude Code
- A deployment-level environment variable gates time-triggered FLOWS —
The two open sub-questions are now ruled (director seat, decision batch #118 addendum, 2026-09-12)
Maintainer, verbatim (live PM chat, 2026-09-12T02:0xZ): 「其他同意」 — to the seat's two recommendations, each derived from the long-term axis:
- Q3 — package-authored
defineJobcron jobs fall under the SAME switch as time-triggered flows. One deployment-level switch governs all package-authored scheduled work; the resource risk is the same and a second switch would be a special case. Platform-internal jobs (approvals escalation, lifecycle Reaper, messaging dispatch loop, membership backfill) are ⛔ not under it — the boundary is "authored by a package", not "runs on the job service". - Q4 — the lint finding
flow-schedule-organization-missingis DELETED. Under ruling G the missing key is not a defect insingleposture, and lint cannot see the deployment's switch; a finding that is false for the default posture is noise. The bind-time near-miss diagnostic (describeMissingScheduleOrganization, theorganizationId/tenantId/ … scan) stays — it fires only where the key is actually required (walled posture, switch on).
The ruling comment above (5642381255) is amended by this note; nothing else in it changes.
Generated by Claude Code
- Q3 — package-authored
下游读数:cloud 决定带着这条契约变更前进,本卡要回答的问题仍然欠着
cloud 的产品负责人 2026-09-12 裁定两个钉版移动照常放行,并明确不考虑存量环境(原话:「同意A,不考虑现有的环境」,记录在 cloud#2216)。
⇒ cloud 侧不再因本卡而阻塞。但本卡的问题没有因此消失,反而变成既成事实:
- 上一条评论里的读数不变:HotCRM 在 cloud 钉版上自带 9 个
type:'schedule'流程,全部没有config.organization,在包内无从修复。新钉版到达后它们停止绑定。 - cloud 侧目前唯一可用的补救是 ADR-0126 §7.1 的手工克隆并声明组织,逐流程、逐组织,而管理员能看到的信号只有启动日志。
⇒ 随包发布的定时流程的长期答案仍在本卡。cloud 侧的排序决定与其后果记录在 cloud#2216。
⚠️ 不认领本卡,不改本卡标签或正文。Generated by Claude Code
- 上一条评论里的读数不变:HotCRM 在 cloud 钉版上自带 9 个
22 remaining items
Landed and closed out — ruling 「18198 确认 + A」 executed end to end
domain:specexecution seat,session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T09:1xZ.Landing, verified git-side
⛔ Not read from the API's
mergedfield. Aftergit fetch origin main:origin/mainisf04be62aa6— 「feat(types,triggers,service-automation,runtime,cli,spec,lint)!: package-authored scheduled work is a deployment decision, off by default (feat(types,triggers,service-automation,runtime,cli,spec,lint)!: package-authored scheduled work is a deployment decision, off by default #18198)」, found by the exact formgit log --oneline origin/main | grep -F '(#18198)', ⛔ not by--grep.- Single-parent squash.
- The subject is on the tree, not merely in the commit message:
SCHEDULED_WORK_ENVreads 4 inpackages/types/src/env.ts; the ruled ledger entry reads 2 inside the@objectstack/trigger-scheduleblock ofscripts/check-type-source-resolution.mjs(the entry plus its annotation) against the pre-existing@objectstack/corecontrol reading 1 ⇒ the probe discriminates.
pm:dispatchedstripped in the same stroke as this comment.⚠️ The sequencing constraint HELD, and it was the one that could have silently failedThe ruling required this PR to land before Version Packages PR #17076 is merged, because the corrected note is one of the changesets inside #17076 and the correction is a same-window narrowing only while that order holds.
Measured at landing: #17076
state=open,merged=false. ⇒ the corrected note had not yet shipped. The narrowing is same-window as ruled.What the ruling's two halves bought
「确认」 —
.changeset/schedule-trigger-acting-organization.md, #17334's pending note, is corrected in place rather than shipped as written and patched after release.Check Changesetstayed red by design throughout, and ⛔ was never restored, renamed, split, deleted, or waved through withskip-changeset.「A」 —
KNOWN_DIST_RESOLVED_TYPE_IMPORTSgains@objectstack/typesin exactly two entries, each annotated in place. ⛔ Not B (rootDir/paths— measured 18 × TS6059 in each of the three affected tsc programs), ⛔ not C, ⛔ not D.⚠️ Recorded because the next reader will look for it and not find itThe ruling stated the by-design red was 「admissible under the protocol's by-design carve-out」. That carve-out does not exist:
按设计红/by-designacross.claude/skills/pm-dispatch/→ 0 files, control落地前检→ 4 files. The one exception landing pre-check ③ does carry — 「例外:merge-base 上同名同失败签名的红不计」 (references/contract-review.md:45) — does not cover it either, since the changeset edit exists only on this branch.⇒ The authority was the maintainer's 「确认」 itself, which outranks ③ directly under the priority order. Mechanically it was also clean:
Check Changesetis not a required context, so ③'s 「⛔ 非 required 子集」 is a seat discipline stricter than the queue, and what was waived is the discipline. Filed separately as a documentation gap.Still open downstream, ⛔ not closed by this landing
The G6 Studio leg was honestly not delivered and no shipping sentence claims it was. objectui#9217 remains
pm:blockedwaiting for the platform to surface the reason, and a successor card carries that.
Generated by Claude Code
- added a commit that references this issue
on Sep 16, 2026 - added 3 commits that reference this issue
on Sep 17, 2026 - added a commit that references this issue
on Sep 17, 2026
一句话问题
我们自己发的示例应用里有四个定时流。新规则要求每个定时流声明它代表哪个组织,而这四个流一个都声明不了 —— 于是要么它们在启动时全部不再运行,要么规则对它们网开一面。
背景
#16659 的维护者裁决(2026-09-08,逐字):
PR #17334 实现了它:一个
schedule/time_relative流若不声明organization,启动时就不再 armed,并打出一条点名该流与该键的拒绝。sys_organization.id,部署自己铸的)⇒ 示例应用的作者写不出它。四个流是:showcase_task_due_reminder、showcase_scheduled_digest,以及另外两个(PR #17334 的文档段落列全)。⭐ 已经被这一轮回答掉的那一半(⛔ 不再是本卡的问题)
原卡曾把「
validate-flow-trigger-readiness能不能学会这条规则」列为待决之一。它学会了,而且严重度是测量出来的,不是选的:warning⇒objectstack build在examples/app-showcase与example-todo上通过;error⇒objectstack build直接失败,点名showcase_task_due_reminder与showcase_scheduled_digest,⛔ 而作者没有任何可写的修法。⇒ 这条探针把本卡的真问题量了出来:规则本身没错,是我们自己的示例满足不了它。
选项 × 真实代价(⛔ 重新写的,不是原卡那三个)
warning业务含义直译
四棱
os-decision-facets
① 项目长远合理性 —— C 不新增任何永久面,最省;A 新增一个必须在多组织下也说得通的记号,⇒ 它是唯一可能扩大契约的;B 造一个"今天成立、明天失效"的条件豁免,⚠️ 那正是 #16659 的缺陷形状(在单组织装机上合法、第二个组织出现当天静默失效)。
② 实际业务拉动 —— 撞上的是照示例学的人:示例是新用户第一眼看到的东西,而它现在演示一个不工作的能力。⛔ 但本席没有实测有多少人真的在跑这四个流,⇒ 拉动强度未知。
③ 防 AI 犯错 —— 出错时谁看到什么:D 最坏——AI 照示例写一个定时流,
os validate过,启动时不 armed,只有一行 boot 日志。⭐ 而这正是 #17123 那张卡的形状(跑了、报告健康、什么都没送达)。A / B / C 都把它变成作者写下时就知道的东西。④ 创业阶段不扩散 —— C remove 优于 declare-and-maintain,最符合;A 是 declare-and-maintain,每个已声明的键都是永久义务;B 是一条要长期维护的条件豁免。
推荐:⛔ 本席不推荐。 四棱在此分歧:①④ 指向 C,③ 指向"任何一个都好过 D",② 数据缺失。⇒ 按纪律分歧即升级,交你拍板。
⛔ 置信缺口 —— 本分析看不见:有多少人真的在用这四个示例流(②轴没有实测);⚠️ 若定义不清,A 就等于把跨组织从后门放回来,而那是裁决明令禁止的。
objectui/cloud里有没有同形状的示例(⛔ 声明为未知,不是"没有");以及 A 的那个"记号"具体能不能在多组织下定义清楚——关联
notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 · PR fix(triggers,spec,service-automation)!: a time-triggered flow declares its acting organization and the run executes as it #17334(其文档段落列全四个流;F4 的严重度探针是本卡的测量来源)