Repository navigation
v0.10.1
-
修一个打开记忆详情就崩掉整个宿主的崩溃(#553) — 0.10.0 里点开侧边栏任意一条记忆的详情抽屉,渲染进程立刻抛
ReferenceError: provenance is not defined。崩的是 renderer,整个宿主跟着重启,进行中的会话被打断,100% 复现。细节
根因是两个隔了三千多行的组件:
MemoryDrawer的归因 effect 与渲染块在读provenance/setProvenance,而那句useState落在了同文件上一个组件EntityPanel里(74ba401加归因区块时放错了位置)。EntityPanel里那份是纯死代码——整个函数体除声明外对provenance零引用。渲染先于 effect,所以第一帧就炸;就算渲染放过,effect 也会在setProvenance上炸。声明挪进MemoryDrawer、死代码删掉,并写明这个 state 为什么必须住在读它的那个组件里。
这个形状躲得过眼睛(隔三千行、同名 state 看着完全正常)、语法检查(provenance是合法标识符,只是不在作用域里)和原有用例(只读源码字符串,从不执行组件),所以补的不是「锁一个名字」的用例,而是按组件扫「调了setXxx(却没有声明」——锁的是这个结构。扫描器要认的几种合法情形都放行了:setTimeout/setInterval这类全局、把 setter 当形参往下传的嵌套函数、模块级同名 setter(openLibrary调setOverlayOpen,那是模块级 pub/sub,不是 React state)。变异自证:把声明原样搬回EntityPanel,用例当场打红MemoryDrawer calls setProvenance without declaring it。 -
图谱的锚定层补上另外两路起点,被动确认有了能查的地方(#24 收尾) — issue #24 的四块设计里,块 1 的「精确名 / Tag / 向量三路起点」当初只接了精确名一路——
anchoring.js本身早就支持多路(anchorSeeds累加、HOP_QUOTA分层都在)…细节
缺的是
service.js的调用点只喂了normalizePath(..., { path: "exact" })一路,而通路身份在合并那一步就丢了(normalizePath的返回值里没有path,anchorSeeds里p.path ?? "?"把每一路都记成"?")。块 4 的「例外管理」同样只有自动确认、没有可见面——面板里passiveConfirm一次都不出现。
Tag 路走全等匹配,不走子串:store.findEntitiesByTags把查询按标点和空白切成词、滤掉单字(中文单字作 tag 没有区分度),再要求tags数组里某个 tag 与词完全相等。子串匹配会让ai命中mail、让Java命中JavaScript,而这条路的作用是「用户提到了某个主题词」,模糊命中的代价是把整类无关记忆拉进图轴。排序按命中记忆数降序、名称升序——同分时要有确定性顺序,否则同一查询两次跑出来的种子次序不同。
锚点反查要看关系两端,不能只看 attr 行:findEntitiesByTags与entityIdsForMemories首版都只读entity_attrs,而dream/sleep.js的phaseRelations只写entity_relations.memory_id、不建 attr 行——仅由关系关联的记忆,对关系两端一个种子都生不出来(实测:只建关系不建 attr 时两个反查都返回空,而走saveAttr的对照组正常)。两个函数改为UNION三源(attrs / from / to),与getLinkedMemoryIds早就有的 attr|relation 双 tier 口径一致。同一条语句有三处 IN 列表共用记忆 id 集合,命中一多就撞 SQLite 变量上限,而调用方try/catch兜着——越限的表现是整条 Tag 锚点静默消失,故按 500 一批分块,块间via直接相加(各块记忆集合互不相交)。
向量路复用同一份 query 向量:searchMemories里已经有qv,把它以{ queryVector }传进entityRecall,而不是在图轴内部再嵌一次——二次嵌入既多一次模型调用,也会因为本地模型的随机性让「检索用的向量」和「图轴用的向量」不是同一个,而这种偏差只在图轴结果上体现,排查时看不到。近邻记忆到实体仍是 MAX 语义(一条记忆挂多个实体时取最高分),最低分项必须滤掉:min-max 会把这路里最弱的那条压成 0,而anchorSeeds照收 0 分种子,下游给种子挂联记忆用的是固定满权 tier(attr 1.0 / relation 0.9),不滤就等于「0 分的种子按满权拉记忆」。
早退条件也得改:原来if (!entities.length && !tagEntities.length) return []会把一个只有向量路能工作的查询直接掐死(比如查询里不含任何已知实体名,但语义上和若干记忆很近)——那恰恰是向量路存在的理由。
被动确认的复核面:新增GET /semantic/graph/boosted-relations(policy: "open",同/semantic/graph/*前缀,SECURITY.md两张表不必新增行),返回「合法抬升量 > 0 的边」——gain必须由后端算,因为要和来源基线比对(RELATION_SOURCE_DEFAULTS:manual 1.0 / confirmed 1.0 / tag 0.3 / llm 0.4),前端手里只有当前weight。判据用gain > 0而不是>= 0:weight是 REAL,1.0 - 1.0在浮点里可能是1e-17,用>=会把每一条从未被抬过的边都列进来。排序也必须在算完gain之后做:SQL 按weight DESC排、循环里再按条数截断,会让 llm 边(0.95,gain 0.55)压掉 tag 边(0.90,gain 0.60),被limit砍掉的恰恰是抬得更多的那条——故收全后按gain降序再slice。ego 路由的edges同时补上weight与source——只看weight会把「tag 边被抬到 0.5」误读成「比 manual 边弱」,而0.3 → 0.5恰恰是被用出来的证据。面板里这一面默认收起、展开才取数:它是审计视角而不是日常视角;闸关时只说「今后不会再自动抬权;下面列的是此前已经抬过的边」,不隐藏列表(关闸之后正是复核既有提升的时候),且空表提示与闸关状态解耦——否则「闸关 + 一条都没有」会只剩一句提示、没有任何行,读起来像这一屏坏了;取数失败单独说,不冒充闸关。
验收:test/graph-multipath.test.js(10 条)与test/graph-boosted-relations.test.js(9 条)新增,面板侧锁在test/client.test.js的 #24 用例;每条新用例都做过变异自证(把被它锁住的那行改坏,看它是否真的变红),其中两处首版用例是空转的、被变异自证当场抓住后重写:0 分种子用例原本把两条记忆挂在同一个实体上,滤不滤 0 分都到不了;排序用例把宽实体命名成字母序靠前的名字,按名字排也能过。全量 2680 tests / 2679 pass / 0 fail / 1 skipped。
贡献者 / Thanks
-
@strange2000 — — #553 的现场报告:把崩溃定位到「打开记忆详情必崩」这一步,贴出两条 renderer 日志、确认库本身无损(27 张表、
integrity_check正常),并顺手指出同一个 slot 里组件抛错会带走整个 renderer、建议补 error boundary。细节
根因到行号级的排查是从这份报告起步的。