数据范围:配置与审计对象按主体过滤,配置类菜单按岗位裁剪(#30) - #32
Conversation
代码评审报告(os-project-dev-review)档位:全量档(依据:改已有共享规则与权限集 = 高风险;diff 10 文件 +518/−31;含五个对象 OWD 收成 评审为冷启动独立只读:输入只取工作项 #30 全文、PR diff、需求符合度清单原文;未读开发方测试报告、开工前方案评论、交接评论或对话记录。全部结论以自建实例实测为准。 实测环境(自建、自有端口、空库):临时 clone 通用质量(
|
| 账号 | 指标下达 | 参与主体 | 到人分工 | 审核记录 | 归档快照 | 核对任务 | 指标库 |
|---|---|---|---|---|---|---|---|
| 管理员 / 马丽(人力审核)/ 何平(人力负责人) | 36 | 11 | 8 | 94 | 1 | 30 | 24 |
| 张伟(研发部·部门填报) | 3 | 1 | 1 | 8 | 0 | 3 | 24 |
| 赵敏(销售部·部门填报) | 3 | 1 | 1 | 16 | 1 | 3 | 24 |
| 陈东(华东·分公司核对) | 4 | 1 | 0 | 7 | 0 | 10 | 24 |
| 徐涛(技术线·分管领导) | 9 | 3 | 3 | 24 | 0 | 9 | 24 |
与清单第 1–10、24–29 条一致(清单第 4 条写「全量 94」,与实测同)。指标库对六类岗位一律 24 条 —— kpi_indicator 的 OWD 与授权确未被动过,清单第 10 / 22 条成立。
- 同口径核实:六个对象的规则与既有填报单规则用同一族收件方 —— 主体维度
recipientType: 'unit_and_subordinates'+recipientId: 主体单元,分管维度recipientType: 'user'+recipientId: leader,同一planRulePrefix命名空间、同一发布期对账、同一「关闭/归档后降级」处理(这六类本就恒为read,不需降级,新增单测对此有断言)。✅ - 新增单测非空断言:12 条新用例均为实断言 ——
toMatchObject带具体object/criteria/recipientType/recipientId/accessLevel;含反向断言(scheck_sheet_east必须不存在、华东无分管领导则不产生领导规则);含不变量断言(最坏形态规则名长度恰为 99 ≤RULE_NAME_MAX)。若byName.get()落空,toMatchObject直接失败,不会静默通过。✅
3 三禁痕迹:✅ 无
- diff 路径不含
node_modules/ 依赖目录,无 patch 类文件。 scope-materialize.ts不是绕行:它不直写sys_record_share(平台明示该表引擎独占、数据 API create 返回 405),而是调用本应用既有的provisionPlanSharing重新声明sys_sharing_rule—— 这正是平台日志给出的官方补偿手段(sharing materialisation skipped for isSystem writes; re-evaluate rules or restart to backfill)。系统上下文取自hooks/util.ts的sys(ctx)→ 平台IScopedContext.sudo(),与既有全部 hook 同一通道,不是伪装用户上下文。bind-admin-set.ts同样走标准面:写sys_user_permission_set(平台标准授权表),挂kernel:bootstrapped,与既有bind-position-sets.ts同构;先查后插,幂等。- 导航闸门用的
requiredPermissions(导航项)与systemPermissions(权限集)都是 spec 一级键,且app.zod.ts明确「navigation ITEM 的requiredPermissions服务端强制剥离」。属平台标准机制的正常使用。
4 硬拍板落地:✅
- 本单不新增任何数字字段(diff 中无
Field.number/currency/percent),四件套不适用。 - 新增用户可见文案只有动态共享规则的
label/description(如「参与主体共享给研发部及其下级」「研发部审核记录共享给分管领导」「考核主体成员」「分管领导」):名词取自需求文档术语表(参与主体 / 指标下达 / 到人分工 / 归档快照 / 审核记录 / 分管领导),无内部代号、无机器名、无异常原文。✅ 过 std-copy 红线。 - 导航项与权限集 label 未新增、未改动;新增的两条失败路径只写服务端日志,不向用户抛文案。
i18n:extract:check通过,元数据 label 与 zh-CN 包同步。
调用面抽查(全量档专项)
OWD 收成 private 后,逐条核实既有读路径未被改坏:
| # | 读路径 | 结论 | 依据 |
|---|---|---|---|
| 1 | 结果汇总服务 results-service.regenerateResults(读参与主体 / 到人分工 / 个人承接项) |
✅ 未受影响 | 调用点在 sheet.hook.ts / bonus.hook.ts / adjustment.hook.ts,传入的 api 一律是 sys(ctx)(sudo() 系统上下文),不经共享过滤 |
| 2 | 归档快照服务 snapshot-service.createSnapshot(读审核记录、写快照) |
✅ 未受影响 | 同上,createSnapshot(api, …) 的 api 来自 const api = sys(ctx) |
| 3 | 共享服务 provisionPlanSharing(读方案 / 主体 / 分工 / 填报单) |
✅ 未受影响 | 四个调用点全部传 sys(ctx) |
| 4 | hook 内部读(发布完整性校验、方案复制、分公司清单、核对任务生成、争议、填报明细) | ✅ 未受影响 | src/ 内对这五个对象不存在任何用户上下文读;grep 逐个对象核对,全部经 sys(ctx) |
| 5 | 人力审核 / 人力负责人 / 管理员的全量可见 | ✅ 未被改坏 | 三者保留 readScope: 'org';平台 buildReadFilter 在 __readScope === 'org' 时 return null,与 OWD 无关。实测三者计数与全量基准逐对象一致(36/11/8/94/1/30) |
| 6 | 分公司核对人员对填报单的 org 读 | ✅ 未被改坏 | kpi_entry_sheet 授权本次未动,其 OWD 改前即为 private。实测陈东可见 11 张(全量) |
| 7 | kpi_personal_item(现私有的到人分工的主从子记录) |
✅ 随主记录收窄,注释属实 | 实测张伟可见 1 条、取他人记录 404;controlled_by_parent 的下推由 ObjectQL 在上游结算(ADR-0055),与 plugin-sharing 的记录共享层是两层,故未被 effectiveSharingModel → public 短路 |
| 8 | 端到端业务链路 | ✅ 未被改坏 | 自建实例 software-flow.mjs 54/54 全 PASS(含 T15a–e 四维结果、T16a–d 归档快照、T17a–d 数据范围);默认档案 e2e-flow.mjs 72 PASS / 1 FAIL,唯一失败为 T39 常量(见下),T50/T51/T53/T55/T56/T57/T64/T70 数据范围断言全 PASS |
| 9 | 越权直取 | ✅ 一律 404 | 张伟对指标下达 / 参与主体 / 到人分工 / 个人承接项 / 审核记录 / 归档快照 / 核对任务七个对象取他人记录一律 HTTP 404,取本人记录 200;强制按他部门主体过滤返回空集 |
bind-admin-set.ts 的匹配条件(误绑 / 漏绑):实测生效 —— 绑定后 admin@objectos.ai 新增一行 kpi_admin_set,管理员导航保持完整 24 项。不会误绑:目标集合是「持有 admin_full_access 的用户」,而 admin_full_access 本就是平台超级权限持有者,授予 KPI 域权限集不构成越权升级;先查后插,幂等,重复启动不产生重复行。存在两个漏绑边界(均未在当前档案触发,见下 ⚪-2)。
requiredPermissions 对六类岗位的导航结果 vs 口径矩阵:逐岗位实测 /meta/app/kpi_app 与 /meta/apps 两个端点,结果一致(服务端剥离,不下发浏览器):
| 岗位 | 导航项数 | 与口径第 11 条 是否一致 |
|---|---|---|
| 管理员 / 人力审核 / 人力负责人 / 分管领导 | 24(完整) | ✅ 口径只要求对另两类隐藏 |
| 部门填报人员 / 分公司核对人员 | 16 | ✅ 恰好剥掉「考核方案」「基础设置」两组 + 其下 7 个子项(方案版本 / 指标下达 / 参与主体 / 到人分工 / 指标争议 / 指标库 / 流程进度),与口径列举一字不差 |
「56」是否与本 PR 的规则生成逻辑一致
一致。 按对象 × 收件方独立推导,并在默认档案实例上实测复核,两者精确吻合:
- 以
e2e-flow.mjs的plan3配置(5 主体 = 2 部门 + 3 分公司,仅市场部配分管领导,2 条到人分工,发布生成 5 张填报单)直接调用planSharingIntents,实际生成 62 条规则:原有 29 条 + 按主体的配置四类 4×5=20 + 分管领导同族 4×1=4 + 按填报单的审核记录 5+1=6 + 本单核对任务 2+1=3。 - T39 的
rulesOf过滤条件是「criteria_json里出现方案 id」,而 6 条kpi_review_record规则的条件是{ sheet }、不含plan键(审核记录上没有方案列,这是代码里写明的取舍),因此被该过滤器漏掉 → 计入 56 条。 - 默认档案实例实跑,T39 输出
rules=56 expected=29—— 与推导值精确一致。
给调度员的提示:同步该常量时,正确值取决于口径 —— 「T39 那条按 criteria_json 过滤的断言」是 56,「本方案实际生成的规则总数」是 62。另外 T39b(「每条规则的条件里都带本方案 id」)当前是空过:那 6 条只带 sheet 的规则在进入 T39b 之前已被 rulesOf 滤掉,断言从未看见它们。若把 T39 改为按方案实配推导,需一并确认 T39b 的口径(规则的方案归属由规则名前缀保证,对账也按名前缀回收,功能上无缺陷)。
发现清单
无 🔴 阻塞,无 🟡 应修。以下为 ⚪ 记录级,不阻塞合并:
| 级 | 位置 | 问题 | 处置出口 |
|---|---|---|---|
| ⚪-1 | src/security/bind-admin-set.ts:1(声明)+ src/apps/index.ts |
kpi_plan_config 只被 systemPermissions / requiredPermissions 按字符串引用,未经 defineCapability 在 stack capabilities[] 里正式声明。按 ADR-0066 D1,这样的能力是「隐式派生、无标题」的,平台权限管理界面里会显示裸机器名 kpi_plan_config 而无中文标签。pnpm validate 接受,不影响功能 |
留痕;后续可补一条 defineCapability(带中文 label)消除管理界面上的内部代号 |
| ⚪-2 | src/security/bind-admin-set.ts:237 |
匹配面只扫 sys_user_permission_set 里 admin_full_access 的直接授权行。若某部署把平台管理员权限经 sys_position_permission_set(本项目正是用这张表绑 KPI 岗位权限集)或其他组/角色路径授予,该管理员漏绑,登录后静默丢失两个配置类菜单组;另 limit: 200 在管理员超过 200 人时静默截断。当前全部档案均为直接授权行,未触发 |
留痕;补一条直接授权行即可自愈,是否加固交维护者 |
| ⚪-3 | src/security/bind-admin-set.ts:193 |
「只增不减」是显式设计,但后果是:撤销 admin_full_access 不会连带撤销 kpi_admin_set,被降权的管理员仍保有全部门目标值与权重的全量可见。与本单「各部门目标值互相保密」的意图方向相反,但不在工作项列举范围内 |
留痕,提请维护者判断是否另立跟踪项 |
| ⚪-4 | src/security/index.ts:137,152 |
分公司核对人员与分管领导本次新拿到 kpi_staff_assignment: readOwn(改前二者无此对象授权),但两个权限集都没有 kpi_personal_item 授权。实测二者读个人承接项返回 HTTP 403 —— 「到人分工」详情页的主从子表对这两个岗位会报 403 |
留痕;是否补只读授权交维护者(补则须同步确认承接项的保密口径) |
| ⚪-5 | src/apps/index.ts:46(nav_disputes)+ src/security/index.ts:96 |
口径第 11 条 允许「隐藏或收成只读入口」,实现取了「隐藏」。副作用:部门填报人员的权限集仍声明 kpi_dispute.allowCreate: true,但「指标争议」是该对象唯一的 UI 入口,隐藏后该新建能力在界面上不可达(REST 仍可创建)。属维护者已明确授权的方向,只是声明与入口不再自洽 |
留痕,提请维护者确认是保留声明、还是改为只读入口 |
| ⚪-6 | src/services/sharing-service.ts:605 |
kpi_entry_sheet 取数写死 limit: 2000。方案填报单超过 2000 张时溢出部分静默拿不到审核记录 / 本单核对任务规则(填报单数 = 参与主体数,现实上限远低于 2000) |
留痕 |
| ⚪-7 | src/services/scope-materialize.ts:47 |
每插入一条审核记录都触发一次 provisionPlanSharing(4 次查询 + 1~2 条规则重申)。已用 sheets 选项把重申收窄到本条记录所属那一张单,避免平方级放大;剩余扇出代价在 sharing-service.ts 既有的「已知取舍」注释里已登记 |
留痕(既有已知取舍,本单已做收窄) |
结论:可合并。 阻塞与应修均为 0;上列 7 条为记录级,按 os-project-dev-review 不阻塞合并。功能是否达标仍归验收线判定,本报告不代替验收。
评审在临时目录进行,评审结束已按 PID 停止两个实例并删除临时目录;未对本仓库任何分支做写操作。
评审闸门处置 + 合入 main(不切状态、不改处理人)
修复点 ↔ 提交
一处顺带修好的截断(同文件,说明原因)改完口径后 T70 变红(岗位规则数出 3 条而不是 4 条)。根因不是本次改动,而是 两脚本实跑摘要均为空库重建( 默认档案 软件档案 门禁 个人承接项修复的实测(修复前这三行是 403)
|
| 对象 | 管理员可见 | 张伟(研发部)可见 | 跨范围逐记录读 |
|---|---|---|---|
kpi_personal_item 个人承接项 |
8 | 1 | 404 |
kpi_entry_line 填报明细 |
36 | 3 | 404 |
读侧的收窄由 plugin-security 的主从判定完成,不走共享层的读过滤 —— 两条路径,我上一轮只看了其中一条就下了结论。因此「个人目标值、填报实际值全员可读」这个我上一轮记下的残留风险并不存在,也不需要为它另立工作项。给评审与验收造成的误导由我负责,特此更正。
其余记录级条目(1 kpi_plan_config 未 defineCapability、2/3 bind-admin-set 漏绑与只增不减、5 争议菜单隐藏但仍 allowCreate、6 limit 2000、7 扇出代价)按轮次纪律留痕不动,未顺手扩围。
提交即止,未合并。
代码评审报告 —— 复查轮(os-project-dev-review)复查范围:仅修复点 + 是否引入新问题(轮次纪律),不重开全面评审;上一轮留痕的记录级条目一律不动、不升级。 改动面: 实测环境:独立临时 clone @ ①
|
| 核查点 | 结论 | 证据 |
|---|---|---|
| 确实不是下限 | ✅ 精确相等 | T39 用 kpiRules.length === EXPECTED_RULES(非 >=);T39b 用 byPlan + bySheet === kpiRules.length && bySheet === EXPECTED_SHEET_SCOPED && byPlan > 0,总数必须被完全归类,出现第三类即红 |
| 抓全了、没漏 | ✅ 无遗漏 | 独立比对:rulesOf 返回 92 条,该方案前缀 kpi_pogtwkzeiba7f_s6l03j_ 下的真实全量也是 92 条(active 92)—— 逐条相等,不是子集 |
| 锚不到时变红而非空过 | ✅ 变红 | 用伪造方案 id 与对不上的主体两种方式打断锚点,rulesOf 均返回空数组;而 EXPECTED_RULES 恒 ≥ 主体数×8 > 0,=== 当场判红。T39b 另有 kpiRules.length > 0 兜底 |
| 推导式按实配、未写死 | ✅ | subs3After.length * 8 + leaderSubs3.length * 6 + 去重领导数 + 2 + 2*2 + sheets3.length + leaderSheets3 + deptSubs3.length + deptLeaderSubs3 —— 每一项都从当次方案实配读出。逐项对照 sharing-service.ts 的生成逻辑(每主体 4 旧 + 4 新配置类 = 8;有领导的主体 2 旧 + 4 新 = 6;每张单审核记录 1 + 领导 1;部门主体那张单核对任务 1 + 领导 1)—— 一致 |
| 前缀不复刻散列算法 | ✅ | 从「每个参与主体必有一条 <前缀>sheet_<单元> 规则」反推,planRulePrefix = kpi_p<ruleSlug(planId)>_ 以 _ 收尾,反推出的前缀与实现完全对齐,不会两地各飘一份 |
实跑(默认档案空库):T39 rules=92 expected=92 PASS;T39b 按方案 82 条 / 按填报单 10 条(期望 10)/ 共 92 条 PASS。上一轮我给出的「56 / 62」两个数字在新口径下已归一为同一个 92(旧口径漏掉的 6 条审核记录规则现已被计入),口径分裂消除。
② 分公司核对人员 / 分管领导补 kpi_personal_item 只读 —— ✅ 通过
| 核查点 | 结论 | 证据(软件档案实测) |
|---|---|---|
| 随主记录收窄、未放宽 | ✅ | 徐涛(分管领导)到人分工 5 / 个人承接项 5(全量 8);陈东(分公司核对)0 / 0;逐行核对子表的 assignment 是否都落在本人可见的主表内 —— 越界 0 行 |
readScope 是最窄的 own |
✅ | readOwnChild = readOwn,即 readScope: 'own',且 allowCreate/allowEdit/allowDelete 全 false |
| 跨范围仍拒 | ✅ | 徐涛、张伟取分管/本部门外的承接项记录一律 HTTP 404,取范围内 200 |
| 未顺手扩围 | ✅ | 部门填报人员原有的 kpi_personal_item: readScope 'org' 未被改动,实测张伟仍是 1/8(与上一轮同),测试注释写明为何把它排除在同范围断言之外 |
| 单测是不变式而非当前取值 | ✅ | 3 条:①「声明了到人分工就必须同时声明个人承接项」(三个受限岗位全覆盖,含部门填报人员)②「新收窄的两个岗位:子表 readScope == 主表 readScope == own」③「个人承接项一律只读」。均为跨岗位遍历的结构断言,任何一方将来被放宽都会红,不是快照 |
上一轮记录的 403(⚪-4)已消除:两个岗位现在拿到的是空表或本范围内的表,不再是报错页面。
③ sys_sharing_rule 翻页 —— ✅ 通过,未改断言语义
- 截断属实:实测
sys_sharing_rule已有 538 行,旧写法?limit=500只取到 500 行。 - 翻页正确:分页取到 538 条,去重后仍 538(无重复),与单次
limit=5000取到的 538 条完全一致(无遗漏)——skip确实生效,不会死循环。 - 语义未变:
rulesOf的过滤条件一字未改,只是输入从「截断后的 500 条」变成全量;T50 的断言(kpiOwned.length > 0+every(managed_by !== 'package')+ 不含kpi_share_)原样保留,检查面变大只会更难通过,不构成放宽。
关于「基准差异第 3 条」的撤回 —— ✅ 撤回成立,应当撤回
controlled_by_parent 子对象在读侧确实随主记录收窄,原「全员可读」的残留风险不存在。软件档案逐记录实测:
| 对象 | 管理员 | 张伟(研发部) | 徐涛(分管 5 主体) | 跨范围逐记录 |
|---|---|---|---|---|
kpi_personal_item 个人承接项 |
8 | 1 | 5 | 404 |
kpi_entry_line 填报明细 |
36 | 3 | 15 | 404 |
这与我上一轮的独立实测一致 —— 上一轮报告调用面抽查第 7 项即已记为「随主记录收窄,注释属实」,并未把它列为风险,故本轮无需改判,只是开发方自述的更正。
机制归属补一处更正:收窄由 plugin-security 的主从(master-detail)派生判定完成 —— 其源码明示「a controlled_by_parent detail derives its access from its master」;plugin-sharing 的 effectiveSharingModel 把该 OWD 归为 public、buildReadFilter 不加过滤,是另一条路径。开发方本轮的归属描述正确;我上一轮把它记成 ObjectQL/ADR-0055,层级指认有误,结论不受影响,一并更正。
(另更正上一轮报告一处计数笔误:特权岗位完整导航为 25 项而非 24 项,受限岗位 16 项;剥离项数与结论不变。)
是否引入新问题 —— 未发现
- 生产代码路径本轮只多了两条只读授权,无其他行为改动;
security/index.ts被改后重新逐岗位核对导航,结果与上一轮一致(管理员 / 人力审核 / 人力负责人 / 分管领导 25 项;部门填报人员 / 分公司核对人员 16 项,两个配置类菜单组仍被服务端剥离)。 - 门禁
pnpm verify绿:validate(17 对象)/ typecheck / 110 单测 / i18n 529 键同步。 - 默认档案
e2e-flow.mjs74 / 74 全 PASS(上一轮为 72 PASS / 1 FAIL,唯一失败的 T39 已修复);软件档案software-flow.mjs54 / 54 全 PASS。 - 合入的
b0f6cdc(主线「每个参与主体须配分管领导」)与本单改动无冲突:T39 推导式已按「分管领导按方案实配」推导,合并后两脚本全绿。 - ⚪(仅留痕,不阻塞、不新增条目):
rulesOf的锚点用name.endsWith('sheet_' + sub.subject)拿原始单元 id 比对,而规则名里是ruleSlug(unit)。当前所有单元 id 均为小写字母数字下划线且不超长,ruleSlug为恒等,锚点必中;若将来出现需要转义或超长的单元 id,锚点会失配 —— 但失配的表现是返回空数组 → 断言变红,属安全的失败方向,已在代码注释里写明。
结论:可合并。 三个修复点全部核实通过,撤回成立,未发现新问题。上一轮的记录级条目(kpi_plan_config 未 defineCapability、bind-admin-set 漏绑与只增不减、争议菜单隐藏但仍 allowCreate、limit 2000、扇出代价)按轮次纪律留痕不动;原记录 ⚪-4(个人承接项 403)本轮已修复消除。功能是否达标仍归验收线判定。
复查在独立临时 clone 进行,两个实例已按 PID 停止、端口经 lsof 确认释放,临时目录已删除;未对本仓库任何分支做写操作。
各部门目标值与权重互相保密(维护者 2026-09-03 拍板):指标下达、参与主体、 到人分工、审核记录、归档快照、本单核对任务对部门填报人员只开放本部门相关行, 对分公司核对人员只开放本分公司相关行,分管领导只开放分管主体;人力审核 / 人力负责人 / 管理员保持全量;指标库作为公共口径不过滤。 根因不在权限集而在 OWD:平台 plugin-sharing 的 effectiveSharingModel 把 controlled_by_parent 与 public_read 都映射成「读不过滤」,buildReadFilter 在 第一道闸门就 return null,权限集里的 readScope 随之失效。因此五个对象的 sharingModel 先收成 private,记录级共享与 readScope: 'own' 才开始起作用。 - 对象:kpi_plan_subject / kpi_plan_indicator / kpi_staff_assignment 由 controlled_by_parent、kpi_review_record / kpi_snapshot 由 public_read 一律收成 private; - 共享规则:沿用「按方案建规则、发布时对账、方案关闭后降只读」的同一套机制, 每个参与主体新增参与主体 / 指标下达 / 到人分工 / 归档快照四类只读规则,分管 领导各配一条;审核记录与「本单核对任务」按填报单建规则 —— 审核记录上没有主体 列,而平台共享条件只认对象自己的列、不支持跨对象遍历; - 「本单核对任务」只给总公司部门主体:分公司既是被考核主体又是核对方,给了它 这条规则,华东的核对人员就会在自己那张填报单上看见华南、华北的核对意见; - 补物化 hook 单独成文件并在 objectstack.config.ts 注册,不动 hooks/ 下既有 文件:审核记录与归档快照由 hook 以系统上下文写入,平台跳过共享物化,官方补偿 手段是重新求值规则;重申时按填报单收窄,避免平方级写放大; - 导航裁剪用 requiredPermissions(服务端剥离)而不是 visible:实测本版本控制台 对导航项的 visible 根本不求值,已按只上报不修复报到 objectstack-ai/objectstack#15135;requiredPermissions 逐条 AND、精确匹配、 无超级用户豁免,因此把「平台管理员即考核系统管理员」这条隐含安排写成绑定行。 自测:软件档案 software-flow 54/54;默认档案 e2e-flow 72 通过、1 失败(T39 的 共享规则条数常量随新增规则类别机械变化,scripts/ 本轮禁改,已在工作项上报)。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
评审闸门三处处置(PR #32): 1. 个人承接项读权(评审记录 4,应修)——分公司核对人员与分管领导上一轮拿到了 `kpi_staff_assignment: readOwn`,却没有 `kpi_personal_item` 的授权,「到人分工」 详情页展开主从子表直接 403,是本单引入的用户可见错误。范围与主记录一致、不放宽: `readScope` 写最窄的 own,行的可见性由主记录决定。实测三个岗位列表与逐记录读均 200/404 正确(张伟 1/8、徐涛 5/8、跨范围一律 404)。补了 3 条单测,盯的是「声明了 到人分工就必须同时声明个人承接项」这条不变式,而不只是当前取值。 2. e2e 共享规则对账口径——按**规则名前缀**回收,不再按「条件里含方案 id」筛: 审核记录对象上没有方案列(它只认填报单),平台共享条件又不支持跨对象遍历,那批 规则的条件是 {sheet},含方案 id 的过滤会把它们整批漏掉 —— 上一轮 T39 因此数出 56 而实际生成 62,T39b 也跟着空过。前缀里的方案片段是稳定散列,脚本不复刻那套算法 (复刻就会两地各飘一份),改从「每个参与主体必有一条 <前缀>sheet_<单元> 规则」的 锚点反推;锚不到就返回空数组,让精确相等断言当场变红而不是退化成零条也算过。 T39 的推导式扩到新增的对象规则,T39b 改判「条件要么带本方案 id、要么带本方案某张 填报单的 id」,两类条数都精确相等且都非空,不是下限。 3. 共享规则要翻页取全——一个方案现在就写近百条规则,几个方案过 500 上限,一把 `?limit=500` 会静默截断;症状是某条规则凭空消失、断言红在与截断毫无关系的地方 (本次是 T70 数出 3 条岗位规则而不是 4 条)。 验证:pnpm verify 绿(110 单测);默认档案 e2e-flow **74/74**(含 T39/T39b/T50/T70); 空库软件档案 software-flow **54/54**。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
关联工作项 #30(数据范围:配置 / 审计对象按主体过滤,菜单按岗位裁剪)。
为什么这一改的地基是 OWD,不是权限集
工作项原本的方向是「权限集从
readOrg收成own+ 共享」。照做不会生效,原因在平台侧:而
buildReadFilter()的第一道闸门是if (effectiveSharingModel(schema) !== 'private') return null;—— 返回 null = 不加任何过滤。指标下达 / 参与主体 / 到人分工的 OWD 是controlled_by_parent(在共享层被当成 public),审核记录 / 归档快照是public_read,读侧过滤在这两种 OWD 下压根不参与求值,权限集里的readScope也随之失效。所以先把这五个对象的
sharingModel收成private,记录级共享与readScope: 'own'才开始起作用。改了什么
kpi_plan_subject/kpi_plan_indicator/kpi_staff_assignment由controlled_by_parent、kpi_review_record/kpi_snapshot由public_read,一律收成private。services/sharing-service.ts)—— 沿用既有的「按方案建规则、发布时对账、方案关闭后降只读」同一套机制,不新造轮子:_leader_变体;software-flow.mjsT17c 抓到的真缺陷,已修并补单测。)security/index.ts)—— 新增readOwn;部门填报 / 分公司核对 / 分管领导三套在上述对象上由readOrg改用它,可见性完全交给记录共享。人力审核 / 人力负责人 / 管理员保持org全量。指标库作为公共口径不动。services/scope-materialize.ts,新文件,在objectstack.config.ts注册)—— 审核记录与归档快照由 hook 以系统上下文写入,平台对isSystem写入跳过共享物化,官方补偿手段是重新求值规则。与hooks/sheet.hook.ts里既有的kpi_check_task/kpi_result补偿同路,只是触发点换成记录自己的afterInsert;重申时按填报单收窄,避免「插一条审核记录 → 重申全方案所有单的规则」的平方级写放大。src/hooks/下既有文件零改动(该目录有并行工作项在改)。apps/index.ts)—— 用requiredPermissions而不是visible。实测本版本控制台对导航项的visible根本不求值(谓词原样下发到浏览器后被忽略,分组与叶子项都不生效),已按「只上报不修复」报到平台:App nav itemvisible(CEL) is served to the client but never evaluated — a silently inert gate (17.2.0) objectstack#15135。requiredPermissions是服务端闸门,不满足的项在/meta里就被剥掉。security/bind-admin-set.ts,新文件)——requiredPermissions逐条 AND、精确匹配、无超级用户豁免,不加这条绑定连管理员自己都会丢掉两个菜单组。按「持有admin_full_access的用户」匹配而不是按邮箱,换人 / 多管理员 / 改邮箱都不需要改代码。验证
pnpm verify绿:validate / typecheck / 103 单测(新增 12 条)/ i18n 同步(本次未新增任何 label,zh-CN529 键无变化)。scripts/software-flow.mjs:54 / 54 全 PASS(空库重建)。scripts/e2e-flow.mjs:72 PASS / 1 FAIL。失败项 T39 是一条「共享规则条数」的精确常量断言(EXPECTED_RULES = 29,实测 56),新增规则类别后该常量按定义必须同步;scripts/在本次派发的禁改清单内,故未改 —— 已按失败升级在工作项上给出逐项核对到条的新常量与改法,交调度员裁决。所有数据范围类断言(T50 / T51 / T53 / T55~T57 / T64 / T70)全 PASS。/meta下发内容,证据与截图见工作项评论。测试报告:#30 (comment)
需求符合度清单:#30 (comment)
截图:
acceptance-evidence分支issue-30/,commit224e77a0611679ecea92d9f264ae12506b182e4b🤖 Generated with Claude Code