Skip to content

feat: 收口会话切换与单例草稿,并更新 Percho 品牌展示 - #69

Merged
Jaxton07 merged 17 commits into
mainfrom
codex/singleton-draft-brand-refresh
Sep 21, 2026
Merged

Jaxton07 merged 17 commits into
mainfrom
codex/singleton-draft-brand-refresh

Conversation

@Jaxton07

Copy link
Copy Markdown
Owner

概要

  • 修复会话切换竞态、重复打开与 GC 关闭竞态,统一导航 latest-wins 语义
  • 将新会话收口为 renderer 单例 draft,并过滤只读子会话的主导航投影
  • 更新 Percho APP/LAN 图标、README 横幅、产品截图与中英文功能说明
  • 删除不再使用的旧图标、截图和设置 GIF

验证

  • npm run lint
  • npm run typecheck
  • npm run test(backend 558 + desktop 521,共 1079 项)
  • git diff --check

按 plan 阶段 0.3 先钉契约(不含业务修复):
- 单例 draft:activateNewSessionDraft 不进 sessions、快照当前真实会话、
  已有 draft 不覆盖、无 cwd 可进入、权限模式写 draft 配置、draft 态换模型不调后端
- promotion:吃 draft 快照、single-flight、失败保留可重试、latest-wins、权限模式应用
- selectBarSessions 过滤只读脏置顶
- 导航投影:isPrimaryNavigationSession + 显式 activeCwd(readOnly 不计数量/搜索/幽灵组)
- 清理:删除 draft 菜单与 GC 伪 draft 特例的旧预期

预期红:19 failed / 494 passed(typecheck、lint 仍全绿)。
新 API 经测试内局部契约类型引用,阶段 1/2 落地后移除 cast。
按 REVIEW 逐条返工(仍只改测试,生产代码零改动):
- 删净旧 multi-draft 契约测试(isDraftSessionId / createDraftSession /
  旧 setDraftCwd / createSession+replaceDraftId / 关闭 draft / 切到 draft /
  draft 下切模型 / draft permission map);remember cwd、latest-wins 改写进新契约
- GC 测试侧删净 isDraft(fixture + 断言)
- activeCwd 语义修正 + 新增「显式 null 不得回退反查 sessions」反向用例
- 新增 draft 输入内容保留(含 promotion 失败不清内容)
- 新增 promotion 边界:cwd null 不发 IPC、在途「+」不产生第二次 create
- 新增 loadModels dirty 语义三条(补默认模型 / 不顶掉已继承模型 / 不覆盖用户思考档位)

预期红:30 failed / 479 passed(typecheck、lint 仍全绿)。
按二次复核只改测试:
- 「继承模型不被全局最近顶掉」:A/B 都改成列表里的合法模型,避免锁死「保留失效模型」
- 「只改思考档位后 loadModels」:补断言空 model 被填成 deepseek/v4 ——
  dirty 必须分字段(改过思考不得阻止模型默认值补齐)

预期红:30 failed / 479 passed(typecheck、lint 全绿)。
新会话收敛为 renderer 全局唯一 draft(activeSessionId === null),不再有 draft: 伪 SessionMeta。

- sessions store:NewSessionDraftConfig(cwd/model/thinking/permission + 两个分字段 resolved 位)、
  activateNewSessionDraft / setDraftCwd / setDraftPermissionMode、
  createSession() 改为 promotion(draft 快照 + single-flight + latest-wins + 失败保留 + 应用 draft 权限档位)、
  optimisticSessionSetting 加 draftPatch(draft 页不调 SDK,失败回滚含 draft 配置)、
  loadModels 按分字段 resolved 补默认值并夹紧档位、删 createDraftSession
- composer:ensureSession 走 createSession()(不再自己拼 cwd/权限);斜杠菜单 draft 页按 cwd 拉命令
- App:启动用 activateNewSessionDraft(lastCwd) 建唯一初始 draft
- 顶栏「+」:activateNewSessionDraft(已在 draft 页则派发 COMPOSER_FOCUS_EVENT)
- picker:useActiveModelInfo / ThinkingPicker / PermissionPicker 增加 draft 页分支

验证:typecheck、lint 全绿;test 9 failed / 500 passed(9 条均为阶段 2/3 红测,阶段 1 的 21 条全绿)。
dev 实跑冒烟:开屏无 draft: 行、发送成功转正为真实会话、再点「+」回新会话页且不造伪 draft 行。
按 REVIEW 的 6 处必修 + 3 处建议:

1. 新会话页必有 draft:store 初始化即建;closeSession 关掉最后一个会话时补种(快照取自被关会话);
   promotion 迟到且此刻在新会话页时补种 —— 不再有「空白新会话页 + 无 draft」破态
2. 真实会话里改模型/思考不再污染后台 draft(optimisticSessionSetting 只在 draft 页写 draftPatch)
3. 冷启动 thinking 不再被占位 medium 锁死:makeDraft 只在有会话快照时置 thinkingResolved,
   无来源时留给 loadModels 用持久化偏好回填
4. promotion single-flight 按 draft 内部 generation 隔离:D1 已消费仍在等权限 IPC 时新建 D2,
   D2 另发一次 create(不再复用旧 Promise 把 D2 消息发进 D1 会话)
5. 新增 components/composer/send-guard.ts 同步发送锁(sending 是渲染态,挡不住 await 窗口):
   同 tick 多次触发只发一轮;补 4 条单测
6. 权限档位统一用入口快照(撤销上一轮「await 后取权限」的做法)

建议项:+ 已有 draft 时「+」派发 COMPOSER_FOCUS_EVENT;+ 测试移除阶段 1 临时 cast;
+ promotion 追加 meta 前按 sessionId 去重。

验证:typecheck/lint 全绿;test 9 failed / 512 passed(9 条均为阶段 2/3 红测)。
dev 实跑:连点 3 次发送只建 1 个会话;删掉最后一个会话后回到可用新会话页(补种 draft 生效)。
1. closeSession:active 变 null 时 cwd 镜像为 newSessionDraft.cwd(原来仍是被关闭会话的项目),
   消除「页面/activeCwd 显示 A 项目、promotion 按 B 项目建会话」的分叉
2. activateNewSessionDraft 的 existing 分支:未选项目的 draft 继承当前上下文项目
   优先级改为 existing.cwd ?? 显式 cwd ?? 当前真实会话 cwd ?? state.cwd(已选过的仍不覆盖)

补两条测试:关闭最后会话后 cwd === draft.cwd;开机空 draft + 打开真实会话 + 「+」→ 继承该项目。
验证:typecheck/lint 全绿;test 9 failed / 513 passed(9 条均为阶段 2/3 红测)。
- 新增 lib/session-visibility.ts:isPrimaryNavigationSession(判据收一处,
  供导航层与 store 的 selectBarSessions 共用,避免 store→lib 成环)
- lib/sidebar-groups.ts:新增 primaryNavigationSessions 与生产装配函数
  deriveSidebarNavigation(合并 → 过滤只读子会话 → 同一份集合喂给
  deriveProjects 与 deriveSidebarGroups,杜绝幽灵项目组与虚高计数);
  SidebarGroupsInput 改收显式 activeCwd(必填)、删掉反查 active 的逻辑与 activeSessionId
- Sidebar.tsx:只取数并调 deriveSidebarNavigation(activeCwd = store.cwd)
- selectBarSessions:过滤只读脏置顶(旧 ui-state/手改)

测试:改用生产装配函数(不再手抄装配顺序),移除阶段 0/1 的临时 cast 与交叉类型。
验证:typecheck/lint 全绿;test 2 failed / 520 passed(仅剩阶段 3 的两条 draft 菜单用例)。
- sessions store:删 DRAFT_SESSION_PREFIX/isDraftSessionId 及全部分支
  (switchSession 守卫、closeSession 本地分支、setSessionPermissionMode draft 分支、
  optimisticSessionSetting 兼容分支、fork/recall 守卫改为 null 守卫)
- composer:ensureSession/handleSend/handleStop/handleRestoreQueue 的前缀判定 → null 守卫;
  use-slash-menu 恢复「真实会话按 sessionId、新会话页按 cwd」
- 左栏:SessionRow 删 draft 固定标题;SessionMenu 删 draft 菜单分支;
  session-menu 的 sidebarMenuKind 只剩 session/none,删 draftSessionMenuItems/discardDraft
- GC:删 SessionGcOpen.isDraft 字段、isProtected 分支与接线层计算
- 其余调用点:SelectionToolbar / ProjectBranchPicker / use-context-usage / settings
- i18n:zh/en 删 sessionMenu.discardDraft 与 sidebar.newSession(未新增文案)
- 过时注释口径同步(sidebar-groups/SessionTabBar/Sidebar/projects/main-ipc/Composer 等)

验证:plan 3.1 搜索零命中;typecheck/lint 全绿;
test 全绿(backend 558、desktop 45 文件 520 用例);dev 冒烟:新会话页 / 菜单 25 条命令、
左栏右键只剩四个真实动作、点行可正常打开会话。
场景:已有 cwd=null 的 draft(开机没 lastCwd、还没选项目)→ 打开并关闭最后一个真实会话。
原来 cwd 回退成被关闭会话的项目,与 draft.cwd 再次分叉(UI 与 promotion 两处说法)。
现在 activeSessionId === null 时 cwd 直接取 newSessionDraft.cwd(**含 null**)。

补测试:draft(cwd=null) + 真实会话 /proj/opened 关闭后 → activeSessionId/draft.cwd/store.cwd 三者为 null。
验证:typecheck/lint 全绿;test 全绿(desktop 45 文件 / 521 用例)。
- INDEX:新增 lib/session-visibility.ts 行;sidebar-groups 补 primaryNavigationSessions/
  deriveSidebarNavigation 与显式 activeCwd;sessions.ts 改写成单例 draft + promotion + 不变式口径;
  drafts/session/sidebar/斜杠命令/顶栏/权限模式/日常空间/选中引用/subagent 行同步
- PITFALLS:新增两条(新会话页不变式与 cwd 镜像;contextBridge API 只读 → CDP 注入失败的替代做法),
  清掉旧符号引用(历史事故记录保留)

验证:lint / typecheck 全绿;文档内旧符号(DRAFT_SESSION_PREFIX/isDraftSessionId/
draftSessionMenuItems/createDraftSession/sidebar.newSession)零残留。
@Jaxton07
Jaxton07 merged commit 12c045e into main Sep 21, 2026
1 check passed
@Jaxton07
Jaxton07 deleted the codex/singleton-draft-brand-refresh branch September 21, 2026 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant