- dasha-unify: fix Ashtottari birth balance (BUG-1344) - rect-scoring-perf: server-side candidate cap 241 (BUG-1345) - node-runtime-resources: enable user-side session cookie cache, 300 s (BUG-1346) - consult-prep-parallel: classifier/reserve concurrency declined - answer-effort-ab: model key supplied by product at acceptance - drop stricter-than-test line-count red lines (AGENTS.md section 6) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Ad9rqAB8pXXvyzh7f4X8v
275 lines
29 KiB
Markdown
275 lines
29 KiB
Markdown
# TASK · Python 卫生:免费层排队开关报反、静默吞异常、功能吉凶兜底三份合一、测试专用脚本归位(issue #待定)
|
||
|
||
- 日期:2026-10-13
|
||
- 来源:2026-10-13 全仓屎山与性能排查 ·「Python 卫生」条目(开关重名、吞异常、功能吉凶兜底重复、`scripts/` 根目录 443 个文件)
|
||
- 执行方:**Grok(Windows + Docker)**,分支 `codex/python-hygiene-20261013`,只推这个分支
|
||
- 验收:Claude(Linux)
|
||
- BUG 编号:**BUG-1340**(开工时核对 `docs/BUG_HISTORY.md` 最大号;写作时最大号 BUG-1317;号段由主会话统一分配)(另:`TASK-upstream-sync7-20261014` 占 BUG-1318~1322,本批号段整体从 BUG-1323 起)
|
||
- **开工前置**:`api-disk-cache-retention`、`api-chart-subprocess-gate` 两单已合入 `origin/staging`(两单都改 `scripts/jyotish_api_server.py`;本单 T2 也改它,必须串行)
|
||
- **主会话补充的串行链(2026-10-14)**:引擎/API 文件链按此顺序逐单合入后开工(`upstream-sync7` 也改 `jyotish_engine.py` / `jyotish_api_server.py`,若在途,后合一方 rebase):`api-disk-cache-retention` + `report-reference-perf` → `api-chart-subprocess-gate` → `pl9-render-split` → `python-hygiene` → `dasha-unify` → `api-server-service-extract`。本单的前置以这条链为准(与上一行不一致时以本行为准)。
|
||
- **后继**:`dasha-unify`(BUG-1341)、`api-server-service-extract` 依赖本单合入
|
||
|
||
## 1. 基线
|
||
|
||
- 基线 commit:`origin/staging` `77057a819`(写作时);开工以前置两单合入后的 `origin/staging` 为准,第 9 节命令会核对。
|
||
- 所有行号以写作基线为准,执行时按符号定位。
|
||
|
||
## 2. 事故实证(`77057a819`,全部可复现)
|
||
|
||
### 2.1 BUG-1340:免费层排队「开关」报的状态与实际行为相反
|
||
|
||
排查报告原说法是「`offline_compute_mixins.py:86` 只认三个变量名中的一个,所以配了可能不生效」。**核实后这一说法不成立**:`scripts/offline_compute_mixins.py::_free_tier_queue_enabled_env` 三个名字都读(`VEDASTRO_FREE_TIER_QUEUE` / `_ENABLED` / `VEDASTRO_ENABLE_FREE_TIER_QUEUE`)。真正的问题更糟:
|
||
|
||
- 这三个变量**不控制任何行为**。真正的限流在 `scripts/vedastro_service_adapter.py::_acquire_free_tier_slot`,它只看三件事:请求地址是不是官方公共端点、有没有 `VEDASTRO_API_KEY`、`VEDASTRO_FREE_TIER_MAX_REQUESTS` / `VEDASTRO_FREE_TIER_WINDOW_SECONDS`(默认 5 次 / 60 秒)是否大于 0。
|
||
- 三个变量只被三处**报告字段**读取,每处各写一遍解析:`offline_compute_mixins.py::_free_tier_queue_enabled_env`(进 `free_tier_strategy.queue_enabled`)、`vedastro_user_entrypoint.py::_cache_and_queue_report`、`diagnose_vedastro_mode.py`(诊断输出 `free_tier_queue_enabled`)。
|
||
- 结果:审计字段与实际限流**两个方向都会报错**。
|
||
|
||
复现(虚构环境,无网络):
|
||
|
||
```bash
|
||
cd <工作树>
|
||
env -u VEDASTRO_API_KEY -u VEDASTRO_FREE_TIER_QUEUE PYTHONPATH=.:scripts python - <<'EOF'
|
||
import os
|
||
from offline_compute_mixins import _free_tier_queue_enabled_env
|
||
import vedastro_service_adapter as a
|
||
url = "https://api.vedastro.org/api/Calculate/x"
|
||
print(_free_tier_queue_enabled_env(), a._acquire_free_tier_slot(url)["mode"])
|
||
os.environ["VEDASTRO_FREE_TIER_QUEUE"] = "1"; os.environ["VEDASTRO_FREE_TIER_MAX_REQUESTS"] = "0"
|
||
print(_free_tier_queue_enabled_env(), a._acquire_free_tier_slot(url)["mode"])
|
||
EOF
|
||
```
|
||
|
||
写作时实测:
|
||
|
||
| 配置 | 报告字段 `queue_enabled` | 实际 `_acquire_free_tier_slot` |
|
||
| --- | --- | --- |
|
||
| 不配任何排队变量、无 key | `False` | `free_tier_queue_active`(**在限流**) |
|
||
| `VEDASTRO_FREE_TIER_QUEUE=1`、`MAX_REQUESTS=0` | `True` | `free_tier_queue_disabled`(**没限流**) |
|
||
|
||
此外 `scripts/vedastro_gateway.py::build_gateway_config` 的 `queue_enabled` 读的是另一组名字(`VEDASTRO_GATEWAY_QUEUE_ENABLED` / `VEDASTRO_QUEUE_ENABLED`),含义是「网关离线排队作业」,与免费层限流无关,但 `docs/engine/vedastro-gateway.md` 把两者写在同一句里(「`VEDASTRO_GATEWAY_QUEUE_ENABLED=1` / `VEDASTRO_FREE_TIER_QUEUE=1` 用于把昂贵或被限流的外部请求排队」)。`.env.cn.example` 同时配了两者。
|
||
|
||
### 2.2 静默吞异常:29 处
|
||
|
||
口径:`scripts/*.py`(根目录)+ `mcp_server.py`,`except Exception:` / `except BaseException:` / 裸 `except:` 且处理体**只有一句** `pass` / `continue` / `return None`。AST 扫描脚本见第 9 节。写作时 **29 处**(宽泛 `except Exception` 全部 343 处,大多数有降级返回或记录,不在本单范围):
|
||
|
||
| 文件 | 处数 | 位置(符号) |
|
||
| --- | ---: | --- |
|
||
| `scripts/muhurta.py` | 4 | `_swisseph_solar_times_local`(2)、`_jd_for_local_time`、`_swisseph_sun_moon_lon` |
|
||
| `scripts/jyotish_engine.py` | 4 | `write_pl9_pdf`(字体循环 continue)、`cmd_planetary_congregation`、`_conflict_arbitration`(Ashtakavarga 失败后冲突仲裁静默少一层)、`cmd_full_reading`(过境月宿) |
|
||
| `scripts/yoga_engine.py` | 3 | `_compiled_exec_code`、`_run_custom_expr`(2)——自定义规则表达式求值 |
|
||
| `scripts/tajika.py` | 2 | `_year_lord_is_daytime`、`_resolve_external_pyjhora_python` |
|
||
| `scripts/pl9_full_data_export.py` | 2 | `_render_pl9_ai_density_raw_data_markdown_en` / `_zh`——**报告「Sudarshana 三参照落宫」整段失败时静默消失** |
|
||
| 其余各 1 | 14 | `annual_tajika_pack._iana_timezone_id`、`transit_trigger._get_transit_lon_precise`、`local_oss_observation_inventory.git_commit`、`jyotishganit_shadbala_surface_probe.clean`、`vedastro_python_bridge._serialize_runtime_value`、`nakshatra_dasha.calc_nakshatra_dasha_breakdown`(裸 except)、`solar_return._get_sun_lon_jd`、`oracle_boundary_audit._date_delta_days`、`cmd_nakshatra_adv.cmd_nakshatra_dasha`(裸 except)、`reference_transparency_contract._case_chart`、`daily_guidance_service._current_mahadasha`、`cmd_muhurta._get_sun_moon_lons`、`vedastro_gateway._sdk_version`、`mcp_server.strict_workflow` |
|
||
|
||
排查报告写 `vedastro_python_bridge` 2 处,实测 1 处;其余与报告一致。
|
||
|
||
### 2.3 功能吉凶兜底三份
|
||
|
||
核心判定已在 `scripts/functional_benefics.py::derive_functional_benefic_malefic`(未知上升本身就返回 `status: blocked` 的完整形状)。外面又包了三层几乎相同的兜底:
|
||
|
||
| 位置 | 形状 | 差异 |
|
||
| --- | --- | --- |
|
||
| `scripts/jyotish_engine.py::_functional_benefic_malefic_snapshot` | try/except 包 import + 调用,异常时手写 blocked 字典 | `effect_on_confidence` = 「未完成功能性吉凶星判定,需降低高严谨结论置信度: {exc}」 |
|
||
| `scripts/jyotish_api_server.py::JyotishAPIHandler._functional_benefic_malefic_snapshot` | 同上,逐字复制 | 同上;`ascendant` 不是 dict 时自己会抛 AttributeError 再被吞 |
|
||
| `mcp_server.py::_derive_functional_benefic_malefic` | 先判 `chart.ascendant` 是否 dict | 文案 「Missing chart.ascendant; functional layer blocked.」 |
|
||
|
||
调用点:引擎 `cmd_full_reading`(`_functional_benefic_malefic_snapshot(planets, chart.get('ascendant', {}))`)、API `_compute_chart_sync` 与模块级 `_attach_local_consultation_layers`(`handler._functional_benefic_malefic_snapshot`)、MCP 两处 `present["functional_benefic_malefic"] = _derive_functional_benefic_malefic(modules)`。
|
||
|
||
### 2.4 `scripts/` 根目录:443 个 `.py`,约 1/3 不在任何生产路径上
|
||
|
||
方法(脚本见第 9 节 `refs2.py` / `classify2.py` 思路,执行方可重写):对每个 `scripts/<m>.py`,按标识符在全部受控文件里找 `m`;以 `jyotish_api_server`、`mcp_server.py`、`jyotish_vedic/**/*.py`、`frontend/src/**`、`deploy/**`、`.gitea/**` 为根,沿 `scripts/` 内的引用求传递闭包。写作时:
|
||
|
||
| 类别 | 个数 | 行数 |
|
||
| --- | ---: | ---: |
|
||
| 生产可达 | 253 | — |
|
||
| 生产不可达 | 190 | 23,438 |
|
||
| 其中被 `tests/` 引用 | 184 | 22,625 |
|
||
| 其中**只被测试引用、也不被其他脚本引用**(排查报告「138 个、约 14,600 行」的口径) | **140** | **14,981** |
|
||
| 零引用(无测试、无脚本) | 6 | 813 |
|
||
|
||
140 个里**不能动**的(写作时):
|
||
|
||
| 原因 | 个数 | 说明 |
|
||
| --- | ---: | --- |
|
||
| 被 `references/**/*.json` 证据索引按路径记录 | 96 | 例:`references/oracle/full_technique_invocation_matrix_2026_07_22.json` 记着 `"scripts/answer_quality_audit.py"`。搬走即改写历史证据指针 |
|
||
| 运营文档(`AGENTS.md`、`SKILL.md`、`skills/**`、`references/**/*.md`、`docs/testing/**`)写了运行方式 | 3 | 例:`AGENTS.md` §9 的 `scripts/pre_work_check.py` |
|
||
| `scripts/run_quality_gate.py` 写死路径 | 1 | |
|
||
| golden 生成器 | 1 | `generate_ephemeris_events_golden`(AGENTS §7.4 golden 出处) |
|
||
| 校正冻结身份(ERR-110 / ERR-114) | — | `scripts/research/sealed_holdout_rerun.py::PRODUCTION_FILES` / `RESEARCH_FILES` 与 `references/real_case_calibration/minute_rectification_holdout_v3.json` `frozen_scoring.files`;`minute_rectification_*` 多个在其中,**按字节哈希核对,搬动或改一个字节即打红** |
|
||
|
||
剩下可搬的「干净集」**42 个、4,353 行**;其中 `niryaana_shoola_dasha` 归 `dasha-unify` 处理,本单不碰,实际 **41 个**。涉及 44 个测试文件,其中 33 个只引用干净集里的模块。**这 44 个测试没有一个在快速门清单里**(`scripts/run_quality_gate.py` 快速门是显式白名单;全量 `tests/` 只在手动 release 门跑),所以「搬到 `tests/audit/` 并从快速门排除」不改变任何门禁实际跑的内容。
|
||
|
||
6 个零引用文件核实结果:
|
||
|
||
| 文件 | 结论 |
|
||
| --- | --- |
|
||
| `build_bright_stars.py` | 生成 `scripts/data/bright_stars.json`,`docs/tasks/PROGRESS-birth-sky-cover-20260928.md` 写了手工运行方式 → **保留** |
|
||
| `generate_birth_sky_golden.py` | `frontend/tests/birth-sky-test-support.ts` 与 `frontend/tests/fixtures/birth-sky-golden.json` 记为 golden 出处 → **保留**(AGENTS §7.4) |
|
||
| `rectification_prior_calibration.py` | `docs/BUG_HISTORY.md`、两份研究 / 任务文档引用 → **保留** |
|
||
| `conversational_minute_rectification_replay.py`、`local_oss_observation_inventory.py`、`vedicastro_kp_surface_locator.py` | 被 `references/oracle/*.json` 证据索引按路径记录 → **保留** |
|
||
|
||
结论:**6 个零引用一个都不删**。
|
||
|
||
打包:`pyproject.toml` `[tool.setuptools.packages.find] include = ["jyotish_vedic*", "scripts*"]`,`scripts/__init__.py` 存在,整个 `scripts` 包都打进 wheel;镜像 `deploy/railway-api.Dockerfile` `COPY scripts ./scripts` 全量复制。`deploy/gated-paths.txt` 含 `scripts/**` 与 `tests/**`,搬到子目录不改变门禁触发。
|
||
|
||
## 3. 根因
|
||
|
||
- BUG-1340:免费层限流(BUG-720 引入预算)在 adapter 里自洽,但更早的「排队开关」报告字段从未接到限流实现上,三处各自复制解析;属于**从未接线**,不是回归。BUG-720 的防复发写的是「前台同步路径不得无上限排队」,没有覆盖「报告字段与行为一致」。
|
||
- 吞异常:早期代码风格;BUG-1037 已经记过一次「宽泛异常把调用不存在的函数吞掉」。
|
||
- 功能吉凶兜底:BUG-1181(功能吉凶 v2)只改了核心模块,三个消费层的兜底是更早各自复制的。
|
||
- 根目录膨胀:研究 / 审计 / 探针脚本与生产模块同目录;部分被证据索引按路径钉住,后续只能「不再新增」而不能整体搬。
|
||
|
||
## 4. 决策记录
|
||
|
||
- 2026-10-13 产品负责人同意按排查结论全部立单交 Grok 执行。
|
||
- **BUG-1340 修法选「报告跟随真实行为」**:三个旧变量名**不变成真开关**(变成真开关等于给运维一个关掉免费层限流的口子,违背 BUG-720 防复发「不得靠调大免费层配额绕过第三方额度」)。报告字段改为由 adapter 的同一套条件算出;三个旧变量名保留读取但只用于在诊断里提示「已废弃、无作用」。
|
||
- **不删除任何脚本**(第 2.4 节 6 个零引用全部有出处)。
|
||
- 搬迁只做「干净集」41 个;96 个被证据索引钉住的、冻结身份里的、运营文档里的一律不动。
|
||
- 测试分层:搬走的测试放 `tests/audit/`,**仍在全量 `pytest`(release 门)里跑**;快速门本来就不跑它们,所以不改任何门禁清单。若主会话或产品希望 release 门也排除 `tests/audit/`,那是削弱门禁,**本单不做,需拍板**。
|
||
- 不改 `.gitea/workflows/**`:排查报告「可选:ruff BLE001/S110 只拦新增」改成仓内 pytest 预算测试(第 6 节 T5),不动 workflow 里的 ruff 文件清单。
|
||
- 不推翻任何既有红线。
|
||
|
||
## 5. 硬红线
|
||
|
||
1. `scripts/jyotish_api_server.py`:行数只按增长合同的粗护栏(AGENTS.md §6),`tests/test_api_server_growth_contract.py` 的类方法数、`JyotishAPIHandler.__new__` 计数不得增长(本单 T2 预期方法数不变、行数下降)。
|
||
2. **校正冻结身份内的文件一个字节都不许改、不许搬**(ERR-110 / ERR-114):开工先打印两份清单(第 9 节命令),本单任何改动命中清单即停下回报。
|
||
3. 不改 `.gitea/workflows/**`、`deploy/gated-paths.txt`、`pyproject.toml` 的打包配置、`deploy/*.Dockerfile`。
|
||
4. 日志**不得包含出生资料、坐标、姓名、请求体、异常消息原文**:异常消息可能带输入值(例如 `strptime` 报错会带出生日期字符串)。WARNING 级只写「模块.函数 + 异常类名」;完整 `exc_info` 只允许在 DEBUG 级。
|
||
5. 加日志不得改变任何返回值、返回形状或控制流;不得把 `except Exception` 收窄到会让原本被吞的异常抛出去的程度(收窄只在能证明只可能抛该类型时做,并附测试)。
|
||
6. 不往 stdout 写任何东西(引擎 CLI 用 stdout 输出 JSON;日志只走 `logging`,默认落 stderr)。
|
||
7. 功能吉凶合一后,**正常路径输出逐字节不变**;新 helper 不得放进 `scripts/functional_benefics.py`(它在冻结身份 `PRODUCTION_FILES` 里)。
|
||
8. 不删除任何脚本;搬迁不改脚本内容(只允许 import 路径与 `ROOT` 层级修正,并逐个列在进度记录)。
|
||
9. 不动表结构与迁移;不顺手升级依赖、不顺手修不在本单的 ruff 告警(`jyotish_api_server.py` 现有 8 条 F401/F841 不在范围)。
|
||
10. 改既有断言写「原值 / 新值 / 原因」三栏;golden 只能来自真实引擎输出。
|
||
11. 不碰 `skills/*/versions/**`(冻结的历史 skill 副本)。
|
||
|
||
## 6. 任务分解
|
||
|
||
### T1 · BUG-1340:排队状态单一来源(必做)
|
||
|
||
改什么:
|
||
- 在 `scripts/vedastro_service_adapter.py` 新增一个纯函数(建议名 `free_tier_throttle_state(request_url: str | None = None) -> dict`),用与 `_acquire_free_tier_slot` **完全相同的条件**算出 `{"active": bool, "reason": "...", "max_requests": int, "window_seconds": float, "wait_budget_seconds": float}`;`_acquire_free_tier_slot` 的判断改为调用它(行为不变)。`request_url` 缺省时取官方公共端点。
|
||
- `offline_compute_mixins._free_tier_queue_enabled_env`、`vedastro_user_entrypoint._cache_and_queue_report`、`diagnose_vedastro_mode` 三处改为读这个函数;三处各自的 `_bool_env(...)` 三连删除。
|
||
- 三个旧变量名:只在 `diagnose_vedastro_mode` 的输出里多给一个 `deprecated_env_present: [...]`,说明「已废弃,不控制限流;限流由 key 与 `VEDASTRO_FREE_TIER_MAX_REQUESTS` / `_WINDOW_SECONDS` 决定」。
|
||
- `docs/engine/vedastro-gateway.md` 与 `.env.cn.example`:拆开「网关离线排队」(`VEDASTRO_GATEWAY_QUEUE_ENABLED`)与「免费层限流」(key + `MAX_REQUESTS` / `WINDOW_SECONDS` / `WAIT_BUDGET_SECONDS`)两件事,删掉 `VEDASTRO_FREE_TIER_QUEUE=1` 示例或标注废弃。
|
||
- 不改:`_acquire_free_tier_slot` 的等待、预算、降级逻辑(BUG-720);`vedastro_gateway.build_gateway_config`。
|
||
|
||
验收:
|
||
- 新测试 `tests/test_vedastro_free_tier_state.py`:第 2.1 节两行表各一条(报告字段 == adapter 实际 `mode == "free_tier_queue_active"`);有 key 时 `active=False`;自建端点时 `active=False`;源码 AST 断言除 `diagnose_vedastro_mode` 外不再出现这三个变量名字符串。
|
||
- 既有 `tests/test_vedastro_runtime_mode_diagnostics.py`、`tests/test_vedastro_user_entrypoint.py`、`tests/test_vedastro_gateway.py`、`tests/test_api_server_security.py`(`VEDASTRO_FREE_TIER_QUEUE` 相关几条)、`tests/test_offline_compute_mixins.py` 全绿;凡断言「设了 `VEDASTRO_FREE_TIER_QUEUE=1` 就 `queue_enabled=True`」的,按三栏改成跟随真实状态。
|
||
- Grok 自跑上述文件;Claude 复验 + 快速门。
|
||
|
||
### T2 · 功能吉凶兜底合一(必做)
|
||
|
||
改什么:
|
||
- 新建 `scripts/functional_layer.py`(名字可议,**不得**是 `functional_benefics.py`),一个函数 `functional_layer_for_ascendant(ascendant) -> dict`:`ascendant` 是 dict 取 `sign`,否则视为缺失;正常路径直接 `return derive_functional_benefic_malefic(sign)`;import 或计算异常时返回与现有引擎版相同的 blocked 字典(字段顺序一致)。
|
||
- 三处改为调用它:引擎模块级函数改为一行转发(或调用点直接换);`JyotishAPIHandler._functional_benefic_malefic_snapshot` 保留方法名(`_attach_local_consultation_layers` 与测试按名调用),方法体改为一行转发;`mcp_server._derive_functional_benefic_malefic` 改为取 `chart.ascendant` 后调用它。
|
||
- 三处 blocked 文案不一致:统一为引擎版文案;MCP「Missing chart.ascendant」场景改走统一文案。任何断言旧文案的测试按三栏改。
|
||
|
||
验收:
|
||
- 新测试:十二个上升星座,三条路径(引擎函数、handler 方法、MCP 函数)返回与 `derive_functional_benefic_malefic` 逐字节一致(`json.dumps(sort_keys=True)` 比较);`ascendant=None` / `{}` / 非法星座三种输入三条路径输出相同;强制 import 失败(monkeypatch)时返回 blocked 且字段齐全。
|
||
- 写一个对照脚本(放 scratch,不入库):基线工作树与本分支各跑一次虚构盘(1990-01-01 12:00 新德里 28.6139/77.2090 +5.5)的引擎全量解读与 MCP `strict_workflow`,`modules.functional_benefic_malefic` 逐字节一致,贴进进度记录。
|
||
- `jyotish_api_server.py` 行数下降、方法数不变;`tests/test_api_server_growth_contract.py` 绿。
|
||
- `tests/test_rectification_validation_integrity_gate.py`、`tests/test_sealed_holdout_contract_freshness.py` 绿(证明没碰冻结文件)。
|
||
|
||
### T3 · 29 处静默吞异常:分类处理(必做)
|
||
|
||
每处按下表二选一,进度记录逐条列「位置 / 类别 / 处理」:
|
||
|
||
| 类别 | 判定 | 处理 |
|
||
| --- | --- | --- |
|
||
| A 预期失败 | 可选依赖导入(`import swisseph`、`importlib.metadata.version("vedastro")`)、用户可控字符串解析(时区名、日期)、自定义规则表达式求值(`yoga_engine` 3 处,每条规则都可能失败,高频) | 保持静默;收窄到确定的异常类型(如 `ImportError`、`ValueError`、`ZoneInfoNotFoundError`)**仅当**能证明只会抛这些,否则保留 `Exception` 并加一行注释说明为何静默 + `# noqa: BLE001` |
|
||
| B 降级吞掉 | 计算失败导致输出里少一段、少一层而用户 / 调用方看不出:`pl9_full_data_export` 2 处、`jyotish_engine._conflict_arbitration` / `cmd_planetary_congregation` / `cmd_full_reading`、`cmd_nakshatra_adv`、`transit_trigger`、`daily_guidance_service`、`tajika._year_lord_is_daytime`、`solar_return`、`muhurta` 计算类 3 处、`cmd_muhurta`、`reference_transparency_contract`、`mcp_server.strict_workflow` | 加模块级 `logger = logging.getLogger(__name__)`;`logger.warning("<module>.<function> degraded: %s", type(exc).__name__)`,`logger.debug(..., exc_info=True)` 可选;返回值不变 |
|
||
|
||
- 裸 `except:` 两处(`nakshatra_dasha`、`cmd_nakshatra_adv`)以及 `jyotish_engine` 两处裸 `except:` 至少改成 `except Exception:`(不再吞 `KeyboardInterrupt` / `SystemExit`),这是唯一允许的控制流变化,写进进度记录。
|
||
- `pl9_full_data_export` 两处另加:在报告该段位置写一行可见的「本段数据缺失」吗?——**本单不做**(改报告正文属于用户可感知变化,需拍板),只加日志。
|
||
- 验收:
|
||
- 新测试 `tests/test_silent_except_logging.py`:至少覆盖 `pl9_full_data_export`(造一个让 Sudarshana 段抛错的 packet)、`daily_guidance_service._current_mahadasha`、`jyotish_engine._conflict_arbitration` 三处,用 `caplog` 断言有一条 WARNING、消息里只有模块函数名与异常类名,**不含**测试盘的出生日期字符串、经纬度。
|
||
- 引擎全量解读(虚构盘)在基线与本分支输出逐字节一致(同 T2 的对照脚本,`PYTHONHASHSEED=0`)。
|
||
|
||
### T4 · 测试专用脚本归位(可砍,最后做)
|
||
|
||
改什么:
|
||
- 新建 `scripts/audit/__init__.py`(一行 docstring:「审计 / 探针 / 研究脚本,不在生产路径上;生产代码不得 import」)。
|
||
- 把第 2.4 节「干净集」41 个脚本 `git mv` 到 `scripts/audit/`。干净集由执行方**开工时重算**(同口径),并把最终名单写进进度记录;与写作时名单(`move.txt`,见附录)差异逐条解释。排除项:冻结身份两份清单、`run_quality_gate.py` 出现的、被 `references/**/*.json` / 运营文档引用的、golden 生成器、`niryaana_shoola_dasha`(归 `dasha-unify`)、`production_smoke`(名字像运维工具,执行方先确认无任何运维文档 / 部署脚本调用,确认不了就留在原处)。
|
||
- 只引用干净集模块的测试(写作时 33 个)`git mv` 到 `tests/audit/`;其余引用了干净集模块的测试留在原处,只改 import。
|
||
- 脚本内 `ROOT = Path(__file__).resolve().parents[1]` 之类层级常量随之 +1;逐个列出。
|
||
- 新测试 `tests/test_audit_scripts_boundary.py`:AST 断言 `scripts/` 根目录、`mcp_server.py`、`jyotish_vedic/**` 不 import `scripts.audit` / `audit.*`。
|
||
- 不改:任何脚本的逻辑;`pyproject.toml`(`scripts*` 已覆盖子包);Dockerfile;门禁清单。
|
||
|
||
验收:
|
||
- 被搬脚本的 CLI 仍能跑(每个 `python scripts/audit/<x>.py --help` 或无参运行的退出码与搬前一致,列表格)。
|
||
- 全量 `pytest` 通过条数与基线一致(只是路径变化),失败清单逐名与基线一致。
|
||
- `python scripts/run_quality_gate.py --profile quick` 的 pytest 段条数与基线一致(证明快速门没变)。
|
||
|
||
### T5 · 吞异常与根目录的「只拦新增」守卫(必做,轻量)
|
||
|
||
- 新测试 `tests/test_python_hygiene_budget.py`(加入 `scripts/run_quality_gate.py` 快速门清单,这是 `scripts/` 内文件,不是 workflow):
|
||
1. 第 2.2 节口径的静默吞异常计数 ≤ 本单收尾实测值(只许降);`# noqa: BLE001` 注释的 A 类不计入。
|
||
2. `scripts/` 根目录 `.py` 文件数 ≤ 本单收尾实测值(只许降);新研究脚本应放 `scripts/audit/` 或 `scripts/research/`。
|
||
- 验收:人为加一处 `except Exception: pass` 让 1 变红;人为在根目录加一个空 `.py` 让 2 变红;两次反向验证输出贴进进度记录。
|
||
|
||
### T6 · 记录(必做)
|
||
|
||
- `docs/BUG_HISTORY.md` 新增 **BUG-1340**(免费层排队状态报告与实际限流相反;关联 BUG-720)。
|
||
- `docs/tasks/PROGRESS-python-hygiene-20261013.md`:第 2 节各表的收尾实测、T3 逐条分类表、T4 名单与差异、反向验证输出。
|
||
- `CHANGELOG.md`:一条(诊断与审计字段口径变化,运维可见);Skill 版本不 bump。
|
||
- `frontend/DESIGN.md`:不改。
|
||
|
||
## 7. 让步顺序
|
||
|
||
时间不够按此顺序砍:**T4 → T5 第 2 条 → T3 的 A 类收窄(保留注释即可)**。T1、T2、T3 的 B 类、T6 不可砍。
|
||
|
||
## 8. 验收命令分工
|
||
|
||
| 谁 | 命令 |
|
||
| --- | --- |
|
||
| Grok 自跑 | `python -m pytest tests/test_vedastro_free_tier_state.py tests/test_vedastro_runtime_mode_diagnostics.py tests/test_vedastro_user_entrypoint.py tests/test_vedastro_gateway.py tests/test_offline_compute_mixins.py tests/test_api_server_growth_contract.py tests/test_silent_except_logging.py tests/test_python_hygiene_budget.py tests/test_rectification_validation_integrity_gate.py tests/test_sealed_holdout_contract_freshness.py -q`;T4 涉及的测试文件;T2/T3 对照脚本。快速门、全量 pytest **不跑**,以 Claude 为准 |
|
||
| Claude 验收 | 上述 + 快速门 + **全量 `pytest` 与基线逐名比对**(本单碰引擎文件,按「引擎改动跑全量」规则)+ `python -m pytest tests/test_api_server_security.py -q` + 隐私标记测试 `tests/test_repo_privacy_markers.py` |
|
||
| 不需要 | `test:db`、前端测试(不碰前端) |
|
||
|
||
## 9. 开工前置命令
|
||
|
||
```bash
|
||
git fetch origin --prune
|
||
git log --oneline origin/staging | grep -i "api-disk-cache-retention\|api-chart-subprocess-gate" # 两单都已合入才开工
|
||
git worktree add -b codex/python-hygiene-20261013 .worktrees/python-hygiene-20261013 origin/staging
|
||
cd .worktrees/python-hygiene-20261013
|
||
grep -n "^## BUG-" docs/BUG_HISTORY.md | tail -3 # 核对最大号
|
||
python - <<'EOF' # 冻结身份两份清单
|
||
import json, sys; sys.path[:0] = ["scripts/research", "scripts", "."]
|
||
from sealed_holdout_rerun import PRODUCTION_FILES, RESEARCH_FILES
|
||
print(PRODUCTION_FILES, RESEARCH_FILES)
|
||
print(json.load(open("references/real_case_calibration/minute_rectification_holdout_v3.json"))["frozen_scoring"]["files"])
|
||
EOF
|
||
wc -l scripts/jyotish_api_server.py # 记基线
|
||
python -m pytest tests -q -p no:cacheprovider > /tmp/baseline-full.txt 2>&1; tail -3 /tmp/baseline-full.txt # 全量基线(Windows 已知失败按 windows-known-failures.txt 比)
|
||
grep -rn "FREE_TIER_QUEUE\|_functional_benefic_malefic_snapshot\|_derive_functional_benefic_malefic" tests scripts mcp_server.py # 找按源码断言的合同测试
|
||
```
|
||
|
||
静默吞异常扫描(AST,口径与第 2.2 节一致):遍历 `scripts/*.py` 与 `mcp_server.py` 的 `ast.ExceptHandler`,类型为空 / `Exception` / `BaseException`,且 `body` 只有一句 `Pass` / `Continue` / `Return(None)`。
|
||
|
||
## 10. BUG 编号起点
|
||
|
||
BUG-1340(写作时最大号 BUG-1317;号段由主会话统一分配)。开工时核对,若被占用顺延并在进度记录说明。
|
||
|
||
## 11. 交付物清单
|
||
|
||
- 代码:`scripts/vedastro_service_adapter.py`、`scripts/offline_compute_mixins.py`、`scripts/vedastro_user_entrypoint.py`、`scripts/diagnose_vedastro_mode.py`、新 `scripts/functional_layer.py`、`scripts/jyotish_engine.py`、`scripts/jyotish_api_server.py`、`mcp_server.py`、T3 涉及的 15 个文件、`scripts/audit/**`、`tests/audit/**`、`scripts/run_quality_gate.py`(只加 T5 测试一行)
|
||
- 测试:`tests/test_vedastro_free_tier_state.py`、`tests/test_silent_except_logging.py`、`tests/test_python_hygiene_budget.py`、`tests/test_audit_scripts_boundary.py`
|
||
- 文档:`docs/tasks/PROGRESS-python-hygiene-20261013.md`、`docs/BUG_HISTORY.md`(BUG-1340)、`CHANGELOG.md`、`docs/engine/vedastro-gateway.md`、`.env.cn.example`
|
||
- 不改:`frontend/DESIGN.md`、`.gitea/**`、`deploy/**`、`pyproject.toml`
|
||
|
||
## 附录 · 写作时「干净集」名单(42 个,执行方开工重算)
|
||
|
||
archive_vedastro_nuget_probe、configure_vedastro_secret、evidence_packet_index_integrity、high_rigor_closure_snapshot、jyotishyamitra_adapter_probe、marriage_benchmark_summary、minute_rectification_development_eval、minute_rectification_holdout_intake、minute_rectification_source_audit、niryaana_shoola_dasha(归 dasha-unify,排除)、ocr_extract、oracle_batch_closure_pack、oracle_packet_assistant_index、prashna_oracle_queue、prashna_sphuta_candidate_replay、prashna_sphuta_review_result_validator、prepare_oracle_capture_packets、prepare_vedicastro_kp_tmp_env、print_cline_mcp_config、production_smoke(先确认)、public_real_case_negative_controls、rangacharya_readiness、shadbala_component_joined_closure、shadbala_d3_branch_audit、shadbala_oracle_component_cluster_summary、shadbala_sthana_targeted_audit、skill_truth_overlay_view、three_engine_mismatch_arbitrator、traditional_maturity_gap_audit、vedastro_contract_arbitrator、vedastro_divisional_contract_probe、vedastro_ingestion_closure_pack、vedastro_method_catalog_sync、vedastro_official_contract_audit、vedastro_official_source_contract、vedastro_reproducible_build_probe、vedastro_strength_oracle_packet、vp_jain_shadbala_benchmark、western_real_case_calibration、xalen_arbitration_gate、xalen_difference_attribution、xalen_public_case_batch
|
||
|
||
`minute_rectification_*` 三个不在写作时的冻结清单里,但执行方必须用第 9 节命令再核一次。
|
||
|
||
## 后续阶段(本单不做)
|
||
|
||
1. 96 个被证据索引钉住的测试专用脚本:要么接受留在根目录,要么另开单「证据索引路径迁移」(改历史证据 JSON,需拍板)。
|
||
2. 其余约 314 处宽泛 `except Exception`(有降级返回的)逐个审。
|
||
3. `pl9_full_data_export` 段落失败时在报告里可见提示(用户可感知,需拍板)。
|
||
4. `jyotish_api_server.py` 现有 8 条 ruff F401/F841。
|