Skip to content

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

Closed

Description

@os-zhuang

发现于 #4632(降级日志级别规则 + 盘点)。packages/metadata-protocol/** 本轮被冻结(#4556 正在改写入路径),因此只记录不修,按 PM 约束单开此单。

现象

packages/metadata-protocol/src/seed-loader.ts(约 953–966 行,pass-2 延迟引用回填):

} catch (err: any) {
  // LOUD FAILURE (framework#2805): the target resolved but the
  // back-fill WRITE failed (a transient error that outlasted the
  // retry budget, a validation veto, …). The reference stays NULL —
  // the very corruption pass 2 exists to prevent — so this must be a
  // reported, counted error, never a silent warning. Swallowing it
  // returned `success: true` / `totalErrored: 0` over a load that
  // left a circular relationship half-written.
  this.logger.warn('[SeedLoader] Failed to write deferred reference', {
    object: deferred.objectName,
    field: deferred.field,
    error: err?.message,
  });
  this.recordDeferredError(deferred, allResults, allErrors, ...);
}

注释与代码直接矛盾:注释明确写 "this must be a reported, counted error, never a silent warning",紧跟着的调用却是 logger.warn。

为什么这是 #4632 的第二类降级

按 #4632 的判定问句 ——「降级后系统对外表现是否仍然『正常』,而声称已持久化的东西实际没有落盘?」—— 答案是是,而且注释自己已经把后果说得比我更清楚:回填写入失败 ⇒ 引用字段停在 NULL ⇒ 循环关系半写入。这就是「记录看起来种下去了、关系实际没落盘」。

一点公允之处:紧随其后的 recordDeferredError(...) 确实把它计入 allErrors,所以不是完全静默 —— 结果对象里数得到。但日志行本身是这次 seed 在控制台唯一可见的痕迹,warn 级别正是 #4420 里没人读的那一级。计数与日志级别应当一致。

期望

机械检查说明

#4632 的 pnpm check:durability-log-level 不会自动发现这一处 —— 该 gate 靠一份显式的「持久化关键操作」词表(DURABILITY_CRITICAL_CALLEES)判定,故意做窄以避免误报,而这里的写入走的是通用 SEED_OPTIONS 写路径,没有可识别的调用名。这是该 gate 已在脚本头部声明的局限(能防已知缝隙回退,不能发现新缝隙)。修本单时如果能给这条写路径一个稳定的命名入口,可一并加进词表。

参考

Activity

  1. claude commented on Aug 3, 2026

    @claude
    Contributor

    🔒 认领:PM 循环第 4 轮(metadata-protocol 串行,#4980 落地后解锁)
    会话:session_01NrmBxj8rK2uGCnh9aipjwX(账号 xuyushun441-sys)
    分支:claude/issue-4729-seed-loader-loud-failure
    Worktree:objectstack-issue-4729
    域:domain:engine
    文件面:packages/metadata-protocol/src/seed-loader.ts 及同目录测试

    前提更新:issue 写「本轮因包冻结未修」—— 该冻结(#4556)已于今晨关闭,失效;同文件今日被 #4836(Seed.env)与 #4980(同包不同文件)改过,基于 origin/main 工作。
    ⛔ 硬约束:spec/** 及生成物零改动;protocol.ts 零改动;releases/ 零触碰。
    全局在飞不相交已核:在飞 #4993(objectql/engine.ts,已入队)与本文件面不交。


    Generated by Claude Code

  2. claude commented on Aug 3, 2026

    @claude
    Contributor

    ✅ 验收通过 —— PR #5001,CI 已全绿(24 项),直接入队

    对照实际 diff 复核(6 文件,+279/-30;⛔ spec / protocol.ts / releases 零触碰,逐一核实):

    1. 点名的 catch 提到 error 且文案齐:后果(引用停 NULL、行计数干净所以损失不可见、循环关系 HALF-WRITTEN、点名 目标对象.目标字段='自然键')+ 修复动作(无自动重试;修因后重跑 seed)+ 原始 cause 并行 —— 完整符合 [convention] best-effort 降级导致"看起来正常、实则不持久"时不应记 warn——把 #4460 的点状修复定成规则 #4632 约定。
    2. 同文件盘点用了客观判据(是否进 allErrors / 是否翻 success),不是主观感觉:另 5 处「计错但 warn」一并对齐;3 处维持 warn 的理由写进注释留档 —— 两个方向都有纪律。
    3. 门禁钉住,不止测试钉住:把藏在 withTransientRetry 闭包里、AST 扫不到的写入抽成 writeDeferredReference,登记进 durability 门禁的 DURABILITY_CRITICAL_CALLEES(--list 可见两条 seam 均 loud),baseline 保持空表 —— 这正是「declared = enforced」的修法。
    4. 变异验证:临时改回 warn → 新测试红(AssertionError 逐字),还原后 git status 干净;metadata-protocol 301 / runtime seed 53 全过,20+ 门禁本地全绿。

    衍生(均已核实立案):#4997(无 pass-2 时整条记录丢弃完全无日志,比 warn 更深一格)、#4998(roll-up summary 失败不计数,改它会改变成功 seed 的输出,A/B/C 选项已列,需维护者拍板)。

    已转 ready 入合并队列。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch looprepo:objectstack

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions