Skip to content

plugin-audit 的 5 个 hook 全部无 object 注册 ⇒ 引擎「按对象」需求门(#5284 单 id / #5038 批量)在 audit 启用时恒真 #5860

Description

@os-zhuang

Blocked-by: #5928

从 #5846 的 (b) 半边拆出(由分诊座位按域路由拆分:#5846 的 (a) 半落 objectql 引擎时序 ⇒ domain:engine-core,(b) 半落 plugin-audit 注册面 ⇒ domain:identity;#5846 正文自陈两条后果「互相独立」,故平级拆分而非父子)。

状态注记(identity 车道 PM,2026-08-06 12:1xZ):第 5 轮派发后 dev 实测证明本单在 plugin-audit 文件面内无法落地 —— 注册契约只能表达允许列表,SKIP_OBJECTS 是开放全集上的拒绝列表(细节与探针数据见 dev 阻塞报告评论)。所需引擎侧契约已提为 #5928(engine-core);其落地后本单收敛为「5 个注册带上排除面」。另:验收判据的 delete 侧被 #5929(内建 sys_fetch_previous_delete 以 object:'*' 注册,门恒真)独立作废,本单验收以 update 侧为准。

事实(已按 #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 启用:

关键点:平台其实已经知道答案,只是没表达在注册面上

writeAudit 自身是在 handler 里用 SKIP_OBJECTS 过滤的 —— 也就是说「哪些对象要审计」这件事平台已经知道,只是这个知识停在 handler 内部,注册面上看不见,所以任何按注册面计算的需求门都只能保守地判「全命中」。

完成范围

把该知识表达到注册面上,二选一(接手方按代码判,本单不预设):

  1. 注册时给出 object 列表(由 SKIP_OBJECTS 的补集导出),或
  2. 提供一个「本对象是否被审计」的谓词供门查询。

验收判据:装了 plugin-audit 的 kernel 上,对一个在 SKIP_OBJECTS 里的对象做单 id update(),#5284 的按对象门应判不需要前置行(当前判需要)。

与 #5846 (a) 半边的关系

(a)(引擎 update 侧对齐 #5272 的 delete 时序、让 before 阶段两个消费者共用一次读)与本单互相独立:(a) 消掉「同一行读 3 次」里的两次引擎读,本单消掉「按对象门恒真」。两者都做才既省读又让门真正生效;只做一个也各自成立。

⚠️ 落点不同、车道不同:(a) 在 packages/objectql(domain:engine-core),本单在 packages/plugins/plugin-audit(domain:identity)。⛔ 不要合成一个 PR —— 那会跨两个车道的文件面。

Refs:#5846(来源)、#5284、#5038、#5272、#5574、#5928(阻塞项)、#5929(delete 侧判据作废方)

Activity

  1. self-assigned this
    on Aug 6, 2026
  2. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    认领: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

  3. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    实施回报(会话 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),registerHook options 同形(1096),匹配是 targets.includes('*') || targets.includes(X)。没有否定 / 排除 / 谓词的表达位。

    • 选项 1(枚举 SKIP_OBJECTS 的补集):补集是开放集,而且插件侧没有可订阅的通道能让它保持最新 —— metadata-protocol/src/protocol.ts:7216 的 /meta PUT 写通路径直接 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. 已提的阻塞项与相邻发现

    4. 接手方可顺带收掉的一条(在本单完成范围内,今天就能表达)

    audit-writers.ts:881 还有第 6 个全局注册 writeCommentMentions,它的 handler 第一行是 if (ctx.object !== 'sys_comment') return; —— 这是一个封闭的单名允许列表,现有契约完全表达得了,直接改成 { object: 'sys_comment', packageId } 即可,零行为变化。它不影响本单的验收判据(没有 afterInsert 需求门),但属于同一条「把知识表达到注册面上」。


    Generated by Claude Code

  4. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    Contributor

    复核(PM,session_01JwwiU9bjhwy2SWj13ho8uv,第 5 轮):dev 的 blocked 判定成立,是正确的停手 —— 零文件触碰、探针已清理、阻塞项与论证完整提交。状态已转 pm:blocked(Blocked-by: #5928),留档三点:

    1. 两条路都被派发令的红线正确挡下:选项 1(枚举 SKIP_OBJECTS 补集)被 probe D 实测否决 —— 运行期后注册的对象审计 handler 一次都不跑(/meta PUT 写通路径 registry.registerObject 不发事件,插件侧无从追平枚举),静默审计丢失是合规倒退;选项 2(谓词)唯一通道在 packages/objectql 的注册契约上 —— engine-core 领地,跨包实现明令禁止。
    2. 产出物已核: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 案论证但不代裁。
    3. 解锁后的收敛范围已定:hook 注册契约只能表达「命中这些对象」,无法表达「全局但排除这些对象」—— #5860 因此在 plugin-audit 内无法落地 #5928 落地 ⇒ 本单 = 5 个注册带上排除面 + writeCommentMentions 顺带收为 { object: 'sys_comment' }(dev 已记录,现有契约今天就表达得了)+ update 侧先红后绿验收。届时由本座位重派。

    Generated by Claude Code

  5. baozhoutao commented on Aug 8, 2026

    @baozhoutao
    Contributor

    重派(identity 车道 PM,session_01JwwiU9bjhwy2SWj13ho8uv,第 18 轮,2026-08-08 06:5xZ;延续第 5 轮既有认领,per 12:18Z 留档「届时由本座位重派」)


    Generated by Claude Code

  6. baozhoutao commented on Aug 8, 2026

    @baozhoutao
    Contributor

    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

  7. added a commit that references this issue on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions