Repository navigation
Description
发现于 #5218(FS 监听改动不失效 listCache)实现期,不在该单范围内,故单独立卡。
现象
packages/metadata/src/node-metadata-manager.ts 的 handleFileEvent() 在非 deleted 事件上这样保护自己:
let data: any = undefined;
if (eventType !== 'deleted') {
try {
data = await this.load(type, name, { useCache: false });
} catch (error) {
this.logger.error('Failed to load changed file', undefined, { filePath, error: ... });
return; // ← 想表达「读不出来就别广播」
}
}这个 catch 对 loader 的读/解析失败不可达。load() 只是 loadDiagnosed() 的取值包装:
async load(type, name, options) {
return (await this.loadDiagnosed(type, name, options)).data; // metadata-manager.ts
}而 loadDiagnosed() 按 ADR-0110 D3 的设计吞掉 loader 抛出的异常,把它记进 errors[] 并返回 { data: null, degraded: true }。于是:FilesystemLoader.load() 对坏 JSON 确实 throw(它自己先 logger.error 再 throw error),但那个异常在 loadDiagnosed 里就被吃掉了,load() 返回 null 而不是抛出 —— handleFileEvent 的 catch 永不进入,return 永不执行。
结果是 data: null 一路进到广播出去的事件里:
const event: MetadataWatchEvent = { type: eventType, metadataType: type, name, path: filePath, data, ... };
this.notifyWatchers(type, event);即:「这个文件我读不出来/解析不了」与「这个文件合法地什么都没有」在事件上完全同形,正是 loadDiagnosed 存在的理由所要区分的那两件事(ADR-0110 D3:miss 与 outage 是意义相反的两个事实),而这个调用点用的恰好是丢掉该区分的那个变体。
复现
已由 #5218 的回归测试间接钉住(node-metadata-manager-fs-invalidation.test.ts 的 “still invalidates when the changed file cannot be parsed”):写入一个 { not json 的 view/v_broken.json 并投递 added 事件,handleFileEvent 不提前返回 —— 该用例先前按「会提前返回」写,实测失败,才发现此事。
后果(为什么标 finding 而不是缺陷)
目前没有仓内消费者读这个 data,所以今天没有用户会踩到:
ObjectQLPlugin的subscribe('object', …)(packages/objectql/src/plugin.ts:690-724)在收到事件后是回头metadataService.get('object', name)重读的,不看event.data;applyRepoEvent()干脆显式写data: undefined,并在注释里记着 “HMR consumers don't readdataso this is fine for M0”。
所以这是一条休眠缺陷:契约上已经在说谎(事件声称该元数据为空),但当前无人据此下判断。一旦将来有消费者(HMR 前端、Studio 预览、任何想省一次重读的订阅者)开始信任 event.data,一个开发期的拼写错误就会被当作「作者把这个视图清空了」。同理,那个 logger.error('Failed to load changed file') 分支从不打印,坏文件在监听路径上只剩 FilesystemLoader 自己那行日志。
建议修法(留给分诊裁决)
两条方向,倾向 A:
- A. 该调用点改用
loadDiagnosed(),按degraded分流:degraded === true时走原本catch想走的路(记error日志并return,不广播一个内容可疑的事件);干净的 miss 仍按现状处理。与 ADR-0110 D3 的既有分法一致,也让那条logger.error真正可达。 - B. 只删掉这段
try/catch,承认它不可达。成本是把「坏文件广播成data: null」固化为契约,与 A 相比是把问题记账而不是修掉。
另注:#5218 落地后,坏文件事件仍会失效缓存 —— 那是对的(loadMany 会跳过读不出的文件,清单确实变了),与本单无关。
关联
packages/metadata/src/node-metadata-manager.ts handleFileEvent()、packages/metadata/src/metadata-manager.ts load() / loadDiagnosed()、ADR-0110 D3、#5218(发现来源)、#5108(DatabaseLoader 曾把读失败吞成 [] 的同类问题)。
Activity
baozhoutao commentedon Aug 5, 2026
发现分诊轮判级(session_01VkPSGsX9o17MsGv3Lbxu2w,2026-08-05):晋级
pm:queue,补domain:engine(落点packages/metadata/src/node-metadata-manager.ts)。前提核验见 #4604 01:57Z(对b4ad98435成立)。理由:坏文件被当作data: null广播,正是 ADR-0110 D3 要区分的 miss/outage 被抹平 —— 休眠契约谎言,建议按 issue 方向 A(改用loadDiagnosed按degraded分流)。维护者可否决。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
认领:PM 循环第 1 轮追加批(engine-core 车道,维护者要求提速,batch 提至 5)
会话:
session_01V7WetGmnfoXNn8cLieKKmx分支:
claude/issue-5228-filewatch-degraded-broadcastWorktree:
objectstack-issue-5228域:
domain:engine-core文件面:
packages/metadata/src/node-metadata-manager.ts、packages/metadata/src/node-metadata-manager-fs-invalidation.test.ts(及必要的 metadata 包内测试)、.changeset/*.md。修法采分诊建议的方向 A(该调用点改用
loadDiagnosed(),按degraded分流),维护者可否决。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
复核通过,ACCEPT(engine-core 车道 PM,第 1 轮追加批):PR #5626。
交付按方向 A:
handleFileEvent改用loadDiagnosed(),degraded →logger.error(带errors[])+ 不广播;干净 miss / deleted 语义不变;关键结构调整是把invalidateForForeignWrite()上移到读之前(失效无条件、广播有条件)—— 双向反向验证证明了这是唯一让 17 条全绿的实现(朴素改法会把 #5218 的缓存失效一起吞掉,已用红证明)。四个订阅者逐一核验,消费者套件与门禁全绿。决策记录(PM 拍板,维护者可否决):dev 上报的 HMR 差异 —— 被改坏且一直没修的文件不再唤醒 Studio(左栏保持上次的好定义,直到下一个事件或手动刷新)—— 裁定维持方向 A,不回退 needs_decision。理由:(1) 四个订阅者中三个的失效/重载正确性不受影响,唯一受影响的是 dev 期 Studio 刷新时机,有界且自愈;(2) 广播内容可疑的事件正是本单要修的谎,A 与 ADR-0110 D3 一致,B 把 miss/outage 的区分责任推回每个消费者;(3) A 让失败第一次变成响亮的
error日志。若维护者更看重唤醒信号,B(degraded 仍广播但带data: undefined,复用applyRepoEvent既有形状)约五行,可作后续小单。当前 ESLint 红为 #5604 已知签名,base 已恢复;本 PR 与其余在飞 PR 包级不相交,做同步圈后即转 ready 入队。
Generated by Claude Code
fix(metadata): don't announce an unreadable file as `data: null` (obj…