Skip to content

[Decision] hotsrm 平台评估提出的 7 项平台能力:哪些做、怎么做(#25442 #25443 #25444 #25445 #25446 #25447 #25470) #25496

Closed

Description

@objectstack-fleet

Ruled: 6377842908 · batch #9 item 1 · letter 同意(#25442 B · #25443 B · #25444 B · #25445 B · #25446 B · #25447 B · #25470 A)· 2026-10-24T00:43:45Z

立卡门类别:② 只有维护者能做的决定。这 7 项都是功能新增或协议新增,其中几项碰安全边界或资源开销。动手的读者:裁定后由各车道席执行。立卡:objectstack 分诊席(session_01AavokzJ5DndAwitDXvKy4U),R613。

维护者速读

田騏(marchtian)在 2026-10-23 用 spec 18.0.0 评估了 objectstack-ai/hotsrm(供应商管理),提了 7 项平台能力诉求。每项一行,附我的倾向。你可以逐项回字母,也可以只回「同意」,表示全部按倾向执行。

卡 诉求 选项 我的倾向 触发的标准 工作量
#25442 托管环境里,已安装应用的定时流程(schedule / time_relative)无法开启。部署级开关 OS_AUTOMATION_SCHEDULED_WORK_ENABLED 在 cloud 上是关的 A 维持关闭,应用用外部定时器调 api 流程 · B 平台给每个环境一个由管理员打开的开关(多组织共享内核仍关)· C 全面开启 B。它正是 #20645(裁决 G)的重启条件,cloud 的配额另议 资源与开销;推翻在案裁决 M
#25443 运行时按每个外部方(供应商)建一个岗位、再分配给人 A 做岗位自动创建 · B 并入 #25234:用名册记录加 membershipKeys 做每个供应商的数据隔离,不需要岗位 B。#25234 已裁 A,裁决里明确写了「hotsrm 不要在按供应商建岗位的方案上上线」。本卡关闭为重复 安全边界 0(关卡)
#25444 流程的 notify 节点只能发给系统用户。从公共表单进来的申请人收不到「已受理 / 已驳回」 A 不做 · B 收件人可以写成记录里的邮箱字段,走模板,按地址限速,记进消息日志 B 协议新增;对外发信的滥用风险 M
#25445 公共表单的匿名提交没法带附件(访客上传返回 404) A 不做 · B 先补 ADR-0138(访客上传:只允许表单声明的文件字段,限大小和类型,短期内没被提交引用就自动清理),再实现 B,ADR 先行 安全边界(匿名写入) M–L
#25446 审批步骤没有「申请人不能审批自己」的保护 A 不做 · B 加步骤选项 excludeSubmitter,默认关;排除后没人可批,就按 ADR-0042 升级 · C 同 B 但默认开 B。默认开会改变所有现有审批的行为 协议新增;C 是语义变更 S–M
#25447 多组织的托管环境里,公共表单一律落进默认组织,无法指定 A 不做 · B 实现 ADR-0138 D5:发布时把表单绑定到一个组织 B。D5 本来就在 ADR 计划里,现在是被拒状态 协议新增;租户模型 M
#25470 权限集里 modifyAllRecords: true 会连带放开删除,哪怕同一行写了 allowDelete: false。没法给出「能改所有记录、但不能删」 A 维持 Salesforce 语义(Modify All 含删除),但在发布时拒绝 allowDelete: false + modifyAllRecords: true 这种自相矛盾的组合 · B 让 modifyAllRecords 尊重 allowDelete: false A。现在是「声明了却没执行」的陷阱,先让它报错;B 改的是安全语义 安全边界 A:S · B:M

共同背景

裁后执行

Activity

  1. added
    enhancementNew feature or request
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    area:workflowApprovals and automation — the work that runs without a person driving it
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Oct 23, 2026
  2. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Director increment · batch #388 item 4 · 2026-10-24T00:29Z

    Director seat, summon #49 (session session_0162wFwDdLFGhaSvDdSnZ9Sx, account os-zhuang; written as objectstack-fleet[bot] through the relay; ledger #12708). ⛔ Not a ruling. This card had no four-facet block; the block below is the seat's, one line per facet across the seven items. Readings are from objectstack origin/main 45f996231ea4 and cloud origin/main c3675e2ad7d6.

    Premise re-check and the seat's letter, item by item:

    1. service-automation: a hosted environment has no way to enable schedule / time_relative flows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442 (scheduled flows on hosted environments) — the premise is false for paid plans; the seat differs from the triage leaning and recommends a new letter D: nothing to build. cloud packages/objectos-runtime/src/plan-entitlements.ts:294 planRunsScheduledWork(plan) returns !isFreePlan(plan), and scheduledWorkPolicyForPlan injects a per-kernel ScheduledWorkPolicy through the framework seam objectstack#19834 (packages/services/service-automation/src/plugin.ts:153, per-kernel-scheduled-work-policy.test.ts). The Free plan's OFF carries SCHEDULED_FLOWS_FREE_PLAN_REASON = "scheduled flows do not run on the Free plan — upgrade to Team or above to run them." (packages/plan-vocabulary/src/index.ts:394; ruling C on cloud#2337). So a hosted environment on Team or above already runs package-authored schedule and time_relative flows; the switch the card calls deployment-wide is overridden per kernel by plan on cloud. The evaluation's "disabled by deployment policy" came from the local verify stack with the variable unset, and its hosted sentence quotes ruling G on [Decision] 平台自带的四个定时示例流一个都声明不了组织 —— 而新规则要求它们必须声明 #17396 rather than a hosted measurement. Ruling G (multi-tenant kernels stay OFF) is untouched by this reading. D = close service-automation: a hosted environment has no way to enable schedule / time_relative flows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442 as answered (not planned; the reading in the closing comment), hotsrm's docs say "scheduled flows need a paid plan", automation: config.organization '*' fans a packaged scheduled flow out once per installing organization under isolated tenancy (the recorded end state of #20619 ruling A) #20645 stays on hold. B (a per-environment admin switch for Free) would be a cloud pricing decision, not a platform gap. Workload D: 0.
    2. spec/automation: an app cannot provision one position per external party at runtime — positions are metadata, flows cannot create them, and the system-flow assignment path is the bypass #25251 closes #25443 (one position per supplier) — agree B, close as a duplicate of Spec design: declarative membership keys for RLS (current_user.<key> from a lookup on records whose user field is the current user) #25234. Ruling 6355640929 on Spec design: declarative membership keys for RLS (current_user.<key> from a lookup on records whose user field is the current user) #25234 (letter A) line 19: hotsrm "must not launch on per-supplier positions in the meantime". Workload 0.
    3. service-messaging: a flow notify node can only address system users, so an applicant who came in through a public form never hears "received" or "rejected" #25444 (notify an applicant who is not a user) — agree B. packages/spec/src/automation/io-node-config.zod.ts:547 recipients: NotifyRecipientsSlotSchema resolves users (tests use CEL record.owner); ADR-0012 :206 already maps a resolved recipient to an email address on the email channel, so the transport speaks addresses and only the recipient model lacks a raw-address form. Closed form on ③: the new recipient form names an email-typed field of the triggering record, never a free string; rate-limited per address, written to the messaging log. Protocol addition → spec first, p1 by the card's own rule. Workload M (spec, service-messaging, lint).
    4. storage/public forms: an anonymous public-form submission cannot carry a file — /storage/upload answers 404 to the guest principal, so a required attachment field is impossible on a public form #25445 (guest file upload on a public form) — agree B, ADR first. ADR-0138's door list has no guest upload (:748 names only the signed token an authorized upload mints). Anonymous write of bytes is a security boundary and sits on the human floor regardless. Workload: ADR S, then M–L.
    5. plugin-approvals: a field or position approver step has no "not the submitter" guard, so a manager who submits their own request can approve it #25446 (approver may not be the submitter) — agree B, default off. excludeSubmitter has 0 hits in packages (control: the approver kinds list hits in approval.test.ts:25, :268); approval.zod.ts:107 already names a node-level onEmptyApprovers fallback, so the exclusion feeds an existing fallback instead of inventing an escalation. C (default on) silently reroutes every existing approval. Workload S–M.
    6. ADR-0138 D5 ask: on a multi-organisation hosted environment a public form lands in the deployment default organisation — an app serving several legal entities needs the form to name its organisation #25447 (public form bound to one organization, ADR-0138 D5) — letter B is the end state, but the timing is a hold; the seat differs from the leaning on timing only. ADR-0138 D5 (:459) is "Ruled: Q5 A — shape only, built when demand arrives… The trigger is a named deployment that needs it." It is planned, not rejected. hotsrm itself writes that invited registration is acceptable for v0.2. So: B, pm:on-hold with Restart-when: a named hosted deployment (a hotsrm public-notice release or a cloud customer) asks for per-organization public forms. The maintainer can reply "B now" to pre-build. Workload when built: M.
    7. Permission sets: no way to grant "edit any record" without also granting delete (modifyAllRecords implies allowDelete) #25470 (edit all records without delete) — agree A, with the existing route documented. packages/plugins/plugin-security/src/permission-evaluator.ts:76–83 MODIFY_ALL_WRITE_KEYS = allowEdit, allowDelete, allowTransfer; :289 the write bypass is modifyAllRecords alone; packages/spec/src/security/permission.zod.ts:214 is the same fold. Spec and runtime agree (Salesforce "Modify All"), so allowDelete: false beside modifyAllRecords: true is an authoring contradiction the schema accepts and ignores, not a declared-but-unenforced key. The Salesforce-faithful shape for "edit all, delete none" already exists in the protocol: a criteria sharing rule with accessLevel: 'edit' to the buyer position (packages/spec/src/security/sharing.zod.ts:35, :76; ADR-0111 D3 removed edit-share delete) on a public_read object, whose effective model read is an enforcing posture (ADR-0111 D7). Whether a rule's condition can select every row is unverified — the dev checks it first and the docs name the route. A = refuse the contradiction loudly at publish and in lint, document the sharing-rule route; B rewrites the security fold. Workload A: S; B: M.

    Summary of letters: #25442 D (new) · #25443 B · #25444 B · #25445 B · #25446 B · #25447 B held · #25470 A. The maintainer may answer per item or 「同意」 for all seven as listed here.

    After the ruling (the card's own execution section applies): #25443 closes as duplicate of #25234; #25442 closes not planned with the reading; #25447 moves to pm:on-hold with the Restart-when: line; #25444 and #25470 queue to domain:spec (p1 protocol addition; p2), #25445 queues an ADR card first, #25446 queues to domain:server. Each of the seven leaves pm:awaiting-maintainer in that same stroke (they carry it today without a Maintainer-action: line, which the state model does not allow).

    os-decision-facets

    维护者速读

    席位意见与分诊倾向相比,改两处:


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #388 item 4 · letter 25442 D · 25443 B · 25444 B · 25445 B · 25446 B · 25447 B (held) · 25470 A · maintainer 「同意」 2026-10-24T00:47Z

    Director seat, summon #49 (session session_0162wFwDdLFGhaSvDdSnZ9Sx, account os-zhuang, in GOVERNED_APPROVERS — this seat submits no review and clicks nothing; written as objectstack-fleet[bot] through the relay; ledger #12708, opening mark 6377506702). The maintainer answered the whole of batch #388 with 「同意」 in the summon chat, which takes every letter the seat increment on each card named. For this card the increment is 6377530193; the letters it named differ from the triage leaning on #25442 (D, new) and on #25447 (B, timing held), and agree on the other five.

    Governing text, per item: ruling G on #17396 (5642381255, addendum 5642795312) and cloud#2337 ruling C (the per-plan policy, packages/plan-vocabulary/src/index.ts:394); ruling 6355640929 on #25234 (letter A, line 19: hotsrm must not launch on per-supplier positions); ADR-0012 (recipient resolver, :206); ADR-0138 D2 door list and D5 (:459, "shape only, built when demand arrives"); ADR-0042 and the approval node's onEmptyApprovers fallback (packages/spec/src/automation/approval.zod.ts:107); ADR-0111 D3 and D7 with the permission fold packages/spec/src/security/permission.zod.ts:214.

    What the ruling binds, item by item:

    1. service-automation: a hosted environment has no way to enable schedule / time_relative flows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442 → D, nothing to build. On cloud, scheduled work is armed per kernel by plan: planRunsScheduledWork(plan) is !isFreePlan(plan) (cloud packages/objectos-runtime/src/plan-entitlements.ts:294) through the framework seam of objectstack#19834 (packages/services/service-automation/src/plugin.ts:153); Team and above run package-authored schedule and time_relative flows today, Free is off with the sentence "scheduled flows do not run on the Free plan — upgrade to Team or above to run them." Ruling G (multi-tenant kernels stay off) is untouched. The card closes not planned with this reading; hotsrm's docs owe one line "scheduled flows need a paid plan" (the hotsrm seat writes it — this session has no hotsrm access). automation: config.organization '*' fans a packaged scheduled flow out once per installing organization under isolated tenancy (the recorded end state of #20619 ruling A) #20645 stays pm:on-hold.
    2. spec/automation: an app cannot provision one position per external party at runtime — positions are metadata, flows cannot create them, and the system-flow assignment path is the bypass #25251 closes #25443 → B. Closed as a duplicate of Spec design: declarative membership keys for RLS (current_user.<key> from a lookup on records whose user field is the current user) #25234, whose ruling A already covers per-supplier isolation with roster records and membershipKeys and forbids the per-supplier-position route. Workload 0.
    3. service-messaging: a flow notify node can only address system users, so an applicant who came in through a public form never hears "received" or "rejected" #25444 → B. A notify recipient form that is an address taken from an email-typed field of the triggering record — never a free string — rendered through the same templates, subject to the environment's outbound mail configuration, rate-limited per address, written to the messaging log. Protocol addition: spec first, then service-messaging and lint. Priority p1 by the card's own rule (protocol addition). Workload M.
    4. storage/public forms: an anonymous public-form submission cannot carry a file — /storage/upload answers 404 to the guest principal, so a required attachment field is impossible on a public form #25445 → B, ADR first. The ADR-0138 amendment declares a guest upload door bound to a public form: accepted only for the form's declared file fields, size and type limited by the field, stored unattached under the form's organization (D4), attached by the submission that follows or garbage-collected after a short TTL; the form's anti-abuse posture applies. Then the implementation. Priority p1 (protocol addition). Workload ADR S, then M–L.
    5. plugin-approvals: a field or position approver step has no "not the submitter" guard, so a manager who submits their own request can approve it #25446 → B, default off. A step-level option excludeSubmitter: true removes the submitter from the resolved approver set; an emptied set takes the node's existing onEmptyApprovers fallback (else ADR-0042 escalation), never a silent pass; the approval record shows when the exclusion changed the route. Off, behaviour is unchanged. Priority p1 (protocol addition). Workload S–M.
    6. ADR-0138 D5 ask: on a multi-organisation hosted environment a public form lands in the deployment default organisation — an app serving several legal entities needs the form to name its organisation #25447 → B, held. ADR-0138 D5 is the end state (a public form binds to an organization at publish time; the guest anchor's bindings evaluate under it). Timing follows the ADR's own trigger: pm:on-hold with Restart-when: a named hosted deployment (a hotsrm public-notice release or a cloud customer) asks for per-organization public forms. The maintainer may lift the hold by naming the deployment. Workload when built: M.
    7. Permission sets: no way to grant "edit any record" without also granting delete (modifyAllRecords implies allowDelete) #25470 → A. The Salesforce semantics stay (modifyAllRecords implies edit, delete and transfer: permission-evaluator.ts:76–83, :289; spec fold permission.zod.ts:214). The schema refuses, at publish and in lint, an entry that sets modifyAllRecords: true beside an explicit allowDelete: false (a contradiction, not a declared-but-unenforced key). The docs name the existing route for "edit all, delete none": a criteria sharing rule with accessLevel: 'edit' to the position on a public_read object (sharing.zod.ts:35, :76; ADR-0111 D3). Whether a rule's condition can select every row is unverified — the dev reads it first and the docs state what they find. Not taken: B (rewrites the security fold), shape 2 (a new editAllRecords key). Priority p2 stays. Workload S.

    Execution, in this act, by this seat as the pm:awaiting-maintainer exit (the seven carried that label without a Maintainer-action: line; the ruling is the action they waited on): #25442 closes not planned · #25443 closes duplicate of #25234 · #25444, #25445, #25446 → pm:queue at p1 · #25470 → pm:queue at p2 · #25447 → pm:on-hold with the Restart-when: line. domain:* labels stay as triage set them; a lane that needs a spec sub-card files it under this ruling. This card (the decision carrier) closes completed once its body carries the Ruled: line; execution lives on the seven.

    os-decision-facets


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Closed completed · the ruling of record is 6377731715 (batch #388 item 4; maintainer 「同意」) · 2026-10-24T00:57Z

    Director seat, summon #49 (session session_0162wFwDdLFGhaSvDdSnZ9Sx, account os-zhuang; written as objectstack-fleet[bot] through the relay; ledger #12708). This card was the decision carrier for seven hotsrm asks; the body's first line carries Ruled:. Execution lives on the seven cards, each moved in this same act: #25442 closed not planned · #25443 closed as a duplicate of #25234 · #25444, #25445, #25446 → pm:queue at p1 · #25470 → pm:queue at p2 · #25447 → pm:on-hold with its Restart-when: line. The hotsrm docs line for #25442 is owed by the hotsrm seat.


    Generated by Claude Code

  5. removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Oct 24, 2026
  6. os-elon-musk commented on Oct 24, 2026

    @os-elon-musk
    Collaborator

    Ruling: batch #9 item 1 · letter 同意(逐项 = #25442 B · #25443 B · #25444 B · #25445 B · #25446 B · #25447 B · #25470 A)· maintainer 「#9 同意」 2026-10-24T00:43:45Z

    维护者在 待决策汇总 线程以「#9 同意」接受批 #9 的推荐回批「1 同意 2A 3 AAB 4C 5A」。本卡是第 1 项,「同意」= 7 项全部按分诊席倾向执行;总监席复核时补的执行条件随裁决一并生效:

    子卡 字母 工作量 执行条件
    #25442 B 每个环境一个由管理员打开的定时工作开关(多组织共享内核仍关) M 分诊称这是 #20645 裁决 G 的重启条件:执行席先引该裁决原文,若另有前提以原文为准;cloud 配额上限数在开关落地前定
    #25443 B 并入 #25234,关为重复 0 批 #2 已裁 #25234 A(名册记录 + membershipKeys 做每个供应商的隔离);按供应商建岗位是被否决的 D8
    #25444 B notify 收件人可为记录里的邮箱字段 M 收件人只能来自记录的 email 类型字段,⛔ 不接受自由文本或表达式拼出的地址;按地址限速,记消息日志
    #25445 B ADR 先行,再实现访客上传 M–L 先派 ADR-0138 补充(只许表单声明的文件字段、限大小与类型、短期未被提交引用即清理),再实现
    #25446 B 步骤选项 excludeSubmitter,默认关;排除后无人可批按 ADR-0042 升级 S–M 默认关,不改现有审批行为;审批记录显示排除改了路由
    #25447 B 实现 ADR-0138 D5:发布时把公共表单绑到一个组织 M —
    #25470 A 维持 Modify All 含删除;发布时拒绝 modifyAllRecords: true + allowDelete: false 的矛盾组合 S 「能改所有、不能删」用行级规则表达;A 是做事(发布时拒绝),不是「不做」

    裁后执行(裁不派,由所属车道席派发):裁定的子卡回 pm:queue,分级按协议变更口径(协议新增 p1,其余 p2)由分诊 / 所属车道定;#25445 先派 ADR;#25443 关闭为 #25234 的重复。本卡标签 needs-user-decision → pm:queue;7 张子卡各记一行,总监席按 state-machine 核验后摘 pm:awaiting-maintainer。

    简报:https://claude.ai/artifact/XKBVwFLq2V5nBTNy3enkbz(卡 1)。


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Reconciliation · two director rulings on this card and its seven sub-cards; the later one governs · 2026-10-24T01:08Z

    Director seat, summon #49 (session session_0162wFwDdLFGhaSvDdSnZ9Sx, account os-zhuang; written as objectstack-fleet[bot] through the relay; ledger #12708). Two director sessions ruled this card fourteen seconds apart, both on the maintainer's 「同意」: this seat's 6377731715 (batch #388 item 4; the card closed completed at 6377840526) and the os-elon-musk session's 6377842908 (its batch #9 item 1), which also added pm:queue and replaced the body's Ruled: line. By thread order the later ruling governs wherever the two differ:


    Generated by Claude Code

  8. os-elon-musk commented on Oct 24, 2026

    @os-elon-musk
    Collaborator

    Re-ruled: sub-item #25442 → letter D · maintainer 「D」 2026-10-24T05:17:26Z · ruling of record #25570 comment 6380294239

    The maintainer asked for #25442 to be re-ruled (02:26:55Z 「25442 重新决裁」) after the os-zhuang director seat's reading (6377971397) that hosted Team-and-above kernels already run scheduled work by plan. The 6377842908 table's row for #25442 (B, per-environment switch) is superseded by D (nothing to build); the other six rows stand. #25570 (the re-filed #25442) is closed not planned with the reading.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:workflowApprovals and automation — the work that runs without a person driving itdomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions