fix(client): address CI and review findings on blank-session reuse
- docs: fix cross-class note links (../feature/...), mirror the membership reuse criterion in README.zh.md and the connectWorkspace architecture note (EN/ZH/sidecars re-recorded) - test(client): stray + member blank at the same path — the scan skips the stray and reuses the member - test(web): re-registration after delete now mints a fresh blank session (the old cwd-only reuse is gone), so the account is no longer empty; assert the retained seeded session is still not re-adopted
This commit is contained in:
+2
-2
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md
|
||||
2026-08-05-workspace-blank-session-reuse-membership.md: 3f60c8d803bb09d6421e09886ec143296ed2b763
|
||||
2026-08-05-workspace-blank-session-reuse-membership.zh.md: 4932e85ef71a3cb2d5cad3cf9cf873460842e046
|
||||
2026-08-05-workspace-blank-session-reuse-membership.md: 910a10e9ada1a835df7a38a04fb04c504b0921df
|
||||
2026-08-05-workspace-blank-session-reuse-membership.zh.md: 7e7aa899f73955b3d34a1eff3d9fedde097a17f0
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ English | [中文](2026-08-05-workspace-blank-session-reuse-membership.zh.md)
|
||||
|
||||
Clicking the `+` on a Workspace group in the sidebar sometimes opened a session that the sidebar showed under Ungrouped instead of under the clicked Workspace — "entered a new session but the Workspace was not selected". The failure was specific to Workspaces registered at the directory the CLI runs from (in practice the harness checkout itself, i.e. `defaults.cwd = process.cwd()`), and appeared once a CLI-born blank session existed there.
|
||||
|
||||
Root cause: `connectWorkspace`'s blank-session reuse scanned the session list mirror on `cwd` equality alone. The host's own membership rule requires **both** an id in the Workspace account (`sessionIds`) **and** a session header whose canonical cwd equals the Workspace path ([Workspace UI product flow](2026-07-25-workspace-ui-product-flow.md)); a cwd match without the account slot is exactly the Ungrouped case. The reuse scan ignored the account slot, so any **live blank** session whose cwd matched qualified — including `main-session-*` sessions the CLI/TUI/headless entry points birth at the host cwd (`session.create({})` falls back to `defaults.cwd` and never attaches to a Workspace). When such a session was live and blank (no `turn/start` yet), the next `+` click on a Workspace registered at that path reused it and navigation opened a session no grouping surface can show under that Workspace. Workspaces at other paths were unaffected because no unaccounted blank sessions accumulate there; the host-cwd Workspace accumulated one per CLI run.
|
||||
Root cause: `connectWorkspace`'s blank-session reuse scanned the session list mirror on `cwd` equality alone. The host's own membership rule requires **both** an id in the Workspace account (`sessionIds`) **and** a session header whose canonical cwd equals the Workspace path ([Workspace UI product flow](../feature/2026-07-25-workspace-ui-product-flow.md)); a cwd match without the account slot is exactly the Ungrouped case. The reuse scan ignored the account slot, so any **live blank** session whose cwd matched qualified — including `main-session-*` sessions the CLI/TUI/headless entry points birth at the host cwd (`session.create({})` falls back to `defaults.cwd` and never attaches to a Workspace). When such a session was live and blank (no `turn/start` yet), the next `+` click on a Workspace registered at that path reused it and navigation opened a session no grouping surface can show under that Workspace. Workspaces at other paths were unaffected because no unaccounted blank sessions accumulate there; the host-cwd Workspace accumulated one per CLI run.
|
||||
|
||||
## Decision
|
||||
|
||||
@@ -22,7 +22,7 @@ The reuse scan now requires workspace membership: `blank` AND `summary.cwd === w
|
||||
|
||||
## Consequences
|
||||
|
||||
Stray blank sessions remain visible in Ungrouped (the user can still open them) but are never hijacked by a Workspace's New Session flow. A stale client mirror (Workspace frame lagging the account) can miss a reuse window and create instead — the pre-existing race behavior, already coalesced per Workspace. No host, wire, or durable-format change.
|
||||
Stray blank sessions remain visible in Ungrouped (the user can still open them) but are never hijacked by a Workspace's New Session flow. Membership is a new condition on the reuse scan, and it has one observable stale-mirror edge: in the window where the session mirror is fresh but the Workspace account frame lags, the Workspace's own member blank can fail the membership check and a duplicate blank is minted where the old code reused — a second `New Session` row under that Workspace rather than the old failure shape (a session that no grouping surface shows). Both windows are transient and the per-Workspace coalescing still prevents duplicate creates racing one another. No host, wire, or durable-format change.
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
|
||||
在侧边栏某个工作区分组的 `+` 上创建会话时,有时会进入一个新会话,但侧边栏把它显示在「未分组」而不是点击的那个工作区下——「进入了新会话,但工作区没有被选中」。故障只出现在注册在 CLI 运行目录(即 `defaults.cwd = process.cwd()`,实际场景里就是 harness 检出目录本身)上的工作区,并且一旦该目录下存在 CLI 创建的空白会话就会出现。
|
||||
|
||||
根因:`connectWorkspace` 的空白会话复用扫描只按 `cwd` 相等匹配会话列表镜像。host 自己的成员规则要求**同时**满足:会话 id 在工作区账户(`sessionIds`)中,**且**会话 header 的规范化 cwd 等于工作区路径([Workspace UI product flow](2026-07-25-workspace-ui-product-flow.md));只有 cwd 匹配而没有账户槽位的恰恰就是「未分组」的情形。复用扫描忽略了账户槽位,因此任何 cwd 匹配的**在线空白**会话都会被选中——包括 CLI/TUI/headless 入口在 host cwd 创建的 `main-session-*` 会话(`session.create({})` 回退到 `defaults.cwd`,从不挂到任何工作区)。当这样的会话在线且空白(尚无 `turn/start`)时,下一次在该路径注册的工作区上点击 `+` 就会复用它,导航打开的是一个任何分组表面都无法显示在该工作区下的会话。其他路径的工作区不受影响,因为那里不会积累未入账的空白会话;而 host-cwd 工作区每次 CLI 运行都会积累一个。
|
||||
根因:`connectWorkspace` 的空白会话复用扫描只按 `cwd` 相等匹配会话列表镜像。host 自己的成员规则要求**同时**满足:会话 id 在工作区账户(`sessionIds`)中,**且**会话 header 的规范化 cwd 等于工作区路径([Workspace UI product flow](../feature/2026-07-25-workspace-ui-product-flow.md));只有 cwd 匹配而没有账户槽位的恰恰就是「未分组」的情形。复用扫描忽略了账户槽位,因此任何 cwd 匹配的**在线空白**会话都会被选中——包括 CLI/TUI/headless 入口在 host cwd 创建的 `main-session-*` 会话(`session.create({})` 回退到 `defaults.cwd`,从不挂到任何工作区)。当这样的会话在线且空白(尚无 `turn/start`)时,下一次在该路径注册的工作区上点击 `+` 就会复用它,导航打开的是一个任何分组表面都无法显示在该工作区下的会话。其他路径的工作区不受影响,因为那里不会积累未入账的空白会话;而 host-cwd 工作区每次 CLI 运行都会积累一个。
|
||||
|
||||
## 决定
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
## 后果
|
||||
|
||||
游离空白会话仍显示在「未分组」(用户仍可手动打开),但不再被某个工作区的新建会话流程劫持。客户端镜像滞后(工作区帧落后于账户)时可能错过复用窗口而改为新建——这是既有的竞态行为,已按工作区合并。无 host、wire 或持久化格式变更。
|
||||
游离空白会话仍显示在「未分组」(用户仍可手动打开),但不再被某个工作区的新建会话流程劫持。成员校验是复用扫描的新增条件,有一个可观察的镜像滞后边界:在会话镜像已新而工作区账户帧滞后的窗口里,工作区自己的成员空白会话可能因成员校验失败而错过复用,多创建一个空白——表现为该工作区下出现第二个「新会话」行,与旧故障形态(打开一个任何分组表面都无法显示的会话)不同。两个窗口都是瞬态的,按工作区的合并逻辑仍然防止并发创建互相竞争。无 host、wire 或持久化格式变更。
|
||||
|
||||
## 测试
|
||||
|
||||
|
||||
Reference in New Issue
Block a user