Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,137 @@

CodeRoughcollie 仓库的 AI Agent 与人类协作约定。

## TDD 工作流(Red → Green → Refactor)

本仓库采用测试驱动开发。一次循环只锁定一个行为:先写会失败的测试(Red),再写最小实现让它通过(Green),最后在测试全绿的前提下重构(Refactor)。探索草稿不得直接合入,必须按本文件用 TDD 重写。

### 先读再改
1. 确认改动落在哪个 crate(本仓库是 Cargo workspace,见「仓库地图」)。
2. 只用本文件列出的 cargo 命令;不要发明裸 `cargo update`、不要擅自切换 toolchain(以 `rust-toolchain.toml` 为准)。
3. 先跑与改动相关的最小测试;提交前再跑 workspace 门禁(fmt + clippy + test)。
4. 完成一个循环后按「完成标准与汇报」汇报,不要只说「做完了」。

### Never / Ask first / Always

**Never(不必请示,直接禁止)**
- 删除、注释、跳过已有测试:`#[ignore]`、注释掉 `#[test]`、把断言改成 `is_ok()` / `unwrap()` 了事
- 修改人类已有测试的断言来迁就实现
- 先提交无测试的业务行为,再「回头补」
- 写永真测试:无断言、只检查 `is_some()`、只 verify 调用次数不查参数与状态
- 用全量端到端测试覆盖本可单测完成的改动
- 提交半成品;每次对人类可见的结果必须能构建且相关测试为绿
- 把探索草稿、临时脚本、调试 `dbg!`/`println!` 留在主代码

**Ask first**
- 改人类已有测试(含断言、fixture、golden 期望值)
- 新增运行时依赖、`unsafe`、新的 workspace crate、新的外部服务
- 为不可测代码做超出当前改动路径的重构
- 接受/更新 golden file 或固定 fixture 的期望值,且行为含义发生变化(本仓库自有 `crates/` 未引入 `insta`;`lib/*/` 子模块里的 `insta` 快照属于上游,见「子模块策略」)
- 关闭 clippy lint、新增 `#[allow]`

**Always**
- 改遗留路径前:先写特征测试,锁定当前可观察行为(允许丑,必须可重复)
- 新行为:先有会失败的行为断言,再写最少实现
- 难以测试时:先造接缝,再写测试(见「遗留代码与接缝」)
- 测试名描述行为:`should_reject_negative_amount`
- 现有测试因你的改动失败:修实现,不修测试(除非人类明确要求)

测试权限:

| 测试来源 | 权限 |
|---|---|
| 人类已有测试 | 只读 |
| 本任务新建测试 | 可改,直到该行为稳定 |
| 过时或环境偶发失败 | 只报告,不擅自跳过 |

### 工作流

**Red** — 写生产行为之前先写测试;测试必须能被收集且必须失败(断言失败,或因缺失 API 导致编译失败,二者都算合法 Red)。修改已有功能先写特征测试锁定当前输出。一次只加一个行为的测试。

**Green** — 只写让当前失败测试通过的最少代码。禁止删掉/改掉失败测试、一次引入多个未验证变更、用更宽断言或 `unwrap()` 换绿。

**Refactor** — 相关测试全绿后才重构;重构后立刻跑同一组测试;范围限于当前 crate。

**探索 vs 实现** — 需求或方案不清可写草稿验证;草稿不得合并;方案确定后必须走 TDD 重写。

### 遗留代码与接缝

**特征测试** — 锁定现有行为,不是证明它正确。本仓库自有 `crates/` **未引入 `insta`**,用固定 fixture + 显式断言,或与 golden 文件逐字比对。更新期望值必须在汇报里写清 diff 含义;默认不接受「看起来差不多」。

**接缝(优先顺序,靠后的更差)**
1. trait + 泛型或 `impl Trait`,测试用假类型
2. 用类型去掉非法状态(enum / newtype),而不是在测试里补分支
3. 时钟、ID、熵、文件系统做成可注入依赖;测试用 `tempfile` / 内存实现
4. `unsafe` 不是接缝。新增 `unsafe` 必须 Ask first,并写 `SAFETY` 注释

只给即将修改的代码路径补测试,不要一次性给整个模块「补全覆盖率」。

### 测试分层

| 层级 | 位置 | 测什么 |
|---|---|---|
| 单元 | `src` 内 `#[cfg(test)] mod tests` | 模块不变量、错误类型、状态转换 |
| 集成 | `tests/*.rs` | 公共 API;不可访问私有项 |
| 文档测试 | `///` 示例 | 公共 API 必须可运行;禁止滥用 `no_run` |
| CLI/二进制 | 项目惯用方式 | 退出码与 stdout 契约 |
| 不变量 | `proptest`(项目已用时) | 往返解析、幂等、单调性 |
| 特征/golden | 固定 fixture(本仓库未引入 `insta`) | 遗留输出;更新期望值必须说明 |

不要把本该测公共契约的内容塞进 `#[cfg(test)]` 去读私有字段。

Rust 的 Red 允许是:测试引用了尚不存在的类型/函数导致编译失败。不要为了先编译而写空 `todo!()` 实现再补测试——可以留 `todo!()` 仅作为 Green 的最小占位,且下一步必须替换。

### Rust Never 补遗
- 库代码(非 main/example/测试)用 `unwrap` / `expect` / `panic!` 做控制流
- 无必要 `unsafe`;有则必须 `SAFETY` 注释
- 一次性 `cargo update` 整个 lockfile
- 用 `#[allow(...)]` 静默应修复的 lint
- 为绿而改 golden/期望值却不解释行为是否应该变

### 命令

```bash
# 单测(单 crate,按测试名过滤)
cargo test -p <crate> <test_name>

# 提交前门禁(CI 门禁,顺序:fmt → clippy → test)
# OUR_CRATES 与 .github/workflows/ci.yml 的同名变量一致——只覆盖本仓库自有 crate
OUR_CRATES='-p cr-core -p cr-db -p cr-git -p cr-config -p cr-report -p cr-audit-static -p cr-audit-explain -p cr-audit-complexity -p cr-audit-impact -p cr-plugin -p cr-mcp-server -p cr-cli -p cr-server'
cargo fmt $OUR_CRATES -- --check
cargo clippy $OUR_CRATES -- -D warnings
cargo test $OUR_CRATES
```

> **CRITICAL**: TDD 在子模块目录内不适用——`lib/<name>/` 的改动必须走上游仓库(见「子模块策略」)。本文件的 TDD 工作流仅针对本仓库自有 crate(`crates/`)。
>
> **不要用 `--workspace`。** 子模块里的 crate 是 path 依赖,已被 cargo 拉进本 workspace:`cargo metadata` 目前解析出 25 个 package(`gaussdb`、`tokio-opengauss`、`ogexplain-core`、`ogsql-complexity`、`metamorphosis-core`、`metamorphosis-rules` 等都在内,只有 `lib/codeweb` 被 `exclude`)。`cargo clippy/test/fmt --workspace` 会把门禁打到上游代码上,与本节的子模块策略直接冲突,还会在子模块工作树留下产物。一律用上面的 `$OUR_CRATES` 显式枚举。

循环内只跑受影响 crate(`cargo test -p <crate>`);提交前跑上面的 `$OUR_CRATES` 全量门禁。

> 注意:CI 的 `cargo clippy $OUR_CRATES` 没有 `-D warnings`,`cargo audit` 是 `|| true`(不阻塞)。本文件要求比 CI 严——warning 与 audit 结论都要处理,不要因为「CI 绿」就放过。

### 完成标准与汇报

提交或交还人类前,确认:
- [ ] 新行为有失败→通过的测试
- [ ] 修改的遗留路径有特征测试
- [ ] 未删除、跳过、改写人类已有测试
- [ ] 已跑与改动匹配的门禁(fmt + clippy + test)
- [ ] `cargo fmt` 与 clippy 干净
- [ ] 没有把草稿、调试输出、无主 lockfile 大面积变更带上

每个 TDD 循环汇报:
1. 测试了什么行为(测试函数名)
2. 最小实现改了哪些文件
3. 是否重构、边界在哪
4. 实际执行的命令和结果(通过 / 失败原因;不要只写「测过了」)

### 质量判断(自我检查)
- 这条测试在实现写错时会失败吗?
- 我是否在测行为,而不是私有实现细节?
- 我是否用 skip、更宽断言、unwrap、golden 盲收换绿?
- 命令是否来自本文件,而不是我编的?

---

## 子模块(Submodule)策略 —— 强制规则
Expand Down
Loading