Skip to content

refactor(trigger-record-change): 摘掉 input.doc 这条没有生产者的防御性 alias 读 (#5671) - #5908

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-5671-drop-input-doc-alias
Aug 6, 2026
Merged

refactor(trigger-record-change): 摘掉 input.doc 这条没有生产者的防御性 alias 读 (#5671)#5908
hotlong merged 1 commit into
mainfrom
claude/issue-5671-drop-input-doc-alias

Conversation

@hotlong

@hotlong hotlong commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5671

背景

record-change-trigger.tsbuildContext()input.data 之后又兜了一条 input.doc。这条分支在任何引擎路径上都不可达:ObjectQL 的写事件上下文一律把载荷拼在 data 上(insert / update),before/afterDelete 两者都不带、直接落到 previous

该 alias 当初留下的唯一依据,是旧的 HookContext.input 契约表把 insert 写成 { doc, options } —— 那张表本身是错的,已由 PR #5668(#5273)改成 { data, options } 并加了 pin。依据消失后,按 PD #12 删掉,而不是继续养一套第二事实契约。

行为零变化 —— 该分支不可达,真值 pin 在 packages/objectql/src/hook-input-shape-contract.test.ts(「insert carries data — never doc」)。本 PR 只引用该 pin,未改动它。

改动

  1. packages/triggers/trigger-record-change/src/record-change-trigger.ts —— 删掉 input.doc 三元限支与类型里的 doc?: 声明,只读 input.data;局部变量 inputDoc 随之更名 inputData(纯局部,不导出),注释改写为指向引擎真值与那条 pin。
    • 保留了同一处 cast 里的 id?: unknown:它虽然在本函数内未被读,但是真的(update 事件上引擎确实绑 input.id)。删的是那个假的键,不是顺手清扫。
  2. record-change-trigger.test.ts —— hookCtx fixture 原本拼的就是 doc,正是它让这条死分支在测试里保持「活着」(validateOrgAxisRedLines 读的 sharing-rule 键是 spec 拒收的:ADR-0105 D6 ① 在 criteria 路径上从不触发 #4984 那一族:fixture 照着错契约表写,于是死逻辑一直绿)。按引擎真值 re-spell 成 data,并把原「falls back to input.doc」用例整条替换为一对承重 pin。
  3. packages/services/service-storage/src/attachment-lifecycle.test.ts —— insert fixture 同样按引擎真值 re-spell(fixture 三分法里的 re-spell 档)。
  4. changeset(patch)。

反向验证 —— 方向先判,再测

这条链的规范键 data 排在读取链首位,所以「把删掉的限支放回去、看新用例转红」对正向用例并不成立:正向用例喂 data,alias 复活与否它都绿。承重的是反向那条 —— 喂一个只拼 doc 的上下文,断言它不播种 record。

doc 限支临时放回源码后实测:

 × does NOT read a `doc` alias off input — no engine path produces that key
 AssertionError: expected { status: 'done' } to deeply equal {}
 Test Files  1 failed | 4 passed (5)
      Tests  1 failed | 55 passed (56)

正好 1 红,且正是反向那条;正向用例如预期保持绿。这一对是刻意配的,注释里也写明了哪条承重。恢复后全绿。

验证

lint.yml / ci.yml 逐条枚举跑了 diff 可能影响的门,全程持容器级验证锁 + --max-old-space-size=4096 + --filter 范围化:

结果
pnpm --filter @objectstack/trigger-record-change typecheck exit 0
pnpm --filter @objectstack/trigger-record-change test Test Files 5 passed / Tests 56 passed
pnpm --filter @objectstack/service-storage test Test Files 21 passed / Tests 283 passed
eslint --no-inline-config(三个改动文件) exit 0
pnpm check:nul-bytes PASS
pnpm check:engine-double-contract PASS
pnpm check:adr-anchors PASS
pnpm check:published-files PASS
pnpm check:type-check-coverage PASS

(@objectstack/service-storagetypecheck script —— 已在 check-type-check-coverage.mjs 的台账里,该门 PASS。)

两个必答项

1. 全仓 doc 读者是否归零?——「hook input 形状」这一面归零,但另有三处同族,已另立单 #5906

改动后 input.doc读者全仓只剩下注释文本,零代码读点在 trigger-record-change。但同一次 grep 翻出另外三处,都不在本 issue 的派发作业面内,按 PD #10 另立 #5906(unassigned,finding),未在本 PR 修:

  • packages/services/service-storage/src/attachment-lifecycle.ts:174 —— ctx?.result ?? ctx?.input?.doc ?? ctx?.input?.data,注意次序与本 PR 相反,docdata 前面;因 doc 恒 undefined 而每次穿到 data,纯死码。
  • packages/plugins/plugin-sharing/src/primary-bu-projection.ts:80 —— (ctx?.input?.data ?? ctx?.input?.doc)?.user_id,doc 限支不可达。
  • packages/runtime/src/sandbox/body-runner.ts:308 —— 读的是顶层 ctx.doc / ctx.previousDoc,不是 ctx.input.doc,严格说不属于本次 grep 的目标键;但 HookContext 只声明 input / result / previous,这两个键同样无生产者,同族,一并列入 同源:另外三处 hook context 上的 doc / previousDoc 防御性 alias 读也没有生产者(#5671 全仓 grep 的剩余部分) #5906 便于一次判。

另需提示:hook-input-shape-contract.test.ts:23 的文件头注释写着「trigger-record-change still carries a defensive input.doc alias read for that reason — filed separately, not fixed here」,本 PR 合入后这句即过时。派发口径明确 ⛔ 不改该 pin 文件,故此处未动,留给该文件的座位顺手修一句。

2. 对 #5886(spec fixture re-spell)的影响:完全无影响,既不变简单也不变难。

本 PR 的 diff 里没有任何 packages/spec 文件(已核:git status 中 spec 文件数 = 0)。#5886 的作业面是 packages/spec/src/data/hook.test.ts:422/474/490 那一组 fixture,喂的是开放形状的 z.record,与本包的消费端读取逻辑无耦合 —— 两边既不共享 fixture,也不互为前提。#5886 该怎么 re-spell 仍怎么 re-spell。

范围

⛔ 未触 packages/spec 任何文件;⛔ 未改 hook-input-shape-contract.test.ts;⛔ 未触 content/docs/releases/


Generated by Claude Code

…5671)

`buildContext()` 里的 `inputDoc` 三元把 `input.data` 之后又兜了一条
`input.doc`。该分支在任何引擎路径上都不可达:ObjectQL 的写事件上下文一律把
载荷拼在 `data` 上(insert / update),before/afterDelete 两者都不带、直接落
到 `previous`。真值由 objectql 的 `hook-input-shape-contract.test.ts` 钉住
(「insert carries `data` — never `doc`」,#5273 / PR #5668)。

该 alias 当初留下的唯一依据是旧的 `HookContext.input` 契约表把 insert 写成
`{ doc, options }` —— 那张表本身是错的,已由 PR #5668 改成 `{ data, options }`。
依据消失后按 PD #12 删掉,而不是继续养成第二套事实契约。

行为零变化(该分支不可达)。同时:
- `record-change-trigger.test.ts` 的 `hookCtx` fixture 原本拼的是 `doc`,正是
  它让这条死分支在测试里保持「活着」;按引擎真值 re-spell 成 `data`。
- 原「falls back to input.doc」用例整条替换为一对承重 pin:正向钉
  `input.data` 播种 record;反向钉「只拼 `doc` 的上下文不播种」—— 后者才是
  承重的那条(`data` 在读取链首位,alias 限支复活时正向用例仍绿)。
- `attachment-lifecycle.test.ts` 的 insert fixture 同样按引擎真值 re-spell。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 11:23am

Request Review

@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/trigger-record-change.

1 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/releases/implementation-status.mdx (via @objectstack/trigger-record-change)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

trigger-record-changeinput.doc 防御性 alias 读的是一个没有生产者的键(引擎只建 input.data)

2 participants