## 现象(条件性静默降级,默认配置下不发作) PR #510 修模式扫描池时,把「外来行」这条政策轴从 SQL `LIMIT` 之后推到了之前(`store.list` 新增 `excludeImported`),但同一个池子里**还留着一条后过滤**:`withoutPaused(...)` = `service.filterPaused(rows)`,它在 `list({ limit: 200 })` **之后**才跑(`src/dream/sleep.js:509`)。于是被 #471 闸门按下的作用域照样占那 200 个名额——窗口被它们填满后,过滤完剩下的自家行可能掉到 `sleepPatternMinMemories` 以下,模式扫描 `return { status: "skipped", reason: "no memories to scan" }`:**它看起来像「库里没东西可扫」,实际是「闸门把可扫的行挡在了窗口里」**。 发作条件(三条同时成立才成立,这也是它排到后面的原因):`scopeStateGate` 的 `enforce` 打开、确有被按下的作用域、且该维标注的行数逼近 200。两个开关默认都关时判定恒为「通」,`filterPaused` 返回原数组本身,行为与改动前逐字段一致(`src/scope-state.js` 头部注释已写明这条取舍)。 ## 范围(先把边界说准,别把它写成全局问题) 全仓扫过 `withoutPaused` 的 7 个调用点:`src/dream.js:64/493/1021/1483` 与 `src/dream/sleep.js:212/454/606` 走的都是 `service.all()`——**没有窗口,饿不着**。带 `LIMIT` 的只有 `sleep.js:509` 这一处。所以本条是一个点的缺陷,不是一类。 ## 期望的方案(有界重取,判定仍只有一份) `sleep.js` 的模式扫描池:先按现有形状取 200 条并过滤;若过滤后的条数不足本次要扫的名额,**不带窗口重取一次**再过滤再截断。 - 只付出「真要饿时」才发生的一次额外查询;`store` / `scope-state` / `service` 的判定实现一行都不动。 - 与 `#230` 那条注释(document 轴下推到查询层、`src/service.js:2247` 记着旧写法先物化的问题)不冲突:这里是**按需**扩大取数,不是无条件全表物化。 - 需要一条行为用例证明这件事:构造「按下作用域的行占满 200 窗口 + 自家行在窗口之外」的库,断言模式扫描**不**以 `no memories to scan` 跳过(修复前该用例红)。 ## 考虑过的替代方案 **把被按下的作用域作为值列表下推给 `store.list`**(如 `excludeScopeValues`,SQL 只做集合排除,判定逻辑仍留在 JS)。**没选它**:`SCOPE_STATE_ALL = "*"` 那一行的语义是「全局按下只命中**在这一维上有标注**的行,无标注的行不属于任何一路、不该被卷进来」(`scope-state.js` 写明)。要把它落进 SQL,就得在 SQL 里再写一遍这条匹配规则——判定长出第二份实现,正是本仓 `gateByScope` 与本模块头部注释点名要防的「过滤点漂移」。标签集合还是开放值,`*` 也无法在 JS 侧安全展开成值列表。 ## 实现口径确认 / Implementation gates - [x] 不加配置键、不改 schema(`store.list` 与 `scope-state.js` 均不动) - [x] 新行为必须带回归用例(上面那条占满窗口的构造) - [x] 判定实现保持一份:改动只允许出现在 `src/dream/sleep.js` 的池子装配处 - [ ] 若实施时发现「有界重取」仍需 store 侧参数,回到本 issue 更新口径,不静默扩面 ## 披露 / Disclosure - [x] 本报告由 AI 辅助整理(人工审核后提交)
Activity
Merge pull request #516 from slow-stack/fix/sleep-pattern-paused-window
test: 认清单形状而不是首行——补上 main 的 #515 锁与本批围栏的碰撞
Merge main into the dedupe candidate-window branch
docs(readme): 填上 v0.9.5 的版本表主题、中英头条与路线图状态格
docs(changelog): 把 v0.9.4 之后攒的东西写进 Unreleased(v0.9.5 发版前)