Skip to content

feat(client): 收敛重复实现(api envelope/extOf/lineage/错误归一化)并删除死代码 - #525

Merged
Menghuan1918 merged 4 commits into
mainfrom
feat/dedup-core
Sep 3, 2026
Merged

feat(client): 收敛重复实现(api envelope/extOf/lineage/错误归一化)并删除死代码#525
Menghuan1918 merged 4 commits into
mainfrom
feat/dedup-core

Conversation

@Menghuan1918

Copy link
Copy Markdown
Collaborator

概述

行为不变的纯重构流(仅 SubagentView 两处日志级别按计划 warn→error 对齐):六项去重中的四项落地为 4 个 commit,收敛五处重复实现并删除一处死代码。tests/ 零改动(无断言修改),既有测试全部原样通过。

各项 before/after

1. api envelope 解析收敛(857646e

  • beforesrc/client/api.tscall()fetchUpload() 各持一份逐字相同的 envelope 解析(response.json().catch(() => null) + !ok || parsed === null || parsed.ok !== true || parsed.value === undefined 判断 + 同款 SidebarApiError 构造)。
  • after:私有 readEnvelope<T>(response) 一份实现,两处 return readEnvelope<T>(response)。判断顺序、parsed?.error?.code ?? 'http' / HTTP <status> 回退逐字保留。

2. 取扩展名双实现收敛(857646e

  • beforelang.tsextOfservice.ts 模块私有的 extOfPath 函数体相同。
  • after:实现迁至零依赖的 paths.tslang.ts re-export extOf 保持既有导入面(tests/file-types.spec.ts 从 lang.ts 导入);service.ts 删除 extOfPathmatchFileViewer 改用 extOf
  • 方向论证service.ts → lang.ts 不可行——lang.ts 携带全部 @codemirror/* 依赖且只经 editor 懒加载 chunk 进入浏览器,core bundle 静态引用会把 CodeMirror 拉进核心包(违反懒加载 chunk 约束);反向(lang→service)会把整个 service 内联进 editor chunk。故落位基础模块 paths.ts构建产物已验证client.js / client-registry.js 中 codemirror 引用为 0,client-editor.js 不变。

3. lineage 遍历收敛(69cc801

  • beforerootAncestor / countSubagentDescendants(subagent-detect.ts)与 treeSessionIds(subagent-jobs.ts)三份「沿 subagent-origin 链上行 + seen 防环 fail-soft」walk。
  • after:新模块 src/client/subagent-lineage.ts 持有唯一核心生成器 subagentOriginChain()(seen 防环,仅在节点为 subagent 且有 parent 时前行);三函数迁入并基于它重写;isSideThreadSummary 一并迁入(countSubagentDescendants 依赖它,避免 detect↔lineage 循环依赖);subagent-detect.ts / subagent-jobs.ts re-export 全部迁出符号——既有 import 调用点零变化
  • 语义等价性论证(逐函数,含 fail-soft 边界):
    • rootAncestor:链为空 → start.id(原循环条件不满足时返回 current?.id,同一行);链非空 → byId[末节点.parentId]?.id ?? sessionId(原循环退出时 current 恰为该行;断链/父行缺失回退 sessionId 不变;环路径下两者同在 seen 处截止,返回同一行)。
    • countSubagentDescendants:生成器在 seen.add 后 yield,消费方判 parentId === sessionId 即 break——与原循环体判断顺序一致;descendant.running 判定与 side-thread 过滤位置不变。
    • treeSessionIds:原实现的成员判定是「上行 walk 访问到的某行 id === rootId」,访问集恒为 byId 中真实存在的行,故根行缺失时提前返回空集等价;根行自身(通常非 subagent)经 summary.id === rootId 特判入集(对应原首轮访问判定);其余行经「链节点 parentId === rootId」判定,与「上行走到 id === rootId 的行」指向同一行(链节点斜上一步的 next 行)。
  • 与背景描述的偏差(按实际证据处置)directSubagentCount 是直接子行的单层过滤,没有上行 walk,无共享逻辑,不迁移;collectBranchIds 遍历 catalog 结构而非 session 链,不动。

4. GitLens 错误归一化 + SubagentView 日志分级(4002f3c

  • before:GitLens.tsx 六处 catch (reason) { … reason instanceof Error ? reason.message : String(reason) … }(refreshTarget / refresh / loadMoreLog / commit / checkout / runConfirmed);SubagentView.tsx 两处同严重度失败用 console.warn
  • after:模块级 errorMessage(reason) 一份,六处复用(含两处 t('historyLoadError')/t('checkoutError') 前缀的模板插值);console.warn → console.error,与 Sidebar.tsx(tab badge)、TerminalView.tsx(xterm open)、service.ts(urlTarget / plugin callback)对齐。前缀 [dsh-better-sidebar] 与消息文本不变;tests/ 无钉 console.warn 的断言(已核实)。

5. 死代码删除(bc1984b

  • src/sidechat-core.tssidechatSeed():src+tests 全仓零引用(含 re-export 面核查,仓库无 export * barrel),删除。SeedEvent / copyEvents 仍被 SidechatInheritance 使用,保留。

测试证据

检查 结果
pnpm typecheck ✅ 通过
pnpm build(tsc + tsdown 全产物) ✅ 通过;core bundle 无 codemirror 泄漏
pnpm test(vitest 全量) ✅ 118 files / 1241 passed / 9 skipped(既有 skip),exit 0
指定套件单独复跑(subagent-detect / subagent-jobs / subagent-jobs-view / git / git-view-worktree / api-surface / sidechat-core / sidechat-seed-validation + file-types) ✅ 9 files / 73 tests 全绿,断言零改动
pnpm check:consumer-types ✅ 通过(service.ts 有改动故按规则执行)

tests/ 目录零文件改动(git diff --name-only | grep ^tests/ 为空)。

- api.ts:call 与 fetchUpload 里逐字相同的响应 envelope 解析
  (json().catch(null) + ok/value 判断 + SidebarApiError 构造)收敛为
  私有 readEnvelope<T>(),两处调用;判断顺序与错误码回退逻辑不变。
- 取扩展名双实现收敛:extOf 的实现迁至零依赖的 paths.ts(lang.ts 与
  service.ts 均可达的基础模块),lang.ts re-export 保持既有导入面
  (tests/file-types.spec.ts 从 lang.ts 导入 extOf);service.ts 删除
  模块私有的同体 extOfPath,matchFileViewer 改用 extOf。方向说明:
  service.ts 不能反向 import lang.ts——lang.ts 携带全部 @codemirror/*
  依赖且只经 editor 懒加载 chunk 进入浏览器,core bundle 静态引用它
  会把 CodeMirror 拉进核心包(构建产物已验证:client.js /
  client-registry.js 中 0 处 codemirror,client-editor.js 不变)。
rootAncestor / countSubagentDescendants / treeSessionIds 三份「沿
subagent-origin 链上行 + seen 集合防环 fail-soft」实现收敛到新模块
src/client/subagent-lineage.ts:核心走链是单个私有生成器
subagentOriginChain()(seen 防环、只在节点为 subagent 且有 parent 时
前行),三个公开函数迁入并基于它重写;isSideThreadSummary 一并迁入
(countSubagentDescendants 依赖它,迁入可避免 detect↔lineage 循环
依赖),subagent-detect.ts / subagent-jobs.ts re-export 全部迁出
符号,既有 import 调用点零变化。

语义逐字保留的等价性论证:
- rootAncestor:链为空返回 start.id(原循环条件不满足即返回
  current?.id);链非空返回 byId[末节点.parentId]?.id ?? sessionId
  (原循环退出时 current 恰为该行,断链/行缺失回退 sessionId 不变)。
- countSubagentDescendants:生成器在 seen.add 后 yield、消费方判
  parentId === sessionId 即 break——与原循环体内判断顺序一致;
  descendant 的 running 判定、side-thread 过滤位置不变。
- treeSessionIds:根行缺失(byId[rootId] 无行)时任何上行都不可能
  「访问到」根(原实现只匹配真实访问过的行),故提前返回空集;
  根行自身(非 subagent)经 summary.id === rootId 特判入集;其余
  行经链节点 parentId === rootId 判定,等价于原「上行走到 id ===
  rootId 的行」——两种表述指向同一行(链上节点的 next 行)。

directSubagentCount 不迁移:它是对直接子行的单层过滤,没有上行
walk,无共享逻辑(背景侦查将其归入 walk 家族与实际代码不符)。
collectBranchIds 遍历的是 catalog 结构而非 session 链,保持不动。
- GitLens.tsx:六处 catch 里的 reason instanceof Error ?
  reason.message : String(reason)(refreshTarget/refresh/loadMoreLog/
  commit/checkout/runConfirmed)收敛为模块级 errorMessage(),
  两处模板字符串插值场景(historyLoadError/checkoutError 前缀)
  同样复用;归一化表达式与输出文本逐字不变。
- SubagentView.tsx:openSubagent/open session 失败的 console.warn
  改为 console.error,与同严重度失败路径对齐(Sidebar.tsx tab
  badge、TerminalView.tsx xterm open、service.ts urlTarget/plugin
  callback 均为 console.error);前缀与消息文本不变。tests/ 无任何
  钉 console.warn 的断言,已核实。
src/sidechat-core.ts 的 sidechatSeed()(buildSidechatInheritance 的
seed 半边,doc 注明 test convenience)在 src+tests 全仓零引用,也无
barrel 转出口,删除。同文件的 SeedEvent 类型与 copyEvents 仍被
SidechatInheritance 使用,保留;其余导出不动。
@Menghuan1918
Menghuan1918 merged commit f24f95f into main Sep 3, 2026
3 checks passed
@Menghuan1918
Menghuan1918 deleted the feat/dedup-core branch September 3, 2026 08:32
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