Merge remote-tracking branch 'origin/staging' into codex/api-server-service-extract-20261013

This commit is contained in:
jesse-ux
2026-10-16 11:30:31 +08:00
@@ -0,0 +1,72 @@
# TASK · API 单体拆类 · 修复单(issue #70,第 1 次打回)
- 日期:2026-10-16
- 被验提交:`c7350bcafbff64d3c526c2dbf3995c9845b3b66e`(分支 `codex/api-server-service-extract-20261013`)
- 原任务书:`docs/tasks/TASK-api-server-service-extract-20261013.md`
- 执行方:Grok;在**同一分支**上追加提交(基于 `c7350bcaf`,不改写已推历史);若 `origin/staging` 已前进,用 `git merge origin/staging`,不要 rebase
- BUG 编号:无(仍属结构改造)
## 验收结论(Claude,Linux)
拆类本身**通过**;新增的金样测试**未通过**,因此整单打回。
| 项 | 结论 | 证据 |
| --- | --- | --- |
| T2 纯移动 | 通过 | AST 对比:基线与本分支 215 个方法名集合相同、方法体逐个 `ast.dump` 相等;模块级函数与其他顶层语句全等;`JyotishComputeService` 200 个方法,`JyotishAPIHandler` 15 个 HTTP 方法 + `server_version` + docstring |
| T3 生产伪造归零 | 通过 | `grep -rn "JyotishAPIHandler.__new__" scripts` 0 命中 |
| T3 结果等价 | 通过(Claude 自证) | 基线工作树与本分支各跑 `capture_mcp('career')`(清缓存),逐字段比对只差 3 个 `manifest_path` 绝对路径(两个工作树目录名不同);基线连跑两次 0 差异;`repairs`、`evidence` 两路哈希两边相同 |
| T4 测试伪造下调 | 通过 | 17 个文件只改构造语句与类型注解;伪造合计 31 → 5 |
| T5 合同与守卫 | 通过(附一处文字要求,见 F3) | 两条新守卫在本机绿 |
| `test_api_server_security.py` 零改动 | 通过 | `git diff --stat` 空 |
| 本机 API 冒烟 | 通过 | 基线与本分支并排:`/api/health` 200、`/api/chart` 虚构盘 200(只差时间戳字段)、非 JSON 400、`text/plain` 415、3 MB 请求体 400、未知路径 404、OPTIONS 200、`Server: JyotishAPI/6.9.14` 头,响应体逐字相同 |
| **T1 金样测试** | **未通过** | 见 F1、F2 |
| 全量 pytest(`-n 4`)与基线逐名比对 | **未通过** | 本分支比基线多 2 条失败:`test_api_service_extract_golden.py::test_api_service_extract_golden_matches_baseline`(单独跑也红)、`test_capture_commercial_astrology_e2e_contexts.py::test_capture_writes_runtime_contexts_without_required_layer_echo`(单独跑绿,并行红,见 F2) |
## F1 · 金样哈希含本机绝对路径,换机器必红(必做)
**现象**:本机单独跑 `pytest tests/test_api_service_extract_golden.py`,7 条哈希全部不等,如 `http.career sha256 6a6ca4… != 0c8ef6… bytes 420555 != 441296`。
**根因**:咨询工作流响应里有 `external_parity_gate.manifest_path`、`real_case_calibration.holdout_replay_manifest.manifest_path`、`real_case_calibration.outcome_replay_manifest.manifest_path` 等字段,值是运行机器上的绝对路径。`canonical_text` 只去掉了两个墙钟字段,没处理路径,所以金样只在采集它的那台 Windows 机器、那个目录下成立。CI 和任何其他检出都会红。
**修法**:
1. 在 `scripts/research/api_service_extract_golden.py` 的规范化里,把所有字符串值中的仓库根目录绝对路径(`str(ROOT)`,以及 Windows 下的正斜杠 / 反斜杠两种写法)替换成固定占位符 `<ROOT>`,并把其后的路径分隔符统一成 `/`。不要按字段名白名单去删,避免以后新增路径字段又漏。
2. 在**基线**(`origin/staging` 上本单开工时的 SHA,写进 `fixtureProvenance`)重新采集金样,覆盖 `tests/golden/api_server_service_extract_v1.json`。
3. 在本分支上用 `check_golden()` 对照,全部相等。
**验收标准**:Claude 在 Linux 上、任意检出目录下单独跑这条测试为绿;Grok 在 Windows 上同样为绿。PROGRESS 里贴两次采集(基线、本分支)的 8 个哈希。
## F2 · 测试过程中删除共用排盘缓存目录,并行时打红别的测试(必做)
**现象**:全量 `pytest -n 4` 时 `test_capture_commercial_astrology_e2e_contexts.py::test_capture_writes_runtime_contexts_without_required_layer_echo` 变红;单独跑为绿,基线全量也为绿。
**根因**:`clear_chart_cache()` 对仓库内的 `scratch/local/api_chart_cache` 做 `shutil.rmtree`。全量测试多进程并行,其他测试正在读写同一个目录,被中途删掉。研究脚本手动跑时清缓存没问题,但放进 pytest 就成了共享状态破坏。
**修法**(二选一,推荐 a):
- a. 不删目录,改为在采集期间设置环境变量 `JYOTISH_API_CHART_CACHE_TTL_SECONDS=0` 让排盘缓存不生效(现有测试 `tests/test_commercial_domain_calculation_contract.py` 已用这个办法),采集结束恢复原值。先确认 TTL 为 0 时确实既不读也不写缓存;若仍会写入,则同时用 `monkeypatch` / 临时目录把 `_api_chart_cache_dir` 指向 `tmp_path`。
- b. 测试里用 `monkeypatch.setattr(jyotish_api_server, "_api_chart_cache_dir", lambda: tmp_path / "cache")`,研究脚本的 `clear_chart_cache` 只清这个临时目录。
**验收标准**:
- 金样测试里不再出现对仓库内 `scratch/local/api_chart_cache` 的删除(`grep -n rmtree` 只允许作用于临时目录)。
- 本分支全量 `pytest -n 4` 的失败清单与基线逐名一致(Claude 复跑)。
- 金样结论不变(缓存不生效时与清空缓存时的输出相同;如有差异,在 PROGRESS 写明并以「缓存不生效」为准重采)。
## F3 · 方法数常量旁的旧三栏被改写(必做,小)
`tests/test_api_server_growth_contract.py` 里 `JYOTISH_API_HANDLER_METHOD_COUNT_BASELINE` 上方原有的三栏(`原值:218(删占位回退之前)/ 新值:217 / 原因:BUG-1341 …`)被改成了 `原值:217 / 新值:217 / 原因:…`,丢了 BUG-1341 那次下调的记录。
**修法**:恢复原来三行原样;本单的说明(数值未变)如需保留,另起一行注释写在其后,不覆盖历史。
**验收标准**:`git diff origin/staging -- tests/test_api_server_growth_contract.py` 中,原有三栏三行不再出现在删除行里。
## 不要做
- 不改拆类本身(T2/T3/T4 已通过),不动 `scripts/jyotish_api_server.py`。
- 不改 `tests/test_api_server_security.py`。
- 不放宽金样比对(不许改成只比键路径或字节数)。
## 回报
在 issue #70 回报新 SHA,表格里写:金样单独跑结果(Windows)、全量 pytest 失败清单相对基线的差(若 Windows 能跑)、F3 的 diff 片段。