Files
HologramAgent-DSH-Memory-Port/docs
jianf 425c441470 feat(memory-b): F5 固化为 fail-closed 判据 + F2 符号路空间过滤
裁定 1(F5):fail-closed,写侧直接拒收
docs/phase-b-criteria.md §0.2 从「待拍板」改为「已定」:
  无名向量的块直接拒收,比放过再靠下游兜更能守住空间隔离;
  安全语义优先,**不为 HA 的疑似缺陷同构**
  (HA 的 block_recall.go:140 三个 && 让空 fp 被放行,这是要修正而非复刻的行为)。
落点分两层并写明「缺一层都不算落实」:
  写侧 putBlock 抛错(已实现)
  读侧存量行归 unknownSpace、不参与向量召回(F 组)

F5 判据(5 种输入形态 + 变异)
  省略 / 空串 / undefined / 数字 / null 一律抛错,且都不落库
  合法形态仍工作(无向量无 fp;有向量有 fp)
  F5† 变异:删掉写侧守卫 ⇒ 无名向量被接受 ⇒ F5 必红

顺带修一处:lib/store.js 的 vectorFP 加 String() 归一。
原先变异摘掉守卫后会以 SQLite bind 错误崩,掩盖了真正的断言失败
(变异因为错误的原因变红 = 不算证据)。

裁定 2(F2):先过滤,后计数
F2 —— 空间过滤必须覆盖符号路,不只向量路
- store.searchFts 现在返回 fingerprint / hasVector,
  两条 intake 路径(FTS 与包含兜底)都携带身份字段
  ★ 兜底路径第一版漏了这两个字段,导致过滤器看到 hasVector === undefined
    而放行 —— 正是「过滤器只覆盖两个入口中的一个」那种 bug
- recallBlocks 新增 fingerprint 选项,在 pool 构建后统一过滤
  刻意**不复刻** HA 的三连 &&:本仓 fail-closed,
  带向量但无法命名空间的块一律排除(§0.2 裁定)
  无向量的块不受影响(它们本来也不参与向量路,文本仍可检索)
- recallBlocksHybrid 在函数入口解析一次空间并注入 options,
  六个降级分支全部经 scoped 调用 —— 逐调用点设置会在加第七个分支时漏掉一条

F2 判据(4 条行为 + 2 条独立变异)
  F2   符号路命中旧空间块 → 被排除
  F2   不传 fingerprint → 过滤器不生效(phase A 行为不变)
  F2b  有向量但 fp 为空 → fail-closed 排除(HA 此处会放行)
  F2   hybrid 在所有降级路径上应用同一过滤
  F2†  删掉过滤器 → 旧空间块回来
  F2†b 让 searchFts 不携带身份 → 过滤器失明(过滤只剩一个入口)

A1b 的调整(必须说明)
A1b 原先逐字节锁死 recallBlocks 源码,而 F2 合法地需要改这个函数。
改为守它真正要守的东西:**不传 fingerprint 时行为不变**
  - 静态:过滤器必须 gated 在显式传入的 fingerprint 上
  - 行为:golden fixture 前 5 例逐字段一致
依据:golden 语料 12 个块全部没有 fingerprint,
所以 F2 对 phase A 是纯增量;A2 仍全绿即为证。

验证(干净副本:git archive → 空目录 → link-dsh → 自跑)
见下一条消息附实数。

未 amend / 未 force push。
2026-10-30 15:29:23 +08:00
..