#4441 让引擎在写入路径上强制 lookup 引用完整性,但刻意豁免了 isSystem 写入:种子重放、包安装、启动期供给合法地以「批次完成后才自洽」的顺序写入,让它们 fail-closed 会把一个排序细节变成启动失败。
这个豁免是对的,但它留下一个缺口:平台自身仍可写入指向虚无的引用,而没有任何东西会说出来。#4441 的 PR 已明确记录该残留、未静默接受,本 issue 是它的落地跟进。
为什么不能靠「把豁免去掉」解决
去掉豁免会把种子排序问题变成启动失败——这正是豁免存在的理由。而且平台确实有合法的非 id 写入:sys_metadata_history.recorded_by 是 lookup('sys_user'),平台以 actor ?? 'system' 填入哨兵字符串,且该写入不带 isSystem(这一条已由 #4441 收窄为跳过 readonly 字段处理)。拒绝平台自身的写入不是报告问题的正确方式。
建议形态:只报告,不改写
与 #4469 的 inspectStrandedRequests() 同一形状——那条巡检的判断已经过一轮实践检验,可直接借鉴:
范围建议
验收
边界
只做巡检与报告。不要改动 #4441 的强制逻辑,不要新增可作者化的 spec key(「悬空引用改为 opt-in」是另一个待维护者决策的方向,见 #4441)。
发现自 #4482 v17 验收批次;#4441 的 PR #4511 记录了这一残留。
#4441 让引擎在写入路径上强制 lookup 引用完整性,但刻意豁免了
isSystem写入:种子重放、包安装、启动期供给合法地以「批次完成后才自洽」的顺序写入,让它们 fail-closed 会把一个排序细节变成启动失败。这个豁免是对的,但它留下一个缺口:平台自身仍可写入指向虚无的引用,而没有任何东西会说出来。#4441 的 PR 已明确记录该残留、未静默接受,本 issue 是它的落地跟进。
为什么不能靠「把豁免去掉」解决
去掉豁免会把种子排序问题变成启动失败——这正是豁免存在的理由。而且平台确实有合法的非 id 写入:
sys_metadata_history.recorded_by是lookup('sys_user'),平台以actor ?? 'system'填入哨兵字符串,且该写入不带isSystem(这一条已由 #4441 收窄为跳过readonly字段处理)。拒绝平台自身的写入不是报告问题的正确方式。建议形态:只报告,不改写
与 #4469 的
inspectStrandedRequests()同一形状——那条巡检的判断已经过一轮实践检验,可直接借鉴:undetermined而非判定为悬空,这样「0 条悬空」永远不会被误读成「一切正常」。[approvals] 存量僵尸请求无人认领:已终态但 run 悬空的请求落在 releaseDeadRunRequests 的盲区里 #4469 的巡检为此专门写了两条测试。范围建议
readonly的lookup/master_detail字段;readonly字段按 data: a lookup accepts an id that does not exist in the referenced object — including the RBAC permission-set link tables #4441 的既有判断跳过(那里的值由平台铸造,非调用方值)。null/''/ 空数组)不是引用,跳过 —— 与 data: a lookup accepts an id that does not exist in the referenced object — including the RBAC permission-set link tables #4441 的判断保持一致。sys_position_permission_set等):一条悬空行在那里是指向虚无的安全面记录,而受众锚点闸门恰恰要解析那个权限集才能评估授权。验收
undetermined而不判死、以及快照前后数据 JSON 相等(钉死「绝不改写」)。边界
只做巡检与报告。不要改动 #4441 的强制逻辑,不要新增可作者化的 spec key(「悬空引用改为 opt-in」是另一个待维护者决策的方向,见 #4441)。
发现自 #4482 v17 验收批次;#4441 的 PR #4511 记录了这一残留。