# PROGRESS · API 计算与 HTTP 拆类(2026-10-16) - 分支:`codex/api-server-service-extract-20261013` - 任务书里的基线 `2994588ad4d858dead4835943e5883c5aa2b03f8` 已过期。本分支从当前 `origin/staging` `fbf04f0b5f31cd8421bc4546840b1c833aaed810` 新建,没有上游。该提交已包含 #69 与 #68。 - 没有新 BUG 编号。结构改造,不写 `docs/BUG_HISTORY.md`,不写 `CHANGELOG.md`。 ## 开工重测 | 指标 | 任务书写作时 | 开工实测 | | --- | --- | --- | | `scripts/jyotish_api_server.py` 换行数 | 10970 | **10906** | | 四空格 `def`(合同口径) | 218 | **217**(BUG-1341 已下调,本单保持) | | `JyotishAPIHandler` 类方法 | 215 | **214** | | `JyotishAPIHandler.__new__` 合计 | 31 | **31**(`scripts/` 2 + `tests/` 29,不含合同文件) | | 行数基线 / 上限 | 10924 / 11224 | 基线不动。搬后换行数 **10920**,低于基线 | HTTP 侧按「直接碰 HTTP 成员或覆写 `BaseHTTPRequestHandler`,再向上收到调用闭包」重算,仍是 15 个方法,外加 `server_version`: - 直接:`_json`、`_html`、`_send_cors_headers`、`_enforce_request_security`、`_job_access_token`、`do_OPTIONS`、`do_GET`、`do_POST`、`_read_json_body` - 调用者:`_error_json`、`_require_dynamic_rectification_token`、`_get_high_rigor_job`、`_get_chart_job`、`_compute_dynamic_rectification_opportunities`、`_compute_dynamic_rectification_score` 类里没有 `self.xxx =` 赋值。 ## 做了什么 - 同一文件里把原类改成 `JyotishComputeService`(199 个计算方法),新增 `JyotishAPIHandler(BaseHTTPRequestHandler, JyotishComputeService)`,放入上面 15 个方法和 `server_version`。`ThreadingHTTPServer` 仍用 `JyotishAPIHandler`。 - 方法体与基线逐个 `ast.dump` 相等:旧类 214 个方法,新文件 214 个方法,不一致 **0**。换类的成员是这 15 个方法加 `server_version`,共 16 个。 - `scripts/consultation_workflow_service.py` 和 `scripts/capture_report_blocked_repairs_golden.py` 改为 `JyotishComputeService()`。`scripts/` 里 `JyotishAPIHandler.__new__` 为 **0**。 - 只用计算方法的 17 个测试文件同样改了构造。`tests/test_active_rectification_api.py` 里 3 处补丁从 HTTP 类改到计算类,否则补丁打不中实例。断言没改。 - 增长合同:总伪造 31 → **5**,生产伪造 2 → **0**,方法数 217 不变,行数基线 10924 不动。新增两条守卫:计算类不是 HTTP 处理器子类(并钉住 MRO 与 `server_version`);计算类方法体不碰 HTTP 成员、也不调用上述 15 个方法。 - 搬前 golden 连跑两次哈希一致,落在 `tests/golden/api_server_service_extract_v1.json`(只存 sha256 和键路径类型)。采集脚本:`scripts/research/api_service_extract_golden.py`。搬后对照由 `tests/test_api_service_extract_golden.py` 重算,通过。 构造语句三栏(17 个测试文件和 2 个生产文件,每处相同): | 原值 | 新值 | 原因 | | --- | --- | --- | | `JyotishAPIHandler.__new__(JyotishAPIHandler)` | `JyotishComputeService()` | 计算类可以正常实例化,不必再绕过 HTTP 构造 | `tests/test_active_rectification_api.py` 另有三处: | 原值 | 新值 | 原因 | | --- | --- | --- | | 补丁打在 `JyotishAPIHandler` | 补丁打在 `JyotishComputeService` | 实例已是计算类,补丁必须打在实例的类上。断言未改 | 未改的 HTTP 侧伪造(5 处,合同新基线就是这 5 处):`test_api_server_security.py`、`test_api_async_job_contract.py`、`test_runtime_security_p0.py`、`test_dynamic_rectification_scoring.py`、`test_professional_report_reference_api.py`。其中安全测试文件 `git diff` 为空。 ## 命令和结果 | 检查 | 命令 | 结果数字 | | --- | --- | --- | | 搬前 golden 连跑两次 | 基线上采集 MCP / HTTP / 报告裁剪 / 证据助手 | 8 个哈希两次相同 | | 搬后 golden | `pytest tests/test_api_service_extract_golden.py`(含在下面那条里) | 通过,与搬前哈希一致 | | 任务书点名测试 + 改过的测试 + 两个调用了采集入口的测试 | `pytest` 上述文件 `-q --tb=line` | **383 通过,1 失败**,约 1046 秒 | | 方法体对比 | 基线文件与工作树 `ast.dump` | 214 对 214,不一致 0 | | 反向:计算类临时写 `self.headers` | `test_compute_service_methods_do_not_touch_http` | 失败后已撤回 | | 反向:`scripts/` 临时加一处伪造标记 | `test_scripts_handler_forgeries_must_not_grow` | 1 > 0,失败后已撤回 | | 安全测试文件 | `git diff -- tests/test_api_server_security.py` | 空 | | 生产伪造 | 在 `scripts/**/*.py` 搜伪造标记 | **0** | | 换行数 | 文件内 `\n` 个数 | 10906 → **10920**(上限 11224) | | 隐私标记 | `pytest tests/test_repo_privacy_markers.py -q` | 退出码 0 | 搬前哈希(去掉两个墙钟字段后的规范 JSON): | 案例 | sha256 | 字节 | | --- | --- | ---: | | mcp.career | `ad38a539cf9036143333acd538567b7eb94528d537c46ef326905ebc47527a05` | 543868 | | http.career | `6a6ca421d942e2577e587647324ef9521ea14841a74a6850c11f629df4d9ba96` | 420555 | | mcp.wealth | `9aaacf64d08f17bca2d3f6b020b1dbb1ec07eaee7727b04bba90430fcbf594d3` | 542629 | | http.wealth | `f22368bbde38b76dc44e2995c34f7809ca96442405c1f13e39f53c4a2dd90a62` | 419316 | | mcp.relationship | `be70fd603b1de52026ad40f0d6582a27cb8b148018474fcd7d8c4134509b40ae` | 537785 | | http.relationship | `4405ae821aef81ec03660994d9f8f4104ae3388f339407bfe2721d05d662059b` | 414472 | | repairs | `41d48a453fbd5d616de083fe07bb8d318c3f60ad2e127764237ef6ae50cc6175` | 74700 | | evidence | `27975ba40a9ee89fc19543a18cb2ee60fb2477b8a6e8f14004ad411adfc408a2` | 2092 | MCP 与 HTTP 彼此不相等:MCP 走 `surface=skill_mcp`,HTTP 方法默认 `api_web`,并且 HTTP 方法还会多挂一层咨询全文。这是原来的两条路径,不是这次拆类造成的。每条路径搬前搬后各自一致。 ## 那 1 条失败 `tests/test_active_rectification_api.py::test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow` 期望时间窗 `05:07–05:08`,实际得到 `07:00–07:02`。把该测试文件还原到基线后再跑,失败内容相同。不是拆类引入的,也不是「用到了 HTTP」。按任务书,这种红不回退该文件。断言没改。同文件其余测试在本轮通过。 ## 偏离 - 任务书交付物写了「删两处死代码」。硬红线写明零引用函数只记进度、本单不删。`_strict_adjudication_bundle_from_contract` 与 `_high_rigor_job_path` 仍只在定义处出现,没有删。 - 后续「把咨询闭包搬出文件、改依赖方向」没做。那一步要改安全测试的补丁落点,任务书明确不做。 - `AGENTS.md` §6 没有写死伪造数字 31 或 2,所以没改措辞。 - `skills/jyotish-vedic-astrology/scripts/` 里的同名副本没改。任务书点名的是 `scripts/`。合同计数也不含技能副本。 - 另有若干 `object.__new__(JyotishAPIHandler)`,不在合同计数里,调用的是计算方法,经继承仍可用。本单没改。 - 没跑:Python 快速门、全量 pytest、前端 `npm test`、`test:db`。没动数据库,也没对生产或 staging 做压测或登录。