feat(channel-watch): 频道订阅保护(不被自动 GC 卸载)+ 持久游标与离线补投 - #67
Merged
Merged
Conversation
…守卫 + renderer fail-safe)
…/ 只认主题根 MESSAGES.md)
Jaxton07
force-pushed
the
feat/channel-watch-retention
branch
from
September 20, 2026 10:44
aeef297 to
5cadff6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
跨会话频道协作(channel-watch)里,订阅是会话实例内的内存态(watcher + 订阅集),而 renderer 的内存策略会把「空闲且不受保护」的会话
dispose()掉。后果:改动
P0 · 有订阅的会话不被自动 GC
onSubscriptionsChanged:enabled + trusted 才计入;disabled/untrusted/shutdown 上报空集;只传副本、回调异常只记日志,不阻断工具与生命周期)channelSubscriptionIds)并提供查询通道session:getChannelSubscriptionSessionIds(只返回仍在 registry 的会话;dispose 兜底清理)isProtected(订阅 = 明确驻留语义,且不占 K=3 保留名额);快照拉取失败 = 保护范围未知 → 整轮跳过一个都不卸(不退化成「让 backend 拒绝一次」)closeSession(id, intent),仅intent:"gc"且有订阅时拒绝({closed:false});用户主动关/删不受此限(含delete仍生效,退订最后一个 topic 即恢复 GC 资格)P1 · 持久游标 + 离线补投
channel-subs):{topics, cursors};cursors里 key 缺失 = 旧载荷未知基线(只记基线不补历史),与null(确认时文件不存在)语义不同session_start恢复后逐 topic 对账:已知 cursor 与当前版本不同 → 补投恰好一次并推进落盘;重开不重复paused期间不推进 cursor(显式重新 subscribe 合并补投一次);自写抑制仅当写前磁盘版本 == cursor 才推进,否则保留 pending(避免吞掉对端已写、我们还没提醒的内容);未订阅/已退订在异步续体里一律复查(不产生幽灵投递/幽灵游标/孤儿 watcher)<topic>/MESSAGES.md(嵌套同名文件不唤醒也不污染 cursor)验证
合并顺序说明(已 rebase 到 #68 之后)
本 PR 已 rebase 到 #68(sidebar 第 2 轮)合并后的
main(d38c4e6)之上:冲突只出现在docs/INDEX.md与docs/PITFALLS.md,按「保留 main 的侧栏内容 + 只落本任务增量」解掉(解完两个文档与原分支的版本逐字节一致),rebase 后在d38c4e6基上重跑了 typecheck / lint / test(543 + 474 = 1017 passed)/ V7+V8 冒烟,CIcheck通过(3m39s)。PR 的改动面未变:20 文件 +1986/−144。文档
docs/INDEX.md:channel-watch / session-gc / session-gc-run / use-session-gc / stores / pi-backend / smoke 行同步docs/PITFALLS.md:新增「长生命周期订阅不能挂在可被自动 GC 的会话实例上(2026-09-20)」(4 条不易自己想到的事实 + 症状索引 3 行)已知限制(本期不修,已在文档标明)
tool_call钩子里读取,外部写入若完全落在「读版本 ↔ 本会话随即落盘」的微秒窗口内,仍可能跟过头一次Checklist
npm run typecheckpassesnpm run testpasseszhandendictionaries(本 PR 无新增用户可见文案;唯一的运行时提示是中英混排的现有 notify 文案风格,与既有 channel-watch 提示一致)