Repository navigation
[Bug] 会话从桌面端永久消失、手机端却能看到:workspace 归属只在首次初始化 bootstrap,且 mutate() 会把校验失败的候选静默剪掉(0.2.2-rc.1) #12683
x-dc-coder
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
环境
0.2.2-rc.1(app.asar内打包的 harness)@deepseek-ai/dsh 0.2.1-rc.5,共用同一个~/.dshthis_session/new)现象
桌面端空闲挂机、只有定时任务在后台跑。回到桌面端时:
历史加载失败:…(code);workspace.json才能把会话弄回来(我已经这样修过 3 次,见下"取证")。取证:磁盘上的历史是好的,坏的是"归属索引"
我用自带 zstd 解码器把 109 条
session.v4.jsonl.zstd逐条解压校验:version: 4,zstd 完整可解,seq从 0 连续无缺口,坏行 0;0.2.2-rc.1的KNOWN_SESSION_EVENT_TYPES白名单完全命中(没有未知事件、没有"拒绝解析日志");workspace.json记账 81 条,磁盘上 27 条是origin: subagent子会话(设计上不入工作区),剩下 1 条真正的孤儿:会话cwd = C:\Users\<me>,没有任何工作区记录指向这个目录,于是它在桌面端永远不可见,而它的日志完好、任何重新列举磁盘的客户端都能看到。历史上已经被迫手工修过 3 次(
~/.dsh/session-repair-backup/里留着 11-04 / 11-07 / 11-08 的workspace.json.bak、projcache.bak、repaired*.zstd)——说明这不是我第一次踩,而是可重复、不可自愈的坑。代码定位(master 仍在)
packages/workspace/workspace/src/index.tsService.initpackages/workspace/workspace/src/entity.tsmutate()candidateMatches=sessionPaths.get(id) === path || missingSessionPaths.has(id),而sessionPaths完全来自启动那一刻的sessionPersistence.list()快照。快照会静默漏掉正在被改名的会话:
packages/session/session-persistence-jsonl/src/index.tslist()里对stat抛ENOENT的 artifact 直接continue(注释也写明了这是给"扫描中间刚好落地"的场景兜底)。而 compaction/迁移正是"写临时文件 → rename"的形态。于是定时任务在后台改日志的那一瞬间被list()扫到,这条会话就不会进sessionPaths。漏掉之后没有任何补救:
bootstrap不再跑,initialized: true之后就永久没有"建立归属"的路径;attachSession()要求realpath(header.cwd) === workspace.path,cwd 属于未登记目录的会话根本无法被任何代码路径 attach(我那条孤儿就是这样);ctx.logger.warn("workspace '<id>' filtered session '<sid>' from membership: ..."),而桌面端 harness 的日志并不落盘(~/.dsh/logs只有启动快照),用户零感知。结果就是:日志在、手机端能看、桌面端打不开,且永不恢复。
最小复现
workspace.json里 A 的sessionIds;list()扫不到它);workspace.json:S 已被永久删除;把日志文件改回来,S 也不会再回来 —— 但sessionPersistence.list()依然能列出 S,任何"重新列举"的客户端(手机端)照旧看得到。变体:启动时
realpath(cwd)暂时不可用(外接盘没就绪 / 目录被临时改名)会走invalidSessionPaths,剪链效果相同。建议
initialized分支也做一次"headers 中 cwd 命中已有工作区、但未被任何工作区记账"的自动 attach(跳过origin: subagent/ 有parentSession的子会话)。这一步就能把绝大多数线上"会话消失"自愈掉。mutate()的filter改成"先标记 + 连续 N 次启动校验失败才真删",或至少把被剪的 id/cwd/reason 写进global或一份可检索的诊断文件,让用户能看见、能上报。list()对 rename 窗口容忍:artifact 瞬时ENOENT时回退到上一次已知 header(或直接读sessions/<encoded>/<id>/目录名),不要让一条会话因为"刚好在改名"而从快照里消失。~/.dsh被两套 harness(0.2.1-rc.5 与 0.2.2-rc.1)同时打开时,storage 域是整份 JSON 覆盖写、没有跨进程互斥;建议加锁或至少在启动时检测版本/实例不一致并提示。我这边~/.dsh/logs里就有webprofile 抢127.0.0.1:3080失败的记录,说明确实同时跑过两个实例。附带
如果你需要,我可以把这次的只读取证脚本(zstd 解码 + 索引对账 + 孤儿/幽灵/重复记账体检 + 可选择修复)整理成一个 Discussions 附件或 PR,方便其他人自查
workspace.json是否也断链了。All reactions