Repository navigation
Replies: 2 comments
"只在一次性 bootstrap 里做采用"——我在源码里核到了对应结构1. 代码(
|
|
Another trigger for this: the one-shot bundle never mounts the workspace layer at all. Environment: I drive
So a session created on that path carries a correct header ( Observed on one machine, single workspace
There is no escape hatch either: Workaround that works, at the cost of downtime: stop Suggestion, if a per-start adoption pass is acceptable: when the registry opens, adopt every stored session whose canonical |
Uh oh!
There was an error while loading. Please reload this page.
Sessions whose stored
cwdmatches a Workspace are not grouped under it ("Ungrouped"), because adoption only runs during the one-time bootstrapWhere:
packages/workspace/workspace/src/index.ts(WorkspaceRegistry[Service.init],bootstrap) +packages/workspace/workspace/src/entity.ts(WorkspaceEntity.attachSession,mutate)Environment: desktop runtime
@deepseek-ai/dsh-desktop-runtime 0.2.0-rc.2(also present in the published CLI@deepseek-ai/dsh@0.1.5-rc.1); Windows; verified againstmaster(packages/workspace/workspace/src/index.ts, blob4b4176c5).Summary
A session is only ever attached to a workspace when
SessionController.create()is called with an explicitworkspaceId(or when a fork child attaches). There is no recovery path afterwards, so a session that starts any other way — or whose workspace accounting is pruned once — stays in Ungrouped forever, even though its stored headercwdis exactly the workspace path.Reproduction / observed data
On a machine with one Workspace
E:\...\Project\EOSregistered since first use (state.initialized === true):storages/workspace.jsonfor that workspaceidentity.cwdis that same path, but which appear in no workspaceThe ungrouped sessions' stored headers are correct, e.g.
{"type":"session","version":4,"id":"session-09f3f60d-...","cwd":"D:\\Larry\\Documents\\Project\\EOS","isSeeded":false}so
indexHeaderresolves them to the workspace path andget sessionIds()would include them — but they were never added torecord.sessionIdsin the first place.Root cause
bootstrap(), which[Service.init]calls only while the registry is uninitialized:Once
initialized: trueis durable (normal after first use),sessionPathsis rebuilt but never reconciled with workspace membership. A session created without aworkspaceIdis therefore permanently loose.attachSessionthen refuses to put it back, because its guard reads the raw snapshot rather than the filtered projection:this.record.sessionIdsis the unfiltered stored array;get sessionIds()filters it byhost.sessionPath(id) === record.path. A pruned/never-added id is absent from the filtered view but may be present in the raw one — so the early return skips re-validation andmutatefinds the id already present and writes nothing. The result is a session that is visible in the sidebar (the list surface) but accounted to no group.Note also that the justification "both inputs … are immutable" does not hold for the index:
host.sessionPath(id)returnsundefinedwhenever the header was absent from that process's index (header not in the persisted listing yet,cwdunresolvable, directory temporarily missing). The startup order makes this reachable:replaceHeaderIndex(...)runs beforeindexLiveSessions(), so any write that lands in between prunes candidates whose path is not yet indexed.Suggested fix
Reconcile membership on every registry open, not only the first. Reusing the existing bootstrap loop is enough and needs no new data:
with
adoptLooseSessions()= thehistoricalhalf ofbootstrap()restricted to already-registered paths (skip sessions already accounted for by any workspace; skip path mismatch or unresolvable cwd with alogger.warn).Two related hardening points worth considering in the same change:
attachSession's guard consult the same projection asget sessionIds()(re-validate when the id is not currently a member), so an explicit re-attach always works;mutatewill immediately prune (today the write can silently no-op).Workaround (for anyone hitting this before a fix)
storages/workspace.jsonis the durable registry. Moving a session id into the target workspace'ssessionIds— via the UI, or by adding it to the array while the app is stopped — restores grouping, since the read projection only filters by path.Verification of the workaround fix
Patching only
WorkspaceRegistry[Service.init]in a packaged build to run the adoption step on every open (node --checkclean, archive byte length unchanged, deterministic replay from a backup) grouped all 31 previously ungrouped sessions under the workspace on restart.中文摘要:会话只有在「带 workspaceId 创建」时才会被挂进工作区;按
cwd自动收编的逻辑(bootstrap)只在注册表首次初始化时执行一次,之后initialized: true便不再收编。同时attachSession的守卫读的是未过滤的原始快照,而读取侧的sessionIds是按规范路径过滤的 —— 于是被剪掉/从未加入的会话再也挂不回去,表现为「工作目录明明匹配,却一直待在未分组」。实测:一个工作区只收编了 22 个会话,另有 31 个cwd完全一致的会话遗留在未分组。建议把收编改成每次打开注册表都执行。All reactions