① 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/交接文档开口清零
124 lines
45 KiB
Markdown
124 lines
45 KiB
Markdown
# TODO
|
||
|
||
> 大任务开始时更新;完成一步打钩并验证后再往下。
|
||
> **新 Agent:** 先看 `MEMORY.md` §0 交接清单,再从此处取下一项。
|
||
|
||
## 进行中
|
||
|
||
**阶段一 AL-01~AL-08 与阶段二 C4~C6 均已完成(2026-09-07)**:全量 pytest **482 passed 0 failed 0 skipped**(真库集成)✓ · uvicorn 冒烟三端点 ✓ · 接口实调验收 ✓(2026-09-07:suitability/check 阻断+放行、simulate/trade 阻断、三条鉴权边界 401/403/403,契约零偏差)· risk-m1~m4 tag 齐。**合并 main 已移交合并执行人**(操作手册:《docs/项目框架设计/合并注意事项-风控模块并入main.md》,随分支上传),后续模块侧待办见下方。
|
||
|
||
**⚡ 并行新线 · 基金转换(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 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 订正)。
|
||
|
||
### 基金转换线待办(推荐顺序)
|
||
|
||
- [x] **【第 4 步重做】产出开发计划 v2.0(T+1 模型)** —— **完成(2026-09-11)**:`docs/项目框架设计/开发计划-基金转换交易.md` **整体重做为 v2.0(797 行)**——T-0/T-0b 沿用(✅ 已完成),T-1~T-17 按架构 v2.1 §15 重排 + §1.4 **代码事实核对表 F-1~F-25**(实测文件:行号)+ §1.5 **实现级裁定 R-1~R-15** + §5 十六条回归面 + §6 验收 31 条全映射(+FR-C29/C30)+ §7 风险 14 条。**经三轮独立审核闭环**:第一轮 2 阻断(**B1** `convert_cutoff_time` 配置缺失违反 PRD §2.6.1 不得硬编码 → 已加 settings 全链读;**B2** `core_ro.get_nav_as_of` 实为 `nav_date <= :d LIMIT 1` 回退查询,缺 T 日净值会静默取 T−1 致 nav_pending 永不触发 → 已改新增 `get_nav_on` 精确匹配)+ 5 重要 + 3 可选全闭环;第二轮验证型逐条核实 12 项零残留;**第三轮白纸重审**抓出 5 重要(C1 撤单引述订正 / C2 §6 补 C29·C30 / C3 回归面补 redeem 6 处 / C4 删 0 命中误引 / C5 cash_flow 归属厘清)+ 4 可选,**验证型复核 22/22 落位 + D-1 残留修复**。**状态:已定稿(2026-09-11 用户批准)**(批准后进第 5 步 todo 开发)。⚠️ 开发计划 v2.0 语义与旧 v1.0 **完全不同**(v1.0 是 v0.x 实时模型,作废)
|
||
- [x] **【第 4 步】产出开发计划 v1.0(v0.x 模型 · 历史)** —— **完成(2026-09-10,已被 v2.0 取代)**:`docs/项目框架设计/开发计划-基金转换交易.md`(原 **1,047 行**,含 T-0~T-13 任务拆解 + 逐条改法 + DoD + **四轮审核记录**)。**四轮独立子代理审核已收敛:12 条意见 → 接受 11 / 驳回 1(附实测证据)/ 0 悬空**。**本计划新增的增量资产**:§1.3 两处 PRD-架构口径裁定(W-1/W-2)· §1.4 十五条代码事实核对表 · §1.5 七条实现级裁定 · §12 十六条回归面 · §13 PRD 19 条验收映射 · §14 新增实现级风险表。**注:该版本属 v0.x 两阶段实时模型,2026-09-11 已随模型切换整体作废(v2.0 重做,见上条)**
|
||
- [x] **【第 5 步 · T-0 · 阻断前置】sqlite/MySQL 结构对齐 + 建库自校验** —— **完成(2026-09-10)**:`tests/_ddl.py` 重写 `core_holding`(`qty`/`cost_amount`/`as_of`/`pnl_pct` + PK + `uk_cust_product`)+ 补 `core_product_nav`(R-f)+ `_assert_ddl_aligned()` 建库自校验(R-g);2 处测试 INSERT(`test_chat_tools.py:81`、`test_concentration_c4.py:74`)改 `qty` **并补 3 个 NOT NULL 列**(原计划漏项);`test_db.py` 新增 `test_core_holding_columns` / `test_core_product_nav_columns` / `test_ddl_alignment_guard_fails_when_column_missing`(反向验证门禁,无需手工改删)
|
||
- [x] **【T-0b · 阻断前置】DB 账号分离(D20)** —— **完成(2026-09-10)**:`scripts/core/00-grant.sql` 新建(3 账号逐表授权,**不进 reset.ps1**);`settings.py` +6 项;`db.py` 改 `get_engine(db, role)` + `_resolve_credentials`(缓存键 `(db, role)`,未配置回退 `mysql_user`);`core_ro`→`ro` · `gateway_repository`→`rw` · `risk_repository`/`session_repository`→`rw` 显式;`conftest.py` 4 处→`admin`(R-e)。**两处口径修正**:① `audit_log` 实授 **`SELECT, INSERT`**(字面「只授 INSERT」会剥夺读,致 `has_engine_error_audit`/`list_audit_events` 失权)② conftest 必须 admin。**遗留环境操作**:`00-grant.sql` 需管理员执行 + 写 `.env`,未执行时 3 条权限断言自动 skip
|
||
- [x] **【T-1 · 第 1 批】DDL + 种子 + sqlite 同步** —— **完成(2026-09-10)**:`scripts/core/01-ddl.sql` 新建 `core_fee_rule`/`core_share_lot`/`core_convert_lot_detail` + `core_trade` 加 `convert_group_id`+索引 + `core_product` 加 8 列(`subscribe_fee_rate` 等)+ `fee_rate` 补 COMMENT;**新增 `07-seed-fee-rule.sql`**(14 产品 × 5 档,按 22 号文 §10)/ **`08-seed-share-lot.sql`**(58 行持仓 → 61 行批次,Σ remain_qty 恒等于 qty,CUST-9527 跨批次)/ **`09-seed-org.sql`**(管理人 + TA + 申购费率 + 最低持有余额);`reset.ps1` 追加 07/08/09;`02-mysql-agent专用.sql` 追加 `risk_convert_detail`(status ENUM 建表即 5 值);`tests/_ddl.py` 同步 4 表 + `REQUIRED_CONVERT_TABLES` 门禁。**验证**:新增 `scripts/dev/verify_convert_seed.py`(pymysql 等价 reset 流程 + 8 条断言)→ **8/8 PASS**;`pytest -q` → **516 passed / 3 skipped(零回归)**。**3 点需注意**:① mysql 不在 PATH → 用该脚本替代 reset.ps1;② `core_fee_rule` 读取走只读账号(T-6 遵守);③ ~~`PROD-005827` 费率分类口径差异(`mixed` vs 主动偏股)待裁定~~ → **已裁定并修正(PRD v0.9.1)**:`mixed` 归位 `0.0050`(其他混合型),主示例转入方改真主动偏股 `PROD-003095`,`09-seed-org.sql` 升 **v1.1** 按「管理人全产品线」重排,并新增断言 ⑧ 机器化卡口
|
||
- [x] **【T-2 · 第 2 批】`service/convert/` 纯函数包** —— **完成(2026-09-10)**:新建 `app/service/convert/` **7 文件**(`__init__` / `types`(`Lot`/`FeeRule`/`LotAllocation`/`PlanResult` frozen dataclass + 3 个归一工具)/ `calc`(`plan_lots`/`lot_amount`/`lot_fee`/`convert_amount`/`in_qty`/`rounding_diff`/`diff_fee`/`hold_days`/`ensure_batch_limit`)/ `fee`(`pick_fee_rate` 左闭右开)/ `nav`(`ensure_nav_ready`→503 / `is_stale`)/ `lot_bootstrap`(D18 单点,`crc32` 确定性偏移)/ `errors`(`ConvertError` + 11 子类))。**新增 `tests/test_convert_calc.py` 93 用例**(12 类:精度 HALF_UP 反向自证 / 分档边界 6-7-29-30-179-180-364-365 / FIFO 含同 `confirmed_at` tiebreak / 跨批计费 / 双口径 252.40 vs 253.91 / 强制全转与强制赎回 / 恰好等于阈值不触发 / **零剩余不触发(新裁定 R-h)** / PRD §5.3 全链自证 / T+1 起算 / 净值 503 与 stale 分家 / D18 确定性 / §8.3 错误码 / **纯函数零 IO 依赖断言**)。**验证**:`pytest -q` → **609 passed / 3 skipped(+93,零回归)**;`calc_convert_demo.py` → **15/15 与 PRD §5.3 一致**(退出码 0)
|
||
- [x] **【T-3 ~ T-9 已完成】** 仓储与锁(并行组 A:T-3 / T-4 / T-5 ✅)→ T-6 ✅(阶段一事务 · 真库 24/24)→ T-7 ✅(八步编排 · 真库 35/35)→ **T-8 ✅(规则引擎改造 · 真库 31/31;⭐ 阶段 1.5 从「跳过」变「真跑」)** → **T-9 ✅(API 模型 + 网关分派 · **HTTP 层 convert 已走通**;新增真 MySQL 集成 7 条 + 11 条错误码映射 + 3 组突变验证)**
|
||
- [x] **【T-10 ✅ · T-11 ✅ · T-12 ✅】均已结项(2026-09-10)**:**T-10** 普通申赎批次维护(改 `trade_gateway` 主流程 + D8 兜底补建 + `rebuild_lots.py`;**+17 用例** · 真库 20/20 · 突变 2 组 · R16 零改动通过)→ **T-11** 工具与 SQL 汇总去重(`_amount_view` **提升为公开 `amount_view`** + `query_recent_trades` 汇总去重 FR-C15 + `list_holdings` 过滤 `qty > 0` + `sum_trades_on_date` 加 convert 去重 **R-d**;**+4 用例** · 真库 14/14 · 突变 3 组)→ **T-12** 补偿脚本(公开 **`compensate_convert`** 单点入口 + `rebuild_alerts --convert-group` 薄封装 + 新增 `scripts/agent/cleanup_pending_convert.py`;**+12 用例** · 真库 34/34 · 突变 3 组 · 全套 7 真库脚本复跑零回归;顺带收口 `concentration_profile` 的 `qty > 0`)
|
||
- [x] **【T-13 ✅】**(最后一个 · **已完成 2026-09-10**):**全量回归 → 50 并发压测 → 性能实测补录 → PRD §9 第 18 条数字回填**。**DoD 三条**:① 性能实测值**已回填** PRD §9 第 18 条(v0.9.3,替换「预估 < 100ms」);② 50 并发结论(不超卖 / `100/200/400ms` 退避**不够用**)**已写入** `交接文档.md` §B.6.6;③ 实测**未超阈值** → 无需重定阈值(**未修改实测数据**)。**产出**:`tests/test_convert_concurrency.py`(9 条 · `CONVERT_STRESS` 门禁)+ `test_convert_service` +3 + 修复 `insert_placeholder` 三步法 / 幂等判定 `pending`→202;**736 passed / 10 skipped**、`CONVERT_STRESS=1` 并发 **9/9**、重灌双库复跑零回归、4 组突变验证全命中。
|
||
- [ ] **【待用户裁定】幂等重放响应的数值位数偏差**(T-9 执行期发现,**未顺手改**):首次响应 2 位(`calc` 量化)vs 重放响应 4 位(`core_trade` `DECIMAL(18,4)` 直读)→ `"53456.95"` vs `"53456.9500"`,**数值相等**,违反 PRD「对外一律 2 位」展示契约,属 T-7 `_rebuild_quote` 范畴。集成测试已「比数值不比字符串」并钉住偏差
|
||
|
||
### 第 5 步 · T+1 受理/确认分离模型(T-1 ~ T-17 · ✅ 已收官 2026-09-12)
|
||
|
||
- [x] **T-1 DDL + 种子 ✅(2026-09-11)**:`01-ddl.sql` 新建 `core_convert_request`(6 态 ENUM 权威状态机)/ `core_trade_calendar` / `core_share_rule` + `core_product` 补 4 列;`11-seed-share-rule.sql`(42 行);`reset.ps1` 追加 10/11;`settings` 加 `convert_cutoff_time="15:00"` / `convert_confirm_sla_days=2`;`tests/_ddl.py` 三表同步;`verify_convert_seed.py` 加 DoD ⑨⑩⑪(503 交易日 / 42 行 / 6 态 ENUM,真库全 PASS)
|
||
- [x] **T-2 calc 扩展 ✅**:`calc.py` 加 `product_round` / `redeem_amount` / `partial_qty`(`__all__` 同步);`test_convert_calc.py` +10 用例
|
||
- [x] **T-2b 示例实算回填 ✅**:`calc_convert_demo.py` 改真实净值(T=2026-09-09 `PROD-110022=1.3604` / `PROD-003095=1.9194`)+ 15 项 EXPECTED;`test_convert_integration.py` 净值常量与 5 处硬断言同步;demo 15 项逐字节一致
|
||
- [x] **T-3 数据层 ✅**:`core_ro` 加 6 方法(含 `get_nav_on` 精确匹配、`sum_inflight_qty`、`is_open`、`get_share_rule`);`share_lot_repository` 加 `available_qty_with_inflight`;新建 `convert_request_repository.py`(6 态条件 UPDATE);修 1 处生产 bug(`sum_inflight_qty` 列名 `product_id`→`from_product_id`)
|
||
- [x] **T-4 语义收窄 ✅**:`convert_repository` 新增唯一写入口 **`sync_mirror`**(三步法幂等重写;status 只允许 pending/completed/failed,写 cancelled/expired 直接 ValueError);v1.0 遗留 `insert_placeholder`/`complete_convert`/`mark_failed` 标 Deprecated(T-7 后删除);状态迁移职责归 `convert_request_repository.transition_status`
|
||
- [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 可用份额变化擅自扩大客户指令。
|
||
- [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 剩余的是**验证面**。
|
||
- [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 同源)
|
||
- [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 项 → ✅ 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 字面偏差,✅ 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「提升为公开函数」是两回事。
|
||
> **本线(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` 只是薄封装;
|
||
> ② 锁键 `convert:rerun:{gid}` 与幂等锚点(**转出端 `out_trade_id`**)**沿用既有口径不新造**;
|
||
> ③ `has_engine_error_audit` **加 `decision` 参数**而非另写一份查询(convert 线失败码是 `engine_error`,与 `risk_engine_error` 不同)。
|
||
> **T-12 真库三处 sqlite 给不了的证据**:`expired` 的 **ENUM 值域** · `DATETIME(3)` 时间边界 · **真 JSON 列 `input_summary LIKE`**。
|
||
|
||
> ⚠️ **基金转换的 T-0b 与下方「架构改进第 3/4 批」的 `core_ro` 只读账号是同一件事** —— 已由本线定案为 D20,**不再挂在架构改进线**(该线原「不要做」清单已更新)。
|
||
|
||
## 待办(推荐顺序)
|
||
|
||
- [x] **【阶段一验收门】接口实调验收** —— **通过(2026-09-07 委托 AI 代验:三接口契约 + 三鉴权边界全部符合;前端 web/ 未 init,验收口径为后端接口验证)**
|
||
- [x] **【阶段二】C4~C6 追加需求编码**(实现方案已定稿)—— **全部完成(C4 RISK-006 `fe4801b` / C5 RISK-007 `90d0660` / C6 RISK-008 `66ec7eb`,全量 pytest 482 passed 0 failed);收尾=演示 SOP 补代理人扮演步骤 + 02-redis-keys.md 增补 + tag `risk-m4`(见下条)**
|
||
- [x] **【阶段二前置】**`python -m pytest` 全量(系统 Python 3.13.14)确认基线绿 —— **482 passed 0 failed ✓(2026-09-07)**
|
||
- [x] **【阶段二】C5 前置联动**(挂账 #6):seed STAFF-31001/31002 + AGENT_ACCESS_MATRIX 增补 risk_manager + chat.py 显式 deny(对话线维持仅 risk_officer)+ risk.py 台账 manager 只读分支 + JWT 手册 §5.3 —— **完成(随 C5 落地,见挂账 #6 核对)**
|
||
- [x] **【阶段二】C4 RISK-006 集中度**(FR-8):rules(rule_concentration + RuleHit.alert_subtype)+ core_ro.concentration_profile + engine 接入 + alert_service(锚点排除 agent_behavior 单【评审 P0-1】+ subtype 合并 + alert_type 重算)+ customer_context 扩展 + settings 11 项配置;验收 A-10 —— **完成(commit `fe4801b`)**
|
||
- [x] **【阶段二】C5 RISK-007 时效升级**(FR-9):escalation_service + scripts/cron/escalation_scan.py + query_overdue_alerts Tool + conftest 回拨 fixture;验收 A-11 —— **完成(commit `90d0660`)**
|
||
- [x] **【阶段二】C6 RISK-008 代理人行为链**(FR-10):trade_gateway actor_id 透传(前置)+ agent_behavior_service + scripts/cron/agent_behavior_scan.py + query_agent_behavior Tool;验收 A-12 —— **完成(commit `66ec7eb`)**
|
||
- [x] **【阶段二收尾】**演示 SOP 补代理人扮演步骤(A-12)+ 02-redis-keys.md 增补(C6 联动)+ 挂账 #1~#9 逐项核对 + 打 `risk-m4` tag —— **全部完成(2026-09-07,`risk-m4` 指向 `323d98b`,仅本地未 push)**
|
||
|
||
**挂账 #1~#9 核对结论(基于 C4~C6 三提交 `fe4801b`/`90d0660`/`66ec7eb` + 文档 `323d98b` 实查):**
|
||
| 挂账 | 内容 | 现状 | 判定 |
|
||
|---|---|---|---|
|
||
| #1 | 持仓汇总 | 收口进 `core_ro.concentration_profile`(持仓画像非流水),C4 `rule_concentration` 直接消费 | ✓ 已收口 |
|
||
| #2 | 定时任务内嵌 lifespan | `app/main.py` lifespan 无 cron 调用;`scripts/cron/escalation_scan.py` + `agent_behavior_scan.py` 仍独立 | ✓ 未触碰 |
|
||
| #3 | agent_profile_l3 建表 | 终态 entities 无 `agent_profile_l3`(仅有 `customer_profile_l3`,非同一表) | ✓ 未建 |
|
||
| #4 | escalated 状态改表 | 升级标记走 `payload.escalation_level/escalated_at`,无 status 列变更、无 ALTER | ✓ 未改表 |
|
||
| #5 | alert_type ENUM 扩展 | `alert_type` 仍为 `VARCHAR`,靠 `payload.alert_subtype` 承载子类型 | ✓ 未扩 ENUM |
|
||
| #6 | risk_manager 联动 | 随 C5 前置落地:seed STAFF-31001/31002 + AGENT_ACCESS_MATRIX 放行只读 + handle/aml/scan 端点保持 risk_officer only(manager 触碰即 403) | ✓ 已收口(零处置权) |
|
||
| #7 | customer_query 事件类型补建 | C6 复用既有 `trade_request`/`authz forbidden` 事件,无新建 `customer_query` 事件类型/枚举 | ✓ 未建 |
|
||
| #8 | core_trade actor 字段 | 终态 `core_trade` ORM 无 `actor` 列;actor 透传仅落在 `audit_log`/`agent_*`(`trade_gateway.actor_id` 写入审计,非 core_trade) | ✓ 未加列 |
|
||
| #9 | payload JSON 检索性能 | `risk_repository` 仅改 `append/merge/find` 逻辑,无新增索引/查询计划优化 | ✓ 未动 |
|
||
|
||
**红线复核**:C4~C6 无 DDL/迁移脚本(`scripts/core/02-seed-base.sql` 仅补 seed 行,非改表);审计仅 INSERT;升级标记仅 cron 可写;manager 无处置权;四 Agent 不互调 LLM。挂账清单无一项退化为本期误做。
|
||
|
||
## 待办(模块侧开放项)
|
||
|
||
- [x] **【架构改进线 · 收尾】`docs/开发计划-架构改进.md` §7.2 七项手工冒烟 —— 2026-09-10 全部补跑,7/7 PASS**:无 key 启动告警(清空/占位非空各一次)· 交易阻断+放行(`SUIT_AGE_CONFIRM` / `RISK-001,002`)· 预警聚合(CUST-9527 连发 4 笔,第 3/4 笔 `alert_ids` 同单、`pending_review` 计数 1、`audit` 呈 `alert_created→alert_appended`)· Redis 不可用→退回进程内锁(日志 2 次 `退回进程内锁`,交易仍 200)· Redis 恢复→`退回进程内锁` 命中 0 行 · 调换 `main.py` 装饰器→T-202 守卫变红(`assert ''` @ `test_audit_middleware.py:221`)→字节级还原后转绿。**`037ce7e` 早已 push(远程 `risk-control-agent` = `fffb78a` = 本地 HEAD),原「未 push」表述作废**;残留已按 SOP §2 重灌,全量 **510 passed**。入口:项目根 `交接文档.md` §C
|
||
- [ ] chat 链路 `risk_suitability_log.actor_id` 暂落 SYSTEM(run_tool 统一透传需动 core_tools/kb_tools 冻结 func 契约,发起者留痕已由 agent_tool_call 覆盖)——AL-06 遗留,动 Tool 契约前先出方案
|
||
- [x] **【前端接入·方案 B】chat 拉侧三端点**(commit `8328c24`,2026-09-08):`GET /api/chat/sessions`(本人+本 Agent 线分页列表)、`GET /api/chat/sessions/{id}/messages`(历史消息升序分页,closed 仍可读)、`POST /api/chat/sessions/{id}/close`(active→closed,重复/非 active 409);chat.py 抽 `_resolve_agent_type`/`_assert_chat_entry`/`_guard_session` 三守卫供四端点共用,POST "" 行为零回归;risk_manager 在对话线数据面保持 403(PRD 4A.1)。测试 482→**494 绿**;独立评审 P0=0,P1(close 并发 rowcount 静默 200)已修复
|
||
- [x] **【前端接入·方案 C】SSE 流式对话**(commit `01ec5fc`,2026-09-08):新增 `POST /api/chat/stream`(OpenAI 兼容 chunk:首帧 meta → delta → finish_reason=stop → `[DONE]`);`agent_service.stream_chat` 生成器 + `needs_disclaimer`;`_guard_request`/`_prepare_turn` 与同步端点共用守卫;`session_repository.insert_turn` user+assistant 同事务落库(修评审 P0/P1)。测试 494→**503 绿**。遗留:无心跳帧、断连留空会话待清理
|
||
- [ ] 前端 React 多 Agent 入口(HashRouter,`web/` init)——是否归风控模块由用户拍板
|
||
- [x] 2026-09-05 memory 文件夹更新(新 Agent 交接清单)
|
||
|
||
> **合并 main 相关(AL-09 / AL-10 / AL-11)已移交合并执行人**——模块侧交付完毕(482 绿 + 接口实调验收通过 2026-09-07),执行手册:《docs/项目框架设计/合并注意事项-风控模块并入main.md》(含基底锁定/20 冲突裁决/14 静默文件/三硬伤/合并后必测清单,实测数据编制)。
|
||
|
||
- [x] T-04 Core RO 封装为 Tool 节点;A-01 归属校验(chat Tool 接入 + agent_tool_call 落库)—— **完成(2026-09-07)**:`app/tool/core_tools.py`(L0/持仓/流水三只读 Tool + TOOL_REGISTRY 白名单)+ `app/service/tool_service.py`(关键词意图仅 customer/advisor;归属校验对齐 deps.assert_customer_access 口径——Tool 层拒绝不抛 403 而 blocked 留痕,纵深防御;run_tool 执行+落库,落库失败降级)+ agent_service 图 tool 节点(START→tool→llm→guard;结果注入 LLM 上下文,降级回复带摘要)+ session_repository.insert_tool_call(message_id 一期 NULL)。**A-01 口径:本任务只做 Tool 层归属校验语义(customer 本人/advisor assigned/risk_officer 全量/其余拒),A-01 完整场景(代理人画像)仍归 T-20**;risk 分支四 Tool 归 C1 复用 run_tool。**评审闭环(2026-09-07):首评 P1×2+P2×3+P3×2 → 修复 `40a9ee2` → 复审 P2×1+P3×4 → 修复 `1216d26`,pytest 321 绿;入参白名单+days 钳制、公共鉴权双写 `utils/authz.py`、list_holdings LIMIT 均落地**
|
||
- [x] T-21 Milvus Lite + kb_product_rules 首批入库 —— **完成(2026-09-07,commits e34973f~535c8a0 共 6 个 + 评审闭环 9cc2e59)**:`app/service/embedding.py`(Ollama bge-m3 批量 /api/embed 1024 维,四类失败一律 EmbeddingError 不静默降级)+ `app/service/milvus_service.py`(kb_product_rules 幂等建集合+upsert+search_kb 溯源/effective_date 合规过滤)+ `data/kb/` 6 只种子产品手册(prospectus/fee/rule/risk 四节 24 块)+ `scripts/kb/build_kb.py`(--dry-run/--kb-dir)+ `app/service/rag_service.py` 落地(search_knowledge→chunks+source_refs,异常上抛防 LLM 编造)+ `app/tool/kb_tools.py` 挂注册表第三层(**ToolSpec 新增 skip_access_check**:公开知识无归属语义,advisor 无绑定客户可查;意图层限定 customer/advisor,risk 不开放——用户拍板)+ tool_node 按 spec 白名单注入 query + summarize 溯源口径。**真库验收:24 块入库,search_knowledge("基金申购") Top3 命中申赎规则(score 0.73+),溯源完整**。三坑收口:Milvus Lite 跨进程重开须显式 load / faiss 不支持中文路径(本机 .env 改 C:/Users/YUAN/.jinrong/milvus/)/ build_kb sys.path 引导。知识库 API(上传/重建端点)一期不做(knowledge.py 仍空壳,用户拍板),425 绿
|
||
- [x] T-03 输入防护规则(F-03:prompt_injection/oversize/限流)—— **完成(2026-09-07,commits 497554d/65b45aa/cc6f34f/207fc79)**:`app/service/input_guard.py`(注入短语词表 42 条四类纯函数检测 + `inspect_message` oversize 4000 先于 injection + `check_rate_limit` Redis INCR 固定窗口 actor 级 30 次/分 fail-open)+ chat 路由接入(顺序:鉴权→准入→空白→限流 429→注入/超长 400→归属→会话;被拒 fail-fast 不建会话不落消息,blocked 落 input_guard_log,留痕降级不改拒绝语义;Pydantic 硬顶 20000,业务限 4000 移 guard 层换留痕)+ redis_gateway.incr + settings.guard_rate_limit_*;拍板:命中即拒不清洗、仅 blocked 留痕、词表代码常量可审计。**独立 AI 评审 PASS with findings(P0=0;P2×3 已收口:词表断言修正/补 7 条同构变体/窗口 TTL 断言+EXPIRE 失败取舍锁定;P3 已收 session_id 截断,挂账对抗变体扩表)**,378 绿
|
||
- [x] 风控阶段 C(C0 pytest-asyncio / C1 chat_tools / C2 risk 分支 StateGraph / C3 A-6 验收)—— **完成(2026-09-07)**:`app/service/risk/chat_tools.py` 四只读 Tool(alert_query/customer_context/suitability_check/aml_lookup)挂 `RISK_TOOL_REGISTRY`;`tool_service.run_tool` 改查 core+risk 统一注册表 `get_registered_tool` 且恒传 `risk_repo`;`_normalize_params` 读 spec 白名单;`match_intent` 按 agent 分组扩 risk 关键词;`agent_service.tool_node` 守卫放宽(`requires_customer=False` 允许无绑定客户,支撑 A-6 全量待审);A-6 验收+诱导处置边界(无处置 Tool、系统提示禁自动处置、诱导只触发只读且预警状态不变);**独立 AI 评审 PASS(P0=0,P2 补 advisor 归属/空 product_id 单测已收口),345 绿,tag `risk-m3`**
|
||
|
||
### 风控模块(PRD v1.0 已冻结 · `docs/PRD/PRD-风控监测Agent.md`,事件驱动线不依赖 T-07 可先行)
|
||
|
||
- [x] T-30 风控事件线 —— **全部完成(B1~B9b,M2 tag risk-m2)**:规则引擎/预警聚合/L3/AML/交易网关/4 API/演示走查 11/11 PASS + 核查单①~⑥收口,均经独立 AI 评审闭环;scan_all 幂等读后写无锁留痕(接批量时 run_locked)
|
||
- [x] T-31 `service/suitability.py` 公共校验(SUIT-001~008)+ `POST /api/risk/suitability/check` —— **交付=suitability 服务函数+单测(77 例;check API 随 B6 带鉴权+直调审计);MySQL 手工 SQL 对照(阶段 A 评审 P2-8)2026-09-07 收口:4 组演示组合真库 SQL 推导与函数判定逐项一致(演示 SOP 核查单附行)。【2026-09-07 阶段一退役】SUIT-001~008 编号体系已随 AL-05 换核退役,判定内核改为 main 的 core_ro.check_suitability(C×R 矩阵 + JR-AST/FM 契约),测试按 main 契约重写(原 77 例作废,全量基线 425→406)**
|
||
- [x] T-32 预警台账与人工处置 API + 对话线 —— **台账/处置 API 已由 B6 覆盖并复审通过(A-7/A-9 用例绿);交付=对话线,归阶段 C(C1~C3,T-07 已就绪)**
|
||
|
||
## 已完成
|
||
|
||
- [x] 2026-09-07 风控 M2 收尾(M4 复核):MEMORY/TODO 状态与边界文字对齐(任务映射 T-30/T-31/T-32 口径确认;B7 复审留痕「成功响应不包裹外壳」演进口径维持并登记开发计划 M4 节)——风控阶段 B 正式完结
|
||
- [x] 2026-09-09 架构改进与稳定性加固 T-101~T-202(分支 `risk-control-agent` 本地完成、待用户目视验收后 push):明细见 `docs/项目框架设计/TODO-架构改进.md`(全勾 ✓);pytest **510 绿**(503+7:T-201.3 双层锁 6 + T-202 trace 顺序守卫 1)。含 Redis 双层分布式锁 T-201(acquire_lock/release_lock + run_locked 三降级,多实例安全)/ 审计中间件 trace 顺序守卫 T-202 / 注入词表 42→45 / 风控只读 Tool 加 query_overdue_alerts 共 5 个 / 架构设计补 §5.8 三项设计遗留(G1 X-Trace-Id 白名单 / G2 uuid4 概率唯一 / G3 admin·knowledge 空壳非 RAG 缺失)。**全部为文档·告警·锁原语修正,接口契约与表结构零变更,合并 main 无新增冲突面**。
|
||
- [x] 2026-09-07 T-01 JWT 鉴权(commit 36f476f + 评审闭环 61711cb):`app/service/auth_service.py` Auth SDK(HS256 dev/RS256 生产、必填 claims 显式校验、jti 吊销 fail-open、RS256/HS256 算法白名单互斥)+ `deps.get_auth_context` 工厂替换(Bearer 全环境优先;debug 头降级 dev 兜底,双闸门=APP_ENV+无 RS256 公钥)+ X-Agent-Type 交叉校验(手册 §4.6/§5.4 AGENT_ACCESS_MATRIX)+ `scripts/dev/issue_dev_token.py` 签发 CLI + lifespan 改验 jwt_ready
|
||
- [x] 2026-09-07 T-02 审计中间件(commit 3cec034):`app/api/audit_middleware.py` http_access 全量访问审计(actor 经 request.state.auth,排障路径/OPTIONS 跳过,写库失败降级)+ 独立 request_id(X-Request-Id 贯通,B7 复审 P3-4)+ 422/404/405/500 统一错误体(500 由 trace 中间件兜底回写 trace 头,B7 复审 P2-2)+ input_guard_log 双写(挂账⑧收口;401 归 platform、四 Agent 入口写 guard)
|
||
- [x] 2026-09-07 T-07 LangGraph 骨架(commit fb18c04):`agent_service` StateGraph(llm→guard)+ DeepSeek/langchain-openai(无 key 降级明确标注)+ 四 Agent 边界 system prompt + customer/risk 免责声明(has_disclaimer)+ 单测 mock LLM 不依赖外网
|
||
- [x] 2026-09-07 T-06 chat 最小闭环(commit ee5c78a):`api/chat.py` POST /api/chat(X-Agent-Type 分流+准入+归属校验+SessionGuard AUTH_403_SESSION_AGENT+closed 409)+ `session_repository`(agent_session/agent_message)+ `memory_service`(Redis 窗口 sess:{agent}:{id}:msgs TTL2h N≤20,miss/异常回源 MySQL)+ main 挂载
|
||
- [x] 2026-09-07 Wave 0 独立 AI 评审有条件通过→闭环(commit 61711cb):P2×5(python-jose>=3.5.0 防 CVE / audit 降级测试 / JWT 通道 chat 用例 / debug 兜底纵深防御 / 留痕失败降级显式化)+ P3×11 顺手,273 测试绿
|
||
- [x] 2026-09-05 init 项目记忆目录 + `app/` 脚手架(api/service/tool/model/config/utils)
|
||
- [x] 2026-09-05 需求拆解文档(业务场景/矩阵/合规)
|
||
- [x] 2026-09-05 表设计 + 技术选型 + JWT 手册
|
||
- [x] 2026-09-05 Core 模拟库 `scripts/core/*` + `reset.ps1` + 文档
|
||
- [x] 2026-09-05 `core_ro.py` + `settings.mysql_core_database` + sync 脚本
|
||
- [x] 2026-09-05 Agent 编排依赖改为 LangGraph(requirements.txt)
|
||
- [x] 2026-09-05 memory 文件夹更新(新 Agent 交接清单)
|