# CM Workflow 项目配置合同

CM 支持一个可选的项目根配置文件：`.cm-workflow.yml`、`.cm-workflow.yaml` 或
`.cm-workflow.json`。项目缺少运行时声明时读取用户级默认，两级均缺失才使用内置默认值。

## 配置边界

- 配置只选择有限的 Workflow Profile、角色、执行适配器、模型别名和测试/交付策略。
- 配置不是权限文件：不能绕过人工审批、N4 独立审查、测试合同、Git 安全边界或外部专家外发确认。
- 配置不得出现 API Key、Token、Cookie、密码、私钥、凭据、Prompt 或模型原始回答。
- `source` 表示订阅、API、本地或浏览器等执行来源；它不等于模型身份。
- `model` 是用户可读的模型别名。CM 不根据订阅名推断后端模型版本。
- 外部专家的项目级 `activation` 只能是 `explicit`；`AUTO` 仍是单次调用的显式选择，不能由仓库配置永久打开。
- 显式 `--config` 或项目根配置优先；仅缺少 `runtimes.available` 时回退用户级 `~/.cm-workflow/runtimes.yml`（`CM_WORKFLOW_HOME` 覆盖目录）。

## 角色

| 角色 | 责任 | 默认执行方式 |
| --- | --- | --- |
| `analyst` | 理解需求、识别影响范围 | 当前 AI |
| `planner` | 形成技术方案和任务拆分 | 当前 AI |
| `coder` | 修改业务代码 | 当前 AI |
| `tester` | 执行逻辑/命令测试与测试合同 | 本地工具 |
| `reviewer` | 任务级独立审查 | 当前 AI/独立审查通道 |
| `browser_qa` | 按用例模拟用户 | Playwright/本地浏览器 |
| `external_expert` | 方案、研究、诊断、测试设计和批判 | 显式外部浏览器通道（`external-browser` + `browser`） |

角色配置只改变“谁负责、用哪个适配器和模型别名”，不改变 N1–N8 顺序。未配置角色继续使用当前 AI 和既有默认行为。

## 运行时声明

`runtimes.available` 记录用户自报的可用运行时：`codex`、`claude` 或 `both`。缺省为
`unknown`（两级均未声明，不做交叉检查）。安装器可写用户默认，`cm-init` 优先继承且不再询问；`cm-runtime` 随时切换。

| 预设 | `runtimes.available` | `roles.coder.adapter` | `roles.reviewer.adapter` |
| --- | --- | --- | --- |
| `codex-only` | `codex` | `codex-cli` | `codex-cli`（全新上下文，仍是独立审查） |
| `claude-only` | `claude` | `claude-cli` | `claude-cli`（同上） |
| `codex-codes` | `both` | `codex-cli` | `claude-cli` |
| `claude-codes` | `both` | `claude-cli` | `codex-cli` |

校验规则：单家声明时任何角色不得指向另一家；`both` 时 `coder` 与 `reviewer` 不得同家。
`current-ai` 表示「当前所在工具」，不参与判定。**声明不等于派发**：与当前运行时不同的
adapter 仍解析为 `declared-adapter`，只记录、不调用；`cm-check --project` 会把这类角色标为
「已声明未派发」。声明也不拦用户在任一工具里交互式执行。

## 有限 Workflow Profile

| Profile | 适用项目 | 额外策略 |
| --- | --- | --- |
| `cm-default` | 通用项目 | 使用默认 N1–N8 与三类测试 |
| `java-backend` | Java/Spring 等后端 | 优先后端命令测试、API/数据库回归 |
| `web-frontend` | React/Vue 等前端 | 优先构建、交互和浏览器测试 |

第一版不支持任意 DAG、动态 Agent 集群或后台调度器。需要新流程时先增加一个有限 Profile，并为它补齐双运行时与真实项目走查。

## 模型与来源示例

```yaml
roles:
  coder:
    adapter: codex-cli
    model: gpt-5.6-sol
    source: subscription
  planner:
    adapter: claude-api
    model: claude-opus
    source: api
```

Codex 订阅、Codex API、Claude/Fable 等兼容 API 和浏览器账号由各自运行时管理；项目配置只保存非敏感别名。`model_policy` 使用 `pro-extra-high-high-skip`；为兼容既有合同，`strict-Pro` 会在有效配置中规范化为 `strict-pro`。

`openai-compatible` 是当前唯一内置的 API 调用边界。它从
`CM_OPENAI_COMPATIBLE_ENABLED`、`CM_OPENAI_COMPATIBLE_BASE_URL` 和
`CM_OPENAI_COMPATIBLE_API_KEY` 读取本次启用、地址与凭据；三者都不得写进项目配置。
使用该适配器的角色必须显式写 `source: api`，不得继承或声明为 `local`/`subscription`。
版本 1 暂不允许把它配置给 `reviewer`：managed 文本回答尚不能生成 N4 接受的独立审查
凭证，提前调用只会增加一次无效 API 消耗。N4 继续使用共享审查合同列出的通道。
其他 API 适配器仍只声明路由，不伪造已执行。

## 字段枚举与降级

- `adapter`：`current-ai`、`codex-cli`、`claude-cli`、`claude-api`、
  `openai-compatible`、`local`、`browser`、`external-browser`。`browser` 只允许给
  `browser_qa`，`external-browser` 只允许给 `external_expert`；v1 的外部专家固定使用
  `source: browser`。
- `source`：`local`、`subscription`、`api`、`browser`、`none`。它只描述执行来源，
  不授予访问权限；`browser` 来源只允许给 `external_expert`。
- `external_expert.enabled` 是布尔值；`activation` 在项目配置中只能是 `explicit`。
  单次调用是否使用 AUTO 仍由调用指令决定，不能从仓库配置自动开启。
- `external_expert.model_policy`：默认 `pro-extra-high-high-skip`，按
  `Pro → Extra High → High → SKIPPED` 选择；`strict-pro`（兼容输入 `strict-Pro`）
  在 Pro 不可用时记录 BLOCKED 且不发送。
- `policies.tests` 只能包含 `logic`、`commands`、`browser`；`auto_fix` 只能是
  `explicit`、`never`、`auto`；`delivery` 只能是 `diff`、`branch`、`draft-mr`。

## 策略消费点

| 字段 | 实际消费点 |
| --- | --- |
| `project.workflow` | N1/N6 的测试优先级；不改变 N1–N8 |
| `policies.tests` | N6 可选测试类型；审批合同里的 blocking case 仍必须执行或 BLOCKED |
| `policies.generate_cases` | cm-prd 是否自动补生成 generated cases；不删除用户用例 |
| `policies.auto_fix` | N6 失败后 never/explicit/auto 三分支 |
| `policies.delivery` | N1 Git 准备、N5 commit 策略和 N8 diff/branch/draft MR 收口 |

策略是执行选择，不是 Git 远端授权。`draft-mr` 到 N8 仍须取得本次明确授权后才能
push 和创建 MR/PR；未授权时保留已验证的本地分支，不伪造 delivery 事件。

配置只选择已有角色和策略，不增加 Agent 数量，不改变 N1–N8，也不改变外部专家的本地
执行、人工审批和 N4 独立审查边界。

## 校验

使用 Node.js 内置能力校验配置，不要求安装第三方依赖：

```bash
node {CM_WORKFLOW_ROOT}/scripts/cm-workflow-config.mjs --project {CODE_PROJECT}
node {CM_WORKFLOW_ROOT}/scripts/cm-workflow-config.mjs --project {CODE_PROJECT} --print-effective
```

`scripts/cm_workflow_config.py` 仅保留为旧调用方的兼容转发入口；配置解析、合并、校验与
角色路由的唯一实现位于 `scripts/cm-workflow-config.mjs`。

配置错误时，入口必须停止并报告字段路径；配置正确时，`--print-effective` 输出合并默认值后的脱敏 JSON。
`cm-check` 会在验证项目配置时使用该输出模式，便于确认实际生效的角色和默认值；它不把
这些值当作权限授权。

角色在实际节点中的投影和 `route_state` 定义见 `runtime/workflow-routing.md`。查看单个
角色的安全路由元数据：

```bash
node {CM_WORKFLOW_ROOT}/scripts/cm-workflow-config.mjs \
  --project {CODE_PROJECT} --role coder --runtime codex --print-role
```

## 用户默认与切换

```yaml
# 用 cm-runtime set --user <preset> 修改用户级默认。
runtimes: {available: both}
preset: codex-codes
```

只接受上述两个字段；预设与 available 必须一致。优先级为项目 > 用户 > 未声明；
项目已声明时完全不读用户文件。用户预设给 coder/reviewer 的 adapter/source 提供默认值，
显式项目角色字段继续优先，模型和其他字段保留；合并结果仍须通过同一校验。
生效的用户文件非法时报告字段路径并阻断。`--print-effective` / `--print-role` 包含
`runtimes_source: project|user|none`，不将这个诊断字段写回项目配置。

`cm-runtime show [--project PATH]` 只读；`set <preset> [--project PATH]` 原子改项目；
`set --user <preset>` 改用户默认；`unset --user` 删除用户默认但不删除项目声明。
新建项目配置使用模板，已有配置只改五个字段，其余原文保留。已创建的 run 绑定原配置，切换不改 run。
`set` 通过 cm-log-event.mjs 的 Python 锁适配器写独立的 `decision/route` 全局日志，
仅附 preset、source、项目路径；没有 specs 指针或业务任务状态写入。日志失败明确报告配置已保存。
