Skip to content

fix(metadata-protocol): seed-loader 中「计为错误」的失败一律记 error,不再自相矛盾地记 warn (#4729) - #5001

Merged
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-4729-seed-loader-loud-failure
Aug 3, 2026
Merged

fix(metadata-protocol): seed-loader 中「计为错误」的失败一律记 error,不再自相矛盾地记 warn (#4729)#5001
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-4729-seed-loader-loud-failure

Conversation

@xuyushun441-sys

@xuyushun441-sys xuyushun441-sys commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #4729

问题

packages/metadata-protocol/src/seed-loader.ts 的 pass-2 延迟引用回填,catch 上方的注释白纸黑字写着这次失败 "must be a reported, counted error, never a silent warning",紧跟着的调用却是 this.logger.warn

公允地说,它并不完全静默:recordDeferredError(...) 把它计入 allErrors,success 会变 false。但日志行本身是一次 seed 在宿主控制台上留下的唯一痕迹,而 warn 正是 #4420 证明没人读的那一级 —— 后果(引用停在 NULL、循环关系半写入)则一个字都没说。计数与日志级别应当一致,这是本单的判据。

issue 正文里的「本轮因包冻结未修」已失效:#4556 的冻结今晨关闭,同包 #4980、同文件 #4836 今天都已合入,本 PR 基于当前 origin/main

改了什么

1. 主接缝提到 error,并补齐一条 error 该有的两件东西(AGENTS.md → "Degradation log levels",参照 #4823 / #4460 的文案形态):

  • 后果:对象.字段 在具名记录上停在 NULL;行本身已经种下,所以每一个行计数都是干净的;循环关系处于 HALF-WRITTEN 状态,并点名它本该指向的 目标对象.目标字段 = '自然键';
  • 修复动作:没有任何东西会重试 —— 修掉下面那个写入错误(超出重试预算的瞬时故障,或某条 validation 规则否决了这次 update)后重跑 seed;
  • 原因(err.message)并入同一行,读者不必再翻第二处。

实际打出来长这样(测试里逐段断言):

[SeedLoader] Deferred reference back-fill FAILED — audit_department.head_id stays NULL on record
'Engineering'. The row itself was seeded, so every row counter looks healthy while the circular
relationship is HALF-WRITTEN: nothing links it to audit_worker.name = 'Alice'. Nothing retries
this — fix the write error below (a transient failure that outlasted the retry budget, or a
validation rule vetoing the update) and re-run the seed to complete the link.
Cause: UPDATE rejected by validation rule

2. 按同一判据盘完本文件其余 logger.warn 判据取「这次失败是否进 errors/allErrors(即是否让 success 变 false)」——buildResultsuccess = !(errors.length 大于 0 || totalErrored 大于 0),所以这条线是客观的。另有五处「计为错误、日志 warn」,一并提到 error:

位置 后果
批量插入的失败行 该记录没落盘
cel 表达式解析失败 整条记录被丢弃
单值字段收到数组(引用被 DROP) 行落了、关联没落,且行计数干净(framework#3932)
引用值是对象(引用被 DROP) 同上
自引用数据集的顺序写 catch 该记录没落盘
upsert/update 路径的 catch 该行声明的值没落盘,原有内容原样留着

两处 DROP 引用的行额外补上后果与修复动作 —— 那正是 #3932 注释自己描述的形态:「行计数一切正常,损失只在别处看得见」,也就是 #4632 第二类的教科书样子。

3. 三处维持 warn,并把审计结论写进注释(免得下一个人再翻一遍):

4. 让门禁钉住,而不只是测试。 pnpm check:durability-log-level 按 callee 名匹配被 try 守护的接缝,且不下潜进嵌套函数体 —— 而原来的写入藏在 withTransientRetry(() => ...) 闭包里,AST 扫描根本看不见。于是把回填写入抽成 writeDeferredReference,与 writeRecord 一同登记进 DURABILITY_CRITICAL_CALLEES(各带一句「失败意味着什么」)。现在这两处 catch 若被降回 warn,CI 直接红:

packages/metadata-protocol/src/seed-loader.ts:813   guards writeRecord()@800              → loud (error@829)
packages/metadata-protocol/src/seed-loader.ts:1085  guards writeDeferredReference()@1077  → loud (error@1098)
✓ durability-degradation log levels: 10 durability-critical catch seam(s), all loud or rethrowing.

durability-degradation.baseline.json 保持空表(本 PR 不新增豁免)。

测试

packages/metadata-protocol/src/seed-loader-deferred-failure.test.ts 新增两条:

  • 回填写入失败时:断言级别是 error、消息点名 audit_department.head_id、含 stays NULL / HALF-WRITTEN / 目标 audit_worker.name / re-run the seed / 原始 cause,第二/三个实参分别是 Error{ object, field },并断言该失败不再出现在 warn 上;同时确认它仍被计数(success: falsetotalErrored 大于 0、errors 里有 head_id);
  • 回填成功时:引用确实落盘、success: true,且 logger.error / logger.warn 一次都没被调用 —— 别把读者训练成跳过 error

两处既有断言随之更新(runtime/src/seed-loader.test.ts 的写入失败、seed-loader-multi-value-reference.test.ts 的 DROP 引用),后者顺带断言新的后果措辞。

顺带记录(未在本 PR 修)

两条都已在代码注释里留下指针。

验证

  • pnpm exec turbo run test --filter=@objectstack/metadata-protocol → 34 files / 301 passed(合入 main 后重跑一次,仍 301)
  • packages/runtime:seed-loader.test.ts + app-plugin.seed.test.ts + seed-datasets.test.ts53 passed;packages/objectql 两个 seed 用例 → 8 passed
  • tsc --noEmit:metadata-protocol 改动前后同为 156 行输出(全是 ledger 记录的既有 TS2835/TS7006 噪声),seed-loader.ts 零错误;runtime 无本文件相关错误
  • 门禁全绿:lintdurability-log-levelstartup-registry-verdictinit-service-contractroute-envelopewildcard-fallthrougherror-code-casingengine-double-contracttype-check-coveragepublished-filesadr-anchorsslot-lookupnul-bytesdoc-authoringdocs-audit-scoperole-wordorg-identifierauthz-resolverservice-providersobjectui-changesetrelease-notesnode-versionmerge-driver
  • check:i18n / check:i18n-coverage 本地未跑通:两者都要求先做整仓 build(CI 里就排在 build 步骤之后,报错是 os CLI 未构建),本 PR 不触碰任何 translation / 声明标签面

changeset:.changeset/seed-loader-loud-failure-log-level.md(@objectstack/metadata-protocol patch)。

🤖 Generated with Claude Code

https://claude.ai/code/session_01NrmBxj8rK2uGCnh9aipjwX

claude added 2 commits August 3, 2026 19:41
…#4729)

`SeedLoaderService` pass-2 延迟引用回填的 catch 上方写着「this must be a
reported, counted error, never a silent warning」,紧跟着的调用却是
`this.logger.warn`。计数是对的(`recordDeferredError` 进 `allErrors` ⇒
`success: false`),但日志级别与它矛盾 —— 而这一行是一次 seed 在宿主控制台上
留下的唯一痕迹,`warn` 正是 #4420 证明没人读的那一级。

- 该行提到 `error`,并按 AGENTS.md「Degradation log levels」补齐一条 error
  该有的两件东西:**后果**(`<object>.<field>` 停在 NULL、行本身已种下所以
  行计数一切正常、循环关系半写入)与**修复动作**(没有任何东西会重试,修掉
  写入错误 —— 超出重试预算的瞬时故障,或某条 validation 规则否决了这次
  update —— 之后重跑 seed)。
- 按同一判据(这次失败是否计入 `errors` / 是否让 `success` 变 false)盘完本
  文件其余 `logger.warn`:另有五处「计为错误、日志 warn」一并提到 `error` ——
  批量插入失败行、`cel` 表达式解析失败被丢弃的记录、两处 DROP 引用字段的非法
  引用路径(行落了、关联没落,而行计数干净 —— framework#3932),以及顺序写
  与 update 两处 catch。两处 DROP 路径的日志行另补后果与修复动作。
- 三处**维持 warn**并把审计结论写进注释:`Halting on first error`(控制流通知,
  它所halt 的错误各自已在 error 级别报过)、`NODE_ENV` 无法判定(功能性、
  fail-open 降级)、roll-up summary 重算失败(记录确实写入了;陈旧汇总列是否
  属于 #4632 第二类另开 #4998 由维护者定夺)。
- 让门禁而不只是测试钉住这个接缝:回填写入抽成 `writeDeferredReference`
  (原写入在 `withTransientRetry` 闭包里,AST 扫描进不去),与 `writeRecord`
  一同登记进 `scripts/check-durability-degradation-log-level.mjs` 的
  `DURABILITY_CRITICAL_CALLEES`,这两处 catch 再被降级即 CI 红。

结果对象、API、schema 均无变化,变的只是级别与措辞。

另记录两处不在本单判据内的同文件发现:#4997(无 pass-2 时整条记录被丢弃却
一行日志都不打)、#4998(roll-up summary 陈旧值不计数、只记 warn)。

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

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

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol.

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

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/metadata-protocol)
  • content/docs/releases/v9.mdx (via @objectstack/metadata-protocol)

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 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.

[metadata-protocol] seed-loader 的「LOUD FAILURE」注释要求 error,代码却记 warn —— 第二类降级(#4632 规则),本轮因包冻结未修

2 participants