Skip to content

fix(seed-loader): roll-up summary 重算耗尽重试后改记 error 并计入结果对象 (#4998) - #5062

Merged
xuyushun441-sys merged 3 commits into
mainfrom
claude/issue-4998-seed-summary-stale-loud
Aug 4, 2026
Merged

fix(seed-loader): roll-up summary 重算耗尽重试后改记 error 并计入结果对象 (#4998)#5062
xuyushun441-sys merged 3 commits into
mainfrom
claude/issue-4998-seed-summary-stale-loud

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4998

维护者裁决 A + B 全部落地。

行为没变的部分

ERR_SUMMARY_RECOMPUTE恢复逻辑一字未动(framework#3147):记录确实已经写入,重写会产生重复,所以照旧返回 e.written。这个 PR 只改后果的等级可发现性

A —— 提到 error,文案带后果与修复动作

roll-up summary 是落盘的派生列(挂在 parent 记录上)。重算耗尽重试之后,库里明细行和汇总它们的那一列互相矛盾,而且不会自愈——要等到后续某次写入恰好碰到同一个 parent,seed 之后未必再有。这正是 AGENTS.md「Degradation log levels」#4632 说的那一类:持久化状态与运行时状态不一致,而外表一切正常

原来整件事只有一行 warn:不点名对象、不计数、success 仍是 true。现在按 #4632 的约定记 error,一行里同时给出:

  • 后果:点名被 seed 的对象和具体陈旧的列(roll_account.total_billed),说明明细与汇总不一致、无法自愈、而且这次 seed 依然报 success;
  • 修复动作:修掉下面附着的 recompute 原始错误后重跑 seed,或对受影响的 parent 记录触发任意一次写入以强制重算;
  • 原始 cause 通过 logger 的 error 参数结构化附上,每条 failure 的 parentId/field/error 进 meta。

B —— 计入结果对象;success 保持 true

新增 SeedLoadResult.summariesStaleSeedLoaderResult.summary.totalSummariesStale,与 referencesDropped / totalReferencesDropped 逐字对齐(z.number().int().min(0).default(0))。后者正是为低一层的同一形状而设的:「行写进去了,由它派生的东西丢了」。日志不是调用方能 branch 的东西,计数才是。

success 判定为保持 true,依据是三个消费方的实测证据,不是偏好:

消费方 若翻转 success 会怎样
packages/metadata-protocol/src/protocol.ts seed-apply 对外返回 success: falseerrors 为空——一个说「失败了」却结构上说不出原因的机器可读面(违反 AGENTS.md「Machine-readable surfaces must not lie」)
packages/runtime/src/app-plugin.ts 启动横幅 走失败分支打印 0 dropped record(s) and 0 error(s)——正是 #3932 注释里骂过的「true and useless」那一行
packages/runtime/src/domains/packages.tscloud-connection marketplace 安装 每一行都写成功的安装被判失败

success 回答的是「行落地了吗」,答案是落地了;信号由新计数承载,想把陈旧汇总当致命的调用方读 summary.totalSummariesStale > 0

门禁那一段:原方案是假保护,已修

原计划是把 fn() 抽成具名私有方法 performSeedWrite 并登记进 DURABILITY_CRITICAL_CALLEES。抽取做了,但只做这一步登记进去也是不生效的,实测确认:

scripts/check-durability-degradation-log-level.mjs 对任何含 throw 的 catch 一律放行(if (rethrows || loud.length > 0) return;)。而本 seam 的形状恰恰是「命中 ERR_SUMMARY_RECOMPUTE 就恢复、其余一律 rethrow」——于是把日志改回 warn,门禁照样绿。那就是一条看着像保护、实际永远不会 fire 的账本条目。

所以同时收紧了规则:只有无条件 rethrow 才算豁免;一个分支恢复、另一个分支 rethrow 的 catch,其恢复分支和别的降级没有区别,必须响。

实测口径:

  • 全仓 11 个 durability seam,收紧前后判定无一改变(全部 loud 或 rethrow),不需要任何 baseline 条目;
  • 把本 seam 的 error 改回 warn:门禁 exit 1,并打印该 callee 登记的后果文案;改回来 exit 0;
  • 门禁自带 self-test 从 10 例加到 13 例,新增三例分别钉住「部分恢复 + warn 必须报」「同形状 + error 必须过」「条件分支里全部 rethrow 仍算豁免」。

测试

新增 packages/metadata-protocol/src/seed-loader-summary-stale.test.ts(7 例),按 #5001seed-loader-deferred-failure.test.ts 立的写法:

变异钉子:把实现改回「warn + 不计数」,新测试 4 例转红,门禁同时 exit 1。

seed-loader-retry.test.ts 里那句 summary recompute failure is a warning, not an error 的 describe 标题已按事实改写(断言未变:陈旧汇总依旧不是写入错误,totalErrored 仍为 0)。

生成物落地纪律

推送前:git merge origin/main(未 rebase)→ 把 .gitattributes 里全部 merge=os-regen 路径 git checkout origin/main -- 重置 → 重建 spec → 整体重生成(check:generated --fix,从不文本合并)。核对结果:本分支在这些生成物上相对 origin/main 的 delta 恰好是自己的两行新增,#5043(chart/theme 未知键批次)落地的兄弟条目原样健在(ui/Chart 44 条、ui/Theme 12 条,两个 reference 页未动)。

未做的事(有意)

本地门禁

28 条 check:* 全跑:27 过。唯一失败 check:objectui-pin-fresh(.objectui-sha 相对 objectui main 已陈旧)与本改动无关——该文件未被触碰,属仓库既有状态。check:engine-double-contract 通过,无需新增基线条目。


🤖 Generated with Claude Code

https://claude.ai/code/session_01NrmBxj8rK2uGCnh9aipjwX


Generated by Claude Code

Claude and others added 2 commits August 4, 2026 01:21
…counted (#4998)

The ERR_SUMMARY_RECOMPUTE recovery is unchanged (framework#3147: the rows WERE
written, re-writing them would duplicate). What changes is the rank of the
consequence and its detectability.

- `error`, not `warn` (#4632): a roll-up summary is a persisted DERIVED column,
  so exhausting its recompute retries leaves the detail rows and the column
  summarizing them disagreeing in the database, with nothing to self-heal it.
  The line names the seeded object and the stale column, states the consequence
  (including that the seed still reports success) and the remedy, and carries
  the original cause.
- Counted: `SeedLoadResult.summariesStale` / `summary.totalSummariesStale`,
  mirroring `referencesDropped` / `totalReferencesDropped`. `success` stays
  `true` — it answers "did the rows land", and they did.
- The guarded write is extracted as `performSeedWrite` and registered in
  `DURABILITY_CRITICAL_CALLEES`, and the gate no longer excuses a catch that
  rethrows on one branch while recovering on another — without that, the ledger
  entry could never have fired.

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

vercel Bot commented Aug 4, 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 4, 2026 2:02am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/metadata-protocol, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol, packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/metadata-protocol, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/metadata-protocol, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

…d-summary-stale-loud

Conflict: scripts/check-durability-degradation-log-level.mjs — both sides
appended a DURABILITY_CRITICAL_CALLEES entry at the same list position.
Resolution keeps BOTH: this branch's `performSeedWrite` (#4998) and #5025's
`dropPromotedDraftRow` (#4981), with the seed-loader callees kept adjacent.

Verified on the merged state: #5025's seam is unaffected by this branch's
tightening of the rethrow rule. Its catch contains no `throw` at all, so
`rethrows` is false and the "only an unconditional rethrow excuses the seam"
change cannot apply to it — it is judged on log level exactly as before, and
reports loud via console.error in draftDrainVerdict(). Gate: 12 seams, all loud
or rethrowing, exit 0; self-test 13/13.

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

Copy link
Copy Markdown
Contributor Author

#5025 的同文件串行化已完成(HEAD 79ff030)

#5025 合入 main 后同样改了 scripts/check-durability-degradation-log-level.mjs,已按串行化流程处理(git merge origin/main,未 rebase)。

冲突与解决:唯一冲突就在该脚本,双方在 DURABILITY_CRITICAL_CALLEES 的同一位置各追加了一条,属纯增量。解决保留两条——本分支的 performSeedWrite(#4998)与 #5025dropPromotedDraftRow(#4981)——并把 seed-loader 系的三条 callee 排在一起。除该脚本外无其他冲突。

关键验证:#5025 的 seam 在收紧后的门禁下依然通过,未放宽门禁、未动 sys-metadata-repository.ts

这在结构上是必然的:#5025 的 catch 是 catch (error) { draftDrainFailed = this.draftDrainVerdict(...); },整块没有 throw,所以 rethrows 恒为 false,而本分支的收紧是 propagatesAlways = rethrows && !catchRecovers(...)——对它恒等于 false,判定路径与收紧前完全一致,仍旧只看日志等级(经 draftDrainVerdict()console.error 判为 loud)。收紧只可能改变「一个分支恢复、另一个分支 rethrow」这一种形状的判定。

Durability-critical catch seams found: 12
  …
  packages/metadata-protocol/src/seed-loader.ts:1294  guards performSeedWrite()@1293
      → recovers on one branch, loud (error@1323 via reportStaleSummaries())
  packages/metadata-protocol/src/sys-metadata-repository.ts:707  guards dropPromotedDraftRow()@706
      → loud (error@1320 via draftDrainVerdict())
  …
✓ durability-degradation log levels: 12 durability-critical catch seam(s), all loud or rethrowing.
GATE EXIT CODE = 0        self-test: 13 case(s) passed

生成物纪律(纪律 A)重跑:.gitattributes 全部 merge=os-regen 路径 git checkout origin/main -- 重置 → 重建 spec → 整体重生成。本分支在这些生成物上相对 origin/main 的 delta 仍恰好是自己的两行新增;兄弟条目全部健在——ui/Chart 44 条、ui/Theme 12 条(#5043)、drilldown 1 条(#5012)。重生成结果与已提交内容逐字节一致,故无新增提交。

合并态复验:@objectstack/metadata-protocol 36 files / 319 tests 全过(含 #5025 新增的 sys-metadata-repository.draft-drain.test.ts 与本 PR 的 seed-loader-summary-stale.test.ts);@objectstack/spec 301 files / 7689 tests 全过;spec + metadata-protocol + runtime typecheck 全过;28 条 check:* 27 过,唯一失败仍是 check:objectui-pin-fresh(.objectui-sha 本分支零触碰,属仓库既有状态)。


Generated by Claude Code

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 4, 2026 02:28
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit c5a5996 Aug 4, 2026
25 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-4998-seed-summary-stale-loud branch August 4, 2026 02:40
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 protocol:data size/l tests tooling

Projects

None yet

2 participants