# DSH Evolution Lab

[English](README.md)

面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的“证据驱动 Skill 自进化”插件。它把项目中重复出现的成功经验整理为隔离候选 `SKILL.md`，在独立 DSH 进程中比较 baseline 与 candidate，执行留出的 canary，然后自动晋升或回滚，无需人工批准队列。

自动化边界是硬编码的：V1 每次只进化一个 Markdown Skill，不能生成 Cordis 插件、脚本、工具、workflow、preset、沙箱规则、批准策略、DSH 配置或 DSH 源码改动。

## 兼容性

| Evolution Lab | DSH | Node.js | Profile |
|---|---|---|---|
| 0.3.x | 0.1.0-rc.6 | Node.js 22 / 24 | web、headless |

DSH 仍处于 developer preview。新的 DSH 版本只有通过真实 tarball/profile smoke 后才会加入支持矩阵。

## 安装

在每个需要托管控制器的 profile 中安装。默认公共安装路径是 npm 包：

```sh
npx @deepseek-ai/dsh@0.1.0-rc.6 plugin --profile web add dsh-evolution-lab@0.3.1
```

GitHub Release 发布同一份构建并附带 SHA-256 校验和，适合需要锁定已校验远程归档的部署。两条路径在发布前都通过了干净的 web/headless profile 冒烟测试：

```sh
npx @deepseek-ai/dsh@0.1.0-rc.6 plugin --profile web add https://github.com/JayDong9130/dsh-evolution-lab/releases/download/v0.3.1/dsh-evolution-lab-0.3.1.tgz
```

安装锁定到 commit 的 GitHub 版本：

```sh
npx @deepseek-ai/dsh@0.1.0-rc.6 plugin --profile web add github:JayDong9130/dsh-evolution-lab#<commit-sha>
```

Git 源码包需要执行 `prepare`，因此 pnpm 会在首次源码安装时主动阻止未授权构建。请把 `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` 输出的完整 key 复制到 `$DSH_HOME/profiles/web/pnpm-workspace.yaml`，然后重新执行安装命令。锁定 commit 时格式如下：

```yaml
allowBuilds:
  dsh-evolution-lab@https://codeload.github.com/JayDong9130/dsh-evolution-lab/tar.gz/<commit-sha>: true
```

必须保留完整的“包名 + URL”key，不要改成宽泛通配符。如果要在 `headless` profile 中安装源码，也要在对应 profile 中重复此步骤。npm 与 Release tarball 已包含编译后的 `lib/`，不需要该 Git 源码构建例外。

也可以安装已经下载并通过 SHA-256 校验的 Release tarball：

```sh
npx @deepseek-ai/dsh@0.1.0-rc.6 plugin --profile web add ./dsh-evolution-lab-0.3.1.tgz
```

验证组合配置中出现 `dsh-evolution-lab` bundle 层和 `id: evolution-lab`：

```sh
npx @deepseek-ai/dsh@0.1.0-rc.6 --profile web --dump-config
```

插件按 profile 安装。仅当 `headless` 本身需要观察会话时才为它重复安装；Arena 评测子进程使用禁用控制器的 evaluator profile，不会递归进化自己。

## 为项目启用

安装或升级后，重启 DSH Web 并刷新页面。打开一个属于目标 Git 项目的对话；当前对话页头会通过 DSH 的增量插槽 `conversation.session.header.actions` 显示紧凑的 **开启自动进化** 按钮。点击一次，Evolution Lab 会使用安全默认值原子创建 `.dsh/evolution/config.yaml` 并启用该项目；之后同一个按钮会显示并切换当前项目的状态。

浏览器只发送当前 `sessionId`。Host 会从权威会话记录解析工作目录和 Git 根目录，不接受浏览器传入项目路径。非 Git 项目中的按钮会禁用；如果已有配置无效，按钮显示 **配置异常**，并拒绝覆盖原文件。

没有 `.dsh/evolution/config.yaml` 时，Evolution Lab 只观察，不调用模型或修改项目。Headless 用户，或希望启用前审阅全部参数的用户，也可以手动创建：

```yaml
version: 1
enabled: true
engine:
  kind: native
  provider: deepseek
  model: deepseek-chat
collection:
  includeSubagents: false
  maxTrajectoryBytes: 131072
  minClusterSize: 3
  minEvidenceWeight: 20
evaluation:
  minTasks: 3
  maxParallel: 2
  timeoutMs: 600000
  requiredPassRateDelta: 0.05
  maxCostRatio: 1.20
  maxDurationRatio: 1.50
canary:
  minTasks: 2
  requiredPassRateDelta: 0
promotion:
  automatic: true
  monitorIntervalMs: 86400000
  rollbackAfterConsecutiveFailures: 2
```

## 编写评测任务

没有 `.dsh/evolution/evals/<task-id>/` 下固定的确定性任务，就不会有任何晋升。任务数量不足时不会报错，而是一直收集轨迹却永不晋升。执行 `/evolution eval init` 写入一份可运行的起步任务包和说明任务契约的 README，再查看还缺什么：

```sh
/evolution status
```

其中 `evals` 一节会按 split 报告已发现的任务数与配置的 `evaluation.minTasks`、`canary.minTasks` 的差距，列出诸如 `INSUFFICIENT_CANARY_TASKS` 的阻塞原因，并指出加载失败的任务目录。

起步任务包只有一个 validation 和一个 canary 任务，测的是一个玩具字符串函数。它只证明流水线跑得通，不代表评测了你的项目。请用本仓库真实失败案例改写并删除示例——候选只在示例上赢过 baseline 什么也没证明。canary 必须真正留出：提案方永远不会看到或修改它们。完整任务契约见[架构文档](docs/architecture.md)。

Arena 会启动干净的 `headless` DSH profile，并且只传递显式环境变量白名单。如果评测模型通过环境变量鉴权，请在 profile 或 home 级 `cordis.patch.yml` 配置插件行；变量值从 DSH 启动环境读取，不会写进 patch：

```yaml
- id: evolution-lab
  config:
    evaluator:
      allowedEnv: [PATH, DEEPSEEK_API_KEY]
```

默认 evaluator 会复用当前 DSH `0.1.0-rc.6` CLI，并传入 `--profile headless`。高级部署可设置 `evaluator.command` 与 `evaluator.args`。子进程会设置 `DSH_EVOLUTION_CONTROLLER_DISABLED=1`，从而对递归进化 fail-closed。

## 使用

- `/evolution status`：配置、活动版本、队列、评测就绪度、证明与 monitor 健康状态。
- `/evolution run`：排队一次进化；不能跳过证据或安全闸门。
- `/evolution eval init`：在 `.dsh/evolution/evals/` 下生成一份可直接运行的起步任务包；不会覆盖已存在的文件。
- `/evolution history [skill]`：候选、晋升与回滚的不可变记录。
- `/evolution rollback <skill>`：恢复最近一个有证明的前任版本。
- `/evolution enable` / `/evolution disable`：只修改项目本地 enabled 标志。

模型仅获得只读 `evolution_status` 工具，不能决定 winner、执行 promote、削弱策略或授予例外。

## 生命周期

```text
已提交的 session event
  -> 本地双重脱敏
  -> intent atom 与证据加权 cluster
  -> 隔离的 Skill candidate
  -> 不可变安全扫描
  -> 隔离 baseline/candidate validation
  -> 留出且确定性的 canary
  -> 先写 proof，再原子激活
  -> 定时探测
  -> 回归时自动 rollback
```

无需人工批准的含义是“不存在待批准队列”，不是“没有约束”。不安全、证据不足、版本过期、隐私校验失败或性能回归的候选会被自动拒绝，且没有 override 开关。

## 数据、隐私、网络与成本

原始 DSH 会话仍由 DSH 管理。Evolution Lab 只在 `.dsh/evolution` 保存有长度上限的脱敏 envelope；敏感标识使用 HMAC 假名，并在持久化或发送给 engine 前执行第二次泄漏扫描。256-bit HMAC key 以 `0600` 权限保存在 `$DSH_HOME/evolution-lab`，绝不写入项目。原始 event、用户绝对路径、凭据、canary fixture 和 proof 不会发送给原生模型或可选 xskill bridge。

默认保留策略：脱敏轨迹 30 天、run report 90 天；验证活动版本和回滚所需的 proof/版本血缘长期保留。要删除学习数据，先执行 `/evolution disable`，保留或恢复需要的 `.dsh/skills` 镜像，再删除项目的 `.dsh/evolution`。删除 proof/版本数据会同时删除回滚能力。

插件默认无遥测。网络仅用于已配置的 DSH 模型、显式配置的 xskill bridge、安装依赖，或执行策略明确允许联网的评测任务。候选生成与 baseline/candidate 都会产生模型 token 成本，Arena 也消耗 CPU 和时间；请设置成本、耗时和并发上限。

## 安全

不可变策略只允许一个不超过 16 KiB 的常规 `SKILL.md`，调用策略固定。评测使用独立 workspace、DSH_HOME、agent home、session、输出上限和环境变量白名单。插件自有 SkillProvider 在晋升/回滚时立即 invalidate；`.dsh/skills` 只是便携镜像，不承担一致性保证。

在敏感仓库启用自动晋升前，请阅读 [SECURITY.md](SECURITY.md) 和[安全模型](docs/security-model.md)。

## 限制

- V1 只进化 Skill，并要求项目维护确定性 EvalTask。
- 系统不声称 Markdown 天然安全；安全性来自产物形态与权限边界。
- 原生 clusterer 是确定性词法算法，不是 embedding 服务。
- 真实模型 E2E 需要用户自己的 DSH 凭据；无密钥 CI 使用 mock adapter。
- 当前只验证 DSH 0.1.0-rc.6。

## 卸载

先禁用进化，并决定是否保留已晋升的项目 Skill：

```sh
npx @deepseek-ai/dsh@0.1.0-rc.6 plugin --profile web remove dsh-evolution-lab
```

卸载不会删除 `.dsh/evolution` 或 `.dsh/skills`；只有在确认回滚影响后再手动删除。

## 开发

```sh
npm install
npm run verify
```

主体是 DSH 原生 TypeScript。项目注明 xskill（MIT）的 atom/cluster/version/canary 思路对设计的启发，但不复制 xskill 源码，也不依赖 Python 或 xskill 运行时。

许可证：MIT。
