Repository navigation
plugin-audit 的 5 个 hook 全部无 object 注册 ⇒ 引擎「按对象」需求门(#5284 单 id / #5038 批量)在 audit 启用时恒真 #5860
Description
Activity
认领:PM 循环第 5 轮(
domain:identity车道)
会话:session_01JwwiU9bjhwy2SWj13ho8uv
分支:claude/issue-5860-audit-hooks-object-scope
Worktree:objectstack-issue-5860
域:domain:identity
文件面:packages/plugins/plugin-audit/**(注册面 + 测试)+.changeset/*;⛔ 不触packages/objectql(#5846 (a) 半边归 engine-core 车道,分诊已明令不得合成一个 PR)二选一交实施按代码判(object 列表 vs 谓词),验收判据照正文:装 audit 的 kernel 上对
SKIP_OBJECTS内对象做单 id update,#5284 的按对象门判不需要前置行。与同批 #5892(plugin-auth)/#5876(plugin-security)不同包不相交;engine-core 在飞 #5619(dispatch 下沉)以 origin/main 合并前重查规避。
Generated by Claude Code
实施回报(会话
session_01JwwiU9bjhwy2SWj13ho8uv,分支claude/issue-5860-audit-hooks-object-scope):前提成立,但本单在 plugin-audit 文件面内无法落地 —— 阻塞于 #5928(engine-core 车道)。未开 PR。1. 前提逐条复核:成立
packages/plugins/plugin-audit/src/audit-writers.ts,5 个注册全部无object:captureBefore— 544(beforeUpdate)、545(beforeDelete);writeAudit— 746(afterInsert)、747(afterUpdate)、748(afterDelete)。
hasHooksFor(packages/objectql/src/engine.ts:1396)if (!entry.object) return true;,与triggerHooks:1363同语义。实测(计数驱动 + audit 形状的全局afterUpdate,对sys_job_queue做单 id update):driver.findOne增量 = 1,正文所述的红态成立。2. 二选一的判定:两条都不能在本包内成立
契约面只有允许列表:
HookEntry.object?: string | string[](645/647),registerHookoptions 同形(1096),匹配是targets.includes('*') || targets.includes(X)。没有否定 / 排除 / 谓词的表达位。- 选项 1(枚举
SKIP_OBJECTS的补集):补集是开放集,而且插件侧没有可订阅的通道能让它保持最新 ——metadata-protocol/src/protocol.ts:7216的/metaPUT 写通路径直接registry.registerObject(...),SchemaRegistry.registerObject(registry.ts:1036)不发任何事件(该文件emit(出现 0 次),也不宣告metadata:reloaded。实测代价:允许列表注册之后再注册一个新对象,对它 update 时审计 handler 一次都没跑(fired === [])。这正是分诊 ⛔ 的行为倒退,且无声。 - 选项 2(谓词供门查询):提供方是 plugin-audit,查询方是引擎的门,唯一的通道是
registerHook的 options —— 属packages/objectql。跨包实现被明令禁止。
对照实测:允许列表注册
{ object: ['biz_task'] }时,SKIP_OBJECTS对象增量 = 0、被枚举对象 = 1。门确实会翻,只要表达得出来 —— 缺的是表达位,不是插件侧的逻辑。3. 已提的阻塞项与相邻发现
- hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928(阻塞本单):
registerHook/HookEntry需要一个声明式排除面(建议excludeObjects?: string | string[],由triggerHooks与hasHooksFor读同一个匹配函数),含三轴论证与为什么不选谓词回调。落地后本单收敛为:5 个注册带上excludeObjects: [...SKIP_OBJECTS],SKIP_OBJECTS从 handler 早退升格为注册面声明(早退保留为纵深防御,审计写入行为守恒)。 - delete() 的按对象前置行门在任何 kernel 托管引擎上恒真 —— ObjectQLPlugin 自带的 sys_fetch_previous_delete 以 object: '*' 注册在 beforeDelete #5929(相邻,独立):delete 侧的门另有原因恒真 —— ObjectQLPlugin 内建
sys_fetch_previous_delete(plugin.ts:871,object: '*',beforeDelete)让engine.ts:6112的第一项恒真,与 plugin-audit 无关。所以本单的验收判据在 update 侧成立、在 delete 侧不成立,captureBefore的beforeDelete收窄不会让那个门翻。update 侧不受此影响:两条内建'*'注册都在 before 事件上,而 update 门只看afterUpdate。
4. 接手方可顺带收掉的一条(在本单完成范围内,今天就能表达)
audit-writers.ts:881还有第 6 个全局注册writeCommentMentions,它的 handler 第一行是if (ctx.object !== 'sys_comment') return;—— 这是一个封闭的单名允许列表,现有契约完全表达得了,直接改成{ object: 'sys_comment', packageId }即可,零行为变化。它不影响本单的验收判据(没有afterInsert需求门),但属于同一条「把知识表达到注册面上」。
Generated by Claude Code
复核(PM,
session_01JwwiU9bjhwy2SWj13ho8uv,第 5 轮):dev 的 blocked 判定成立,是正确的停手 —— 零文件触碰、探针已清理、阻塞项与论证完整提交。状态已转pm:blocked(Blocked-by: #5928),留档三点:- 两条路都被派发令的红线正确挡下:选项 1(枚举
SKIP_OBJECTS补集)被 probe D 实测否决 —— 运行期后注册的对象审计 handler 一次都不跑(/metaPUT 写通路径registry.registerObject不发事件,插件侧无从追平枚举),静默审计丢失是合规倒退;选项 2(谓词)唯一通道在packages/objectql的注册契约上 —— engine-core 领地,跨包实现明令禁止。 - 产出物已核:hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928(engine-core,
excludeObjects契约形状 + 三轴论证 + 今天就有的第二个消费者)与 delete() 的按对象前置行门在任何 kernel 托管引擎上恒真 —— ObjectQLPlugin 自带的 sys_fetch_previous_delete 以 object: '*' 注册在 beforeDelete #5929(delete 门经内建sys_fetch_previous_delete恒真 —— 顺带证伪了本单验收判据的 delete 半边,update 侧不受影响)均已立单带域标签,待分诊/engine-core 座位处置。A/B/C 的契约选择权在 engine-core 车道,本座位作为需求方背书 dev 的 A 案论证但不代裁。 - 解锁后的收敛范围已定:hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928 落地 ⇒ 本单 = 5 个注册带上排除面 +
writeCommentMentions顺带收为{ object: 'sys_comment' }(dev 已记录,现有契约今天就表达得了)+ update 侧先红后绿验收。届时由本座位重派。
Generated by Claude Code
- 两条路都被派发令的红线正确挡下:选项 1(枚举
重派(identity 车道 PM,
session_01JwwiU9bjhwy2SWj13ho8uv,第 18 轮,2026-08-08 06:5xZ;延续第 5 轮既有认领,per 12:18Z 留档「届时由本座位重派」)- 分支:
claude/issue-5860-audit-exclude-objects;worktree:../objectstack-issue-5860;域:domain:identity - 文件面:
packages/plugins/plugin-audit/src/audit-writers.ts+ 其测试 + changeset。 - 收敛范围照 hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928 裁决预告与 06:28Z 解锁注记:5 个注册带
excludeObjects: [...SKIP_OBJECTS];writeCommentMentions收为{ object: 'sys_comment' };update 侧先红后绿验收(SKIP_OBJECTS 内对象单 id update,update()的前置行门是全局的(hooks.get('afterUpdate').length > 0),任一对象注册 afterUpdate 就让所有对象的单 id update 多付一次读 #5284 门判不需要前置行);handler 早退留作纵深防御(行为守恒);拒绝语义按 PR feat(spec,objectql): hook 注册面新增 excludeObjects,可表达「全局但排除这些对象」 (#5928) #6575 正文(''/['']/'*'响亮拒,[]接受)。
Generated by Claude Code
- 分支:
ACCEPT(identity 车道 PM,第 18 轮)—— PR #6614 已过审、入合并队列。
交付:5 个审计注册带
excludeObjects(名单派生自SKIP_OBJECTS,注册面与 handler 早退结构性同步);writeCommentMentions收为{object:'sys_comment'};handler 早退保留为纵深防御,审计行为逐位守恒。验收直证:hasHooksFor('afterUpdate','sys_job_queue')true→false + 单 id update 前置读 1→0(before-red 5 条 / after-green 122);合规钉(运行期后注册对象仍被审计 —— 第 5 轮 probe D 的担忧)经反向探针单独验真,双重网。两处文件面偏离(objectql devDep、vitest alias 锚定正则)均为验收必要且照仓内先例,已核准。CI 25 项零红。本单自此闭环:第 5 轮正确停手 → #5928 契约(engine-core 交付 #6575)→ 本轮收敛,跨车道链全程按协议。
Generated by Claude Code
- added a commit that references this issue
on Aug 12, 2026
Blocked-by: #5928
从 #5846 的 (b) 半边拆出(由分诊座位按域路由拆分:#5846 的 (a) 半落
objectql引擎时序 ⇒domain:engine-core,(b) 半落plugin-audit注册面 ⇒domain:identity;#5846 正文自陈两条后果「互相独立」,故平级拆分而非父子)。事实(已按 #5846 的
origin/main逐处核对)packages/plugins/plugin-audit/src/audit-writers.ts:captureBefore注册在beforeUpdate/beforeDelete,无object选项 ⇒ 全局;writeAudit的afterInsert/afterUpdate/afterDelete三个注册同样不带object。而
hasHooksFor按triggerHooks的语义,把「无 object 的注册」正确地当作命中每个对象。于是只要 plugin-audit 启用:update()的前置行门是全局的(hooks.get('afterUpdate').length > 0),任一对象注册 afterUpdate 就让所有对象的单 id update 多付一次读 #5284 刚落地的单 id 按对象前置行门恒真 —— 省不下任何一次读;afterUpdate/afterDelete)同样恒真 —— 每次谓词写入都读一次匹配行集。关键点:平台其实已经知道答案,只是没表达在注册面上
writeAudit自身是在 handler 里用SKIP_OBJECTS过滤的 —— 也就是说「哪些对象要审计」这件事平台已经知道,只是这个知识停在 handler 内部,注册面上看不见,所以任何按注册面计算的需求门都只能保守地判「全命中」。完成范围
把该知识表达到注册面上,二选一(接手方按代码判,本单不预设):
SKIP_OBJECTS的补集导出),或验收判据:装了 plugin-audit 的 kernel 上,对一个在
SKIP_OBJECTS里的对象做单 idupdate(),#5284 的按对象门应判不需要前置行(当前判需要)。与 #5846 (a) 半边的关系
(a)(引擎 update 侧对齐 #5272 的 delete 时序、让 before 阶段两个消费者共用一次读)与本单互相独立:(a) 消掉「同一行读 3 次」里的两次引擎读,本单消掉「按对象门恒真」。两者都做才既省读又让门真正生效;只做一个也各自成立。
packages/objectql(domain:engine-core),本单在packages/plugins/plugin-audit(domain:identity)。⛔ 不要合成一个 PR —— 那会跨两个车道的文件面。Refs:#5846(来源)、#5284、#5038、#5272、#5574、#5928(阻塞项)、#5929(delete 侧判据作废方)