- 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
29 KiB
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/staging77057a819(写作时);开工以前置两单合入后的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. 硬红线
scripts/jyotish_api_server.py:行数只按增长合同的粗护栏(AGENTS.md §6),tests/test_api_server_growth_contract.py的类方法数、JyotishAPIHandler.__new__计数不得增长(本单 T2 预期方法数不变、行数下降)。- 校正冻结身份内的文件一个字节都不许改、不许搬(ERR-110 / ERR-114):开工先打印两份清单(第 9 节命令),本单任何改动命中清单即停下回报。
- 不改
.gitea/workflows/**、deploy/gated-paths.txt、pyproject.toml的打包配置、deploy/*.Dockerfile。 - 日志不得包含出生资料、坐标、姓名、请求体、异常消息原文:异常消息可能带输入值(例如
strptime报错会带出生日期字符串)。WARNING 级只写「模块.函数 + 异常类名」;完整exc_info只允许在 DEBUG 级。 - 加日志不得改变任何返回值、返回形状或控制流;不得把
except Exception收窄到会让原本被吞的异常抛出去的程度(收窄只在能证明只可能抛该类型时做,并附测试)。 - 不往 stdout 写任何东西(引擎 CLI 用 stdout 输出 JSON;日志只走
logging,默认落 stderr)。 - 功能吉凶合一后,正常路径输出逐字节不变;新 helper 不得放进
scripts/functional_benefics.py(它在冻结身份PRODUCTION_FILES里)。 - 不删除任何脚本;搬迁不改脚本内容(只允许 import 路径与
ROOT层级修正,并逐个列在进度记录)。 - 不动表结构与迁移;不顺手升级依赖、不顺手修不在本单的 ruff 告警(
jyotish_api_server.py现有 8 条 F401/F841 不在范围)。 - 改既有断言写「原值 / 新值 / 原因」三栏;golden 只能来自真实引擎输出。
- 不碰
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/**不 importscripts.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):- 第 2.2 节口径的静默吞异常计数 ≤ 本单收尾实测值(只许降);
# noqa: BLE001注释的 A 类不计入。 scripts/根目录.py文件数 ≤ 本单收尾实测值(只许降);新研究脚本应放scripts/audit/或scripts/research/。
- 第 2.2 节口径的静默吞异常计数 ≤ 本单收尾实测值(只许降);
- 验收:人为加一处
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 节命令再核一次。
后续阶段(本单不做)
- 96 个被证据索引钉住的测试专用脚本:要么接受留在根目录,要么另开单「证据索引路径迁移」(改历史证据 JSON,需拍板)。
- 其余约 314 处宽泛
except Exception(有降级返回的)逐个审。 pl9_full_data_export段落失败时在报告里可见提示(用户可感知,需拍板)。jyotish_api_server.py现有 8 条 ruff F401/F841。