跳转至

优化路线图

English page

本文汇总 2026-07-31 代码质量审计发现的待办项,与 refactor-plan-etl-report.md(已闭环的拆分记录)并列集中管理。本文只列计划,不改动代码。

状态标注:✅ 已完成、⬜ 待做。历史审计数字均保留原日期;当前代码和门禁状态以最新主线为准。

2026-10-16 复核:自动化门禁、复杂度拆分和因子工具拆分已有后续实现。旧审计中的若干文件路径和整体结论已过期;下文保留仍能指向当前代码的兼容层与债务。

优先级说明:高优先级项影响核心逻辑的回归保护,建议先推进。中优先级为过渡期兼容层与复杂度的清理。低优先级为配置微调。

高优先级

1. 建立自动化测试门禁 ✅

原问题:两个 GitHub Actions 工作流(cross-market.yml、tushare-daily.yml)只做数据快照提交,不跑 pytest。本地 .githooks/pre-push 是空操作(exit 0),测试结果不阻塞任何合并或部署。

历史进展(PR #54,2026-07-31,已回退): - 曾新增 .github/workflows/pytest.yml,在推送 main 与每个 PR 上 checkout(含子模块)、装 uv、uv sync --group dev、uv run pytest。独立于两个快照工作流,不干扰数据快照提交。 - 三个子模块通过 submodules: recursive 一并拉取,跨仓契约测试因此能在 CI 中运行(见第 3 项)。 - 注意:门禁合入后会暴露现有预存在测试失败(如 test_ai_stock_picker_presentation、test_submodule_contracts),需先修掉才能真正生效。见第 12 项。

当前状态: - PR 和 main 推送由 public-quality.yml 运行完整 project_tools/check_all.py --scope all --strict-tools,并执行公开边界检查和包构建。 - .githooks/pre-push、本地安装器和分支拦截逻辑已删除。生产数据访问和部署验证仍按手册单独运行。

2. 覆盖率盲区 ✅

原问题:pyproject.toml 的 coverage.source 只列 daily_messenger 一个包,其余四个包脱离门禁。

已完成(PR #47,2026-07-31): - coverage.source 扩到五个包(daily_messenger、a_share_daily、a_share_analysis、tushare_jobs、ops_common),报告现在展示各包真实数字。 - fail_under 曾暂设 1。历史全量覆盖率约 15%,因此本轮先把门槛提高到 10,挡住明显回退;不把历史值当作本提交的实测结果。 - 后续随测试补齐(见第 4、8 项)逐步把 fail_under 上调至 70,并在提高前用完整离线测试重新采集覆盖率。

13. 收敛复杂度规则豁免 ⬜

2026-09-26 在全仓临时启用 PLR0911、PLR0913 后发现 134 条现存诊断,主要是参数过多。不能直接移除全局忽略,否则会让既有复杂度债务一次性阻塞所有检查。当前保留两条忽略,并加入逐文件、逐规则的 no-growth ratchet,按生产模块逐批收敛;不再配置未启用 preview 时无效果的 PLR0917。

同日维护性 ratchet 扫描发现 10 个超过 100 行的函数,高于预算 9。拆分 weekly_basket_command.py 的长编排函数后回到 9,并已把维护性及 PLR ratchet 加入 PR 质量工作流。后续只收紧 ratchet 预算。

3. 跨仓模型契约检查不被静默跳过 ✅

原问题:tests/test_ai_stock_picker_shadow.py:65 在子模块未初始化时 pytest.skip,跨仓生产模型一致性检查可能不执行。

已完成(PR #54,2026-07-31): - 新增的 pytest.yml 用 submodules: recursive checkout 子模块,CI 环境下子模块已就位,该 skip 不再被触发,跨仓模型常量一致性检查在 CI 中真实运行。 - 本地未初始化子模块时仍 skip(避免本地噪音),CI 中则强制运行。这比简单改成 pytest.fail 更稳妥:既不在本地制造红噪音,又在 CI 门禁里兜住漂移。

4. 补活跃模块单元测试 ✅

原问题:src/a_share_analysis/value_regime_weekly.py 被 scripts/weekly_recap.sh 调用,却无单元测试。

已完成(PR #48,2026-07-31): - 新增 tests/test_value_regime_weekly.py,覆盖离线纯函数层:compute_features、assign_regime、historical_patterns、load_weekly_returns。 - 该文件覆盖率从 0 升到约 30%。渲染与 lark 发送等 IO/CLI 层留待后续。

中优先级

5. 清理兼容重导出层 ✅(主项)+ ⬜(子项)

  • report_delivery.py 的职责后来拆分,但仍保留部分兼容导出供调用方和测试使用;不应沿用旧审计中的“删壳已完成”结论。继续清理前先核对当前导出使用情况。
  • src/daily_messenger/etl/fetchers/ai_news.py:293-299 的 _fetch_gemini_market_news 是 backward compatible wrapper,仅做同名转发。gemini 调用方迁移完成后删除。(待独立 PR)
  • daily_watch20_validation 的实现目前位于独立子模块;确认包入口是否需要公开符号后再决定是否增加导出,不把历史行号当作当前接口事实。
  • src/a_share_daily/delivery/targets.py:107 的 _legacy_chat_id() 命名带 legacy,仅作内部 fallback。确认无真实数据源走 legacy 分支后移除。(待独立 PR)

6. 拆分高圈复杂度函数 ✅

原始审计记录了 23 个函数的复杂度超过 15;该数量及下列路径是当时的快照。后续复杂度重构(例如提交 9442993 和 66641f9)拆分了剩余 C901 函数并移除逐文件豁免。当前以 PR 门禁及其复杂度预算为准,不沿用这份旧列表作为待办。

位置 复杂度
src/a_share_daily/freshness.py:render_freshness_section 28
src/a_share_daily/delivery/report_delivery.py:_deliver_markdown_files_and_images 23
src/daily_messenger/etl/fetchers/btc_flow.py:_fetch_sosovalue_latest_flow 22
src/a_share_daily/delivery/report_delivery.py:_deliver_morning_routes 22

按现有拆分惯例(git 历史已有多次 C901 拆分)继续拆这些多分支渲染与多源抓取函数。

7. 删除过渡期脚本,拆分双职责文件 ✅

  • scripts/check_mdp_reference_consistency.py 已删除(PR #61):MDP 切换完成、TuShare fallback 已移除,遗留的一致性校验脚本失去意义。其依赖的 tests/tushare_jobs/test_mdp_reference_consumption.py 两个 _compare 过渡期一致性测试一并删除。
  • 原 minute_factor_smoke.py 已在 PR #73 拆分为职责模块;PR #81 修复包边界问题,后续提交 d6bb906 清退了重复因子研究工具。原文件已不存在,不再作为当前待办。

8. 补运维脚本 smoke 测试 ✅

scripts/check_index_daily_freshness.py、scripts/check_mdp_reference_consistency.py、scripts/evening_review_pipeline.py、scripts/refresh_a_share_index_daily.py 没有对应的 *_script.py smoke 测试,与现有 refresh_daily_watch20.sh 测试不对称。补 bash -n 加关键逻辑断言的 smoke 测试。

进度(PR #51,2026-07-31):已补 refresh_a_share_index_daily.py 的 smoke 测试(语法校验 + 纯函数与 _self_check_index_daily 断言),共 7 个用例。 进度(PR #52,2026-07-31):已补 check_index_daily_freshness.py 的 smoke 测试(语法校验 + _most_recent_weekday / _latest_trade_date / _trading_days_behind / check_freshness 缺失与新鲜分支),共 7 个用例。 进度(PR #53,2026-07-31):已补 evening_review_pipeline.py 的语法校验 smoke(该脚本是薄编排入口,import 时即调用 a-share-daily evening --send-feishu,只校验可编译、不测逻辑)。 第四个脚本 check_mdp_reference_consistency.py 按第 7 项计划在 MDP 切换完成后删除,不再补 smoke 测试。故第 8 项实质完成(3/4 补测试,1/4 走删除路径)。

低优先级

9. 收敛脚本区 lint 豁免 ✅

原问题:pyproject.toml 对 scripts/** 与 project_tools/** 整组禁用 PLR/TRY/EM/S/DTZ/N/C90/RET/SIM/B,运维脚本完全脱离复杂度与风格门禁。

已完成(PR #50,2026-07-31): - 对 scripts/** 恢复 C90(复杂度)门禁,防止脚本无声膨胀。 - check_daily_watch20_producer_freshness.py 的 check 复杂度为 16,仍有精准 per-file 豁免待后续重构。此前列出的 send_hotsector_client_preview.py::main 复杂度债务随 2026-10 兼容入口退役一并移除。 - project_tools/** 维持原豁免(门禁脚本已有测试覆盖)。 - 顺带修复 tests/test_value_regime_weekly.py 中未使用的 import pytest,使 ruff 全量通过。

10. 收敛裸 except 豁免 ⬜(低风险子集已完成)

全仓 73 处 # noqa 中 57 处是 BLE001(裸 except Exception),集中在抓取层。逐步改为捕获具体异常并加日志,降低掩盖真实错误的风险。 - 低风险子集已完成(PR #59):纯本地 JSON/文件 IO 的 9 处(factor_observation×2、pipeline×3、deploy_check×2、minute_factor_smoke×1、stock_st×1)已收窄为 (json.JSONDecodeError, OSError) / OSError,消除 BLE001,控制流不变。 - 审计时估计约 46 处仍位于 ETL 抓取层,多为有意的多源容错;此数量是历史估算,不代表当前计数。是否收窄应按当前调用点逐一判断,避免泄漏密钥或破坏降级行为。

11. 忽略覆盖率产物 ✅

核实结果:.gitignore 已有 .coverage、.coverage.*、coverage.xml(第 39 至 43 行),无需改动。本项实际已完成,无代码动作。

进度总览

项 标题 状态 合并 PR
1 自动化测试门禁 已完成:PR/main CI 执行完整代码质量门禁,仓库 pre-push hook 已移除 当前任务 PR
2 覆盖率盲区 已完成 #47
3 跨仓契约检查不跳过 已完成(随 #54 子模块 checkout 解决) #54
4 value_regime 单元测试 已完成 #48
5 兼容重导出层清理 仍保留部分兼容导出;核对当前调用方后再定是否清理
6 高复杂度函数拆分 后续重构移除 C901 逐文件豁免;旧计数只作历史记录 9442993, 66641f9
7 过渡期脚本和双职责拆分 因子工具已拆分;旧实现后续清退 #61 #73 #81
8 运维脚本 smoke 测试 已完成(3/4 测试 + 1/4 走删除) #51 #52 #53 #61
9 脚本区 lint 豁免收敛 已完成 #50
10 裸 except 豁免收敛 低风险子集完成(9 处 PR #59),剩余 46 处待独立处理 #59
11 覆盖率产物忽略 已完成(无需改动)
12 修复预存在测试失败 已完成 #55

12. 修复预存在测试失败 ✅

建立 pytest 门禁(第 1 项)后暴露的预存失败已全部修复,门禁真正变绿(PR #55 时 718 passed / 91 skipped / 0 failed)。当前全量结果为 716 passed / 91 skipped / 0 failed(含本路线图以外的后续修复)。 - 子模块未初始化时,test_ai_stock_picker_presentation.py、test_submodule_contracts.py::test_ty_is_the_only_type_checker_and_preserves_owner_scopes、test_ai_stock_picker_cross_repo_e2e.py、test_ai_stock_picker_delivery.py::test_owner_contract_handshake_accepts_current_prompt_without_network 改为 pytest.skip,与既有 test_ai_stock_picker_shadow.py 风格一致。 - 修复 maintainability ratchet 漂移:4 处测试断言超长行换行(84→80)+ value_regime_weekly.py 展示字符串换行消除最后 1 行历史漂移(81→80),回到预算内。

注:原 roadmap 描述的「presentation 断言失败、submodule_contracts 作用域失败」实际根因是子模块未 checkout(FileNotFoundError),并非逻辑错误,故以 skip 守卫而非改断言修复。

子模块说明

三个子模块(a-share-factor-core、ai-stock-picker、hot-sector-screener)均自带 ruff 配置、规模适中,采用 owner-submodule 加 CLI/JSON 交接模式,无跨仓循环依赖。各自复杂度治理由其独立 pyproject 负责,本文未深入。

整体结论

本文主体源自 2026-07-31 审计,诊断数量、覆盖率和文件清单是历史快照。当前 PR 与 main 质量门禁由 public-quality.yml 执行;历史上提出的缺失远程测试门禁已由该工作流覆盖。后续工作应从 CI 当前结果和仍存在的兼容包装、target fallback 及裸 except 个案出发,不能沿用旧审计总体结论。