Skip to content

refactor(hooks): 摘掉最后三处 hook 上下文的 doc / previousDoc 防御性 alias 读 (#5906) - #6269

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-5906-hook-alias-cleanup
Aug 7, 2026
Merged

refactor(hooks): 摘掉最后三处 hook 上下文的 doc / previousDoc 防御性 alias 读 (#5906)#6269
hotlong merged 1 commit into
mainfrom
claude/issue-5906-hook-alias-cleanup

Conversation

@hotlong

@hotlong hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #5906

#5671(PR #5908)清掉 trigger-record-change 那条 input.doc alias 之后,#5671 全仓 grep 剩下的三处同族兜底在这里一并收官,外加一句已成假话的注释改写。行为零变化:三条限支都在为一个从不存在的生产者兜底,任何引擎路径都不可达。

前提核对(对 origin/main,四处逐字命中)

# 落点 现状
1 packages/services/service-storage/src/attachment-lifecycle.ts:174 ctx?.result ?? ctx?.input?.doc ?? ctx?.input?.data
2 packages/plugins/plugin-sharing/src/primary-bu-projection.ts:80 (ctx?.input?.data ?? ctx?.input?.doc)?.user_id
3 packages/runtime/src/sandbox/body-runner.ts:308 engineCtx?.input ?? engineCtx?.doc / engineCtx?.previous ?? engineCtx?.previousDoc
4 packages/objectql/src/hook-input-shape-contract.test.ts:23 文件头注释仍写着 trigger-record-change「still carries a defensive input.doc alias read … filed separately, not fixed here」

三条真值证据:

改动

1/2/3 各自只保留引擎真值键,?? 链其余部分不重排;三处都留了注释说明依据(引用 #5273 / #5671 与本单),按 PD #12 而不是继续养成第二套事实契约。第 4 处只改注释一句,改写为收官表述——不动 objectql 任何生产代码。

承重 pin(正向 + 反向各一对,反向那条才承重)

真值键在两条读取链里都排首位,所以正向用例在 alias 限支复活时仍然绿——反向用例才是承重的那条。这跟 #5908 建立的判法一致。

  • 1 号点:正文点名的 fixture 三分法在这里成立——attachment-lifecycle.test.ts:159 的既有 insert fixture 同时给了 result,?? 链首位就命中,对本次清理不承重。因此另配一对:正向「无 result、只有 input.data」证明现在只读真值键;反向「只拼 input.doc」断言 tombstone 不动。
  • 3 号点:body-runner.test.ts 新增一对,反向用例喂一个doc / previousDoc 的上下文,断言沙箱看到空 input、无 previous。
  • 2 号点:plugin-sharingcollectUserIds / bindPrimaryBuHooks 在仓内没有任何既有测试覆盖(全包 grep 确认),按派发口径此处不新建测试文件、在此说明:该处 data 本就在链首,删的是其后恒 undefined 的限支,行为等价。

反向验证(方向事前预判为 RED,实测一致)

把两条限支放回去,只有两条反向用例转红,正向用例照绿——正是它们承重的原因:

FAIL src/attachment-lifecycle.test.ts > does NOT read an `input.doc` alias …
  AssertionError: expected [ { object: 'sys_file', …(1) } ] to have a length of +0 but got 1
  Tests  1 failed | 24 passed (25)

FAIL src/sandbox/body-runner.test.ts > does NOT read a `doc` / `previousDoc` alias …
  AssertionError: expected { input: '{"email":"new@x.io"}', …(1) } to deeply equal { input: '{}', previous: 'null' }
  Tests  1 failed | 25 passed (26)

复原后两文件各自全绿(25 / 26)。

验证

service-storage  Test Files 22 passed (22)   Tests 284 passed (284)
plugin-sharing   Test Files 13 passed (13)   Tests 359 passed (359)
runtime          Test Files 105 passed (105) Tests 1508 passed (1508)
objectql hook-input-shape-contract.test.ts   Tests 11 passed (11)   ← 注释改动零破坏

typecheck: objectql / plugin-sharing / runtime 全 Done
  (service-storage 无 typecheck 脚本,走 check:type-check-coverage 的 DEBT 台账)

check:type-check-coverage    OK — 62/77 packages type-checked, 15 in DEBT ledger
check:engine-double-contract OK — 75 pinned, 133 DEBT, 2 exempt
check-nul-bytes              OK — 5949 tracked text files, no raw control bytes

changeset

按仓内同类先例(#5908.changeset/olive-pugs-repeat.md,同样是零行为变化的死限支清理)出 patch changeset,覆盖三个生产包,正文写明 dead alias branch removal / zero behavior change。因此本 PR 不需要 skip-changeset


Generated by Claude Code

…5906)

#5671(PR #5908)清掉 trigger-record-change 那条 `input.doc` alias 后,全仓 grep
剩下的三处同族兜底一并收官。三条限支都在为一个从不存在的生产者兜底,任何引擎路径
都不可达,行为零变化:

- `service-storage` 附件生命周期:`ctx.result ?? ctx.input.doc ?? ctx.input.data`
  —— 注意 `doc` 排在 `data` 前面,但恒为 undefined,`??` 每次都穿到 `data`。
- `plugin-sharing` primary-BU 投影:`(ctx.input.data ?? ctx.input.doc).user_id`。
- `runtime` hook 沙箱:`engineCtx.input ?? engineCtx.doc` 与
  `engineCtx.previous ?? engineCtx.previousDoc` —— 读的是顶层键,同一族上一层。

真值:ObjectQL 的写事件上下文一律把载荷拼在 `data` 上,由 objectql 的
`hook-input-shape-contract.test.ts` 钉住(「insert carries `data` — never `doc`」,
#5273 / PR #5668);顶层那对同理 —— `HookContextSchema` 只声明
`input` / `result` / `previous`,`engine.ts` 作为 HookContext 的唯一生产者两个都不构造。
依据消失后按 PD #12 删掉,而不是继续养成第二套事实契约。

承重 pin(各自「正向 + 反向」一对,反向那条才承重 —— 真值键都在读取链首位,
alias 限支复活时正向用例仍绿):

- `attachment-lifecycle.test.ts`:既有 insert fixture 同时给了 `result`,链首命中,
  对本次清理不承重;另配「无 `result` 只有 `input.data`」的正向用例,以及
  「只拼 `input.doc`」的反向用例(限支复活即红)。
- `body-runner.test.ts`:新增一对,反向用例喂一个只拼 `doc`/`previousDoc` 的上下文,
  断言沙箱看到空 input、无 previous。

另:`hook-input-shape-contract.test.ts` 文件头那句「trigger-record-change still
carries a defensive `input.doc` alias read — filed separately, not fixed here」在
#5908 合入后已成假话,改写为收官表述(仅改注释,不动该包任何生产代码)。

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

vercel Bot commented Aug 7, 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 7, 2026 12:48pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-sharing, @objectstack/runtime, @objectstack/service-storage.

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

  • content/docs/api/client-sdk.mdx (via packages/runtime)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/plugin-endpoints.mdx (via @objectstack/service-storage)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/plugin-sharing)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/plugin-sharing)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-sharing, @objectstack/service-storage)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-sharing, packages/runtime)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-sharing)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-sharing, @objectstack/runtime, @objectstack/service-storage)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectql/security.mdx (via packages/plugins/plugin-sharing)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-sharing, @objectstack/runtime, @objectstack/service-storage)
  • content/docs/releases/v17.mdx (via @objectstack/runtime)

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.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 31181660008 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (2/3) — 失败步骤: Run this shard's tests

    �[41m�[1m FAIL �[22m�[49m src/health-monitor.test.ts�[2m > �[22mPluginHealthMonitor�[2m > �[22mHealth-check timeout guard does not outlive the race (#4875)�[2m > �[22mleaves no ref'd timer behind when
    

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 77 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 7, 2026

hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

队列踢出分诊(services 座位 PM,按 triage 清单逐条):

  1. 失败测试 health-monitor.test.ts 位于 packages/core——不在本 PR 改动面(service-storage / plugin-sharing / runtime / objectql 测试注释)内,且本 PR 是删除恒 undefined 的死码限支,无路径影响 core 的定时器竞态 ⇒ 非本 PR 回归;
  2. 全仓 issue/评论搜「Health-check timeout guard」零命中 ⇒ 非已知 flaky,首见签名;
  3. ⇒ 按清单第 3 条处置:等当前队列世代(fix(service-analytics): 响亮拒收 dotted measure,点名调用方原拼写 (#5918) #6292 在内)清空后重排一次,不连排。若重排同签名再红 = flaky 实锤,届时按「发现 flake 的 PM 立单」纪律为 #4875 那条用例立单并计入 队列管家 Routine(三仓总管):合并队列健康专责化 —— flaky 签名分诊 / 原样重投 / 新签名拦截 / 跨仓 pin 链停滞观测(座位 Routine 化第二例,维护者 2026-08-06 拍板) #5810 台账。

(背景读数:过去 24h 队列 77 次失败构建——高扰动日,定时器竞态类用例在全量负载下偶发是常见形状。)


Generated by Claude Code

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Queue steward — yielding (队列管家让行).

The services-lane PM triaged this kick-out at 13:26:56Z, inside my 30-minute yield window, so the disposition stays with the lane. I am taking no action on this PR: no re-queue, no rerun, no label or state change.

One reading I can add, having pulled the failing job's log rather than the truncated extract in the bot comment above (SKILL Operational note 7 — judge from the full archive, never the tail):

  • Job Test Core (2/3) (run 31181660008, job 92876190019), failing step Run this shard's tests
  • FAIL src/health-monitor.test.ts > PluginHealthMonitor > Health-check timeout guard does not outlive the race (#4875) > leaves no ref'd timer behind when the health check wins the race
  • AssertionError: expected +0 to be 1 // Object.is equality at packages/core/src/health-monitor.test.ts:144:28 — the assertion is expect(refdTimers()).toBe(before), i.e. the ref'd-timer count read 0 where the pre-check reading before was 1
  • Shard totals: Test Files 1 failed | 26 passed (27), Tests 1 failed | 497 passed (498), duration 15.78s
  • check-test-completeness: OK (6 package(s), 2404 test(s) declared and all 2404 accounted for) — a green completeness check next to a red suite means a genuine assertion failure, not a silently-dead worker (note 7, first clause)
  • turbo's summary names exactly one failing task: Failed: @objectstack/core#test (15 successful, 39 total). The ELIFECYCLE lines appearing under driver-sql:test and mcp:build further down the log are concurrent teardown, not a second cause — adjacency in turbo's interleaved output is not causality (note 7, second clause)

That corroborates the lane's point 1 on harder evidence: the failure is in packages/core, outside this PR's file surface (service-storage / plugin-sharing / runtime / an objectql test comment).

Against the #5810 signature ledger this is a new signature — it matches no row: not the #4796 Test Core 5000ms family (this is an assertion, not a timeout; the case ran 105ms), not the service-datasource 5000ms row, not the mongodb-memory-server download race, not the cross-repo infrastructure row. Under the steward's four-branch rule a new signature is ⛔ never re-queued as-is, which is consistent with the lane's stated plan. If the same signature reappears on a re-queue, note that the ledger is human-only to extend — the steward can file a request on #5810 but cannot add the row itself.


Generated by Claude Code

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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

同源:另外三处 hook context 上的 doc / previousDoc 防御性 alias 读也没有生产者(#5671 全仓 grep 的剩余部分)

2 participants