Skip to content

[Decision] DESIGN.md §04 makes clm_requester every employee's default, but a set that grants clm_requester.access cannot bind to everyone #11

Description

@hotlong

维护者速读

事情:DESIGN.md §04 说 clm_requester 是「所有员工默认」的权限集,同时又说导航分区的门控能力 clm_requester.access 由这个权限集的 systemPermissions 授予。平台的规则是:带任何 systemPermissions 的权限集属于高权限集,不能挂到 everyone 锚点(ADR-0090 D5/D9)。运行时在启动时拒绝绑定并打警告,os lint 直接报错(security-anchor-high-privilege)。所以「默认给所有员工」在这个平台版本上没有自动机制,卡 04 只能把它做成「分发」:七个岗位各自额外绑定 clm_requester(法务、财务、档案、管理员、两级领导都同时是发起人),没有岗位的普通员工由管理员在 Setup 里逐人授予。

为什么现在问你:改法有两个方向,一个改 §04 措辞,一个向平台提需求;两者都不是开发 agent 能定的。今天不定不阻塞 M1(验收测试用管理员逐人授予跑通了),但卡 07 要按 clm_requester.access 门控「我的合同」分区,到那时普通员工看不看得到这个分区,取决于这个决定。

选项:

  • A(推荐):接受分发模型,§04 加一句「clm_requester 由管理员授予未持岗位的员工;持岗位者随岗位绑定」。零改代码;同时向 objectstack 报一个平台缺口:应用自己声明的 capability(scope: 'org')不该被算作高权限位。平台修好后,把 clm_requester 标成 isDefault 就能自动给所有人,这边只需删一行绑定。
  • B:把 clm_requester.access 从 systemPermissions 拿掉,clm_requester 标成 isDefault: true 自动绑到 everyone;「我的合同」分区改为只要登录就可见(§04 与 §05 都要改一句)。
  • C:新增第八个岗位 clm_employee,绑定 clm_requester,管理员给每个员工发岗位。§04「岗位七个」要改。

代价:A 多一步管理员操作(每个新员工),文档改一句,等平台修好可以收回;B 少一个门控能力,导航分区退化成「登录即见」,但和 §04 的 access 令牌体系不一致;C 多一个岗位,仍然要逐人发,只是从「发权限集」变成「发岗位」。

你要做的:回一个字母,A、B 或 C。


Context

DESIGN.md §04, verbatim:

Permission set 五个:clm_requester(所有员工默认)· clm_legal · clm_finance · clm_records · clm_admin。
导航分区的门控能力由权限集 systemPermissions 授予:clm_requester.access · …

The platform's only mechanism for "the set every authenticated member holds" is isDefault: true on the set, which auto-binds it to the built-in everyone position at boot (ADR-0090 D5). That binding is gated by describeHighPrivilegeBits (@objectstack/spec/security), whose first rule is: any non-empty systemPermissions makes the set high-privilege. Measured on 17.3.0:

  • plugin-security bindBaselineToEveryone: refusing to bind fallback set to everyone — high-privilege bits { offending: "system permissions" } — a boot warning, and no binding.
  • @objectstack/lint security-anchor-high-privilege (severity error): isDefault:true suggests binding this set to the 'everyone' audience anchor, but it carries system permissions — the runtime will refuse the binding (ADR-0090 D5/D9).

So §04's two sentences cannot both be satisfied automatically on this platform version. Card 04 (#4) implemented the distributed reading without editing §04:

  • src/security/bind-position-sets.ts binds every one of the seven positions to clm_requester as well as to its own set (12 rows; measured on a running app);
  • the two requester test accounts in the acceptance run received the set through sys_user_permission_set rows written by the seeded admin — the Setup path.

Governing text: DESIGN.md §04 (the two sentences above); AGENTS.md → Delivery process → a code PR never touches DESIGN.md §01–§04 without a needs-user-decision first; → Platform gaps → report, never patch.

Options

A — keep the tokens, state the distribution, report the platform gap. §04 gains one sentence; objectstack gets an issue proposing that an app-declared capability with scope: 'org' (ADR-0066 D1) not count as a high-privilege bit for the everyone anchor — manage_users and clm_requester.access are not the same kind of thing. When that lands, isDefault: true on clm_requester restores the automatic default and the seven extra bindings can go.

B — drop clm_requester.access, make the set isDefault. The "我的合同" navigation group (§05) is then gated by authentication alone. Consistent with the platform today; inconsistent with the five-token *.access scheme §04 and §05 build the navigation on.

C — an eighth position, clm_employee. Keeps every token; changes "Position 扁平,七个" and turns the per-employee admin step into a position assignment rather than a set grant.

四棱分析

实际业务需求 — A 或 C。业务上「所有员工都能发起合同」是产品定位(§01),门控能力是卡 07 导航分区的基础;B 把「我的合同」分区退化成登录即见,短期无害,但一旦客户要按员工类型(例如外包、实习)收窄「谁能发起」,B 没有开关。A 保留开关。

项目长远合理性 — A。矛盾在平台的高权限判定粗于 ADR-0066 的能力模型(应用能力令牌与平台系统权限被一视同仁),根因应回到平台修;应用侧 A 的代码在平台修好后可整体收回,B 与 C 都是把平台的粗糙固化进设计文本。

防 AI 写错 — A。三个方案里只有 A 不改 systemPermissions 的语义;B 让「一个权限集既是默认又不带令牌」成为惯例,下一个 AI 作者会在别的分区照抄,门控能力体系逐渐空心化;C 引入一个只为绑定而存在的岗位,与 ADR-0090「岗位是分发点」勉强相容,但七变八的理由是平台限制而非业务,容易被下一个读者当成业务岗位再挂规则。

创业阶段不扩散 — 三者都不扩散能力面。A 增加一步管理员操作与一条上游 issue;B 删一个令牌;C 加一个岗位。A 的成本最低且可逆。

结论:四棱同向 A。推荐 A。

Impact if deferred

M1 is not blocked: card 04's acceptance run staffed the requester accounts through Setup-equivalent grants and every refusal was observed. Card 07 (views/app) gates the "我的合同" group on clm_requester.access; an employee without the grant will not see it. Deciding before card 07 is dispatched avoids a second pass over the navigation.

Activity

  1. hotlong commented on Sep 7, 2026

    @hotlong
    ContributorAuthor

    第二问(PM 追加):六个动作门控挂在哪些权限集上

    §04 列出了六个动作门控,但没说哪个权限集持有哪个。卡 04 只能推导,推导结果在 PR #12 里。这一条落在「安全与权限边界」上,按规矩归你,不归我。

    维护者速读:下面这张表里只有最后一行需要你多看一眼——「审批合同」这个门控挂在了发起人这个权限集上,看起来像「人人都能审批」。实际不是:审批阶梯的第一级就是直接主管,而主管在系统里就是个普通员工,只持有发起人权限集。不给他这个令牌,第一级永远没人能点。持有令牌不等于能审批任何合同,能审批哪一份由审批流程指派。

    动作门控 推导出的持有者 依据
    维护审批矩阵 管理员 §04 矩阵里只有管理员对审批规则有写权限
    维护条款库 法务、管理员 §04 矩阵:条款对法务是 RCU
    登记执行、归档 档案与记录、管理员 §04 矩阵:签署记录与归档字段归档案岗
    终止合同 法务、管理员 终止是法律动作,§04 矩阵里业务承办对生效合同无写权限
    审批合同 发起人、法务、财务、管理员 §06 F5 第一级是直接主管,主管只持有发起人权限集

    选项:

    • A(推荐):接受这张表。每一行都由 §04 矩阵或 F5 的阶梯结构推出来,最后一行是唯一能让第一级审批工作的挂法。
    • B:你改动其中若干行,改法是 src/profiles/ 里的一行编辑,不影响其他任何东西。

    你要做的:和本卡第一问一起回,比如「A A」。


    Both questions on this card are one-line reversible edits and neither blocks M1 — PR #12 is merged and the acceptance run staffed its personas through the Setup path. Card 07 is the first consumer of question 1 (it gates the 我的合同 navigation group on clm_requester.access); question 2's only consumer is card 06's approval ladder.


    Generated by Claude Code

  2. zhuangjianguo commented on Sep 9, 2026

    @zhuangjianguo
    Collaborator

    第一问裁定:A —— 接受分发模型,缺口报到平台

    维护者裁决,2026-09-09,常设决裁批第 1 批,逐字:

    第 1 批 平台的问题去平台修,业务的问题按照你的意见。

    第一问的阻塞源是平台缺陷(平台的高权限判定把应用自己声明的能力令牌与平台系统权限一视同仁,粗于 ADR-0066 的能力模型)⇒ 走「去平台修」,而选项 A 恰好就是这个形状:应用侧保持现状、把缺口报到平台、平台修好后删一行绑定即可恢复自动默认。

    ⛔ 因此 B 与 C 明确不采纳:两者都是把平台今天的粗糙固化进设计文本,且不可逆。

    Governing text(录裁前重跑):DESIGN.md §04「Permission set 五个:clm_requester(所有员工默认)…导航分区的门控能力由权限集 systemPermissions 授予:clm_requester.access…」;AGENTS.md → Platform gaps → report, never patch。

    鲜度门:正文最后编辑 2026-09-07T14:00:44Z;其后一条评论(2026-09-07T14:05:46Z,前任 PM 追加的第二问)—— 见下,它改变了本卡的状态转换。

    ⚠️ 第二问未裁,本卡因此不离开决策箱

    本卡带两问。前任 PM 在 2026-09-07T14:05:46Z 追加了第二问(六个动作门控分别挂在哪些权限集上),并写明「和本卡第一问一起回」。

    我在第 1 批只呈报了第一问。 这是我的呈报缺漏,不是你的遗漏 —— 你说「业务的问题按照我的意见」,而第二问我从未给过意见,你无从背书一个不存在的推荐。

    而且第二问不能由我代裁:它自己写明落在「安全与权限边界」上,而安全/权限边界是代裁的人工地板,任何自裁通道都够不到它。第二问的实质是「审批合同」这个门控挂在了发起人权限集上 —— 表面像「人人都能审批」,实际是因为审批阶梯第一级是直接主管、而主管在系统里只是普通员工。这个取舍必须你亲自看一眼。

    ⇒ 状态转换:本卡维持 needs-user-decision。 第一问的落地作为搭车项(Refs #11,⛔ 非 Fixes)随设计文档 PR 走;第二问随下一批呈给你,届时我补齐它的四棱与推荐。

    裁后执行(仅第一问)

    1. §04 编辑(受管面,随折叠的设计文档 PR,链首 [Decision] DESIGN.md §03 requires a termination reason but declares no field for it #6):加一句 —— clm_requester 由管理员授予未持岗位的员工;持岗位者随岗位绑定。
    2. 代码零改动:卡 04(Positions, permission sets, sharing rules, FLS, onEnable bindings (card 04, M1) #4 / PR Positions, permission sets, sharing rules, FLS, onEnable bindings (card 04, M1) #12)实现的就是这个分发模型,七个岗位各自额外绑定 clm_requester,已在 main 上。
    3. 平台缺口卡已立:见下方本席的立卡回链。按跨仓规矩 file-at-destination,普通卡、无 assignee、待分诊。
    4. 平台修好后的收回动作(预写在此,免得将来重新论证):把 clm_requester 标 isDefault: true,删掉 src/security/bind-position-sets.ts 里那七行额外绑定。

    与 #28 的耦合,现已解开

    #28 明写「先回 #11 第一问,这一张的答案就基本定了」。第一问裁 A ⇒ dev admin 永远看不到「我的合同」分区 ⇒ #28 的选项 C 自动出局,剩下 A(维持现状,操作员手工建号)与 B(让 pnpm demo 自己建三个演示账号)二选一。#28 在第 2 批,届时按这个已定的前提呈报。


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    Contributor

    Evidence for this card's second question · 2026-10-09T16:01Z — measured by the #82 dev (PR #85, Acceptance notes), identical on 17.4.0 and 17.7.0.

    A clm_admin holder cannot edit a contract past submitted: PATCH /api/v1/data/clm_contract/ID → 403, and editing its child review is refused with "master not editable … row-level security". Cause, as measured: every position also binds clm_requester (the workaround for the everyone anchor this card is about), and that set's using-only contract_requester_edit_window is the only update policy that applies — so the requester's edit window caps the administrator too. README says clm_admin "holds full reach over every CLM object".

    Not filed as its own card: its cause is the binding this card's open question decides. Whichever permission sets hold clm_requester once Q2 is ruled, this is the case that ruling must not leave behind.


    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions