Files
Jyotisha/docs/tasks/TASK-python-hygiene-20261013.md
T
Jesse_ChenandClaude Opus 5.5 2994588ad4 docs(tasks): fold product decisions into audit briefs (BUG-1344..1346)
- 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
2026-10-14 00:41:02 +08:00

29 KiB
Raw Blame History

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)。
  • 结果:审计字段与实际限流两个方向都会报错。

复现(虚构环境,无网络):

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. 开工前置命令

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。