Skip to content

docs(objectql): RUNTIME_OWNED_FIELD_TYPES 的 summary 排除理由改为与实现相符 (#6014) - #6119

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-6014-summary-comment-truth
Aug 7, 2026
Merged

docs(objectql): RUNTIME_OWNED_FIELD_TYPES 的 summary 排除理由改为与实现相符 (#6014)#6119
baozhoutao merged 2 commits into
mainfrom
claude/issue-6014-summary-comment-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #6014

纯注释订正,零行为变化:未触及 RUNTIME_OWNED_FIELD_TYPES 集合本身,未触及任何一行代码。

前提复核(在 origin/main 重验,单据是 lead 不是 spec)

单据前提成立,四条逐条实读:

前提 结论 证据
误述句逐字仍在 packages/objectql/src/validation/rule-validator.ts:568-571(改前),原文 "Deliberately NOT formula / summary: those are computed on read from a plan, never stored from the write payload, so there is no caller value to strip."
summary 是物理存储列 engine.ts recomputeSummaries()await this.update(desc.parentObject, { id: parentId, [desc.summaryField]: value }, ...) 写入
#6013 已落,insert 也写初值 commit d56bcdb45 在 main;initializeSummaryFields(engine.ts:4329)在 insert 路径 :5162 / :5167 调用
formula 那半句是对的 applyFormulaPlan(engine.ts:570)只按 FormulaPlanEntry[] 求值,summary 不在 plan 里 —— 所以 formula/summary 并列才是错的

补充一条单据没写、但让危险方向更具体的实测:strip 站点跑在 seed 之后initializeSummaryFields:5162,stripRuntimeOwnedFields:5276,而 suppliedPerRow(:5248)是 原始 caller payload。所以若把 summary 加进集合,caller 送 task_count: 42 时:42 被 strip 掉,0 也补不上(seed 正因为 caller 供了值而 continue),列落回 null —— 恰是 #6013 要消灭的状态,且写入仍报成功。

新旧措辞对照

(4 行,一段):

Deliberately NOT formula / summary: those are computed on read from a plan, never stored from the write payload, so there is no caller value to strip. Keep this set to types whose value is (a) persisted and (b) issued by the runtime.

(拆成四段,要点):

  1. formula 单独一段 —— 保留原判断,补上出处 applyFormulaPlan;
  2. summary 单独一段 —— 写明它 NOT computed on read,是 runtime 维护的存储列(recomputeSummaries 写入 + Field.summary 的 count 汇总:从未有过子记录的父行停在 NULL,删光子记录才变 0 —— 同一个「零」两种值,筛选 = 0 静默漏行 #5749 / PR fix(objectql): 新建父行时把 count/sum 型 summary 汇总初始化为 0 (#5749) #6013 的 insert seed),读路径直读该列,所以 ["task_count","=",0] 是库内比对、never-seeded 的 null 才会静默漏行;并明确点出 summary 同时满足 (a) persisted 和 (b) runtime-issued 却仍被排除 —— 即旧判据根本判不出它;
  3. 真实判据一段 —— 第三条 (c) 没有合法的 caller 供值:autonumber 满足(伪造业务标识、无人纠正),summary 不满足(派生缓存、下次子表写入自愈,且显式写初值是 fix(objectql): 新建父行时把 count/sum 型 summary 汇总初始化为 0 (#5749) #6013 明确支持的路径);
  4. 反向警告一段 —— DO NOT "fix the code to match this comment",连同上面那条 seed/strip 顺序的后果;
  5. 收尾判据从 (a)(b) 两条补成 (a)(b)(c) 三条

验证(如实)

这是注释级变更,没有反向验证肢可做 —— 删掉/还原注释不会让任何测试变色,任何"反向验证"输出都只能是编造,故不提供。实际证据是"零行为变化"+"文本已换":

$ pnpm --filter @objectstack/objectql typecheck
> tsc --noEmit                      -- 退出码 0,无输出

$ pnpm --filter @objectstack/objectql test -- --maxWorkers=2
 Test Files  131 passed (131)
      Tests  2168 passed (2168)
   Duration  87.77s

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

diff 面自证只动注释:

$ git diff --stat
 packages/objectql/src/validation/rule-validator.ts | 42 +++++++++++++++++++---
 1 file changed, 38 insertions(+), 4 deletions(-)

$ git diff -U0 | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -vE '^[+-] \* ' | grep -vE '^[+-]\s*$'
+ *
+ *
+ *
+ *                        -- 只剩 4 行空注释分隔行,无一行代码

$ git diff origin/main -- packages/objectql/src/validation/rule-validator.ts | grep -E "^[+-].*new Set\("
(no diff line touches the Set literal -- collection unchanged)

grep 证明旧误述已不在、新措辞在:

$ grep -n 'formula` / `summary' packages/objectql/src/validation/rule-validator.ts
(no match -- the joint formula/summary misstatement is gone)

$ grep -n "NOT computed on read\|COMPLETELY DIFFERENT reason\|never legitimately supplied" packages/objectql/src/validation/rule-validator.ts
572: * Deliberately NOT `summary` either — but for a COMPLETELY DIFFERENT reason,
574: * `summary` is NOT computed on read: it is a real stored column the runtime
605: * runtime, and (c) never legitimately supplied by a caller.

$ grep -n "RUNTIME_OWNED_FIELD_TYPES: ReadonlySet" packages/objectql/src/validation/rule-validator.ts
607:const RUNTIME_OWNED_FIELD_TYPES: ReadonlySet(string) = new Set(['autonumber']);   -- 集合原样

必答项

1. #6013 已落 —— 新措辞是否把「insert 也写初值」算进去了?

是。 新注释原文:"ObjectQL.recomputeSummaries writes it with an ordinary update(parent, { [summaryField]: value }) after any child write, and since #5749 / PR #6013 initializeSummaryFields also seeds it at parent INSERT." 且第三段进一步用它作论据(显式写初值是受支持的授权路径,initializeSummaryFields 特意保留 caller 供的值),第四段用 seed 与 strip 的先后顺序推出具体回归后果。#6013 = d56bcdb45,已在 main。

2. record-validator.ts 核对结论 —— 含不含同一误解?

不含,原文成立,未改。 packages/objectql/src/validation/record-validator.ts:550-551 原文:

// Remaining types (formula/summary/autonumber outputs, json/code payloads)
// are explicitly open per the spec contract — see field-value.zod.ts.

它讲的是 ADR-0104 的 value-shape 开放性(哪些值类别没有严格形状门禁),不是持久化/写路径归属 —— 整段上下文是 valueShapeStrictEffective / mediaStrictEffective 的 warn-first 判定。它对 summary 没有任何 "computed on read" 或 "never stored" 的主张。反向佐证:它把 summary 与 autonumber outputs 并列,而 autonumber 恰恰是 persisted + runtime-owned 的那一个 —— 这个分组与"summary 是存储列"是一致的,而非矛盾。故不动。

3. #5503 / preserveAudit —— 写下的理由与实现是否逐字相符?

逐字相符,四处代码位置(行号为本 PR 落地后):

  • 只针对 caller-supplied:rule-validator.ts:750 if (!suppliedKeys.has(name)) continue; // hook/middleware stamp — keep(insert 侧 stripRuntimeOwnedFields,:736);update 侧同形于 :700(stripReadonlyFields,:682)。
  • preserveAudit 豁免::751 if (preserveAudit && isPreservableUnderAudit(name, def)) continue;
  • 豁免范围:isPreservableUnderAudit(:828)= if (AUDIT_TIMELINE_FIELDS.has(name)) return true; return def.system !== true; —— 所以未标 system: true 的 summary 字段在 preserveAudit 下确实会被保留。新注释就是这么写的("kept by isPreservableUnderAudit, since a summary field is not system: true"),没有夸大成"preserveAudit 也救不了"。
  • isSystem 整趟跳过:engine.ts:5271 if (!opCtx.context?.isSystem) { 包住整个 strip 循环。

即:受损的是既非 isSystem 也非 preserveAudit 的普通导入/种子写入 —— 新注释正是这样限定的。

无 changeset

零生成物、零行为变化、无任何用户可见 surface(不动键、类型、.describe()、导出),本 PR 不发布任何东西,故不加 changeset,改用 skip-changeset 标签。依据:.github/workflows/pr-automation.yml 的 changeset gate 自身把"releases nothing"一类明列为 PREFERRED 走标签;AGENTS.md:763 只要求 feature / functional improvement 出 changeset。同形先例:PR #5964(docs(spec): HookEvent 枚举注释不再声称批量写把行级谓词放在 input,纯 TSDoc 订正)已合并,标签 skip-changeset,无 changeset。

范围

未越界:未改 content/docs/releases/,未 rebase,未 force-push,未动集合与任何行为代码。本轮未发现需另行开单的范围外缺陷。


Generated by Claude Code

claude added 2 commits August 7, 2026 02:26
`RUNTIME_OWNED_FIELD_TYPES` 上方的 TSDoc 把 `formula` 和 `summary` 并列成
「computed on read from a plan, never stored from the write payload」。formula
那半句对(`applyFormulaPlan` 读时按 plan 求值);summary 那半句是错的:roll-up
summary 是实打实的物理存储列 —— `recomputeSummaries()` 用
`update(parent, { [summaryField]: value })` 写入,#5749 / PR #6013 之后
`initializeSummaryFields` 在 insert 时也落初值,读路径直读该列(#5749 的
「筛选静默漏行」正因为它是库内列)。

危险方向具体:下一个作者按注释字面「修正代码以匹配注释」,把 summary 加进集合,
带汇总初值的历史导入/种子写入就会被静默 strip。而且 strip 站点(engine.insert
:5276)跑在 seed 之后、且只认原始 caller payload,所以 caller 送了
`task_count: 42` 时,42 被 strip 掉、0 也不会补(seed 正因为 caller 供了值而跳过),
列落回 null —— 恰是 #6013 要消灭的状态,且写入仍报成功。

改注释,不改行为:formula / summary 拆成两段,写明 summary 是 persisted +
runtime-maintained 但故意不 strip,真实判据是第三条「没有合法的 caller 供值」——
autonumber 满足(伪造业务标识且无人纠正),summary 不满足(派生缓存,下次子表写入
自愈,且显式写初值是 #6013 明确支持的路径)。成员判据补成 (a)/(b)/(c) 三条。

未触及 `RUNTIME_OWNED_FIELD_TYPES` 集合本身,未触及任何行为代码。

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

Request Review

@github-actions github-actions Bot added the size/s label Aug 7, 2026
@baozhoutao baozhoutao added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 7, 2026 — with Claude
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql.

14 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/runtime-services/examples.mdx (via packages/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.

Copy link
Copy Markdown
Contributor Author

ACCEPT(执行席 PM 验收)

核过:① diff 单文件纯注释(+38/-4),RUNTIME_OWNED_FIELD_TYPES 集合字面量零改动,objectql 全量 2168 绿 + typecheck 绿证零行为变化;② 新措辞按裁定拆段:formula 与 summary 分开、判据补第三条 (c)「无合法 caller 供值」、反向警告段把 seed→strip 顺序导致的具体回归写成可验证的推导链(含 #6013 的 insert 初值与 preserveAudit 豁免边界的逐字核对)—— 这段散文现在是可查证的,不再要求下一个读者「只能相信」;③ record-validator.ts 核过不含同一误解、原文成立未改,证据在 PR 正文;④ 反向验证如实报「注释级无肢可做」而非套模板造证据 —— 正确;⑤ skip-changeset 走 #5964 先例,标签已读回;Check Changeset 首跑红为已知 #5580 竞态(后两跑 skipped 即豁免生效),记录为信息不作裁决依据。

翻 ready + auto-merge,进队列。


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review August 7, 2026 02:32
@baozhoutao
baozhoutao enabled auto-merge August 7, 2026 02:32
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 61122e8 Aug 7, 2026
29 of 30 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-6014-summary-comment-truth branch August 7, 2026 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rule-validator 里 RUNTIME_OWNED_FIELD_TYPES 的排除理由对 summary 是错的:summary 是「存储列」不是「读时计算」

2 participants