Skip to content

fix(metadata-protocol): 引用解析不了而整条丢弃的 seed 记录,现在会在 error 级别说出来 (#4997) - #5128

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4997-seed-drop-silent-log
Aug 4, 2026
Merged

fix(metadata-protocol): 引用解析不了而整条丢弃的 seed 记录,现在会在 error 级别说出来 (#4997)#5128
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4997-seed-drop-silent-log

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4997

问题

packages/metadata-protocol/src/seed-loader.ts pass-1 的引用解析分支:引用解析不了、且 multiPass 关闭(没有 pass 2 来补救)时,整条记录被丢弃 —— 这是正确的取舍(写下去要么把原始自然键塞进 FK 列,要么在 upsert UPDATE 路径上污染已有行)。丢弃被计数(errored)、被上报(result.errorssuccess: false),分支自己的注释还写着 "LOUD",但这里没有任何 logger 调用

结果就是:一次丢了 N 条记录的 seed,与一次干净的 seed 在控制台上一模一样。唯一的区别是调用方有没有去看 result.success —— 而 packages/runtime 的若干 seed 调用点只是 await 之后继续。

#4729 盘的是本文件的 this.logger.warn 调用,所以看不到这一格;check:durability-log-level 只扫 try/catch,这里不是 catch,也扫不到。

改动

1)pass-1 丢弃分支补 error 日志(按 AGENTS.md →「Degradation log levels」/ #4632 的两项义务)

一行里同时给出后果修复动作:

[SeedLoader] Cannot resolve reference: drop_order.customer_id = 'Ghost Inc' → drop_customer.name not found. drop_order record #1 was NOT seeded AT ALL — the WHOLE record is dropped, not just its \customer_id` link, because writing it would put the raw natural key 'Ghost Inc' into the FK column (or, on an upsert UPDATE, corrupt the row already there). Nothing retries this: pass 2 is off. Restore the record in one of three ways, then re-run the seed — seed drop_customer BEFORE drop_order so the target row exists; or enable `multiPass` so pass 2 back-fills the reference once every object is loaded; or fix the natural key in the drop_order seed data so it names a real drop_customer.name.`

三条补救路径就是 issue 列的三条:先种目标对象 / 打开 multiPass 让 pass 2 回填 / 修掉 seed 里的自然键。注释里那句名不副实的 "LOUD: counted + reported" 也改写成现在真实成立的说法(计数 + 上报 + 日志三者齐了)。

2)dry-run 分支:维持安静,并把理由写进注释 + 用测试钉住

dry-run 什么都不写,没有任何损失;它的调用方按定义就是来读结果对象的(validate() 的全部意义)。为一次模拟结果打 error,正是 AGENTS.md 警告的过度施加 —— 训练读者跳过 error,而这恰恰是 #4420 那条 warn 没人读的成因。所以它保持安静,但这是一个决定而不是疏忽,因此有测试钉住:往这个分支加日志会立刻变红。

3)同一客观判据扫完本文件,顺手对齐了另一处

判据是 #5001 那一条:这个结局有没有进 errors/allErrors? 全文件只剩一处「进了、却一行日志都没有」—— resolveDeferredUpdates() 里的 "deferred reference unresolved after pass 2"(目标始终没出现)。它的孪生分支(回填 失败)从 #4729 起就在 error 上报,而它计数完全相同、日志为零。现在也补上:说清行本身已经种下、关系永久缺失、以及怎么补齐。

测试

新增 packages/metadata-protocol/src/seed-loader-unresolved-drop.test.ts(5 例):

  • multiPass 关闭 + 引用解析不了 → error 日志点名 object / record #i / field / target 与三条补救;并且仍然被计数(totalErrored: 1success: false、行计数仍与 total 对账、referencesDropped 保持 0 —— 丢的是记录不是字段);
  • 全部解析得开的 load → 控制台一片安静(不训练读者跳过 error);
  • dry-run → 结果对象里带着 [dry-run] Reference may not resolve 的完整说明,engine.insert 一次没调,控制台安静;
  • pass 2 之后仍未解析 → error 日志 + 已有计数。

变异钉(mutation pin),三处都验过:

变异 结果
删掉 pass-1 新增的 logger.error expected "vi.fn()" to be called 1 times, but got 0 times → 红
删掉 pass-2 新增的 logger.error 同上 → 红
给 dry-run 分支一条 logger.error expected "vi.fn()" to not be called at all, but actually been called 1 times → 红

跑过的命令(均在 flock /tmp/os-heavy-verify.lock 下,NODE_OPTIONS=--max-old-space-size=4096,--maxWorkers=2):

  • pnpm --filter @objectstack/metadata-protocol test37 files / 324 tests passed
  • pnpm --filter @objectstack/runtime exec vitest run src/seed-loader.test.ts(下游消费方)→ 41 passed
  • tsc --noEmit(本包在 [P2] framework: 66 个包用 tsup 构建、无人做类型检查 —— 实测 18 个包共 380 处 code-tier 错误(#4118 的 framework 侧对应) #4311 的 DEBT 账本里,无 typecheck 脚本):改动前后同为 62 条,新增 0 —— 新测试文件按 nodenext 用 ./seed-loader.js 导入,不给账本添噪
  • 门禁:check:durability-log-levelcheck:startup-registry-verdictcheck:type-check-coveragecheck:engine-double-contract(13 pinned / 31 DEBT / 1 exempt,加文件前后一致)、check:published-filescheck:nul-bytescheck:doc-authoringcheck:error-code-casingcheck:role-wordcheck:adr-anchorscheck:release-notescheck:merge-drivercheck:docs-audit-scopecheck:node-versioncheck:driver-conformancecheck:wildcard-fallthroughcheck:init-service-contractcheck:route-envelopecheck:service-providerscheck:slot-lookupcheck:authz-resolvercheck:org-identifiercheck:stall-guardcheck:console-shacheck:objectui-changeset 全绿;eslint 干净
  • 本地环境先天失败、与本改动无关(在原始树上 git stash -u 后复跑同样失败):check:objectui-pin-fresh(objectui pin 过期)、check:i18n / check:i18n-coverage(本 worktree 未全量 build)

范围

只动 seed-loader.ts + 新测试 + 一个 changeset(@objectstack/metadata-protocol patch)。packages/spec/** 与生成物零改动;protocol.ts(#5088 在飞)零改动;content/docs/releases/ 未触碰。计数、结果对象形状、result.errors 里的 message 一律未变 —— 补的是本该有却没有的控制台输出,不是契约变更。

顺带发现(已另开 issue,未在本 PR 修)


Generated by Claude Code

…le reference at `error` (#4997)

The pass-1 branch that drops a WHOLE record — reference unresolvable and no
pass 2 to fix it — counted the loss (`errored`), reported it (`result.errors`
→ `success: false`) and its own comment claimed "LOUD", but it made no logger
call at all. A seed that dropped N records printed exactly what a clean one
printed, and the `packages/runtime` seed call sites that only `await` the load
never read `result.success`.

It now logs at `error` per AGENTS.md → "Degradation log levels" (#4632),
naming the consequence (record #i of <object> was not seeded AT ALL, not just
its association) and all three remedies (seed the target first, enable
`multiPass` so pass 2 back-fills, or fix the natural key in the seed data).

Applying the same objective criterion — does the outcome enter
`errors`/`allErrors`? — to the rest of the file found one more branch with a
count and no log: "deferred reference unresolved after pass 2", whose sibling
(back-fill write failed) has logged at `error` since #4729. Aligned.

The dry-run branch stays QUIET by decision, with the reason in a comment and a
test pinning it: a dry run writes nothing, its caller is reading the result
object by definition, and an `error` about a simulated outcome trains readers
to skim `error`.

No counters, result shapes or `result.errors` messages changed.

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 5:31am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation 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 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.

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 4, 2026 06:35
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 5ea8e1e Aug 4, 2026
24 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-4997-seed-drop-silent-log branch August 4, 2026 06:47
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

2 participants