Files
group_xinghuo_jinrong/docs/memory/TODO.md
T
GaoYiYuan_0626 1a615bbbef 基金转换 T+1 模型:T-14 D27 产品舍入真正接入 + 联网核实纠偏(837 绿 / 真库 9/9 / 种子 v1.1 全 2 位)
- 接入面 4 处单点收敛:core_ro.get_share_digits(唯一回退链:规则行→core_product.share_digits→PLACES=2)· calc.in_qty 加 digits=PLACES 可选参数 · 确认段(business_type=convert)· 网关申购折算(subscribe);redeem 份额申报无折算无消费方
- 边界裁定:只接落库侧(开发计划 DoD 即边界),响应展示维持 q(D2) 收敛(2 位产品逐字节零漂移);PRD §2.5.2 展示按产品覆盖为登记开口
- 联网核实(用户铁律):主流 2 位 ✅ · 位数按各基金合同配置 ✅(南方 2012 调整公告)· 舍去 TRUNCATE 型真实存在 ✅ · 货基 2 位 ✅ · 「指数基金份额 4 位」证伪 ❌(ETF 联接/指数合同全 2 位)→ 11-seed-share-rule.sql v1.1:3 产品 9 行 4→2 + 头注重写为核实出处;「易方达 ETF 场外整数位」无证据有反证(登记开口待拍板)
- 测试 +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 易方达句修订 ② rounding_mode=truncate 消费接入(真实存在、T-14 范围外)
2026-09-12 12:41:20 +08:00

122 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-11):T-1 ~ T-14 完成(全量 pytest 837 passed / 7 skipped 零失败;真库 verify 套件 9/9 退出码 0:accept 37 / confirm 83 / engine 39 / api 89 / compensate 56 / apply 24 / lots 20 / seed 全 PASS / tools 14,v1.0 链路 verify_convert_service 已随 convert_fund 退役删除),下一步 T-15(D28 部分成交专项 + 边界补强)**。撤单范围: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 · 进行中)
- [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 项待用户拍板**:① PRD §2.5.3「易方达 ETF 场外取整数位」修订;② `rounding_mode=truncate` 消费接入(真实存在、T-14 范围外)。
- [ ] T-15 ~ T-17(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)。
> ⚠️ **以下四条引用块均为 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 交接清单)