Files

10 KiB
Raw Permalink Blame History

开发流程(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。

架构与跨包指引

文档纪律

  • 任何改动触及 README / AGENTS.md / docs/ 描述的行为时,同 PR 更新文档;
  • 改包 README 任一侧后,同步另一侧并 pnpm docs:write-pair <包名>;
  • 一次性记录(任务交接、验证快照)放 docs/archive/,不进长期文档目录。