Skip to content

chore(objectql): 退休 applyFormulaPlan 的零调用者 nowSnapshot 形参,并把 docstring 收窄到实际成立的范围 (#5699) - #5894

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-5699-retire-nowsnapshot-param
Aug 6, 2026
Merged

chore(objectql): 退休 applyFormulaPlan 的零调用者 nowSnapshot 形参,并把 docstring 收窄到实际成立的范围 (#5699)#5894
baozhoutao merged 2 commits into
mainfrom
claude/issue-5699-retire-nowsnapshot-param

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5699

结论:前提成立,走方向 (2)(退休零调用者形参 + docstring 收窄)

先在 origin/main 上核前提,三项全部复现:

即:该形参自诞生起只走 ?? new Date() 分支,是休眠代码。

没有发现即将到来的调用者:is:open 搜索 PR/issue 正文里的 nowSnapshot / nowSnap / applyFormulaPlan,除本单外零命中;没有在飞 PR 或已裁决需求要传它。因此按 PM 预裁取方向 (2),不退到 (3)。⛔ 方向 (1)(insert 传 nowSnap)未实现 —— 它会让写路径比读路径多一条确定性保证,是语义决策。

改了什么

  1. 签名:applyFormulaPlan(plan, records, execCtx) —— 删掉 nowSnapshot?: Date,函数体 const now = new Date();。私有函数、无公共 API 变化、行为不变(被删的分支从来不可达)。

  2. docstring 收窄。原文「The eval context mirrors applyFieldDefaults」只成立一半:

    • 成立的一半:上下文形状相同(同一组 key),所以 formuladefaultValue 共享一套表达式词汇;
    • 不成立的一半:两边各取各的 nowapplyFieldDefaults 拿的是 insert 的写前快照,applyFormulaPlan 自己读一次时钟 —— 因为 formula 是「记录被物化时」求值(读时,以及写响应上),不是「该行 default 被解析时」。

    docstring 现在把这条差异写明,并点名 applyFormulaPlannowSnapshot 形参零调用者 —— 同一次 insert 里 defaultValue 的 now() 与 formula 的 now() 观察到两个不同瞬时 #5699 是「要不要让两者共享瞬时」该被论证的地方,同时记下形参已按 enforce-or-remove(ADR-0049 对 spec 属性用的同一条反射)退休 —— 理由正是立单说的:形参看起来是活的,任何按它推理的人都会得出「两边已共享瞬时」的错误结论。

  3. 补确定性 pin(engine-write-formula-hydration.test.ts 新增 #5699 一组三条)。形参退休后,「一次调用一个 new Date()」是这个函数唯一的确定性来源,所以它必须被真正钉住,而不是靠一个形参的外观暗示。

反向验证 —— 三个方向,先预判再跑

⚠️ 必须如实说明的一点:本次改动本身拿不到「还原即变红」。这不是覆盖不足,而恰恰是立单要证明的事实 —— 被删的是死枝。三条探针分别预判、分别实测:

探针 预判 实测
A. 把形参原样加回去(nowSnapshot?: Date + ?? new Date(),无人传) 全绿 —— 死枝还原不可观察 全绿:Test Files 1 passed / Tests 21 passed
B. 把时钟读进逐次求值({ now: new Date(), ... } 放进内层循环) 两条 identity pin 变红 变红,且多带出一条:engine.test.ts 既有的 pins now once per find (#1979) 也红了(它是值比较,本次跑到了跨毫秒)。Tests 3 failed / 148 passed
C. 真去实现方向 (1)(把 nowSnap 一路传到水合点) 只有 tripwire 那条变红 正是如此:× the insert's defaultValue instant and the response formula's instant are INDEPENDENTexpected 2026-08-06T10:59:45.324Z not to be 2026-08-06T10:59:45.324Z // Object.is equality,其余 20 条全绿

A 是「模板预设的 before-green/after-red 在这里不可能成立」的如实记录;B 证明新 pin 可证伪;C 证明 tripwire 真的会在方向 (1) 落地时响 —— 也就是说,以后谁想走方向 (1),会被一条测试逼着把决定说出口,而不是悄悄改掉。

为什么 identity 而不是值比较:逐次 new Date() 在同一毫秒内产生的两个对象,值相等而对象不同。断言在 ExpressionEngine.evaluate 收到的上下文对象上做 ===,毫秒是否恰好相同就不再影响判定 —— B 里既有的 #1979 值比较测试这次红了,但它本来是可能侥幸绿的,新 pin 不依赖运气。

验证

pnpm --filter '@objectstack/objectql' test
  Test Files  127 passed (127)
        Tests  2087 passed (2087)

pnpm --filter '@objectstack/objectql' typecheck
  > tsc --noEmit          (无输出 = 通过)

node scripts/check-engine-double-contract.mjs
  check-engine-double-contract: OK — 64 pinned, 139 in the DEBT ledger, 2 exempt.

node scripts/check-nul-bytes.mjs
  check-nul-bytes: OK (scanned 5704 tracked text file(s); ... no raw ASCII control bytes).

npx eslint 两个改动文件   (无输出 = 通过)

行为不变性的证据就是 find / findOne / insert 水合三条路径的既有测试全绿 —— 上面 127 个文件、2087 条测试是在 git merge origin/main(dc6abfd,含 #5871 的 dispatch 迁移)之后重跑的。

新增测试没有引入任何新的 fake engine —— 三条 pin 复用 engine-write-formula-hydration.test.ts 既有的 stub driver 装置(它是 driver 替身,不是 engine 替身),所以 check:engine-double-contractassertEngineDeleteDispatch / assertEngineUpdateDispatch 要求在本 PR 无适用面,gate 也实测为绿。

未触碰的区域

按派发令:diff 只动 applyFormulaPlan 的定义与 docstring;update 单 id 门与 #5850 刚校准的三处注释零改动(git diff origin/main 只含三个文件:engine.ts 的 43 行、测试 147 行、changeset 36 行)。hydrateWriteFormulas 自己的 docstring 也没动 —— 新的 applyFormulaPlan docstring 已经显式覆盖写路径。

changeset

@objectstack/objectql patch。内部私有函数签名变化、无公共 API 变化,changeset 正文写明「行为不变 + docstring 收窄的边界在哪」,给发布说明一个可读的落点。


Generated by Claude Code

claude added 2 commits August 6, 2026 10:56
…rameter (#5699)

The fourth optional parameter `nowSnapshot?: Date` had exactly one effect,
`nowSnapshot ?? new Date()`, and not one of the three call sites (find,
findOne, and #5504's write-response hydration) ever passed it. Dormant from
birth, so it is removed rather than archived: a parameter that looks live makes
every reader conclude the caller can pin the instant.

Narrows the docstring's "mirrors applyFieldDefaults" claim to the half that
holds (same context shape, one expression vocabulary) and records the half that
does not (each side pins its own `now`, so an insert's `NOW()` default and its
`now()` formula are one driver round-trip apart), naming #5699 as where sharing
one instant would have to be argued.

Adds the determinism pins the parameter's appearance stood in for: one snapshot
per call across every row x every formula field, asserted by object identity so
a per-evaluation clock read fails even when the milliseconds agree, on both the
write and read paths, plus a tripwire on the two instants staying independent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Q7oc7ASjh8yxyS3Yz78We
@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:07am

Request Review

@github-actions github-actions Bot added the size/m label 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/objectql.

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

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/plugins/index.mdx (via @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx (via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql)

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 added documentation Improvements or additions to documentation tests tooling labels Aug 6, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review August 6, 2026 11:31
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit b9cc17d Aug 6, 2026
24 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5699-retire-nowsnapshot-param branch August 6, 2026 11:38
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.

applyFormulaPlannowSnapshot 形参零调用者 —— 同一次 insert 里 defaultValue 的 now() 与 formula 的 now() 观察到两个不同瞬时

2 participants