T-14/T-17 开口闭环:PRD §2.5.3 修订 + truncate 舍去法接入 + R-12 定稿(用户三项拍板 · 849 绿 / 真库 9/9)

① PRD §2.5.3 修订:删「易方达 ETF 场外取整数位」(无证据且有反证,真实整数位载体为场内),改记「2 位主流 + 舍去型真实存在」,版本块加注记
② rounding_mode=truncate 消费接入(用户拍板补做):
   - core_ro.get_share_rounding = 位数+模式回退链单点(规则行 → core_product → 默认 2/half_up,mode 白名单读取即校验,get_share_digits 转薄包装)
   - calc.product_round/in_qty 加 mode 参数(truncate → ROUND_DOWN)
   - 确认段 + 申购折算两处透传;载体 09-seed-org.sql ⑦ 段 PROD-005828(product 层回退,零转换场景引用)
   - 测试 +7(842→849):calc 4 + 确认段 2(truncate 34945.53 vs half_up .54 分化 + 回退链遮蔽语义)+ 申购段 1
   - 突变:两调用点同拆 mode 透传 → 2 红,还原绿;真库 9/9(seed 新增 ⑫ 载体在位断言)
③ R-12 口径裁定追认定稿:条目回写开发计划(NULL 不参与 A/C 判定 / 同类走一般转换规则)

审核教训留痕:PRD 独立 AI 三轮审查未抓出外部事实断言无出处 —— 联网核实铁律再添例证
文档:开发计划 T-14 开口闭环段 + 基线 849 · 日志新节 · TODO/MEMORY/AGENTS/交接文档开口清零
This commit is contained in:
2026-09-12 14:31:35 +08:00
parent cde0c226fe
commit b411d5a087
16 changed files with 292 additions and 46 deletions
+46
View File
@@ -490,3 +490,49 @@ cancelled)是幂等 skip**(批处理单笔容错语义),`ConfirmConflict
**T-1~T-17 全部完成,转换线收官**。剩余:全套 9 个真库脚本复跑 + 三任务
(T-15/T-16/T-17)提交 + 交接文档/TODO/MEMORY 终态更新;之后按开发计划第 6 步
做集成测试。
---
# T-14/T-17 开口闭环(2026-09-12 用户三项拍板)+ truncate 舍去法接入
## 一、三项拍板与执行
| # | 事项 | 拍板 | 执行 |
|---|---|---|---|
| 1 | PRD §2.5.3「易方达 ETF 场外取整数位」 | **改** | 已按核实结论修订(该句无证据且有反证,真实整数位载体为场内渠道),版本块加修订注记 |
| 2 | `rounding_mode='truncate'` 消费接入 | **补接** | 当轮完成(见下) |
| 3 | T-17 R-12 口径裁定(NULL 不参与判定 / 同类走一般规则) | **认可** | 开发计划 R-12 条目回写定稿 + T-17 执行记录同步 |
**审核教训留痕**:PRD v1.1 经独立 AI 三轮审查(0 阻断级)但「易方达 ETF 场外
取整数位」无出处一句未被任何一轮抓出——逻辑矛盾审查抓不住**外部事实断言**,
只能靠执行期联网核实兜底。「外部事实必须联网核实」铁律再添一例证。
## 二、truncate 接入(生产代码 3 处 + 单点扩展)
- **`core_ro.get_share_rounding`**:位数+模式回退链**单点**(规则行 →
core_product → 默认 `(2, 'half_up')`),逐层独立回退、mode 合法性读取即
校验(`half_up`/`truncate` 白名单);`get_share_digits` 转薄包装(T-14
既有调用与测试零破坏)。
- **`calc.product_round` / `in_qty`**:加 `mode` 参数(`_ROUND_MODES`:
half_up → ROUND_HALF_UP / truncate → ROUND_DOWN),非法 mode ValueError。
- **确认段(confirm_service)与网关申购折算(trade_gateway)**:两处透传。
- **载体**:09-seed-org.sql **⑦ 段** `PROD-005828.rounding_mode='truncate'`
(11-seed 规则行未覆盖 → 走 product 层回退;无任何转换场景引用 → 全套
零回归)。11-seed 头注「登记开口」同步改「已接入」。
## 三、验证(全量 849 passed / 8 skipped · 真库 9/9)
- **测试 +7(842 → 849)**:calc 4(truncate/half_up 分化 34945.53 vs .54、
整数边界向零、非法 mode、in_qty 透传 16.66 vs 16.67)+ 确认段 2(链路
truncate 舍去生效;回退链逐层独立 + 规则行整体优先遮蔽 product 层错配)+
申购段 1(对称,166.66 vs 166.67)。首跑踩坑:非法值断言放在规则行存在
之后 → DID NOT RAISE(规则行遮蔽 product 层)——即遮蔽语义本身,改用例
先删规则行。
- **突变**:两调用点同拆 mode 透传 → 2 链路用例红;还原绿,MUTATION 零残留。
- **真库 9/9 退出码 0**:09 ⑦ 段幂等灌库后 `verify_convert_seed` 新增 ⑫
断言(载体在位)通过;其余 8 脚本零回归。
## 四、下一步
**开口清零**。转换线(T-1~T-17 + 开口闭环)全部完成;下一步 = 开发计划
**第 6 步集成测试**。
+1 -1
View File
@@ -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 开发已收官(2026-09-12,T-1~T-17 全部完成,**842 passed / 8 skipped** 零失败;真库 verify 套件 **9/9 退出码 0**:accept 43 / confirm 103 / engine 39 / api 89 / compensate 56 / apply 30 / lots 20 / seed 全 PASS / tools 14。T-15 = D28 部分成交专项(终态单幂等 skipped 口径 + 防御纵深三道防线实证 + Barrier 窄窗争批);T-16 = D29 资金流中转(core_cash_flow 全仓唯一写入点,out/redeem + in/subscribe 各 1 条 remark 带 gid 同事务,5 处清理段 FK 坑修复);T-17 = D30 A/C 互转(share_class 非空且不同才查 allow_ac_convert 开关,任一端=1 两向放行,联网核实 = 摩根 2025-11「开通」公告;口径裁定:NULL 不参与判定、同类走一般规则,待用户追认),下一步 = 第 6 步集成测试)**。**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 交易日)。
- **(二)下一线 · 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 开发已收官(2026-09-12,T-1~T-17 全部完成 + 三项开口闭环【PRD §2.5.3 修订 / truncate 已接入 / R-12 定稿】,**849 passed / 8 skipped** 零失败;真库 verify 套件 **9/9 退出码 0**:accept 43 / confirm 103 / engine 39 / api 89 / compensate 56 / apply 30 / lots 20 / seed 全 PASS / tools 14。T-15 = D28 部分成交专项(终态单幂等 skipped 口径 + 防御纵深三道防线实证 + Barrier 窄窗争批);T-16 = D29 资金流中转(core_cash_flow 全仓唯一写入点,out/redeem + in/subscribe 各 1 条 remark 带 gid 同事务,5 处清理段 FK 坑修复);T-17 = D30 A/C 互转(share_class 非空且不同才查 allow_ac_convert 开关,任一端=1 两向放行,联网核实 = 摩根 2025-11「开通」公告;口径裁定:NULL 不参与判定、同类走一般规则,已追认定稿),下一步 = 第 6 步集成测试)**。**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 交易日)。
**仓库地图:**
| 路径 | 状态 | 说明 |
+3 -3
View File
@@ -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-12):T-1 ~ T-17 全部完成(全量 pytest 842 passed / 8 skipped 零失败;真库 verify 套件 9/9 退出码 0:accept 43 / confirm 103 / engine 39 / api 89 / compensate 56 / apply 30 / lots 20 / seed 全 PASS / tools 14,v1.0 链路 verify_convert_service 已随 convert_fund 退役删除),下一步 = 第 6 步集成测试**。撤单范围: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-12):T-1 ~ T-17 全部完成(全量 pytest 849 passed / 8 skipped 零失败(含 T-14 开口② truncate 接入 +7);真库 verify 套件 9/9 退出码 0:accept 43 / confirm 103 / engine 39 / api 89 / compensate 56 / apply 30 / lots 20 / seed 全 PASS / tools 14,v1.0 链路 verify_convert_service 已随 convert_fund 退役删除),下一步 = 第 6 步集成测试**。撤单范围:PRD §5.5 原表述与实现(仅 `accepted`)不矛盾、无需回填(第三轮 C1 订正)。
### 基金转换线待办(推荐顺序)
@@ -40,10 +40,10 @@
- [x] **T-11 core_tools 汇总去重 + 持仓过滤 ✅(2026-09-12,生产代码零改动)**:口径三处同调(F-10)与 `list_holdings` 的 `qty > 0` SQL 过滤均已就位(v0.x T-11 产物),本轮增量 = 把口径用例升级为 **T+1 真实链路场景**(R-回归 8「保留 + 补用例」/ R-回归 13「补确认段场景」—— T+1 下流水与持仓只在确认段产生):`tests/test_core_tools.py` +2 —— ① `test_confirm_then_sum_amount_counts_once`(受理→`confirm_one` 落两条同 gid 流水后 `sum_amount` = 转出端 40812.00 + 无组普通 100000 = 140812.00,明细仍 3 条全量,`sum_trades_on_date(T+1)` 跨口径一致)② `test_confirm_full_transfer_holding_zero_row_excluded`(先证归零行仍在库(台账留痕),再断言 `list_holdings`/`query_holdings` 只剩转入端)。**验证**:全量 **846 passed / 10 skipped**(844+2 零回归);突变 2 组均精准 3 红(去 `qty>0` / 绕开 `amount_view`)已还原;真库 `verify_convert_tools.py` 复跑 **14/14** 零残留。**执行期裁定 2 条**:① 转出端流水 `amount` = **费前**转出金额(与 PRD §5.3.2 的 68020.00 同语义,赎回费单列 `redeem_fee`)—— 初版锚点误按费后被真跑抓出;② `t1_env` 必须调 `seed_suitability_matrix`(缺 C×R 矩阵 → 受理全量 forbidden,与 conftest 同源)
- [x] **T-12 补偿 + SLA 清理切 T+1 链路 ✅(2026-09-12)**:① `cleanup_pending_convert.py` **整体重写**(扫描对象迁 Core:`list_inflight_before` 扫 `accepted/nav_pending` 且 `requested_at` 严格早于 cutoff → 条件 UPDATE 置 `expired`(S2 不硬删、天然幂等不加锁);SLA 边界 = 当前业务日上推 `convert_confirm_sla_days` 个交易日 00:00(`previous_biz_day` 链式,与 `_window_start` 同口径,日历缺失降级自然日并告警);`--days`/`--dry-run`;**不动镜像**(T-4 契约));② `compensate_convert` **从 v1.0 Deprecated 组救回在役**(详情侧 = Core 三件套前置校验(流水 ≥2 → `confirmed` → 明细非空,任一不满足 `missing` 零写入),补写与确认段第⑧步同口径 `sync_mirror` + `confirmed` 审计 `phase='confirm-compensate'`);③ `convert_request_repository` +`list_inflight_before`。**联网核实**(用户铁律):基金转换「无效申请」真实口径(多家基金公司业务规则:TA T+1 确认、无效资金退回 → `rejected` 映射成立)+ A 股连续休市最长 = 2020 春节 10 自然日(上交所通知,`_MAX_SCAN_DAYS=256` 依据成立)。**⚠️ 真库脚本暴露并修复**:`confirm_one` 第⑦步引擎异常只 log 不留痕(全仓唯一不落痕路径)→ except 补写 `decision='engine_error'` 审计 + `engine_error` 标志贯穿(D17 不阻断保留;test_convert_confirm 断言翻转 is True + 补审计断言)。**验证**:全量 **849 passed / 10 skipped**(846+3 零回归);真库 `verify_convert_compensate.py` **整体重写 T+1 链路 56/56 残留 0**(含幂等重跑;真库修正 1 条口径:`confirmed` 审计真链路恰 2 条 = 确认段 + 补偿各 1);**突变 4 组全被抓还原**(cleanup 窗口反转 5 红 / 前置守卫删除 1 红 / 真库 expired→cancelled 2 红 / 真库 nav_stale=True 1 红)。**勘误**:开发计划原写 `scripts/agent/rebuild_alerts.py` 实际在 `scripts/demo/`(零改动)
- [x] **T-13 全量门禁 + convert_fund 退役 ✅(2026-09-12,提交 `3bc84a0`)**:① `convert_fund` 及 v1.0 依赖链净删 468 行(`rebuild_convert_response` 按实际改判**保留在役**:T-9 查询接口读路径);`verify_convert_service.py` git rm;`test_convert_service.py`(805→约 440 行)与 `test_convert_concurrency.py`(v1.0 九用例 → T+1 六用例)全量重写;grep 仅余 4 处注释性历史说明、零调用。② **门禁**:全量 **830 passed / 7 skipped**(849 → 830 为 v1.0 用例组退役净减);CONVERT_STRESS=1 **6/6**;真库 **9/9 退出码 0**。③ **性能实测(PRD §9 第 18 条已回填)**:端到端 P50 79.2 / P95 110.3 / max 114.7 ms —— < 2s 硬门禁余量 17 倍。④ **紧池 100%(验收 31 已回填 PRD)**:50/50 受理 + 50/50 串行确认(v1.0 同场景 80% → 病根结构性消除)。⑤ **计划外产出**:`verify_convert_lots.py` B/C/D 组仍按 v1.0 金额赎回传 `amount=`(T-9 已改份额申报,redeem 分支 `qty is None` 静默跳过)→ 修正为 `qty=` 后 20/20。
- [x] **T-14 D27 产品舍入真正接入 + 联网核实纠偏 ✅(2026-09-12)**:**接入面 4 处单点收敛**——`core_ro.get_share_digits`(全仓唯一回退链:规则行 → `core_product.share_digits` → `calc.PLACES`=2)· `calc.in_qty` 加可选 `digits=PLACES`(默认行为逐字节不变,内部走 `product_round`)· 确认段(business_type=`convert`)· 网关申购折算(`subscribe`);redeem 份额申报(D26)无消费方。**边界裁定**:只接落库侧(DoD 即边界),展示维持 `q(D2)`(库内 DECIMAL(18,4) 本高于展示位,2 位产品零漂移)。**联网核实(用户铁律 · 本轮最重要产出)**:主流 2 位 ✅(民生加银/富时/新华/泰康)· **位数按各基金合同配置 = 行业真实做法** ✅(南方 2012 转换调整公告)· 「舍去 TRUNCATE」型真实存在 ✅(南方利众/宝元、邮储、华宝、申万菱信、东方)· 货基 2 位 ✅(中银/融通/鑫元)· **「指数基金份额 4 位」证伪** ❌(易方达沪深300非银 ETF 合同、汇添富深证300/华夏恒生/科创板 ETF 联接全 2 位;易方达创业板新能源 ETF 联接转换公告明写「转入份额保留两位」)→ **`11-seed-share-rule.sql` v1.1:3 产品 9 行 4→2 + 头注重写为核实出处**;「易方达 ETF 场外整数位」⚠️ 无证据且有反证(真实整数位载体 = 场内渠道,LOF 场内先 2 位再截位整数)。**测试 +7(830→837 零回归)**:calc 层 2 + confirm 段 3(回退链三层各 1,第一层带 business_type 干扰行)+ 申购段 2;4 位种子行 = D27 机制验证载体(docstring 钉明)。**突变 2 组**(confirm/gateway digits 改固定 2 → 各 2 红,还原零残留);真库 **9/9 退出码 0**(seed 重灌 42 行全 2 位生效)。**开口 2 项待用户拍板**:① PRD §2.5.3「易方达 ETF 场外取整数位」修订;② `rounding_mode=truncate` 消费接入(真实存在、T-14 范围外)。
- [x] **T-14 D27 产品舍入真正接入 + 联网核实纠偏 ✅(2026-09-12)**:**接入面 4 处单点收敛**——`core_ro.get_share_digits`(全仓唯一回退链:规则行 → `core_product.share_digits` → `calc.PLACES`=2)· `calc.in_qty` 加可选 `digits=PLACES`(默认行为逐字节不变,内部走 `product_round`)· 确认段(business_type=`convert`)· 网关申购折算(`subscribe`);redeem 份额申报(D26)无消费方。**边界裁定**:只接落库侧(DoD 即边界),展示维持 `q(D2)`(库内 DECIMAL(18,4) 本高于展示位,2 位产品零漂移)。**联网核实(用户铁律 · 本轮最重要产出)**:主流 2 位 ✅(民生加银/富时/新华/泰康)· **位数按各基金合同配置 = 行业真实做法** ✅(南方 2012 转换调整公告)· 「舍去 TRUNCATE」型真实存在 ✅(南方利众/宝元、邮储、华宝、申万菱信、东方)· 货基 2 位 ✅(中银/融通/鑫元)· **「指数基金份额 4 位」证伪** ❌(易方达沪深300非银 ETF 合同、汇添富深证300/华夏恒生/科创板 ETF 联接全 2 位;易方达创业板新能源 ETF 联接转换公告明写「转入份额保留两位」)→ **`11-seed-share-rule.sql` v1.1:3 产品 9 行 4→2 + 头注重写为核实出处**;「易方达 ETF 场外整数位」⚠️ 无证据且有反证(真实整数位载体 = 场内渠道,LOF 场内先 2 位再截位整数)。**测试 +7(830→837 零回归)**:calc 层 2 + confirm 段 3(回退链三层各 1,第一层带 business_type 干扰行)+ 申购段 2;4 位种子行 = D27 机制验证载体(docstring 钉明)。**突变 2 组**(confirm/gateway digits 改固定 2 → 各 2 红,还原零残留);真库 **9/9 退出码 0**(seed 重灌 42 行全 2 位生效)。**开口 2 项 → ✅ 2026-09-12 用户拍板全闭环**:① PRD §2.5.3 已修订(删易方达句);② truncate 已接入(`get_share_rounding` 单点 + `product_round`/`in_qty` mode 参数 + 09 ⑦ 段 PROD-005828 载体;测试 +7 → **849 passed / 8 skipped**,真库 9/9,突变红/还原绿)。
- [x] **T-15 D28 部分成交专项测试 + 边界补强 ✅(2026-09-12)**:sqlite +1 `test_confirm_after_cancel_conflicts`(**时序口径裁定**:`confirm_one` 对终态单(含 cancelled)= 幂等 `skipped` + current_status 回显,`ConfirmConflict` 仅限「读单后状态才变」窗口);真库 verify_confirm +3 组 19 项(E2 占用释放双向证明 / G2 `actual_qty=0` → `rejected` 零残留 / H2 撤单后确认)→ **102/102**;R-10 实测钉死 = 部分确认吃掉全部物理可用、「释放」的是受理占用。**CONVERT_STRESS 争批**:`Barrier(2)` 钉 `apply_convert` 事务门口构造「两单都完成 ④⑤ 读」窄窗,断言恰 1 confirmed + 1 rejected。**突变 = 防御纵深实证**:单独拆事务内哨兵不红(④⑤ 拦)、单独拆 ④ partial_qty 不红(⑤ plan_lots 拦)→ 窄窗 + 同拆哨兵 ×3 红、还原 ×3 绿——④可用量复核 / ⑤plan_lots / 事务内哨兵三道防线拆任何一道仍收敛 rejected。全量 **838 passed / 8 skipped**。
- [x] **T-16 D29 资金流中转建模 ✅(2026-09-12)**:`_apply_convert_once` 在 `_insert_trades` 后加 `_insert_cash_flows` —— **全仓唯一写入点**,2 条 `out/redeem`=out_amount(费前)+ `in/subscribe`=in_amount(净转入),remark=`convert:{gid}`、channel=online,与流水/明细/持仓/置态同事务(幂等由 T-7 哨兵保证);`tests/_ddl.py` 补 sqlite 表 + 门禁;`test_confirm_writes_no_cash_flow` **反转为** `test_confirm_writes_exactly_two_cash_flows`(T-7 登记「表建立后改 0 行断言」闭环);真库 apply **A2 组 5 项 + B 组冲突 0 残留 → 24 → 30 项**、confirm「不写资金流」断言反转 → **103 项**。**⚠️ FK 新坑(T-9 同款)**:`core_cash_flow.fk_cf_customer` 挡客户删除 → **5 处清理段补删**(verify_apply / test_convert_integration / verify_engine / verify_api / verify_compensate,remark LIKE 精准定位)。**突变**:拆写入调用 → 反转用例红,还原全绿。
- [x] **T-17 D30 share_class + A/C 互转 ✅(2026-09-12 · 转换线收官)**:`_validate_products` ta_code 后**纯新增拦截分支**——两端 share_class 非空且不同 = A/C 场景,任一端 `allow_ac_convert=1` 两向放行、全关 → `CrossEntityNotSupported`(错误码沿用,message 指明 A/C)。**口径裁定 2 条(R-12 字面偏差,待用户追认)**:① NULL 不参与 A/C 判定(字面会打穿全部既有受理用例);② 「share_class 相同 → 400」不采纳(同类别跨基金走一般转换规则)。**联网核实**:A/C 互转需管理人「开通」(摩根行业睿选 2025-11 公告)= 开关真实载体;转换后持有期重新起算(本仓转入新批次一致零改动);转换费 = 赎回费 + 申购补差(本仓范围外)。**种子 ⑥ 段**(09-seed-org.sql:双开对/双关对照/同类对照,其余 NULL 零回归)。**测试 +4(838→842)**:双开/单开/双关 400/同类+NULL 回归保护;真库 accept **H 组 6 项 → 43/43**(H3 首跑撞种子 min_redeem_qty=100 教训:种子产品做载体须通读既有段);**突变**:分支恒 False → closed 红,还原绿。模拟库近似留痕:无 fund_code,判定 = 同管理人同 TA + share_class 非空且不同。
- [x] **T-17 D30 share_class + A/C 互转 ✅(2026-09-12 · 转换线收官)**:`_validate_products` ta_code 后**纯新增拦截分支**——两端 share_class 非空且不同 = A/C 场景,任一端 `allow_ac_convert=1` 两向放行、全关 → `CrossEntityNotSupported`(错误码沿用,message 指明 A/C)。**口径裁定 2 条(R-12 字面偏差,✅ 2026-09-12 用户追认定稿,R-12 条目已回写)**:① NULL 不参与 A/C 判定(字面会打穿全部既有受理用例);② 「share_class 相同 → 400」不采纳(同类别跨基金走一般转换规则)。**联网核实**:A/C 互转需管理人「开通」(摩根行业睿选 2025-11 公告)= 开关真实载体;转换后持有期重新起算(本仓转入新批次一致零改动);转换费 = 赎回费 + 申购补差(本仓范围外)。**种子 ⑥ 段**(09-seed-org.sql:双开对/双关对照/同类对照,其余 NULL 零回归)。**测试 +4(838→842)**:双开/单开/双关 400/同类+NULL 回归保护;真库 accept **H 组 6 项 → 43/43**(H3 首跑撞种子 min_redeem_qty=100 教训:种子产品做载体须通读既有段);**突变**:分支恒 False → closed 红,还原绿。模拟库近似留痕:无 fund_code,判定 = 同管理人同 TA + share_class 非空且不同。
> **第 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)。
> ⚠️ **以下四条引用块均为 v0.x 两阶段实时线记录,其 T 编号与新线(T-1~T-17)无关、勿混**:新线 T-11 亦涉 `amount_view`,与 v0.x T-11「提升为公开函数」是两回事。