---
name: contract-first
description: >-
  分前端/后端（或多个服务）多端开发的项目，用 CONTRACT.md 指向的唯一机器契约，各端只照它各做各的，防止字段漂移导致集成时白屏。支持单会话多 agent 和多终端各自跑两种模式。只要项目有前后端/多服务、接口字段老对不上、各端联调卡住、某端改了字段忘了通知别人、或前端为渲染一个页面要调一堆接口拼数据，就用这个 skill，哪怕用户没明说"契约"。这套方法论的学名是消费者驱动契约（Consumer-Driven Contracts, CDC）/ 契约测试。中文触发：契约式开发、接口契约、前后端协作、多终端协作、防字段漂移、接口对不上、联调、API 契约、字段命名不一致、集成白屏、唯一真相源、契约测试、CDC。English triggers: contract-first development, consumer-driven contracts, contract testing, API contract, frontend backend collaboration, multi-terminal, prevent field drift, provider verification.
metadata:
  origin: ECC
---

# 契约优先（Contract-First / Consumer-Driven Contracts）

多端并行开发的项目（前端 + 后端，或再加多个服务），最容易炸在"连接"那一刻：

> 后端把 `userName` 改成 `user_name`，忘了通知前端。两边各自"自测通过"，一集成——白屏。**两份文档各自为真，合起来是假的。**

**契约优先**把接口当成一份**唯一机器契约**（由 `CONTRACT.md` 登记入口）：所有数据接口只定义一次，各端照它各做各的，谁都不许私自偏离。它和 `living-docs-governance` 是姊妹篇——那套防"项目文档"漂移，这套防"端与端之间的接口"漂移。

> 学名：这套就是 **消费者驱动契约（Consumer-Driven Contracts, CDC）/ 契约测试（contract testing）**。"消费方需求先行"=CDC 核心；"后端写返回符合契约的测试"=提供者验证（provider verification）；标杆工具是 Pact，契约规格常用 OpenAPI/Swagger。

## 什么时候启用

- 项目分前端 + 后端（或多个服务），且各端可能**并行**开发。
- 接口字段老对不上：`userName` vs `user_name`、类型不符、枚举值不一致。
- 某端为渲染一个页面要调 5 个接口拼数据。
- 某端改了接口忘了通知别人，集成时才发现。

**不要**用在只有单端、不存在跨端集成的项目上——那时退化成单层，用 `living-docs-governance` 即可。接口少、单人、不会漂移时也别上，过度工程化。

## 两种协作模式（关键：选对你的现实）

这套契约协作有两种落地方式，纪律一致、组织方式不同：

### 模式 A — 单会话多 agent（中心化派活）
一个支持多 agent 的会话里，契约拥有者**派出**前端 / 后端（及更多服务工人）并行干活，最后由它集成对账。Claude Code 可使用 `contract-director`、`frontend-dev`、`backend-dev`；Codex 可由当前 agent 持有契约并使用内置 worker，任务提示中明确端别、文件所有权和“只读契约”的边界。适合一人一个会话内推进、需要实时编排时。

### 模式 B — 多终端各自跑（去中心化，契约当异步媒介）⭐ 更贴近真实团队
终端1 跑前端、终端2 跑后端、终端N 跑某个服务，**各端完全独立、上下文隔离**，**没有一个活的主任在线派活**。协调的唯一媒介就是那份 `CONTRACT.md` 文件：

- "主任"在这里**退化成"契约拥有者"**——就是定契约、有权改契约那个人/终端（很可能是你本人或某个指定终端），不是实时调度器。
- 各端要改接口时，不存在"喊一个在线 agent"，而是**提一条"契约变更请求"**：写进约定位置（如 Issue，或 `PROJECT_LOG.md` 追加一条 `contract-request`），由契约拥有者评估后更新契约，各端再各自重新拉取对齐。
- 适合双终端/多终端、多人、跨时区——这才是大多数真实前后端团队的样子。

**两种模式的铁律完全相同**：接口只在 `CONTRACT.md` 指向的机器契约定义一次；各端只读不改；要改接口必须先改契约，绝不在实现里私自偏离。

> 宿主适配：Claude Code 的 `/contract` 与自定义 agents 是交互适配层；Codex / ChatGPT 直接调用 `$contract-first` 并由当前 agent 执行同一流程。没有可用子 agent 时退化为顺序执行，不得因此跳过契约前置、提供方验证或集成对账。

## 三条核心纪律

### 1. 契约是唯一真相源，只有一个拥有者，且分两层

**将协作约定与机器定义分开，字段只保留一个来源**：

- **入口与协作层**（`CONTRACT.md`）：登记机器契约路径、版本/hash、拥有者、生成/校验命令和兼容策略；不手抄字段表。
- **机器定义层**：沿用项目已有 OpenAPI / JSON Schema / GraphQL / protobuf。HTTP 项目无现有契约时可用 `templates/openapi.example.json`；方法、路径、字段、类型、错误响应只在机器契约定义，重复类型用引用复用。

跨接口字段约束通过机器契约的公共 schema 或类型定义复用。所有接口只在 `CONTRACT.md` 指向的机器契约定义**一次**，各端只读；改契约的权力归**契约拥有者**（模式 A 是 director，模式 B 是指定的人/终端）。要改接口 → 提契约变更请求 → 拥有者改契约 → 各端再对齐。**绝不在实现里单方偏离契约**——这是头号集成杀手。

### 2. 消费方需求先行（CDC 核心：别让提供方拍脑袋定）

接口是给消费方（如前端）用的，**先看消费方渲染/使用需要什么**，再定接口形状，而不是照着数据库表结构透传。定契约时优先问：

- 这个页面/调用方实际需要哪些字段？一次请求能不能拿全？
- 字段类型有没有坑？（19 位商品 ID 必须 `string`，用 `number` 会截零；金额用 `number` 保留 2 位；状态用枚举别用裸字符串）
- 分页、错误码、空值怎么约定？

### 3. 让契约机器可校验，谁偏离谁先红

- 机器契约必须能被对应格式的标准工具直接解析和校验；JSONC 响应示例、Markdown 字段表与内部 DTO 不能替代 schema。`CONTRACT.md` 使用 `templates/CONTRACT.example.md` 只登记权威入口。
- 消费方拿它**生成类型和 mock**（提供方没好也能先把界面跑起来）。
- 提供方拿它写**"返回必须符合契约"的校验测试**（即 provider verification）——提供方改实现不小心偏离了，**自己的测试先红，炸在自己这边，炸不到别人**。

## 工作流程

> 模式 A 由 `contract-director` 串起全流程；模式 B 下每端在自己终端各做第 1、2、4 步，第 3 步（定契约）和第 5 步（对账）由契约拥有者做。

1. **先反问消歧义，再定契约。** 定契约前，就模糊点反问消费方（字段语义、类型、空值怎么传、枚举到底有哪几个），把歧义消灭在动手前（借 Spec Kit 的 `/clarify` 思路）。然后按"消费方需求先行"更新唯一机器契约，并在 `CONTRACT.md` 登记入口和版本（模板见 `templates/CONTRACT.example.md`）。**契约必须前置**——绝不先写实现、再从代码事后导出契约，那样契约永远滞后、必然漂移。
2. **各端以契约为强制起点开发。** 每端读取机器契约对应段、只读不改。每个接口任务**第一步就是读 `CONTRACT.md` 及其机器契约**，不是凭记忆；复杂改动先声明"我打算怎么对齐契约"，审过再写代码——在偏离前就拦下来。
3. **要改接口 → 提契约变更请求。** 不在实现里偷改。模式 A 回报 director；模式 B 写进约定位置（Issue 或 `PROJECT_LOG.md` 的 `contract-request`），由契约拥有者裁决后更新契约。破坏性变化必须同时写明兼容期、消费者迁移顺序、回滚条件和不可逆部分；缺失时不进入实现。
4. **本端自检。** 消费方回查所有用到的字段是否都在契约里；提供方跑契约校验测试。
5. **集成对账。** 逐字段核对：提供方返回 vs 契约、消费方用到的字段 vs 契约、字段名大小写/枚举值是否一致。对不上 → 指出哪边偏离、让其修正；若契约本身不合理 → 契约拥有者改契约再让各端对齐。
6. **记账。** 契约有变更 → 往 `PROJECT_LOG.md` 追加一行 `## [日期] contract | 改了什么接口、为什么`（与 `living-docs-governance` 共用同一本流水账）。

## 例子

- **字段名漂移**：前端按契约用 `userName`，后端数据库列叫 `user_name`。后端在接口层做映射，对外一律按契约 `userName`，集成对得上。
- **多终端不用互等**：契约先定好，前端在终端1按契约造 mock 把整个下单页跑通，后端在终端2按契约写实现 + 校验测试，两边并行、互不打扰，联调时一次对齐。
- **多终端改字段**：终端1 前端发现少个字段，不去打断终端2，而是在契约"待定变更"区写一条请求；契约拥有者评估后更新 `CONTRACT.md`，两个终端各自重新拉取对齐。

## 相关

- `contract-director`（契约拥有者/对账）、`frontend-dev` / `backend-dev`（各端工人）—— 执行这套方法论的 agent，两种模式通用。
- `living-docs-governance` skill —— 防项目文档漂移的姊妹篇；两套共用一本 `PROJECT_LOG.md`。


## 角色边界与执行证据

- 契约拥有者只维护契约、处理变更请求、按已选择模式分工与集成，不实现业务代码；派工注明文件所有权，各端不得回退其他协作者的改动。
- 消费方只写分配的消费方目录；提供方只写分配的提供方目录。两者均只读契约，变更请求写到约定的 Issue/LOG，不越权编辑契约入口的“待定变更”区。
- 消费方从同一机器契约生成或校验类型和 mock；没有提供方时可先做契约已定义范围内的页面，不能把新猜测字段塞入 mock 当真。
- 提供方验证真实序列化响应，覆盖字段改名、大整数 ID、空值、枚举和错误结构；内部 DTO 或状态码为 200 不是足够证据。
- 用 `test-collaboration` 将契约格式、消费者、提供者、真实联调四层证据关联到同一 TEST-ID 和契约版本；未跑真实联调时标缺口。
- 模板参考 `tests/test_contract_template.py` 只证明模板格式和响应约束，不证明用户项目已经生成类型、实现服务或完成联调。
