#107(PR #114)把员工确认 → 主管审定/打回做成了 duly_duty.review_status 状态机,演示能跑通,因为演示账号是平台管理员。在真实部署里,没有任何一个 Duly 权限集能写这个字段:duly_member 对 duly_duty 没有 allowEdit,三个权限集的 writeScope 都是 own。审定流水线在部署环境里是一件摆设。
这不是 #107 漏做,是它刻意没做——两个能打开的口子都会推翻文件里写明理由的决定:
| 方案 | 打开什么 | 代价 |
|---|
| A | duly_member.duly_duty.allowEdit(writeScope 仍 own),员工能确认自己的清单 | 平台的这个位是对象级的,不分字段——成员因此也能改自己职责的节奏(频次/宽限期),而 permission-sets.ts 明文拒绝这一点(「事后改节奏是纠正,属于 duly_admin 和 catalog_sync」) |
| B | duly_manager.duly_duty.writeScope 放宽到 unit_and_below,主管能审定下属的职责 | 同样分不清字段:审定和改写别人职责的节奏在权限上是同一件事;且撞上 AGENTS.md「主管只有一个写入动作:分派」和 test/security.test.ts 的钉子 |
| C | A + B | 工作流真正可用;两条写明的决定同时推翻 |
| D | 暂不动 | 演示不受影响;部署前必须回到这里 |
#107 选了 D,我同意——把对象级编辑权放开去换一个字段,正是那种放开之后永远收不回来的过度授权。
真正要决定的
审批是不是一次「普通字段写入」。 如果是,就需要平台的字段级写权限(先量 17.2.0 有没有——权限集 schema 里有没有 per-field write)。如果不是,审定应该是一个独立的动作(有自己的权限位,不经过对象编辑权)——但 app 声明的 action 现在渲染不出来(objectui#7234),所以这条路今天走不通。
所以三条可能的路:
- 平台有字段级写权限 → 用它,只放开
review_status 和 review_note,A/B 的代价消失。先量。 - 没有 → 等 objectui#7234,把审定做成 action,权限挂在 action 上。
- 都不等 → 接受 A(成员能改自己职责节奏)作为已知妥协,写进 AGENTS.md。
我的建议:先做 1 的测量(一张小卡,只回答「平台能不能按字段限制写」),结果决定走哪条。这是安全模型的决定,不是我该替你拍的。
关联:#107、objectui#7234、src/security/permission-sets.ts 文件头。
#107(PR #114)把员工确认 → 主管审定/打回做成了
duly_duty.review_status状态机,演示能跑通,因为演示账号是平台管理员。在真实部署里,没有任何一个 Duly 权限集能写这个字段:duly_member对duly_duty没有allowEdit,三个权限集的writeScope都是own。审定流水线在部署环境里是一件摆设。这不是 #107 漏做,是它刻意没做——两个能打开的口子都会推翻文件里写明理由的决定:
duly_member.duly_duty.allowEdit(writeScope 仍own),员工能确认自己的清单permission-sets.ts明文拒绝这一点(「事后改节奏是纠正,属于 duly_admin 和 catalog_sync」)duly_manager.duly_duty.writeScope放宽到unit_and_below,主管能审定下属的职责test/security.test.ts的钉子#107 选了 D,我同意——把对象级编辑权放开去换一个字段,正是那种放开之后永远收不回来的过度授权。
真正要决定的
审批是不是一次「普通字段写入」。 如果是,就需要平台的字段级写权限(先量 17.2.0 有没有——权限集 schema 里有没有 per-field write)。如果不是,审定应该是一个独立的动作(有自己的权限位,不经过对象编辑权)——但 app 声明的 action 现在渲染不出来(objectui#7234),所以这条路今天走不通。
所以三条可能的路:
review_status和review_note,A/B 的代价消失。先量。我的建议:先做 1 的测量(一张小卡,只回答「平台能不能按字段限制写」),结果决定走哪条。这是安全模型的决定,不是我该替你拍的。
关联:#107、objectui#7234、
src/security/permission-sets.ts文件头。