10 KiB
开发流程(development)
dsh-web 是 DeepSeek Harness Web 的插件 monorepo(皮肤以「皮肤」插件的资产包形式存在)。本文定义 贡献者日常流程;仓库规则见根 AGENTS.md,包级规则见 packages/AGENTS.md,文档标准见 AGENTS.md。
环境准备
- Node.js >= 22 与 pnpm 11;
- 依赖解析官方 NPM SDK(registry.npmjs.org)。仍使用私有 scope 认证时需
NPM_TOKEN环境变量(真实令牌只放环境变量,勿提交);token 配置放 用户级~/.npmrc,项目.npmrc只留 scope 映射(见 plugins.md)。
分支模型
dev:开发分支(集成分支),本地开发与远程 PR 的统一目标;提交 / 提 PR 前先git fetch origin && git rebase origin/dev同步上游最新代码。main:稳定分支,只接收从dev合入且测试通过的代码;dev上 验证通过后由维护者合入main(发布 tag 仍从main打)。
日常循环
pnpm install
pnpm build # 全仓构建
pnpm dev:watch # 监听重编浏览器产物(dsh web 宿主自动刷新 GUI)
pnpm typecheck # 全仓类型检查
pnpm test # 全仓单测
pnpm test:standards # 测试纪律门禁(BDD 结构 / 确定性时间 / 断言质量)
pnpm docs:check # 文档一致性(链接 / README / i18n 配对)
pnpm i18n:check # 双语与第三语言俄语键集一致性及 CJK 泄漏审计
pnpm emoji:check # 手写源码无表情符号审计
pnpm libs:check # 校验已提交 lib/ 产物与源码指纹一致性
pnpm coverage:check # 覆盖率棘轮(Tier 2,整仓约一分钟)
改动提交前至少跑 pnpm typecheck && pnpm test && pnpm test:standards && pnpm docs:check && pnpm i18n:check;涉及聚合包或市场时运行对应 pnpm aggregate:check / pnpm market:check;皮肤、宠物与社区插件索引的门禁在各自的独立仓运行;CI 会全量跑所有门禁。
测试与门禁
业务测试纪律是仓库契约,由 scripts/test-standards.mjs 机械执行,规则与理由写在脚本头部:no-arbitrary-sleep(确定性时间用 vi.useFakeTimers() 配 advanceTimersByTime() 或带超时的 waitFor,不写 setTimeout / sleep 等待)、no-ad-hoc-mock(不用 vi.mock / vi.spyOn 打补丁,注入假实现或用真实后端)、bdd-title(it / test 标题以被测角色开头,如 user ...)、given-when-then(测试体写明前置条件、动作与可观察结果)、call-count-only-assertion(只断言调用次数不构成验证)、tautological-assertion(toBeDefined() 之类只重申值存在)。
历史测试按「文件 → 规则 → 计数」记入 scripts/test-standards-baseline.json:新测试文件从零基线开始,全部规则即刻生效;已有文件的计数只允许下降,修正后运行 pnpm test:standards:write 收紧基线。确需例外时在违规行尾或文件头部注释块写 test-standards-allow: <原因>,与 i18n-allow: 同一约定。
仓库工具测试(scripts/)只受机械规则约束,业务行为测试(packages/、tests/)适用全部规则;纯算法单测放在前者,用户可见行为放在后者。
覆盖率棘轮由 scripts/coverage-gate.mjs 执行:逐包跑 vitest --coverage,把 lines / statements / functions / branches 记入 scripts/coverage-baseline.json,任一指标低于基线超过 0.5 个百分点即失败(插桩本身有约 0.04 个百分点的抖动,故留容差),提升后运行 pnpm coverage:write 收紧。仓库当前并存两代 vitest,覆盖率 provider 按代声明:3.x 包各自声明 @vitest/coverage-v8@^3.2.7,4.x 包由根 devDependency 经 Node 解析提供;升级某包 vitest 主版本必须同步升级其 provider,否则门禁直接报错而不是静默跳过。基线必须在 CI 侧也成立:包内有条件运行的测试(如 harness 安装可用才跑的 benchmark 用例)会让本机覆盖率高于 Linux runner,两侧不一致时按 CI 的较低值记录。
门禁分两层:ci.yml 是 PR 门禁,一次跑完全部检查;nightly.yml 是 Tier 2,每晚补充 PR 单趟看不到的证据——覆盖率棘轮与全量测试三连跑(flake 检测)。
测试环境里的 storage 由 shared/vitest.setup.ts 统一修好(Node 25 在全局定义了 localStorage,那个残桩会存活到 DOM 测试里,机理见该文件头部);包自带的 vitest setup 必须接上它——用 shared/vitest.config.ts、import shared/vitest.setup.ts,或加进 scripts/sync-shared.mjs 的副本清单——否则该包在 Node 25 上跑的是降级路径,覆盖率随之偏低。
失败路径审计是业务特性的交付要求,以下分支必须各有测试,或在交付说明中写明其不可达:并发与幂等(重复提交、竞态、锁过期)、资源耗尽(余额不足、缺货、限流)、基础设施故障(死锁重试、事务回滚、连接中断)、第三方故障(假实现返回 500、网关超时、熔断降级)、校验与安全(越权租户、签名篡改、非法状态流转)。
常见任务
审核远程 PR
维护者可用 node scripts/pr-review.mjs 本地批量审核外部 PR(一次多个,
如 --open 审核全部 open PR):先做静态硬性检查(规模上限新增/删除各
1 万行直接拒绝、禁止提交依赖缓存与密钥、emoji 扫描、PR 模板必填项、
密钥扫描、CI 文件保护),再在工作区 worktree 上按 CI 门禁序列构建验证
(序列与 .github/workflows/ci.yml 一致)。worktree 建在 ~/remote-e2e/pr-<N>
(同 head 复用,跑完保留便于排查),定期用 pnpm pr:review --cleanup 或手动
rm -rf ~/remote-e2e 清理。
外部 PR 的模板硬检查含「测试证据与上游同步」与「视觉修复要求」:贡献者
必须提供自己本地测试的证据,并附上同步上游最新 dev 分支后重新测试
通过的证据;文本类改动可不附截图,视觉修复 / 用户可见变更必须附截图,
且视觉修复必须使用支持图像输入的多模态模型完成(纯文本模型如
deepseek-chat / deepseek-reasoner / gpt-3.5 直接拒绝)。缺失即 REJECT;
.github/workflows/pr-contribution-rules.yml 在 CI 侧同步拦截(评论 + 挂红)。
皮肤、宠物与社区插件索引的 PR 投到各自的独立仓(
.github/workflows/reject-non-content-pr.yml 会关闭投错仓库的 PR),
相应检查由那些仓库自己的 CI 跑;本仓的硬检查与视觉修复要求只针对本仓接收的改动。
用法与 verdict 语义见脚本头部注释;pnpm pr:review --help 查看全部选项。
修改 shared 运行时模块
shared/ 是 settings 卡片、轮询护栏、DSH_HOME 解析等跨包模块的唯一事实源;各包内的 同名文件是 scripts/sync-shared.mjs 生成的同步副本。改 shared 源后运行 node scripts/sync-shared.mjs 并把副本一并提交;pnpm test:scripts 的 drift 门禁防止副本漂移。
新增插件包
node scripts/dsh-plugin-new <name> # 生成 packages/<name>/ 骨架
然后按 plugins.md 把包注册进聚合包(aggregate.yml 的
patchFrom 与 deps),跑 node scripts/aggregate.mjs 重新生成聚合包。
新包必须自带 README 三件套(README.md + README.zh.md +
README.i18n.yaml)与测试。
新增皮肤
皮肤与宠物的骨架、契约校验在各自的独立仓(dsh-skins / dsh-pet)内运行;本仓库消费
它们发布的 npm 包,市场内容取自 dsh-skins 子模块(钉版见
market-inputs.lock.json)。预览图由 scripts/capture-previews
直接写进该子模块的工作树:
node scripts/capture-previews <id> # 重拍 satellites/dsh-skins/skins/<id>/preview/{light,dark}.jpg
pnpm market:fetch # 子模块 gitlink 指向新提交后,缓存输入过期并重新物化
pnpm market:build # 刷新市场产物(market/dist)
node scripts/skins-montage.mjs # 重排根 README 皮肤一览图(docs/images/skins-montage.png)
预览图随皮肤源码提交在 dsh-skins 仓,本仓随后提交该子模块的新钉版与 market/dist,pnpm market:check 校验两者一致。要在提交进 dsh-skins 之前先看市场效果,用 pnpm market:fetch --local --force 读子模块工作树;由未 pin 内容生成的 market/dist 不得提交。
皮肤启用互斥由 dsh-skins 仓的 dsh-skin use 管理(客户端原子切换,不改
cordis.patch.yml);skin-center npm 包只随附 blue-fantasy,其余皮肤由用户经
Workshop 按需安装到 $DSH_HOME/skins/<id>/。
本地验证(挂载进 dsh web)
node scripts/link-profile.mjs # 把全家桶链接进 web profile
dsh plugin --profile web add link:<仓库绝对路径>/packages/dsh-web-all
dsh web # 重启后侧边栏出现插件入口
发布
发布流程见 publish-prep.md 与 .github/workflows/
release.yml:推送 vX.Y.Z tag 触发发布,tag 是版本唯一来源,
scripts/verify-version.mjs 在发布前校验每个包版本与 tag 一致。
多代理并行开发资源纪律
多子代理并发工作流(如 wave 批量实施、并行 worktree 开发)与本机 DSH Web GUI 共享同一台机器的 CPU 与内存。具体并发硬上限、worktree 管理与防卡顿规则见 multi-agent-resources.md。
架构与跨包指引
- 架构总览与全景图见 architecture.md;
- 新插件脚手架与入桶流程见 plugins.md;
- 匿名安装遥测机制见 telemetry.md;
- 双语文档配对契约见 i18n.md。
文档纪律
- 任何改动触及 README / AGENTS.md / docs/ 描述的行为时,同 PR 更新文档;
- 改包 README 任一侧后,同步另一侧并
pnpm docs:write-pair <包名>; - 一次性记录(任务交接、验证快照)放
docs/archive/,不进长期文档目录。