From bfcfbaef92a16f4e620e340422cbab2bd83b6374 Mon Sep 17 00:00:00 2001 From: lhl Date: Sun, 9 Aug 2026 15:23:32 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E9=94=99=E8=AF=AF=E7=A0=81=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=80=A7=E6=B5=8B=E8=AF=95=E5=AE=9E=E6=96=BD=E8=AE=A1?= =?UTF-8?q?=E5=88=92=EF=BC=88TDD=20=E5=8D=95=E4=BB=BB=E5=8A=A1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-08-09-error-code-consistency-test.md | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-09-error-code-consistency-test.md diff --git a/docs/superpowers/plans/2026-08-09-error-code-consistency-test.md b/docs/superpowers/plans/2026-08-09-error-code-consistency-test.md new file mode 100644 index 0000000..75335ce --- /dev/null +++ b/docs/superpowers/plans/2026-08-09-error-code-consistency-test.md @@ -0,0 +1,176 @@ +# 错误码一致性测试实施计划(api §7 ↔ exceptions.py) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 新增回归测试解析 `docs/api-design.md` §7 表格,与 `exceptions.py` 的 error_code 双向一致验证,防文档↔代码漂移。 + +**Architecture:** 新建 `tests/test_api_design_consistency.py`,内联正则解析函数 + 4 个测试用例;零生产代码改动。断言失败带双向差异清单(DX 修正 F1/F2);0 行解析硬失败护栏(F3);契约 docstring(F4)。 + +**Tech Stack:** Python 3.11+、pytest、re、pathlib + +## Global Constraints + +- 项目为中文交流:注释、docstring、commit 消息用中文;标识符英文 +- 零真实网络:本任务是纯本地文件解析,不得引入网络/API Key +- 覆盖红线:`pyproject.toml` `fail_under = 99`(当前基线 128 passed / 100.00%);本任务零生产代码,红线不受触碰 +- 全量回归:`python -m pytest -q`;聚焦:`python -m pytest tests/test_api_design_consistency.py -v` +- 提交消息:`feat:` / `test:` / `docs:` + 简中文描述 +- 每次修改后追加 `_AI_USAGE_LOG.md`(范式步骤:"测试验证",模型:"deepseek-v4-flash-free") +- 本仓库工作在 master 分支(项目惯例);Windows/PowerShell 环境 +- Spec: `docs/superpowers/specs/2026-08-09-error-code-consistency-test-design.md` + +--- + +### Task 1: 新建一致性门禁测试文件 + +**Files:** +- Create: `tests/test_api_design_consistency.py` +- Modify: `_AI_USAGE_LOG.md`(追加一行) + +**Interfaces:** +- Consumes: `src/genesis/inference/exceptions.py` 的 `LLMError.error_code` 类属性(基类 `None`;四子类 `LLM_TIMEOUT`/`LLM_NETWORK_ERROR`/`LLM_NOT_CONFIGURED`/`LLM_PARSE_ERROR`);`docs/api-design.md` §7 表格(L311-314 五行 LLM 行,行格式 `| \`CODE\` | 含义 | ... | exceptions.Xxx |`) +- Produces: 无(测试文件独立,无下游依赖) + +- [ ] **Step 1: 写失败测试** + +创建 `tests/test_api_design_consistency.py`(用例引用解析函数,解析函数尚未实现 → RED): + +```python +"""错误码一致性门禁:api-design §7 ↔ 异常树 error_code 的机器契约。 + +变更错误码必须两处同步修改:api-design.md §7 错误码表 +与 src/genesis/inference/exceptions.py 的 error_code 类属性。 +本测试防文档漂移:文档多/少/错一行都会变红,且失败信息给出差异清单。 +""" + +from __future__ import annotations + +import re +from pathlib import Path + +from genesis.inference.exceptions import LLMError + +# 项目根定位(与 tests/test_real_samples.py:7 同款先例;不依赖 CWD) +API_DOC = Path(__file__).resolve().parents[1] / "docs/api-design.md" + +# §7 表格 LLM 行:| `CODE` | 语义 | ... | exceptions.Xxx | +_LLM_ROW_RE = re.compile( + r"^\|\s*`([A-Z][A-Z_]+)`\s*\|.*\|\s*exceptions\.([A-Za-z_]+)\s*\|$" +) + + +def _extract_llm_error_rows(document: str) -> dict[str, str]: + """解析 §7 表格中来源列以 exceptions. 开头的行,返回 {错误码: 来源类名}。 + + 非 LLM 行(来源列为 "—")被天然排除。 + """ + mapping: dict[str, str] = {} + for line in document.splitlines(): + m = _LLM_ROW_RE.match(line.strip()) + if m: + mapping[m.group(1)] = m.group(2) + return mapping + + +def _llm_exception_classes() -> list[type[LLMError]]: + """error_code 非 None 的全部 LLM 异常子类(不硬编码清单,新增异常自动纳入)。""" + return sorted( + (cls for cls in LLMError.__subclasses__() if cls.error_code is not None), + key=lambda cls: cls.__name__, + ) + + +def test_error_doc_lists_every_llm_error_code(): + # 异常树 → 文档:每个子类 error_code 必须登记在 §7,且来源类名匹配 + mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8")) + missing = { + cls.__name__: cls.error_code + for cls in _llm_exception_classes() + if cls.error_code not in mapping or mapping[cls.error_code] != cls.__name__ + } + assert not missing, ( + "异常类存在文档未登记的 error_code(请同步 api-design.md §7):\n" + + repr(missing) + ) + + +def test_error_doc_llm_rows_resolve_to_exception(): + # 文档 → 异常树:每个 LLM 行都要能解析到同码同名的异常类 + mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8")) + by_code = {cls.error_code: cls.__name__ for cls in _llm_exception_classes()} + unresolved = {code: name for code, name in mapping.items() if by_code.get(code) != name} + assert not unresolved, ( + "§7 LLM 行解析不到对应异常类(请核对来源列或错误码名):\n" + + repr(unresolved) + ) + + +def test_error_doc_stable_llm_row_count(): + # 格式护栏:LLM 行数 === 5;新增/删除 LLM 错误码需显式更新此数 + mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8")) + assert len(mapping) == 5, ( + f"api §7 LLM 错误码行数应为 5,实际 {len(mapping)};" + "新增/删除错误码须同步更新此计数。" + ) + + +def test_error_doc_parse_guard_prevents_silent_noop(): + # 格式护栏:解析结果为空须硬失败(表格格式变更会静默失效,这里立即暴露) + mapping = _extract_llm_error_rows(API_DOC.read_text(encoding="utf-8")) + assert mapping, "api §7 表格解析结果为空:若无匹配行,请检查表格格式与 _LLM_ROW_RE 是否一致。" +``` + +- [ ] **Step 2: 运行验证失败** + +Run: `python -m pytest tests/test_api_design_consistency.py -v` +Expected: FAIL(`ImportError: cannot import name 'LLMError' from 'genesis.inference.exceptions'` 或 `ModuleNotFoundError`——取决于包导入是否已通;若 import 正常则 4 用例中 count/guard 断言失败。**真实预期:导入路径正确则 `_extract_llm_error_rows` 已定义、用例应 PASS** ——正确行为是 TDD 反向(先红),因此若首跑即绿,说明本任务实质是「交付完整测试」,红绿灯以 Step 4 全绿为准;此步只需记录实际输出,若因实现已被写全而直接绿,在报告中注明「RED 无步骤,直接 GREEN」) + +> 说明:本任务整体是一次性创建完整测试文件,TDD 的 RED 信号是"文件不存在时 import 失败"。若你想严格走红,可在 Step 1 先只写用例 + 在调用 `_extract_llm_error_rows` 处留 `raise NotImplementedError`,实现后再替换——不必须,直接完整创建并在 Step 2 观察实际结果即可。 + +- [ ] **Step 3: 实现(完整文件已就位)** + +Step 1 已为完整文件的全部内容——无需额外改实现代码。若 Step 2 是 RED(import 或断言失败),对照 Step 1 代码修正后即转绿。 + +- [ ] **Step 4: 运行验证通过** + +Run: `python -m pytest tests/test_api_design_consistency.py -v` +Expected: PASS(4 passed) + +- [ ] **Step 5: 全量回归** + +Run: `python -m pytest -q` +Expected: PASS(128 + 4 = ≥ 132 passed / 覆盖 100.00%,`fail_under=99` 达标) + +- [ ] **Step 6: 追加 `_AI_USAGE_LOG.md`** + +范式步骤:"测试验证",摘要:新增 api §7 ↔ 异常树 error_code 一致性门禁测试(4 用例:双向断言 + 行数/空解析护栏,防文档漂移)。 + +- [ ] **Step 7: 提交** + +```bash +git add tests/test_api_design_consistency.py _AI_USAGE_LOG.md +git commit -m "test: api §7 与异常树错误码一致性门禁测试(防文档漂移)" +``` + +--- + +## Self-Review + +**1. Spec 覆盖:** +- §3.1 落点 → Task 1 新建 `tests/test_api_design_consistency.py` ✓ +- §3.2 文档定位 → `API_DOC = Path(__file__)....parents[1] / "docs/api-design.md"` ✓ +- §3.3 解析函数 → `_extract_llm_error_rows` ✓ +- §3.4 四用例 → 测试 1(树→文档)/测试 2(文档→树)/测试 3(行数护栏)/测试 4(空解析护栏)✓ +- §3.5 失败可诊断 → `assert not missing, ( "…差异清单" )` 含双向差异 ✓ +- §3.6 契约 docstring → 文件头 docstring ✓ +- §4 错误处理 → 文档缺失 `read_text` 抛 FileNotFoundError;空解析护栏 ✓ +- §5 测试策略 → Step 4 聚焦 + Step 5 全量 ✓ +- §6 验收 1-4 → Step 4 / Step 5 / 手工破坏验证可选 §4 → Step 7 提交 ✓ + +**2. 占位符检查:** 无 TBD/TODO;测试代码全文可见。✓ + +**3. 类型一致性:** `mapping: dict[str, str]`(错误码:来源类名)在解析函数与 3 个用例间一致;`_llm_exception_classes()` 返回 `list[type[LLMError]]` 与使用处一致。✓ + +## 提交消息汇总 + +- Task 1: `test: api §7 与异常树错误码一致性门禁测试(防文档漂移)` \ No newline at end of file