Why
481e31e2b7(2026-08-28,已在 dev 上)给 summary.diffs 加了 256 KiB 闸,实测把事件峰值从 36.12 MiB 压到 0.25 MiB(见 #522 撤回说明)。该 commit 同时引入了 239 行的 packages/opencode/test/session/summary-diff-guard.test.ts。闸本身仍有两处问题:
① truncateSummaryDiffs 的混合场景缺口(session.ts:77)
for (const item of diffs) {
const size = Buffer.byteLength(JSON.stringify(item)) + (kept.length > 0 ? 1 : 0)
if (total + size > MAX_SUMMARY_DIFF_BYTES) break // ← :77
total += size
kept.push(item)
}
现有测试覆盖:
| 测试 |
输入 |
期望 |
summary-diff-guard.test.ts:180 |
[] / undefined |
[] / undefined |
:185 |
单项超预算 |
[](已钉为有意行为,"without throwing") |
:190 |
等长小项 × (count+5) |
恰好填满预算的 count 项 |
:115 |
giantDiffs(300) |
从 f000.txt 起的连续前缀,字节 ≤ 预算 |
未覆盖:[超限项, 小项, 小项…] —— 首项超预算时 break 立刻退出,后面本可容纳的小项被一并丢弃。实测存在单个 patch 长 7,097,898 字符(7.5 MiB)的真实数据,故该场景会实际发生;此时 SessionSummary.diff()(summary.ts:135 读 message.info.summary?.diffs)返回空列表。
break 改 continue 与上述 4 处断言全部兼容:
:185 单项超限 → continue 后循环结束 → 仍返回 [] ✓
:190 等长序列 → 首个超限后其余同尺寸亦超限 → kept.length 仍为 count ✓
:115 / :134-137 连续前缀断言 → 等长输入下仍是前缀 ✓
② summary_diffs 列是 legacy 死代码(session.ts:97 / :168)
summary.ts:106-113 的 setSummary 只传 {additions: 0, deletions: 0, files: 0},从不传 diffs,故写 session 行时 info.summary?.diffs 恒为 undefined,:168 的 truncateSummaryDiffs 直接走 if (!diffs) return undefined 分支;:97 的 stripOversizedDiffs(row.summary_diffs) 同理恒返回 undefined。
实测:summary_diffs 列 2,025/2,025 全为 NULL,而 summary_additions / summary_files 有 1,999 个非空 —— 与上述路径分析完全一致。diffs 实际只存在于 message 侧(summary.ts:125 写、:135 读)。
测试 :144 的标题已自称 "strips oversized legacy summary_diffs on read" —— house 侧认知里该列就是 legacy,倾向删除而非复活。
Scope
packages/opencode/src/session/session.ts(:71-82 truncateSummaryDiffs、:84-88 stripOversizedDiffs、:90-99 fromRow、:165-168 行写入)
packages/opencode/test/session/summary-diff-guard.test.ts(扩展边界用例)
- 若选择删列:
packages/core/src/session/sql.ts、packages/core/src/session/projector.ts、packages/core/src/database/migration/
Approach
- ①:
:77 的 break 改 continue,跳过超限单项后继续累积其余项。可选加强:对单个 patch 做长度级截断,避免一个大 patch 独占整个预算(若做,需同步评估 :190 的 count 断言)
- ②:按
:144 已表明的 legacy 定位,倾向删除 summary_diffs 列与 :97 / :168 两处死代码,而非让 setSummary 开始传 diffs。删除需配 migration,并确认 projector.ts 的 summary_diffs 投影与 core/src/session/sql.ts 的列定义同步移除
- 若最终决定保留该列,则需让
setSummary 真正传 diffs,并复核 stripOversizedDiffs 的全有/全无语义(超预算直接 undefined)与 truncateSummaryDiffs 的部分保留语义为何不一致
Acceptance
- 新增单测:输入
[超预算项, 若干小项],断言 truncateSummaryDiffs 返回那些小项而非 []
- 新增单测:断言任意输入下返回值序列化后 ≤
MAX_SUMMARY_DIFF_BYTES
- 既有
summary-diff-guard.test.ts 全部 4 处断言在不修改的前提下继续通过(:180 / :185 / :190 / :115)—— 这是 break→continue 向后兼容的硬门槛
- 若删列:
rg summary_diffs packages/ 无残留引用,migration 可正向/回滚执行,fromRow 不再产出 summary.diffs
- 回归:session / summary / revert / projector 相关测试全绿
Why
481e31e2b7(2026-08-28,已在dev上)给summary.diffs加了 256 KiB 闸,实测把事件峰值从 36.12 MiB 压到 0.25 MiB(见 #522 撤回说明)。该 commit 同时引入了 239 行的packages/opencode/test/session/summary-diff-guard.test.ts。闸本身仍有两处问题:①
truncateSummaryDiffs的混合场景缺口(session.ts:77)现有测试覆盖:
summary-diff-guard.test.ts:180[]/undefined[]/undefined:185[](已钉为有意行为,"without throwing"):190count项:115giantDiffs(300)f000.txt起的连续前缀,字节 ≤ 预算未覆盖:
[超限项, 小项, 小项…]—— 首项超预算时break立刻退出,后面本可容纳的小项被一并丢弃。实测存在单个patch长 7,097,898 字符(7.5 MiB)的真实数据,故该场景会实际发生;此时SessionSummary.diff()(summary.ts:135读message.info.summary?.diffs)返回空列表。break改continue与上述 4 处断言全部兼容::185单项超限 → continue 后循环结束 → 仍返回[]✓:190等长序列 → 首个超限后其余同尺寸亦超限 →kept.length仍为count✓:115/:134-137连续前缀断言 → 等长输入下仍是前缀 ✓②
summary_diffs列是 legacy 死代码(session.ts:97/:168)summary.ts:106-113的setSummary只传{additions: 0, deletions: 0, files: 0},从不传 diffs,故写 session 行时info.summary?.diffs恒为 undefined,:168的truncateSummaryDiffs直接走if (!diffs) return undefined分支;:97的stripOversizedDiffs(row.summary_diffs)同理恒返回 undefined。实测:
summary_diffs列 2,025/2,025 全为 NULL,而summary_additions/summary_files有 1,999 个非空 —— 与上述路径分析完全一致。diffs 实际只存在于 message 侧(summary.ts:125写、:135读)。测试
:144的标题已自称 "strips oversized legacy summary_diffs on read" —— house 侧认知里该列就是 legacy,倾向删除而非复活。Scope
packages/opencode/src/session/session.ts(:71-82truncateSummaryDiffs、:84-88stripOversizedDiffs、:90-99fromRow、:165-168行写入)packages/opencode/test/session/summary-diff-guard.test.ts(扩展边界用例)packages/core/src/session/sql.ts、packages/core/src/session/projector.ts、packages/core/src/database/migration/Approach
:77的break改continue,跳过超限单项后继续累积其余项。可选加强:对单个 patch 做长度级截断,避免一个大 patch 独占整个预算(若做,需同步评估:190的count断言):144已表明的 legacy 定位,倾向删除summary_diffs列与:97/:168两处死代码,而非让setSummary开始传 diffs。删除需配 migration,并确认projector.ts的summary_diffs投影与core/src/session/sql.ts的列定义同步移除setSummary真正传 diffs,并复核stripOversizedDiffs的全有/全无语义(超预算直接undefined)与truncateSummaryDiffs的部分保留语义为何不一致Acceptance
[超预算项, 若干小项],断言truncateSummaryDiffs返回那些小项而非[]MAX_SUMMARY_DIFF_BYTESsummary-diff-guard.test.ts全部 4 处断言在不修改的前提下继续通过(:180/:185/:190/:115)—— 这是break→continue向后兼容的硬门槛rg summary_diffs packages/无残留引用,migration 可正向/回滚执行,fromRow不再产出summary.diffs