From 9f28633e556dfef4da46d9995689cb2d5f2b6433 Mon Sep 17 00:00:00 2001 From: YUAN Date: Fri, 11 Sep 2026 19:53:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9AT-8=20=E5=9B=9E?= =?UTF-8?q?=E5=86=99=20+=20=E5=85=A8=E4=BB=93=E8=BF=9B=E5=BA=A6=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=80=A7=E5=A4=8D=E6=A0=B8=EF=BC=88=E8=AE=A2=E6=AD=A3?= =?UTF-8?q?=205=20=E5=A4=84=E6=BC=8F=E5=90=8C=E6=AD=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本轮在 T-8 提交(57f16a5)后做文档回写,并对全仓「同一事实的副本」做一致性扫描, 发现并当场订正 5 处漏同步(均属上次改了主状态位、漏改副本): - AGENTS.md 入口引用(最重要,属项目记忆已强调的「全仓同步入口引用」漏项): T-1~T-6 ✅/797 基线/下一步 T-7 → T-1~T-8 ✅/829 基线/下一步 T-9; T-6 真库 verify_convert_accept.py 36/36 → 37/37,并并列 83/83、39/39。 - docs/memory/TODO.md:汇总待办行「T-8 ~ T-17(含已完成之引擎时机)」→「T-9 ~ T-17」; 为 v0.x 线引用块加编号歧义提示(其 T-11/T-12 与新线同号且均涉 amount_view,易混)。 - docs/memory/MEMORY.md:自检问答第 6 问进度 T-1~T-7 ✅/827 → T-1~T-8 ✅/829。 - 开发计划(T-8 执行期订正,承 57f16a5 代码提交): 就地订正 T-7 执行记录第 2 条「T-8 剩余仅为 rules.amount_view」之误(F-10 明确 T-8 不动去重口径,属 T-11); 新增 T-8「执行记录」5 条差异裁定;DoD 全勾。 不入库但同批完成(交接文档.md 受 .gitignore 忽略): - 顶部版本 v3.7 → v3.8;§③ 表格行;§B.11 状态块(T-8 完成 + 两处订正); - 开工纪律行 T-1~T-6 → T-1~T-8; - §B.11「未落地(T-1 起执行)」立项期清单 → 「已落地」并附实测行号 (01-ddl.sql 272/297/305 行;已进 reset.ps1 与 verify_convert_seed.py 清单),原文折叠留痕。 复核:全量 pytest -q → 829 passed / 10 skipped(16.37s,零失败)。 --- AGENTS.md | 2 +- docs/memory/MEMORY.md | 4 +- docs/memory/TODO.md | 8 ++-- docs/项目框架设计/开发计划-基金转换交易.md | 47 ++++++++++++++++++---- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8cdf0eb..b07492d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,7 @@ - 技术选型 / JWT:`docs/项目框架设计/技术选型和版本/` - Core 模拟底座:`docs/项目框架设计/Core模拟底座/` - 业务记忆分层:`docs/业务记忆管理/业务记忆管理手册.md` -- **基金转换线(第 5 步 todo 开发进行中 · T+1 受理/确认分离模型)**:**T-1 ~ T-6 ✅ / T-7 ~ T-17 ⬜**(当前基线 **797 passed / 10 skipped** 实测;T-6 真库 `verify_convert_accept.py` **36/36**;下一步 **T-7** = 三段编排 + 新增 `confirm_service`,核心重写);开工入口 **`交接文档.md` §B**(§B.11 下一条立项指引 = 最新状态)· `docs/PRD/PRD-基金转换交易.md`(**v1.1**)· `docs/项目框架设计/架构设计-基金转换交易.md`(**v2.1**)· `docs/项目框架设计/开发计划-基金转换交易.md`(**v2.0** · T-1~T-17 逐任务改法 + DoD) +- **基金转换线(第 5 步 todo 开发进行中 · T+1 受理/确认分离模型)**:**T-1 ~ T-8 ✅ / T-9 ~ T-17 ⬜**(当前基线 **829 passed / 10 skipped** 实测;真库 T-6 `verify_convert_accept.py` **37/37**、T-7 `verify_convert_confirm.py` **83/83**、T-8 `verify_convert_engine.py` **39/39**,退出码均 0;下一步 **T-9** = api 层:redeem 份额申报 + 撤单/确认/查询接口 + 网关分派);开工入口 **`交接文档.md` §B**(§B.11 下一条立项指引 = 最新状态)· `docs/PRD/PRD-基金转换交易.md`(**v1.1**)· `docs/项目框架设计/架构设计-基金转换交易.md`(**v2.1**)· `docs/项目框架设计/开发计划-基金转换交易.md`(**v2.0** · T-1~T-17 逐任务改法 + DoD) - **架构改进线(✅ 2026-09-10 已结项,无待办)**:开工入口 **`交接文档.md` §C** - **风控模块主线**:**`交接文档.md` §A** diff --git a/docs/memory/MEMORY.md b/docs/memory/MEMORY.md index b375e87..802cb8c 100644 --- a/docs/memory/MEMORY.md +++ b/docs/memory/MEMORY.md @@ -13,7 +13,7 @@ **⚡ 基金转换(convert)线 — 两条子线**: - **(一)v0.x 两阶段实时模型(已结项 2026-09-10)**:PRD **v0.9.3** + 架构 **v1.0.1** + 开发计划 **v1.0**,T-0 ~ T-13 全部完成(第 5+6 步闭环,最终 **739 passed / 10 skipped**,`CONVERT_STRESS=1` 并发 9/9,含死锁重试补丁 `43cf2a1`);**该模型已被下一线取代,其 PRD/架构/开发计划作废,代码保留作过渡基线**。 -- **(二)下一线 · T+1 受理/确认分离模型(进行中 · 2026-09-11)**:入口 **交接文档.md §B.11**(10 项真实业务拍板)· PRD **v1.1** + 架构 **v2.1** 已整体重写并经独立 AI 联网审查通过(0 阻断级),**已定稿**(2026-09-11 用户批准);**第 4 步开发计划 v2.0 已整体重做**(`docs/项目框架设计/开发计划-基金转换交易.md`,797 行:T-0~T-17 按架构 §15 重排 + §1.4 代码事实核对表 F-1~F-25 + §1.5 实现级裁定 R-1~R-15 + §5 十六条回归面 + §6 验收 31 条 + FR-C29/C30 全映射),**经三轮独立审核闭环(第一轮挑毛病 2 阻断+5 重要+3 可选 / 第二轮验证型 12 项零残留 / 第三轮白纸重审 C1~C5+O1~O4 + 验证型复核 22/22 落位),已定稿(2026-09-11 用户批准)**。**核心认知:不新增冻结列,在途占用由受理单推导;紧池 80% 病根在 T+1 串行确认下自动消失。** 下一步:**第 5 步 todo 开发进行中(T-1~T-7 ✅,**827 passed / 10 skipped** 零失败;T-6 真库 verify **37/37**、T-7 真库 verify **83/83**,退出码均 0;下一步 T-8 `rules.amount_view` / T-9 api 层 —— 注意引擎「时机」部分已在 T-7 由 `engine_call.py` 完成,全仓 `process_convert_event(` 恰 1 处调用)**。**T-7 关键裁定:强制全转属受理段决策** —— 受理由 `plan_lots` 判定并把 `qty` 收敛为实际全转量 + 受理单 `remark='full_transfer'`,确认段**只继承不重判**(`plan_lots` 不传 `min_hold_qty`);重判会因 T→T+1 可用份额变化(部分成交/他人申赎/产品参数改)得出与受理承诺不一致的结论(擅自扩大客户指令)。撤单范围:PRD §5.5 原表述与实现(仅 `accepted`)不矛盾、无需回填(第三轮 C1 订正)。已就绪:`fetch_nav.py` / `06-seed-nav.sql`(60 个真实净值日)/ `gen_trade_calendar.py` / `10-seed-trade-calendar.sql`(503 交易日)。 +- **(二)下一线 · T+1 受理/确认分离模型(进行中 · 2026-09-11)**:入口 **交接文档.md §B.11**(10 项真实业务拍板)· PRD **v1.1** + 架构 **v2.1** 已整体重写并经独立 AI 联网审查通过(0 阻断级),**已定稿**(2026-09-11 用户批准);**第 4 步开发计划 v2.0 已整体重做**(`docs/项目框架设计/开发计划-基金转换交易.md`,797 行:T-0~T-17 按架构 §15 重排 + §1.4 代码事实核对表 F-1~F-25 + §1.5 实现级裁定 R-1~R-15 + §5 十六条回归面 + §6 验收 31 条 + FR-C29/C30 全映射),**经三轮独立审核闭环(第一轮挑毛病 2 阻断+5 重要+3 可选 / 第二轮验证型 12 项零残留 / 第三轮白纸重审 C1~C5+O1~O4 + 验证型复核 22/22 落位),已定稿(2026-09-11 用户批准)**。**核心认知:不新增冻结列,在途占用由受理单推导;紧池 80% 病根在 T+1 串行确认下自动消失。** 下一步:**第 5 步 todo 开发进行中(T-1~T-8 ✅,**829 passed / 10 skipped** 零失败;T-6 真库 verify **37/37**、T-7 真库 verify **83/83**、T-8 真库 verify **39/39**,退出码均 0;下一步 T-9 api 层)**。**T-7 关键裁定:强制全转属受理段决策** —— 受理由 `plan_lots` 判定并把 `qty` 收敛为实际全转量 + 受理单 `remark='full_transfer'`,确认段**只继承不重判**(`plan_lots` 不传 `min_hold_qty`);重判会因 T→T+1 可用份额变化(部分成交/他人申赎/产品参数改)得出与受理承诺不一致的结论(擅自扩大客户指令)。**T-8 关键裁定**:引擎时机(R-7)的实现部分在 T-7 已完成,T-8 补的是**守护与验证**(AST 守护唯一调用点 / 受理不触引擎 / 确认链路真引擎正证+反证);⚠️ **T-8 不动去重口径** —— `amount_view` 三处消费方原样不动(属 T-11);`convert_fund` 的阶段 1.5 调用点随 T-9 删函数一并清除(只摘调用会让 v1.0 过渡函数「扣份额但不出预警单」,风控缺失比时机错更危险)。撤单范围:PRD §5.5 原表述与实现(仅 `accepted`)不矛盾、无需回填(第三轮 C1 订正)。已就绪:`fetch_nav.py` / `06-seed-nav.sql`(60 个真实净值日)/ `gen_trade_calendar.py` / `10-seed-trade-calendar.sql`(503 交易日)。 **仓库地图:** | 路径 | 状态 | 说明 | @@ -201,7 +201,7 @@ RBAC 联调账号:scripts/dev/rbac-seed-reference.md 3. 是否需 customer_id 归属与 JWT RBAC? 4. Core 是模拟库只读还是 agent 库读写? 5. 如何验证?(`python -m pytest` 全量(当前 **739 passed / 10 skipped**,基线 510)· uvicorn 启动 + /health · SQL / sync 脚本 · 对照 REQUIREMENTS 验收列) -6. **当前有哪两条并行线?**(① 风控/架构改进线:**已结项**(510 基线绿、§7.2 七项冒烟 7/7 PASS、`037ce7e` 已核实早已推送);② **基金转换线**:v0.x 实时模型已结项(739 绿,**已被 T+1 模型取代**);**下一线 T+1 受理/确认分离模型进行中(2026-09-11)**:PRD v1.1 / 架构 v2.1 / 开发计划 v2.0 重写完毕并经独立审查收敛,**已定稿**(2026-09-11 用户批准),批准后进第 5 步 todo 开发(T-1~T-17;**当前进度 T-1~T-7 ✅,827 绿**))——动代码前先确认自己属于哪条线,别混淆前置条件。 +6. **当前有哪两条并行线?**(① 风控/架构改进线:**已结项**(510 基线绿、§7.2 七项冒烟 7/7 PASS、`037ce7e` 已核实早已推送);② **基金转换线**:v0.x 实时模型已结项(739 绿,**已被 T+1 模型取代**);**下一线 T+1 受理/确认分离模型进行中(2026-09-11)**:PRD v1.1 / 架构 v2.1 / 开发计划 v2.0 重写完毕并经独立审查收敛,**已定稿**(2026-09-11 用户批准),批准后进第 5 步 todo 开发(T-1~T-17;**当前进度 T-1~T-8 ✅,829 绿**))——动代码前先确认自己属于哪条线,别混淆前置条件。 7. **远程分支到底还在不在?**(**在**。`git ls-remote --heads origin` 实测 `refs/heads/risk-control-agent` = `fffb78a`。⚠️ **判断远程存亡只能用 `git ls-remote`** —— 2026-09-10 **同日误报两次**,勿再踩。) **本仓特有异常(2026-09-10 深挖确认,别误判为 stale ref)**:`.git/refs/remotes/**` **写入不落盘** —— `git update-ref refs/remotes/origin/X ` 返回 0,但松散引用消失,**且整个 `refs/remotes/origin/` 目录被删** diff --git a/docs/memory/TODO.md b/docs/memory/TODO.md index c090252..c759b2f 100644 --- a/docs/memory/TODO.md +++ b/docs/memory/TODO.md @@ -9,7 +9,7 @@ **⚡ 并行新线 · 基金转换(convert)**—— **两条子线**: - **(一)v0.x 两阶段实时模型(已结项 2026-09-10)**:PRD **v0.9.3** + 架构 **v1.0.1** + 开发计划 **v1.0**,**T-0 ~ T-13 全部完成**(第 5 步 + 第 6 步闭环,**736 passed / 10 skipped**;7 个 convert 真库脚本复跑零回归;压测修复 2 处并发缺陷 + 死锁重试后补 `43cf2a1` 最终 **739 passed / 10 skipped**)。**注:该模型已被下一线取代(按下述),其设计文档作废,代码仍保留作过渡基线**。 -- **(二)下一线 · T+1 受理/确认分离模型(进行中 · 2026-09-11)**:入口 **交接文档.md §B.11** · PRD **v1.1** + 架构 **v2.1** 均已整体重写并经独立 AI 联网审查通过(0 阻断级),**已定稿**(2026-09-11 用户批准);**第 4 步开发计划 v2.0 已整体重做(`docs/项目框架设计/开发计划-基金转换交易.md`,T-1~T-17 按架构 §15 重排 + 代码事实 F-1~F-25 + 裁定 R-1~R-15 + 16 条回归面 + 验收 31 条 + FR-C29/C30 全映射),经三轮独立审核闭环(挑毛病 / 验证型 / 白纸重审 + 验证型复核 22/22 落位),已定稿(2026-09-11 用户批准)**。**第 5 步 todo 开发已开工(2026-09-11):T-1 ~ T-7 完成(全量 pytest 827 passed / 10 skipped 零失败;T-6 真库 verify 37/37、T-7 真库 verify 83/83,退出码均 0),下一步 T-8(`rules.amount_view`;引擎时机部分已在 T-7 完成)/ T-9(api 层)**。撤单范围:PRD §5.5 原表述与实现(仅 `accepted`)不矛盾、无需回填(第三轮 C1 订正)。 +- **(二)下一线 · T+1 受理/确认分离模型(进行中 · 2026-09-11)**:入口 **交接文档.md §B.11** · PRD **v1.1** + 架构 **v2.1** 均已整体重写并经独立 AI 联网审查通过(0 阻断级),**已定稿**(2026-09-11 用户批准);**第 4 步开发计划 v2.0 已整体重做(`docs/项目框架设计/开发计划-基金转换交易.md`,T-1~T-17 按架构 §15 重排 + 代码事实 F-1~F-25 + 裁定 R-1~R-15 + 16 条回归面 + 验收 31 条 + FR-C29/C30 全映射),经三轮独立审核闭环(挑毛病 / 验证型 / 白纸重审 + 验证型复核 22/22 落位),已定稿(2026-09-11 用户批准)**。**第 5 步 todo 开发已开工(2026-09-11):T-1 ~ T-8 完成(全量 pytest 829 passed / 10 skipped 零失败;T-6 真库 verify 37/37、T-7 真库 verify 83/83、T-8 真库 verify 39/39,退出码均 0),下一步 T-9(api 层:redeem 份额申报 + 撤单/确认/查询接口 + 网关分派)**。撤单范围:PRD §5.5 原表述与实现(仅 `accepted`)不矛盾、无需回填(第三轮 C1 订正)。 ### 基金转换线待办(推荐顺序) @@ -34,10 +34,12 @@ - [x] **T-5 锁原语 ✅**:新增 `app/service/convert/trading_calendar.py`(纯函数包,`is_open` 谓词注入,不查库)+ 单测;`locks.py` 补锁键单一事实源;受理接入 `run_locked` - [x] **T-6 受理事务 ✅**:`accept_convert` 八步落地(幂等前置 → 产品/份额/适当性 → 受理日顺延 R-5 → 可用份额 R-3 → 落 `core_convert_request(accepted)` → 镜像 + 审计 → 202 回执);新增 `tests/test_convert_accept.py`;**真库 `verify_convert_accept.py` 37/37 一致(退出码 0)** - [x] **T-7 三段编排 + `confirm_service`(核心重写)✅(2026-09-11,提交 `048f1a9`)**:新增 `confirm_service.py`(`confirm_one` 8 步:读单+状态闸门 → T 日净值精确匹配(缺→`nav_pending`)→ 产品复核 + T+1 适当性复核 → 部分成交 R-10 → FIFO 扣批 + 折算 → 确认事务(扣批次 + 2 流水 + 转入批次 + 两端持仓 + 明细 + 受理单置 `confirmed`,同事务,`rowcount!=1` → `ConfirmConflict` 整体回滚)→ 引擎恰好一次(commit 后,异常不阻断)→ 镜像 + 审计;`confirm_batch`:整批锁 + 业务日捞单窗口 + 串行 + 单笔容错);抽出 `format.py`/`audit.py`/`engine_call.py` 三个共用出口;`convert_service` 加 `cancel_convert`(T 日撤单两道闸门)+ `_t1_t2_dates`,受理响应改 PRD §5.3.1 字段;`convert_core_repository.ConvertApplyInput` 加 `convert_request_id`/`diff_fee`/`request_remark` 并内置 `_confirm_request` 状态守卫;`convert_request_repository` 加 `_end_of_day` 闭区间 + `reject()`。**单测 `tests/test_convert_confirm.py`(24 例)+ 真库 `verify_convert_confirm.py` 83/83 一致(含 PRD §5.3.2 示例真库逐字节重放)**;全量 **827 passed / 10 skipped**。**关键裁定(计划缺口,已回写开发计划)**:强制全转是受理段决策,受理单 `remark='full_transfer'` 传承,确认段**只继承不重判**(`plan_lots` 不传 `min_hold_qty`)—— 重判会因 T→T+1 可用份额变化擅自扩大客户指令。 -- [ ] T-8 ~ T-17(引擎时机 / API 受理接口 / 撤单 / 调换 / 工具 / 补偿 / 回归 / D27 产品级舍入 / D28 巨额赎回 / D29 资金流中转 / D30 A/C 互转) +- [x] **T-8 引擎时机收口 ✅(2026-09-11,提交 `57f16a5`)**:**R-7 的实现部分在 T-7 已完成**(`engine_call.run_convert_engine` 是引擎唯一出口、位于确认事务 commit 之后;`accept_convert` 自实现起不含引擎调用)。本轮补**守护与端到端验证**:① `test_process_convert_event_has_single_call_site` —— 用 **AST** 统计 `app/` 下 `process_convert_event(` 的 Call 节点**恰 1 处**且宿主为 `run_convert_engine`(字符串匹配会被 `engine_call.py` 说明段误判);② 受理无副作用用例补「不触引擎」断言(受理后 `risk_alert` 0 行、无 `pass`/`engine_error` 审计,验收 29 前半);③ `test_real_engine_on_confirm_dedupes_daily_total` —— 确认链路接**真引擎**(不注入 hook)的正证 + 反证(阈值夹逼 → RISK-002 不命中 / 阈值下调 → 命中),并断言一张单 + `payload.events` 两条 + 去重不删行(验收 5/6/7)。两条新用例**均做突变验证**(加第二处调用 / 禁用引擎调用 → 变红 → 还原)。真库 `verify_convert_engine.py` **整体切换 T+1 链路**(此前走 v1.0 `convert_fund`):受理 → 确认,新增 A0 组「受理不触引擎」,折算期望改用 T 日净值,**真库 39/39 一致**。**订正**:原记「T-8 剩余工作仅为 `rules.amount_view`」有误 —— F-10 明确 T-8 **不动**去重口径(属 T-11 范围),T-8 剩余的是**验证面**。 +- [ ] T-9 ~ T-17(API 受理接口 / 撤单 / 调换 / 工具 / 补偿 / 回归 / D27 产品级舍入 / D28 巨额赎回 / D29 资金流中转 / D30 A/C 互转) > **第 0~2 批结果(2026-09-10)**:基线 **510 passed** → 批 0 后 **516 passed / 3 skipped**(+3 T-0 用例 +3 T-0b 引擎用例)→ 批 1(T-1)后**仍 516 passed / 3 skipped**(只加表与种子,未加用例 → **零回归**);**T-1 数据层断言 8/8 PASS**(含新增断言 ⑧:费率档 ↔ `product_type` 匹配,越档即 FAIL)→ 批 2(T-2 + T-2b)后 **609 passed / 3 skipped**(**+93 纯函数用例**,零回归);T-2b 实算脚本 15/15 与 PRD §5.3 一致(退出码 0)。 -> **本线已全部闭环(T-0 ~ T-13)**。基线 **736 passed / 10 skipped**(T-10 后 714 → T-11 后 718 → T-12 后 731 → **T-13 后 736**,+5);`CONVERT_STRESS=1` 另跑 9 条并发用例全绿。 +> ⚠️ **以下四条引用块均为 v0.x 两阶段实时线记录,其 T 编号与新线(T-1~T-17)无关、勿混**:新线 T-11 亦涉 `amount_view`,与 v0.x T-11「提升为公开函数」是两回事。 +> **本线(v0.x)已全部闭环(T-0 ~ T-13)**。基线 **736 passed / 10 skipped**(T-10 后 714 → T-11 后 718 → T-12 后 731 → **T-13 后 736**,+5);`CONVERT_STRESS=1` 另跑 9 条并发用例全绿。 > **T-11 执行期两条裁定**:① `_amount_view` **提升为公开 `amount_view`**(跨层复用,全仓唯一金额聚合口径); > ② SQL 去重条件从计划的 `IS NULL` 扩为 **`IS NULL OR convert_group_id = ''`** —— 真库实证「只写 `IS NULL` 会漏掉空串那笔」(400000 vs 450000,差 50000)。 > **T-12 执行期三条裁定**:① 补偿逻辑收敛到 **`convert_service.compensate_convert` 单点**,`rebuild_alerts --convert-group` 只是薄封装; diff --git a/docs/项目框架设计/开发计划-基金转换交易.md b/docs/项目框架设计/开发计划-基金转换交易.md index ee2e6f6..59841f8 100644 --- a/docs/项目框架设计/开发计划-基金转换交易.md +++ b/docs/项目框架设计/开发计划-基金转换交易.md @@ -487,9 +487,12 @@ 2. **新增三个共用模块**(计划未列):`format.py`(`D2/D4/new_id/q` 展示规格唯一出口)、 `audit.py`(`write_convert_audit` 唯一审计写入点)、`engine_call.py` (`run_convert_engine` 引擎唯一调用点)。理由:受理段与确认段共用同一套量化 / 审计 / - 引擎口径,若两段各写一份必然漂移。`engine_call.py` 的引入**同时完成了 T-8 的「引擎时机改」** - (调用点已迁到确认事务 commit 之后,且全仓恰 1 处调用)—— T-8 剩余工作仅为 - `rules.amount_view` 与三处消费方同步。 + 引擎口径,若两段各写一份必然漂移。`engine_call.py` 的引入完成了 T-8「引擎时机」的 + **实现部分**(调用点已迁到确认事务 commit 之后,且全仓恰 1 处调用)。 + ⚠️ **订正(2026-09-11,T-8 执行期发现)**:本行原写「T-8 剩余工作仅为 `rules.amount_view` + 与三处消费方同步」——**错**。F-10 已明确 T-8 **不动**去重口径(`amount_view` 三处消费方 + **原样不动**,属 T-11 的范围)。T-8 剩余的是**验证面**:调用点唯一性守护 + 受理不触引擎用例 + + 确认链路真引擎出单 / RISK-002 不翻倍的真库断言。详见下方 T-8「执行记录」。 3. **`nav.py` 未改**:确认段直接调 `core_ro.get_nav_on(product, T)`(T-3 已新增的精确匹配口径), 无需经 `nav.py`。计划第 4 条要删的「取 T−1 路径」(`_nav_as_of`)属 v1.0 `convert_fund`, 该函数已标 `⛔ Deprecated`,随 T-9 一并删除。 @@ -522,15 +525,43 @@ **依赖**:无(引擎时机改动不与 T-7 强绑定前置,但联调在 T-7 DoD 覆盖) **DoD**: -- [ ] grep:`process_convert_event(` 全仓恰 1 处调用(确认段) -- [ ] 受理路径用例:受理后 `risk_alert` 0 新增(验收 29 前半) -- [ ] 确认路径用例:引擎跑一次、RISK-002 计一次(验收 5/7) -- [ ] RISK-001/003 仍见两条(去重未删行,验收 6) -- [ ] pytest 全绿 +- [x] grep:`process_convert_event(` 全仓恰 1 处调用(确认段) +- [x] 受理路径用例:受理后 `risk_alert` 0 新增(验收 29 前半) +- [x] 确认路径用例:引擎跑一次、RISK-002 计一次(验收 5/7) +- [x] RISK-001/003 仍见两条(去重未删行,验收 6) +- [x] pytest 全绿 **风险**:调用点漏清(旧受理段残留)→ DoD 1 守护 **回滚点**:单点改动,git 回退即还原 +**执行记录(2026-09-11 · 提交 `57f16a5`)** + +真库 `verify_convert_engine.py` **39 项一致 / 0 不一致**(A0/A/B/C/D/E/F 七组); +全量 pytest **829 passed / 10 skipped**(T-7 基线 827,+2);复跑 T-6 `verify_convert_accept.py` +**37/37**、T-7 `verify_convert_confirm.py` **83/83**、v1.0 链路 `verify_convert_service.py` **35/35**,均零失败。 + +**本轮实际范围(与任务书字面的差异,已留痕)**: + +1. **引擎时机的「实现」在 T-7 已完成,本轮只做「守护 + 端到端验证」**。T-7 抽出 + `engine_call.run_convert_engine` 时即把调用点放在确认事务 commit 之后,且全仓仅此一处; + `accept_convert` 自 T-6 实现起就不含引擎调用(阶段 1.5 属 v1.0 `convert_fund`,不在受理段内)。 + 故 R-7 的两条要求(受理不触引擎 / 确认后唯一调用点)在 T-7 已成立,本轮补的是**可回归的守护**。 +2. **DoD 1 的 grep 改用 AST 实现**。字符串匹配会被 `engine_call.py` 的说明段 + (「`process_convert_event(` 全仓应恰 1 处调用」)误判为第二处调用 —— 该守护若用 grep 写, + 会恒红或被迫加脆弱的排除规则。改用 AST 统计 `Call` 节点,并同时断言宿主函数为 + `run_convert_engine`(防「文件对但函数被搬走」)。**已做突变验证**: + 临时追加第二处调用 → 断言变红 → 还原。 +3. **`convert_fund` 的阶段 1.5 调用点本轮不动**。该函数是 v1.0 实时模型的全流程入口 + (已被 4 个测试文件 + gateway 依赖),T-9 切换 API 后整体删除。本轮若只摘掉它的引擎调用, + 会让过渡函数变成「扣了份额但不出预警单」——**风控缺失比时机错更危险**,故保留, + 由 T-9 随函数一并清除。 +4. **确认链路的真引擎断言采用「正证 + 反证」双断言**。仅断言「RISK-002 不命中」无法区分 + 「去重生效」与「引擎根本没跑」;故正证(阈值夹逼 → 不命中)之外补反证 + (阈值下调 → 命中),并做突变验证(禁用引擎调用 → 变红)。 +5. **真库脚本踩坑(已钉进脚本注释)**:`confirm_one(thresholds=...)` 必须**显式传阈值**, + 缺省退回 `RiskThresholds.from_settings()`;settings 的 `daily_total` 一旦低于单条金额, + RISK-002 就会「命中」,**症状看着像去重失效,实为阈值口径没传**(首轮实跑被 B 组断言捕获)。 + --- ### T-9 · api 层(redeem 份额申报 + 撤单/确认/查询接口 + 网关分派)