Skip to content

[metadata-protocol] seed-loader:roll-up summary 重算耗尽重试后只记 warn 且不计数 —— 落盘的汇总值与事实不符,seed 仍报 success: true #4998

Description

@xuyushun441-sys

发现于 #4729(seed-loader 的日志级别盘点)。本条不在 #4729 的判据(「计数为错、日志为 warn」)内 —— 它压根不计数 —— 所以只记录,不在那个 PR 里动。

现象

packages/metadata-protocol/src/seed-loader.ts 的 writeRecoveringSummary():

private async writeRecoveringSummary(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (e: any) {
    if (e?.code === 'ERR_SUMMARY_RECOMPUTE') {
      this.logger.warn(
        '[SeedLoader] roll-up summary recompute failed after retries; records were written (summary values may be stale)',
        { failures: Array.isArray(e.failures) ? e.failures.length : undefined },
      );
      return e.written as T;
    }
    throw e;
  }
}

行本身的判断是对的(framework#3147:记录确实写进去了,不能重写,否则重复)。有疑问的是后果的等级:roll-up summary 是一列已持久化的派生值,重算耗尽重试后,库里存着的汇总数与被汇总的明细不一致,而:

  • 这次 seed 不计一条错(errored 不动、allErrors 不动),result.success 仍是 true;
  • 控制台上唯一的痕迹是一行 warn。

判定问句(#4632 / AGENTS.md「Degradation log levels」)

降级之后系统对外是否仍然「正常」,而某个声称已持久化的东西实际不对?

我的读法是 是:一切看起来干净(success: true,行数全对),而一列已落盘的汇总值是错的,并且不会自己修 —— 直到下一次触发该 summary 重算的写入才可能纠正,而 seed 之后未必还有这种写入。这正是 #4420 的形态(那次是 in-flight approvals,这次是汇总列)。

反方也有分量,所以我不想擅自改:

  1. 记录本身是真的落盘了,和 [automation/approvals] 进程重启后审批决策静默失效:挂起 flow run 仍只存内存(#1518 标记 COMPLETED 但 17.0.0-rc.1 未生效),approve 落库却永不推进且零报错 #4420「写入完全没发生」不同;
  2. 提到 error 会让一次除汇总外全部成功的 seed 打出 error 行,可能属于 AGENTS.md 明确警告的「过度适用」(把人训练成跳过 error);
  3. 也可以不动日志级别,而是把它计入结果(新增一个 summariesStale 计数或一条 ReferenceResolutionError),让结果对象诚实,日志维持 warn。

期望(需要维护者拍板)

在下面三条里选一条并落地 + 加测试:

我的倾向是 A + B:后果确实是「已落盘的数据不对」,而且计数与日志级别应当一致(#4729 的同一条论点)。但这会改变一次成功 seed 的控制台输出,属于产品口径,不该由实现方顺手决定。

未认领。

Activity

  1. claude commented on Aug 4, 2026

    @claude
    Contributor

    维护者已裁:A + B(2026-08-04,经主 backlog PM 呈报后照准)

    • A:提到 error,文案带后果(该对象 roll-up summary 列为陈旧值、明细与汇总不一致而 seed 报 success)与修复动作;
    • B:计入结果对象(如 summariesStale 计数),调用方可编程发现;success 语义是否翻转按 B 的实现证据定 —— 汇总列错数据不算「成功得干净」。

    摘 needs-user-decision,回队,本轮即派。


    Generated by Claude Code

  2. claude commented on Aug 4, 2026

    @claude
    Contributor

    🔒 认领:PM 循环第 7 轮(维护者裁决落地批)
    会话:session_01NrmBxj8rK2uGCnh9aipjwX(账号 xuyushun441-sys)
    分支:claude/issue-4998-seed-summary-stale-loud
    Worktree:objectstack-issue-4998
    域:domain:engine
    文件面:packages/metadata-protocol/src/seed-loader.ts(writeRecoveringSummary)及同目录测试

    ⛔ 硬约束:spec/** 及生成物零改动;protocol.ts 零改动;releases/ 零触碰。
    全局在飞不相交已核:与 #5025(sys-metadata-repository.ts + 基线)、#4762(packages/lint)、本批其余两单文件面均不交。


    Generated by Claude Code

  3. claude commented on Aug 4, 2026

    @claude
    Contributor

    PM 中间处置:子问题即裁,B 半边的 spec 例外呈维护者

    1. success 不翻转 —— 确认,按 dev 证据即裁(维护者原裁决明言此项交实现证据):三个消费方证据决定性 —— wire 返回结构上无法解释 false 的原因、boot banner 会打出「true and useless」行、marketplace install 会把「行全部落库」的一次安装判失败,直接违反 framework#3147 的「records WERE written」不变量。success 语义 = 行是否落库;新计数字段承载陈旧信号。
    2. B 半边(机器可读计数)撞 spec 冻结:结果类型为 spec 定义(seed-loader.zod.ts),非包内。dev 的方案 A(为本单开一个窄例外:summariesStale + totalSummariesStale,逐字镜像 seed-loader: a dropped reference FIELD is invisible in the summary — totalErrored counts dropped records only, so the boot banner reads clean #3932 referencesDropped 先例,纯增量 + default(0),~10 行手写 + 两个生成物重生)已呈维护者拍板;方案 C(包内私加字段、契约分叉)按 PD#12 弃。
    3. 拍板前本单 PARK;half A 的实施笔记(含 durability 门禁需先具名提取 fn() 才能真保护的关键发现)已在停工报告里,解锁即续。

    Generated by Claude Code

  4. claude commented on Aug 4, 2026

    @claude
    Contributor

    维护者已裁(2026-08-04):批准本单的 spec 窄例外,取 dev 方案 A

    原 dev agent 带上下文续做。⚠️ 入队前按纪律 A 处理 spec 生成物(在飞 #5042/#5043/#5012 同在动 spec)。


    Generated by Claude Code

  5. claude commented on Aug 4, 2026

    @claude
    Contributor

    ✅ 验收通过 —— PR #5062(draft,落地按同文件串行排程,见下)

    对照实际 diff 复核(9 文件,+676/-34;spec 改动恰在授权例外内:seed-loader.zod.ts + authorable-surface.json + 生成 docs 页各一;⛔ protocol.ts / releases 零触碰,逐一核实):

    1. A + B 全落地:error 级文案(点名对象与陈旧列、后果、双修复动作、cause 结构化附队);summariesStale / totalSummariesStale 逐字镜像 seed-loader: a dropped reference FIELD is invisible in the summary — totalErrored counts dropped records only, so the boot banner reads clean #3932 先例,buildResult 恒填充,success 不翻转(已确认子裁定);框架bug(objectql): 汇总重算失败仅 warn——子记录批量插入报成功,父 summary 静默过期且无重试 #3147 的「行已写入不重写」原样。
    2. 申报越界:采纳。dev 实测证明「登记进门禁」是假保护(门禁豁免任何含 throw 的 catch,本接缝条件性重抛 → warn 回退带着登记照样过 CI),收紧为仅无条件重抛可豁免 —— 这是「门禁必须真的检查它声称检查的东西」的 restore-invariant 类,免上交;全仓测量 11 接缝零判定变化、自测 10→13 例、变异 pin 证明门禁现在真的会红,且带独立 changeset。比照单登记更对。
    3. 双向变异 pin:实现回退 → 4 测试红;同回退 → 门禁 exit 1(打印精确接缝行号);恢复全绿。
    4. 生成物纪律 A 有终局断言:os-regen 全路径 checkout + 整体重生成后,git diff origin/main --stat 恰为本分支两行增量,且 收紧 theme 全部/chart 五个站点的未知键,并按门测量把 chart 两个站点判为 no gate(#4001 批 15) #5043 的兄弟条目(ui/Chart 44、ui/Theme 12)验证存活。
    5. 28 门禁 27 过(唯一红为 Release process: prevent frontend (objectui) changesets being missed when the console pin lags #3340 既有的 objectui pin,与本分支无关);spec 7613 全过。

    落地排程(同文件串行):队列中的 #5025 与本 PR 都改 check-durability-degradation-log-level.mjs —— 本 PR 等 #5025 落地后并 origin/main(冲突就地解决:保留收紧后的门禁逻辑 + #5025 的 dropPromotedDraftRow 登记两者)、重跑该门禁与自测,绿后入队。


    Generated by Claude Code

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

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions