test(memory): 修正数据丢失回归用例的构造——它被媒体三元组修复本身弄失效了

两个修复之间产生了耦合:TestArchiveColdDocs_KeepsDocWhenGraphWriteEmpty
的前提是「三元组全被 validEntityName 拒绝」,而同批引入的
mediaTriplesFromText 会为正文里的 [image/png <digest>] 标记产出合规的
「图片 <digest>」三元组,于是 ec=4 rc=2、绑定成功、释放引用变成正确行为,
用例的前提消失。

(注意当时 GC 断言并未触发——内容没丢,只是"引用被释放"这条断言不再
适用于该构造。)

改法:正文不再含媒体标记,媒体引用直接 AddRef 挂上。这模拟的是更危险的
组合——文档持有媒体引用,但正文里的媒体标记已在清洗中丢失,于是有引用
要释放却没有句子能承载它。那正是这个守卫要防的情形。

反向验证重做后仍成立:回退守卫 → FAIL(引用被释放 + GC 删掉了本该保留的
内容);恢复修复 → ok。

教训:只跑针对性测试不足以发现修复之间的耦合。提交前我跑的是
internal/agent/core 与 internal/memory,当时通过是因为缺陷二尚未修完;
两个修复都落地后的第一次全仓回归才暴露它。
This commit is contained in:
JianFeeeee
2026-09-05 12:08:33 +08:00
parent 387b28ce09
commit a7b54fef7f

View File

@ -388,9 +388,14 @@ func TestArchiveColdDocs_KeepsDocWhenGraphWriteEmpty(t *testing.T) {
// 用超长 Source 而不是指望 NLP 提取器docToTriples 在
// Source != "context_archived" 时会写一条 {文档 -来源-> Source}
// Source 超过 validEntityName 的 50 字符上限 → Commit 静默跳过
// → len(triples)==1 但 ec=0 rc=0。这正是生产上 456 字 LLM 描述
// 造成的同一状态,但构造是确定的,不依赖提取器的具体行为
//(提取器行为随版本变化,测试不该押在它身上)。
// → len(triples)==1 但 ec=0 rc=0。构造是确定的,不依赖提取器的
// 具体行为(提取器行为随版本变化,测试不该押在它身上)。
//
// 正文里刻意**不放**媒体标记mediaTriplesFromText 会为标记产出
// 合规的「图片 <digest>」三元组,那样 ec/rc 就不为 0这个用例
// 也就测不到「全被拒绝」这个状态了。媒体引用直接用 AddRef 挂上,
// 模拟「文档持有媒体但正文的媒体标记已在清洗中丢失」这一情形——
// 那正是最危险的组合:有引用要释放,却没有句子能承载它。
longSource := strings.Repeat("超长来源名", 20) // 100 字,远超 50 字符上限
// Summary 也必须超长docToTriples 会为合理 summary 写一条
// {文档 -主题-> summary}那条能通过校验ec/rc 就不为 0 了。
@ -399,7 +404,7 @@ func TestArchiveColdDocs_KeepsDocWhenGraphWriteEmpty(t *testing.T) {
doc := &document.Doc{
ID: "doc_keep",
Summary: longSummary,
Content: "[image/png " + shortDigest(digest) + "] 一张图片的描述",
Content: "一段没有媒体标记的正文",
Source: longSource,
CreatedAt: time.Now().Add(-200 * time.Hour),
LastAccess: time.Now().Add(-200 * time.Hour),