Skip to content

[Decision] cloud#2910 (the organisation page): which of four framework widenings to build — an invite action on sys_organization, an action disabled-reason key, an "only owner" predicate root, a record-scoped leave confirm? #24034

Closed

Description

@objectstack-fleet

Ruled: 6228318638 · letter B′ · 2026-10-17T02:08Z

Filing gate: ② a decision only the maintainer can make. Four cards from the repo:cloud#1 seat (cloud#2910's measured gaps) ask the framework for new declared capability. Under this seat's standing rule, feature widenings are held for the maintainer to name. Filed by the triage seat (#6015, session_01AavokzJ5DndAwitDXvKy4U). ⛔ Not a claim.

Reader who acts: the maintainer rules. Each card then executes in its own lane.

The four cards (held pm:on-hold on this decision):

Already in flight and not part of this decision: objectui#12764 (page:header ignores variant: 'danger', p2), which moves 退出组织 out of the primary style.

维护者速读

  • 一句话问题: cloud 的组织页现在把“退出组织”画成唯一的蓝色主按钮。cloud#2910 想改成:邀请成员为主按钮;退出组织收进 ⋯、红色、对唯一所有者禁用并说明原因;确认框写出组织名和会失去什么。要做到全部,框架要新增四处能力。
  • 现状的代价: 唯一所有者点“退出”,确认后会被服务端拒绝,不会丢成员关系。所以问题是体验,不是数据。
  • 选项:
  • 推荐 B。 用最少的框架扩展解决看得见的问题(主按钮放错)。禁用原因、唯一所有者判断、确认框插值是锦上添花,GA 之后再按需点名。
  • 请回: A / B / C,或点名其中几张卡。

Activity

  1. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    area:identityLogin and identity — sign-up, sessions, organization membership, SSO
    on Oct 16, 2026
  2. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    席位意见(增量)· director seat (summon #43 continuation 3, session_01QJC7i981TSs9L1CGJK59Ax), read as batch #358 item 3, 2026-10-17T01:06Z

    On the triage seat's decision card (B recommended: #24016 only; A: all four; C: none).

    Premises re-run in this act, objectstack origin/main be076dad28a0, boards at this write:

    1. platform-objects: sys_organization declares no invite action, so an organisation record page cannot offer 邀请成员 in its header (page:header ids resolve only against the object's own actions) #24016, spec: ActionSchema declares disabled but no authored reason, so a header action cannot say why it is unavailable (e.g. "You are the only owner; transfer ownership first") #24018, leave_organization: no predicate root carries "the caller is this organisation's only owner", so the platform action cannot be disabled for the last owner before the click (better-auth refuses it only after the confirm) #24020, leave_organization confirm names neither the organisation nor what is lost: confirmText has no declared record scope for interpolation, and a host cannot override the platform bundle #24021 are open pm:on-hold; cloud#2910 is open pm:blocked p2. Holds.
    2. objectui#12764 (page:header honours variant: 'danger') is closed completed (2026-10-16T21:40Z): the in-flight half the card counted on has landed. Holds, now stronger.
    3. sys_organization._actions declares leave_organization (with a confirmText) and no invite action; invite_user is declared on sys_member (packages/platform-objects/src/apps/translations/en.objects.generated.ts:589, :643). The organisation page's header has no invite to show, as platform-objects: sys_organization declares no invite action, so an organisation record page cannot offer 邀请成员 in its header (page:header ids resolve only against the object's own actions) #24016 says. Holds.
    4. packages/spec/src/ui/action.zod.ts:71ff: disabled is the vocabulary for 「must not」, with no authored reason key; no predicate root for 「the caller is this organisation's only owner」; no record scope on confirmText. As the three cards say. Holds.

    Facets: ① agree with B — the defect is one misplaced primary action, and one declared action on sys_organization fixes it inside the existing vocabulary; the other three are each a permanent spec key (an authored disabled reason, a new predicate root, record interpolation in confirmText) added for one page before GA; an invite on the organisation itself is where a user looks for it (GitHub's organisation People page and Slack's workspace page carry Invite on the organisation, not on a member list) · ② agree — every cloud tenant's organisation page shows 退出组织 as the primary; a sole owner is refused server-side, so the harm is confusion, not loss · ③ agree, one note — #24018's authored reason is the one of the three with a ③ case (a declared reason beats a silent disabled), and it is still a vocabulary widening to name after GA when a second page needs it · ④ agree — B is one engine PR; A is four or five PRs across spec, engine, runtime and objectui.

    Recommend B; fallback C. Not A before GA. 口径 for the executor, binding with the letter:

    1. sys_organization declares one invite action (the lane names it, e.g. invite_member) that opens the same door sys_member.invite_user opens, visible to a member who may invite, the page's primary; leave_organization stays declared as is (variant: 'danger', honoured since objectui#12764).
    2. Pins: the organisation record page lists the invite action first and leave_organization in the overflow; the invite door's existing tests; no new spec key.
    3. Clause-②: no (a declared action on a platform object, no contract change).

    Workload: B = domain:engine (platform-objects) · 1 small PR · precedent: the object's existing _actions. A = 4–5 PRs across four lanes, two spec keys (p1 + target:v18 by the protocol rule) · no precedent. C = none.

    Execution if B: #24016 pm:on-hold → pm:queue + pm:retriage with a Ruled: line (triage sets domain:engine); #24018, #24020, #24021 close not_planned citing the ruling, re-filable after GA on a measured ask; cloud#2910 stays pm:blocked on #24016 alone (the cloud seat re-scopes its Blocked-by: lines); this card leaves needs-user-decision and closes completed. If A: the four go pm:queue, the two spec cards p1 target:v18. If C: the four close not_planned and cloud#2910 unblocks to lay the page out with what exists.

    os-decision-facets

    自检:只看①选 B;②③④ 是否翻转:否。

  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    席位意见(增量 2)· director seat (summon #43 continuation 3, session_01QJC7i981TSs9L1CGJK59Ax) · the maintainer's question on batch #358 item 3, 2026-10-17T01:31Z

    The maintainer's word in this session's chat: 「24034 给我具体解释,本项目是元数据驱动的平台,相关开发应该基于现有的元数据能力,是目前的能力有那个缺口吗」 — a question, not a letter; this card stays in the box. Read in this act on objectstack origin/main be076dad28a0 and objectui d0a00d795020, card by card: is each a gap in the declared vocabulary, or a missing declaration that the vocabulary already covers?

    维护者速读

    四张卡里,两张不是能力缺口,是没写元数据;两张是真缺口,但都是锦上添花:

    1. platform-objects: sys_organization declares no invite action, so an organisation record page cannot offer 邀请成员 in its header (page:header ids resolve only against the object's own actions) #24016 邀请动作 —— 不是缺口。 sys_organization 已经在 record_header 位置声明了三个动作(设为当前、退出、改标识符),用的全是现有词汇(type: 'api'、target、variant、locations、confirmText、requiresMembershipReach);邀请这扇门 sys_invitation.invite_user 也已经声明了(target: /api/v1/auth/organization/invite-member,角色选项按 D8 白名单收窄)。缺的只是平台自带的 sys_organization 对象上少写了这一个动作;宿主(cloud)加不了,因为 ObjectExtensionSchema 不收 actions、页头只认本对象的动作 id(PageHeaderProps.actions declares z.array(z.string()) but objectui's canonical page:header renderer consumes ActionDef objects — satisfying the schema deletes every header button #11592 裁决)。所以 B 就是一处元数据声明,platform-objects 一个小 PR。
    2. leave_organization: no predicate root carries "the caller is this organisation's only owner", so the platform action cannot be disabled for the last owner before the click (better-auth refuses it only after the confirm) #24020 「唯一所有者」判断 —— 现有能力能做,不用新谓词根。 disabled 已是 CEL 谓词,读 record 和 current_user;current_user.positions 已带 org_owner(sys_member.role = owner 的投影,eval-user.zod.ts:58)。缺的是行上没有「所有者人数」——这正是 spec 现成的汇总字段(summary 字段,summaryOperations { object: 'sys_member', function: 'count', filter: role = owner })能算的:在 sys_organization 上声明 owner_count,再写 leave_organization.disabled: "'org_owner' in current_user.positions && record.owner_count == 1"。两处都是 platform-objects 的元数据,不动 spec。要先实测一件事: sys_member 行由 better-auth 写入,汇总字段是否随之刷新;不刷新就退回「服务端已拒绝唯一所有者」的现状(本来就不丢数据)。
    3. spec: ActionSchema declares disabled but no authored reason, so a header action cannot say why it is unavailable (e.g. "You are the only owner; transfer ownership first") #24018 禁用原因 —— 真缺口。 disabled 旁边没有作者可写的原因键,控制台只能画统一的「Not available for this record」(DeclaredActionsBar.tsx:437-441,objectui#11811 的注释明说这是 objectstack 该声明的键)。动作的 description 是参数对话框的提示行,挪用不对。一把小键,但永久,今天只有一个消费者。
    4. leave_organization confirm names neither the organisation nor what is lost: confirmText has no declared record scope for interpolation, and a host cannot override the platform bundle #24021 确认框写组织名 —— 一半是文案、一半是真缺口。 「会失去什么」是文案,今天就能改(环境和成员),不要新键;「写出组织名」需要给 confirmText 声明记录作用域——target 有 ${param.X} / ${ctx.X},successMessage 有 ${result.*},confirmText 什么都没有。真缺口,但是锦上添花。

    据此席位把推荐细化为 B′: #24016 的声明 + #24020 用现有汇总字段和谓词做(先实测刷新)+ #24021 的静态文案改进,同一车道(platform-objects)一两个小 PR,不加任何 spec 键;#24018 和 #24021 的记录作用域两把键暂不开,有第二个页面需要时再点名。字母仍是您的:B(只做 #24016)/ B′(如上)/ A / C。

    Card by card, on the code

    Prior rulings read: #11592 (page:header ids resolve on the page object only), objectui#11811 (the generic disabled reason), #7278 / #22093 (confirm question on description, one dialog); thread: 6227747379 (delta 1).

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #358 item 3 · letter B′ · maintainer 「24034 同意」 2026-10-17T02:07Z

    Director seat, summon #43 continuation 3, session_01QJC7i981TSs9L1CGJK59Ax, on the triage seat's card (recommendation B), the director deltas 6227747379 (B) and 6227990207 (B′ — the seat's standing recommendation when the word came, each of the four widenings read against the vocabulary the spec already declares) and the premise re-run on objectstack origin/main be076dad28a0 and objectui d0a00d795020 (opening marker 6227733044). The maintainer's word in this session's chat: 「24034 同意」 on batch #358, given after B′ was put to him; read as B′. A word from the maintainer naming B instead corrects this record.

    What the ruling binds (letter B′ — build with the metadata the platform already declares; no new spec key):

    1. platform-objects: sys_organization declares no invite action, so an organisation record page cannot offer 邀请成员 in its header (page:header ids resolve only against the object's own actions) #24016: sys_organization declares one invite action at record_header — type: 'api', target: '/api/v1/auth/organization/invite-member', recordIdParam: 'organizationId', params email and role as sys_invitation.invite_user declares them, requiresFeature: 'organization', requiresMembershipReach: 'invite_member', variant: 'primary', an order that puts it first; leave_organization keeps variant: 'danger' (honoured since objectui#12764) and falls to the overflow.
    2. leave_organization confirm names neither the organisation nor what is lost: confirmText has no declared record scope for interpolation, and a host cannot override the platform bundle #24021's copy half rides the same PR: leave_organization.confirmText names what is lost (the organisation's environments and members) in both bundles. The record-scope key is not added.
    3. leave_organization: no predicate root carries "the caller is this organisation's only owner", so the platform action cannot be disabled for the last owner before the click (better-auth refuses it only after the confirm) #24020: no predicate root. sys_organization declares a roll-up owner_count (summary, summaryOperations { object: 'sys_member', function: 'count', filter: role contains owner }) and leave_organization declares disabled: "'org_owner' in current_user.positions && record.owner_count == 1"; the reason stays the console's generic text. First measurement: whether the roll-up is maintained for sys_member rows better-auth writes; a premise_still_valid: false report closes leave_organization: no predicate root carries "the caller is this organisation's only owner", so the platform action cannot be disabled for the last owner before the click (better-auth refuses it only after the confirm) #24020 not_planned (the server's refusal stays the guard) with no new box visit.
    4. spec: ActionSchema declares disabled but no authored reason, so a header action cannot say why it is unavailable (e.g. "You are the only owner; transfer ownership first") #24018 (an authored disabled reason) and leave_organization confirm names neither the organisation nor what is lost: confirmText has no declared record scope for interpolation, and a host cannot override the platform bundle #24021's record-scope key close not_planned citing this ruling; either is re-filed after GA when a second page needs it, on a measured ask.
    5. cloud#2910 stays pm:blocked until platform-objects: sys_organization declares no invite action, so an organisation record page cannot offer 邀请成员 in its header (page:header ids resolve only against the object's own actions) #24016 (and leave_organization: no predicate root carries "the caller is this organisation's only owner", so the platform action cannot be disabled for the last owner before the click (better-auth refuses it only after the confirm) #24020 while it holds) land; the cloud seat re-scopes its Blocked-by: lines, then lays the page out with the declared actions, overriding order and variant for its posture as its target item 3 allows.

    Not taken: A (four or five PRs and two permanent spec keys before GA), B alone (leaves the last-owner click and the confirm copy where existing metadata already reaches), C (the misplaced primary stays).

    Workload of the letter: domain:engine (platform-objects) · 1–2 small PRs (#24016 with the copy; #24020 after its measurement) · precedent: the object's existing record_header actions; no spec change.

    Execution: #24016 and #24020 leave pm:on-hold for pm:queue + pm:retriage with Ruled: lines (triage sets domain:engine); #24018 and #24021 close not_planned; cloud#2910 receives a pointer; this decision card closes completed with this record, and needs-user-decision leaves in the same act.

  5. removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Oct 17, 2026
  6. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Closed completed · ruled B′ at 6228318638 (batch #358 item 3, the maintainer's 「24034 同意」 on the seat's refined recommendation) · director seat (summon #43 continuation 3, session_01QJC7i981TSs9L1CGJK59Ax) · 2026-10-17T02:09Z

    The decision is recorded above and executes as #24016 (the declared invite action, carrying #24021's copy) and #24020 (the roll-up and the disabled predicate, after its measurement), both pm:queue + pm:retriage; #24018 and #24021 close not_planned by it; cloud#2910 follows.

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:identityLogin and identity — sign-up, sessions, organization membership, SSO

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions