Files
dingtalk-workspace-cli/docs/skill-multi-migration-plan.md
T
玉澜andCursor b6851e641e fix(skill): back up skill dirs before removal and satisfy coverage gate
Address the two P1 review findings and the coverage-gate CI failures:
- Every install/upgrade path that removes a skill dir (opposite-mode
  leftovers, stale dingtalk-* / dws-shared, and same-name refreshes) now
  moves the directory to ~/.dws/skill-backups/<stamp>/ first across
  install.sh, install-skills.sh, install.ps1, install.js, `dws skill
  setup`, and `dws upgrade`. A backup failure preserves the original
  directory and never removes it.
- Remove --yes from every copyable `dws skill setup` example and document
  what the command may remove; add regression tests that declining the
  confirmation performs no removal and that the confirmation previews
  every directory slated for backup+removal.
- Rename the skill-mode tests to the TestCrossPlatformCoverage* prefix so
  the platform coverage gate selects them, and add edge tests for the
  backup/prune/cleanup fallback branches, restoring changed-code coverage
  to 100%.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 20:23:35 +08:00

19 KiB
Raw Blame History

Skill 多 skill(multi)切换方案

目标:一个月内把 DWS 的 Agent skill 默认安装形态从 mono(单 skill)切换为 multi(按产品拆分),两套并行期后下掉 mono。 本文基于对分发/消费链路的代码级梳理(2026-08-04),所有锚点均可跳转验证。 机制调研与 lark-cli 对标细节:skill-distribution-mechanism.md

1. 目标与约束

项 内容
终态 新装/升级默认铺 multi(<agent-home>/dingtalk-*/、dws-shared/);mono 仅 opt-in;最终物理删除 mono
并行期 约一个月,mono 保留可切换、可回退
硬约束 DWS 无服务端灰度/远程配置;skill 是本地文件分发;安装面至少 7 个且已有漂移
前置原则 先修已存在的 upgrade×multi 双份 bug,再谈切默认

2. 现状关键事实(梳理结论)

分发侧:

  • dws-skills.zip 布局:zip 根 = mono 副本(向后兼容)+ mono/ + multi/ (scripts/release/post-goreleaser.sh:220-248)。产物无需改动。
  • 二进制 //go:embed all:skills/mono all:skills/multi(skills_embed.go:28), dws skill setup 默认源就是 embed,与分发渠道无关。
  • 7 个安装/分发面:install.sh、install.ps1、install-skills.sh、npm install.js、 Homebrew formula、install-event.sh、install-devapp.sh。
  • OSS 只发不读;大陆链路靠 Gitee fallback。

消费侧:

  • dws skill setup:源优先级 --source/env → embed;目标 16 个 agent home (父目录门控);互斥清理 best-effort、失败仅 warning、无备份无回滚 (internal/app/skill_setup.go)。
  • dws upgrade:LocateSkillMD 命中 zip 根 mono → UpgradeSkillLocations 只写 <agent>/dws/、不清理 dingtalk-*、不更新 ~/.dws/skills (internal/upgrade/paths.go:119-172)。multi 用户升级后 mono+multi 共存。
  • dws upgrade --rollback 只回滚二进制,不回滚 skill。
  • 无任何已安装模式状态:~/.dws/ 无 install manifest。
  • ~/.dws/skills 缓存事实上只写不读(默认 setup 走 embed),且 upgrade 不更新, 版本漂移不可见。
  • skill setup --target all 不含 opencode,但 agentSkillPaths 含 (skill_command.go:119-141 vs skill_setup.go:20-37)。
  • 市场 skill(dws skill install)解压到 agent skills 根,与内置 skill 无 mode 概念;互斥清理按 dingtalk-* 前缀扫描,依赖"市场无同名前缀"这一隐性约定。

3. 总体设计

3.1 单一事实源收敛(P0c)

agent home 清单目前在 5+ 处各写一份(注释约定 keep in sync,无门禁)。

  • Go 侧:skillSetupAgentHomes 与 knownSkillDirs 合并为一个导出列表 (放在 internal/upgrade 或新 internal/skillhome 包),补 opencode, setup/upgrade/测试共用。
  • 脚本侧:install.sh / install.ps1 / install.js / install-skills.sh / install-event.sh / install-devapp.sh 的清单由同一个 JSON 生成或政策脚本 比对(新增 scripts/policy/check-agent-homes-sync.sh,进 make policy)。

3.2 安装状态文件(P0b)— ❌ CANCELLED(2026-08-05)

原计划新增 ~/.dws/skills/state.json(mode / cli_version / installed / previous / backup)。已取消:owner 决策不做运行时模式切换产品;upgrade 有 multi 包时直接刷 multi,不再需要状态文件作为正确性或切换前提。

3.3 备份式安装与真回滚(P0a)— ❌ CANCELLED(2026-08-05)

原计划把 RemoveAll 改为 mv 到 ~/.dws/skills/backup/<ts>-<mode>/ 并 支持失败整体回滚 / dws skill mode rollback。已取消:无运行时切换则 不交付备份回滚产品面。安装/清理仍可为直接删除;mono retirement 版本若做 一次性迁移,届时再单独评估临时备份,不预建 switch UX。

3.4 upgrade 包驱动 multi 刷新(P0a,已落地)

取代「按 state.json mode 刷新」与「磁盘粘性」:有 multi 包时一次性刷成 multi。

  • UpgradeSkillLocations(extractedDir):不推断磁盘布局。
    • 包内有 multi 技能树 → 始终 multi 刷新(清 dws/、刷产品 skill、刷 ~/.dws/skills/multi);存量 mono 在日常 upgrade 上一并迁走。
    • 无 multi 树的 legacy 包 → mono 刷新回退。
  • 这是包驱动的一次性迁移,不是运行时 mode-switch 产品。
  • 互斥清理在 multi 路径内执行,避免双布局长期共存。

3.5 模式切换命令(P1)— ❌ CANCELLED(2026-08-05)

原计划 dws skill mode status|set|rollback|--dry-run。已取消。

用户若需改布局:重新走安装入口(dws skill setup --mode <mono|multi> 或安装脚本 DWS_SKILL_MODE=),不是 lifecycle 切换命令。

3.6 默认切换(P2)

改默认值="multi 默认、mono opt-in",七个面一起改 + 门禁锁一致性:

面 改动
dws skill setup 无 --mode 时默认 multi(交互选项顺序反转,mono 标 legacy)
install.sh / install.ps1 multi 分支真正安装(现在是打印提示跳过);DWS_SKILL_MODE=mono opt-in;TTY 默认项改 multi
install-skills.sh / npm install.js 从零加 mode 支持(env DWS_SKILL_MODE / --skill-mode),默认 multi
Homebrew formula caveats 改提示 dws skill setup(默认即 multi),无需改资源
install-event.sh / install-devapp.sh 维持单 skill 语义,但改走共享 install 函数

文档同步:README/README_zh(README_zh.md:81 "默认 1")、 skills/multi/dingtalk-skill/SKILL.md 的 🧪 EXPERIMENTAL 措辞下调、 install.sh 内 multi 警告文案、AGENTS.md"生产优先 mono"表述。

3.7 灰度与止血(无服务端能力下的替代)

  • L1 渠道灰度(先行):beta 轨(GitHub prerelease / npm beta dist-tag / dws upgrade --beta)先切默认 multi,stable 保持 mono。零新增代码。
  • L2 确定性分桶(可选增强):随 release 发 rollout.json (GitHub asset + OSS 同步),安装/升级时 hash(machine-id) % 100 < pct 决策 并粘入 state.rollout。规则:本地显式 env/flag 永远优先;拉取失败 fail-safe mono(并行期)/ 保持现状(切默认后);存量机器不被 rollout 改模式。
  • Kill switch:rollout.json pct=0(若上 L2,已砍)+ beta 撤回 + 公告引导重装 dws skill setup --mode mono(无 skill mode 命令;备份回滚产品已随 D5 取消)。
  • 可观测:原 x-dws-skill-mode 请求头方案 CANCELLED(2026-08-05 owner);灰度与下线判断改 issue 反馈 + 主动回访。

3.8 安装与升级的语义矩阵(两条路径都要支持 multi)

场景 时期 行为
新装 并行期(切默认前) mono 默认,DWS_SKILL_MODE=multi / 交互可选 multi
新装 切默认后 multi 默认,DWS_SKILL_MODE=mono / --mode mono / TTY opt-in(仅安装时)
升级(存量 mono) 含 multi/ 的包 一次性刷成 multi(清 dws/);无 switch 提示命令
升级(存量 multi) 含 multi/ 的包 multi 刷新,清过期 skill,刷 multi 缓存
升级(无安装) 含 multi/ 的包 安装 multi(与安装默认一致)
升级(任意磁盘) legacy 无 multi 树的包 mono 刷新回退
dws upgrade --rollback 任意 只回滚二进制(现状);skill 无独立 rollback 命令

原则:升级不做磁盘粘性;有 multi 包时一律刷 multi(含存量 mono 一次性 迁移)。默认翻转与安装 opt-in 仍只影响新装/重装。无运行时 dws skill mode 产品。

4. 阶段与时间线(4 周)

周 内容 出口标准
W1 P0a upgrade mode-aware + P0b state.json + P0c 清单收敛与门禁 + 备份式安装 upgrade×multi 集成测试(装 multi → upgrade → 无 dws/ 残留);备份回滚测试(模拟中途失败);make policy 含 homes 同步检查
W2 P1 dws skill mode(status/set/rollback/dry-run);beta 轨默认切 multi(L1);x-dws-skill-mode 头 双向切换 + 中断恢复手工验收;beta 轨冒烟
W3 P2 stable 默认切 multi、mono 降为 opt-in;文案翻转;(可选 L2 分桶 5%→20%) 七面默认行为一致(政策脚本);issue/请求头占比观察
W4 (L2 则 50%→100%);mono 打 deprecation 警告,不删代码 连续 7 天无 multi 相关 P1

5. mono 下线判据(不满足则不删)

判据更新(2026-08-05):原判据 3「悟空 bundled skill 分发线(dws_res → bundled-skills,本仓库外)已切 multi」作废——悟空分发线已于当日决策 下线(见 skill-multi-roadmap.md 「悟空线下线的影响(2026-08-05)」),无仓外 mono 依赖,mono 下线不再 有仓外节奏闸门。判据重新编号如下(原 4/5 顺延为 3/4)。

  1. x-dws-skill-mode=multi 请求占比 ≥ 90%;
  2. mono 主动 opt-in 率 ≤ 2%(install 脚本/命令埋点);
  3. 连续两周无 multi P1;
  4. 已有等价政策门替代 scripts/policy/check-skill-context-budget.sh 对 skills/mono/SKILL.md 的依赖;skills_embed.go 去掉 all:skills/mono; 存量 <agent>/dws 目录有"遇到即迁移清理"逻辑。

满足后单独一个版本窗口物理删除 skills/mono/,install/setup/upgrade 中 mono 分支改为报错并指向 dws skill mode set multi。

6. 风险与对策

风险 对策
半装状态(清理成功、拷贝失败) 3.3 备份式安装,失败整体回滚
multi 用户被 upgrade 塞回 mono 3.4 mode-aware,P0a 先修
Agent 目录清单继续漂移 3.1 单一事实源 + policy 门禁
互斥清理误伤市场 dingtalk-* skill 清理前校验目录内 SKILL.md frontmatter 属 DWS 产品集,写成测试
无灰度全切翻车 beta 轨先行 + 备份回滚 + kill switch 公告命令
用户不重启 AI 工具读到旧 skill mode set / setup / install 输出统一提示重启
~/.dws/skills 缓存与 embed 版本漂移 upgrade 时同步刷新对应 mode 缓存;长期可评估废弃缓存

7. 分发机制对标 lark-cli(2026-08-04 调研)

7.1 lark-cli 的实际分发结构

npm 包 @larksuite/cli 是一个薄壳(package.json files 只有 install.js / install-wizard.js / run.js / checksums.txt):

  • 二进制:postinstall 时按平台从 GitHub Releases 下载,SHA256 校验 (checksums.txt 随 npm 包发布);镜像链 = GitHub → 用户 registry 派生镜像 → npmmirror 兜底,host allowlist + checksum 双保险。run.js 在二进制缺失时 自动补下载;Windows 有 .old 崩溃恢复。
  • Skills:不进 npm 包、不进二进制。repo 根 skills/ 目录即事实源, 由生态通用安装器 npx skills add larksuite/cli -y -g(vercel-labs/skills) 安装;wizard 首选 https://open.feishu.cn 直链、GitHub shorthand 兜底。
  • 一键向导 npx @larksuite/cli@latest install:run.js 拦截 install 子命令 → install-wizard.js 串联 4 步(npm 全局装/升级 → skills 安装 → config init → auth login),每步幂等可跳过(skills ls -g 检测 lark-* 已装则跳过);非 TTY 自动降级为"装完打印后续命令"。

生态安装器 skills CLI 的能力(lark-cli 免费获得的):

  • 76 个 agent 的目录清单由生态维护,自动探测已装 agent;project/global 两种 scope;
  • symlink 到 canonical 副本(推荐)或 copy —— symlink 模式下升级 = 更新 canonical 一份,所有 agent 即时生效;
  • list / find / update / remove 全套生命周期命令,skill 升级由 npx skills update 承担,lark-cli 自己不写 skill 刷新逻辑;
  • 发现约定兼容 catalog 布局 skills/<catalog>/<name>/SKILL.md。

7.2 与 DWS 的关键差异

维度 lark-cli DWS 现状
分发单元 repo skills/ 目录(源码即事实源) zip + 二进制 embed + 5 处拷贝缓存
安装器数量 1 个(生态工具,76 agent 清单别人维护) 7 个自维护脚本,清单已漂移
落盘方式 symlink(canonical 单点更新) 全量 copy,升级要重写 16 个 home
skill 升级 npx skills update(生态承担) dws upgrade 自写,且不识 multi
mono/multi 问题 不存在 —— 天生按目录多 skill 互斥清理/模式切换/状态全是自建
npm 包体积 薄壳(运行时下载单平台二进制) 全平台 archive + skills.zip 全打进 tarball
大陆镜像 registry 派生 + npmmirror Gitee fallback + OSS(只发不读)

7.3 结论:生态分发通道已否决(2026-08-04)

借生态安装器做分发通道 方向经评估后放弃。否决原因(均为实测):

  1. 无版本固定:skills add(v1.5.21)不支持 tag/ref 安装,而 DWS skill 从 Cobra 树生成、与 CLI 版本强耦合,错配不可接受;唯一可复现机制 skills-lock.json 仍是 experimental。
  2. 依赖 Node + GitHub 可达:curl|sh / Homebrew / 大陆 Gitee fallback 的 用户环境大量无 Node,生态通道在这些场景是断的。
  3. mono 会被一起发现:发布即制造双份,须等 mono 下线才能发布,节奏不合。
  4. 悟空 bundled / 离线预装生态通道永远覆盖不了。(2026-08-05 注: 悟空分发线已下线,此条约束随之消失;前 3 条否决理由仍成立。)

决策:分发维持全自维护通道(zip + embed + 安装脚本),按 P0–P2 落地; 不从 lark-cli 借鉴分发架构。可保留的借鉴点只剩两个本地语义,与生态无关:

  1. wizard 式幂等安装:install.sh 的 multi 分支从"打印提示跳过"改为 检测 state 可重入的真正安装(参考 install-wizard 的步骤化幂等)。
  2. npm 下载校验:install.js 如后续薄壳化,可参考其 host allowlist + checksum 双保险与镜像链(GitHub → registry 派生 → npmmirror)设计。 与本次 multi 迁移解耦,不单列任务。

8. 工作量评估与任务拆解

以 1 名熟悉本仓库的工程师计(人日)。规模基线:涉及生产代码约 5.0k 行 (Go 3.0k + shell/ps1/js 2.0k),已有测试 1.2k 行可复用。

8.1 总体判断

核心路径 13–17 人日,一人一个月可行但偏紧。风险不在 Go 而在"shell / ps1 / js 三语言 × 七面同步"和对应的测试矩阵。可选项(rollout 分桶、npm 薄壳化、symlink、生态通道发布)全部可砍可后置,砍后最小可行集 8–10 人日(见 8.4)。

8.2 任务拆解(带依赖)

P0c 清单收敛 ──┐
P0b state.json ─┼─► P0a upgrade mode-aware ─► P1 skill mode ─► P2 切默认
备份式安装 ────┘         │                        │
                         └─► x-dws-skill-mode 头   └─► beta 轨先切(P2 预演)
# 任务 改动面 估时 依赖
P0c-1 agent home 清单下沉共享包(setup/upgrade 共用),补 opencode internal/upgrade/paths.go 或新 internal/skillhome(~80 行新代码) 0.5d —
P0c-2 scripts/policy/check-agent-homes-sync.sh:比对 sh/ps1/js/sh 专项清单与 Go 清单,进 make policy 新脚本 ~120 行 + 各脚本清单改成可解析块 1d P0c-1
P0b-1 state.json schema + setup 写入(含 installed 列表、agent_homes、previous) internal/app/skill_setup.go +新文件 ~200 行 1d —
P0b-2 磁盘形态反推(dws/ → mono;dingtalk-* → multi;都有 → drift 报错)+ 单测 ~120 行 0.5–1d P0b-1 schema
P0-3 备份式安装:RemoveAll→mv backup、失败自动回滚、保留最近 2 份、改 warning 语义 setup install 两函数重写 ~150 行 + 测试 1.5–2d —
P0a-1 UpgradeSkillLocations(dir, mode):mono 现状+清残留;multi 从 zip multi/ 平铺刷新+清 dws/+刷缓存 internal/upgrade/paths.go ~150 行 1.5d P0c-1、P0b
P0a-2 upgrade×multi 集成测试(装 multi→upgrade→无 dws/ 残留、dingtalk-* 已刷新) 测试 ~200 行 1d P0a-1
P0-4 x-dws-skill-mode 请求头(仿 x-dws-channel) internal/auth/oauth_helpers.go 附近 ~30 行 0.5d P0b
P1-1 dws skill mode status/set/rollback/--dry-run 新文件 ~350 行 + 测试 2–2.5d P0b、P0-3
P1-2 beta 轨默认切 multi(版本门控的默认值翻转,stable 不变) setup/install.sh 默认值逻辑 ~40 行 0.5–1d P1-1
P2-1 setup 默认翻转 + 交互选项反转 + mono 标 legacy ~30 行 + 测试更新 0.5d P1
P2-2 install.sh / install.ps1 的 multi 分支真装(幂等、读 state 跳过) 两个脚本各 ~80 行 1.5d P0b(脚本侧写 state)
P2-3 install.js / install-skills.sh 加 mode 支持(env + flag,默认 multi) 各 ~60 行 1d P2-2 同批
P2-4 文案翻转:README×2、SKILL.md EXPERIMENTAL 下调、install 脚本提示、AGENTS.md 纯文档 0.5d —
W4 mono deprecation 警告(不删代码)+ 观察 ~20 行 0.5d P2

小计:核心 13–17 人日(P0 ≈ 6.5–8.5,P1 ≈ 3–3.5,P2 ≈ 3.5,W4 0.5)。

可选/后置:rollout.json 分桶 2–3d;setup --link symlink 1–2d(需逐 agent 验证);npm 薄壳化 2–3d(独立立项)。

8.3 风险最高的两处(先动)

  1. P0a upgrade multi 刷新语义:additive 安装 vs upgrade 全量刷新之间存在 一个真实设计题 —— 用户手动 -x 排除过的 skill,upgrade 要不要装回来? 答案:以 state.json 的 installed 为准做增量刷新,未装的不得补装 (否则违背 additive 语义)。这条必须在 P0a 开工前定死。
  2. P0-3 备份回滚改语义:现有测试断言"清理失败继续装",改语义会动 skill_setup_full_coverage_test.go 多处;回滚恢复顺序(先恢复再报错) 要用故障注入测试覆盖。

8.4 一个月做不完时的砍法

按价值/成本比从后往前砍:rollout 分桶(L1 beta 轨已够)→ install.js / install-skills.sh mode 支持(npm 渠道用户量小,可先只改 sh/ps1)。砍后最小可行集 8–10 人日:

P0c-1 + P0b + P0-3 + P0a + P1-1 + P2-1 + P2-2(仅 sh/ps1)+ 文案

即:upgrade 不再制造双份、有状态可回滚、setup 与主流安装脚本默认 multi。 npm/install-skills.sh 维持 mono 显式行为并在输出中标注即将切换。

9. 明确不做

  • 不做服务端远程配置/灰度平台(用 beta 轨 + rollout.json 替代)。
  • 不动 dws-skills.zip 产物布局(已含 mono/multi 双树)。
  • 不动市场 skill(dws skill install)的安装语义。
  • 并行期内不删 mono 代码与产物,只降级为 opt-in。