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

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 typecheckexit 0
pnpm --filter @objectstack/trigger-record-change testTest Files 5 passed / Tests 56 passed
pnpm --filter @objectstack/service-storage testTest Files 21 passed / Tests 283 passed
eslint --no-inline-config(三个改动文件)exit 0
pnpm check:nul-bytesPASS
pnpm check:engine-double-contractPASS
pnpm check:adr-anchorsPASS
pnpm check:published-filesPASS
pnpm check:type-check-coveragePASS

(@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

vercelBot commented Aug 6, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 6, 2026 11:23am

Request Review

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

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants

@hotlong@claude