文档:T-12 回写 + 全仓进度一致性(开发计划 DoD 全勾 + 执行记录)
- 开发计划 v2.0 T-12 节:DoD 4/4 全勾 + 执行记录(三改动面 / 联网核实留痕 / confirm_one 留痕修复 / 真库 56/56 / 突变 4 组 / rebuild_alerts 路径勘误) - 交接文档 v4.2(T-12 完成块 + 基线 849,.gitignore 内不入库) - TODO.md / MEMORY.md / AGENTS.md:T-1~T-12 ✅ / T-13~T-17 ⬜,基线 849 - 2026-09-12.md 追加 T-12 执行记录(含用户铁律:外部真实业务事实必须联网核实)
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
- 技术选型 / JWT:`docs/项目框架设计/技术选型和版本/`
|
||||
- Core 模拟底座:`docs/项目框架设计/Core模拟底座/`
|
||||
- 业务记忆分层:`docs/业务记忆管理/业务记忆管理手册.md`
|
||||
- **基金转换线(第 5 步 todo 开发进行中 · T+1 受理/确认分离模型)**:**T-1 ~ T-11 ✅ / T-12 ~ T-17 ⬜**(当前基线 **846 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**、T-9/T-10 `verify_convert_api.py` **89/89**、T-11 复跑 `verify_convert_tools.py` **14/14**,退出码均 0;下一步 **T-12** = 补偿脚本改写);开工入口 **`交接文档.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-12 ✅ / T-13 ~ T-17 ⬜**(当前基线 **849 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**、T-9/T-10 `verify_convert_api.py` **89/89**、T-11 复跑 `verify_convert_tools.py` **14/14**、T-12 重写 `verify_convert_compensate.py` **56/56**,退出码均 0;下一步 **T-13** = 全量回归 + 并发/性能补录 + convert_fund 退役);开工入口 **`交接文档.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**
|
||||
|
||||
|
||||
+110
-1
@@ -1,6 +1,6 @@
|
||||
# 2026-09-12 工作日志
|
||||
|
||||
> 本日:基金转换线(T+1 受理/确认分离模型)**第 5 步 T-11 完成** —— core_tools 汇总去重 + 持仓过滤(确认段场景补用例,生产代码零改动)。
|
||||
> 本日:基金转换线(T+1 受理/确认分离模型)**第 5 步 T-11、T-12 完成** —— T-11 core_tools 汇总去重 + 持仓过滤(确认段场景补用例,生产代码零改动);T-12 补偿 + SLA 清理切 T+1 链路 + **confirm_one 引擎异常留痕修复**(真库脚本暴露)。**用户新增铁律:不确定的外部真实业务事实必须联网核实,禁止凭训练记忆假设。**
|
||||
|
||||
---
|
||||
|
||||
@@ -70,3 +70,112 @@
|
||||
**T-12 补偿脚本改写**(R-14):`cleanup_pending_convert.py` 扫描对象迁 `core_convert_request`
|
||||
(accepted/nav_pending 超 2 交易日 → expired)+ `rebuild_alerts.py` 凭 Core 侧数据补写。
|
||||
入口:开发计划 T-12 节。
|
||||
|
||||
|
||||
---
|
||||
|
||||
# T-12 执行记录(补偿 + SLA 清理切 T+1 链路 · 2026-09-12)
|
||||
|
||||
## 一、改动面(三处)
|
||||
|
||||
1. **`scripts/agent/cleanup_pending_convert.py` 整体重写**(R-14):扫描对象迁 Core——
|
||||
`ConvertRequestRepository.list_inflight_before` 扫 `accepted/nav_pending` 且
|
||||
`requested_at` **严格早于** cutoff → 逐单 `transition_status` 条件 UPDATE 置 `expired`
|
||||
(S2 不硬删;并发双跑天然幂等,不加锁);SLA 边界 `_slab_cutoff` = 当前业务日
|
||||
(今天开市取今天否则 `previous_biz_day` 回退)上推 `convert_confirm_sla_days` 个
|
||||
**交易日** 00:00,与 `confirm_batch._window_start` 同口径,日历数据缺失降级自然日并
|
||||
告警(宁可少清、不可误清);`--days` / `--dry-run` 参数、退出码 0/1、
|
||||
**不动 agent 镜像**(T-4 契约:镜像不推 cancelled/expired)。
|
||||
2. **`compensate_convert`(convert_service)从 `__all__` 的 v1.0 Deprecated 组救回在役**:
|
||||
详情侧数据源 = **Core 三件套前置校验**(流水 ≥2 条 → 受理单 `status=confirmed` →
|
||||
计费明细非空,任一不满足 `state=missing` **零写入不硬补**);补写与确认段第⑧步
|
||||
同口径(`sync_mirror(completed)` 聚合口径 nav=T 日净值 / nav_date=受理日 /
|
||||
fee=Σ逐批 / hold_days=min/max)+ `decision='confirmed'` 审计 `phase='confirm-compensate'`
|
||||
+ `engine_error` 如实标记;预警侧幂等锚点不变(转出端 `out_trade_id` +
|
||||
`has_engine_error_audit` 中间态「人工核对」提示)。`convert_fund` /
|
||||
`rebuild_convert_response` 仍留 Deprecated(T-13 退役)。
|
||||
3. **`convert_request_repository` +`list_inflight_before`**(T-12 专用捞单,复用 R-2
|
||||
条件 UPDATE 守卫,不复制 SQL——自检第 13 问)。
|
||||
|
||||
**勘误**:开发计划 T-12 节原写 `scripts/agent/rebuild_alerts.py`,实际在 `scripts/demo/`
|
||||
(零改动——薄封装委托 compensate_convert,签名未变)。
|
||||
|
||||
## 二、联网核实(用户铁律 2026-09-12)
|
||||
|
||||
> 用户原话:「你不确定的都要联网查找,这个项目要贴近真实项目,这是铁律,比如你刚才说的过年假期问题。」
|
||||
|
||||
1. **「基金转换申请失效」真实口径**(多家基金公司《开放式基金业务规则》:
|
||||
华安 / 富国 / 嘉实 / 苏新 / 太平 / 德邦 / 民生加银):转出不可赎回或转入不可申购 →
|
||||
申请无效;账户/份额冻结 → 无效;TA **T+1 确认、T+2 可查**;无效申请资金退回原账户;
|
||||
太平基金:转换转出参照巨额赎回规则。**映射本项目**:`rejected`(T+1 复核不通过)=
|
||||
真实「无效申请」,`expired` 定位为运维兜底(释放占用 = 份额退回效果)——设计贴近真实 ✓。
|
||||
2. **A 股连续休市历史最长 = 2020 年春节 10 个自然日**(国务院办公厅延长春节假期;
|
||||
上交所《关于2020年春节延长休市相关业务衔接安排的通知》:休市延至 2 月 2 日、
|
||||
2 月 3 日恢复开市)——`trading_calendar._MAX_SCAN_DAYS=256` 的量级依据成立;
|
||||
结论留痕进 verify 脚本头注释。**WebSearch 三次超时后改换关键词命中**;
|
||||
verify 造数的受理日 / SLA 边界全部改 `previous_biz_day` 链式走**真库交易日历**
|
||||
(对任意假期安排鲁棒,不猜自然日)。
|
||||
|
||||
## 三、⚠️ 真库脚本暴露并修复:confirm_one 引擎异常不留痕(本轮最重要产出)
|
||||
|
||||
**问题**:`confirm_one` 第⑦步引擎异常只 `logger.exception` 不落审计——
|
||||
`engine_result=None` → confirmed 审计 / return dict 的 `engine_error=False`。
|
||||
**全仓唯一不落痕路径**(v1.0 阶段 1.5 落 `decision='engine_error'` 审计
|
||||
convert_service.py:1013、trade_gateway 落 `risk_engine_error`),且补偿侧
|
||||
`has_engine_error_audit` 的「人工核对」保护对 T+1 主链路**永远失效**
|
||||
(单测 `_prepare_failed_confirm_t1` 是手插审计造数,看不见这个洞)。
|
||||
|
||||
**修复**(confirm_service.py 第⑦步 except):补写 `decision='engine_error'` 审计
|
||||
(summary 带 phase/accept_date/confirm_date/两流水号)+ `engine_error` 标志贯穿
|
||||
confirmed 审计与 return dict;**D17「不阻断」语义保留**(测试注释钉死
|
||||
「不阻断但留痕 + 标记」);`test_convert_confirm.py::test_engine_exception_...`
|
||||
断言翻转 `is False → is True` + 补审计计数断言(audit_log 无 convert_group_id 列,
|
||||
按 `input_summary LIKE %gid%` 查)。
|
||||
|
||||
## 四、测试与真库验证
|
||||
|
||||
- `tests/test_convert_service.py`:补偿用例 4 条改 T+1 断言 + 新增 2
|
||||
(`missing_when_request_not_confirmed` / `inserts_mirror_when_row_absent`——
|
||||
造数注意:`insert_audit_log` 需补齐 risk_score/handler_* 四个 None 键、无 created_at);
|
||||
- `tests/test_demo_scripts.py`:cleanup 段重写(`_seed_calendar_allopen` 每天开市走
|
||||
真实路径不测降级 + `_t12_cleanup` 新签名)+ 新增
|
||||
`test_cleanup_window_matches_confirm_batch_window`(行为化等价:窗口下界日
|
||||
confirm_batch 能捞到且 cleanup 不清;前一交易日两边都排除);
|
||||
**方向教训:days 越大 → cutoff 越早 → 候选越少**(首版断言写反被自查抓出);
|
||||
- `tests/test_convert_concurrency.py`:并发补偿改 T+1 造数(core 库插 confirmed
|
||||
受理单 + agent 库 UPDATE 镜像 failed——两库引擎别用混,pymysql 1146)+
|
||||
真库清场补 `core_convert_request` 删除(按 customer 与按 product 两路、先于
|
||||
product 删——**FK 1451**);
|
||||
- 全量 **849 passed / 10 skipped**(846 + 3,零回归);
|
||||
- 真库 `scripts/dev/verify_convert_compensate.py` **整体重写为 T+1 链路**:
|
||||
受理(真实 `resolve_accept_date` 定 T、`next_biz_day` 定 T1,脚本对任意运行日稳)
|
||||
→ `confirm_one`(patch sync_mirror + engine_hook=boom,第⑦⑧步双失败真实落痕)
|
||||
→ Core 三件套对表 → 补偿 rebuilt / 复跑 skipped + 人工核对 / 真 JSON 列 LIKE /
|
||||
cleanup(ENUM 值域 / dry-run / 窗口同口径对表 `_window_start` / 幂等 / S2 / 镜像不推 /
|
||||
NEW 恒不进候选)→ 残留清零,**56/56 退出码 0、残留 0**(含幂等重跑);
|
||||
**真库断言口径修正 1 条**:`decision='confirmed'` 审计真链路下**恰 2 条**
|
||||
(确认段 `phase='confirm'` + 补偿 `phase='confirm-compensate'` 各 1、各司其职)——
|
||||
v1.0「主审计 1 条」是单测造数(未跑 confirm_one)的盲区,真库端到端才验得出。
|
||||
|
||||
## 五、突变验证 4 组(全部被抓后还原)
|
||||
|
||||
| # | 突变 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | cleanup `_slab_cutoff` 回推方向反转(previous→next_biz_day) | 单测 **5 红**(含窗口等价用例) |
|
||||
| 2 | compensate 前置 `confirmed` 守卫删除(只留 request_row is None) | 单测 **精准 1 红** |
|
||||
| 3 | cleanup 目标状态 expired→cancelled | 真库 **2 红**(ENUM/状态断言) |
|
||||
| 4 | 补偿镜像 `nav_stale=True` | 真库 **1 红**(nav_stale 断言) |
|
||||
|
||||
## 六、文档回写
|
||||
|
||||
开发计划 v2.0 T-12 节(DoD 4/4 全勾 + 执行记录)· `交接文档.md`(v4.1 → **v4.2**)·
|
||||
`docs/memory/TODO.md`(进度行 + T-12 勾选)· `docs/memory/MEMORY.md`(3 处进度)·
|
||||
`AGENTS.md`(进度行)· 本日志。
|
||||
|
||||
## 七、下一步
|
||||
|
||||
**T-13**(全量回归 + 集成 + 并发 + 性能补录 + **`convert_fund` 退役**,用户 2026-09-11
|
||||
裁定并入):全量 pytest 基线回填本文件头 · 7+2 真库 verify 套件退出码 0 ·
|
||||
CONVERT_STRESS 并发 · 性能实测(端到端 < 2s 硬门禁)· 紧池成功率重测 ·
|
||||
`convert_fund` / `rebuild_convert_response` 删除 + v1.0 依赖链
|
||||
(约 25 处测试 + `verify_convert_service.py`)改写。
|
||||
|
||||
@@ -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-11 ✅,**846 passed / 10 skipped** 零失败;T-6 真库 verify **37/37**、T-7 真库 verify **83/83**、T-8 真库 verify **39/39**、T-9/T-10 真库 verify_convert_api **89/89**、T-11 复跑 verify_convert_tools **14/14**,退出码均 0;下一步 T-12 补偿脚本改写)**。**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 开发进行中(T-1~T-12 ✅,**849 passed / 10 skipped** 零失败;T-6 真库 verify **37/37**、T-7 真库 verify **83/83**、T-8 真库 verify **39/39**、T-9/T-10 真库 verify_convert_api **89/89**、T-11 复跑 verify_convert_tools **14/14**、T-12 重写 verify_convert_compensate **56/56**,退出码均 0;下一步 T-13 全量回归 + convert_fund 退役)**。**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 交易日)。
|
||||
**仓库地图:**
|
||||
|
||||
| 路径 | 状态 | 说明 |
|
||||
@@ -200,8 +200,8 @@ RBAC 联调账号:scripts/dev/rbac-seed-reference.md
|
||||
2. 改动属于 api / service / tool / repository 哪一层?
|
||||
3. 是否需 customer_id 归属与 JWT RBAC?
|
||||
4. Core 是模拟库只读还是 agent 库读写?
|
||||
5. 如何验证?(`python -m pytest` 全量(当前 **846 passed / 10 skipped**,风控基线 510 / v0.x convert 完结 739)· 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-11 ✅,846 绿**))——动代码前先确认自己属于哪条线,别混淆前置条件。
|
||||
5. 如何验证?(`python -m pytest` 全量(当前 **849 passed / 10 skipped**,风控基线 510 / v0.x convert 完结 739)· 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-12 ✅,849 绿**))——动代码前先确认自己属于哪条线,别混淆前置条件。
|
||||
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 <sha>` 返回 0,但松散引用消失,**且整个 `refs/remotes/origin/` 目录被删**
|
||||
|
||||
+3
-2
@@ -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-11 完成(全量 pytest 846 passed / 10 skipped 零失败;T-6 真库 verify 37/37、T-7 真库 verify 83/83、T-8 真库 verify 39/39、T-9/T-10 真库 verify_convert_api 89/89、T-11 复跑 verify_convert_tools 14/14,退出码均 0),下一步 T-12(补偿脚本改写)**。撤单范围: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-12 完成(全量 pytest 849 passed / 10 skipped 零失败;T-6 真库 verify 37/37、T-7 真库 verify 83/83、T-8 真库 verify 39/39、T-9/T-10 真库 verify_convert_api 89/89、T-11 复跑 verify_convert_tools 14/14、T-12 重写 verify_convert_compensate 56/56,退出码均 0),下一步 T-13(全量回归 + 并发/性能补录 + convert_fund 退役)**。撤单范围:PRD §5.5 原表述与实现(仅 `accepted`)不矛盾、无需回填(第三轮 C1 订正)。
|
||||
|
||||
### 基金转换线待办(推荐顺序)
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
- [x] **T-9 API |网关分派 ✅(2026-09-11)**:三接口路径以 PRD 为准(裁定 11/12)——`convert_admin.py` 撤单/确认批处理/查询 + `simulate.py` convert 分支 **202 + 受理回执**(两类 202)+ `trade_gateway` 分派改调 `accept_convert`(`SUPPORTED_TRADE_TYPES` 维持 2 类型)+ **redeem 份额申报**(D26/R-6 `_redeem_quote = qty × D 净值 − 赎回费`)+ 错误码 `CANCEL_NOT_ALLOWED`/`CONCURRENT_CONFLICT`(409)。**验证**:端到端 test_convert_integration 重写 T+1 两段链路 13 passed;全量 **834 passed / 10 skipped**;真库 `verify_convert_api.py` HTTP 全链路 **88/88**;3 组突变验证全命中;修 2 处口径缺陷(`out_nav` 反推漂移 → 直读明细、幂等字段面对齐)。**执行缺口**:`convert_fund` 退役并入 **T-13**
|
||||
- [x] **T-10 普通申赎批次维护 + T+2 校验 + rebuild_lots ✅(2026-09-11)**:`trade_gateway` 主流程改 —— `_redeem_quote` 走 `available_qty_with_inflight` **硬校验**(R-3,申报超可赎抛 `InsufficientShares` 不静默裁剪);`_maintain_lots` redeem 三重裁剪(T+2 过滤 → D8 补建 → 在途占用 → 哨兵 FIFO 扣减),subscribe 落新批次;`core_ro.list_share_lots`/`share_lot_repository.select_for_convert` 加 `available_from` **T+2 半开区间过滤**(R-4,日历缺行降级不过滤同口径);`gateway_repository` 扣批次哨兵 `remain_qty >= :q`(防超扣)。**验证**:`test_share_lot` +10(半开区间双向/区分度/在途拦截与释放/哨兵回滚/转换后不超扣)+ `test_trade_gateway` +1(金额公式 995.00)→ 全量 **844 passed / 10 skipped**(834+11 零回归);真库 `verify_convert_api.py` **89/89**(H 节提前 + H2 在途占用拦截断言);`rebuild_lots.py --dry-run` 58 持仓 0 补建 0 写入;6 项 DoD 全闭环 + 突防验证(移除透传 → t2 用例变红 → 还原)
|
||||
- [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 同源)
|
||||
- [ ] T-12 ~ T-17(补偿改写 / 全量回归 / D27 产品级舍入 / D28 巨额赎回 / D29 资金流中转 / D30 A/C 互转)。**注(2026-09-11 用户裁定)**:`convert_fund` 退役并入 **T-13**(T-13 目标/改法 6/DoD/风险已加)
|
||||
- [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/`(零改动)
|
||||
- [ ] T-13 ~ T-17(全量回归 + 并发/性能补录 + convert_fund 退役 / D27 产品级舍入 / D28 巨额赎回 / D29 资金流中转 / D30 A/C 互转)。**注(2026-09-11 用户裁定)**:`convert_fund` 退役并入 **T-13**(T-13 目标/改法 6/DoD/风险已加)
|
||||
|
||||
> **第 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「提升为公开函数」是两回事。
|
||||
|
||||
@@ -671,14 +671,24 @@
|
||||
**依赖**:T-4 / T-7
|
||||
|
||||
**DoD**:
|
||||
- [ ] 造数:accepted 超 2 交易日 → 脚本执行后 `expired`(真库,仿 verify_convert_compensate.py)
|
||||
- [ ] nav_pending 超 SLA → `expired`(不早于确认窗口)
|
||||
- [ ] 第二次执行 0 处理(幂等)
|
||||
- [ ] `rebuild_alerts.py` 从 Core 净数据补出的 detail 与流水一致(验收 17)
|
||||
- [x] 造数:accepted 超 2 交易日 → 脚本执行后 `expired`(真库,仿 verify_convert_compensate.py)
|
||||
- [x] nav_pending 超 SLA → `expired`(不早于确认窗口)
|
||||
- [x] 第二次执行 0 处理(幂等)
|
||||
- [x] `rebuild_alerts.py` 从 Core 净数据补出的 detail 与流水一致(验收 17)
|
||||
|
||||
**风险**:SLA 判定用交易日而非自然日(Q14)→ 用 trading_calendar.previous_biz_day 链式计算
|
||||
**回滚点**:脚本只改状态不硬删(S2),天然可逆
|
||||
|
||||
**✅ T-12 完成(补偿 + SLA 清理切 T+1 链路 · 2026-09-12)**:
|
||||
**三个改动面**:① `scripts/agent/cleanup_pending_convert.py` **整体重写**:扫描对象迁 Core(`list_inflight_before` 扫 `accepted/nav_pending` 且 `requested_at` 严格早于 cutoff)→ 逐单 `transition_status` 条件 UPDATE 置 `expired`(S2 不硬删;并发双跑天然幂等,不加锁);SLA 边界 `_slab_cutoff` = 当前业务日(今天开市取今天否则 `previous_biz_day` 回退)上推 `convert_confirm_sla_days` 个交易日的 00:00,日历数据缺失降级自然日并告警(宁可少清);`--days`/`--dry-run` 参数、退出码 0/1、**不动 agent 镜像**(T-4 契约:镜像不推 cancelled/expired)。② `compensate_convert`(convert_service)**从 `__all__` 的 v1.0 Deprecated 组救回在役**:详情侧数据源 = Core 三件套前置校验(流水 ≥2 条 → 受理单 `confirmed` → 计费明细非空,任一不满足 `state=missing` **零写入不硬补**),补写与确认段第⑧步同口径(`sync_mirror(completed)` + `decision='confirmed'` 审计 `phase='confirm-compensate'`、`engine_error` 如实标记);`convert_fund`/`rebuild_convert_response` 仍留 Deprecated(T-13 退役)。③ `ConvertRequestRepository` 新增 `list_inflight_before`(T-12 专用捞单,复用 R-2 条件 UPDATE 守卫,不复制 SQL)。
|
||||
**勘误**:原「涉及文件 `scripts/agent/rebuild_alerts.py`」路径有误,实际在 `scripts/demo/`;该脚本零改动(薄封装委托 compensate_convert,签名未变)。
|
||||
**联网核实(用户铁律 2026-09-12)**:① 「基金转换申请失效」真实口径——多家基金公司《开放式基金业务规则》(华安/富国/嘉实/太平/德邦等):转出不可赎回或转入不可申购 → 无效;TA T+1 确认、T+2 可查;无效申请资金退回原账户。映射本项目:`rejected`(T+1 复核不通过)= 真实「无效申请」,`expired` 定位为运维兜底(释放占用 = 份额退回效果)——设计贴近真实 ✓;② A 股**连续休市历史最长 = 2020 年春节 10 个自然日**(国务院办公厅延长假期;上交所《关于2020年春节延长休市相关业务衔接安排的通知》:休市延至 2 月 2 日、2 月 3 日恢复开市)——`trading_calendar._MAX_SCAN_DAYS=256` 的量级依据成立,结论留痕进 verify 脚本头注释;verify 造数的受理日/SLA 边界全部改为 `previous_biz_day` 链式走**真库交易日历**,不猜自然日(对任意假期安排鲁棒)。
|
||||
**T-12 真库脚本暴露并修复 1 处三链路不一致(本轮最重要产出)**:`confirm_one` 第⑦步引擎异常**只 log 不留痕**(`engine_result=None` → confirmed 审计/return dict 的 `engine_error=False`)——全仓唯一不落痕路径(v1.0 阶段 1.5 落 `decision='engine_error'` 审计、trade_gateway 落 `risk_engine_error`),且补偿侧 `has_engine_error_audit` 的「人工核对」保护对 T+1 主链路**永远失效**。修复:confirm_service 第⑦步 except 分支补写 `decision='engine_error'` 审计 + `engine_error` 标志贯穿 confirmed 审计与 return dict(D17「不阻断」语义保留,测试注释同步钉死「不阻断但留痕 + 标记」)。
|
||||
**测试**:`tests/test_convert_service.py` 补偿用例 4 条改 T+1 断言 + 新增 2(`missing_when_request_not_confirmed` / `inserts_mirror_when_row_absent`);`tests/test_demo_scripts.py` cleanup 段重写(真实日历路径 + 幂等 + dry-run + 自定义 SLA 方向断言)+ 新增 `test_cleanup_window_matches_confirm_batch_window`(行为化等价:窗口下界日 confirm_batch 能捞到且 cleanup 不清,前一交易日两边都排除);`tests/test_convert_concurrency.py` 并发补偿改 T+1 造数 + 真库清场补 core_convert_request(FK 1451)。全量 **849 passed / 10 skipped**(846+3,零回归)。
|
||||
**真库**:`scripts/dev/verify_convert_compensate.py` **整体重写为 T+1 链路**(受理 → confirm_one 第⑦⑧步双失败真实落痕 → Core 三件套对表 → 补偿 rebuilt / 复跑 skipped / 真 JSON 列 LIKE / cleanup ENUM·窗口同口径·dry-run·幂等·镜像不推),**56/56 退出码 0、残留 0**(含幂等重跑)。真库断言口径修正 1 条:`decision='confirmed'` 审计真链路下**恰 2 条**(确认段 `phase='confirm'` + 补偿 `phase='confirm-compensate'` 各 1,各司其职)——v1.0「主审计 1 条」是单测造数(未跑 confirm_one)造成的盲区,真库端到端才验得出。
|
||||
**突变 4 组防假绿(全部被抓并还原)**:① cleanup 窗口方向反转(previous→next_biz_day)→ **5 红**(含窗口等价用例);② compensate 前置 confirmed 守卫删除 → **精准 1 红**;③ 真库 expired→cancelled → **2 红**(ENUM/状态断言);④ 真库镜像 `nav_stale=True` → **1 红**。
|
||||
**待办移交 T-13**:`convert_fund`/`rebuild_convert_response` 退役 + verify_convert_service.py 处置 + 全量门禁。
|
||||
|
||||
---
|
||||
|
||||
### T-13 · 全量回归 + 集成 + 并发 + 性能补录 + `convert_fund` 退役
|
||||
|
||||
Reference in New Issue
Block a user